Lines Matching +full:has +full:- +full:interrupt +full:- +full:mask +full:- +full:reg

6  * SPDX-License-Identifier: Apache-2.0
12 * @file Driver for PCA95XX and PCAL95XX I2C-based GPIO driver.
98 /* Self-reference to the driver instance */
104 /* interrupt triggering pin masks */
120 static int read_port_reg(const struct device *dev, uint8_t reg, uint8_t pin, in read_port_reg() argument
123 const struct gpio_pca95xx_config * const config = dev->config; in read_port_reg()
128 reg++; in read_port_reg()
131 ret = i2c_reg_read_byte_dt(&config->bus, reg, &b_buf); in read_port_reg()
134 config->bus.addr, reg, ret); in read_port_reg()
146 LOG_DBG("PCA95XX[0x%X]: Read: REG[0x%X] = 0x%X", in read_port_reg()
147 config->bus.addr, reg, b_buf); in read_port_reg()
155 * Given the register in reg, read the pair of port 0 and port 1.
158 * @param reg Register to read (the PORT0 of the pair of registers).
164 static int read_port_regs(const struct device *dev, uint8_t reg, in read_port_regs() argument
167 const struct gpio_pca95xx_config * const config = dev->config; in read_port_regs()
171 ret = i2c_burst_read_dt(&config->bus, reg, (uint8_t *)&port_data, in read_port_regs()
175 config->bus.addr, reg, ret); in read_port_regs()
183 LOG_DBG("PCA95XX[0x%X]: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", in read_port_regs()
184 config->bus.addr, reg, (*buf & 0xFF), (reg + 1), (*buf >> 8)); in read_port_regs()
190 static int write_port_reg(const struct device *dev, uint8_t reg, uint8_t pin, in write_port_reg() argument
193 const struct gpio_pca95xx_config * const config = dev->config; in write_port_reg()
201 reg++; in write_port_reg()
203 buf[0] = reg; in write_port_reg()
205 LOG_DBG("PCA95XX[0x%X]: Write: REG[0x%X] = 0x%X", config->bus.addr, in write_port_reg()
206 reg, buf[1]); in write_port_reg()
208 ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); in write_port_reg()
213 "(%d)", config->bus.addr, reg, ret); in write_port_reg()
222 * Given the register in reg, write the pair of port 0 and port 1.
225 * @param reg Register to write into (the PORT0 of the pair of registers).
231 static int write_port_regs(const struct device *dev, uint8_t reg, in write_port_regs() argument
234 const struct gpio_pca95xx_config * const config = dev->config; in write_port_regs()
238 LOG_DBG("PCA95XX[0x%X]: Write: REG[0x%X] = 0x%X, REG[0x%X] = " in write_port_regs()
239 "0x%X", config->bus.addr, reg, (value & 0xFF), in write_port_regs()
240 (reg + 1), (value >> 8)); in write_port_regs()
242 buf[0] = reg; in write_port_regs()
245 ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); in write_port_regs()
250 "(%d)", config->bus.addr, reg, ret); in write_port_regs()
260 (struct gpio_pca95xx_drv_data * const)dev->data; in update_input_reg()
263 &drv_data->reg_cache.input, buf); in update_input_reg()
269 (struct gpio_pca95xx_drv_data * const)dev->data; in update_input_regs()
272 &drv_data->reg_cache.input, buf); in update_input_regs()
279 (struct gpio_pca95xx_drv_data * const)dev->data; in update_output_reg()
282 &drv_data->reg_cache.output, value); in update_output_reg()
288 (struct gpio_pca95xx_drv_data * const)dev->data; in update_output_regs()
291 &drv_data->reg_cache.output, value); in update_output_regs()
298 (struct gpio_pca95xx_drv_data * const)dev->data; in update_direction_reg()
301 &drv_data->reg_cache.dir, value); in update_direction_reg()
307 (struct gpio_pca95xx_drv_data * const)dev->data; in update_direction_regs()
310 &drv_data->reg_cache.dir, value); in update_direction_regs()
317 (struct gpio_pca95xx_drv_data * const)dev->data; in update_pul_sel_reg()
320 &drv_data->reg_cache.pud_sel, value); in update_pul_sel_reg()
327 (struct gpio_pca95xx_drv_data * const)dev->data; in update_pul_en_reg()
330 &drv_data->reg_cache.pud_en, value); in update_pul_en_reg()
338 (struct gpio_pca95xx_drv_data * const)dev->data; in update_int_mask_reg()
340 /* If the interrupt mask is present, so is the input latch */ in update_int_mask_reg()
341 write_port_reg(dev, REG_INPUT_LATCH_PORT0, pin, &drv_data->reg_cache.input_latch, ~value); in update_int_mask_reg()
344 &drv_data->reg_cache.int_mask, value); in update_int_mask_reg()
360 (struct gpio_pca95xx_drv_data * const)dev->data; in setup_pin_dir()
361 uint16_t reg_dir = drv_data->reg_cache.dir; in setup_pin_dir()
362 uint16_t reg_out = drv_data->reg_cache.output; in setup_pin_dir()
410 const struct gpio_pca95xx_config * const config = dev->config; in setup_pin_pullupdown()
412 (struct gpio_pca95xx_drv_data * const)dev->data; in setup_pin_pullupdown()
416 if ((config->capabilities & PCA_HAS_PUD) == 0) { in setup_pin_pullupdown()
419 return -ENOTSUP; in setup_pin_pullupdown()
434 reg_pud = drv_data->reg_cache.pud_sel; in setup_pin_pullupdown()
446 reg_pud = drv_data->reg_cache.pud_en; in setup_pin_pullupdown()
470 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_config()
473 const struct gpio_pca95xx_config * const config = dev->config; in gpio_pca95xx_config()
478 return -ENOTSUP; in gpio_pca95xx_config()
481 /* Open-drain support is per port, not per pin. in gpio_pca95xx_config()
482 * So can't really support the API as-is. in gpio_pca95xx_config()
485 return -ENOTSUP; in gpio_pca95xx_config()
490 return -EWOULDBLOCK; in gpio_pca95xx_config()
493 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_config()
498 config->bus.addr, ret); in gpio_pca95xx_config()
505 "(%d)", config->bus.addr, ret); in gpio_pca95xx_config()
510 k_sem_give(&drv_data->lock); in gpio_pca95xx_config()
517 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_port_get_raw()
523 return -EWOULDBLOCK; in gpio_pca95xx_port_get_raw()
526 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_port_get_raw()
536 k_sem_give(&drv_data->lock); in gpio_pca95xx_port_get_raw()
541 uint32_t mask, uint32_t value) in gpio_pca95xx_port_set_masked_raw() argument
544 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_port_set_masked_raw()
550 return -EWOULDBLOCK; in gpio_pca95xx_port_set_masked_raw()
553 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_port_set_masked_raw()
555 reg_out = drv_data->reg_cache.output; in gpio_pca95xx_port_set_masked_raw()
556 reg_out = (reg_out & ~mask) | (mask & value); in gpio_pca95xx_port_set_masked_raw()
560 k_sem_give(&drv_data->lock); in gpio_pca95xx_port_set_masked_raw()
566 uint32_t mask) in gpio_pca95xx_port_set_bits_raw() argument
568 return gpio_pca95xx_port_set_masked_raw(dev, mask, mask); in gpio_pca95xx_port_set_bits_raw()
572 uint32_t mask) in gpio_pca95xx_port_clear_bits_raw() argument
574 return gpio_pca95xx_port_set_masked_raw(dev, mask, 0); in gpio_pca95xx_port_clear_bits_raw()
578 uint32_t mask) in gpio_pca95xx_port_toggle_bits() argument
581 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_port_toggle_bits()
587 return -EWOULDBLOCK; in gpio_pca95xx_port_toggle_bits()
590 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_port_toggle_bits()
592 reg_out = drv_data->reg_cache.output; in gpio_pca95xx_port_toggle_bits()
593 reg_out ^= mask; in gpio_pca95xx_port_toggle_bits()
597 k_sem_give(&drv_data->lock); in gpio_pca95xx_port_toggle_bits()
609 input_cache = drv_data->reg_cache.input; in get_triggered_it()
610 ret = update_input_regs(drv_data->instance, &input_new); in get_triggered_it()
615 drv_data->interrupts.edge_rising); in get_triggered_it()
617 drv_data->interrupts.edge_falling); in get_triggered_it()
618 *trig_level |= (input_new & drv_data->interrupts.level_high); in get_triggered_it()
619 *trig_level |= (~input_new & drv_data->interrupts.level_low); in get_triggered_it()
627 const struct gpio_pca95xx_config * const config = drv_data->instance->config; in gpio_pca95xx_interrupt_worker()
631 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_interrupt_worker()
633 /* Note: PCA Interrupt status is cleared by reading inputs */ in gpio_pca95xx_interrupt_worker()
634 if (config->capabilities & PCA_HAS_INTERRUPT_MASK_REG) { in gpio_pca95xx_interrupt_worker()
638 /* gpio unlatched read values, in case signal has flipped again */ in gpio_pca95xx_interrupt_worker()
643 k_sem_give(&drv_data->lock); in gpio_pca95xx_interrupt_worker()
646 gpio_fire_callbacks(&drv_data->callbacks, drv_data->instance, in gpio_pca95xx_interrupt_worker()
653 k_work_submit(&drv_data->interrupt_worker); in gpio_pca95xx_interrupt_worker()
667 k_work_submit(&drv_data->interrupt_worker); in gpio_pca95xx_interrupt_callback()
676 const struct gpio_pca95xx_config * const config = dev->config; in gpio_pca95xx_pin_interrupt_configure()
678 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_pin_interrupt_configure()
679 uint16_t reg; in gpio_pca95xx_pin_interrupt_configure() local
683 if ((config->capabilities & PCA_HAS_INTERRUPT) == 0U) { in gpio_pca95xx_pin_interrupt_configure()
684 return -ENOTSUP; in gpio_pca95xx_pin_interrupt_configure()
688 if (BIT(pin) > config->common.port_pin_mask) { in gpio_pca95xx_pin_interrupt_configure()
689 return -EINVAL; in gpio_pca95xx_pin_interrupt_configure()
694 (BIT(pin) & drv_data->reg_cache.dir) != BIT(pin)) { in gpio_pca95xx_pin_interrupt_configure()
695 LOG_ERR("PCA95XX[0x%X]: output pin cannot trigger interrupt", in gpio_pca95xx_pin_interrupt_configure()
696 config->bus.addr); in gpio_pca95xx_pin_interrupt_configure()
697 return -ENOTSUP; in gpio_pca95xx_pin_interrupt_configure()
700 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_pin_interrupt_configure()
702 /* Check if GPIO port has an interrupt mask register */ in gpio_pca95xx_pin_interrupt_configure()
703 if (config->capabilities & PCA_HAS_INTERRUPT_MASK_REG) { in gpio_pca95xx_pin_interrupt_configure()
706 reg_out = drv_data->reg_cache.int_mask; in gpio_pca95xx_pin_interrupt_configure()
711 LOG_ERR("PCA95XX[0x%X]: failed to update int mask (%d)", in gpio_pca95xx_pin_interrupt_configure()
712 config->bus.addr, ret); in gpio_pca95xx_pin_interrupt_configure()
717 /* Update interrupt masks */ in gpio_pca95xx_pin_interrupt_configure()
721 WRITE_BIT(drv_data->interrupts.edge_rising, pin, (enabled && in gpio_pca95xx_pin_interrupt_configure()
723 WRITE_BIT(drv_data->interrupts.edge_falling, pin, (enabled && in gpio_pca95xx_pin_interrupt_configure()
725 WRITE_BIT(drv_data->interrupts.level_high, pin, (enabled && in gpio_pca95xx_pin_interrupt_configure()
727 WRITE_BIT(drv_data->interrupts.level_low, pin, (enabled && in gpio_pca95xx_pin_interrupt_configure()
730 active = ((drv_data->interrupts.edge_rising || in gpio_pca95xx_pin_interrupt_configure()
731 drv_data->interrupts.edge_falling || in gpio_pca95xx_pin_interrupt_configure()
732 drv_data->interrupts.level_high || in gpio_pca95xx_pin_interrupt_configure()
733 drv_data->interrupts.level_low) > 0); in gpio_pca95xx_pin_interrupt_configure()
735 /* Enable / disable interrupt as needed */ in gpio_pca95xx_pin_interrupt_configure()
736 if (active != drv_data->interrupt_active) { in gpio_pca95xx_pin_interrupt_configure()
738 &config->int_gpio, active ? in gpio_pca95xx_pin_interrupt_configure()
742 LOG_ERR("PCA95XX[0x%X]: failed to configure interrupt " in gpio_pca95xx_pin_interrupt_configure()
743 "on pin %d (%d)", config->bus.addr, in gpio_pca95xx_pin_interrupt_configure()
744 config->int_gpio.pin, ret); in gpio_pca95xx_pin_interrupt_configure()
747 drv_data->interrupt_active = active; in gpio_pca95xx_pin_interrupt_configure()
753 update_input_regs(dev, &reg); in gpio_pca95xx_pin_interrupt_configure()
758 k_sem_give(&drv_data->lock); in gpio_pca95xx_pin_interrupt_configure()
766 const struct gpio_pca95xx_config * const config = dev->config; in gpio_pca95xx_manage_callback()
768 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_manage_callback()
770 if ((config->capabilities & PCA_HAS_INTERRUPT) == 0U) { in gpio_pca95xx_manage_callback()
771 return -ENOTSUP; in gpio_pca95xx_manage_callback()
774 k_sem_take(&drv_data->lock, K_FOREVER); in gpio_pca95xx_manage_callback()
776 gpio_manage_callback(&drv_data->callbacks, callback, set); in gpio_pca95xx_manage_callback()
778 k_sem_give(&drv_data->lock); in gpio_pca95xx_manage_callback()
804 const struct gpio_pca95xx_config * const config = dev->config; in gpio_pca95xx_init()
806 (struct gpio_pca95xx_drv_data * const)dev->data; in gpio_pca95xx_init()
809 if (!device_is_ready(config->bus.bus)) { in gpio_pca95xx_init()
810 return -ENODEV; in gpio_pca95xx_init()
813 k_sem_init(&drv_data->lock, 1, 1); in gpio_pca95xx_init()
822 if ((config->capabilities & PCA_HAS_INTERRUPT) != 0) { in gpio_pca95xx_init()
823 /* Store self-reference for interrupt handling */ in gpio_pca95xx_init()
824 drv_data->instance = dev; in gpio_pca95xx_init()
826 /* Prepare interrupt worker */ in gpio_pca95xx_init()
827 k_work_init(&drv_data->interrupt_worker, in gpio_pca95xx_init()
830 /* Configure GPIO interrupt pin */ in gpio_pca95xx_init()
831 if (!gpio_is_ready_dt(&config->int_gpio)) { in gpio_pca95xx_init()
832 LOG_ERR("PCA95XX[0x%X]: interrupt GPIO not ready", in gpio_pca95xx_init()
833 config->bus.addr); in gpio_pca95xx_init()
834 return -ENODEV; in gpio_pca95xx_init()
837 ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); in gpio_pca95xx_init()
839 LOG_ERR("PCA95XX[0x%X]: failed to configure interrupt" in gpio_pca95xx_init()
840 " pin %d (%d)", config->bus.addr, in gpio_pca95xx_init()
841 config->int_gpio.pin, ret); in gpio_pca95xx_init()
845 /* Prepare GPIO callback for interrupt pin */ in gpio_pca95xx_init()
846 gpio_init_callback(&drv_data->gpio_callback, in gpio_pca95xx_init()
848 BIT(config->int_gpio.pin)); in gpio_pca95xx_init()
849 ret = gpio_add_callback(config->int_gpio.port, &drv_data->gpio_callback); in gpio_pca95xx_init()
851 LOG_ERR("PCA95XX[0x%X]: failed to add interrupt callback for" in gpio_pca95xx_init()
852 " pin %d (%d)", config->bus.addr, in gpio_pca95xx_init()
853 config->int_gpio.pin, ret); in gpio_pca95xx_init()