diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi index 3d113bd5e26c..6dda19e9c402 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi @@ -420,7 +420,7 @@ reg = <0x0 0x50>; interrupt-controller; - #interrupt-cells = <2>; + #interrupt-cells = <1>; interrupt-parent = <&gpio5>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; @@ -431,8 +431,12 @@ reg = <0x10 0x08>; gpio-controller; #gpio-cells = <2>; + + interrupt-parent = <&fpga_intc>; + interrupts = <30 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; gpio-line-names = "", "DIO_PIN1", "DIO_PIN3", "DIO_PIN5", "DIO_PIN7", "DIO_PIN8", @@ -450,6 +454,9 @@ interrupt-controller; #interrupt-cells = <1>; + interrupt-parent = <&fpga_intc>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + gpio-line-names = "ISA_AEN", "ISA_BALE", "ISA_TC", "ISA_ENDX", "EN_NIMBEL_3V3", "ISA_IORDY", "ISA_REFRESH", "ISA_DRQ1", diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 28c83ab8dea6..da0ce63ad30c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -795,6 +795,7 @@ config GPIO_LOONGSON1 config GPIO_TS71XXWEIM bool "embeddedTS WEIM FPGA GPIO" depends on IMX_WEIM + select GPIOLIB_IRQCHIP help Say yes here to enable the GPIO driver for embeddedTS's FPGA core connected to the i.MX6UL WEIM bus. diff --git a/drivers/gpio/gpio-ts71xxweim.c b/drivers/gpio/gpio-ts71xxweim.c index 069bc5adbf38..61a8dd04af58 100644 --- a/drivers/gpio/gpio-ts71xxweim.c +++ b/drivers/gpio/gpio-ts71xxweim.c @@ -6,24 +6,40 @@ #include #include +#include #include #include +#include +#include + /* Most that this driver can currently support in a single bank is 16. This is * due simply to how the FPGA used for these devices is structured. */ -#define TSWEIM_NR_DIO 16 +#define TSWEIM_NR_DIO 16 /* Register offsets from the 'reg' value passed in device tree source */ -#define TSWEIM_SET_REG 0x00 -#define TSWEIM_GET_REG 0x00 -#define TSWEIM_EN_SET_REG 0x02 -#define TSWEIM_CLR_REG 0x04 -#define TSWEIM_EN_CLR_REG 0x06 +#define TSWEIM_SET_REG 0x00 +#define TSWEIM_GET_REG 0x00 +#define TSWEIM_EN_SET_REG 0x02 +#define TSWEIM_CLR_REG 0x04 +#define TSWEIM_EN_CLR_REG 0x06 + +/* The IRQ registers are from a second register range */ +#define TSWEIM_IRQ_ACK 0x0 +#define TSWEIM_IRQ_ACK_MASK 0x2 +#define TSWEIM_IRQ_EDGE_LEVEL 0x4 +#define TSWEIM_IRQ_EDGE_SEL 0x6 +#define TSWEIM_IRQ_POLARITY 0x8 +#define TSWEIM_IRQ_MASK 0xA +#define TSWEIM_IRQ_PENDING 0xC struct tsweim_gpio_priv { - void __iomem *syscon; + void __iomem *syscon; + void __iomem *irqregs; struct gpio_chip gpio_chip; + struct irq_chip irqchip; + int irq; }; static inline struct tsweim_gpio_priv *to_gpio_tsweim(struct gpio_chip *chip) @@ -32,7 +48,7 @@ static inline struct tsweim_gpio_priv *to_gpio_tsweim(struct gpio_chip *chip) } static int tsweim_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) + unsigned int offset) { struct tsweim_gpio_priv *priv = to_gpio_tsweim(chip); @@ -75,7 +91,7 @@ static int tsweim_gpio_get(struct gpio_chip *chip, unsigned int offset) } static void tsweim_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) + int value) { struct tsweim_gpio_priv *priv = to_gpio_tsweim(chip); @@ -89,26 +105,161 @@ static void tsweim_gpio_set(struct gpio_chip *chip, unsigned int offset, } static const struct gpio_chip template_chip = { - .owner = THIS_MODULE, - .direction_input = tsweim_gpio_direction_input, - .direction_output = tsweim_gpio_direction_output, - .get = tsweim_gpio_get, - .set = tsweim_gpio_set, - .base = -1, - .can_sleep = false, + .owner = THIS_MODULE, + .direction_input = tsweim_gpio_direction_input, + .direction_output = tsweim_gpio_direction_output, + .get = tsweim_gpio_get, + .set = tsweim_gpio_set, + .base = -1, + .can_sleep = false, }; static const struct of_device_id tsweim_gpio_of_match_table[] = { - { .compatible = "technologic,ts71xxweim-gpio", }, + { + .compatible = "technologic,ts71xxweim-gpio", + }, {}, }; MODULE_DEVICE_TABLE(of, tsweim_gpio_of_match_table); +static void gpio_tsweim_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct tsweim_gpio_priv *priv = gpiochip_get_data(chip); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long pending; + u32 bit; + + pending = readw(priv->irqregs + TSWEIM_IRQ_PENDING); + chained_irq_enter(irqchip, desc); + for_each_set_bit(bit, &pending, 32) { + generic_handle_domain_irq(chip->irq.domain, bit); + } + chained_irq_exit(irqchip, desc); +} + +static void gpio_tsweim_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + u16 reg = BIT(irqd_to_hwirq(d)); + + writew(reg, priv->irqregs + TSWEIM_IRQ_ACK); +} + +static void gpio_tsweim_irq_mask_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + u16 reg = BIT(irqd_to_hwirq(d)); + + writew(reg, priv->irqregs + TSWEIM_IRQ_ACK_MASK); + gpiochip_disable_irq(gc, d->hwirq); +} + +static void gpio_tsweim_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + u16 mask; + + mask = readw(priv->irqregs + TSWEIM_IRQ_MASK); + writew(mask | BIT(irqd_to_hwirq(d)), priv->irqregs + TSWEIM_IRQ_MASK); + gpiochip_disable_irq(gc, d->hwirq); +} + +static void gpio_tsweim_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + u16 mask; + + mask = readw(priv->irqregs + TSWEIM_IRQ_MASK); + mask = mask & ~BIT(irqd_to_hwirq(d)); + writew(mask, priv->irqregs + TSWEIM_IRQ_ACK); + gpiochip_disable_irq(gc, d->hwirq); +} + +static int gpio_tsweim_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct tsweim_gpio_priv *priv = gpiochip_get_data(gc); + unsigned int hwirq = irqd_to_hwirq(d); + u32 polarity, edge, edge_sel; + int ret = 0; + + polarity = readw(priv->syscon + TSWEIM_IRQ_POLARITY); + edge = readw(priv->syscon + TSWEIM_IRQ_EDGE_LEVEL); + edge_sel = readw(priv->syscon + TSWEIM_IRQ_EDGE_SEL); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_LEVEL_HIGH: + edge &= ~BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity |= BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + edge &= ~BIT(hwirq); + polarity &= ~BIT(hwirq); + edge_sel &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_level_irq); + break; + case IRQ_TYPE_EDGE_RISING: + edge |= BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity |= BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + edge |= BIT(hwirq); + edge_sel &= ~BIT(hwirq); + polarity &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + edge |= BIT(hwirq); + edge_sel |= BIT(hwirq); + polarity &= ~BIT(hwirq); + irq_set_handler_locked(d, handle_edge_irq); + break; + default: + ret = -EINVAL; + } + + writew(polarity, priv->syscon + TSWEIM_IRQ_POLARITY); + writew(edge, priv->syscon + TSWEIM_IRQ_EDGE_LEVEL); + writew(edge_sel, priv->syscon + TSWEIM_IRQ_EDGE_SEL); + + printk("GPIO TSWEIM: set_type hwirq=%u type=0x%x\n", hwirq, type); + return ret; +} + +static void gpio_tsweim_irq_print_chip(struct irq_data *data, + struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + + seq_printf(p, dev_name(gc->parent)); +} + +static const struct irq_chip tsweim_irq_chip = { + .irq_ack = gpio_tsweim_irq_ack, + .irq_mask = gpio_tsweim_irq_mask, + .irq_mask_ack = gpio_tsweim_irq_mask_ack, + .irq_unmask = gpio_tsweim_irq_unmask, + .irq_set_type = gpio_tsweim_irq_set_type, + .irq_print_chip = gpio_tsweim_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + static int tsweim_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tsweim_gpio_priv *priv; - void __iomem *membase; + struct gpio_irq_chip *girq; + void __iomem *membase; struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -117,8 +268,7 @@ static int tsweim_gpio_probe(struct platform_device *pdev) return -EFAULT; } - membase = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (IS_ERR(membase)) { pr_err("Could not map resource\n"); return -ENOMEM; @@ -136,6 +286,19 @@ static int tsweim_gpio_probe(struct platform_device *pdev) priv->gpio_chip.parent = dev; pdev->dev.platform_data = &priv; + if (priv->irq >= 0) { + girq = &priv->gpio_chip.irq; + gpio_irq_chip_set_chip(girq, &tsweim_irq_chip); + girq->parent_handler = gpio_tsweim_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc( + &pdev->dev, 1, sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = priv->irq; + girq->handler = handle_bad_irq; + } + return devm_gpiochip_add_data(&pdev->dev, &priv->gpio_chip, &priv); }