drivers: gpio: add initial support for cy8c95xx I/O expander
add initial support for cy8c95xx I/O expander, no interrupt support currently. Signed-off-by: Watson Zeng <zhiwei@synopsys.com>
This commit is contained in:
parent
64c4c74ee8
commit
8c8afa82b9
6 changed files with 382 additions and 0 deletions
|
@ -38,6 +38,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_PSOC6 gpio_psoc6.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_GPIO_PCAL6408A gpio_pcal6408a.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_EOS_S3 gpio_eos_s3.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_RCAR gpio_rcar.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_CY8C95XX gpio_cy8c95xx.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
||||
|
||||
|
|
|
@ -91,4 +91,6 @@ source "drivers/gpio/Kconfig.eos_s3"
|
|||
|
||||
source "drivers/gpio/Kconfig.rcar"
|
||||
|
||||
source "drivers/gpio/Kconfig.cy8c95xx"
|
||||
|
||||
endif # GPIO
|
||||
|
|
24
drivers/gpio/Kconfig.cy8c95xx
Normal file
24
drivers/gpio/Kconfig.cy8c95xx
Normal file
|
@ -0,0 +1,24 @@
|
|||
# CY8C95XX GPIO configuration options
|
||||
|
||||
# Copyright (c) 2021 Synopsys
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Workaround for not being able to have commas in macro arguments
|
||||
DT_COMPAT_CYPRESS_CY8C95XX := cypress,cy8c95xx-gpio
|
||||
|
||||
menuconfig GPIO_CY8C95XX
|
||||
bool "CY8C95XX I2C GPIO chip"
|
||||
default $(dt_compat_enabled,$(DT_COMPAT_CYPRESS_CY8C95XX))
|
||||
depends on I2C
|
||||
help
|
||||
Enable driver for CY8C95XX I2C GPIO chip.
|
||||
|
||||
if GPIO_CY8C95XX
|
||||
|
||||
config GPIO_CY8C95XX_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 70
|
||||
help
|
||||
Device driver initialization priority.
|
||||
|
||||
endif # GPIO_CY8C95XX
|
314
drivers/gpio/gpio_cy8c95xx.c
Normal file
314
drivers/gpio/gpio_cy8c95xx.c
Normal file
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Synopsys
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT cypress_cy8c95xx_gpio_port
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <kernel.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(cy8c95xx, CONFIG_GPIO_LOG_LEVEL);
|
||||
|
||||
#include "gpio_utils.h"
|
||||
|
||||
/** Cache of the output configuration and data of the pins. */
|
||||
struct cy8c95xx_pin_state {
|
||||
uint8_t dir;
|
||||
uint8_t data_out;
|
||||
uint8_t pull_up;
|
||||
uint8_t pull_down;
|
||||
};
|
||||
|
||||
/** Runtime driver data */
|
||||
struct cy8c95xx_drv_data {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_data common;
|
||||
struct cy8c95xx_pin_state pin_state;
|
||||
struct k_sem *lock;
|
||||
};
|
||||
|
||||
/** Configuration data */
|
||||
struct cy8c95xx_config {
|
||||
/* gpio_driver_config needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
const struct device *i2c_master;
|
||||
uint16_t i2c_slave_addr;
|
||||
uint8_t port_num;
|
||||
};
|
||||
|
||||
#define CY8C95XX_REG_INPUT_DATA0 0x00
|
||||
#define CY8C95XX_REG_OUTPUT_DATA0 0x08
|
||||
#define CY8C95XX_REG_PORT_SELECT 0x18
|
||||
#define CY8C95XX_REG_DIR 0x1C
|
||||
#define CY8C95XX_REG_PULL_UP 0x1D
|
||||
#define CY8C95XX_REG_PULL_DOWN 0x1E
|
||||
#define CY8C95XX_REG_ID 0x2E
|
||||
|
||||
static int write_pin_state(const struct cy8c95xx_config *cfg,
|
||||
struct cy8c95xx_drv_data *drv_data,
|
||||
struct cy8c95xx_pin_state *pins)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_OUTPUT_DATA0 + cfg->port_num, pins->data_out);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_PORT_SELECT, cfg->port_num);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_DIR, pins->dir);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_PULL_UP, pins->pull_up);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_PULL_DOWN, pins->pull_down);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cy8c95xx_config(const struct device *dev,
|
||||
gpio_pin_t pin,
|
||||
gpio_flags_t flags)
|
||||
{
|
||||
const struct cy8c95xx_config *cfg = dev->config;
|
||||
struct cy8c95xx_drv_data *drv_data = dev->data;
|
||||
struct cy8c95xx_pin_state *pins = &drv_data->pin_state;
|
||||
int rc = 0;
|
||||
uint32_t io_flags;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
/* Strengths not implemented */
|
||||
if ((flags & (GPIO_DS_ALT_LOW | GPIO_DS_ALT_HIGH)) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Open-drain not implemented */
|
||||
if ((flags & GPIO_SINGLE_ENDED) != 0U) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
WRITE_BIT(pins->pull_up, pin, (flags & GPIO_PULL_UP) != 0U);
|
||||
WRITE_BIT(pins->pull_down, pin, (flags & GPIO_PULL_DOWN) != 0U);
|
||||
|
||||
/* Disconnected pin not implemented*/
|
||||
io_flags = flags & (GPIO_INPUT | GPIO_OUTPUT);
|
||||
if (io_flags == GPIO_DISCONNECTED) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_take(drv_data->lock, K_FOREVER);
|
||||
|
||||
if ((flags & GPIO_OUTPUT) != 0) {
|
||||
pins->dir &= ~BIT(pin);
|
||||
if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
|
||||
pins->data_out &= ~BIT(pin);
|
||||
} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
|
||||
pins->data_out |= BIT(pin);
|
||||
}
|
||||
} else {
|
||||
pins->dir |= BIT(pin);
|
||||
}
|
||||
|
||||
LOG_DBG("CFG %u %x : DIR %04x ; DAT %04x",
|
||||
pin, flags, pins->dir, pins->data_out);
|
||||
|
||||
rc = write_pin_state(cfg, drv_data, pins);
|
||||
|
||||
k_sem_give(drv_data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int port_get(const struct device *dev,
|
||||
gpio_port_value_t *value)
|
||||
{
|
||||
const struct cy8c95xx_config *cfg = dev->config;
|
||||
struct cy8c95xx_drv_data *drv_data = dev->data;
|
||||
uint8_t pin_data = 0;
|
||||
int rc = 0;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(drv_data->lock, K_FOREVER);
|
||||
|
||||
rc = i2c_reg_read_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_INPUT_DATA0 + cfg->port_num, &pin_data);
|
||||
|
||||
if (rc == 0) {
|
||||
*value = pin_data;
|
||||
}
|
||||
|
||||
k_sem_give(drv_data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int port_write(const struct device *dev,
|
||||
gpio_port_pins_t mask,
|
||||
gpio_port_value_t value,
|
||||
gpio_port_value_t toggle)
|
||||
{
|
||||
const struct cy8c95xx_config *cfg = dev->config;
|
||||
struct cy8c95xx_drv_data *drv_data = dev->data;
|
||||
uint8_t *outp = &drv_data->pin_state.data_out;
|
||||
uint8_t out = ((*outp & ~mask) | (value & mask)) ^ toggle;
|
||||
|
||||
/* Can't do I2C bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(drv_data->lock, K_FOREVER);
|
||||
|
||||
int rc = i2c_reg_write_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_OUTPUT_DATA0 + cfg->port_num, out);
|
||||
|
||||
if (rc == 0) {
|
||||
*outp = out;
|
||||
}
|
||||
k_sem_give(drv_data->lock);
|
||||
|
||||
LOG_DBG("write msk %04x val %04x tog %04x => %04x: %d",
|
||||
mask, value, toggle, out, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int port_set_masked(const struct device *dev,
|
||||
gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
return port_write(dev, mask, value, 0);
|
||||
}
|
||||
|
||||
static int port_set_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
return port_write(dev, pins, pins, 0);
|
||||
}
|
||||
|
||||
static int port_clear_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
return port_write(dev, pins, 0, 0);
|
||||
}
|
||||
|
||||
static int port_toggle_bits(const struct device *dev,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
return port_write(dev, 0, 0, pins);
|
||||
}
|
||||
|
||||
static int pin_interrupt_configure(const struct device *dev,
|
||||
gpio_pin_t pin,
|
||||
enum gpio_int_mode mode,
|
||||
enum gpio_int_trig trig)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialization function of CY8C95XX
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @return 0 if successful, failed otherwise.
|
||||
*/
|
||||
static int cy8c95xx_init(const struct device *dev)
|
||||
{
|
||||
const struct cy8c95xx_config *cfg = dev->config;
|
||||
struct cy8c95xx_drv_data *drv_data = dev->data;
|
||||
int rc;
|
||||
uint8_t data = 0;
|
||||
|
||||
k_sem_take(drv_data->lock, K_FOREVER);
|
||||
|
||||
if (!device_is_ready(cfg->i2c_master)) {
|
||||
LOG_ERR("%s is not ready", cfg->i2c_master->name);
|
||||
rc = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i2c_reg_read_byte(cfg->i2c_master, cfg->i2c_slave_addr,
|
||||
CY8C95XX_REG_ID, &data);
|
||||
LOG_DBG("cy8c95xx device ID %02X", data & 0xF0);
|
||||
if ((data & 0xF0) != 0x20) {
|
||||
LOG_WRN("driver only support [0-2] ports operations");
|
||||
}
|
||||
|
||||
/* Reset state mediated by initial configuration */
|
||||
drv_data->pin_state = (struct cy8c95xx_pin_state) {
|
||||
.dir = 0xFF,
|
||||
.data_out = 0xFF,
|
||||
.pull_up = 0xFF,
|
||||
.pull_down = 0x00,
|
||||
};
|
||||
rc = write_pin_state(cfg, drv_data, &drv_data->pin_state);
|
||||
out:
|
||||
if (rc != 0) {
|
||||
LOG_ERR("%s init failed: %d", dev->name, rc);
|
||||
} else {
|
||||
LOG_INF("%s init ok", dev->name);
|
||||
}
|
||||
k_sem_give(drv_data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api api_table = {
|
||||
.pin_configure = cy8c95xx_config,
|
||||
.port_get_raw = port_get,
|
||||
.port_set_masked_raw = port_set_masked,
|
||||
.port_set_bits_raw = port_set_bits,
|
||||
.port_clear_bits_raw = port_clear_bits,
|
||||
.port_toggle_bits = port_toggle_bits,
|
||||
.pin_interrupt_configure = pin_interrupt_configure,
|
||||
};
|
||||
|
||||
static struct k_sem cy8c95xx_lock = Z_SEM_INITIALIZER(cy8c95xx_lock, 1, 1);
|
||||
|
||||
#define GPIO_PORT_INIT(idx) \
|
||||
static const struct cy8c95xx_config cy8c95xx_##idx##_cfg = { \
|
||||
.common = { \
|
||||
.port_pin_mask = 0xFF, \
|
||||
}, \
|
||||
.i2c_master = DEVICE_DT_GET(DT_BUS(DT_INST(0, cypress_cy8c95xx_gpio))), \
|
||||
.i2c_slave_addr = DT_REG_ADDR_BY_IDX(DT_INST(0, cypress_cy8c95xx_gpio), 0), \
|
||||
.port_num = DT_INST_REG_ADDR(idx), \
|
||||
}; \
|
||||
static struct cy8c95xx_drv_data cy8c95xx_##idx##_drvdata = { \
|
||||
.lock = &cy8c95xx_lock, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(idx, cy8c95xx_init, device_pm_control_nop, \
|
||||
&cy8c95xx_##idx##_drvdata, &cy8c95xx_##idx##_cfg, \
|
||||
POST_KERNEL, CONFIG_GPIO_CY8C95XX_INIT_PRIORITY, \
|
||||
&api_table);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_PORT_INIT)
|
26
dts/bindings/gpio/cypress,cy8c95xx-gpio-port.yaml
Normal file
26
dts/bindings/gpio/cypress,cy8c95xx-gpio-port.yaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2021 Synopsys
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Cypress cy8c95xx GPIO port node
|
||||
|
||||
compatible: "cypress,cy8c95xx-gpio-port"
|
||||
|
||||
include: [gpio-controller.yaml, base.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
label:
|
||||
required: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
ngpios:
|
||||
required: true
|
||||
const: 8
|
||||
|
||||
gpio-cells:
|
||||
- pin
|
||||
- flags
|
15
dts/bindings/gpio/cypress,cy8c95xx-gpio.yaml
Normal file
15
dts/bindings/gpio/cypress,cy8c95xx-gpio.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2021 Synopsys
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Cypress cy8c95xx GPIO node
|
||||
|
||||
compatible: "cypress,cy8c95xx-gpio"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
label:
|
||||
required: true
|
Loading…
Add table
Add a link
Reference in a new issue