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 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 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 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 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 1286ee532e2SLinus Walleij 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 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 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 1656ee532e2SLinus Walleij 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