driver: gpio: rt1718s: Add RT1718S GPIO driver
RT1718S is an i2c-based TCPC chip that supports 3 additional GPIOs. The pins can be used for USB-C operations e.g. handling FRS, but they can also work as usual GPIOs. Add a driver for the RT1718S GPIO and a handler for an alert signal from the chip. The handler reads the alert register once asserted and calls the GPIO interrupt handler if needed(Vendor-defined alert). gpio_rt1718s.c file and "richtek,rt1718s" node collect common properties and data for all RS1718S functionalities. The file can be extended for TCPC driver. rt1718s.h file also defines inline functions with i2c operations common for all drivers. The common header and source files can be moved to tcpc directories once the tcpc driver is added since it is the main functionality. Signed-off-by: Dawid Niedzwiecki <dn@semihalf.com>
This commit is contained in:
parent
a9ac0a884e
commit
2d93f03c25
8 changed files with 794 additions and 0 deletions
|
|
@ -67,3 +67,5 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_USERSPACE gpio_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_XMC4XXX gpio_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_NPM6001 gpio_npm6001.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s_port.c)
|
||||
|
|
|
|||
|
|
@ -158,4 +158,6 @@ source "drivers/gpio/Kconfig.tca6424a"
|
|||
|
||||
source "drivers/gpio/Kconfig.npm6001"
|
||||
|
||||
source "drivers/gpio/Kconfig.rt1718s"
|
||||
|
||||
endif # GPIO
|
||||
|
|
|
|||
35
drivers/gpio/Kconfig.rt1718s
Normal file
35
drivers/gpio/Kconfig.rt1718s
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# RT1718S driver configuration options
|
||||
|
||||
# Copyright 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig GPIO_RT1718S
|
||||
bool "RT1718S I2C-based TCPC chip with GPIOs"
|
||||
default y
|
||||
depends on DT_HAS_RICHTEK_RT1718S_GPIO_PORT_ENABLED
|
||||
help
|
||||
Enable driver GPIO for RT1718S I2C-based TCPC chip.
|
||||
|
||||
if GPIO_RT1718S
|
||||
|
||||
config RT1718S_INIT_PRIORITY
|
||||
int "RT1718S GPIO init priority"
|
||||
default 60
|
||||
help
|
||||
RT1718S device driver initialization priority. The priority should be
|
||||
lower than I2C device.
|
||||
|
||||
config GPIO_RT1718S_PORT_INIT_PRIORITY
|
||||
int "RT1718S GPIO port init priority"
|
||||
default 61
|
||||
help
|
||||
RT1718S GPIO driver initialization priority. The priority should be lower
|
||||
than I2C & RT1718S_INIT_PRIORITY device.
|
||||
|
||||
config GPIO_RT1718S_INTERRUPT
|
||||
bool "RT1718S alert handler"
|
||||
help
|
||||
Enable support for handling RT1718S Alert with a GPIO interrupt connected
|
||||
from the chip.
|
||||
|
||||
endif # GPIO_RT1718S
|
||||
144
drivers/gpio/gpio_rt1718s.c
Normal file
144
drivers/gpio/gpio_rt1718s.c
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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)
|
||||
171
drivers/gpio/gpio_rt1718s.h
Normal file
171
drivers/gpio/gpio_rt1718s.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_GPIO_RT1718S_H_
|
||||
#define ZEPHYR_DRIVERS_GPIO_RT1718S_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define RT1718S_GPIO_NUM 3
|
||||
|
||||
#define RT1718S_REG_ALERT 0x10
|
||||
#define RT1718S_REG_ALERT_VENDOR_DEFINED_ALERT BIT(15)
|
||||
|
||||
#define RT1718S_REG_ALERT_MASK 0x12
|
||||
#define RT1718S_REG_ALERT_MASK_VENDOR_DEFINED_ALERT BIT(15)
|
||||
|
||||
#define RT1718S_REG_RT_MASK8 0xA6
|
||||
#define RT1718S_REG_RT_MASK8_GPIO1_R BIT(0)
|
||||
#define RT1718S_REG_RT_MASK8_GPIO2_R BIT(1)
|
||||
#define RT1718S_REG_RT_MASK8_GPIO3_R BIT(2)
|
||||
#define RT1718S_REG_RT_MASK8_GPIO1_F BIT(4)
|
||||
#define RT1718S_REG_RT_MASK8_GPIO2_F BIT(5)
|
||||
#define RT1718S_REG_RT_MASK8_GPIO3_F BIT(6)
|
||||
|
||||
#define RT1718S_REG_RT_INT8 0xA8
|
||||
#define RT1718S_REG_RT_INT8_GPIO1_R BIT(0)
|
||||
#define RT1718S_REG_RT_INT8_GPIO2_R BIT(1)
|
||||
#define RT1718S_REG_RT_INT8_GPIO3_R BIT(2)
|
||||
#define RT1718S_REG_RT_INT8_GPIO1_F BIT(4)
|
||||
#define RT1718S_REG_RT_INT8_GPIO2_F BIT(5)
|
||||
#define RT1718S_REG_RT_INT8_GPIO3_F BIT(6)
|
||||
#define RT1718S_GPIO_INT_MASK \
|
||||
(RT1718S_REG_RT_INT8_GPIO1_R | RT1718S_REG_RT_INT8_GPIO2_R | RT1718S_REG_RT_INT8_GPIO3_R | \
|
||||
RT1718S_REG_RT_INT8_GPIO1_F | RT1718S_REG_RT_INT8_GPIO2_F | RT1718S_REG_RT_INT8_GPIO3_F)
|
||||
|
||||
#define RT1718S_REG_RT_ST8 0xAA
|
||||
#define RT1718S_REG_RT_ST8_GPIO1_I BIT(0)
|
||||
#define RT1718S_REG_RT_ST8_GPIO2_I BIT(1)
|
||||
#define RT1718S_REG_RT_ST8_GPIO3_I BIT(2)
|
||||
|
||||
#define RT1718S_REG_GPIO_CTRL(pin) (0xED + pin)
|
||||
#define RT1718S_REG_GPIO_CTRL_PU BIT(5)
|
||||
#define RT1718S_REG_GPIO_CTRL_PD BIT(4)
|
||||
#define RT1718S_REG_GPIO_CTRL_OD_N BIT(3)
|
||||
#define RT1718S_REG_GPIO_CTRL_OE BIT(2)
|
||||
#define RT1718S_REG_GPIO_CTRL_O BIT(1)
|
||||
#define RT1718S_REG_GPIO_CTRL_I BIT(0)
|
||||
|
||||
/* RT1718S chip driver config */
|
||||
struct rt1718s_config {
|
||||
/* I2C device */
|
||||
const struct i2c_dt_spec i2c_dev;
|
||||
/* Alert GPIO pin */
|
||||
const struct gpio_dt_spec irq_gpio;
|
||||
/* GPIO port device */
|
||||
const struct device *gpio_port_dev;
|
||||
};
|
||||
|
||||
/* RT1718S chip driver data */
|
||||
struct rt1718s_data {
|
||||
/* RT1718S device */
|
||||
const struct device *dev;
|
||||
/* lock TCPCI registers access */
|
||||
struct k_sem lock_tcpci;
|
||||
/* Alert pin callback */
|
||||
struct gpio_callback gpio_cb;
|
||||
/* Alert worker */
|
||||
struct k_work alert_worker;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read a RT1718S register
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
* @param reg_addr Register address
|
||||
* @param val A pointer to a buffer for the data to return
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int rt1718s_reg_read_byte(const struct device *dev, uint8_t reg_addr, uint8_t *val)
|
||||
{
|
||||
const struct rt1718s_config *const config = (const struct rt1718s_config *)dev->config;
|
||||
|
||||
return i2c_reg_read_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a sequence of RT1718S registers
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
* @param start_addr The register start address
|
||||
* @param buf A pointer to a buffer for the data to return
|
||||
* @param num_bytes Number of data to read
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int rt1718s_reg_burst_read(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes)
|
||||
{
|
||||
const struct rt1718s_config *const config = (const struct rt1718s_config *)dev->config;
|
||||
|
||||
return i2c_burst_read_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a RT1718S register
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
* @param reg_addr Register address
|
||||
* @param val Data to write
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int rt1718s_reg_write_byte(const struct device *dev, uint8_t reg_addr, uint8_t val)
|
||||
{
|
||||
const struct rt1718s_config *const config = (const struct rt1718s_config *)dev->config;
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a sequence of RT1718S registers
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
* @param start_addr The register start address
|
||||
* @param buf A pointer to a buffer for the data to write
|
||||
* @param num_bytes Number of data to write
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int rt1718s_reg_burst_write(const struct device *dev, uint8_t start_addr,
|
||||
uint8_t *buf, uint32_t num_bytes)
|
||||
{
|
||||
const struct rt1718s_config *const config = (const struct rt1718s_config *)dev->config;
|
||||
|
||||
return i2c_burst_write_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare data & write a RT1718S register
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
* @param reg_addr Register address
|
||||
* @param reg_val Old register data
|
||||
* @param new_val New register data
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int rt1718s_reg_update(const struct device *dev, uint8_t reg_addr, uint8_t reg_val,
|
||||
uint8_t new_val)
|
||||
{
|
||||
if (reg_val == new_val)
|
||||
return 0;
|
||||
|
||||
return rt1718s_reg_write_byte(dev, reg_addr, new_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dispatch GPIO port alert
|
||||
*
|
||||
* @param dev RT1718S device
|
||||
*/
|
||||
void rt1718s_gpio_alert_handler(const struct device *dev);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_GPIO_RT1718S_H_*/
|
||||
376
drivers/gpio/gpio_rt1718s_port.c
Normal file
376
drivers/gpio/gpio_rt1718s_port.c
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT richtek_rt1718s_gpio_port
|
||||
|
||||
/**
|
||||
* @file Driver for RS1718S TCPC chip GPIOs.
|
||||
*/
|
||||
|
||||
#include "gpio_rt1718s.h"
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(gpio_rt1718s_port, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
/* Driver config */
|
||||
struct gpio_rt1718s_port_config {
|
||||
/* gpio_driver_config needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
/* RT1718S chip device */
|
||||
const struct device *rt1718s_dev;
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct gpio_rt1718s_port_data {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_data common;
|
||||
/* GPIO callback list */
|
||||
sys_slist_t cb_list_gpio;
|
||||
/* lock GPIO registers access */
|
||||
struct k_sem lock;
|
||||
};
|
||||
|
||||
/* GPIO api functions */
|
||||
static int gpio_rt1718s_pin_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
uint8_t new_reg = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* Don't support simultaneous in/out mode */
|
||||
if ((flags & GPIO_INPUT) && (flags & GPIO_OUTPUT)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Don't support "open source" mode */
|
||||
if ((flags & GPIO_SINGLE_ENDED) && !(flags & GPIO_LINE_OPEN_DRAIN)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* RT1718S has 3 GPIOs so check range */
|
||||
if (pin >= RT1718S_GPIO_NUM) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Configure pin as input. */
|
||||
if (flags & GPIO_INPUT) {
|
||||
/* Do not set RT1718S_REG_GPIO_CTRL_OE bit for input */
|
||||
/* Set pull-high/low input */
|
||||
if (flags & GPIO_PULL_UP) {
|
||||
new_reg |= RT1718S_REG_GPIO_CTRL_PU;
|
||||
}
|
||||
if (flags & GPIO_PULL_DOWN) {
|
||||
new_reg |= RT1718S_REG_GPIO_CTRL_PD;
|
||||
}
|
||||
} else if (flags & GPIO_OUTPUT) {
|
||||
/* Set GPIO as output */
|
||||
new_reg |= RT1718S_REG_GPIO_CTRL_OE;
|
||||
|
||||
/* Set push-pull or open-drain */
|
||||
if (!(flags & GPIO_SINGLE_ENDED)) {
|
||||
new_reg |= RT1718S_REG_GPIO_CTRL_OD_N;
|
||||
}
|
||||
|
||||
/* Set init state */
|
||||
if (flags & GPIO_OUTPUT_INIT_HIGH) {
|
||||
new_reg |= RT1718S_REG_GPIO_CTRL_O;
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
ret = rt1718s_reg_write_byte(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin), new_reg);
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_port_get_raw(const struct device *dev, gpio_port_value_t *value)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
uint8_t reg;
|
||||
int ret;
|
||||
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_RT_ST8, ®);
|
||||
*value = reg & (RT1718S_REG_RT_ST8_GPIO1_I | RT1718S_REG_RT_ST8_GPIO2_I |
|
||||
RT1718S_REG_RT_ST8_GPIO3_I);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
uint8_t new_reg, reg;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
|
||||
for (int pin = 0; pin < RT1718S_GPIO_NUM; pin++) {
|
||||
if (mask & BIT(pin)) {
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (value & BIT(pin)) {
|
||||
new_reg = reg | RT1718S_REG_GPIO_CTRL_O;
|
||||
} else {
|
||||
new_reg = reg & ~RT1718S_REG_GPIO_CTRL_O;
|
||||
}
|
||||
ret = rt1718s_reg_update(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
reg, new_reg);
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
uint8_t new_reg, reg;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
|
||||
for (int pin = 0; pin < RT1718S_GPIO_NUM; pin++) {
|
||||
if (mask & BIT(pin)) {
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
new_reg = reg | RT1718S_REG_GPIO_CTRL_O;
|
||||
ret = rt1718s_reg_update(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
reg, new_reg);
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
uint8_t new_reg, reg;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
|
||||
for (int pin = 0; pin < RT1718S_GPIO_NUM; pin++) {
|
||||
if (mask & BIT(pin)) {
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
new_reg = reg & ~RT1718S_REG_GPIO_CTRL_O;
|
||||
ret = rt1718s_reg_update(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
reg, new_reg);
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_port_toggle_bits(const struct device *dev, gpio_port_pins_t mask)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
uint8_t new_reg, reg;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
|
||||
for (int pin = 0; pin < RT1718S_GPIO_NUM; pin++) {
|
||||
if (mask & BIT(pin)) {
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
new_reg = reg ^ RT1718S_REG_GPIO_CTRL_O;
|
||||
ret = rt1718s_reg_update(config->rt1718s_dev, RT1718S_REG_GPIO_CTRL(pin),
|
||||
reg, new_reg);
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
|
||||
enum gpio_int_mode mode, enum gpio_int_trig trig)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
struct rt1718s_data *const data_rt1718s = config->rt1718s_dev->data;
|
||||
uint8_t reg_int8, reg_mask8, new_reg_mask8 = 0;
|
||||
uint8_t mask_rise = BIT(pin), mask_fall = BIT(4 + pin);
|
||||
uint16_t alert_mask;
|
||||
int ret;
|
||||
|
||||
/* Check passed arguments */
|
||||
if (mode == GPIO_INT_MODE_LEVEL || pin >= RT1718S_GPIO_NUM) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(&data_rt1718s->lock_tcpci, K_FOREVER);
|
||||
|
||||
ret = rt1718s_reg_read_byte(config->rt1718s_dev, RT1718S_REG_RT_MASK8, ®_mask8);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Disable GPIO interrupt */
|
||||
if (mode == GPIO_INT_MODE_DISABLED) {
|
||||
new_reg_mask8 = reg_mask8 & ~(mask_rise | mask_fall);
|
||||
} else if (mode == GPIO_INT_MODE_EDGE) {
|
||||
switch (trig) {
|
||||
case GPIO_INT_TRIG_BOTH:
|
||||
new_reg_mask8 = reg_mask8 | mask_rise | mask_fall;
|
||||
break;
|
||||
case GPIO_INT_TRIG_HIGH:
|
||||
new_reg_mask8 = (reg_mask8 | mask_rise) & ~mask_fall;
|
||||
break;
|
||||
case GPIO_INT_TRIG_LOW:
|
||||
new_reg_mask8 = (reg_mask8 | mask_fall) & ~mask_rise;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = rt1718s_reg_burst_read(config->rt1718s_dev, RT1718S_REG_ALERT_MASK,
|
||||
(uint8_t *)&alert_mask, sizeof(alert_mask));
|
||||
if (ret) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Enable Vendor Defined Alert for GPIO interrupts */
|
||||
if (!(alert_mask & RT1718S_REG_ALERT_MASK_VENDOR_DEFINED_ALERT)) {
|
||||
alert_mask |= RT1718S_REG_ALERT_MASK_VENDOR_DEFINED_ALERT;
|
||||
ret = rt1718s_reg_burst_write(config->rt1718s_dev, RT1718S_REG_ALERT_MASK,
|
||||
(uint8_t *)&alert_mask, sizeof(alert_mask));
|
||||
|
||||
if (ret) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear pending interrupts, which were trigger before enabling the pin
|
||||
* interrupt by user.
|
||||
*/
|
||||
reg_int8 = mask_rise | mask_fall;
|
||||
rt1718s_reg_write_byte(config->rt1718s_dev, RT1718S_REG_RT_INT8, reg_int8);
|
||||
}
|
||||
|
||||
/* MASK8 handles 3 GPIOs interrupts, both edges */
|
||||
ret = rt1718s_reg_update(config->rt1718s_dev, RT1718S_REG_RT_MASK8, reg_mask8,
|
||||
new_reg_mask8);
|
||||
|
||||
done:
|
||||
k_sem_give(&data_rt1718s->lock_tcpci);
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_rt1718s_manage_callback(const struct device *dev, struct gpio_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
|
||||
return gpio_manage_callback(&data->cb_list_gpio, callback, set);
|
||||
}
|
||||
|
||||
void rt1718s_gpio_alert_handler(const struct device *dev)
|
||||
{
|
||||
const struct rt1718s_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data_port = config->gpio_port_dev->data;
|
||||
uint8_t reg_int8, reg_mask8;
|
||||
|
||||
k_sem_take(&data_port->lock, K_FOREVER);
|
||||
|
||||
/* Get mask and state of GPIO interrupts */
|
||||
if (rt1718s_reg_read_byte(dev, RT1718S_REG_RT_INT8, ®_int8) ||
|
||||
rt1718s_reg_read_byte(dev, RT1718S_REG_RT_MASK8, ®_mask8)) {
|
||||
k_sem_give(&data_port->lock);
|
||||
LOG_ERR("i2c access failed");
|
||||
return;
|
||||
}
|
||||
|
||||
reg_int8 &= reg_mask8;
|
||||
/* Clear the interrupts */
|
||||
if (reg_int8) {
|
||||
if (rt1718s_reg_write_byte(dev, RT1718S_REG_RT_INT8, reg_int8)) {
|
||||
k_sem_give(&data_port->lock);
|
||||
LOG_ERR("i2c access failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
k_sem_give(&data_port->lock);
|
||||
|
||||
if (reg_int8 & RT1718S_GPIO_INT_MASK)
|
||||
/* Call the GPIO callbacks for rising *or* falling edge */
|
||||
gpio_fire_callbacks(&data_port->cb_list_gpio, config->gpio_port_dev,
|
||||
(reg_int8 & 0x7) | ((reg_int8 >> 4) & 0x7));
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api gpio_rt1718s_driver = {
|
||||
.pin_configure = gpio_rt1718s_pin_config,
|
||||
.port_get_raw = gpio_rt1718s_port_get_raw,
|
||||
.port_set_masked_raw = gpio_rt1718s_port_set_masked_raw,
|
||||
.port_set_bits_raw = gpio_rt1718s_port_set_bits_raw,
|
||||
.port_clear_bits_raw = gpio_rt1718s_port_clear_bits_raw,
|
||||
.port_toggle_bits = gpio_rt1718s_port_toggle_bits,
|
||||
.pin_interrupt_configure = gpio_rt1718s_pin_interrupt_configure,
|
||||
.manage_callback = gpio_rt1718s_manage_callback,
|
||||
};
|
||||
|
||||
static int gpio_rt1718s_port_init(const struct device *dev)
|
||||
{
|
||||
const struct gpio_rt1718s_port_config *const config = dev->config;
|
||||
struct gpio_rt1718s_port_data *const data = dev->data;
|
||||
|
||||
if (!device_is_ready(config->rt1718s_dev)) {
|
||||
LOG_ERR("%s is not ready", config->rt1718s_dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RT1718S GPIO port driver must be initialized after RT1718S chip driver */
|
||||
BUILD_ASSERT(CONFIG_GPIO_RT1718S_PORT_INIT_PRIORITY > CONFIG_RT1718S_INIT_PRIORITY);
|
||||
|
||||
#define GPIO_RT1718S_PORT_DEVICE_INSTANCE(inst) \
|
||||
static const struct gpio_rt1718s_port_config gpio_rt1718s_port_cfg_##inst = { \
|
||||
.common = {.port_pin_mask = 0x7}, \
|
||||
.rt1718s_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
}; \
|
||||
static struct gpio_rt1718s_port_data gpio_rt1718s_port_data_##inst; \
|
||||
DEVICE_DT_INST_DEFINE(inst, gpio_rt1718s_port_init, NULL, &gpio_rt1718s_port_data_##inst, \
|
||||
&gpio_rt1718s_port_cfg_##inst, POST_KERNEL, \
|
||||
CONFIG_GPIO_RT1718S_PORT_INIT_PRIORITY, &gpio_rt1718s_driver);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_RT1718S_PORT_DEVICE_INSTANCE)
|
||||
28
dts/bindings/gpio/richtek,rt1718s-gpio-port.yaml
Normal file
28
dts/bindings/gpio/richtek,rt1718s-gpio-port.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Copyright 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Richtek RT1718S TCPC chip GPIO port
|
||||
|
||||
"richtek,rt1718s-gpio-port" node handles GPIO feature of the RT1718S TCPC
|
||||
chip.
|
||||
|
||||
Example:
|
||||
&i2c2_0 {
|
||||
rt1718s_port0: rt1718s@40 {
|
||||
compatible = "richtek,rt1718s";
|
||||
reg = <0x40>;
|
||||
|
||||
rt1718s_gpio_port0: rt1718s_gpio {
|
||||
compatible = "richtek,rt1718s-gpio-port";
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
include: [gpio-controller.yaml, base.yaml]
|
||||
|
||||
compatible: "richtek,rt1718s-gpio-port"
|
||||
36
dts/bindings/gpio/richtek,rt1718s.yaml
Normal file
36
dts/bindings/gpio/richtek,rt1718s.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2022 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Richtek RT1718S TCPC chip
|
||||
|
||||
The Richtek RT1718S chip is TCPC, but also has 3 pins, which can be used as
|
||||
a usual GPIO. This node collects common proprties for RT1718S chip e.g. I2C
|
||||
address. Feature-specific(GPIO, TCPC) properties should be placed in a child
|
||||
node e.g. a number of GPIOs.
|
||||
|
||||
Example:
|
||||
&i2c2_0 {
|
||||
rt1718s_port0: rt1718s@40 {
|
||||
compatible = "richtek,rt1718s";
|
||||
reg = <0x40>;
|
||||
irq-gpios = <&gpioe 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
|
||||
rt1718s_gpio_port0: rt1718s_gpio {
|
||||
compatible = "richtek,rt1718s-gpio-port";
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "richtek,rt1718s"
|
||||
|
||||
include: [i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
irq-gpios:
|
||||
type: phandle-array
|
||||
description: Interrupt GPIO pin connected from the chip(IRQB)
|
||||
Loading…
Reference in a new issue