1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26ee532e2SLinus Walleij /*
36ee532e2SLinus Walleij * irqchip for the Faraday Technology FTINTC010 Copyright (C) 2017 Linus
46ee532e2SLinus Walleij * Walleij <linus.walleij@linaro.org>
56ee532e2SLinus Walleij *
66ee532e2SLinus Walleij * Based on arch/arm/mach-gemini/irq.c
76ee532e2SLinus Walleij * Copyright (C) 2001-2006 Storlink, Corp.
86ee532e2SLinus Walleij * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@gmail.com>
96ee532e2SLinus Walleij */
106ee532e2SLinus Walleij #include <linux/bitops.h>
116ee532e2SLinus Walleij #include <linux/irq.h>
126ee532e2SLinus Walleij #include <linux/io.h>
136ee532e2SLinus Walleij #include <linux/irqchip.h>
146ee532e2SLinus Walleij #include <linux/irqdomain.h>
156ee532e2SLinus Walleij #include <linux/module.h>
166ee532e2SLinus Walleij #include <linux/of.h>
176ee532e2SLinus Walleij #include <linux/of_address.h>
186ee532e2SLinus Walleij #include <linux/of_irq.h>
196ee532e2SLinus Walleij #include <linux/cpu.h>
206ee532e2SLinus Walleij
216ee532e2SLinus Walleij #include <asm/exception.h>
226ee532e2SLinus Walleij #include <asm/mach/irq.h>
236ee532e2SLinus Walleij
246ee532e2SLinus Walleij #define FT010_NUM_IRQS 32
256ee532e2SLinus Walleij
266ee532e2SLinus Walleij #define FT010_IRQ_SOURCE(base_addr) (base_addr + 0x00)
276ee532e2SLinus Walleij #define FT010_IRQ_MASK(base_addr) (base_addr + 0x04)
286ee532e2SLinus Walleij #define FT010_IRQ_CLEAR(base_addr) (base_addr + 0x08)
29d2d55ab8SLinus Walleij /* Selects level- or edge-triggered */
306ee532e2SLinus Walleij #define FT010_IRQ_MODE(base_addr) (base_addr + 0x0C)
31d2d55ab8SLinus Walleij /* Selects active low/high or falling/rising edge */
326ee532e2SLinus Walleij #define FT010_IRQ_POLARITY(base_addr) (base_addr + 0x10)
336ee532e2SLinus Walleij #define FT010_IRQ_STATUS(base_addr) (base_addr + 0x14)
346ee532e2SLinus Walleij #define FT010_FIQ_SOURCE(base_addr) (base_addr + 0x20)
356ee532e2SLinus Walleij #define FT010_FIQ_MASK(base_addr) (base_addr + 0x24)
366ee532e2SLinus Walleij #define FT010_FIQ_CLEAR(base_addr) (base_addr + 0x28)
376ee532e2SLinus Walleij #define FT010_FIQ_MODE(base_addr) (base_addr + 0x2C)
386ee532e2SLinus Walleij #define FT010_FIQ_POLARITY(base_addr) (base_addr + 0x30)
396ee532e2SLinus Walleij #define FT010_FIQ_STATUS(base_addr) (base_addr + 0x34)
406ee532e2SLinus Walleij
416ee532e2SLinus Walleij /**
426ee532e2SLinus Walleij * struct ft010_irq_data - irq data container for the Faraday IRQ controller
436ee532e2SLinus Walleij * @base: memory offset in virtual memory
446ee532e2SLinus Walleij * @chip: chip container for this instance
456ee532e2SLinus Walleij * @domain: IRQ domain for this instance
466ee532e2SLinus Walleij */
476ee532e2SLinus Walleij struct ft010_irq_data {
486ee532e2SLinus Walleij void __iomem *base;
496ee532e2SLinus Walleij struct irq_chip chip;
506ee532e2SLinus Walleij struct irq_domain *domain;
516ee532e2SLinus Walleij };
526ee532e2SLinus Walleij
ft010_irq_mask(struct irq_data * d)536ee532e2SLinus Walleij static void ft010_irq_mask(struct irq_data *d)
546ee532e2SLinus Walleij {
556ee532e2SLinus Walleij struct ft010_irq_data *f = irq_data_get_irq_chip_data(d);
566ee532e2SLinus Walleij unsigned int mask;
576ee532e2SLinus Walleij
586ee532e2SLinus Walleij mask = readl(FT010_IRQ_MASK(f->base));
596ee532e2SLinus Walleij mask &= ~BIT(irqd_to_hwirq(d));
606ee532e2SLinus Walleij writel(mask, FT010_IRQ_MASK(f->base));
616ee532e2SLinus Walleij }
626ee532e2SLinus Walleij
ft010_irq_unmask(struct irq_data * d)636ee532e2SLinus Walleij static void ft010_irq_unmask(struct irq_data *d)
646ee532e2SLinus Walleij {
656ee532e2SLinus Walleij struct ft010_irq_data *f = irq_data_get_irq_chip_data(d);
666ee532e2SLinus Walleij unsigned int mask;
676ee532e2SLinus Walleij
686ee532e2SLinus Walleij mask = readl(FT010_IRQ_MASK(f->base));
696ee532e2SLinus Walleij mask |= BIT(irqd_to_hwirq(d));
706ee532e2SLinus Walleij writel(mask, FT010_IRQ_MASK(f->base));
716ee532e2SLinus Walleij }
726ee532e2SLinus Walleij
ft010_irq_ack(struct irq_data * d)736ee532e2SLinus Walleij static void ft010_irq_ack(struct irq_data *d)
746ee532e2SLinus Walleij {
756ee532e2SLinus Walleij struct ft010_irq_data *f = irq_data_get_irq_chip_data(d);
766ee532e2SLinus Walleij
776ee532e2SLinus Walleij writel(BIT(irqd_to_hwirq(d)), FT010_IRQ_CLEAR(f->base));
786ee532e2SLinus Walleij }
796ee532e2SLinus Walleij
ft010_irq_set_type(struct irq_data * d,unsigned int trigger)806ee532e2SLinus Walleij static int ft010_irq_set_type(struct irq_data *d, unsigned int trigger)
816ee532e2SLinus Walleij {
826ee532e2SLinus Walleij struct ft010_irq_data *f = irq_data_get_irq_chip_data(d);
836ee532e2SLinus Walleij int offset = irqd_to_hwirq(d);
846ee532e2SLinus Walleij u32 mode, polarity;
856ee532e2SLinus Walleij
866ee532e2SLinus Walleij mode = readl(FT010_IRQ_MODE(f->base));
876ee532e2SLinus Walleij polarity = readl(FT010_IRQ_POLARITY(f->base));
886ee532e2SLinus Walleij
89d2d55ab8SLinus Walleij if (trigger & (IRQ_TYPE_LEVEL_LOW)) {
906ee532e2SLinus Walleij irq_set_handler_locked(d, handle_level_irq);
91d2d55ab8SLinus Walleij mode &= ~BIT(offset);
92d2d55ab8SLinus Walleij polarity |= BIT(offset);
93d2d55ab8SLinus Walleij } else if (trigger & (IRQ_TYPE_LEVEL_HIGH)) {
94d2d55ab8SLinus Walleij irq_set_handler_locked(d, handle_level_irq);
956ee532e2SLinus Walleij mode &= ~BIT(offset);
966ee532e2SLinus Walleij polarity &= ~BIT(offset);
97d2d55ab8SLinus Walleij } else if (trigger & IRQ_TYPE_EDGE_FALLING) {
986ee532e2SLinus Walleij irq_set_handler_locked(d, handle_edge_irq);
996ee532e2SLinus Walleij mode |= BIT(offset);
1006ee532e2SLinus Walleij polarity |= BIT(offset);
101d2d55ab8SLinus Walleij } else if (trigger & IRQ_TYPE_EDGE_RISING) {
1026ee532e2SLinus Walleij irq_set_handler_locked(d, handle_edge_irq);
1036ee532e2SLinus Walleij mode |= BIT(offset);
1046ee532e2SLinus Walleij polarity &= ~BIT(offset);
1056ee532e2SLinus Walleij } else {
1066ee532e2SLinus Walleij irq_set_handler_locked(d, handle_bad_irq);
107d2d55ab8SLinus Walleij pr_warn("Faraday IRQ: no supported trigger selected for line %d\n",
1086ee532e2SLinus Walleij offset);
1096ee532e2SLinus Walleij }
1106ee532e2SLinus Walleij
1116ee532e2SLinus Walleij writel(mode, FT010_IRQ_MODE(f->base));
1126ee532e2SLinus Walleij writel(polarity, FT010_IRQ_POLARITY(f->base));
1136ee532e2SLinus Walleij
1146ee532e2SLinus Walleij return 0;
1156ee532e2SLinus Walleij }
1166ee532e2SLinus Walleij
1176ee532e2SLinus Walleij static struct irq_chip ft010_irq_chip = {
1186ee532e2SLinus Walleij .name = "FTINTC010",
1196ee532e2SLinus Walleij .irq_ack = ft010_irq_ack,
1206ee532e2SLinus Walleij .irq_mask = ft010_irq_mask,
1216ee532e2SLinus Walleij .irq_unmask = ft010_irq_unmask,
1226ee532e2SLinus Walleij .irq_set_type = ft010_irq_set_type,
1236ee532e2SLinus Walleij };
1246ee532e2SLinus Walleij
1256ee532e2SLinus Walleij /* Local static for the IRQ entry call */
1266ee532e2SLinus Walleij static struct ft010_irq_data firq;
1276ee532e2SLinus Walleij
ft010_irqchip_handle_irq(struct pt_regs * regs)128*97bb0f8eSArnd Bergmann static asmlinkage void __exception_irq_entry ft010_irqchip_handle_irq(struct pt_regs *regs)
1296ee532e2SLinus Walleij {
1306ee532e2SLinus Walleij struct ft010_irq_data *f = &firq;
1316ee532e2SLinus Walleij int irq;
1326ee532e2SLinus Walleij u32 status;
1336ee532e2SLinus Walleij
1346ee532e2SLinus Walleij while ((status = readl(FT010_IRQ_STATUS(f->base)))) {
1356ee532e2SLinus Walleij irq = ffs(status) - 1;
1360953fb26SMark Rutland generic_handle_domain_irq(f->domain, irq);
1376ee532e2SLinus Walleij }
1386ee532e2SLinus Walleij }
1396ee532e2SLinus Walleij
ft010_irqdomain_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hwirq)1406ee532e2SLinus Walleij static int ft010_irqdomain_map(struct irq_domain *d, unsigned int irq,
1416ee532e2SLinus Walleij irq_hw_number_t hwirq)
1426ee532e2SLinus Walleij {
1436ee532e2SLinus Walleij struct ft010_irq_data *f = d->host_data;
1446ee532e2SLinus Walleij
1456ee532e2SLinus Walleij irq_set_chip_data(irq, f);
1466ee532e2SLinus Walleij /* All IRQs should set up their type, flags as bad by default */
1476ee532e2SLinus Walleij irq_set_chip_and_handler(irq, &ft010_irq_chip, handle_bad_irq);
1486ee532e2SLinus Walleij irq_set_probe(irq);
1496ee532e2SLinus Walleij
1506ee532e2SLinus Walleij return 0;
1516ee532e2SLinus Walleij }
1526ee532e2SLinus Walleij
ft010_irqdomain_unmap(struct irq_domain * d,unsigned int irq)1536ee532e2SLinus Walleij static void ft010_irqdomain_unmap(struct irq_domain *d, unsigned int irq)
1546ee532e2SLinus Walleij {
1556ee532e2SLinus Walleij irq_set_chip_and_handler(irq, NULL, NULL);
1566ee532e2SLinus Walleij irq_set_chip_data(irq, NULL);
1576ee532e2SLinus Walleij }
1586ee532e2SLinus Walleij
1596ee532e2SLinus Walleij static const struct irq_domain_ops ft010_irqdomain_ops = {
1606ee532e2SLinus Walleij .map = ft010_irqdomain_map,
1616ee532e2SLinus Walleij .unmap = ft010_irqdomain_unmap,
1626ee532e2SLinus Walleij .xlate = irq_domain_xlate_onetwocell,
1636ee532e2SLinus Walleij };
1646ee532e2SLinus Walleij
ft010_of_init_irq(struct device_node * node,struct device_node * parent)165*97bb0f8eSArnd Bergmann static int __init ft010_of_init_irq(struct device_node *node,
1666ee532e2SLinus Walleij struct device_node *parent)
1676ee532e2SLinus Walleij {
1686ee532e2SLinus Walleij struct ft010_irq_data *f = &firq;
1696ee532e2SLinus Walleij
1706ee532e2SLinus Walleij /*
1716ee532e2SLinus Walleij * Disable the idle handler by default since it is buggy
1726ee532e2SLinus Walleij * For more info see arch/arm/mach-gemini/idle.c
1736ee532e2SLinus Walleij */
1746ee532e2SLinus Walleij cpu_idle_poll_ctrl(true);
1756ee532e2SLinus Walleij
1766ee532e2SLinus Walleij f->base = of_iomap(node, 0);
1776ee532e2SLinus Walleij WARN(!f->base, "unable to map gemini irq registers\n");
1786ee532e2SLinus Walleij
1796ee532e2SLinus Walleij /* Disable all interrupts */
1806ee532e2SLinus Walleij writel(0, FT010_IRQ_MASK(f->base));
1816ee532e2SLinus Walleij writel(0, FT010_FIQ_MASK(f->base));
1826ee532e2SLinus Walleij
1836ee532e2SLinus Walleij f->domain = irq_domain_add_simple(node, FT010_NUM_IRQS, 0,
1846ee532e2SLinus Walleij &ft010_irqdomain_ops, f);
1856ee532e2SLinus Walleij set_handle_irq(ft010_irqchip_handle_irq);
1866ee532e2SLinus Walleij
1876ee532e2SLinus Walleij return 0;
1886ee532e2SLinus Walleij }
1896ee532e2SLinus Walleij IRQCHIP_DECLARE(faraday, "faraday,ftintc010",
1906ee532e2SLinus Walleij ft010_of_init_irq);
1916ee532e2SLinus Walleij IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller",
1926ee532e2SLinus Walleij ft010_of_init_irq);
193390d2d49SLinus Walleij IRQCHIP_DECLARE(moxa, "moxa,moxart-ic",
194390d2d49SLinus Walleij ft010_of_init_irq);
195