rt-thread/bsp/cvitek/drivers/drv_gpio.c

324 lines
8.5 KiB
C

/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023/10/19 xiunian first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#ifdef RT_USING_SMART
#include <ioremap.h>
#endif
#ifdef RT_USING_PIN
#include "drv_gpio.h"
#define GPIO_SWPORTA_DR 0x00
#define GPIO_SWPORTA_DDR 0x04
#define GPIO_INTEN 0x30
#define GPIO_INTTYPE_LEVEL 0x38
#define GPIO_INT_POLARITY 0x3c
#define GPIO_INTSTATUS 0x40
#define GPIO_PORTA_EOI 0x4c
#define GPIO_EXT_PORTA 0x50
#define DWAPB_GPIOA_BASE 0x03020000
#define DWAPB_GPIOE_BASE 0x05021000
#define DWAPB_GPIO_SIZE 0x1000
#define DWAPB_GPIO_PORT_NR 5
#define DWAPB_GPIO_NR 32
#define PIN_NUM(port, no) (((((port) & 0xFu) << 8) | ((no) & 0xFFu)))
#define PIN_PORT(pin) ((uint8_t)(((pin) >> 8) & 0xFu))
#define PIN_NO(pin) ((uint8_t)((pin) & 0xFFu))
#define BIT(x) (1UL << (x))
rt_inline rt_uint32_t dwapb_read32(rt_ubase_t addr)
{
return HWREG32(addr);
}
rt_inline void dwapb_write32(rt_ubase_t addr, rt_uint32_t value)
{
HWREG32(addr) = value;
}
static rt_ubase_t dwapb_gpio_base = DWAPB_GPIOA_BASE;
static rt_ubase_t dwapb_gpio_base_e = DWAPB_GPIOE_BASE;
static struct dwapb_event
{
void (*(hdr[DWAPB_GPIO_NR]))(void *args);
void *args[DWAPB_GPIO_NR];
rt_uint8_t is_both_edge[DWAPB_GPIO_NR];
} _dwapb_events[DWAPB_GPIO_PORT_NR];
static void dwapb_toggle_trigger(rt_uint8_t port, rt_uint8_t bit)
{
rt_uint8_t val;
rt_ubase_t base_addr;
rt_uint32_t pol;
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
pol = dwapb_read32(base_addr + GPIO_INT_POLARITY);
/* Just read the current value right out of the data register */
val = (rt_uint8_t)((dwapb_read32(base_addr + GPIO_EXT_PORTA) >> (bit)) & 1);
if (val)
pol &= ~BIT(bit);
else
pol |= BIT(bit);
dwapb_write32(base_addr + GPIO_INT_POLARITY, pol);
}
static void dwapb_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode)
{
rt_uint8_t bit, port;
rt_ubase_t base_addr;
rt_uint32_t reg_val;
bit = PIN_NO(pin);
port = PIN_PORT(pin);
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
reg_val = dwapb_read32(base_addr + GPIO_SWPORTA_DDR);
switch (mode)
{
case PIN_MODE_OUTPUT:
reg_val |= BIT(bit);
break;
case PIN_MODE_INPUT:
reg_val &= ~BIT(bit);
break;
}
dwapb_write32(base_addr + GPIO_SWPORTA_DDR, reg_val);
}
static void dwapb_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value)
{
rt_uint8_t bit, port;
rt_ubase_t base_addr;
rt_uint32_t reg_val;
bit = PIN_NO(pin);
port = PIN_PORT(pin);
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
reg_val = dwapb_read32(base_addr + GPIO_SWPORTA_DR);
reg_val = (value ? (reg_val | BIT(bit)) : (reg_val & (~BIT(bit))));
dwapb_write32(base_addr + GPIO_SWPORTA_DR, reg_val);
}
static rt_ssize_t dwapb_pin_read(struct rt_device *device, rt_base_t pin)
{
rt_uint8_t bit, port;
rt_ubase_t base_addr;
bit = PIN_NO(pin);
port = PIN_PORT(pin);
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
rt_uint32_t reg_val = dwapb_read32(GPIO_EXT_PORTA + base_addr);
return ((reg_val >> (bit)) & 1);
}
static rt_base_t dwapb_pin_get(const char *name)
{
rt_base_t pin = 0;
int port_num, pin_num = 0;
int i, name_len;
name_len = rt_strlen(name);
if ((name_len < 2) || (name_len > 3))
{
goto out;
}
if ((name[0] >= 'A') && (name[0] <= 'E'))
{
port_num = (int)(name[0] - 'A');
}
else
{
goto out;
}
for (i = 1; i < name_len; i++)
{
pin_num *= 10;
pin_num += name[i] - '0';
}
pin = PIN_NUM(port_num, pin_num);
return pin;
out:
rt_kprintf("xy x:A~E y:0~31, e.g. C24\n");
return -RT_EINVAL;
}
static rt_err_t dwapb_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args)
{
rt_uint8_t bit, port;
rt_ubase_t base_addr;
rt_uint32_t it_val, ip_val;
bit = PIN_NO(pin);
port = PIN_PORT(pin);
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
it_val = dwapb_read32(base_addr + GPIO_INTTYPE_LEVEL);
ip_val = dwapb_read32(base_addr + GPIO_INT_POLARITY);
if (mode == PIN_IRQ_MODE_HIGH_LEVEL || mode == PIN_IRQ_MODE_LOW_LEVEL)
{
rt_bool_t polarity = (mode == PIN_IRQ_MODE_HIGH_LEVEL);
/* Enable level detection */
it_val = (it_val & (~BIT(bit)));
/* Select polarity */
ip_val = (polarity ? (ip_val | BIT(bit)) : (ip_val & (~BIT(bit))));
}
else if (mode == PIN_IRQ_MODE_RISING_FALLING)
{
/* Disable level detection */
it_val = (it_val | BIT(bit));
/* Select both edges */
dwapb_toggle_trigger(port, bit);
}
else if (mode == PIN_IRQ_MODE_RISING || mode == PIN_IRQ_MODE_FALLING)
{
rt_bool_t rising = (mode == PIN_IRQ_MODE_RISING);
/* Disable level detection */
it_val = (it_val | BIT(bit));
/* Select edge */
ip_val = (rising ? (ip_val | BIT(bit)) : (ip_val & (~BIT(bit))));
}
else
{
/* No trigger: disable everything */
it_val = (it_val & (~BIT(bit)));
ip_val = (ip_val & (~BIT(bit)));
}
dwapb_write32(base_addr + GPIO_INTTYPE_LEVEL, it_val);
if (mode != PIN_IRQ_MODE_RISING_FALLING)
dwapb_write32(base_addr + GPIO_INT_POLARITY, ip_val);
_dwapb_events[PIN_PORT(pin)].hdr[PIN_NO(pin)] = hdr;
_dwapb_events[PIN_PORT(pin)].args[PIN_NO(pin)] = args;
_dwapb_events[PIN_PORT(pin)].is_both_edge[PIN_NO(pin)] = (mode == PIN_IRQ_MODE_RISING_FALLING);
return RT_EOK;
}
static rt_err_t dwapb_pin_detach_irq(struct rt_device *device, rt_base_t pin)
{
_dwapb_events[PIN_PORT(pin)].hdr[PIN_NO(pin)] = RT_NULL;
_dwapb_events[PIN_PORT(pin)].args[PIN_NO(pin)] = RT_NULL;
_dwapb_events[PIN_PORT(pin)].is_both_edge[PIN_NO(pin)] = 0;
return RT_EOK;
}
static rt_err_t dwapb_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
{
rt_uint8_t bit, port;
rt_ubase_t base_addr;
bit = PIN_NO(pin);
port = PIN_PORT(pin);
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
rt_uint32_t reg_val = dwapb_read32(base_addr + GPIO_INTEN);
reg_val = (enabled ? (reg_val | BIT(bit)) : (reg_val & (~BIT(bit))));
dwapb_write32(base_addr + GPIO_INTEN, reg_val);
return RT_EOK;
}
static const struct rt_pin_ops _dwapb_ops =
{
dwapb_pin_mode,
dwapb_pin_write,
dwapb_pin_read,
dwapb_pin_attach_irq,
dwapb_pin_detach_irq,
dwapb_pin_irq_enable,
dwapb_pin_get,
};
static void rt_hw_gpio_isr(int irqno, void *param)
{
rt_uint8_t port;
rt_ubase_t base_addr;
rt_uint32_t pending, mask;
mask = 0;
port = (irqno == SYS_GPIO_IRQ_BASE ? 4 : (irqno - GPIO_IRQ_BASE));
base_addr = (port == 4 ? dwapb_gpio_base_e : (dwapb_gpio_base + DWAPB_GPIO_SIZE * port));
pending = dwapb_read32(base_addr + GPIO_INTSTATUS);
if (pending)
{
rt_base_t bit;
for (bit = 0; bit < DWAPB_GPIO_NR; ++bit)
{
if (pending & BIT(bit))
{
mask = (mask | (BIT(bit)));
if (_dwapb_events[port].hdr[bit] != RT_NULL)
{
_dwapb_events[port].hdr[bit](_dwapb_events[port].args[bit]);
}
if (_dwapb_events[port].is_both_edge[bit]) {
dwapb_toggle_trigger(port, bit);
}
}
}
}
dwapb_write32(base_addr + GPIO_PORTA_EOI, mask);
}
int rt_hw_gpio_init(void)
{
rt_device_pin_register("gpio", &_dwapb_ops, RT_NULL);
#define INT_INSTALL_GPIO_DEVICE(no) \
rt_hw_interrupt_install(GPIO_IRQ_BASE + (no), rt_hw_gpio_isr, RT_NULL, "gpio"); \
rt_hw_interrupt_umask(GPIO_IRQ_BASE + (no));
INT_INSTALL_GPIO_DEVICE(0);
INT_INSTALL_GPIO_DEVICE(1);
INT_INSTALL_GPIO_DEVICE(2);
INT_INSTALL_GPIO_DEVICE(3);
rt_hw_interrupt_install(SYS_GPIO_IRQ_BASE, rt_hw_gpio_isr, RT_NULL, "gpio");
rt_hw_interrupt_umask(SYS_GPIO_IRQ_BASE);
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_gpio_init);
#endif /* RT_USING_PIN */