drivers: gpio: Add support for Awinic AW9523B GPIO controller
Add support for GPIO controller feature of AW9523B. Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com> Co-authored-by: Benjamin Cabé <kartben@gmail.com>
This commit is contained in:
parent
62f725a0ab
commit
0161dd0938
12 changed files with 691 additions and 1 deletions
|
|
@ -11,6 +11,7 @@ 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)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_ANDES_ATCGPIO100 gpio_andes_atcgpio100.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_AW9523B gpio_aw9523b.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_AXP192 gpio_axp192.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_BCM2711 gpio_bcm2711.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_BD8LB600FS gpio_bd8lb600fs.c)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ source "drivers/gpio/Kconfig.ads114s0x"
|
|||
source "drivers/gpio/Kconfig.altera"
|
||||
source "drivers/gpio/Kconfig.ambiq"
|
||||
source "drivers/gpio/Kconfig.andes_atcgpio100"
|
||||
source "drivers/gpio/Kconfig.aw9523b"
|
||||
source "drivers/gpio/Kconfig.axp192"
|
||||
source "drivers/gpio/Kconfig.b91"
|
||||
source "drivers/gpio/Kconfig.bcm2711"
|
||||
|
|
|
|||
11
drivers/gpio/Kconfig.aw9523b
Normal file
11
drivers/gpio/Kconfig.aw9523b
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) 2024 TOKITA Hiroshi
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config GPIO_AW9523B
|
||||
bool "AW9523B GPIO driver"
|
||||
default y
|
||||
depends on DT_HAS_AWINIC_AW9523B_GPIO_ENABLED
|
||||
select I2C
|
||||
select MFD
|
||||
help
|
||||
Enable the AW9523B GPIO controller.
|
||||
505
drivers/gpio/gpio_aw9523b.c
Normal file
505
drivers/gpio/gpio_aw9523b.c
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
* Copyright (c) 2024 TOKITA Hiroshi
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT awinic_aw9523b_gpio
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/mfd/aw9523b.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(gpio_aw9523b, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
#define AW9523B_GPOMD BIT(4)
|
||||
#define AW9523B_RESET_PULSE_WIDTH 20
|
||||
#define AW9523B_REG_CONFIG(n) (AW9523B_REG_CONFIG0 + n)
|
||||
#define AW9523B_REG_INT(n) (AW9523B_REG_INT0 + n)
|
||||
#define AW9523B_REG_OUTPUT(n) (AW9523B_REG_OUTPUT0 + n)
|
||||
|
||||
enum read_write_toggle_t {
|
||||
READ,
|
||||
WRITE,
|
||||
TOGGLE,
|
||||
};
|
||||
|
||||
struct gpio_aw9523b_config {
|
||||
struct gpio_driver_config common;
|
||||
const struct device *mfd_dev;
|
||||
struct i2c_dt_spec i2c;
|
||||
bool port0_push_pull;
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
struct gpio_dt_spec reset_gpio;
|
||||
#endif
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
struct gpio_dt_spec int_gpio;
|
||||
gpio_callback_handler_t int_cb;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct gpio_aw9523b_data {
|
||||
struct gpio_driver_data common;
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
const struct device *dev;
|
||||
sys_slist_t callbacks;
|
||||
struct gpio_callback gpio_callback;
|
||||
struct k_work intr_worker;
|
||||
gpio_port_value_t prev_value;
|
||||
gpio_port_pins_t rising_event_pins;
|
||||
gpio_port_pins_t falling_event_pins;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int gpio_aw9523b_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
|
||||
{
|
||||
const struct gpio_aw9523b_config *const config = dev->config;
|
||||
const uint8_t port = (pin < 8) ? 0 : 1;
|
||||
const uint8_t mask = BIT(pin % 8);
|
||||
const uint8_t input_en = (flags & GPIO_INPUT) ? mask : 0x00;
|
||||
const uint8_t out_high = (flags & GPIO_OUTPUT_INIT_HIGH) ? mask : 0x00;
|
||||
int err;
|
||||
|
||||
/* Can't do I2C operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
/* Either INPUT or OUTPUT must be set */
|
||||
if ((!(flags & GPIO_INPUT) && !(flags & GPIO_OUTPUT)) ||
|
||||
((flags & GPIO_INPUT) && (flags & GPIO_OUTPUT))) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Open-drain support is per port, not per pin.
|
||||
* So can't really support the API as-is.
|
||||
*/
|
||||
if (port == 0 && !config->port0_push_pull) {
|
||||
if (!((flags & GPIO_SINGLE_ENDED) && (flags & GPIO_LINE_OPEN_DRAIN))) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else {
|
||||
if (flags & GPIO_SINGLE_ENDED) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & GPIO_INPUT) && ((flags & GPIO_PULL_UP) || (flags & GPIO_PULL_DOWN))) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_take(aw9523b_get_lock(config->mfd_dev), K_FOREVER);
|
||||
|
||||
err = i2c_reg_update_byte_dt(&config->i2c, AW9523B_REG_CONFIG(port), mask, input_en);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to set pin%d direction (%d)", dev->name, pin, err);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
if (config->int_gpio.port) {
|
||||
if (input_en) {
|
||||
struct gpio_aw9523b_data *const data = dev->data;
|
||||
uint8_t buf[2];
|
||||
|
||||
/* Read initial pin state */
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT0, buf, sizeof(buf));
|
||||
if (err) {
|
||||
LOG_ERR("%s: Read initial pin state failed (%d)", dev->name, err);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
WRITE_BIT(data->prev_value, pin, sys_get_le16(buf) & BIT(pin));
|
||||
} else {
|
||||
struct gpio_aw9523b_data *const data = dev->data;
|
||||
|
||||
WRITE_BIT(data->falling_event_pins, pin, 0);
|
||||
WRITE_BIT(data->rising_event_pins, pin, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = i2c_reg_update_byte_dt(&config->i2c, AW9523B_REG_OUTPUT(port), mask, out_high);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to set initial pin state (%d)", dev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
on_error:
|
||||
k_sem_give(aw9523b_get_lock(config->mfd_dev));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common implementation of Read, Write, and Toggle
|
||||
*
|
||||
* @param[in] dev Specify device instance.
|
||||
* @param[in] mask Register mask to select pins to operate.
|
||||
* @param[in,out] value When mode is READ, this param is pointer to result value storing region.
|
||||
* When mode is WRITE, this param is used as input value.
|
||||
* When mode is TOGGLE, this param will ignored.
|
||||
* @param[in] mode Choose mode from READ, WRITE or TOGGLE.
|
||||
*/
|
||||
static int gpio_aw9523b_port_read_write_toggle(const struct device *dev, gpio_port_pins_t mask,
|
||||
gpio_port_value_t *value,
|
||||
enum read_write_toggle_t mode)
|
||||
{
|
||||
const struct gpio_aw9523b_config *const config = dev->config;
|
||||
uint8_t buf[2];
|
||||
gpio_port_value_t old_value;
|
||||
gpio_port_value_t new_value;
|
||||
int err;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(aw9523b_get_lock(config->mfd_dev), K_FOREVER);
|
||||
|
||||
/*
|
||||
* As with interrupts, the INPUT values are read for each address
|
||||
* to keep the internal state correct.
|
||||
*/
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT0, &buf[0], 1);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to read port0 status (%d)", dev->name, err);
|
||||
goto end;
|
||||
}
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT1, &buf[1], 1);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to read port1 status (%d)", dev->name, err);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (mode == READ) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
old_value = sys_get_le16(buf);
|
||||
|
||||
if (mode == WRITE) {
|
||||
new_value = (old_value & ~mask) | (*value & mask);
|
||||
} else {
|
||||
new_value = (old_value & ~mask) | (~old_value & mask);
|
||||
}
|
||||
|
||||
if (new_value == old_value) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
*(uint16_t *)buf = sys_get_le16((uint8_t *)&new_value);
|
||||
err = i2c_burst_write_dt(&config->i2c, AW9523B_REG_OUTPUT0, buf, sizeof(buf));
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to set port (%d)", dev->name, err);
|
||||
}
|
||||
|
||||
end:
|
||||
k_sem_give(aw9523b_get_lock(config->mfd_dev));
|
||||
|
||||
if (err == 0 && mode == READ) {
|
||||
*value = sys_get_le16(buf);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gpio_aw9523b_port_get_raw(const struct device *dev, gpio_port_value_t *value)
|
||||
{
|
||||
return gpio_aw9523b_port_read_write_toggle(dev, UINT16_MAX, value, READ);
|
||||
}
|
||||
|
||||
static int gpio_aw9523b_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
return gpio_aw9523b_port_read_write_toggle(dev, mask, &value, WRITE);
|
||||
}
|
||||
|
||||
static int gpio_aw9523b_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
return gpio_aw9523b_port_read_write_toggle(dev, pins, &pins, WRITE);
|
||||
}
|
||||
|
||||
static int gpio_aw9523b_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
const gpio_port_value_t zero = 0;
|
||||
|
||||
/* If WRITE is specified, this pointer is used for reading only. It can cast. */
|
||||
return gpio_aw9523b_port_read_write_toggle(dev, pins, (gpio_port_value_t *)&zero, WRITE);
|
||||
}
|
||||
|
||||
static int gpio_aw9523b_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
|
||||
{
|
||||
return gpio_aw9523b_port_read_write_toggle(dev, pins, NULL, TOGGLE);
|
||||
}
|
||||
|
||||
static __maybe_unused void gpio_aw9523b_interrupt_worker(struct k_work *work)
|
||||
{
|
||||
struct gpio_aw9523b_data *const data =
|
||||
CONTAINER_OF(work, struct gpio_aw9523b_data, intr_worker);
|
||||
const struct gpio_aw9523b_config *const config = data->dev->config;
|
||||
gpio_port_value_t value, rising, falling;
|
||||
uint8_t buf[2];
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We need to read INPUT0 to deassert INTN when that is asserted by
|
||||
* pin0-7 interruption, and same also INPUT1 for pin8-15.
|
||||
* It cannot deassert by burst-read.
|
||||
*/
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT0, &buf[0], 1);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to read INPUT0 %d", data->dev->name, err);
|
||||
}
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT1, &buf[1], 1);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to read INPUT1 %d", data->dev->name, err);
|
||||
}
|
||||
|
||||
value = sys_get_le16(buf);
|
||||
|
||||
rising = (value ^ data->prev_value) & (value & data->rising_event_pins);
|
||||
falling = (value ^ data->prev_value) & (~value & data->falling_event_pins);
|
||||
|
||||
data->prev_value = value;
|
||||
|
||||
gpio_fire_callbacks(&data->callbacks, data->dev, rising | falling);
|
||||
}
|
||||
|
||||
static __maybe_unused int gpio_aw9523b_pin_interrupt_configure(const struct device *dev,
|
||||
gpio_pin_t pin,
|
||||
enum gpio_int_mode mode,
|
||||
enum gpio_int_trig trig)
|
||||
{
|
||||
const struct gpio_aw9523b_config *const config = dev->config;
|
||||
struct gpio_aw9523b_data *const data = dev->data;
|
||||
const uint8_t port = (pin < 8) ? 0 : 1;
|
||||
const uint8_t mask = BIT(port ? pin - 8 : pin);
|
||||
const uint8_t n_int_en = (mode & GPIO_INT_MODE_EDGE) ? 0x00 : 0xFF;
|
||||
uint8_t buf[2];
|
||||
int err;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
if (mode == GPIO_INT_MODE_LEVEL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (data->common.invert & BIT(pin)) {
|
||||
WRITE_BIT(data->falling_event_pins, pin, trig & GPIO_INT_HIGH_1);
|
||||
WRITE_BIT(data->rising_event_pins, pin, trig & GPIO_INT_LOW_0);
|
||||
} else {
|
||||
WRITE_BIT(data->falling_event_pins, pin, trig & GPIO_INT_LOW_0);
|
||||
WRITE_BIT(data->rising_event_pins, pin, trig & GPIO_INT_HIGH_1);
|
||||
}
|
||||
|
||||
k_sem_take(aw9523b_get_lock(config->mfd_dev), K_FOREVER);
|
||||
|
||||
err = i2c_reg_update_byte_dt(&config->i2c, AW9523B_REG_INT(port), mask, n_int_en);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to configure pin interruption (%d)", dev->name, err);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!n_int_en) {
|
||||
/* Read initial pin state */
|
||||
err = i2c_burst_read_dt(&config->i2c, AW9523B_REG_INPUT0, buf, sizeof(buf));
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to read initial pin state (%d)", dev->name, err);
|
||||
goto end;
|
||||
}
|
||||
|
||||
WRITE_BIT(data->prev_value, pin, sys_get_le16(buf) & BIT(pin));
|
||||
} else {
|
||||
WRITE_BIT(data->falling_event_pins, pin, 0);
|
||||
WRITE_BIT(data->rising_event_pins, pin, 0);
|
||||
}
|
||||
|
||||
end:
|
||||
k_sem_give(aw9523b_get_lock(config->mfd_dev));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __maybe_unused int gpio_aw9523b_manage_callback(const struct device *dev,
|
||||
struct gpio_callback *callback, bool set)
|
||||
{
|
||||
const struct gpio_aw9523b_config *const config = dev->config;
|
||||
struct gpio_aw9523b_data *const data = dev->data;
|
||||
int err;
|
||||
|
||||
k_sem_take(aw9523b_get_lock(config->mfd_dev), K_FOREVER);
|
||||
|
||||
err = gpio_manage_callback(&data->callbacks, callback, set);
|
||||
if (err) {
|
||||
LOG_ERR("%s: gpio_manage_callback failed (%d)", dev->name, err);
|
||||
}
|
||||
|
||||
k_sem_give(aw9523b_get_lock(config->mfd_dev));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __maybe_unused void gpio_aw9523b_int_handler(const struct device *gpio_dev,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
struct gpio_aw9523b_data *data = CONTAINER_OF(cb, struct gpio_aw9523b_data, gpio_callback);
|
||||
|
||||
k_work_submit(&data->intr_worker);
|
||||
}
|
||||
|
||||
static DEVICE_API(gpio, gpio_aw9523b_api) = {
|
||||
.pin_configure = gpio_aw9523b_pin_configure,
|
||||
.port_get_raw = gpio_aw9523b_port_get_raw,
|
||||
.port_set_masked_raw = gpio_aw9523b_port_set_masked_raw,
|
||||
.port_set_bits_raw = gpio_aw9523b_port_set_bits_raw,
|
||||
.port_clear_bits_raw = gpio_aw9523b_port_clear_bits_raw,
|
||||
.port_toggle_bits = gpio_aw9523b_port_toggle_bits,
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
.pin_interrupt_configure = gpio_aw9523b_pin_interrupt_configure,
|
||||
.manage_callback = gpio_aw9523b_manage_callback,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int gpio_aw9523b_init(const struct device *dev)
|
||||
{
|
||||
const struct gpio_aw9523b_config *const config = dev->config;
|
||||
const uint8_t int_init_data[] = {0xFF, 0xFF};
|
||||
uint8_t buf[2];
|
||||
int err;
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
|
||||
struct gpio_aw9523b_data *const data = dev->data;
|
||||
|
||||
if (!config->int_gpio.port) {
|
||||
goto end_init_int_gpio;
|
||||
}
|
||||
|
||||
/* Store self-reference for interrupt handling */
|
||||
data->dev = dev;
|
||||
|
||||
/* Prepare interrupt worker */
|
||||
k_work_init(&data->intr_worker, gpio_aw9523b_interrupt_worker);
|
||||
|
||||
if (!gpio_is_ready_dt(&config->int_gpio)) {
|
||||
LOG_ERR("%s: Interrupt GPIO not ready", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to configure interrupt pin %d (%d)", dev->name,
|
||||
config->int_gpio.pin, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to configure interrupt %d (%d)", dev->name,
|
||||
config->int_gpio.pin, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->gpio_callback, config->int_cb, BIT(config->int_gpio.pin));
|
||||
err = gpio_add_callback(config->int_gpio.port, &data->gpio_callback);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to add interrupt callback for pin %d (%d)", dev->name,
|
||||
config->int_gpio.pin, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
end_init_int_gpio:
|
||||
#endif
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
if (!config->reset_gpio.port) {
|
||||
goto end_hw_reset;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&config->reset_gpio)) {
|
||||
LOG_ERR("%s: Reset GPIO not ready", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_ACTIVE);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to configure reset pin %d (%d)", dev->name,
|
||||
config->reset_gpio.pin, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
k_busy_wait(AW9523B_RESET_PULSE_WIDTH);
|
||||
|
||||
err = gpio_pin_set_dt(&config->reset_gpio, 0);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to set 0 reset pin %d (%d)", dev->name, config->reset_gpio.pin,
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
end_hw_reset:
|
||||
#endif
|
||||
|
||||
if (!device_is_ready(config->i2c.bus)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(aw9523b_get_lock(config->mfd_dev), 1, 1);
|
||||
|
||||
/* Software reset */
|
||||
err = i2c_reg_read_byte_dt(&config->i2c, AW9523B_REG_SW_RSTN, buf);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to software reset (%d)", dev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Disabling all interrupts */
|
||||
err = i2c_burst_write_dt(&config->i2c, AW9523B_REG_INT0, int_init_data, sizeof(buf));
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to disable all interrupts (%d)", dev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!config->port0_push_pull) {
|
||||
/* Configure port0 to push-pull mode */
|
||||
err = i2c_reg_update_byte_dt(&config->i2c, AW9523B_REG_CTL, AW9523B_GPOMD, 0xFF);
|
||||
if (err) {
|
||||
LOG_ERR("%s: Failed to configure port0 to push-pull (%d)", dev->name, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GPIO_AW9523B_DEFINE(inst) \
|
||||
static struct gpio_aw9523b_data gpio_aw9523b_data##inst; \
|
||||
\
|
||||
static const struct gpio_aw9523b_config gpio_aw9523b_config##inst = { \
|
||||
.common = { \
|
||||
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
|
||||
}, \
|
||||
.mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \
|
||||
.port0_push_pull = DT_INST_PROP_OR(inst, port0_push_pull, false), \
|
||||
IF_ENABLED(DT_INST_PROP_HAS_IDX(inst, int_gpios, 0), ( \
|
||||
.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \
|
||||
.int_cb = gpio_aw9523b_int_handler, \
|
||||
)) \
|
||||
IF_ENABLED(DT_INST_PROP_HAS_IDX(inst, reset_gpios, 0), ( \
|
||||
.reset_gpio = GPIO_DT_SPEC_INST_GET(inst, reset_gpios), \
|
||||
)) \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, gpio_aw9523b_init, NULL, &gpio_aw9523b_data##inst, \
|
||||
&gpio_aw9523b_config##inst, POST_KERNEL, \
|
||||
CONFIG_MFD_INIT_PRIORITY, &gpio_aw9523b_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_AW9523B_DEFINE)
|
||||
|
|
@ -19,3 +19,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_BD8LB600FS mfd_bd8lb600fs.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801 mfd_ite_it8801.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801_ALTCTRL mfd_it8801_altctrl.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_AW9523B mfd_aw9523b.c)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ config MFD_INIT_PRIORITY
|
|||
source "drivers/mfd/Kconfig.ad559x"
|
||||
source "drivers/mfd/Kconfig.adp5585"
|
||||
source "drivers/mfd/Kconfig.axp192"
|
||||
source "drivers/mfd/Kconfig.aw9523b"
|
||||
source "drivers/mfd/Kconfig.bd8lb600fs"
|
||||
source "drivers/mfd/Kconfig.max20335"
|
||||
source "drivers/mfd/Kconfig.max31790"
|
||||
|
|
|
|||
10
drivers/mfd/Kconfig.aw9523b
Normal file
10
drivers/mfd/Kconfig.aw9523b
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Copyright (c) 2024 TOKITA Hiroshi
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config MFD_AW9523B
|
||||
bool "Awinic AW9523B GPIO/LED controller device driver"
|
||||
default y
|
||||
depends on DT_HAS_AWINIC_AW9523B_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable the Awinic AW9523B GPIO/LED controller device driver
|
||||
65
drivers/mfd/mfd_aw9523b.c
Normal file
65
drivers/mfd/mfd_aw9523b.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2024 TOKITA Hiroshi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT awinic_aw9523b
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/mfd/aw9523b.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define AW9523B_ID_VALUE 0x23
|
||||
|
||||
struct mfd_aw9523b_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
};
|
||||
|
||||
struct mfd_aw9523b_data {
|
||||
struct k_sem lock;
|
||||
};
|
||||
|
||||
static int mfd_aw9523b_init(const struct device *dev)
|
||||
{
|
||||
const struct mfd_aw9523b_config *config = dev->config;
|
||||
struct mfd_aw9523b_data *data = dev->data;
|
||||
uint8_t reg;
|
||||
int ret;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
|
||||
ret = i2c_reg_read_byte_dt(&config->i2c, AW9523B_REG_ID, ®);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
};
|
||||
|
||||
if (reg != AW9523B_ID_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct k_sem *aw9523b_get_lock(const struct device *dev)
|
||||
{
|
||||
struct mfd_aw9523b_data *data = dev->data;
|
||||
|
||||
return &data->lock;
|
||||
}
|
||||
|
||||
#define MFD_AW9523B_DEFINE(inst) \
|
||||
static const struct mfd_aw9523b_config config##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
static struct mfd_aw9523b_data data##inst; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, mfd_aw9523b_init, NULL, &data##inst, &config##inst, \
|
||||
POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MFD_AW9523B_DEFINE)
|
||||
36
dts/bindings/gpio/awinic,aw9523b-gpio.yaml
Normal file
36
dts/bindings/gpio/awinic,aw9523b-gpio.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright (c) 2024 TOKITA Hiroshi
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: AW9523B GPIO Controller
|
||||
|
||||
compatible: "awinic,aw9523b-gpio"
|
||||
|
||||
include: gpio-controller.yaml
|
||||
|
||||
on-bus: aw9523b
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Set GPIO connected to the controller INTN pin.
|
||||
This is used for notifying interruption.
|
||||
|
||||
reset-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Set GPIO connected to the controller RSTN pin.
|
||||
This is used for controlling resets via GPIO.
|
||||
|
||||
port0-push-pull:
|
||||
type: boolean
|
||||
description: |
|
||||
Configure Port0 to Push-Pull mode.
|
||||
Port0 is worked in Open-Drain mode by default.
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
gpio-cells:
|
||||
- pin
|
||||
- flags
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
STEMMA QT is a 1.0mm-JST-SH connector for I2C devices.
|
||||
STEMMA QT is a 4-pin JST-SH connector for I2C devices.
|
||||
The pin assignments are as follows.
|
||||
|
||||
0 SCL
|
||||
|
|
|
|||
14
dts/bindings/mfd/awinic,aw9523b.yaml
Normal file
14
dts/bindings/mfd/awinic,aw9523b.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2024 TOKITA Hiroshi
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Awinic AW9523B
|
||||
|
||||
compatible: "awinic,aw9523b"
|
||||
|
||||
include: i2c-device.yaml
|
||||
|
||||
bus: aw9523b
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
45
include/zephyr/drivers/mfd/aw9523b.h
Normal file
45
include/zephyr/drivers/mfd/aw9523b.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2024 TOKITA Hiroshi
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_AW9523B_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MFD_AW9523B_H_
|
||||
|
||||
struct k_sem;
|
||||
struct device;
|
||||
|
||||
#define AW9523B_REG_INPUT0 0x00
|
||||
#define AW9523B_REG_INPUT1 0x01
|
||||
#define AW9523B_REG_OUTPUT0 0x02
|
||||
#define AW9523B_REG_OUTPUT1 0x03
|
||||
#define AW9523B_REG_CONFIG0 0x04
|
||||
#define AW9523B_REG_CONFIG1 0x05
|
||||
#define AW9523B_REG_INT0 0x06
|
||||
#define AW9523B_REG_INT1 0x07
|
||||
#define AW9523B_REG_ID 0x10
|
||||
#define AW9523B_REG_CTL 0x11
|
||||
#define AW9523B_REG_MODE0 0x12
|
||||
#define AW9523B_REG_MODE1 0x13
|
||||
#define AW9523B_REG_DIM0 0x20
|
||||
#define AW9523B_REG_DIM1 0x21
|
||||
#define AW9523B_REG_DIM2 0x22
|
||||
#define AW9523B_REG_DIM3 0x23
|
||||
#define AW9523B_REG_DIM4 0x24
|
||||
#define AW9523B_REG_DIM5 0x25
|
||||
#define AW9523B_REG_DIM6 0x26
|
||||
#define AW9523B_REG_DIM7 0x27
|
||||
#define AW9523B_REG_DIM8 0x28
|
||||
#define AW9523B_REG_DIM9 0x29
|
||||
#define AW9523B_REG_DIM10 0x2A
|
||||
#define AW9523B_REG_DIM11 0x2B
|
||||
#define AW9523B_REG_DIM12 0x2C
|
||||
#define AW9523B_REG_DIM13 0x2D
|
||||
#define AW9523B_REG_DIM14 0x2E
|
||||
#define AW9523B_REG_DIM15 0x2F
|
||||
#define AW9523B_REG_SW_RSTN 0x7F
|
||||
|
||||
struct k_sem *aw9523b_get_lock(const struct device *dev);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_AW9523B_H_ */
|
||||
Loading…
Reference in a new issue