drivers: led: ht16k33: reprocess keyscan events in case of i2c error

Reprocess keyscan event processing in case of an I2C bus read
error. Otherwise, the HT16K33 will be stuck (when used in IRQ mode, not
in polling mode) and no new IRQs will be delivered.

Signed-off-by: Henrik Brix Andersen <henrik@brixandersen.dk>
This commit is contained in:
Henrik Brix Andersen 2021-06-14 22:59:06 +02:00 committed by Carles Cufí
commit 93587e9178

View file

@ -62,18 +62,15 @@ LOG_MODULE_REGISTER(ht16k33, CONFIG_LED_LOG_LEVEL);
#define HT16K33_KEYSCAN_DATA_SIZE 6 #define HT16K33_KEYSCAN_DATA_SIZE 6
struct ht16k33_cfg { struct ht16k33_cfg {
char *i2c_dev_name; const struct device *i2c_dev;
uint16_t i2c_addr; uint16_t i2c_addr;
bool irq_enabled; bool irq_enabled;
#ifdef CONFIG_HT16K33_KEYSCAN #ifdef CONFIG_HT16K33_KEYSCAN
char *irq_dev_name; struct gpio_dt_spec irq;
uint32_t irq_pin;
int irq_flags;
#endif /* CONFIG_HT16K33_KEYSCAN */ #endif /* CONFIG_HT16K33_KEYSCAN */
}; };
struct ht16k33_data { struct ht16k33_data {
const struct device *i2c;
const struct device *dev; const struct device *dev;
struct led_data dev_data; struct led_data dev_data;
/* Shadow buffer for the display data RAM */ /* Shadow buffer for the display data RAM */
@ -121,7 +118,7 @@ static int ht16k33_led_blink(const struct device *dev, uint32_t led,
cmd |= HT16K33_OPT_BLINK_2HZ; cmd |= HT16K33_OPT_BLINK_2HZ;
} }
if (i2c_write(data->i2c, &cmd, sizeof(cmd), config->i2c_addr)) { if (i2c_write(config->i2c_dev, &cmd, sizeof(cmd), config->i2c_addr)) {
LOG_ERR("Setting HT16K33 blink frequency failed"); LOG_ERR("Setting HT16K33 blink frequency failed");
return -EIO; return -EIO;
} }
@ -148,7 +145,7 @@ static int ht16k33_led_set_brightness(const struct device *dev, uint32_t led,
dim = (value * (HT16K33_DIMMING_LEVELS - 1)) / dev_data->max_brightness; dim = (value * (HT16K33_DIMMING_LEVELS - 1)) / dev_data->max_brightness;
cmd = HT16K33_CMD_DIMMING_SET | dim; cmd = HT16K33_CMD_DIMMING_SET | dim;
if (i2c_write(data->i2c, &cmd, sizeof(cmd), config->i2c_addr)) { if (i2c_write(config->i2c_dev, &cmd, sizeof(cmd), config->i2c_addr)) {
LOG_ERR("Setting HT16K33 brightness failed"); LOG_ERR("Setting HT16K33 brightness failed");
return -EIO; return -EIO;
} }
@ -183,7 +180,7 @@ static int ht16k33_led_set_state(const struct device *dev, uint32_t led,
return 0; return 0;
} }
if (i2c_write(data->i2c, cmd, sizeof(cmd), config->i2c_addr)) { if (i2c_write(config->i2c_dev, cmd, sizeof(cmd), config->i2c_addr)) {
LOG_ERR("Setting HT16K33 LED %s failed", on ? "on" : "off"); LOG_ERR("Setting HT16K33 LED %s failed", on ? "on" : "off");
return -EIO; return -EIO;
} }
@ -216,12 +213,13 @@ static bool ht16k33_process_keyscan_data(const struct device *dev)
int col; int col;
int err; int err;
err = i2c_burst_read(data->i2c, config->i2c_addr, err = i2c_burst_read(config->i2c_dev, config->i2c_addr,
HT16K33_CMD_KEY_DATA_ADDR, keys, HT16K33_CMD_KEY_DATA_ADDR, keys,
sizeof(keys)); sizeof(keys));
if (err) { if (err) {
LOG_ERR("Failed to to read HT16K33 key data (err %d)", err); LOG_WRN("Failed to to read HT16K33 key data (err %d)", err);
return false; /* Reprocess */
return true;
} }
k_mutex_lock(&data->lock, K_FOREVER); k_mutex_lock(&data->lock, K_FOREVER);
@ -312,9 +310,8 @@ static int ht16k33_init(const struct device *dev)
data->dev = dev; data->dev = dev;
data->i2c = device_get_binding(config->i2c_dev_name); if (!device_is_ready(config->i2c_dev)) {
if (data->i2c == NULL) { LOG_ERR("I2C bus device not ready");
LOG_ERR("Failed to get I2C device");
return -EINVAL; return -EINVAL;
} }
@ -328,7 +325,7 @@ static int ht16k33_init(const struct device *dev)
/* System oscillator on */ /* System oscillator on */
cmd[0] = HT16K33_CMD_SYSTEM_SETUP | HT16K33_OPT_S; cmd[0] = HT16K33_CMD_SYSTEM_SETUP | HT16K33_OPT_S;
err = i2c_write(data->i2c, cmd, 1, config->i2c_addr); err = i2c_write(config->i2c_dev, cmd, 1, config->i2c_addr);
if (err) { if (err) {
LOG_ERR("Enabling HT16K33 system oscillator failed (err %d)", LOG_ERR("Enabling HT16K33 system oscillator failed (err %d)",
err); err);
@ -338,7 +335,7 @@ static int ht16k33_init(const struct device *dev)
/* Clear display RAM */ /* Clear display RAM */
memset(cmd, 0, sizeof(cmd)); memset(cmd, 0, sizeof(cmd));
cmd[0] = HT16K33_CMD_DISP_DATA_ADDR; cmd[0] = HT16K33_CMD_DISP_DATA_ADDR;
err = i2c_write(data->i2c, cmd, sizeof(cmd), config->i2c_addr); err = i2c_write(config->i2c_dev, cmd, sizeof(cmd), config->i2c_addr);
if (err) { if (err) {
LOG_ERR("Clearing HT16K33 display RAM failed (err %d)", err); LOG_ERR("Clearing HT16K33 display RAM failed (err %d)", err);
return -EIO; return -EIO;
@ -346,7 +343,7 @@ static int ht16k33_init(const struct device *dev)
/* Full brightness */ /* Full brightness */
cmd[0] = HT16K33_CMD_DIMMING_SET | 0x0f; cmd[0] = HT16K33_CMD_DIMMING_SET | 0x0f;
err = i2c_write(data->i2c, cmd, 1, config->i2c_addr); err = i2c_write(config->i2c_dev, cmd, 1, config->i2c_addr);
if (err) { if (err) {
LOG_ERR("Setting HT16K33 brightness failed (err %d)", err); LOG_ERR("Setting HT16K33 brightness failed (err %d)", err);
return -EIO; return -EIO;
@ -354,7 +351,7 @@ static int ht16k33_init(const struct device *dev)
/* Display on, blinking off */ /* Display on, blinking off */
cmd[0] = HT16K33_CMD_DISP_SETUP | HT16K33_OPT_D | HT16K33_OPT_BLINK_OFF; cmd[0] = HT16K33_CMD_DISP_SETUP | HT16K33_OPT_D | HT16K33_OPT_BLINK_OFF;
err = i2c_write(data->i2c, cmd, 1, config->i2c_addr); err = i2c_write(config->i2c_dev, cmd, 1, config->i2c_addr);
if (err) { if (err) {
LOG_ERR("Enabling HT16K33 display failed (err %d)", err); LOG_ERR("Enabling HT16K33 display failed (err %d)", err);
return -EIO; return -EIO;
@ -366,27 +363,23 @@ static int ht16k33_init(const struct device *dev)
/* Configure interrupt */ /* Configure interrupt */
if (config->irq_enabled) { if (config->irq_enabled) {
const struct device *irq_dev;
uint8_t keys[HT16K33_KEYSCAN_DATA_SIZE]; uint8_t keys[HT16K33_KEYSCAN_DATA_SIZE];
irq_dev = device_get_binding(config->irq_dev_name); if (!device_is_ready(config->irq.port)) {
if (!irq_dev) { LOG_ERR("IRQ device not ready");
LOG_ERR("IRQ device '%s' not found",
config->irq_dev_name);
return -EINVAL; return -EINVAL;
} }
err = gpio_pin_configure(irq_dev, config->irq_pin, err = gpio_pin_configure_dt(&config->irq, GPIO_INPUT);
GPIO_INPUT | config->irq_flags);
if (err) { if (err) {
LOG_ERR("Failed to configure IRQ pin (err %d)", err); LOG_ERR("Failed to configure IRQ pin (err %d)", err);
return -EINVAL; return -EINVAL;
} }
gpio_init_callback(&data->irq_cb, &ht16k33_irq_callback, gpio_init_callback(&data->irq_cb, &ht16k33_irq_callback,
BIT(config->irq_pin)); BIT(config->irq.pin));
err = gpio_add_callback(irq_dev, &data->irq_cb); err = gpio_add_callback(config->irq.port, &data->irq_cb);
if (err) { if (err) {
LOG_ERR("Failed to add IRQ callback (err %d)", err); LOG_ERR("Failed to add IRQ callback (err %d)", err);
return -EINVAL; return -EINVAL;
@ -394,21 +387,21 @@ static int ht16k33_init(const struct device *dev)
/* Enable interrupt pin */ /* Enable interrupt pin */
cmd[0] = HT16K33_CMD_ROW_INT_SET | HT16K33_OPT_INT_LOW; cmd[0] = HT16K33_CMD_ROW_INT_SET | HT16K33_OPT_INT_LOW;
if (i2c_write(data->i2c, cmd, 1, config->i2c_addr)) { if (i2c_write(config->i2c_dev, cmd, 1, config->i2c_addr)) {
LOG_ERR("Enabling HT16K33 IRQ output failed"); LOG_ERR("Enabling HT16K33 IRQ output failed");
return -EIO; return -EIO;
} }
/* Flush key data before enabling interrupt */ /* Flush key data before enabling interrupt */
err = i2c_burst_read(data->i2c, config->i2c_addr, err = i2c_burst_read(config->i2c_dev, config->i2c_addr,
HT16K33_CMD_KEY_DATA_ADDR, keys, sizeof(keys)); HT16K33_CMD_KEY_DATA_ADDR, keys, sizeof(keys));
if (err) { if (err) {
LOG_ERR("Failed to to read HT16K33 key data"); LOG_ERR("Failed to to read HT16K33 key data");
return -EIO; return -EIO;
} }
err = gpio_pin_interrupt_configure(irq_dev, config->irq_pin, err = gpio_pin_interrupt_configure_dt(&config->irq,
GPIO_INT_EDGE_FALLING); GPIO_INT_EDGE_FALLING);
if (err) { if (err) {
LOG_ERR("Failed to configure IRQ pin flags (err %d)", LOG_ERR("Failed to configure IRQ pin flags (err %d)",
err); err);
@ -417,7 +410,7 @@ static int ht16k33_init(const struct device *dev)
} else { } else {
/* No interrupt pin, enable ROW15 */ /* No interrupt pin, enable ROW15 */
cmd[0] = HT16K33_CMD_ROW_INT_SET | HT16K33_OPT_ROW; cmd[0] = HT16K33_CMD_ROW_INT_SET | HT16K33_OPT_ROW;
if (i2c_write(data->i2c, cmd, 1, config->i2c_addr)) { if (i2c_write(config->i2c_dev, cmd, 1, config->i2c_addr)) {
LOG_ERR("Enabling HT16K33 ROW15 output failed"); LOG_ERR("Enabling HT16K33 ROW15 output failed");
return -EIO; return -EIO;
} }
@ -447,7 +440,7 @@ static const struct led_driver_api ht16k33_leds_api = {
#define HT16K33_DEVICE(id) \ #define HT16K33_DEVICE(id) \
static const struct ht16k33_cfg ht16k33_##id##_cfg = { \ static const struct ht16k33_cfg ht16k33_##id##_cfg = { \
.i2c_dev_name = DT_INST_BUS_LABEL(id), \ .i2c_dev = DEVICE_DT_GET(DT_INST_BUS(id)), \
.i2c_addr = DT_INST_REG_ADDR(id), \ .i2c_addr = DT_INST_REG_ADDR(id), \
.irq_enabled = false, \ .irq_enabled = false, \
}; \ }; \
@ -462,14 +455,10 @@ static const struct led_driver_api ht16k33_leds_api = {
#ifdef CONFIG_HT16K33_KEYSCAN #ifdef CONFIG_HT16K33_KEYSCAN
#define HT16K33_DEVICE_WITH_IRQ(id) \ #define HT16K33_DEVICE_WITH_IRQ(id) \
static const struct ht16k33_cfg ht16k33_##id##_cfg = { \ static const struct ht16k33_cfg ht16k33_##id##_cfg = { \
.i2c_dev_name = DT_INST_BUS_LABEL(id), \ .i2c_dev = DEVICE_DT_GET(DT_INST_BUS(id)), \
.i2c_addr = DT_INST_REG_ADDR(id), \ .i2c_addr = DT_INST_REG_ADDR(id), \
.irq_enabled = true, \ .irq_enabled = true, \
.irq_dev_name = \ .irq = GPIO_DT_SPEC_INST_GET(id, irq_gpios), \
DT_INST_GPIO_LABEL(id, irq_gpios), \
.irq_pin = DT_INST_GPIO_PIN(id, irq_gpios), \
.irq_flags = \
DT_INST_GPIO_FLAGS(id, irq_gpios), \
}; \ }; \
\ \
static struct ht16k33_data ht16k33_##id##_data; \ static struct ht16k33_data ht16k33_##id##_data; \