drivers: mfd: add new driver "mfd_adp5585"
Add mfd_adp5585 and gpio_adp5585 driver. This driver enables ADP5585 as an GPIO expander. This chip is used as an GPIO expander on i.MX93 EVK. GPIO pinctrl, read/write and interrupt is supported. Note that ADP5585 has 2 GPIO banks with 5 pins each. The driver combines two group into a 16-bit port. Index 0~4 correspond to R0~R4 lines, index 8~12 correspond to C0~C4 lines. Index 5~7 is reserved unavailable. Signed-off-by: Chekhov Ma <chekhov.ma@nxp.com>
This commit is contained in:
parent
ef7ddc07d3
commit
ad2745471c
12 changed files with 829 additions and 0 deletions
|
@ -6,6 +6,7 @@ zephyr_library()
|
|||
|
||||
# zephyr-keep-sorted-start
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_AD559X gpio_ad559x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_ADP5585 gpio_adp5585.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_ADS114S0X gpio_ads114s0x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_ALTERA_PIO gpio_altera_pio.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_AMBIQ gpio_ambiq.c)
|
||||
|
|
|
@ -95,6 +95,7 @@ config GPIO_ENABLE_DISABLE_INTERRUPT
|
|||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/gpio/Kconfig.ad559x"
|
||||
source "drivers/gpio/Kconfig.adp5585"
|
||||
source "drivers/gpio/Kconfig.ads114s0x"
|
||||
source "drivers/gpio/Kconfig.altera"
|
||||
source "drivers/gpio/Kconfig.ambiq"
|
||||
|
|
18
drivers/gpio/Kconfig.adp5585
Normal file
18
drivers/gpio/Kconfig.adp5585
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig GPIO_ADP5585
|
||||
bool "ADP5585_GPIO I2C GPIO chip"
|
||||
default y
|
||||
depends on DT_HAS_ADI_ADP5585_GPIO_ENABLED
|
||||
select MFD
|
||||
select I2C
|
||||
help
|
||||
Enable the ADP5585 GPIO driver.
|
||||
|
||||
config GPIO_ADP5585_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 70
|
||||
depends on GPIO_ADP5585
|
||||
help
|
||||
Device driver initialization priority.
|
469
drivers/gpio/gpio_adp5585.c
Normal file
469
drivers/gpio/gpio_adp5585.c
Normal file
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/drivers/mfd/adp5585.h>
|
||||
|
||||
#define DT_DRV_COMPAT adi_adp5585_gpio
|
||||
|
||||
LOG_MODULE_REGISTER(adp5585_gpio, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
#define ADP5585_BANK(offs) (offs >> 3)
|
||||
#define ADP5585_BIT(offs) (offs & GENMASK(2, 0))
|
||||
|
||||
enum adp5585_gpio_pin_direction {
|
||||
adp5585_pin_input = 0U,
|
||||
adp5585_pin_output,
|
||||
};
|
||||
|
||||
enum adp5585_gpio_pin_drive_mode {
|
||||
adp5585_pin_drive_pp = 0U,
|
||||
adp5585_pin_drive_od,
|
||||
};
|
||||
|
||||
enum adp5585_gpio_pull_config {
|
||||
adp5585_pull_up_300k = 0U,
|
||||
adp5585_pull_dn_300k,
|
||||
adp5585_pull_up_100k, /* not used */
|
||||
adp5585_pull_disable,
|
||||
};
|
||||
|
||||
enum adp5585_gpio_int_en {
|
||||
adp5585_int_disable = 0U,
|
||||
adp5585_int_enable,
|
||||
};
|
||||
|
||||
enum adp5585_gpio_int_level {
|
||||
adp5585_int_active_low = 0U,
|
||||
adp5585_int_active_high,
|
||||
};
|
||||
|
||||
/** Configuration data */
|
||||
struct adp5585_gpio_config {
|
||||
/* gpio_driver_config needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
const struct device *mfd_dev;
|
||||
const struct gpio_dt_spec gpio_int;
|
||||
};
|
||||
|
||||
/** Runtime driver data */
|
||||
struct adp5585_gpio_data {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_data common;
|
||||
uint16_t output;
|
||||
|
||||
sys_slist_t callbacks;
|
||||
};
|
||||
|
||||
static int gpio_adp5585_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
struct adp5585_gpio_data *data = dev->data;
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
|
||||
int ret = 0;
|
||||
uint8_t reg_value;
|
||||
|
||||
/* ADP5585 has non-contiguous gpio pin layouts, account for this */
|
||||
if ((pin & cfg->common.port_pin_mask) == 0) {
|
||||
LOG_ERR("pin %d is invalid for this device", pin);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint8_t bank = ADP5585_BANK(pin);
|
||||
uint8_t bank_pin = ADP5585_BIT(pin);
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
/* Simultaneous PU & PD mode not supported */
|
||||
if (((flags & GPIO_PULL_UP) != 0) && ((flags & GPIO_PULL_DOWN) != 0)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Simultaneous input & output mode not supported */
|
||||
if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
if ((flags & GPIO_SINGLE_ENDED) != 0) {
|
||||
reg_value = adp5585_pin_drive_od << bank_pin;
|
||||
} else {
|
||||
reg_value = adp5585_pin_drive_pp << bank_pin;
|
||||
}
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, ADP5585_GPO_OUT_MODE_A + bank,
|
||||
BIT(bank_pin), reg_value);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint8_t regaddr = ADP5585_RPULL_CONFIG_A + (bank << 1);
|
||||
uint8_t shift = bank_pin << 1;
|
||||
|
||||
if (bank_pin > 3U) {
|
||||
regaddr += 1U;
|
||||
shift = (bank_pin - 3U) << 1;
|
||||
}
|
||||
if ((flags & GPIO_PULL_UP) != 0) {
|
||||
reg_value = adp5585_pull_up_300k << shift;
|
||||
} else if ((flags & GPIO_PULL_DOWN) != 0) {
|
||||
reg_value = adp5585_pull_dn_300k << shift;
|
||||
} else {
|
||||
reg_value = adp5585_pull_disable << shift;
|
||||
}
|
||||
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, regaddr,
|
||||
0b11U << shift, reg_value);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ensure either Output or Input is specified */
|
||||
if ((flags & GPIO_OUTPUT) != 0) {
|
||||
|
||||
/* Set Low or High if specified */
|
||||
if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
|
||||
data->output &= ~BIT(pin);
|
||||
} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
|
||||
data->output |= BIT(pin);
|
||||
}
|
||||
if (bank == 0) {
|
||||
/* reg_value for ADP5585_GPO_OUT_MODE */
|
||||
reg_value = (uint8_t)data->output;
|
||||
} else {
|
||||
/* reg_value for ADP5585_GPO_OUT_MODE */
|
||||
reg_value = (uint8_t)(data->output >> 8);
|
||||
}
|
||||
ret = i2c_reg_write_byte_dt(&parent_cfg->i2c_bus,
|
||||
ADP5585_GPO_OUT_MODE_A + bank,
|
||||
reg_value);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
/* reg_value for ADP5585_GPIO_DIRECTION */
|
||||
reg_value = adp5585_pin_output << bank_pin;
|
||||
} else if ((flags & GPIO_INPUT) != 0) {
|
||||
/* reg_value for ADP5585_GPIO_DIRECTION */
|
||||
reg_value = adp5585_pin_output << bank_pin;
|
||||
}
|
||||
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
|
||||
ADP5585_GPIO_DIRECTION_A + bank,
|
||||
BIT(bank_pin), reg_value);
|
||||
|
||||
out:
|
||||
k_sem_give(&parent_data->lock);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("pin configure error: %d", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_read(const struct device *dev, gpio_port_value_t *value)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
/* struct adp5585_gpio_data *data = dev->data; */
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
|
||||
uint16_t input_data = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
/** Read Input Register */
|
||||
|
||||
uint8_t gpi_status_reg;
|
||||
uint8_t gpi_status_buf[2];
|
||||
|
||||
ret = i2c_write_read_dt(&parent_cfg->i2c_bus, &gpi_status_reg, 1U,
|
||||
gpi_status_buf, 2U);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
input_data = sys_le16_to_cpu(*((uint16_t *)gpi_status_buf));
|
||||
*value = input_data;
|
||||
|
||||
out:
|
||||
k_sem_give(&parent_data->lock);
|
||||
LOG_DBG("read %x got %d", input_data, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_write(const struct device *dev, gpio_port_pins_t mask,
|
||||
gpio_port_value_t value, gpio_port_value_t toggle)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
struct adp5585_gpio_data *data = dev->data;
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
|
||||
uint16_t orig_out;
|
||||
uint16_t out;
|
||||
uint8_t reg_value;
|
||||
int ret;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
orig_out = data->output;
|
||||
out = ((orig_out & ~mask) | (value & mask)) ^ toggle;
|
||||
|
||||
reg_value = (uint8_t)out;
|
||||
uint8_t gpo_data_out_buf[] = { ADP5585_GPO_DATA_OUT_A,
|
||||
(uint8_t)out, (uint8_t)(out >> 8) };
|
||||
|
||||
ret = i2c_write_dt(&parent_cfg->i2c_bus, gpo_data_out_buf, sizeof(gpo_data_out_buf));
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->output = out;
|
||||
|
||||
out:
|
||||
k_sem_give(&parent_data->lock);
|
||||
LOG_DBG("write %x msk %08x val %08x => %x: %d", orig_out, mask, value, out, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_set_masked(const struct device *dev, gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
return gpio_adp5585_port_write(dev, mask, value, 0);
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_set_bits(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
return gpio_adp5585_port_write(dev, pins, pins, 0);
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_clear_bits(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
return gpio_adp5585_port_write(dev, pins, 0, 0);
|
||||
}
|
||||
|
||||
static int gpio_adp5585_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
return gpio_adp5585_port_write(dev, 0, 0, pins);
|
||||
}
|
||||
|
||||
static int gpio_adp5585_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
|
||||
enum gpio_int_mode mode, enum gpio_int_trig trig)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
/* struct adp5585_gpio_data *data = dev->data; */
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
int ret = 0;
|
||||
|
||||
if (parent_cfg->nint_gpio.port == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* ADP5585 has non-contiguous gpio pin layouts, account for this */
|
||||
if ((pin & cfg->common.port_pin_mask) == 0) {
|
||||
LOG_ERR("pin %d is invalid for this device", pin);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* This device supports only level-triggered interrupts. */
|
||||
/* This device does NOT support either-level interrupt. */
|
||||
if (mode == GPIO_INT_MODE_EDGE || trig == GPIO_INT_TRIG_BOTH) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
uint8_t bank = ADP5585_BANK(pin);
|
||||
uint8_t bank_pin = ADP5585_BIT(pin);
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
if (mode == GPIO_INT_MODE_DISABLED) {
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
|
||||
ADP5585_GPI_INTERRUPT_EN_A + bank, BIT(bank_pin),
|
||||
(adp5585_int_disable << bank_pin));
|
||||
} else if ((trig & GPIO_INT_TRIG_BOTH) != 0) {
|
||||
if (trig == GPIO_INT_TRIG_LOW) {
|
||||
ret = i2c_reg_update_byte_dt(
|
||||
&parent_cfg->i2c_bus, ADP5585_GPI_INT_LEVEL_A + bank,
|
||||
BIT(bank_pin), (adp5585_int_active_low << bank_pin));
|
||||
} else {
|
||||
ret = i2c_reg_update_byte_dt(
|
||||
&parent_cfg->i2c_bus, ADP5585_GPI_INT_LEVEL_A + bank,
|
||||
BIT(bank_pin), (adp5585_int_active_high << bank_pin));
|
||||
}
|
||||
|
||||
/* make sure GPI_n_EVENT_EN is disabled, otherwise it will generate FIFO event */
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
|
||||
ADP5585_GPI_EVENT_EN_A + bank, BIT(bank_pin), 0U);
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
|
||||
ADP5585_GPI_INTERRUPT_EN_A + bank,
|
||||
BIT(bank_pin), (adp5585_int_enable << bank_pin));
|
||||
}
|
||||
|
||||
k_sem_give(&parent_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_adp5585_manage_callback(const struct device *dev, struct gpio_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
struct adp5585_gpio_data *data = dev->data;
|
||||
|
||||
return gpio_manage_callback(&data->callbacks, callback, set);
|
||||
}
|
||||
|
||||
void gpio_adp5585_irq_handler(const struct device *dev)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
struct adp5585_gpio_data *data = dev->data;
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
|
||||
uint16_t reg_int_status;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
/* Read Input Register */
|
||||
ret = i2c_burst_read_dt(&parent_cfg->i2c_bus, ADP5585_GPI_INT_STAT_A,
|
||||
(uint8_t *)®_int_status, 2U);
|
||||
if (ret != 0) {
|
||||
LOG_WRN("%s failed to read interrupt status %d", dev->name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
k_sem_give(&parent_data->lock);
|
||||
|
||||
if (ret == 0 && reg_int_status != 0) {
|
||||
gpio_fire_callbacks(&data->callbacks, dev, reg_int_status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialization function of ADP5585_GPIO
|
||||
*
|
||||
* This sets initial input/ output configuration and output states.
|
||||
* The interrupt is configured if this is enabled.
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @return 0 if successful, failed otherwise.
|
||||
*/
|
||||
static int gpio_adp5585_init(const struct device *dev)
|
||||
{
|
||||
const struct adp5585_gpio_config *cfg = dev->config;
|
||||
struct adp5585_gpio_data *data = dev->data;
|
||||
const struct mfd_adp5585_config *parent_cfg =
|
||||
(struct mfd_adp5585_config *)(cfg->mfd_dev->config);
|
||||
struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
|
||||
int ret = 0;
|
||||
|
||||
if (!device_is_ready(cfg->mfd_dev)) {
|
||||
LOG_ERR("%s: parent dev not ready", dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!device_is_ready(parent_cfg->i2c_bus.bus)) {
|
||||
LOG_ERR("I2C bus device not found");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
k_sem_take(&parent_data->lock, K_FOREVER);
|
||||
|
||||
/** Read output register */
|
||||
uint8_t gpo_data_out_buf[] = { ADP5585_GPO_DATA_OUT_A,
|
||||
0x00, 0x00 };
|
||||
|
||||
ret = i2c_write_read_dt(&parent_cfg->i2c_bus, gpo_data_out_buf, 1U,
|
||||
gpo_data_out_buf + 1, 2U);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
data->output = sys_le16_to_cpu(*((uint16_t *)(gpo_data_out_buf + 1)));
|
||||
|
||||
/** Set RPULL to high-z by default */
|
||||
uint8_t rpull_config_buf[] = { ADP5585_RPULL_CONFIG_A,
|
||||
0xffU, 0x03U, 0xffU, 0x03U };
|
||||
|
||||
ret = i2c_write_dt(&parent_cfg->i2c_bus, rpull_config_buf, sizeof(rpull_config_buf));
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
parent_data->child.gpio_dev = dev;
|
||||
|
||||
/** Enable GPI interrupt */
|
||||
if ((ret == 0) && gpio_is_ready_dt(&parent_cfg->nint_gpio)) {
|
||||
ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, ADP5585_INT_EN, (1U << 1),
|
||||
(1U << 1));
|
||||
}
|
||||
|
||||
out:
|
||||
k_sem_give(&parent_data->lock);
|
||||
if (ret) {
|
||||
LOG_ERR("%s init failed: %d", dev->name, ret);
|
||||
} else {
|
||||
LOG_INF("%s init ok", dev->name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api api_table = {
|
||||
.pin_configure = gpio_adp5585_config,
|
||||
.port_get_raw = gpio_adp5585_port_read,
|
||||
.port_set_masked_raw = gpio_adp5585_port_set_masked,
|
||||
.port_set_bits_raw = gpio_adp5585_port_set_bits,
|
||||
.port_clear_bits_raw = gpio_adp5585_port_clear_bits,
|
||||
.port_toggle_bits = gpio_adp5585_port_toggle_bits,
|
||||
.pin_interrupt_configure = gpio_adp5585_pin_interrupt_configure,
|
||||
.manage_callback = gpio_adp5585_manage_callback,
|
||||
};
|
||||
|
||||
#define GPIO_ADP5585_INIT(inst) \
|
||||
static const struct adp5585_gpio_config adp5585_gpio_cfg_##inst = { \
|
||||
.common = { \
|
||||
.port_pin_mask = GPIO_DT_INST_PORT_PIN_MASK_NGPIOS_EXC( \
|
||||
inst, DT_INST_PROP(inst, ngpios)) \
|
||||
}, \
|
||||
.mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
}; \
|
||||
static struct adp5585_gpio_data adp5585_gpio_drvdata_##inst; \
|
||||
DEVICE_DT_INST_DEFINE(inst, gpio_adp5585_init, NULL, \
|
||||
&adp5585_gpio_drvdata_##inst, \
|
||||
&adp5585_gpio_cfg_##inst, POST_KERNEL, \
|
||||
CONFIG_GPIO_ADP5585_INIT_PRIORITY, &api_table);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_ADP5585_INIT)
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_ADP5585 mfd_adp5585.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_MAX20335 mfd_max20335.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NCT38XX mfd_nct38xx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NPM1300 mfd_npm1300.c)
|
||||
|
|
|
@ -19,6 +19,7 @@ config MFD_INIT_PRIORITY
|
|||
Multi-function devices initialization priority.
|
||||
|
||||
source "drivers/mfd/Kconfig.ad559x"
|
||||
source "drivers/mfd/Kconfig.adp5585"
|
||||
source "drivers/mfd/Kconfig.axp192"
|
||||
source "drivers/mfd/Kconfig.bd8lb600fs"
|
||||
source "drivers/mfd/Kconfig.max20335"
|
||||
|
|
17
drivers/mfd/Kconfig.adp5585
Normal file
17
drivers/mfd/Kconfig.adp5585
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig MFD_ADP5585
|
||||
bool "Analog ADP5585 I2C configurable GPIO/PWM/KeyScan chip"
|
||||
default y
|
||||
depends on DT_HAS_ADI_ADP5585_ENABLED
|
||||
depends on I2C
|
||||
help
|
||||
Enable driver for Analog ADP5585.
|
||||
|
||||
config MFD_ADP5585_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 65
|
||||
depends on MFD_ADP5585
|
||||
help
|
||||
Device driver initialization priority.
|
145
drivers/mfd/mfd_adp5585.c
Normal file
145
drivers/mfd/mfd_adp5585.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT adi_adp5585
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/drivers/mfd/adp5585.h>
|
||||
|
||||
LOG_MODULE_REGISTER(adp5585, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
static int mfd_adp5585_software_reset(const struct device *dev)
|
||||
{
|
||||
const struct mfd_adp5585_config *config = dev->config;
|
||||
int ret = 0;
|
||||
|
||||
/** Set CONFIG to gpio by default */
|
||||
uint8_t pin_config_buf[] = { ADP5585_PIN_CONFIG_A, 0x00U, 0x00U };
|
||||
|
||||
ret = i2c_write_dt(&config->i2c_bus, pin_config_buf, sizeof(pin_config_buf));
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
LOG_ERR("%s: software reset failed: %d", dev->name, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mfd_adp5585_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
struct mfd_adp5585_data *data = CONTAINER_OF(gpio_cb, struct mfd_adp5585_data, int_gpio_cb);
|
||||
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
|
||||
static void mfd_adp5585_work_handler(struct k_work *work)
|
||||
{
|
||||
struct mfd_adp5585_data *data = CONTAINER_OF(work, struct mfd_adp5585_data, work);
|
||||
const struct mfd_adp5585_config *config = data->dev->config;
|
||||
uint8_t reg_int_status;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->lock, K_FOREVER);
|
||||
/* Read Interrput Flag */
|
||||
if (ret == 0) {
|
||||
ret = i2c_reg_read_byte_dt(&config->i2c_bus, ADP5585_INT_STATUS, ®_int_status);
|
||||
}
|
||||
/* Clear Interrput Flag */
|
||||
if (ret == 0) {
|
||||
ret = i2c_reg_write_byte_dt(&config->i2c_bus, ADP5585_INT_STATUS, reg_int_status);
|
||||
}
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
#ifdef CONFIG_GPIO_ADP5585
|
||||
if ((reg_int_status & ADP5585_INT_GPI) && device_is_ready(data->child.gpio_dev)) {
|
||||
(void)gpio_adp5585_irq_handler(data->child.gpio_dev);
|
||||
}
|
||||
#endif /* CONFIG_GPIO_ADP5585 */
|
||||
}
|
||||
|
||||
static int mfd_adp5585_init(const struct device *dev)
|
||||
{
|
||||
const struct mfd_adp5585_config *config = dev->config;
|
||||
struct mfd_adp5585_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c_bus)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* reset gpio can be left float */
|
||||
if (gpio_is_ready_dt(&config->reset_gpio)) {
|
||||
ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
|
||||
if (ret) {
|
||||
LOG_ERR("%s: configure reset pin failed: %d", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
LOG_WRN("%s: reset pin not configured", dev->name);
|
||||
}
|
||||
|
||||
ret = mfd_adp5585_software_reset(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_is_ready_dt(&config->nint_gpio)) {
|
||||
ret = gpio_pin_configure_dt(&config->nint_gpio, GPIO_INPUT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = gpio_pin_interrupt_configure_dt(&config->nint_gpio, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("%s: failed to configure INT interrupt: %d", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->int_gpio_cb, mfd_adp5585_int_gpio_handler,
|
||||
BIT(config->nint_gpio.pin));
|
||||
ret = gpio_add_callback_dt(&config->nint_gpio, &data->int_gpio_cb);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("%s: failed to add INT callback: %d", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
LOG_WRN("%s: nint pin not configured", dev->name);
|
||||
}
|
||||
|
||||
LOG_DBG("%s: init ok\r\n", dev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MFD_ADP5585_DEFINE(inst) \
|
||||
static const struct mfd_adp5585_config mfd_adp5585_config_##inst = { \
|
||||
.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
|
||||
.nint_gpio = GPIO_DT_SPEC_INST_GET_OR(n, nint_gpios, {0}), \
|
||||
.i2c_bus = I2C_DT_SPEC_INST_GET(inst), \
|
||||
}; \
|
||||
static struct mfd_adp5585_data mfd_adp5585_data_##inst = { \
|
||||
.work = Z_WORK_INITIALIZER(mfd_adp5585_work_handler), \
|
||||
.lock = Z_SEM_INITIALIZER(mfd_adp5585_data_##inst.lock, 1, 1), \
|
||||
.dev = DEVICE_DT_INST_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, mfd_adp5585_init, NULL, &mfd_adp5585_data_##inst, \
|
||||
&mfd_adp5585_config_##inst, POST_KERNEL, \
|
||||
CONFIG_MFD_ADP5585_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MFD_ADP5585_DEFINE);
|
|
@ -10,6 +10,7 @@
|
|||
#include <zephyr/dt-bindings/clock/imx_ccm_rev2.h>
|
||||
#include <zephyr/dt-bindings/gpio/gpio.h>
|
||||
#include <zephyr/dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <zephyr/dt-bindings/gpio/gpio.h>
|
||||
#include <zephyr/dt-bindings/i2c/i2c.h>
|
||||
|
||||
/ {
|
||||
|
|
30
dts/bindings/gpio/adi,adp5585-gpio.yaml
Normal file
30
dts/bindings/gpio/adi,adp5585-gpio.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ADP5585 GPIO Controller
|
||||
|
||||
compatible: "adi,adp5585-gpio"
|
||||
|
||||
include: gpio-controller.yaml
|
||||
|
||||
properties:
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
ngpios:
|
||||
const: 13
|
||||
description: |
|
||||
Number of GPIOs available on port expander.
|
||||
|
||||
gpio-reserved-ranges:
|
||||
required: true
|
||||
const: [5, 3]
|
||||
description: |
|
||||
Ranges of GPIOs reserved unavailable on port expander.
|
||||
The ADP5585 has 10 GPIO lines divided in 2 groups. GPIO number
|
||||
5, 6, 7 is reserved. That's to say, GPIO R0~R4 occupy line
|
||||
number 0~4, GPIO C0~C4 occupy line number 8~12.
|
||||
|
||||
gpio-cells:
|
||||
- pin
|
||||
- flags
|
19
dts/bindings/mfd/adi,adp5585.yaml
Normal file
19
dts/bindings/mfd/adi,adp5585.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Analog ADP5585 GPIO/keypad/PWM chip
|
||||
|
||||
compatible: "adi,adp5585"
|
||||
|
||||
include: i2c-device.yaml
|
||||
|
||||
properties:
|
||||
reset-gpios:
|
||||
type: phandle-array
|
||||
description: RESET pin
|
||||
|
||||
nint-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Connection for the NINT signal. This signal is active-low when
|
||||
produced by adp5585 GPIO node.
|
126
include/zephyr/drivers/mfd/adp5585.h
Normal file
126
include/zephyr/drivers/mfd/adp5585.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_ADP5585_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MFD_ADP5585_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#define ADP5585_ID 0x00
|
||||
#define ADP5585_INT_STATUS 0x01
|
||||
#define ADP5585_STATUS 0x02
|
||||
#define ADP5585_FIFO_1 0x03
|
||||
#define ADP5585_FIFO_2 0x04
|
||||
#define ADP5585_FIFO_3 0x05
|
||||
#define ADP5585_FIFO_4 0x06
|
||||
#define ADP5585_FIFO_5 0x07
|
||||
#define ADP5585_FIFO_6 0x08
|
||||
#define ADP5585_FIFO_7 0x09
|
||||
#define ADP5585_FIFO_8 0x0A
|
||||
#define ADP5585_FIFO_9 0x0B
|
||||
#define ADP5585_FIFO_10 0x0C
|
||||
#define ADP5585_FIFO_11 0x0D
|
||||
#define ADP5585_FIFO_12 0x0E
|
||||
#define ADP5585_FIFO_13 0x0F
|
||||
#define ADP5585_FIFO_14 0x10
|
||||
#define ADP5585_FIFO_15 0x11
|
||||
#define ADP5585_FIFO_16 0x12
|
||||
#define ADP5585_GPI_INT_STAT_A 0x13
|
||||
#define ADP5585_GPI_INT_STAT_B 0x14
|
||||
#define ADP5585_GPI_STATUS_A 0x15
|
||||
#define ADP5585_GPI_STATUS_B 0x16
|
||||
#define ADP5585_RPULL_CONFIG_A 0x17
|
||||
#define ADP5585_RPULL_CONFIG_B 0x18
|
||||
#define ADP5585_RPULL_CONFIG_C 0x19
|
||||
#define ADP5585_RPULL_CONFIG_D 0x1A
|
||||
#define ADP5585_GPI_INT_LEVEL_A 0x1B
|
||||
#define ADP5585_GPI_INT_LEVEL_B 0x1C
|
||||
#define ADP5585_GPI_EVENT_EN_A 0x1D
|
||||
#define ADP5585_GPI_EVENT_EN_B 0x1E
|
||||
#define ADP5585_GPI_INTERRUPT_EN_A 0x1F
|
||||
#define ADP5585_GPI_INTERRUPT_EN_B 0x20
|
||||
#define ADP5585_DEBOUNCE_DIS_A 0x21
|
||||
#define ADP5585_DEBOUNCE_DIS_B 0x22
|
||||
#define ADP5585_GPO_DATA_OUT_A 0x23
|
||||
#define ADP5585_GPO_DATA_OUT_B 0x24
|
||||
#define ADP5585_GPO_OUT_MODE_A 0x25
|
||||
#define ADP5585_GPO_OUT_MODE_B 0x26
|
||||
#define ADP5585_GPIO_DIRECTION_A 0x27
|
||||
#define ADP5585_GPIO_DIRECTION_B 0x28
|
||||
#define ADP5585_RESET1_EVENT_A 0x29
|
||||
#define ADP5585_RESET1_EVENT_B 0x2A
|
||||
#define ADP5585_RESET1_EVENT_C 0x2B
|
||||
#define ADP5585_RESET2_EVENT_A 0x2C
|
||||
#define ADP5585_RESET2_EVENT_B 0x2D
|
||||
#define ADP5585_RESET_CFG 0x2E
|
||||
#define ADP5585_PWM_OFFT_LOW 0x2F
|
||||
#define ADP5585_PWM_OFFT_HIGH 0x30
|
||||
#define ADP5585_PWM_ONT_LOW 0x31
|
||||
#define ADP5585_PWM_ONT_HIGH 0x32
|
||||
#define ADP5585_PWM_CFG 0x33
|
||||
#define ADP5585_LOGIC_CFG 0x34
|
||||
#define ADP5585_LOGIC_FF_CFG 0x35
|
||||
#define ADP5585_LOGIC_INT_EVENT_EN 0x36
|
||||
#define ADP5585_POLL_PTIME_CFG 0x37
|
||||
#define ADP5585_PIN_CONFIG_A 0x38
|
||||
#define ADP5585_PIN_CONFIG_B 0x39
|
||||
#define ADP5585_PIN_CONFIG_C 0x3A
|
||||
#define ADP5585_GENERAL_CFG 0x3B
|
||||
#define ADP5585_INT_EN 0x3C
|
||||
|
||||
/* ID Register */
|
||||
#define ADP5585_DEVICE_ID_MASK 0xF
|
||||
#define ADP5585_MAN_ID_MASK 0xF
|
||||
#define ADP5585_MAN_ID_SHIFT 4
|
||||
#define ADP5585_MAN_ID 0x02
|
||||
|
||||
#define ADP5585_PWM_CFG_EN 0x1
|
||||
#define ADP5585_PWM_CFG_MODE 0x2
|
||||
#define ADP5585_PIN_CONFIG_R3_PWM 0x8
|
||||
#define ADP5585_PIN_CONFIG_R3_MASK 0xC
|
||||
#define ADP5585_GENERAL_CFG_OSC_EN 0x80
|
||||
|
||||
/* INT_EN and INT_STATUS Register */
|
||||
#define ADP5585_INT_EVENT (1U << 0)
|
||||
#define ADP5585_INT_GPI (1U << 1)
|
||||
#define ADP5585_INT_OVERFLOW (1U << 2)
|
||||
#define ADP5585_INT_LOGIC (1U << 4)
|
||||
|
||||
#define ADP5585_REG_MASK 0xFF
|
||||
|
||||
struct mfd_adp5585_config {
|
||||
struct gpio_dt_spec reset_gpio;
|
||||
struct gpio_dt_spec nint_gpio;
|
||||
struct i2c_dt_spec i2c_bus;
|
||||
};
|
||||
|
||||
struct mfd_adp5585_data {
|
||||
struct k_work work;
|
||||
struct k_sem lock;
|
||||
const struct device *dev;
|
||||
struct {
|
||||
#ifdef CONFIG_GPIO_ADP5585
|
||||
const struct device *gpio_dev;
|
||||
#endif /* CONFIG_GPIO_ADP5585 */
|
||||
} child;
|
||||
struct gpio_callback int_gpio_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Forward declaration of child device interrupt
|
||||
* handler
|
||||
*/
|
||||
#ifdef CONFIG_GPIO_ADP5585
|
||||
void gpio_adp5585_irq_handler(const struct device *dev);
|
||||
#endif /* CONFIG_GPIO_ADP5585 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_AD5952_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue