You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

353 lines
7.2 KiB

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