drivers: gpio: add andes atcgpio100 GPIO driver
Add atcgpio100 GPIO driver for andes_v5 soc series. Signed-off-by: Jim Shu <cwshu@andestech.com>
This commit is contained in:
parent
f9726c9186
commit
45bcef1de9
4 changed files with 388 additions and 0 deletions
|
@ -44,6 +44,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_STMPE1600 gpio_stmpe1600.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_GPIO_XEC_V2 gpio_mchp_xec_v2.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_PCA953X gpio_pca953x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_FXL6408 gpio_fxl6408.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_ANDES_ATCGPIO100 gpio_andes_atcgpio100.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c)
|
||||
|
||||
|
|
|
@ -101,4 +101,6 @@ source "drivers/gpio/Kconfig.pca953x"
|
|||
|
||||
source "drivers/gpio/Kconfig.fxl6408"
|
||||
|
||||
source "drivers/gpio/Kconfig.andes_atcgpio100"
|
||||
|
||||
endif # GPIO
|
||||
|
|
15
drivers/gpio/Kconfig.andes_atcgpio100
Normal file
15
drivers/gpio/Kconfig.andes_atcgpio100
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Kconfig Andes ATCGPIO100 configuration option
|
||||
#
|
||||
# Copyright (c) 2021 Andes Technology Corporation.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config GPIO_ANDES_ATCGPIO100
|
||||
bool "Andes ATCGPIO100 GPIO driver"
|
||||
depends on SOC_SERIES_RISCV_ANDES_V5
|
||||
select HAS_DTS_GPIO
|
||||
help
|
||||
Enable driver for the Andes ATCGPIO100 GPIO controller.
|
||||
|
||||
Says n if not sure.
|
370
drivers/gpio/gpio_andes_atcgpio100.c
Normal file
370
drivers/gpio/gpio_andes_atcgpio100.c
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Andes Technology Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file GPIO driver for the AndesTech ATCGPIO100 controller
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <kernel.h>
|
||||
#include <device.h>
|
||||
#include <soc.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <sys/util.h>
|
||||
#include <sys/sys_io.h>
|
||||
|
||||
#include "gpio_utils.h"
|
||||
|
||||
#define DT_DRV_COMPAT andestech_atcgpio100
|
||||
|
||||
/* Andes ATCGPIO100 register definition */
|
||||
#define REG_IDR 0x00 /* ID and Revision reg. */
|
||||
#define REG_CFG 0x10 /* Hardware configure reg. */
|
||||
#define REG_DIN 0x20 /* Data In reg. */
|
||||
#define REG_DOUT 0x24 /* Data Out reg. */
|
||||
#define REG_DIR 0x28 /* Channel direction reg. */
|
||||
#define REG_DCLR 0x2C /* Data out clear reg. */
|
||||
#define REG_DSET 0x30 /* Data out set reg. */
|
||||
#define REG_PUEN 0x40 /* Pull enable reg. */
|
||||
#define REG_PTYP 0x44 /* Pull type reg. */
|
||||
#define REG_INTE 0x50 /* Interrupt enable reg. */
|
||||
#define REG_IMD0 0x54 /* Interrupt mode 0 ~ 7 reg. */
|
||||
#define REG_IMD1 0x58 /* Interrupt mode 8 ~ 15 reg. */
|
||||
#define REG_IMD2 0x5C /* Interrupt mode 16 ~ 23 reg. */
|
||||
#define REG_IMD3 0x60 /* Interrupt mode 24 ~ 31 reg. */
|
||||
#define REG_ISTA 0x64 /* Interrupt status reg. */
|
||||
#define REG_DEBE 0x70 /* De-bounce enable reg. */
|
||||
#define REG_DEBC 0x74 /* De-Bounce control reg. */
|
||||
|
||||
#define INT_NO_OPERATION 0x0
|
||||
#define INT_HIGH_LEVEL 0x2
|
||||
#define INT_LOW_LEVEL 0x3
|
||||
#define INT_NEGATIVE_EDGE 0x5
|
||||
#define INT_POSITIVE_EDGE 0x6
|
||||
#define INT_DUAL_EDGE 0x7
|
||||
|
||||
#define PULL_CONFIGURED BIT(31)
|
||||
#define DEBOUNCE_CONFIGURED BIT(29)
|
||||
#define DF_DEBOUNCED_SETTING (0x80000003)
|
||||
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct gpio_atcgpio100_config * const)(dev)->config)
|
||||
|
||||
#define DEV_DATA(dev) \
|
||||
((struct gpio_atcgpio100_data *)(dev)->data)
|
||||
|
||||
|
||||
#define GPIO_CFG(dev) (DEV_CFG(dev)->base + REG_CFG)
|
||||
#define GPIO_DIR(dev) (DEV_CFG(dev)->base + REG_DIR)
|
||||
#define GPIO_DIN(dev) (DEV_CFG(dev)->base + REG_DIN)
|
||||
#define GPIO_DOUT(dev) (DEV_CFG(dev)->base + REG_DOUT)
|
||||
#define GPIO_DCLR(dev) (DEV_CFG(dev)->base + REG_DCLR)
|
||||
#define GPIO_DSET(dev) (DEV_CFG(dev)->base + REG_DSET)
|
||||
#define GPIO_PUEN(dev) (DEV_CFG(dev)->base + REG_PUEN)
|
||||
#define GPIO_PTYP(dev) (DEV_CFG(dev)->base + REG_PTYP)
|
||||
#define GPIO_INTE(dev) (DEV_CFG(dev)->base + REG_INTE)
|
||||
#define GPIO_IMD(dev, idx) (DEV_CFG(dev)->base + REG_IMD0 + (idx * 4))
|
||||
#define GPIO_ISTA(dev) (DEV_CFG(dev)->base + REG_ISTA)
|
||||
#define GPIO_DEBE(dev) (DEV_CFG(dev)->base + REG_DEBE)
|
||||
#define GPIO_DEBC(dev) (DEV_CFG(dev)->base + REG_DEBC)
|
||||
|
||||
#define INWORD(x) sys_read32(x)
|
||||
#define OUTWORD(x, d) sys_write32(d, x)
|
||||
|
||||
#define SET_GPIO_INT_MODE(cur_val, mode, ch_idx) \
|
||||
do { \
|
||||
cur_val &= ~(BIT_MASK(3) << (ch_idx * 4)); \
|
||||
cur_val |= (mode << (ch_idx * 4)); \
|
||||
} while (false)
|
||||
|
||||
|
||||
typedef void (*atcgpio100_cfg_func_t)(void);
|
||||
|
||||
struct gpio_atcgpio100_config {
|
||||
/* gpio_driver_config needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
uint32_t base;
|
||||
uint32_t irq_num;
|
||||
atcgpio100_cfg_func_t cfg_func;
|
||||
};
|
||||
|
||||
struct gpio_atcgpio100_data {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_data common;
|
||||
/* list of callbacks */
|
||||
sys_slist_t cb;
|
||||
struct k_spinlock lock;
|
||||
};
|
||||
|
||||
static int gpio_atcgpio100_config(const struct device *port,
|
||||
gpio_pin_t pin,
|
||||
gpio_flags_t flags)
|
||||
{
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
uint32_t port_value, pin_mask, io_flags;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
/* Does not support disconnected pin, and
|
||||
* not supporting both input/output at same time.
|
||||
*/
|
||||
io_flags = flags & (GPIO_INPUT | GPIO_OUTPUT);
|
||||
if ((io_flags == GPIO_DISCONNECTED)
|
||||
|| (io_flags == (GPIO_INPUT | GPIO_OUTPUT))) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
pin_mask = BIT(pin);
|
||||
|
||||
if (flags & GPIO_OUTPUT) {
|
||||
|
||||
if (flags & GPIO_OUTPUT_INIT_HIGH) {
|
||||
OUTWORD(GPIO_DSET(port), pin_mask);
|
||||
} else if (flags & GPIO_OUTPUT_INIT_LOW) {
|
||||
OUTWORD(GPIO_DCLR(port), pin_mask);
|
||||
}
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
/* Set channel output */
|
||||
port_value = INWORD(GPIO_DIR(port));
|
||||
OUTWORD(GPIO_DIR(port), port_value | pin_mask);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
} else if (flags & GPIO_INPUT) {
|
||||
|
||||
if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
/* Set de-bounce */
|
||||
if (flags & GPIO_INT_DEBOUNCE) {
|
||||
/* Default settings: Filter out pulses which are
|
||||
* less than 4 de-bounce clock period
|
||||
*/
|
||||
OUTWORD(GPIO_DEBC(port), DF_DEBOUNCED_SETTING);
|
||||
port_value = INWORD(GPIO_DEBE(port));
|
||||
OUTWORD(GPIO_DEBE(port), port_value | pin_mask);
|
||||
}
|
||||
|
||||
/* Set channel input */
|
||||
port_value = INWORD(GPIO_DIR(port));
|
||||
OUTWORD(GPIO_DIR(port), port_value & ~pin_mask);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_port_get_raw(const struct device *port,
|
||||
gpio_port_value_t *value)
|
||||
{
|
||||
*value = INWORD(GPIO_DIN(port));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_set_masked_raw(const struct device *port,
|
||||
gpio_port_pins_t mask,
|
||||
gpio_port_value_t value)
|
||||
{
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
uint32_t port_value;
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
port_value = INWORD(GPIO_DOUT(port));
|
||||
OUTWORD(GPIO_DOUT(port), (port_value & ~mask) | (value & mask));
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_set_bits_raw(const struct device *port,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
OUTWORD(GPIO_DSET(port), pins);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_clear_bits_raw(const struct device *port,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
OUTWORD(GPIO_DCLR(port), pins);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_toggle_bits(const struct device *port,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
uint32_t port_value;
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&data->lock);
|
||||
|
||||
port_value = INWORD(GPIO_DOUT(port));
|
||||
OUTWORD(GPIO_DOUT(port), port_value ^ pins);
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_pin_interrupt_configure(
|
||||
const struct device *port,
|
||||
gpio_pin_t pin,
|
||||
enum gpio_int_mode mode,
|
||||
enum gpio_int_trig trig)
|
||||
{
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
uint32_t port_value, int_mode, imr_idx, ch_idx;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
switch (mode | trig) {
|
||||
case GPIO_INT_EDGE_BOTH:
|
||||
int_mode = INT_DUAL_EDGE;
|
||||
break;
|
||||
case GPIO_INT_EDGE_RISING:
|
||||
int_mode = INT_POSITIVE_EDGE;
|
||||
break;
|
||||
case GPIO_INT_EDGE_FALLING:
|
||||
int_mode = INT_NEGATIVE_EDGE;
|
||||
break;
|
||||
case GPIO_INT_LEVEL_LOW:
|
||||
int_mode = INT_LOW_LEVEL;
|
||||
break;
|
||||
case GPIO_INT_LEVEL_HIGH:
|
||||
int_mode = INT_HIGH_LEVEL;
|
||||
break;
|
||||
default:
|
||||
int_mode = INT_NO_OPERATION;
|
||||
break;
|
||||
}
|
||||
|
||||
imr_idx = (pin / 8);
|
||||
ch_idx = (pin % 8);
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
if (int_mode == INT_NO_OPERATION) {
|
||||
/* Disable interrupt of pin */
|
||||
port_value = INWORD(GPIO_INTE(port));
|
||||
OUTWORD(GPIO_INTE(port), port_value & ~BIT(pin));
|
||||
|
||||
/* Clear the remain pending interrupt */
|
||||
port_value = INWORD(GPIO_ISTA(port));
|
||||
OUTWORD(GPIO_ISTA(port), port_value);
|
||||
} else {
|
||||
/* Set interrupt mode of pin */
|
||||
port_value = INWORD(GPIO_IMD(port, imr_idx));
|
||||
SET_GPIO_INT_MODE(port_value, int_mode, ch_idx);
|
||||
OUTWORD(GPIO_IMD(port, imr_idx), port_value);
|
||||
|
||||
/* Enable interrupt of pin */
|
||||
port_value = INWORD(GPIO_INTE(port));
|
||||
OUTWORD(GPIO_INTE(port), port_value | BIT(pin));
|
||||
}
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_atcgpio100_manage_callback(const struct device *port,
|
||||
struct gpio_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
|
||||
return gpio_manage_callback(&data->cb, callback, set);
|
||||
}
|
||||
|
||||
static void gpio_atcgpio100_irq_handler(const struct device *port)
|
||||
{
|
||||
struct gpio_atcgpio100_data * const data = DEV_DATA(port);
|
||||
uint32_t port_value;
|
||||
|
||||
port_value = INWORD(GPIO_ISTA(port));
|
||||
OUTWORD(GPIO_ISTA(port), port_value);
|
||||
|
||||
gpio_fire_callbacks(&data->cb, port, port_value);
|
||||
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api gpio_atcgpio100_api = {
|
||||
.pin_configure = gpio_atcgpio100_config,
|
||||
.port_get_raw = gpio_atcgpio100_port_get_raw,
|
||||
.port_set_masked_raw = gpio_atcgpio100_set_masked_raw,
|
||||
.port_set_bits_raw = gpio_atcgpio100_set_bits_raw,
|
||||
.port_clear_bits_raw = gpio_atcgpio100_clear_bits_raw,
|
||||
.port_toggle_bits = gpio_atcgpio100_toggle_bits,
|
||||
.pin_interrupt_configure = gpio_atcgpio100_pin_interrupt_configure,
|
||||
.manage_callback = gpio_atcgpio100_manage_callback
|
||||
};
|
||||
|
||||
static int gpio_atcgpio100_init(const struct device *port)
|
||||
{
|
||||
const struct gpio_atcgpio100_config * const dev_cfg = DEV_CFG(port);
|
||||
|
||||
/* Disable all interrupts */
|
||||
OUTWORD(GPIO_INTE(port), BIT_MASK(0));
|
||||
|
||||
/* Write 1 to clear interrupt status */
|
||||
OUTWORD(GPIO_ISTA(port), (uint32_t) BIT64_MASK(32));
|
||||
|
||||
/* Configure GPIO device */
|
||||
dev_cfg->cfg_func();
|
||||
|
||||
/* Enable PLIC interrupt GPIO source */
|
||||
irq_enable(dev_cfg->irq_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GPIO_ATCGPIO100_INIT(n) \
|
||||
static void gpio_atcgpio100_cfg_func_##n(void); \
|
||||
static struct gpio_atcgpio100_data gpio_atcgpio100_data_##n; \
|
||||
\
|
||||
static const struct gpio_atcgpio100_config \
|
||||
gpio_atcgpio100_config_##n = { \
|
||||
.common = { \
|
||||
.port_pin_mask = \
|
||||
GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
|
||||
}, \
|
||||
.base = DT_INST_REG_ADDR(n), \
|
||||
.irq_num = DT_INST_IRQN(n), \
|
||||
.cfg_func = gpio_atcgpio100_cfg_func_##n \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, \
|
||||
gpio_atcgpio100_init, \
|
||||
NULL, \
|
||||
&gpio_atcgpio100_data_##n, \
|
||||
&gpio_atcgpio100_config_##n, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&gpio_atcgpio100_api); \
|
||||
\
|
||||
static void gpio_atcgpio100_cfg_func_##n(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(n), \
|
||||
DT_INST_IRQ(n, priority), \
|
||||
gpio_atcgpio100_irq_handler, \
|
||||
DEVICE_DT_INST_GET(n), \
|
||||
0); \
|
||||
return; \
|
||||
} \
|
||||
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_ATCGPIO100_INIT)
|
Loading…
Add table
Add a link
Reference in a new issue