123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- * Copyright (c) 2020-2021, Bluetrum Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2020-11-19 greedyhao first version
- * 2025-05-15 kurisaW support gpio interrupt
- */
- #include "drv_gpio.h"
- #ifdef RT_USING_PIN
- // #define DRV_DEBUG
- #define LOG_TAG "drv.gpio"
- #include <drv_log.h>
- struct port_info
- {
- rt_uint8_t start_pin;
- rt_uint8_t delta_pin;
- rt_uint8_t total_pin;
- };
- /* It needs to be adjusted to the hardware. */
- static const struct port_info port_table[] =
- {
- {0, 8, 0}, /* PA0-PA7 */
- {0, 5, 8}, /* PB0-PB4 */
- {0, 8, 13}, /* PE0-PE7 */
- {0, 6, 21}, /* PF0-PF5 */
- };
- static const hal_sfr_t port_sfr[] =
- {
- GPIOA_BASE,
- GPIOB_BASE,
- GPIOE_BASE,
- GPIOF_BASE,
- };
- static struct ab32_pin_irq pin_irq_table[8] = {0}; // For WAKEUP_CRICUIT_0 to _7
- static rt_mq_t gpio_irq_mq = RT_NULL;
- static rt_uint8_t _pin_port(rt_uint32_t pin)
- {
- rt_uint8_t port = 0;
- for (port = 0; port < 3; port++) {
- if (pin < (port_table[port].total_pin + port_table[port].delta_pin)) {
- break;
- }
- }
- return port;
- }
- #define PIN_NUM(port, no) ((rt_uint8_t)(port_table[port].total_pin + no - port_table[port].start_pin))
- #define PIN_PORT(pin) _pin_port(pin)
- #define PORT_SFR(port) (port_sfr[(port)])
- #define PIN_NO(pin) (rt_uint8_t)((pin) & 0xFu)
- static rt_base_t ab32_pin_get(const char *name)
- {
- rt_base_t pin = 0;
- int hw_port_num, hw_pin_num = 0;
- int i, name_len;
- name_len = rt_strlen(name);
- if ((name_len < 4) || (name_len >= 6))
- {
- return -RT_EINVAL;
- }
- if ((name[0] != 'P') || (name[2] != '.'))
- {
- return -RT_EINVAL;
- }
- if ((name[1] >= 'A') && (name[1] <= 'B'))
- {
- hw_port_num = (int)(name[1] - 'A');
- }
- else if ((name[1] >= 'E') && (name[1] <= 'G'))
- {
- hw_port_num = (int)(name[1] - 'A') - 2; /* Without 'C' and 'D'. */
- }
- else
- {
- return -RT_EINVAL;
- }
- for (i = 3; i < name_len; i++)
- {
- hw_pin_num *= 10;
- hw_pin_num += name[i] - '0';
- }
- pin = PIN_NUM(hw_port_num, hw_pin_num);
- LOG_D("name=%s", name);
- LOG_D("hw_port_num=%d hw_pin_num=%d pin=%d", hw_port_num, hw_pin_num, pin);
- return pin;
- }
- static void ab32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
- {
- rt_uint8_t port = PIN_PORT(pin);
- rt_uint8_t gpio_pin = pin - port_table[port].total_pin;
- hal_gpio_write(PORT_SFR(port), gpio_pin, (rt_uint8_t)value);
- }
- static rt_ssize_t ab32_pin_read(rt_device_t dev, rt_base_t pin)
- {
- rt_uint8_t port = PIN_PORT(pin);
- rt_uint8_t gpio_pin = pin - port_table[port].total_pin;
- return hal_gpio_read(PORT_SFR(port), gpio_pin);
- }
- static void ab32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode)
- {
- struct gpio_init gpio_init;
- rt_uint8_t port = PIN_PORT(pin);
- gpio_init.pin = BIT(pin - port_table[port].total_pin);
- gpio_init.de = GPIO_DIGITAL;
- gpio_init.af_con = GPIO_AFDIS;
- LOG_D("port=%d pin=%d", port, gpio_init.pin);
- switch (mode)
- {
- case PIN_MODE_INPUT:
- gpio_init.pull = GPIO_NOPULL;
- gpio_init.dir = GPIO_DIR_INPUT;
- break;
- case PIN_MODE_INPUT_PULLUP:
- gpio_init.pull = GPIO_PULLUP;
- gpio_init.dir = GPIO_DIR_INPUT;
- break;
- case PIN_MODE_INPUT_PULLDOWN:
- gpio_init.pull = GPIO_PULLDOWN;
- gpio_init.dir = GPIO_DIR_INPUT;
- break;
- case PIN_MODE_OUTPUT:
- case PIN_MODE_OUTPUT_OD:
- default:
- gpio_init.pull = GPIO_NOPULL;
- gpio_init.dir = GPIO_DIR_OUTPUT;
- break;
- }
- hal_gpio_init(PORT_SFR(port), &gpio_init);
- }
- static rt_err_t get_port_pin(rt_base_t pin, rt_uint8_t *port, rt_uint8_t *hw_pin)
- {
- rt_uint8_t i;
- for (i = 0; i < sizeof(port_table) / sizeof(port_table[0]); i++)
- {
- if (pin >= port_table[i].total_pin &&
- pin < port_table[i].total_pin + port_table[i].delta_pin)
- {
- *port = i;
- *hw_pin = pin - port_table[i].total_pin + port_table[i].start_pin;
- return RT_EOK;
- }
- }
- return -RT_EINVAL;
- }
- static rt_int32_t get_wakeup_circuit(rt_base_t pin)
- {
- /* Special interrupt pins */
- if (pin == PIN_NUM(0, 7)) return WAKEUP_CRICUIT_0; // PA7
- if (pin == PIN_NUM(1, 1)) return WAKEUP_CRICUIT_1; // PB1
- if (pin == PIN_NUM(1, 2)) return WAKEUP_CRICUIT_2; // PB2
- if (pin == PIN_NUM(1, 3)) return WAKEUP_CRICUIT_3; // PB3
- if (pin == PIN_NUM(1, 4)) return WAKEUP_CRICUIT_4; // PB4
- /* WAKEUP_CRICUIT_5 is for RTC (WKO), assuming not a GPIO pin */
- /* Other pins use WAKEUP_CRICUIT_6 (falling) or WAKEUP_CRICUIT_7 (rising) */
- return -1; // Will be handled in attach_irq based on mode
- }
- static rt_uint32_t get_edge_select_bit(rt_uint8_t circuit)
- {
- switch (circuit)
- {
- case WAKEUP_CRICUIT_0: return WAKEUP_EDGE_SELECT_0;
- case WAKEUP_CRICUIT_1: return WAKEUP_EDGE_SELECT_1;
- case WAKEUP_CRICUIT_2: return WAKEUP_EDGE_SELECT_2;
- case WAKEUP_CRICUIT_3: return WAKEUP_EDGE_SELECT_3;
- case WAKEUP_CRICUIT_4: return WAKEUP_EDGE_SELECT_4;
- case WAKEUP_CRICUIT_5: return WAKEUP_EDGE_SELECT_5;
- case WAKEUP_CRICUIT_6: return WAKEUP_EDGE_SELECT_6;
- case WAKEUP_CRICUIT_7: return WAKEUP_EDGE_SELECT_7;
- default: return 0;
- }
- }
- void __attribute__((section(".irq.gpio"))) ab32_pin_irq_handler(void *args)
- {
- rt_interrupt_enter();
- rt_uint32_t pending = WKUPEDG;
- rt_uint8_t circuit = 0;
- for (circuit = 0; circuit <= WAKEUP_CRICUIT_7; circuit++)
- {
- if (pending & (BIT(circuit) << WAKEUP_INT_ENABLE))
- {
- /* clear pending interrupt */
- WKUPCPND = (BIT(circuit) << WAKEUP_INT_ENABLE);
- rt_mq_send(gpio_irq_mq, &circuit, sizeof(circuit));
- }
- }
- rt_interrupt_leave();
- }
- static void ab32_pin_irq_thread(void *parameter)
- {
- rt_uint8_t circuit;
- while (1)
- {
- if (rt_mq_recv(gpio_irq_mq, &circuit, sizeof(circuit), RT_WAITING_FOREVER) == RT_EOK)
- {
- if (circuit <= WAKEUP_CRICUIT_7 && pin_irq_table[circuit].hdr)
- {
- pin_irq_table[circuit].hdr(pin_irq_table[circuit].args);
- }
- }
- }
- }
- static rt_err_t ab32_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
- rt_uint32_t mode, void (*hdr)(void *args), void *args)
- {
- rt_uint8_t port, hw_pin;
- rt_int32_t circuit;
- uint8_t pin_mode;
- if (get_port_pin(pin, &port, &hw_pin) != RT_EOK)
- {
- return -RT_EINVAL;
- }
- circuit = get_wakeup_circuit(pin);
- if(circuit == -1)
- {
- circuit = (mode == PIN_IRQ_MODE_FALLING) ? WAKEUP_CRICUIT_6 : WAKEUP_CRICUIT_7;
- }
- /* store handler and arguments */
- pin_irq_table[circuit].hdr = hdr;
- pin_irq_table[circuit].args = args;
- /* The interrupt source in the port is: Port_intsrc = {PG[4:0], PF[5:0], PE[7:0], PB[4:0], PA[7:0]}. */
- /* Such as:the interrupt source number of PA0 is 0, interrupt source number of PG4 is 31. */
- PORTINTEN |= BIT(pin); // Enable interrupt
- PORTINTEDG |= BIT(pin); // Edge trigger
- return RT_EOK;
- }
- static rt_err_t ab32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
- {
- rt_uint8_t port, hw_pin;
- rt_int32_t circuit;
- if (get_port_pin(pin, &port, &hw_pin) != RT_EOK)
- {
- return -RT_EINVAL;
- }
- circuit = get_wakeup_circuit(pin);
- if (circuit < 0)
- {
- /* assume previously assigned to WAKEUP_CRICUIT_6 or _7 */
- /* check both circuits for handler */
- if (pin_irq_table[WAKEUP_CRICUIT_6].hdr)
- circuit = WAKEUP_CRICUIT_6;
- else if (pin_irq_table[WAKEUP_CRICUIT_7].hdr)
- circuit = WAKEUP_CRICUIT_7;
- else
- return RT_EOK;
- }
- PORTINTEN &= ~BIT(circuit);
- WKUPCON &= ~BIT(circuit);
- WKUPCPND = BIT(get_edge_select_bit(circuit));
- /* clear handler */
- pin_irq_table[circuit].hdr = RT_NULL;
- pin_irq_table[circuit].args = RT_NULL;
- return RT_EOK;
- }
- static rt_err_t ab32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
- rt_uint32_t enabled)
- {
- rt_uint8_t port, hw_pin;
- rt_int32_t circuit;
- if (get_port_pin(pin, &port, &hw_pin) != RT_EOK)
- {
- return -RT_EINVAL;
- }
- circuit = get_wakeup_circuit(pin);
- if (circuit < 0)
- {
- if (pin_irq_table[WAKEUP_CRICUIT_6].hdr)
- {
- circuit = WAKEUP_CRICUIT_6;
- }
- else if (pin_irq_table[WAKEUP_CRICUIT_7].hdr)
- {
- circuit = WAKEUP_CRICUIT_7;
- }
- else
- return -RT_EINVAL;
- }
- rt_uint32_t edge_bit = get_edge_select_bit(circuit);
- if (enabled == RT_TRUE)
- {
- WKUPEDG = BIT(circuit) | BIT(edge_bit);
- WKUPCON = BIT(circuit) | BIT(WAKEUP_INT_ENABLE);
- WKUPCPND = BIT(circuit);
- /* install interrupt handler */
- rt_hw_interrupt_install(IRQ_GPIO_IRQ, ab32_pin_irq_handler, RT_NULL, "gpio_isr");
- rt_hw_irq_enable(IRQ_GPIO_IRQ);
- }
- else
- {
- /* disable interrupt */
- WKUPCON &= ~BIT(circuit);
- }
- return RT_EOK;
- }
- static int ab32_pin_irq_init(void)
- {
- gpio_irq_mq = rt_mq_create("gpio_irq", sizeof(rt_uint8_t), 128, RT_IPC_FLAG_FIFO);
- if (gpio_irq_mq == RT_NULL)
- {
- return -RT_ENOMEM;
- }
- rt_thread_t tid = rt_thread_create("gpio_irq",
- ab32_pin_irq_thread,
- RT_NULL,
- 512,
- 15,
- 5);
- if (tid != RT_NULL)
- {
- rt_thread_startup(tid);
- }
- else
- {
- rt_mq_delete(gpio_irq_mq);
- return -RT_ENOMEM;
- }
- return RT_EOK;
- }
- INIT_PREV_EXPORT(ab32_pin_irq_init);
- const static struct rt_pin_ops _ab32_pin_ops =
- {
- ab32_pin_mode,
- ab32_pin_write,
- ab32_pin_read,
- ab32_pin_attach_irq,
- ab32_pin_dettach_irq,
- ab32_pin_irq_enable,
- ab32_pin_get,
- };
- int rt_hw_pin_init(void)
- {
- return rt_device_pin_register("pin", &_ab32_pin_ops, RT_NULL);
- }
- #endif
|