From 4e6670fe63b534b8dfcf07b7547078f9298dd574 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Mon, 25 Sep 2017 14:44:10 +0800 Subject: [PATCH] drivers: add irq interrupt framework support This patch add support for IRQ interrupt, FIQ not included. It will be enabled when you select CONFIG_GICV2 or CONFIG_GICV3. The framework support gic interrupt and gpio interrupt, relative APIs are provided in: ./include/irq-platform.h If you'd like to add a new platform support into interrupt framework, please follow the steps: 1. add relative definitions in the file like other platforms: ./include/irq-platform.h 2. add GICD, GICC and GICR(for GICV3) base address definitions in the rkxxx-common.h, they are needed in: arch/arm/cpu/armv8/start.S; 3. enable CONFIG_GICV2 or CONFIG_GICV3. Notice: 1. the framework is initialize in function 'interrupt_init()' of _sequence_r[]. So you should not request irqs too early. 2. IRQ stack size is configured by CONFIG_IRQ_STACK_SIZE, the default value is 8KB when CONFIG_IRQ_STACK_SIZE is absent. Change-Id: I3d9e29873c9d64cd28aabd13a61111438c5902b0 Signed-off-by: Joseph Chen --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/irq/Kconfig | 6 + drivers/irq/Makefile | 10 ++ drivers/irq/irq-generic.c | 268 +++++++++++++++++++++++++++++++ drivers/irq/irq-gic.c | 289 ++++++++++++++++++++++++++++++++++ drivers/irq/irq-gic.h | 60 +++++++ drivers/irq/irq-gpio-switch.c | 161 +++++++++++++++++++ drivers/irq/irq-gpio-switch.h | 52 ++++++ drivers/irq/irq-gpio.c | 246 +++++++++++++++++++++++++++++ drivers/irq/irq-gpio.h | 12 ++ include/irq-generic.h | 83 ++++++++++ include/irq-platform.h | 110 +++++++++++++ 13 files changed, 1300 insertions(+) create mode 100644 drivers/irq/Kconfig create mode 100644 drivers/irq/Makefile create mode 100644 drivers/irq/irq-generic.c create mode 100644 drivers/irq/irq-gic.c create mode 100644 drivers/irq/irq-gic.h create mode 100644 drivers/irq/irq-gpio-switch.c create mode 100644 drivers/irq/irq-gpio-switch.h create mode 100644 drivers/irq/irq-gpio.c create mode 100644 drivers/irq/irq-gpio.h create mode 100644 include/irq-generic.h create mode 100644 include/irq-platform.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 613e60235d..baf2d5bac0 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -98,6 +98,8 @@ source "drivers/video/Kconfig" source "drivers/watchdog/Kconfig" +source "drivers/irq/Kconfig" + config PHYS_TO_BUS bool "Custom physical to bus address mapping" help diff --git a/drivers/Makefile b/drivers/Makefile index dab5c182c2..5edf096a3a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_$(SPL_TPL_)SERIAL_SUPPORT) += serial/ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/ obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/ obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ +obj-$(CONFIG_IRQ) += irq/ ifndef CONFIG_TPL_BUILD ifdef CONFIG_SPL_BUILD diff --git a/drivers/irq/Kconfig b/drivers/irq/Kconfig new file mode 100644 index 0000000000..090b4e1ceb --- /dev/null +++ b/drivers/irq/Kconfig @@ -0,0 +1,6 @@ +config IRQ + bool "IRQ support" + depends on GICV2 || GICV3 + default y + help + Provide IRQ support for platforms diff --git a/drivers/irq/Makefile b/drivers/irq/Makefile new file mode 100644 index 0000000000..244a65d123 --- /dev/null +++ b/drivers/irq/Makefile @@ -0,0 +1,10 @@ +# +# (C) Copyright 2017 Rockchip Electronics Co., Ltd. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += irq-gic.o +obj-y += irq-gpio.o +obj-y += irq-generic.o +obj-y += irq-gpio-switch.o \ No newline at end of file diff --git a/drivers/irq/irq-generic.c b/drivers/irq/irq-generic.c new file mode 100644 index 0000000000..93a557f857 --- /dev/null +++ b/drivers/irq/irq-generic.c @@ -0,0 +1,268 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "irq-gic.h" +#include "irq-gpio.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct irq_desc { + void (*handle_irq)(void *data); +}; + +static struct irq_desc irqs_desc[PLATFORM_MAX_IRQS_NR]; +static struct irq_chip *gic_irq_chip, *gpio_irq_chip; +static bool initialized; + +static int irq_bad(int irq) +{ + if (irq >= PLATFORM_MAX_IRQS_NR) { + printf("WARN: IRQ %d is out of max supported IRQ %d\n", + irq, PLATFORM_MAX_IRQS_NR); + return -EINVAL; + } + + if (!initialized) { + printf("WARN: Interrupt framework is not initialized\n"); + return -EINVAL; + } + + return 0; +} + +/* general interrupt handler for gpio chip */ +void _generic_gpio_handle_irq(int irq, void *data) +{ + if (irq_bad(irq)) + return; + + if (irq < PLATFORM_GIC_IRQS_NR) { + printf("WRAN: IRQ %d is not a GPIO irq\n", irq); + return; + } + + if (irqs_desc[irq].handle_irq) + irqs_desc[irq].handle_irq(data); +} + +void _do_generic_irq_handler(void) +{ + u32 irq = gic_irq_chip->irq_get(); + + if (irq < PLATFORM_GIC_IRQS_NR) { + if (irqs_desc[irq].handle_irq) + irqs_desc[irq].handle_irq((void *)(unsigned long)irq); + } + + gic_irq_chip->irq_eoi(irq); +} + +static int chip_irq_bad(struct irq_chip *chip) +{ + if (!chip->name || + !chip->irq_init || + !chip->irq_enable || + !chip->irq_disable || + !chip->irq_set_type) + return -EINVAL; + + return 0; +} + +static int _do_arch_irq_init(void) +{ + int irq, err = -EINVAL; + + /* After relocation done, bss data initialized */ + if (!(gd->flags & GD_FLG_RELOC)) { + printf("WARN: interrupt should be init after reloc\n"); + return -EINVAL; + } + + /* + * should set true before arch_gpio_irq_init(), otherwise + * can't request irqs for gpio banks. + */ + initialized = true; + + for (irq = 0; irq < PLATFORM_MAX_IRQS_NR; irq++) + irqs_desc[irq].handle_irq = NULL; + + gic_irq_chip = arch_gic_irq_init(); + if (chip_irq_bad(gic_irq_chip)) { + printf("ERROR: bad gic irq chip\n"); + goto out; + } + + gpio_irq_chip = arch_gpio_irq_init(); + if (chip_irq_bad(gpio_irq_chip)) { + printf("ERROR: bad gpio irq chip\n"); + goto out; + } + + err = gic_irq_chip->irq_init(); + if (err) { + printf("ERROR: gic interrupt init failed\n"); + goto out; + } + + err = gpio_irq_chip->irq_init(); + if (err) { + printf("ERROR: gpio interrupt init failed\n"); + goto out; + } + + return 0; + +out: + initialized = false; + + return err; +} + +int irq_handler_enable(int irq) +{ + if (irq_bad(irq)) + return -EINVAL; + + if (irq < PLATFORM_GIC_IRQS_NR) + return gic_irq_chip->irq_enable(irq); + else + return gpio_irq_chip->irq_enable(irq); +} + +int irq_handler_disable(int irq) +{ + if (irq_bad(irq)) + return -EINVAL; + + if (irq < PLATFORM_GIC_IRQS_NR) + return gic_irq_chip->irq_disable(irq); + else + return gpio_irq_chip->irq_disable(irq); +} + +int irq_set_irq_type(int irq, unsigned int type) +{ + if (irq_bad(irq)) + return -EINVAL; + + if (irq < PLATFORM_GIC_IRQS_NR) + return gic_irq_chip->irq_set_type(irq, type); + else + return gpio_irq_chip->irq_set_type(irq, type); +} + +void irq_install_handler(int irq, interrupt_handler_t *handler, void *data) +{ + if (irq_bad(irq)) + return; + + irqs_desc[irq].handle_irq = handler; +} + +void irq_free_handler(int irq) +{ + if (irq_bad(irq)) + return; + + irqs_desc[irq].handle_irq = NULL; +} + +#ifdef CONFIG_ARM64 +static void cpu_local_irq_enable(void) +{ + asm volatile("msr daifclr, #0x02"); +} + +static int cpu_local_irq_disable(void) +{ + asm volatile("msr daifset, #0x02"); + + return 0; +} + +void do_irq(struct pt_regs *pt_regs, unsigned int esr) +{ + _do_generic_irq_handler(); +} +#else +static void cpu_local_irq_enable(void) +{ + unsigned long cpsr; + + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0x80\n" + "msr cpsr_c, %0" + : "=r" (cpsr) : : "memory"); +} + +static int cpu_local_irq_disable(void) +{ + unsigned long old_cpsr, new_cpsr; + + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old_cpsr), "=r" (new_cpsr) + : + : "memory"); + + return (old_cpsr & 0x80) == 0; +} + +void do_irq(struct pt_regs *pt_regs) +{ + _do_generic_irq_handler(); +} +#endif + +int arch_interrupt_init(void) +{ +#ifndef CONFIG_ARM64 + unsigned long cpsr __maybe_unused; + + /* stack has been reserved in: arch_reserve_stacks() */ + IRQ_STACK_START = gd->irq_sp; + + __asm__ __volatile__("mrs %0, cpsr\n" + : "=r" (cpsr) + : + : "memory"); + + __asm__ __volatile__("msr cpsr_c, %0\n" + "mov sp, %1\n" + : + : "r" (IRQ_MODE | I_BIT | + F_BIT | (cpsr & ~FIQ_MODE)), + "r" (IRQ_STACK_START) + : "memory"); + + __asm__ __volatile__("msr cpsr_c, %0" + : + : "r" (cpsr) + : "memory"); +#endif + return _do_arch_irq_init(); +} + +int interrupt_init(void) +{ + return arch_interrupt_init(); +} + +void enable_interrupts(void) +{ + cpu_local_irq_enable(); +} + +int disable_interrupts(void) +{ + return cpu_local_irq_disable(); +} diff --git a/drivers/irq/irq-gic.c b/drivers/irq/irq-gic.c new file mode 100644 index 0000000000..3b1c91db9d --- /dev/null +++ b/drivers/irq/irq-gic.c @@ -0,0 +1,289 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "irq-gic.h" + +typedef enum INT_TRIG { + INT_LEVEL_TRIGGER, + INT_EDGE_TRIGGER +} eINT_TRIG; + +typedef enum INT_SECURE { + INT_SECURE, + INT_NOSECURE +} eINT_SECURE; + +typedef enum INT_SIGTYPE { + INT_SIGTYPE_IRQ, + INT_SIGTYPE_FIQ +} eINT_SIGTYPE; + +#define g_gicd ((pGICD_REG)GICD_BASE) +#define g_gicc ((pGICC_REG)GICC_BASE) + +__maybe_unused static u8 g_gic_cpumask = 0x01; + +static inline void int_set_prio_filter(u32 nprio) +{ + g_gicc->iccpmr = (nprio & 0xff); +} + +static inline void int_enable_distributor(void) +{ + g_gicd->icddcr = 0x01; +} + +static inline void int_disable_distributor(void) +{ + g_gicd->icddcr = 0x00; +} + +static inline void int_enable_secure_signal(void) +{ + g_gicc->iccicr |= 0x01; +} + +static inline void int_disable_secure_signal(void) +{ + g_gicc->iccicr &= (~0x01); +} + +static inline void int_enable_nosecure_signal(void) +{ + g_gicc->iccicr |= 0x02; +} + +static inline void int_disable_nosecure_signal(void) +{ + g_gicc->iccicr &= (~0x02); +} + +static int gic_irq_set_trigger(int irq, eINT_TRIG ntrig) +{ + u32 group, offset; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + group = irq / 16; + offset = irq % 16; + + if (ntrig == INT_LEVEL_TRIGGER) + g_gicd->icdicfr[group] &= (~(1 << (2 * offset + 1))); + else + g_gicd->icdicfr[group] |= (1 << (2 * offset + 1)); + + return 0; +} + +__maybe_unused static int gic_irq_set_pending(int irq) +{ + u32 group, offset; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + group = irq / 32; + offset = irq % 32; + g_gicd->icdispr[group] = (0x1 << offset); + + return 0; +} + +__maybe_unused static int gic_irq_clear_pending(int irq) +{ + u32 group, offset; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + group = irq / 32; + offset = irq % 32; + g_gicd->icdicpr[group] = (0x1 << offset); + + return 0; +} + +__maybe_unused static int gic_irq_set_secure(int irq, eINT_SECURE nsecure) +{ + u32 group, offset; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + group = irq / 32; + offset = irq % 32; + g_gicd->icdiser[group] |= nsecure << offset; + + return 0; +} + +__maybe_unused static u32 gic_get_cpumask(void) +{ + u32 mask = 0, i; + + for (i = mask = 0; i < 32; i += 4) { + mask = g_gicd->itargetsr[i]; + mask |= mask >> 16; + mask |= mask >> 8; + if (mask) + break; + } + + if (!mask) + printf("GIC CPU mask not found.\n"); + + debug("GIC CPU mask = 0x%08x\n", mask); + + return mask; +} + +static int gic_irq_enable(int irq) +{ +#ifdef CONFIG_GICV2 + u32 shift = (irq % 4) * 8; + u32 offset = (irq / 4); + u32 M, N; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + M = irq / 32; + N = irq % 32; + + g_gicc->iccicr &= (~0x08); + g_gicd->icdiser[M] = (0x1 << N); + g_gicd->itargetsr[offset] &= ~(0xFF << shift); + g_gicd->itargetsr[offset] |= (g_gic_cpumask << shift); +#else + u32 M, N; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + M = irq / 32; + N = irq % 32; + g_gicd->icdiser[M] = (0x1 << N); +#endif + + return 0; +} + +static int gic_irq_disable(int irq) +{ + u32 group, offset; + + if (irq >= PLATFORM_GIC_IRQS_NR) + return -EINVAL; + + group = irq / 32; + offset = irq % 32; + g_gicd->icdicer[group] = (0x1 << offset); + + return 0; +} + +/* + * irq_set_type - set the irq trigger type for an irq + * + * @irq: irq number + * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see asm/arch/irq.h + */ +static int gic_irq_set_type(int irq, unsigned int type) +{ + unsigned int int_type; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + int_type = INT_EDGE_TRIGGER; + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + int_type = INT_LEVEL_TRIGGER; + break; + default: + return -EINVAL; + } + + gic_irq_set_trigger(irq, int_type); + + return 0; +} + +static void gic_irq_eoi(int irq) +{ +#ifdef CONFIG_GICV2 + g_gicc->icceoir = irq; +#else + asm volatile("msr " __stringify(ICC_EOIR1_EL1) ", %0" + : : "r" ((u64)irq)); + asm volatile("msr " __stringify(ICC_DIR_EL1) ", %0" + : : "r" ((u64)irq)); + isb(); +#endif +} + +static int gic_irq_get(void) +{ +#ifdef CONFIG_GICV2 + return g_gicc->icciar & 0x3ff; /* bit9 - bit0 */ +#else + u64 irqstat; + + asm volatile("mrs %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); + return (u32)irqstat & 0x3ff; +#endif +} + +static int gic_irq_init(void) +{ + /* GICV3 done in: arch/arm/cpu/armv8/start.S */ +#ifdef CONFIG_GICV2 + /* end of interrupt */ + g_gicc->icceoir = PLATFORM_GIC_IRQS_NR; + + /* disable gicc and gicd */ + g_gicc->iccicr = 0x00; + g_gicd->icddcr = 0x00; + + /* enable interrupt */ + g_gicd->icdicer[0] = 0xFFFFFFFF; + g_gicd->icdicer[1] = 0xFFFFFFFF; + g_gicd->icdicer[2] = 0xFFFFFFFF; + g_gicd->icdicer[3] = 0xFFFFFFFF; + g_gicd->icdicfr[3] &= ~(1 << 1); + + /* set interrupt priority threhold min: 256 */ + int_set_prio_filter(0xff); + int_enable_secure_signal(); + int_enable_nosecure_signal(); + int_enable_distributor(); + + g_gic_cpumask = gic_get_cpumask(); +#endif + + return 0; +} + +static struct irq_chip gic_irq_chip = { + .name = "gic-irq-chip", + .irq_init = gic_irq_init, + .irq_get = gic_irq_get, + .irq_enable = gic_irq_enable, + .irq_disable = gic_irq_disable, + .irq_eoi = gic_irq_eoi, + .irq_set_type = gic_irq_set_type, +}; + +struct irq_chip *arch_gic_irq_init(void) +{ + return &gic_irq_chip; +} diff --git a/drivers/irq/irq-gic.h b/drivers/irq/irq-gic.h new file mode 100644 index 0000000000..4bd53c2092 --- /dev/null +++ b/drivers/irq/irq-gic.h @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IRQ_GIC_H_ +#define _IRQ_GIC_H_ + +#include + +/* INTC Registers */ +typedef volatile struct tagGICD_REG { + u32 icddcr; /* 0x000 */ + u32 icdictr; /* 0x004 */ + u32 icdiidr; /* 0x008 */ + u32 reserved0[29]; + u32 icdisr[4]; /* 0x080 */ + u32 reserved1[28]; + u32 icdiser[4]; /* 0x100 */ + u32 reserved2[28]; + u32 icdicer[4]; /* 0x180: GICD_ISENABLERn */ + u32 reserved3[28]; + u32 icdispr[4]; /* 0x200 */ + u32 reserved4[28]; + u32 icdicpr[4]; /* 0x280 */ + u32 reserved5[28]; + u32 icdiabr[4]; /* 0x300 */ + u32 reserved6[60]; + u32 icdipr_sgi[4]; /* 0x400 */ + u32 icdipr_ppi[4]; /* 0x410 */ + u32 icdipr_spi[18]; /* 0x420 */ + u32 reserved7[230]; + u32 itargetsr[255]; /* 0x800 */ + u32 reserved9[1]; + u32 icdicfr[7]; /* 0xc00: GICD_ICFGRn: trigger level/edge */ + u32 reserved8[185]; + u32 icdsgir; /* 0xf00 */ +} GICD_REG, *pGICD_REG; + +typedef volatile struct tagGICC_REG { + u32 iccicr; /* 0x00 */ + u32 iccpmr; /* 0x04: GICC_PMR */ + u32 iccbpr; /* 0x08 */ + u32 icciar; /* 0x0c */ + u32 icceoir; /* 0x10 */ + u32 iccrpr; /* 0x14 */ + u32 icchpir; /* 0x18 */ + u32 iccabpr; /* 0x1c */ + u32 reserved0[55]; + u32 icciidr; /* 0xfc */ +} GICC_REG, *pGICC_REG; + +#define PLATFORM_GIC_IRQS_NR GIC_IRQS_NR +#define PLATFORM_GPIO_IRQS_NR GPIO_IRQS_NR +#define PLATFORM_MAX_IRQS_NR (GIC_IRQS_NR + GPIO_IRQS_NR) + +struct irq_chip *arch_gic_irq_init(void); + +#endif /* _IRQ_GIC_H_ */ diff --git a/drivers/irq/irq-gpio-switch.c b/drivers/irq/irq-gpio-switch.c new file mode 100644 index 0000000000..79c78034cf --- /dev/null +++ b/drivers/irq/irq-gpio-switch.c @@ -0,0 +1,161 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include "irq-gpio-switch.h" + +static struct gpio_bank gpio_banks[GPIO_BANK_NUM] = { +#if GPIO_BANK_NUM >= 1 + GPIO_BANK_REGISTER(0, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 2 + GPIO_BANK_REGISTER(1, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 3 + GPIO_BANK_REGISTER(2, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 4 + GPIO_BANK_REGISTER(3, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 5 + GPIO_BANK_REGISTER(4, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 6 + GPIO_BANK_REGISTER(5, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 7 + GPIO_BANK_REGISTER(6, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 8 + GPIO_BANK_REGISTER(7, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 9 + GPIO_BANK_REGISTER(8, GPIO_BANK_PINS), +#endif +#if GPIO_BANK_NUM >= 10 + GPIO_BANK_REGISTER(9, GPIO_BANK_PINS), +#endif +}; + +static int gpio_is_valid(u32 gpio) +{ + if ((gpio == EINVAL_GPIO) || !GPIO_BANK_VALID(gpio) || + !GPIO_PIN_VALID(gpio)) { + printf("gpio = 0x%x is not valid!\n", gpio); + return 0; + } + + return 1; +} + +static int _hard_gpio_to_irq(u32 gpio) +{ + int idx, bank = 0, pin = 0; + + if (!gpio_is_valid(gpio)) + return -EINVAL; + + bank = (gpio & GPIO_BANK_MASK) >> GPIO_BANK_OFFSET; + pin = (gpio & GPIO_PIN_MASK) >> GPIO_PIN_OFFSET; + + for (idx = 0; idx < ARRAY_SIZE(gpio_banks); idx++) { + if (gpio_banks[idx].id == bank) + return (gpio_banks[idx].irq_base + pin); + } + + return -EINVAL; +} + +static int _irq_to_gpio(int irq) +{ + int bank, pin, idx; + + bank = (irq - PIN_BASE) / GPIO_BANK_PINS; + pin = (irq - PIN_BASE) % GPIO_BANK_PINS; + + for (idx = 0; idx < ARRAY_SIZE(gpio_banks); idx++) { + if (gpio_banks[idx].id == bank) { + return (bank << GPIO_BANK_OFFSET) | + (pin << GPIO_PIN_OFFSET); + } + } + + return -EINVAL; +} + +int gpio_to_irq(struct gpio_desc *gpio) +{ + int irq_gpio, bank; + bool found; + char *name; + + if (!gpio->dev->name) { + printf("can't find device name for the gpio bank\n"); + return EINVAL_GPIO; + } + + name = strtok((char *)gpio->dev->name, "@"); + if (!name) { + printf("can't find correct device name for the gpio bank\n"); + return EINVAL_GPIO; + } + + for (bank = 0; bank < ARRAY_SIZE(gpio_banks); bank++) { + if (!strcmp(gpio_banks[bank].name, name)) { + found = true; + break; + } + } + + if (!found) { + printf("irq gpio framework can't find %s\n", name); + return EINVAL_GPIO; + } + + irq_gpio = RK_IRQ_GPIO(bank, gpio->offset); + if (!gpio_is_valid(irq_gpio)) + return EINVAL_GPIO; + + return _hard_gpio_to_irq(irq_gpio); +} + +int hard_gpio_to_irq(u32 gpio) +{ + if (!gpio_is_valid(gpio)) + return EINVAL_GPIO; + + return _hard_gpio_to_irq(gpio); +} + +int irq_to_gpio(int irq) +{ + return _irq_to_gpio(irq); +} + +struct gpio_bank *gpio_id_to_bank(u32 id) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(gpio_banks); idx++) { + if (gpio_banks[idx].id == id) + return &gpio_banks[idx]; + } + + return NULL; +} + +struct gpio_bank *gpio_to_bank(u32 gpio) +{ + int id; + + if (!gpio_is_valid(gpio)) + return NULL; + + id = (gpio & GPIO_BANK_MASK) >> GPIO_BANK_OFFSET; + + return gpio_id_to_bank(id); +} diff --git a/drivers/irq/irq-gpio-switch.h b/drivers/irq/irq-gpio-switch.h new file mode 100644 index 0000000000..1442825132 --- /dev/null +++ b/drivers/irq/irq-gpio-switch.h @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IRQ_GPIO_SWITCH_H_ +#define _IRQ_GPIO_SWITCH_H_ + +#include +#include +#include + +/* bank and pin bit mask */ +#define GPIO_BANK_MASK 0xFFFFFF00 +#define GPIO_BANK_OFFSET 8 +#define GPIO_PIN_MASK 0x000000FF +#define GPIO_PIN_OFFSET 0 + +#define EINVAL_GPIO -1 +#define PIN_BASE GIC_IRQS_NR + +struct gpio_bank { + char *name; + void __iomem *regbase; + int id; + int irq_base; + int ngpio; +}; + +#define GPIO_BANK_REGISTER(ID, GPIO_BANK_NUM) \ + { \ + .name = __stringify(gpio##ID), \ + .regbase = (unsigned char __iomem *)GPIO##ID##_PHYS, \ + .id = ID, \ + .irq_base = PIN_BASE + (ID) * (GPIO_BANK_NUM), \ + .ngpio = GPIO_BANK_NUM, \ + } + +/* gpio bank[31:8] and pin[7:0] */ +#define GPIO_BANK(gpio) ((gpio & GPIO_BANK_MASK) >> GPIO_BANK_OFFSET) +#define GPIO_PIN(gpio) ((gpio & GPIO_PIN_MASK) >> GPIO_PIN_OFFSET) +#define GPIO_BANK_VALID(gpio) (GPIO_BANK(gpio) < GPIO_BANK_NUM) +#define GPIO_PIN_VALID(gpio) (GPIO_PIN(gpio) < GPIO_BANK_PINS) + +int hard_gpio_to_irq(u32 gpio); +int irq_to_gpio(int irq); + +struct gpio_bank *gpio_id_to_bank(unsigned int id); +struct gpio_bank *gpio_to_bank(unsigned gpio); + +#endif /* _IRQ_GPIO_SWITCH_H_ */ diff --git a/drivers/irq/irq-gpio.c b/drivers/irq/irq-gpio.c new file mode 100644 index 0000000000..b331ffeb7f --- /dev/null +++ b/drivers/irq/irq-gpio.c @@ -0,0 +1,246 @@ +/* + * (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; +} diff --git a/drivers/irq/irq-gpio.h b/drivers/irq/irq-gpio.h new file mode 100644 index 0000000000..78aa1e8adb --- /dev/null +++ b/drivers/irq/irq-gpio.h @@ -0,0 +1,12 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IRQ_GPIO_H_ +#define _IRQ_GPIO_H_ + +struct irq_chip *arch_gpio_irq_init(void); + +#endif /* _IRQ_GPIO_H_ */ diff --git a/include/irq-generic.h b/include/irq-generic.h new file mode 100644 index 0000000000..6dfed10095 --- /dev/null +++ b/include/irq-generic.h @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IRQ_GENERIC_H +#define _IRQ_GENERIC_H + +#include +#include +#include + +/* + * IRQ line status. + * + * IRQ_TYPE_NONE - default, unspecified type + * IRQ_TYPE_EDGE_RISING - rising edge triggered + * IRQ_TYPE_EDGE_FALLING - falling edge triggered + * IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered + * IRQ_TYPE_LEVEL_HIGH - high level triggered + * IRQ_TYPE_LEVEL_LOW - low level triggered + * IRQ_TYPE_LEVEL_MASK - mask to filter out the level bits + * IRQ_TYPE_SENSE_MASK - mask for all the above bits + */ +enum { + IRQ_TYPE_NONE = 0x00000000, + IRQ_TYPE_EDGE_RISING = 0x00000001, + IRQ_TYPE_EDGE_FALLING = 0x00000002, + IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING), + IRQ_TYPE_LEVEL_HIGH = 0x00000004, + IRQ_TYPE_LEVEL_LOW = 0x00000008, + IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), + IRQ_TYPE_SENSE_MASK = 0x0000000f, +}; + +/* + * struct irq_chip - hardware interrupt chip descriptor + * + * @name: name for irq chip + * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL) + * @irq_disable: disable the interrupt + * @irq_ack: start of a new interrupt + * @irq_eoi: end of interrupt + * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ + */ +struct irq_chip { + const char *name; + int (*irq_init)(void); + int (*irq_get)(void); + int (*irq_enable)(int irq); + int (*irq_disable)(int irq); + void (*irq_ack)(int irq); + void (*irq_eoi)(int irq); + int (*irq_set_type)(int irq, unsigned int flow_type); +}; + +/* APIs for irqs */ +void irq_install_handler(int irq, interrupt_handler_t *handler, void *data); +void irq_free_handler(int irq); +int irq_set_irq_type(int irq, unsigned int type); +int irq_handler_enable(int irq); +int irq_handler_disable(int irq); +int gpio_to_irq(struct gpio_desc *gpio); + +/* + * Assign gpio to irq directly. Don't use it without special reasons. + * + * Usage example: + * int gpio0_a0, irq; + * + * gpio = RK_IRQ_GPIO(RK_GPIO0, RK_PA0); + * irq = hard_gpio_to_irq(gpio0_a0); + * irq_install_handler(irq, ...); + */ +#define GPIO_BANK_SHIFT 8 +#define RK_IRQ_GPIO(bank, pin) (((bank) << GPIO_BANK_SHIFT) | (pin)) +int hard_gpio_to_irq(unsigned gpio); + +/* only irq-gpio.c can use it */ +void _generic_gpio_handle_irq(int irq, void *data); + +#endif /* _IRQ_GENERIC_H */ diff --git a/include/irq-platform.h b/include/irq-platform.h new file mode 100644 index 0000000000..e3d16429ac --- /dev/null +++ b/include/irq-platform.h @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ROCKCHIP_PLAT_IRQ_H_ +#define _ROCKCHIP_PLAT_IRQ_H_ + +#if defined(CONFIG_ROCKCHIP_RK322X) +#define GPIO0_PHYS 0x11110000 +#define GPIO1_PHYS 0x11120000 +#define GPIO2_PHYS 0x11130000 +#define GPIO3_PHYS 0x11140000 + +#define GIC_IRQS_NR (4 * 32) +#define GPIO_IRQS_NR (4 * 32) + +#define GPIO_BANK_NUM 4 +#define GPIO_BANK_PINS 32 + +#define IRQ_GPIO0 83 +#define IRQ_GPIO1 84 +#define IRQ_GPIO2 85 +#define IRQ_GPIO3 86 + +#elif defined(CONFIG_ROCKCHIP_RK3288) +#define GPIO0_PHYS 0xFF750000 +#define GPIO1_PHYS 0xFF780000 +#define GPIO2_PHYS 0xFF790000 +#define GPIO3_PHYS 0xFF7A0000 +#define GPIO4_PHYS 0xFF7B0000 +#define GPIO5_PHYS 0xFF7C0000 +#define GPIO6_PHYS 0xFF7D0000 +#define GPIO7_PHYS 0xFF7E0000 +#define GPIO8_PHYS 0xFF7F0000 + +#define GIC_IRQS_NR (5 * 32) +#define GPIO_IRQS_NR (9 * 32) + +#define GPIO_BANK_NUM 9 +#define GPIO_BANK_PINS 32 + +#define IRQ_GPIO0 113 +#define IRQ_GPIO1 114 +#define IRQ_GPIO2 115 +#define IRQ_GPIO3 116 +#define IRQ_GPIO4 117 +#define IRQ_GPIO5 118 +#define IRQ_GPIO6 119 +#define IRQ_GPIO7 120 +#define IRQ_GPIO8 121 + +#elif defined(CONFIG_ROCKCHIP_RK3328) +#define GPIO0_PHYS 0xFF210000 +#define GPIO1_PHYS 0xFF220000 +#define GPIO2_PHYS 0xFF230000 +#define GPIO3_PHYS 0xFF240000 + +#define GIC_IRQS_NR (4 * 32) +#define GPIO_IRQS_NR (4 * 32) + +#define GPIO_BANK_NUM 4 +#define GPIO_BANK_PINS 32 + +#define IRQ_GPIO0 83 +#define IRQ_GPIO1 84 +#define IRQ_GPIO2 85 +#define IRQ_GPIO3 86 + +#elif defined(CONFIG_ROCKCHIP_RK3368) +#define GPIO0_PHYS 0xFF750000 +#define GPIO1_PHYS 0xFF780000 +#define GPIO2_PHYS 0xFF790000 +#define GPIO3_PHYS 0xFF7A0000 + +#define GIC_IRQS_NR (5 * 32) +#define GPIO_IRQS_NR (4 * 32) + +#define GPIO_BANK_NUM 4 +#define GPIO_BANK_PINS 32 + +#define IRQ_GPIO0 113 +#define IRQ_GPIO1 114 +#define IRQ_GPIO2 115 +#define IRQ_GPIO3 116 + +#elif defined(CONFIG_ROCKCHIP_RK3399) +#define GPIO0_PHYS 0xFF720000 +#define GPIO1_PHYS 0xFF730000 +#define GPIO2_PHYS 0xFF780000 +#define GPIO3_PHYS 0xFF788000 +#define GPIO4_PHYS 0xFF790000 + +#define IRQ_GPIO0 46 +#define IRQ_GPIO1 47 +#define IRQ_GPIO2 48 +#define IRQ_GPIO3 49 +#define IRQ_GPIO4 50 + +#define GIC_IRQS_NR (6 * 32) +#define GPIO_IRQS_NR (5 * 32) + +#define GPIO_BANK_NUM 5 +#define GPIO_BANK_PINS 32 +#else +"Missing define RIQ relative things" +#endif + +#endif /* _ROCKCHIP_PLAT_IRQ_H_ */