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 <chenjh@rock-chips.com>
This commit is contained in:
Joseph Chen 2017-09-25 14:44:10 +08:00 committed by Kever Yang
parent 27a50c86ba
commit 4e6670fe63
13 changed files with 1300 additions and 0 deletions

View File

@ -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

View File

@ -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

6
drivers/irq/Kconfig Normal file
View File

@ -0,0 +1,6 @@
config IRQ
bool "IRQ support"
depends on GICV2 || GICV3
default y
help
Provide IRQ support for platforms

10
drivers/irq/Makefile Normal file
View File

@ -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

268
drivers/irq/irq-generic.c Normal file
View File

@ -0,0 +1,268 @@
/*
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/io.h>
#include <asm/u-boot-arm.h>
#include <irq-generic.h>
#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();
}

289
drivers/irq/irq-gic.c Normal file
View File

@ -0,0 +1,289 @@
/*
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/io.h>
#include <asm/gic.h>
#include <config.h>
#include <irq-generic.h>
#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;
}

60
drivers/irq/irq-gic.h Normal file
View File

@ -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 <irq-platform.h>
/* 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_ */

View File

@ -0,0 +1,161 @@
/*
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <dm.h>
#include <irq-generic.h>
#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);
}

View File

@ -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 <asm/io.h>
#include <common.h>
#include <irq-platform.h>
/* 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_ */

246
drivers/irq/irq-gpio.c Normal file
View File

@ -0,0 +1,246 @@
/*
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/io.h>
#include <irq-generic.h>
#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;
}

12
drivers/irq/irq-gpio.h Normal file
View File

@ -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_ */

83
include/irq-generic.h Normal file
View File

@ -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 <asm-generic/gpio.h>
#include <common.h>
#include <dt-bindings/pinctrl/rockchip.h>
/*
* 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 */

110
include/irq-platform.h Normal file
View File

@ -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_ */