/* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include "irq-gpio.h" #include "irq-gpio-switch.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 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) { 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 edges */ if (ilr & (1 << pin)) { unmasked = 1; gpio_irq_unmask(bank->regbase, offset_to_bit(pin)); } _generic_gpio_handle_irq(gpio_irq + pin, NULL); 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_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_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)); 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; gpio &= GPIO_PIN_MASK; if (gpio >= bank->ngpio) return -EINVAL; gpio_irq_mask(bank->regbase, offset_to_bit(gpio)); return 0; } static int gpio_irq_init(void) { struct gpio_bank *bank = NULL; int i = 0; for (i = 0; i < GPIO_BANK_NUM; i++) { bank = gpio_id_to_bank(i); if (bank) { /* 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, NULL); /* default enable all gpio group interrupt */ irq_handler_enable(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, }; struct irq_chip *arch_gpio_irq_init(void) { return &gpio_irq_chip; }