| /* |
| * Copyright 2022 Google LLC |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file File that collects common data and configs for RS1718S chip. The file |
| * doesn't provide any API itself. The feature-specific API should be provided |
| * in separated files e.g. GPIO API. |
| * |
| * This file is placed in drivers/gpio directory, because GPIO is only one |
| * supported feature at the moment. It can be move to tcpc dir in the future. |
| */ |
| |
| #define DT_DRV_COMPAT richtek_rt1718s |
| |
| #include "gpio_rt1718s.h" |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/util_macro.h> |
| LOG_MODULE_REGISTER(gpio_rt1718s, CONFIG_GPIO_LOG_LEVEL); |
| |
| static void rt1718s_alert_callback(const struct device *dev, struct gpio_callback *cb, |
| uint32_t pins) |
| { |
| ARG_UNUSED(pins); |
| struct rt1718s_data *data = CONTAINER_OF(cb, struct rt1718s_data, gpio_cb); |
| |
| k_work_submit(&data->alert_worker); |
| } |
| |
| static void rt1718s_alert_worker(struct k_work *work) |
| { |
| struct rt1718s_data *const data = CONTAINER_OF(work, struct rt1718s_data, alert_worker); |
| const struct device *const dev = data->dev; |
| const struct rt1718s_config *const config = dev->config; |
| uint16_t alert, mask; |
| |
| do { |
| /* Read alert and mask */ |
| k_sem_take(&data->lock_tcpci, K_FOREVER); |
| if (rt1718s_reg_burst_read(dev, RT1718S_REG_ALERT, (uint8_t *)&alert, |
| sizeof(alert)) || |
| rt1718s_reg_burst_read(dev, RT1718S_REG_ALERT_MASK, (uint8_t *)&mask, |
| sizeof(mask))) { |
| k_sem_give(&data->lock_tcpci); |
| LOG_ERR("i2c access failed"); |
| continue; |
| } |
| |
| /* Content of the alert and alert mask registers are |
| * defined by the TCPCI specification - "A masked |
| * register will still indicate in the ALERT register, |
| * but shall not set the Alert# pin low" |
| */ |
| alert &= mask; |
| if (alert) { |
| /* Clear all alert bits that causes the interrupt */ |
| if (rt1718s_reg_burst_write(dev, RT1718S_REG_ALERT, (uint8_t *)&alert, |
| sizeof(alert))) { |
| k_sem_give(&data->lock_tcpci); |
| LOG_ERR("i2c access failed"); |
| continue; |
| } |
| } |
| k_sem_give(&data->lock_tcpci); |
| |
| /* There are a few sources of the vendor |
| * defined alert for the RT1718S, but handle |
| * only GPIO at the moment. |
| */ |
| if (alert & RT1718S_REG_ALERT_VENDOR_DEFINED_ALERT) { |
| rt1718s_gpio_alert_handler(dev); |
| } |
| /* While the interrupt signal is still active, we have more work to do. */ |
| } while (gpio_pin_get_dt(&config->irq_gpio)); |
| } |
| |
| static int rt1718s_init(const struct device *dev) |
| { |
| const struct rt1718s_config *const config = dev->config; |
| struct rt1718s_data *data = dev->data; |
| int ret; |
| |
| /* Check I2C is ready */ |
| if (!device_is_ready(config->i2c_dev.bus)) { |
| LOG_ERR("%s device not ready", config->i2c_dev.bus->name); |
| return -ENODEV; |
| } |
| |
| k_sem_init(&data->lock_tcpci, 1, 1); |
| |
| if (IS_ENABLED(CONFIG_GPIO_RT1718S_INTERRUPT)) { |
| if (!device_is_ready(config->irq_gpio.port)) { |
| LOG_ERR("%s device not ready", config->irq_gpio.port->name); |
| return -ENODEV; |
| } |
| |
| /* Set the interrupt pin for handling the alert */ |
| k_work_init(&data->alert_worker, rt1718s_alert_worker); |
| |
| gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT); |
| |
| gpio_init_callback(&data->gpio_cb, rt1718s_alert_callback, |
| BIT(config->irq_gpio.pin)); |
| |
| ret = gpio_add_callback(config->irq_gpio.port, &data->gpio_cb); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| gpio_pin_interrupt_configure_dt(&config->irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); |
| } |
| |
| return 0; |
| } |
| |
| #define CHECK_PORT_DEVICE(node_id) \ |
| COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(richtek_rt1718s_gpio_port), DEVICE_DT_GET(node_id), \ |
| ()) |
| |
| #define IRQ_GPIO(inst) \ |
| COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ |
| (.irq_gpio = GPIO_DT_SPEC_INST_GET(inst, irq_gpios)), ()) |
| |
| #define GET_PORT_DEVICE(inst) DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, CHECK_PORT_DEVICE) |
| |
| #define GPIO_RT1718S_DEVICE_INSTANCE(inst) \ |
| static const struct rt1718s_config rt1718s_cfg_##inst = { \ |
| .i2c_dev = I2C_DT_SPEC_INST_GET(inst), \ |
| .gpio_port_dev = GET_PORT_DEVICE(inst), \ |
| IRQ_GPIO(inst) \ |
| }; \ |
| static struct rt1718s_data rt1718s_data_##inst = { \ |
| .dev = DEVICE_DT_INST_GET(inst), \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(inst, rt1718s_init, NULL, &rt1718s_data_##inst, &rt1718s_cfg_##inst, \ |
| POST_KERNEL, CONFIG_RT1718S_INIT_PRIORITY, NULL); |
| |
| DT_INST_FOREACH_STATUS_OKAY(GPIO_RT1718S_DEVICE_INSTANCE) |