/* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include "irq-internal.h" typedef enum GPIOIntType { GPIOLevelLow = 0, GPIOLevelHigh, GPIOEdgelFalling, GPIOEdgelRising } eGPIOIntType_t; typedef enum eGPIOPinLevel { GPIO_LOW = 0, GPIO_HIGH } eGPIOPinLevel_t; typedef enum eGPIOPinDirection { GPIO_IN = 0, GPIO_OUT } eGPIOPinDirection_t; #define GPIO_SWPORT_DR 0x00 #define GPIO_SWPORT_DDR 0x04 #define GPIO_INTEN 0x30 #define GPIO_INTMASK 0x34 #define GPIO_INTTYPE_LEVEL 0x38 #define GPIO_INT_POLARITY 0x3c #define GPIO_INT_STATUS 0x40 #define GPIO_INT_RAWSTATUS 0x44 #define GPIO_DEBOUNCE 0x48 #define GPIO_PORTS_EOI 0x4c #define GPIO_EXT_PORT 0x50 #define GPIO_LS_SYNC 0x60 static inline unsigned pin_to_bit(unsigned pin) { return (1 << pin); } static inline unsigned offset_to_bit(unsigned offset) { return (1 << offset); } static void gpio_bit_op(void __iomem *regbase, unsigned int offset, u32 bit, unsigned char flag) { u32 val = readl(regbase + offset); if (flag) val |= bit; else val &= ~bit; writel(val, regbase + offset); } static int gpio_bit_rd(void __iomem *regbase, unsigned int offset, u32 bit) { return readl(regbase + offset) & bit ? 1 : 0; } static void gpio_irq_unmask(void __iomem *regbase, unsigned int bit) { gpio_bit_op(regbase, GPIO_INTEN, bit, 1); } static void gpio_irq_mask(void __iomem *regbase, unsigned int bit) { gpio_bit_op(regbase, GPIO_INTEN, bit, 0); } static void gpio_irq_ack(void __iomem *regbase, unsigned int bit) { gpio_bit_op(regbase, GPIO_PORTS_EOI, bit, 1); } static void generic_gpio_handle_irq(int irq, void *data __always_unused) { struct gpio_bank *bank = gpio_id_to_bank(irq - IRQ_GPIO0); unsigned gpio_irq, pin, unmasked = 0; u32 isr, ilr; isr = readl(bank->regbase + GPIO_INT_STATUS); ilr = readl(bank->regbase + GPIO_INTTYPE_LEVEL); gpio_irq = bank->irq_base; while (isr) { pin = fls(isr) - 1; /* first mask and ack irq */ gpio_irq_mask(bank->regbase, offset_to_bit(pin)); gpio_irq_ack(bank->regbase, offset_to_bit(pin)); /* * If gpio is edge triggered, clear condition before executing * the handler, so that we don't miss next edges trigger. */ if (ilr & (1 << pin)) { unmasked = 1; gpio_irq_unmask(bank->regbase, offset_to_bit(pin)); } __generic_gpio_handle_irq(gpio_irq + pin); isr &= ~(1 << pin); if (!unmasked) gpio_irq_unmask(bank->regbase, offset_to_bit(pin)); } } static void gpio_set_intr_type(void __iomem *regbase, unsigned int bit, eGPIOIntType_t type) { switch (type) { case GPIOLevelLow: gpio_bit_op(regbase, GPIO_INT_POLARITY, bit, 0); gpio_bit_op(regbase, GPIO_INTTYPE_LEVEL, bit, 0); break; case GPIOLevelHigh: gpio_bit_op(regbase, GPIO_INTTYPE_LEVEL, bit, 0); gpio_bit_op(regbase, GPIO_INT_POLARITY, bit, 1); break; case GPIOEdgelFalling: gpio_bit_op(regbase, GPIO_INTTYPE_LEVEL, bit, 1); gpio_bit_op(regbase, GPIO_INT_POLARITY, bit, 0); break; case GPIOEdgelRising: gpio_bit_op(regbase, GPIO_INTTYPE_LEVEL, bit, 1); gpio_bit_op(regbase, GPIO_INT_POLARITY, bit, 1); break; } } static int gpio_get_intr_type(void __iomem *regbase, unsigned int bit) { u32 polarity, level, magic = 0; int type; polarity = gpio_bit_rd(regbase, GPIO_INT_POLARITY, bit); level = gpio_bit_rd(regbase, GPIO_INTTYPE_LEVEL, bit); magic = (polarity << 1) | (level << 0); switch (magic) { case 0x00: type = GPIOLevelLow; break; case 0x02: type = GPIOLevelHigh; break; case 0x01: type = GPIOEdgelFalling; break; case 0x03: type = GPIOEdgelRising; break; default: type = -EINVAL; } return type; } static int gpio_irq_set_type(int gpio_irq, unsigned int type) { int gpio = irq_to_gpio(gpio_irq); struct gpio_bank *bank = gpio_to_bank(gpio); eGPIOIntType_t int_type = 0; if (!bank) return -EINVAL; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; switch (type) { case IRQ_TYPE_EDGE_RISING: int_type = GPIOEdgelRising; break; case IRQ_TYPE_EDGE_FALLING: int_type = GPIOEdgelFalling; break; case IRQ_TYPE_LEVEL_HIGH: int_type = GPIOLevelHigh; break; case IRQ_TYPE_LEVEL_LOW: int_type = GPIOLevelLow; break; default: return -EINVAL; } /* Before set interrupt type, gpio must set input */ gpio_bit_op(bank->regbase, GPIO_SWPORT_DDR, offset_to_bit(gpio), GPIO_IN); gpio_set_intr_type(bank->regbase, offset_to_bit(gpio), int_type); return 0; } static int gpio_irq_revert_type(int gpio_irq) { int gpio = irq_to_gpio(gpio_irq); struct gpio_bank *bank = gpio_to_bank(gpio); eGPIOIntType_t int_type = 0; int type; if (!bank) return -EINVAL; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; type = gpio_get_intr_type(bank->regbase, offset_to_bit(gpio)); switch (type) { case GPIOEdgelFalling: int_type = GPIOEdgelRising; break; case GPIOEdgelRising: int_type = GPIOEdgelFalling; break; case GPIOLevelHigh: int_type = GPIOLevelLow; break; case GPIOLevelLow: int_type = GPIOLevelHigh; break; default: return -EINVAL; } gpio_set_intr_type(bank->regbase, offset_to_bit(gpio), int_type); return 0; } static int gpio_irq_get_gpio_level(int gpio_irq) { int gpio = irq_to_gpio(gpio_irq); struct gpio_bank *bank = gpio_to_bank(gpio); if (!bank) return -EINVAL; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; return gpio_bit_rd(bank->regbase, GPIO_EXT_PORT, offset_to_bit(gpio)); } static int gpio_irq_enable(int gpio_irq) { int gpio = irq_to_gpio(gpio_irq); struct gpio_bank *bank = gpio_to_bank(gpio); if (!bank) return -EINVAL; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; gpio_irq_unmask(bank->regbase, offset_to_bit(gpio)); if (bank->use_count == 0) irq_handler_enable(IRQ_GPIO0 + bank->id); bank->use_count++; return 0; } static int gpio_irq_disable(int irq) { int gpio = irq_to_gpio(irq); struct gpio_bank *bank = gpio_to_bank(gpio); if (!bank) return -EINVAL; if (bank->use_count <= 0) return 0; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; gpio_irq_mask(bank->regbase, offset_to_bit(gpio)); if (bank->use_count == 1) irq_handler_disable(IRQ_GPIO0 + bank->id); bank->use_count--; return 0; } static int gpio_irq_init(void) { struct gpio_bank *bank = NULL; int i = 0; for (i = 0; i < GPIO_BANK_NUM; i++) { struct udevice *dev; dev = malloc(sizeof(*dev)); if (!dev) return -ENOMEM; bank = gpio_id_to_bank(i); if (bank) { dev->name = bank->name; /* disable gpio pin interrupt */ writel(0, bank->regbase + GPIO_INTEN); /* register gpio group irq handler */ irq_install_handler(IRQ_GPIO0 + bank->id, (interrupt_handler_t *)generic_gpio_handle_irq, dev); /* default disable all gpio group interrupt */ irq_handler_disable(IRQ_GPIO0 + bank->id); } } return 0; } static struct irq_chip gpio_irq_chip = { .name = "gpio-irq-chip", .irq_init = gpio_irq_init, .irq_enable = gpio_irq_enable, .irq_disable = gpio_irq_disable, .irq_set_type = gpio_irq_set_type, .irq_revert_type = gpio_irq_revert_type, .irq_get_gpio_level = gpio_irq_get_gpio_level, }; struct irq_chip *arch_gpio_get_irqchip(void) { return &gpio_irq_chip; }