drivers: gpio: ti: add gpio extender sn74hc595
Add driver for spi based gpio extender ti sn74hc595. Signed-off-by: Matthias Freese <m.freese@web.de>
This commit is contained in:
parent
5043083a0f
commit
938eae3372
|
@ -56,6 +56,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NCT38XX_INTERRUPT gpio_nct38xx_alert.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_TEST gpio_test.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_TEST gpio_test.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_GD32 gpio_gd32.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_GD32 gpio_gd32.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_PS gpio_xlnx_ps.c gpio_xlnx_ps_bank.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_PS gpio_xlnx_ps.c gpio_xlnx_ps_bank.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_GPIO_SN74HC595 gpio_sn74hc595.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
||||||
|
|
||||||
|
|
|
@ -133,4 +133,6 @@ source "drivers/gpio/Kconfig.gd32"
|
||||||
|
|
||||||
source "drivers/gpio/Kconfig.xlnx_ps"
|
source "drivers/gpio/Kconfig.xlnx_ps"
|
||||||
|
|
||||||
|
source "drivers/gpio/Kconfig.sn74hc595"
|
||||||
|
|
||||||
endif # GPIO
|
endif # GPIO
|
||||||
|
|
18
drivers/gpio/Kconfig.sn74hc595
Normal file
18
drivers/gpio/Kconfig.sn74hc595
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Copyright (c) 2022 Matthias Freese
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config GPIO_SN74HC595
|
||||||
|
bool "SN74HC595 shift register as GPIO extender"
|
||||||
|
depends on SPI
|
||||||
|
help
|
||||||
|
Use SN74HC595 as GPIO extender
|
||||||
|
|
||||||
|
if GPIO_SN74HC595
|
||||||
|
|
||||||
|
config GPIO_SN74HC595_INIT_PRIORITY
|
||||||
|
int "Init priority"
|
||||||
|
default 71
|
||||||
|
help
|
||||||
|
Device driver initialization priority.
|
||||||
|
|
||||||
|
endif
|
211
drivers/gpio/gpio_sn74hc595.c
Normal file
211
drivers/gpio/gpio_sn74hc595.c
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Matthias Freese
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ti_sn74hc595
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file Driver for 74 HC shift register
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/drivers/spi.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/zephyr.h>
|
||||||
|
|
||||||
|
#include "gpio_utils.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(gpio_sn74hc595, CONFIG_GPIO_LOG_LEVEL);
|
||||||
|
|
||||||
|
#if CONFIG_SPI_INIT_PRIORITY >= CONFIG_GPIO_SN74HC595_INIT_PRIORITY
|
||||||
|
#error SPI_INIT_PRIORITY must be lower than SN74HC595_INIT_PRIORITY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gpio_sn74hc595_config {
|
||||||
|
/* gpio_driver_config needs to be first */
|
||||||
|
struct gpio_driver_config config;
|
||||||
|
|
||||||
|
struct spi_dt_spec bus;
|
||||||
|
struct gpio_dt_spec reset_gpio;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gpio_sn74hc595_drv_data {
|
||||||
|
/* gpio_driver_data needs to be first */
|
||||||
|
struct gpio_driver_data data;
|
||||||
|
|
||||||
|
struct k_mutex lock;
|
||||||
|
uint8_t output;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sn74hc595_spi_write(const struct device *dev, void *buf, size_t len_bytes)
|
||||||
|
{
|
||||||
|
const struct gpio_sn74hc595_config *config = dev->config;
|
||||||
|
|
||||||
|
__ASSERT(((buf != NULL) || (len_bytes == 0)), "no valid buffer given");
|
||||||
|
__ASSERT(!k_is_in_isr(), "attempt to access SPI from ISR");
|
||||||
|
|
||||||
|
struct spi_buf tx_buf[] = { { .buf = buf, .len = len_bytes } };
|
||||||
|
const struct spi_buf_set tx = { .buffers = tx_buf, .count = 1 };
|
||||||
|
|
||||||
|
return spi_write_dt(&config->bus, &tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
ARG_UNUSED(pin);
|
||||||
|
ARG_UNUSED(flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_port_get_raw(const struct device *dev, uint32_t *value)
|
||||||
|
{
|
||||||
|
struct gpio_sn74hc595_drv_data *drv_data = dev->data;
|
||||||
|
|
||||||
|
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||||
|
|
||||||
|
*value = drv_data->output;
|
||||||
|
|
||||||
|
k_mutex_unlock(&drv_data->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_port_set_masked_raw(const struct device *dev, uint32_t mask,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
struct gpio_sn74hc595_drv_data *drv_data = dev->data;
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t output;
|
||||||
|
|
||||||
|
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||||
|
|
||||||
|
/* check if we need to do something at all */
|
||||||
|
/* current output differs from new masked value */
|
||||||
|
if ((drv_data->output & mask) != (mask & value)) {
|
||||||
|
output = (drv_data->output & ~mask) | (mask & value);
|
||||||
|
|
||||||
|
ret = sn74hc595_spi_write(dev, &output, 1U);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_data->output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
k_mutex_unlock(&drv_data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_port_set_bits_raw(const struct device *dev, uint32_t mask)
|
||||||
|
{
|
||||||
|
return gpio_sn74hc595_port_set_masked_raw(dev, mask, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_port_clear_bits_raw(const struct device *dev, uint32_t mask)
|
||||||
|
{
|
||||||
|
return gpio_sn74hc595_port_set_masked_raw(dev, mask, 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_port_toggle_bits(const struct device *dev, uint32_t mask)
|
||||||
|
{
|
||||||
|
struct gpio_sn74hc595_drv_data *drv_data = dev->data;
|
||||||
|
int ret;
|
||||||
|
uint8_t toggled_output;
|
||||||
|
|
||||||
|
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||||
|
|
||||||
|
toggled_output = drv_data->output ^ mask;
|
||||||
|
|
||||||
|
ret = sn74hc595_spi_write(dev, &toggled_output, 1U);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_data->output ^= mask;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
k_mutex_unlock(&drv_data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sn74hc595_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
|
||||||
|
enum gpio_int_mode mode, enum gpio_int_trig trig)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
ARG_UNUSED(pin);
|
||||||
|
ARG_UNUSED(mode);
|
||||||
|
ARG_UNUSED(trig);
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct gpio_driver_api gpio_sn74hc595_drv_api_funcs = {
|
||||||
|
.pin_configure = gpio_sn74hc595_config,
|
||||||
|
.port_get_raw = gpio_sn74hc595_port_get_raw,
|
||||||
|
.port_set_masked_raw = gpio_sn74hc595_port_set_masked_raw,
|
||||||
|
.port_set_bits_raw = gpio_sn74hc595_port_set_bits_raw,
|
||||||
|
.port_clear_bits_raw = gpio_sn74hc595_port_clear_bits_raw,
|
||||||
|
.port_toggle_bits = gpio_sn74hc595_port_toggle_bits,
|
||||||
|
.pin_interrupt_configure = gpio_sn74hc595_pin_interrupt_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialization function of sn74hc595
|
||||||
|
*
|
||||||
|
* @param dev Device struct
|
||||||
|
* @return 0 if successful, failed otherwise.
|
||||||
|
*/
|
||||||
|
static int gpio_sn74hc595_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct gpio_sn74hc595_config *config = dev->config;
|
||||||
|
struct gpio_sn74hc595_drv_data *drv_data = dev->data;
|
||||||
|
|
||||||
|
if (!spi_is_ready(&config->bus)) {
|
||||||
|
LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!device_is_ready(config->reset_gpio.port)) {
|
||||||
|
LOG_ERR("GPIO port %s not ready", config->reset_gpio.port->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_ACTIVE) < 0) {
|
||||||
|
LOG_ERR("Unable to configure RST GPIO pin %u", config->reset_gpio.pin);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_set(config->reset_gpio.port, config->reset_gpio.pin, 0);
|
||||||
|
|
||||||
|
drv_data->output = 0U;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SN74HC595_SPI_OPERATION \
|
||||||
|
((uint16_t)(SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)))
|
||||||
|
|
||||||
|
#define SN74HC595_INIT(n) \
|
||||||
|
static struct gpio_sn74hc595_drv_data sn74hc595_data_##n = { \
|
||||||
|
.output = 0, \
|
||||||
|
.lock = Z_MUTEX_INITIALIZER(sn74hc595_data_##n.lock), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static const struct gpio_sn74hc595_config sn74hc595_config_##n = { \
|
||||||
|
.config = { \
|
||||||
|
.port_pin_mask = \
|
||||||
|
GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
|
||||||
|
}, \
|
||||||
|
.bus = SPI_DT_SPEC_INST_GET(n, SN74HC595_SPI_OPERATION, 0), \
|
||||||
|
.reset_gpio = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_DEFINE(DT_DRV_INST(n), &gpio_sn74hc595_init, NULL, \
|
||||||
|
&sn74hc595_data_##n, &sn74hc595_config_##n, POST_KERNEL, \
|
||||||
|
CONFIG_GPIO_SN74HC595_INIT_PRIORITY, &gpio_sn74hc595_drv_api_funcs);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(SN74HC595_INIT)
|
Loading…
Reference in a new issue