/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023/10/19 xiunian first version */ #include #include #include #include #ifdef RT_USING_SMART #include #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_DRIVER_NAME "gpio-dwapb" #define DWAPB_GPIOA_BASE 0x03020000 #define DWAPB_GPIOE_BASE 0x05021000 #define DWAPB_GPIO_SIZE 0x1000 #define DWAPB_GPIOA_IRQNUM 0x3c #define DWAPB_GPIOE_IRQNUM 0x46 #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_int8_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 == DWAPB_GPIOE_IRQNUM ? 4 : (irqno - DWAPB_GPIOA_IRQNUM)); 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) { #ifdef RT_USING_SMART #define BSP_IOREMAP_GPIO_DEVICE(no) \ rt_ioremap((void *)(DWAPB_GPIOA_BASE + (no) * DWAPB_GPIO_SIZE), DWAPB_GPIO_SIZE); dwapb_gpio_base = (rt_size_t)BSP_IOREMAP_GPIO_DEVICE(0); BSP_IOREMAP_GPIO_DEVICE(1); BSP_IOREMAP_GPIO_DEVICE(2); BSP_IOREMAP_GPIO_DEVICE(3); dwapb_gpio_base_e = (rt_size_t)rt_ioremap((void *)DWAPB_GPIOE_BASE, DWAPB_GPIO_SIZE); #endif rt_device_pin_register("gpio", &_dwapb_ops, RT_NULL); #define INT_INSTALL_GPIO_DEVICE(no) \ rt_hw_interrupt_install(DWAPB_GPIOA_IRQNUM + (no), rt_hw_gpio_isr, RT_NULL, "gpio"); \ rt_hw_interrupt_umask(DWAPB_GPIOA_IRQNUM + (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(DWAPB_GPIOE_IRQNUM, rt_hw_gpio_isr, RT_NULL, "gpio"); rt_hw_interrupt_umask(DWAPB_GPIOE_IRQNUM); return 0; } INIT_DEVICE_EXPORT(rt_hw_gpio_init); #endif /* RT_USING_PIN */