From e04182f60d498bf75600e7c826528980bc5dc4f7 Mon Sep 17 00:00:00 2001 From: Luiz Souza Date: Tue, 19 Sep 2017 15:48:12 -0500 Subject: Export the LED breath timers to userland to make easier to control the LEDs effects from PHP. (cherry picked from commit 9fbcb1b9dac21d044f29fd2d90a8bf2292b9bc57) --- sys/dev/iicbus/is31fl319x.c | 378 ++++++++++++++++++++++++++++++++++------- sys/dev/iicbus/is31fl319xreg.h | 9 + 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 -- cgit v1.1