mfd: Add NCT38xx multi-function driver
The Nuvoton NCT38xx is a multi-function device providing a TCPC controller and a I/O expander (GPIO driver). Add a multi-function driver to manage exclusive access to the device. Tested with "twister -T tests/drivers/build_all/gpio". Signed-off-by: Keith Short <keithshort@google.com>
This commit is contained in:
parent
617c7cb337
commit
ea40f3af24
|
@ -7,7 +7,8 @@ config GPIO_NCT38XX
|
|||
bool "NCT38XX I2C-based GPIO chip"
|
||||
default y
|
||||
depends on DT_HAS_NUVOTON_NCT38XX_GPIO_PORT_ENABLED
|
||||
depends on I2C
|
||||
select I2C
|
||||
select MFD
|
||||
help
|
||||
Enable driver for NCT38XX I2C-based GPIO chip.
|
||||
|
||||
|
@ -15,17 +16,17 @@ if GPIO_NCT38XX
|
|||
|
||||
config GPIO_NCT38XX_INIT_PRIORITY
|
||||
int "NCT38XX GPIO init priority"
|
||||
default 51
|
||||
default 62
|
||||
help
|
||||
Device driver initialization priority. The priority should be lower
|
||||
than I2C device.
|
||||
NCT38xx GPIO driver initialization priority. The priority must be lower
|
||||
than MFD_INIT_PRIORITY.
|
||||
|
||||
config GPIO_NCT38XX_PORT_INIT_PRIORITY
|
||||
int "NCT38XX GPIO port init priority"
|
||||
default 52
|
||||
default 64
|
||||
help
|
||||
Device driver initialization priority. The priority should be lower
|
||||
than I2C & GPIO_NCT38XX_INIT_PRIORITY device.
|
||||
NCT38xx GPIO port device driver initialization priority. The priority
|
||||
must be lower than GPIO_NCT38XX_INIT_PRIORITY device.
|
||||
|
||||
config GPIO_NCT38XX_ALERT
|
||||
bool "NCT38XX GPIO interrupt"
|
||||
|
@ -36,7 +37,7 @@ config GPIO_NCT38XX_ALERT
|
|||
|
||||
config GPIO_NCT38XX_ALERT_INIT_PRIORITY
|
||||
int "NCT38XX GPIO alert handler init priority"
|
||||
default 52
|
||||
default 64
|
||||
depends on GPIO_NCT38XX_ALERT
|
||||
help
|
||||
NCT38XX alert handler initialization priority. This initialization
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_nct38xx.h>
|
||||
#include <zephyr/drivers/mfd/nct38xx.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/util_macro.h>
|
||||
|
||||
|
@ -17,6 +18,25 @@
|
|||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
/* Driver config */
|
||||
struct gpio_nct38xx_config {
|
||||
/* Multi-function device, parent to the NCT38xx GPIO controller */
|
||||
const struct device *mfd;
|
||||
/* GPIO ports */
|
||||
const struct device **sub_gpio_dev;
|
||||
uint8_t sub_gpio_port_num;
|
||||
/* Alert handler */
|
||||
const struct device *alert_dev;
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct gpio_nct38xx_data {
|
||||
/* NCT38XX device */
|
||||
const struct device *dev;
|
||||
/* lock NCT38xx register access */
|
||||
struct k_sem *lock;
|
||||
};
|
||||
|
||||
void nct38xx_gpio_alert_handler(const struct device *dev)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config = dev->config;
|
||||
|
@ -29,11 +49,16 @@ void nct38xx_gpio_alert_handler(const struct device *dev)
|
|||
static int nct38xx_init_interrupt(const struct device *dev)
|
||||
{
|
||||
uint16_t alert, alert_mask = 0;
|
||||
int ret = 0;
|
||||
struct gpio_nct38xx_data *data = dev->data;
|
||||
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
/* Disable all interrupt */
|
||||
if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
|
||||
sizeof(alert_mask))) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Enable vendor-defined alert for GPIO. */
|
||||
|
@ -41,34 +66,42 @@ static int nct38xx_init_interrupt(const struct device *dev)
|
|||
|
||||
/* Clear alert */
|
||||
if (nct38xx_reg_burst_read(dev, NCT38XX_REG_ALERT, (uint8_t *)&alert, sizeof(alert))) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
alert &= alert_mask;
|
||||
if (alert) {
|
||||
if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT, (uint8_t *)&alert,
|
||||
sizeof(alert))) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (nct38xx_reg_burst_write(dev, NCT38XX_REG_ALERT_MASK, (uint8_t *)&alert_mask,
|
||||
sizeof(alert_mask))) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nct38xx_gpio_init(const struct device *dev)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config = dev->config;
|
||||
struct gpio_nct38xx_data *data = dev->data;
|
||||
|
||||
/* Check I2C is ready */
|
||||
if (!device_is_ready(config->i2c_dev.bus)) {
|
||||
LOG_ERR("%s device not ready", config->i2c_dev.bus->name);
|
||||
/* Verify multi-function parent is ready */
|
||||
if (!device_is_ready(config->mfd)) {
|
||||
LOG_ERR("%s device not ready", config->mfd->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->lock = mfd_nct38xx_get_lock_reference(config->mfd);
|
||||
|
||||
if (IS_ENABLED(CONFIG_GPIO_NCT38XX_ALERT)) {
|
||||
nct38xx_init_interrupt(dev);
|
||||
}
|
||||
|
@ -81,7 +114,7 @@ static int nct38xx_gpio_init(const struct device *dev)
|
|||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, DEVICE_DT_GET, (,)) \
|
||||
}; \
|
||||
static const struct gpio_nct38xx_config gpio_nct38xx_cfg_##inst = { \
|
||||
.i2c_dev = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
.sub_gpio_dev = sub_gpio_dev_##inst, \
|
||||
.sub_gpio_port_num = ARRAY_SIZE(sub_gpio_dev_##inst), \
|
||||
}; \
|
||||
|
@ -93,3 +126,6 @@ static int nct38xx_gpio_init(const struct device *dev)
|
|||
CONFIG_GPIO_NCT38XX_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_NCT38XX_DEVICE_INSTANCE)
|
||||
|
||||
/* The nct38xx MFD parent must be initialized before this driver */
|
||||
BUILD_ASSERT(CONFIG_GPIO_NCT38XX_INIT_PRIORITY > CONFIG_MFD_INIT_PRIORITY);
|
||||
|
|
|
@ -31,110 +31,6 @@
|
|||
#define NCT38XX_REG_ALERT_VENDOR_DEFINDED_ALERT 15
|
||||
#define NCT38XX_REG_ALERT_MASK_VENDOR_DEFINDED_ALERT 15
|
||||
|
||||
/* Driver config */
|
||||
struct gpio_nct38xx_config {
|
||||
/* I2C device */
|
||||
const struct i2c_dt_spec i2c_dev;
|
||||
/* GPIO ports */
|
||||
const struct device **sub_gpio_dev;
|
||||
uint8_t sub_gpio_port_num;
|
||||
/* Alert handler */
|
||||
const struct device *alert_dev;
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct gpio_nct38xx_data {
|
||||
/* NCT38XX device */
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Read a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX 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 nct38xx_reg_read_byte(const struct device *dev, uint8_t reg_addr, uint8_t *val)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config =
|
||||
(const struct gpio_nct38xx_config *)dev->config;
|
||||
return i2c_reg_read_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a sequence of NCT38XX registers
|
||||
*
|
||||
* @param dev NCT38XX 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 nct38xx_reg_burst_read(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config =
|
||||
(const struct gpio_nct38xx_config *)dev->config;
|
||||
return i2c_burst_read_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX device
|
||||
* @param reg_addr Register address
|
||||
* @param val Data to write
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
static inline int nct38xx_reg_write_byte(const struct device *dev, uint8_t reg_addr, uint8_t val)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config =
|
||||
(const struct gpio_nct38xx_config *)dev->config;
|
||||
return i2c_reg_write_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a sequence of NCT38XX registers
|
||||
*
|
||||
* @param dev NCT38XX 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 nct38xx_reg_burst_write(const struct device *dev, uint8_t start_addr,
|
||||
uint8_t *buf, uint32_t num_bytes)
|
||||
{
|
||||
const struct gpio_nct38xx_config *const config =
|
||||
(const struct gpio_nct38xx_config *)dev->config;
|
||||
return i2c_burst_write_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare data & write a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX 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 nct38xx_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 nct38xx_reg_write_byte(dev, reg_addr, new_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dispatch GPIO port ISR
|
||||
*
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_nct38xx.h>
|
||||
#include <zephyr/drivers/mfd/nct38xx.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/util_macro.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include "gpio_nct38xx.h"
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/mfd/nct38xx.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
|
@ -18,7 +18,7 @@ struct gpio_nct38xx_port_config {
|
|||
/* gpio_driver_config needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
/* NCT38XX controller dev */
|
||||
const struct device *nct38xx_dev;
|
||||
const struct device *mfd;
|
||||
/* GPIO port index */
|
||||
uint8_t gpio_port;
|
||||
/* GPIO port 0 pinmux mask */
|
||||
|
@ -31,8 +31,8 @@ struct gpio_nct38xx_port_data {
|
|||
struct gpio_driver_data common;
|
||||
/* GPIO callback list */
|
||||
sys_slist_t cb_list_gpio;
|
||||
/* lock GPIO register access */
|
||||
struct k_sem lock;
|
||||
/* lock NCT38xx register access */
|
||||
struct k_sem *lock;
|
||||
};
|
||||
|
||||
/* GPIO api functions */
|
||||
|
@ -59,11 +59,11 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
/* Pin multiplexing */
|
||||
if (config->gpio_port == 0) {
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_MUX_CONTROL, ®);
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_MUX_CONTROL, ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -72,8 +72,7 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
/* NCT3807 bit3 must be set to 0 */
|
||||
new_reg &= config->pinmux_mask;
|
||||
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_MUX_CONTROL, reg,
|
||||
new_reg);
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_MUX_CONTROL, reg, new_reg);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -81,20 +80,20 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
|
||||
/* Configure pin as input. */
|
||||
if (flags & GPIO_INPUT) {
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DIR(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg & ~mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_DIR(config->gpio_port), reg, new_reg);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Select open drain 0:push-pull 1:open-drain */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_GPIO_OD_SEL(config->gpio_port),
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_GPIO_OD_SEL(config->gpio_port),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -104,14 +103,14 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
} else {
|
||||
new_reg = reg & ~mask;
|
||||
}
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_OD_SEL(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_OD_SEL(config->gpio_port),
|
||||
reg, new_reg);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Set level 0:low 1:high */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
|
@ -122,7 +121,7 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
|
||||
new_reg = reg & ~mask;
|
||||
}
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
reg, new_reg);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -130,18 +129,18 @@ static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpi
|
|||
|
||||
/* Configure pin as output, if requested 0:input 1:output */
|
||||
if (flags & GPIO_OUTPUT) {
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DIR(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg | mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_DIR(config->gpio_port), reg, new_reg);
|
||||
}
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -154,7 +153,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
uint8_t reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
if (config->gpio_port == 0) {
|
||||
if (mask & (~config->common.port_pin_mask)) {
|
||||
|
@ -162,7 +161,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
goto done;
|
||||
}
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_MUX_CONTROL, ®);
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_MUX_CONTROL, ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -173,7 +172,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
}
|
||||
}
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port),
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_GPIO_DIR(config->gpio_port),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -184,7 +183,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
*flags = GPIO_OUTPUT;
|
||||
|
||||
/* 0 - push-pull, 1 - open-drain */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_OD_SEL(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -195,7 +194,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
}
|
||||
|
||||
/* Output value */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -212,7 +211,7 @@ int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_f
|
|||
}
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_GPIO_GET_CONFIG */
|
||||
|
@ -223,12 +222,12 @@ static int gpio_nct38xx_port_get_raw(const struct device *dev, gpio_port_value_t
|
|||
const struct gpio_nct38xx_port_config *const config = dev->config;
|
||||
struct gpio_nct38xx_port_data *const data = dev->data;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_IN(config->gpio_port), (uint8_t *)value);
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -240,19 +239,19 @@ static int gpio_nct38xx_port_set_masked_raw(const struct device *dev, gpio_port_
|
|||
uint8_t reg, new_reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = ((reg & ~mask) | (value & mask));
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
reg, new_reg);
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -264,19 +263,19 @@ static int gpio_nct38xx_port_set_bits_raw(const struct device *dev, gpio_port_pi
|
|||
uint8_t reg, new_reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg | mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
reg, new_reg);
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -288,19 +287,19 @@ static int gpio_nct38xx_port_clear_bits_raw(const struct device *dev, gpio_port_
|
|||
uint8_t reg, new_reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg & ~mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
reg, new_reg);
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -312,19 +311,19 @@ static int gpio_nct38xx_port_toggle_bits(const struct device *dev, gpio_port_pin
|
|||
uint8_t reg, new_reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), ®);
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg ^ mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
ret = nct38xx_reg_update(config->mfd, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
|
||||
reg, new_reg);
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -338,16 +337,16 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
int ret;
|
||||
uint32_t mask = BIT(pin);
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
/* Disable irq before configuring them */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg & ~mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), reg, new_reg);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -359,12 +358,12 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
}
|
||||
|
||||
/* set edge register */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_RISE(config->gpio_port), &rise);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_FALL(config->gpio_port), &fall);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -390,13 +389,13 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
new_fall = fall & ~mask;
|
||||
}
|
||||
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_RISE(config->gpio_port), rise, new_rise);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_FALL(config->gpio_port), fall, new_fall);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -404,7 +403,7 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
|
||||
if (mode == GPIO_INT_MODE_LEVEL) {
|
||||
/* set active high/low */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_LEVEL(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -419,7 +418,7 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_LEVEL(config->gpio_port), reg,
|
||||
new_reg);
|
||||
|
||||
|
@ -429,24 +428,24 @@ static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_p
|
|||
}
|
||||
|
||||
/* Clear pending bit */
|
||||
ret = nct38xx_reg_write_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_write_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port), mask);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Enable it after configuration is completed */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), ®);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
new_reg = reg | mask;
|
||||
ret = nct38xx_reg_update(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_update(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), reg, new_reg);
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -468,12 +467,12 @@ static int gpio_nct38xx_port_get_direction(const struct device *dev, gpio_port_p
|
|||
uint8_t dir_reg;
|
||||
int ret;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
|
||||
if (config->gpio_port == 0) {
|
||||
uint8_t enabled_gpios;
|
||||
/* Remove the disabled GPIOs from the mask */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_MUX_CONTROL,
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_MUX_CONTROL,
|
||||
&enabled_gpios);
|
||||
mask &= (enabled_gpios & config->common.port_pin_mask);
|
||||
|
||||
|
@ -483,7 +482,7 @@ static int gpio_nct38xx_port_get_direction(const struct device *dev, gpio_port_p
|
|||
}
|
||||
|
||||
/* Read direction register, 0 - input, 1 - output */
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port),
|
||||
ret = nct38xx_reg_read_byte(config->mfd, NCT38XX_REG_GPIO_DIR(config->gpio_port),
|
||||
&dir_reg);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
|
@ -498,7 +497,7 @@ static int gpio_nct38xx_port_get_direction(const struct device *dev, gpio_port_p
|
|||
}
|
||||
|
||||
done:
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_GPIO_GET_DIRECTION */
|
||||
|
@ -511,32 +510,32 @@ int gpio_nct38xx_dispatch_port_isr(const struct device *dev)
|
|||
int ret;
|
||||
|
||||
do {
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
k_sem_take(data->lock, K_FOREVER);
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port),
|
||||
&alert_pins);
|
||||
if (ret < 0) {
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nct38xx_reg_read_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_read_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), &mask);
|
||||
if (ret < 0) {
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
alert_pins &= mask;
|
||||
if (alert_pins) {
|
||||
ret = nct38xx_reg_write_byte(config->nct38xx_dev,
|
||||
ret = nct38xx_reg_write_byte(config->mfd,
|
||||
NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port),
|
||||
alert_pins);
|
||||
if (ret < 0) {
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
k_sem_give(&data->lock);
|
||||
k_sem_give(data->lock);
|
||||
|
||||
gpio_fire_callbacks(&data->cb_list_gpio, dev, alert_pins);
|
||||
|
||||
|
@ -572,12 +571,12 @@ static int gpio_nct38xx_port_init(const struct device *dev)
|
|||
const struct gpio_nct38xx_port_config *const config = dev->config;
|
||||
struct gpio_nct38xx_port_data *const data = dev->data;
|
||||
|
||||
if (!device_is_ready(config->nct38xx_dev)) {
|
||||
LOG_ERR("%s is not ready", config->nct38xx_dev->name);
|
||||
if (!device_is_ready(config->mfd)) {
|
||||
LOG_ERR("%s is not ready", config->mfd->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
data->lock = mfd_nct38xx_get_lock_reference(config->mfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -589,7 +588,7 @@ BUILD_ASSERT(CONFIG_GPIO_NCT38XX_PORT_INIT_PRIORITY > CONFIG_GPIO_NCT38XX_INIT_P
|
|||
static const struct gpio_nct38xx_port_config gpio_nct38xx_port_cfg_##inst = { \
|
||||
.common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst) & \
|
||||
DT_INST_PROP(inst, pin_mask)}, \
|
||||
.nct38xx_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
.mfd = DEVICE_DT_GET(DT_INST_GPARENT(inst)), \
|
||||
.gpio_port = DT_INST_REG_ADDR(inst), \
|
||||
.pinmux_mask = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pinmux_mask), \
|
||||
(DT_INST_PROP(inst, pinmux_mask)), (0)), \
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NCT38XX mfd_nct38xx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NPM1300 mfd_npm1300.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NPM6001 mfd_npm6001.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_AXP192 mfd_axp192.c)
|
||||
|
|
|
@ -19,6 +19,7 @@ config MFD_INIT_PRIORITY
|
|||
Multi-function devices initialization priority.
|
||||
|
||||
source "drivers/mfd/Kconfig.axp192"
|
||||
source "drivers/mfd/Kconfig.nct38xx"
|
||||
source "drivers/mfd/Kconfig.npm1300"
|
||||
source "drivers/mfd/Kconfig.npm6001"
|
||||
|
||||
|
|
10
drivers/mfd/Kconfig.nct38xx
Normal file
10
drivers/mfd/Kconfig.nct38xx
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Copyright (c) 2023 Google, LLC
|
||||
# SPDX -License-Identifier: Apache-2.0
|
||||
|
||||
config MFD_NCT38XX
|
||||
bool "Nuvton NCT38xx multi-function device driver"
|
||||
default y
|
||||
depends on DT_HAS_NUVOTON_NCT38XX_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable the Nuvoton NCT38xx TCPC multi-function device driver.
|
88
drivers/mfd/mfd_nct38xx.c
Normal file
88
drivers/mfd/mfd_nct38xx.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Google, LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nuvoton_nct38xx
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
|
||||
struct mfd_nct38xx_config {
|
||||
const struct i2c_dt_spec i2c_dev;
|
||||
};
|
||||
|
||||
struct mfd_nct38xx_data {
|
||||
/* lock NC38xx register access */
|
||||
struct k_sem lock;
|
||||
};
|
||||
|
||||
static int mfd_nct38xx_init(const struct device *dev)
|
||||
{
|
||||
const struct mfd_nct38xx_config *config = dev->config;
|
||||
struct mfd_nct38xx_data *data = dev->data;
|
||||
|
||||
if (!device_is_ready(config->i2c_dev.bus)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct k_sem *mfd_nct38xx_get_lock_reference(const struct device *dev)
|
||||
{
|
||||
struct mfd_nct38xx_data *data = dev->data;
|
||||
|
||||
return &data->lock;
|
||||
}
|
||||
|
||||
int nct38xx_reg_read_byte(const struct device *dev, uint8_t reg_addr, uint8_t *val)
|
||||
{
|
||||
const struct mfd_nct38xx_config *const config = dev->config;
|
||||
|
||||
return i2c_reg_read_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
int nct38xx_reg_burst_read(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes)
|
||||
{
|
||||
const struct mfd_nct38xx_config *const config = dev->config;
|
||||
|
||||
return i2c_burst_read_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
int nct38xx_reg_write_byte(const struct device *dev, uint8_t reg_addr, uint8_t val)
|
||||
{
|
||||
const struct mfd_nct38xx_config *const config = dev->config;
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->i2c_dev, reg_addr, val);
|
||||
}
|
||||
|
||||
int nct38xx_reg_burst_write(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes)
|
||||
{
|
||||
const struct mfd_nct38xx_config *const config = dev->config;
|
||||
|
||||
return i2c_burst_write_dt(&config->i2c_dev, start_addr, buf, num_bytes);
|
||||
}
|
||||
|
||||
int nct38xx_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 nct38xx_reg_write_byte(dev, reg_addr, new_val);
|
||||
}
|
||||
|
||||
#define MFD_NCT38XX_DEFINE(inst) \
|
||||
static struct mfd_nct38xx_data nct38xx_data_##inst; \
|
||||
static const struct mfd_nct38xx_config nct38xx_cfg_##inst = { \
|
||||
.i2c_dev = I2C_DT_SPEC_INST_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, mfd_nct38xx_init, NULL, &nct38xx_data_##inst, \
|
||||
&nct38xx_cfg_##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MFD_NCT38XX_DEFINE)
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Nuvoton NCT38XX series I2C-based GPIO expander alert handler
|
||||
Nuvoton NCT38XX series I2C-based GPIO expander alert handler.
|
||||
|
||||
Example:
|
||||
nct3807_alert_1 {
|
||||
|
@ -25,4 +25,4 @@ properties:
|
|||
type: phandles
|
||||
required: true
|
||||
description: |
|
||||
NCT38XX devices which alert are handled by this alert handler.
|
||||
List of NCT38XX multi-function devices managed by this alert handler.
|
||||
|
|
|
@ -4,69 +4,83 @@
|
|||
description: |
|
||||
Nuvoton NCT38XX series I2C-based GPIO expander
|
||||
|
||||
This must be a child of the NCT38xx multi-function device.
|
||||
|
||||
Example:
|
||||
&i2c0_0 {
|
||||
nct3807@70 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x70>;
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
pinmux_mask = <0xf7>;
|
||||
};
|
||||
nct3807-gpio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@1 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
pinmux_mask = <0xf7>;
|
||||
};
|
||||
|
||||
gpio@1 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nct3808_0_P1@71 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x71>;
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
nct3808-0-p1-gpio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nct3808_0_P2@75 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x75>;
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
nct3808-0-P2-gpio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "nuvoton,nct38xx-gpio"
|
||||
|
||||
include: [i2c-device.yaml]
|
||||
include: [base.yaml]
|
||||
|
|
14
dts/bindings/mfd/nuvoton,nct38xx.yaml
Normal file
14
dts/bindings/mfd/nuvoton,nct38xx.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2023, Google, LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Nuvoton NCT38xx multi-function device
|
||||
|
||||
The NCT38xx provides a TCPC and an I/O Expander capabilities.
|
||||
|
||||
The TCPC and I/O expander drivers are added to the devicetree
|
||||
as children of the nuvoton,nct38xx device.
|
||||
|
||||
compatible: "nuvoton,nct38xx"
|
||||
|
||||
include: i2c-device.yaml
|
91
include/zephyr/drivers/mfd/nct38xx.h
Normal file
91
include/zephyr/drivers/mfd/nct38xx.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Google, LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_NCT38XX_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MFD_NCT38XX_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the semaphore reference for a NCT38xx instance. Callers
|
||||
* should pass the return value to k_sem_take/k_sem_give
|
||||
*
|
||||
* @param[in] dev Pointer to device struct of the driver instance
|
||||
*
|
||||
* @return Address of the semaphore
|
||||
*/
|
||||
struct k_sem *mfd_nct38xx_get_lock_reference(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Read a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX multi-function device
|
||||
* @param reg_addr Register address
|
||||
* @param val A pointer to a buffer for the data to return
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
int nct38xx_reg_read_byte(const struct device *dev, uint8_t reg_addr, uint8_t *val);
|
||||
|
||||
/**
|
||||
* @brief Read a sequence of NCT38XX registers
|
||||
*
|
||||
* @param dev NCT38XX multi-function 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.
|
||||
*/
|
||||
int nct38xx_reg_burst_read(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes);
|
||||
|
||||
/**
|
||||
* @brief Write a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX device
|
||||
* @param reg_addr Register address
|
||||
* @param val Data to write
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
int nct38xx_reg_write_byte(const struct device *dev, uint8_t reg_addr, uint8_t val);
|
||||
|
||||
/**
|
||||
* @brief Write a sequence of NCT38XX registers
|
||||
*
|
||||
* @param dev NCT38XX 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.
|
||||
*/
|
||||
int nct38xx_reg_burst_write(const struct device *dev, uint8_t start_addr, uint8_t *buf,
|
||||
uint32_t num_bytes);
|
||||
|
||||
/**
|
||||
* @brief Compare data & write a NCT38XX register
|
||||
*
|
||||
* @param dev NCT38XX device
|
||||
* @param reg_addr Register address
|
||||
* @param reg_val Old register data
|
||||
* @param new_val New register data
|
||||
*
|
||||
* @return 0 if successful, otherwise failed.
|
||||
*/
|
||||
int nct38xx_reg_update(const struct device *dev, uint8_t reg_addr, uint8_t reg_val,
|
||||
uint8_t new_val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_NCT38XX_H_ */
|
|
@ -103,63 +103,72 @@
|
|||
gpio-controller;
|
||||
};
|
||||
|
||||
test_i2c_nct3807: nct3807@72 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
mfd-nct38xx@72 {
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x72>;
|
||||
test_i2c_nct3807: nct3807 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
pinmux_mask = <0xf7>;
|
||||
};
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
pinmux_mask = <0xf7>;
|
||||
};
|
||||
|
||||
gpio@1 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
gpio@1 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x1>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_i2c_nct3808_p1: nct3808_0_P1@71 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
test_i2c_nct3808_p1: mfd-nct38xx@71 {
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x71>;
|
||||
nct3808_0_P1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_i2c_nct3808_p2: nct3808_0_P2@75 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
test_i2c_nct3808_p2: mfd-nct38xx@75 {
|
||||
compatible = "nuvoton,nct38xx";
|
||||
reg = <0x75>;
|
||||
nct3808_0_P2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "nuvoton,nct38xx-gpio";
|
||||
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
gpio@0 {
|
||||
compatible = "nuvoton,nct38xx-gpio-port";
|
||||
reg = <0x0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <8>;
|
||||
pin_mask = <0xdc>;
|
||||
pinmux_mask = <0xff>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue