2022-10-17 13:58:41 +08:00
|
|
|
/*
|
|
|
|
* 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)) {
|
2023-08-26 11:43:58 +08:00
|
|
|
if (!gpio_is_ready_dt(&config->irq_gpio)) {
|
2022-10-17 13:58:41 +08:00
|
|
|
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)
|