summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Souza <luiz@netgate.com>2017-09-19 15:48:12 -0500
committerLuiz Souza <luiz@netgate.com>2017-09-19 15:51:20 -0500
commite04182f60d498bf75600e7c826528980bc5dc4f7 (patch)
tree336766dbdf1fa001517505b330478333fa8a4924
parent0887ca7ad9342d789f8dc7900a32de03d6d710a1 (diff)
downloadFreeBSD-src-e04182f60d498bf75600e7c826528980bc5dc4f7.zip
FreeBSD-src-e04182f60d498bf75600e7c826528980bc5dc4f7.tar.gz
Export the LED breath timers to userland to make easier to control the LEDs effects from PHP.
(cherry picked from commit 9fbcb1b9dac21d044f29fd2d90a8bf2292b9bc57)
-rw-r--r--sys/dev/iicbus/is31fl319x.c378
-rw-r--r--sys/dev/iicbus/is31fl319xreg.h9
2 files changed, 323 insertions, 64 deletions
diff --git a/sys/dev/iicbus/is31fl319x.c b/sys/dev/iicbus/is31fl319x.c
index 5d33efc..9635d4f 100644
--- a/sys/dev/iicbus/is31fl319x.c
+++ b/sys/dev/iicbus/is31fl319x.c
@@ -65,22 +65,307 @@ static struct ofw_compat_data compat_data[] = {
{ NULL, 0 }
};
+struct is31fl319x_reg {
+ struct is31fl319x_softc *sc;
+ uint8_t data;
+ uint8_t id;
+ uint8_t reg;
+};
+
struct is31fl319x_softc {
device_t sc_dev;
device_t sc_gpio_busdev;
int sc_max_pins;
- uint16_t sc_addr;
uint8_t sc_pwm[IS31FL319X_MAX_PINS];
+ uint8_t sc_conf1;
+ struct is31fl319x_reg sc_t0[IS31FL319X_MAX_PINS];
+ struct is31fl319x_reg sc_t123[IS31FL319X_MAX_PINS / 3];
+ struct is31fl319x_reg sc_t4[IS31FL319X_MAX_PINS];
};
static __inline int
-is31fl319x_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
+is31fl319x_write(device_t dev, uint8_t reg, uint8_t *data, size_t len)
+{
+
+ return (iicdev_writeto(dev, reg, data, len, IIC_INTRWAIT));
+}
+
+static __inline int
+is31fl319x_reg_update(struct is31fl319x_softc *sc, uint8_t reg)
+{
+ uint8_t data = 0;
+
+ return (iicdev_writeto(sc->sc_dev, reg, &data, 1, IIC_INTRWAIT));
+}
+
+static int
+is31fl319x_pwm_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int error, led;
+ int32_t enable;
+ struct is31fl319x_softc *sc;
+
+ sc = (struct is31fl319x_softc *)arg1;
+ led = arg2;
+
+ enable = ((sc->sc_conf1 & IS31FL319X_CONF1_PWM(led)) != 0) ? 0 : 1;
+ error = sysctl_handle_int(oidp, &enable, sizeof(enable), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ enable = (enable == 0) ? 1 : 0;
+ sc->sc_conf1 ^= IS31FL319X_CONF1_PWM(led);
+ if (is31fl319x_write(sc->sc_dev, IS31FL319X_CONF1, &sc->sc_conf1,
+ sizeof(sc->sc_conf1)) != 0)
+ return (ENXIO);
+ if (is31fl319x_reg_update(sc, IS31FL319X_DATA_UPDATE) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+is31fl319x_pin_timer_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ int32_t a, b, ms;
+ struct is31fl319x_reg *timer;
+ struct is31fl319x_softc *sc;
+
+ timer = (struct is31fl319x_reg *)arg1;
+ sc = timer->sc;
+
+ a = timer->data & IS31FL319X_T0_A_MASK;
+ b = (timer->data & IS31FL319X_T0_B_MASK) >> 4;
+ ms = 260 * a * (2 << b);
+ error = sysctl_handle_int(oidp, &ms, sizeof(ms), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (ms > IS31FL319X_T0_MAX_TIME)
+ ms = IS31FL319X_T0_MAX_TIME;
+
+ a = b = 0;
+ if (ms > 260) {
+ ms /= 260;
+ while (ms / (2 << b) > 15) {
+ if (ms / (2 << b) > 15)
+ b++;
+ else
+ break;
+ }
+ a = ms / (2 << b);
+ }
+ timer->data = (b << 4) | a;
+
+ if (is31fl319x_write(sc->sc_dev, timer->reg, &timer->data,
+ sizeof(timer->data)) != 0)
+ return (ENXIO);
+ if (is31fl319x_reg_update(sc, IS31FL319X_TIME_UPDATE) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+is31fl319x_dt_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ int32_t enable;
+ struct is31fl319x_reg *led;
+ struct is31fl319x_softc *sc;
+
+ led = (struct is31fl319x_reg *)arg1;
+ sc = led->sc;
+
+ enable = ((led->data & IS31FL319X_DT) != 0) ? 1 : 0;
+ error = sysctl_handle_int(oidp, &enable, sizeof(enable), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (enable)
+ led->data |= IS31FL319X_DT;
+ else
+ led->data &= ~IS31FL319X_DT;
+ if (is31fl319x_write(sc->sc_dev, IS31FL319X_T123(led->id), &led->data,
+ sizeof(led->data)) != 0)
+ return (ENXIO);
+ if (is31fl319x_reg_update(sc, IS31FL319X_TIME_UPDATE) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+is31fl319x_t1t3_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ int32_t a, ms;
+ struct is31fl319x_reg *led;
+ struct is31fl319x_softc *sc;
+
+ led = (struct is31fl319x_reg *)arg1;
+ sc = led->sc;
+
+ a = (led->data & IS31FL319X_T1_A_MASK);
+ if (a >= 5 && a <= 6)
+ ms = 0;
+ else if (a == 7)
+ ms = 100;
+ else
+ ms = 260 * (2 << a);
+ error = sysctl_handle_int(oidp, &ms, sizeof(ms), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (ms > IS31FL319X_T1_MAX_TIME)
+ ms = IS31FL319X_T1_MAX_TIME;
+
+ a = 0;
+ if (ms == 0)
+ a = 5; /* Breathing function disabled. */
+ if (ms == 100)
+ a = 7; /* 100 ms */
+ else if (ms >= 260) {
+ ms /= 260;
+ while (ms / (2 << a) > 0) {
+ if (ms / (2 << a) > 0)
+ a++;
+ else
+ break;
+ }
+ }
+ led->data &= ~IS31FL319X_T1_A_MASK;
+ led->data |= (a & IS31FL319X_T1_A_MASK);
+
+ if (is31fl319x_write(sc->sc_dev, IS31FL319X_T123(led->id), &led->data,
+ sizeof(led->data)) != 0)
+ return (ENXIO);
+ if (is31fl319x_reg_update(sc, IS31FL319X_TIME_UPDATE) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+is31fl319x_t2_sysctl(SYSCTL_HANDLER_ARGS)
{
- struct iic_msg msg[1] = {
- { addr, IIC_M_WR, len, data },
- };
+ int error;
+ int32_t b, ms;
+ struct is31fl319x_reg *led;
+ struct is31fl319x_softc *sc;
+
+ led = (struct is31fl319x_reg *)arg1;
+ sc = led->sc;
- return (iicbus_transfer(dev, msg, nitems(msg)));
+ b = (led->data & IS31FL319X_T2_B_MASK) >> 4;
+ if (b > 0)
+ ms = 260 * (2 << (b - 1));
+ else
+ ms = 0;
+ error = sysctl_handle_int(oidp, &ms, sizeof(ms), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (ms > IS31FL319X_T2_MAX_TIME)
+ ms = IS31FL319X_T2_MAX_TIME;
+
+ b = 0;
+ if (ms > 260) {
+ ms /= 260;
+ b = 1;
+ while (ms / (2 << (b - 1)) > 0) {
+ if (ms / (2 << (b - 1)) > 0)
+ b++;
+ else
+ break;
+ }
+ }
+ led->data &= ~IS31FL319X_T2_B_MASK;
+ led->data |= ((b << 4) & IS31FL319X_T2_B_MASK);
+
+ if (is31fl319x_write(sc->sc_dev, IS31FL319X_T123(led->id), &led->data,
+ sizeof(led->data)) != 0)
+ return (ENXIO);
+ if (is31fl319x_reg_update(sc, IS31FL319X_TIME_UPDATE) != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static void
+is31fl319x_sysctl_attach(device_t dev)
+{
+ char strbuf[4];
+ struct is31fl319x_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node, *led_node, *ledN_node, *pin_node;
+ struct sysctl_oid *pinN_node;
+ struct sysctl_oid_list *tree, *led_tree, *ledN_tree, *pin_tree;
+ struct sysctl_oid_list *pinN_tree;
+ int led, pin;
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree_node = device_get_sysctl_tree(dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", CTLFLAG_RD,
+ NULL, "Output Pins");
+ pin_tree = SYSCTL_CHILDREN(pin_node);
+
+ sc = device_get_softc(dev);
+ for (pin = 0; pin < sc->sc_max_pins; pin++) {
+
+ snprintf(strbuf, sizeof(strbuf), "%d", pin);
+ pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, strbuf,
+ CTLFLAG_RD, NULL, "Output Pin");
+ pinN_tree = SYSCTL_CHILDREN(pinN_node);
+
+ sc->sc_t0[pin].sc = sc;
+ sc->sc_t0[pin].data = 0;
+ sc->sc_t0[pin].id = pin;
+ sc->sc_t0[pin].reg = IS31FL319X_T0(pin);
+ SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "T0",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE,
+ &sc->sc_t0[pin], 0, is31fl319x_pin_timer_sysctl, "IU",
+ "T0 timer in ms");
+ sc->sc_t4[pin].sc = sc;
+ sc->sc_t4[pin].data = 0;
+ sc->sc_t4[pin].id = pin;
+ sc->sc_t4[pin].reg = IS31FL319X_T4(pin);
+ SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "T4",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE,
+ &sc->sc_t4[pin], 0, is31fl319x_pin_timer_sysctl, "IU",
+ "T4 timer in ms");
+ }
+ led_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "led", CTLFLAG_RD,
+ NULL, "RGB LEDs");
+ led_tree = SYSCTL_CHILDREN(led_node);
+ for (led = 0; led < (sc->sc_max_pins / 3); led++) {
+ snprintf(strbuf, sizeof(strbuf), "%d", led);
+ ledN_node = SYSCTL_ADD_NODE(ctx, led_tree, OID_AUTO, strbuf,
+ CTLFLAG_RD, NULL, "RGB LED");
+ ledN_tree = SYSCTL_CHILDREN(ledN_node);
+
+ SYSCTL_ADD_PROC(ctx, ledN_tree, OID_AUTO, "pwm",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, led,
+ is31fl319x_pwm_sysctl, "IU", "Enable the PWM control");
+ sc->sc_t123[led].sc = sc;
+ sc->sc_t123[led].data = 0;
+ sc->sc_t123[led].id = led;
+ sc->sc_t123[led].reg = IS31FL319X_T123(led);
+ SYSCTL_ADD_PROC(ctx, ledN_tree, OID_AUTO, "T1-T3",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE,
+ &sc->sc_t123[led], 0, is31fl319x_t1t3_sysctl, "IU",
+ "T1 and T3 timer");
+ SYSCTL_ADD_PROC(ctx, ledN_tree, OID_AUTO, "DT",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE,
+ &sc->sc_t123[led], 0, is31fl319x_dt_sysctl, "IU",
+ "T3 Double Time (T3 = 2T1)");
+ SYSCTL_ADD_PROC(ctx, ledN_tree, OID_AUTO, "T2",
+ CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE,
+ &sc->sc_t123[led], 0, is31fl319x_t2_sysctl, "IU",
+ "T2 timer");
+ }
}
static int
@@ -124,73 +409,42 @@ is31fl319x_probe(device_t dev)
return (BUS_PROBE_DEFAULT);
}
-static __inline int
-is31fl319x_reg_update(struct is31fl319x_softc *sc, uint8_t reg)
-{
- uint8_t data[2];
-
- data[0] = reg;
- data[1] = 0;
-
- return (is31fl319x_write(sc->sc_dev, sc->sc_addr, data, sizeof(data)));
-}
-
static int
is31fl319x_attach(device_t dev)
{
struct is31fl319x_softc *sc;
- uint8_t data[4];
+ uint8_t data[3];
sc = device_get_softc(dev);
sc->sc_dev = dev;
- sc->sc_addr = iicbus_get_addr(dev);
/* Reset the LED driver. */
- data[0] = IS31FL319X_RESET;
- data[1] = 0;
- if (is31fl319x_write(dev, sc->sc_addr, data, 2) != 0)
+ data[0] = 0;
+ if (is31fl319x_write(dev, IS31FL319X_RESET, data, 1) != 0)
return (ENXIO);
+
/* Disable the shutdown mode. */
- data[0] = IS31FL319X_SHUTDOWN;
- data[1] = 1;
- if (is31fl319x_write(dev, sc->sc_addr, data, 2) != 0)
+ data[0] = 1;
+ if (is31fl319x_write(dev, IS31FL319X_SHUTDOWN, data, 1) != 0)
return (ENXIO);
- /* Set a basic breath sequence. */
- data[0] = IS31FL319X_CONF1;
- data[1] = (1 << 4);
- if (is31fl319x_write(dev, sc->sc_addr, data, 2) != 0)
+ /* Attach gpiobus. */
+ sc->sc_gpio_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_gpio_busdev == NULL)
return (ENXIO);
- data[0] = IS31FL319X_PWM(0);
- data[1] = 200;
+
+ is31fl319x_sysctl_attach(dev);
+
+ /* Update the booting status, kernel is loading. */
+ data[0] = 0;
+ data[1] = 0;
data[2] = 50;
- data[3] = 90;
- if (is31fl319x_write(dev, sc->sc_addr, data, sizeof(data)) != 0)
- return (ENXIO);
- if (is31fl319x_reg_update(sc, IS31FL319X_DATA_UPDATE) != 0)
- return (ENXIO);
- data[0] = IS31FL319X_T0(0);
- data[1] = 0x7;
- data[2] = 0x8;
- data[3] = 0x9;
- if (is31fl319x_write(dev, sc->sc_addr, data, sizeof(data)) != 0)
- return (ENXIO);
- data[0] = IS31FL319X_T123(0);
- data[1] = 0x12;
- if (is31fl319x_write(dev, sc->sc_addr, data, 2) != 0)
+ if (is31fl319x_write(dev, IS31FL319X_PWM(6), data, sizeof(data)) != 0)
return (ENXIO);
- data[0] = IS31FL319X_T4(0);
- data[1] = 0x0;
- data[2] = 0x1;
- data[3] = 0x2;
- if (is31fl319x_write(dev, sc->sc_addr, data, sizeof(data)) != 0)
+ data[2] = 100;
+ if (is31fl319x_write(dev, IS31FL319X_PWM(3), data, sizeof(data)) != 0)
return (ENXIO);
- if (is31fl319x_reg_update(sc, IS31FL319X_TIME_UPDATE) != 0)
- return (ENXIO);
-
- /* Attach gpiobus. */
- sc->sc_gpio_busdev = gpiobus_attach_bus(dev);
- if (sc->sc_gpio_busdev == NULL)
+ if (is31fl319x_reg_update(sc, IS31FL319X_DATA_UPDATE) != 0)
return (ENXIO);
return (0);
@@ -280,7 +534,6 @@ static int
is31fl319x_gpio_pin_set(device_t dev, uint32_t pin, uint32_t value)
{
struct is31fl319x_softc *sc;
- uint8_t data[2];
sc = device_get_softc(dev);
if (pin >= sc->sc_max_pins)
@@ -290,9 +543,8 @@ is31fl319x_gpio_pin_set(device_t dev, uint32_t pin, uint32_t value)
sc->sc_pwm[pin] = IS31FL319X_PWM_MAX;
else
sc->sc_pwm[pin] = 0;
- data[0] = IS31FL319X_PWM(pin);
- data[1] = sc->sc_pwm[pin];
- if (is31fl319x_write(dev, sc->sc_addr, data, sizeof(data)) != 0)
+ if (is31fl319x_write(dev, IS31FL319X_PWM(pin),
+ &sc->sc_pwm[pin], 1) != 0)
return (ENXIO);
return (is31fl319x_reg_update(sc, IS31FL319X_DATA_UPDATE));
@@ -350,7 +602,6 @@ is31fl319x_gpio_pwm_set(device_t dev, int32_t pwm, uint32_t pin, uint32_t reg,
uint32_t val)
{
struct is31fl319x_softc *sc;
- uint8_t data[2];
sc = device_get_softc(dev);
if (pin >= sc->sc_max_pins)
@@ -360,9 +611,8 @@ is31fl319x_gpio_pwm_set(device_t dev, int32_t pwm, uint32_t pin, uint32_t reg,
return (EINVAL);
sc->sc_pwm[pin] = (uint8_t)val;
- data[0] = IS31FL319X_PWM(pin);
- data[1] = sc->sc_pwm[pin];
- if (is31fl319x_write(dev, sc->sc_addr, data, sizeof(data)) != 0)
+ if (is31fl319x_write(dev, IS31FL319X_PWM(pin),
+ &sc->sc_pwm[pin], 1) != 0)
return (ENXIO);
return (is31fl319x_reg_update(sc, IS31FL319X_DATA_UPDATE));
diff --git a/sys/dev/iicbus/is31fl319xreg.h b/sys/dev/iicbus/is31fl319xreg.h
index cda7207..d780eb9 100644
--- a/sys/dev/iicbus/is31fl319xreg.h
+++ b/sys/dev/iicbus/is31fl319xreg.h
@@ -37,6 +37,7 @@
#define IS31FL319X_LEDCTRL1 0x01
#define IS31FL319X_LEDCTRL2 0x02
#define IS31FL319X_CONF1 0x03
+#define IS31FL319X_CONF1_PWM(led) (1 << (4 + led))
#define IS31FL319X_CONF2 0x04
#define IS31FL319X_RAMPMODE 0x05
#define IS31FL319X_BREATHMARK 0x06
@@ -44,7 +45,15 @@
#define IS31FL319X_PWM_MAX 0xff
#define IS31FL319X_DATA_UPDATE 0x10
#define IS31FL319X_T0(out) (0x11 + (out))
+#define IS31FL319X_T0_A_MASK 0x0f
+#define IS31FL319X_T0_B_MASK 0x30
+#define IS31FL319X_T0_MAX_TIME 31200
#define IS31FL319X_T123(led) (0x1a + (led))
+#define IS31FL319X_T1_A_MASK 0x07
+#define IS31FL319X_T1_MAX_TIME 4160
+#define IS31FL319X_T2_B_MASK 0x70
+#define IS31FL319X_T2_MAX_TIME 16640
+#define IS31FL319X_DT (1 << 7)
#define IS31FL319X_T4(out) (0x1d + (out))
#define IS31FL319X_TIME_UPDATE 0x26
#define IS31FL319X_RESET 0xff
OpenPOWER on IntegriCloud