1fead4dd4SJonathan Neuschäfer // SPDX-License-Identifier: GPL-2.0-only 2fead4dd4SJonathan Neuschäfer // Copyright 2021 Jonathan Neuschäfer 3fead4dd4SJonathan Neuschäfer 4fead4dd4SJonathan Neuschäfer #include <linux/irqchip.h> 5fead4dd4SJonathan Neuschäfer #include <linux/of_address.h> 6fead4dd4SJonathan Neuschäfer #include <linux/of_irq.h> 7fead4dd4SJonathan Neuschäfer #include <linux/printk.h> 8fead4dd4SJonathan Neuschäfer 9fead4dd4SJonathan Neuschäfer #include <asm/exception.h> 10fead4dd4SJonathan Neuschäfer 11fead4dd4SJonathan Neuschäfer #define AIC_SCR(x) ((x)*4) /* Source control registers */ 12fead4dd4SJonathan Neuschäfer #define AIC_GEN 0x84 /* Interrupt group enable control register */ 13fead4dd4SJonathan Neuschäfer #define AIC_GRSR 0x88 /* Interrupt group raw status register */ 14fead4dd4SJonathan Neuschäfer #define AIC_IRSR 0x100 /* Interrupt raw status register */ 15fead4dd4SJonathan Neuschäfer #define AIC_IASR 0x104 /* Interrupt active status register */ 16fead4dd4SJonathan Neuschäfer #define AIC_ISR 0x108 /* Interrupt status register */ 17fead4dd4SJonathan Neuschäfer #define AIC_IPER 0x10c /* Interrupt priority encoding register */ 18fead4dd4SJonathan Neuschäfer #define AIC_ISNR 0x110 /* Interrupt source number register */ 19fead4dd4SJonathan Neuschäfer #define AIC_IMR 0x114 /* Interrupt mask register */ 20fead4dd4SJonathan Neuschäfer #define AIC_OISR 0x118 /* Output interrupt status register */ 21fead4dd4SJonathan Neuschäfer #define AIC_MECR 0x120 /* Mask enable command register */ 22fead4dd4SJonathan Neuschäfer #define AIC_MDCR 0x124 /* Mask disable command register */ 23fead4dd4SJonathan Neuschäfer #define AIC_SSCR 0x128 /* Source set command register */ 24fead4dd4SJonathan Neuschäfer #define AIC_SCCR 0x12c /* Source clear command register */ 25fead4dd4SJonathan Neuschäfer #define AIC_EOSCR 0x130 /* End of service command register */ 26fead4dd4SJonathan Neuschäfer 27fead4dd4SJonathan Neuschäfer #define AIC_SCR_SRCTYPE_LOW_LEVEL (0 << 6) 28fead4dd4SJonathan Neuschäfer #define AIC_SCR_SRCTYPE_HIGH_LEVEL (1 << 6) 29fead4dd4SJonathan Neuschäfer #define AIC_SCR_SRCTYPE_NEG_EDGE (2 << 6) 30fead4dd4SJonathan Neuschäfer #define AIC_SCR_SRCTYPE_POS_EDGE (3 << 6) 31fead4dd4SJonathan Neuschäfer #define AIC_SCR_PRIORITY(x) (x) 32fead4dd4SJonathan Neuschäfer #define AIC_SCR_PRIORITY_MASK 0x7 33fead4dd4SJonathan Neuschäfer 34fead4dd4SJonathan Neuschäfer #define AIC_NUM_IRQS 32 35fead4dd4SJonathan Neuschäfer 36fead4dd4SJonathan Neuschäfer struct wpcm450_aic { 37fead4dd4SJonathan Neuschäfer void __iomem *regs; 38fead4dd4SJonathan Neuschäfer struct irq_domain *domain; 39fead4dd4SJonathan Neuschäfer }; 40fead4dd4SJonathan Neuschäfer 41fead4dd4SJonathan Neuschäfer static struct wpcm450_aic *aic; 42fead4dd4SJonathan Neuschäfer 43fead4dd4SJonathan Neuschäfer static void wpcm450_aic_init_hw(void) 44fead4dd4SJonathan Neuschäfer { 45fead4dd4SJonathan Neuschäfer int i; 46fead4dd4SJonathan Neuschäfer 47fead4dd4SJonathan Neuschäfer /* Disable (mask) all interrupts */ 48fead4dd4SJonathan Neuschäfer writel(0xffffffff, aic->regs + AIC_MDCR); 49fead4dd4SJonathan Neuschäfer 50fead4dd4SJonathan Neuschäfer /* 51fead4dd4SJonathan Neuschäfer * Make sure the interrupt controller is ready to serve new interrupts. 52fead4dd4SJonathan Neuschäfer * Reading from IPER indicates that the nIRQ signal may be deasserted, 53fead4dd4SJonathan Neuschäfer * and writing to EOSCR indicates that interrupt handling has finished. 54fead4dd4SJonathan Neuschäfer */ 55fead4dd4SJonathan Neuschäfer readl(aic->regs + AIC_IPER); 56fead4dd4SJonathan Neuschäfer writel(0, aic->regs + AIC_EOSCR); 57fead4dd4SJonathan Neuschäfer 58fead4dd4SJonathan Neuschäfer /* Initialize trigger mode and priority of each interrupt source */ 59fead4dd4SJonathan Neuschäfer for (i = 0; i < AIC_NUM_IRQS; i++) 60fead4dd4SJonathan Neuschäfer writel(AIC_SCR_SRCTYPE_HIGH_LEVEL | AIC_SCR_PRIORITY(7), 61fead4dd4SJonathan Neuschäfer aic->regs + AIC_SCR(i)); 62fead4dd4SJonathan Neuschäfer } 63fead4dd4SJonathan Neuschäfer 64fead4dd4SJonathan Neuschäfer static void __exception_irq_entry wpcm450_aic_handle_irq(struct pt_regs *regs) 65fead4dd4SJonathan Neuschäfer { 66fead4dd4SJonathan Neuschäfer int hwirq; 67fead4dd4SJonathan Neuschäfer 68fead4dd4SJonathan Neuschäfer /* Determine the interrupt source */ 69fead4dd4SJonathan Neuschäfer /* Read IPER to signal that nIRQ can be de-asserted */ 70fead4dd4SJonathan Neuschäfer hwirq = readl(aic->regs + AIC_IPER) / 4; 71fead4dd4SJonathan Neuschäfer 72*0953fb26SMark Rutland generic_handle_domain_irq(aic->domain, hwirq); 73fead4dd4SJonathan Neuschäfer } 74fead4dd4SJonathan Neuschäfer 75fead4dd4SJonathan Neuschäfer static void wpcm450_aic_eoi(struct irq_data *d) 76fead4dd4SJonathan Neuschäfer { 77fead4dd4SJonathan Neuschäfer /* Signal end-of-service */ 78fead4dd4SJonathan Neuschäfer writel(0, aic->regs + AIC_EOSCR); 79fead4dd4SJonathan Neuschäfer } 80fead4dd4SJonathan Neuschäfer 81fead4dd4SJonathan Neuschäfer static void wpcm450_aic_mask(struct irq_data *d) 82fead4dd4SJonathan Neuschäfer { 83fead4dd4SJonathan Neuschäfer unsigned int mask = BIT(d->hwirq); 84fead4dd4SJonathan Neuschäfer 85fead4dd4SJonathan Neuschäfer /* Disable (mask) the interrupt */ 86fead4dd4SJonathan Neuschäfer writel(mask, aic->regs + AIC_MDCR); 87fead4dd4SJonathan Neuschäfer } 88fead4dd4SJonathan Neuschäfer 89fead4dd4SJonathan Neuschäfer static void wpcm450_aic_unmask(struct irq_data *d) 90fead4dd4SJonathan Neuschäfer { 91fead4dd4SJonathan Neuschäfer unsigned int mask = BIT(d->hwirq); 92fead4dd4SJonathan Neuschäfer 93fead4dd4SJonathan Neuschäfer /* Enable (unmask) the interrupt */ 94fead4dd4SJonathan Neuschäfer writel(mask, aic->regs + AIC_MECR); 95fead4dd4SJonathan Neuschäfer } 96fead4dd4SJonathan Neuschäfer 97fead4dd4SJonathan Neuschäfer static int wpcm450_aic_set_type(struct irq_data *d, unsigned int flow_type) 98fead4dd4SJonathan Neuschäfer { 99fead4dd4SJonathan Neuschäfer /* 100fead4dd4SJonathan Neuschäfer * The hardware supports high/low level, as well as rising/falling edge 101fead4dd4SJonathan Neuschäfer * modes, and the DT binding accommodates for that, but as long as 102fead4dd4SJonathan Neuschäfer * other modes than high level mode are not used and can't be tested, 103fead4dd4SJonathan Neuschäfer * they are rejected in this driver. 104fead4dd4SJonathan Neuschäfer */ 105fead4dd4SJonathan Neuschäfer if ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) 106fead4dd4SJonathan Neuschäfer return -EINVAL; 107fead4dd4SJonathan Neuschäfer 108fead4dd4SJonathan Neuschäfer return 0; 109fead4dd4SJonathan Neuschäfer } 110fead4dd4SJonathan Neuschäfer 111fead4dd4SJonathan Neuschäfer static struct irq_chip wpcm450_aic_chip = { 112fead4dd4SJonathan Neuschäfer .name = "wpcm450-aic", 113fead4dd4SJonathan Neuschäfer .irq_eoi = wpcm450_aic_eoi, 114fead4dd4SJonathan Neuschäfer .irq_mask = wpcm450_aic_mask, 115fead4dd4SJonathan Neuschäfer .irq_unmask = wpcm450_aic_unmask, 116fead4dd4SJonathan Neuschäfer .irq_set_type = wpcm450_aic_set_type, 117fead4dd4SJonathan Neuschäfer }; 118fead4dd4SJonathan Neuschäfer 119fead4dd4SJonathan Neuschäfer static int wpcm450_aic_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) 120fead4dd4SJonathan Neuschäfer { 121fead4dd4SJonathan Neuschäfer if (hwirq >= AIC_NUM_IRQS) 122fead4dd4SJonathan Neuschäfer return -EPERM; 123fead4dd4SJonathan Neuschäfer 124fead4dd4SJonathan Neuschäfer irq_set_chip_and_handler(irq, &wpcm450_aic_chip, handle_fasteoi_irq); 125fead4dd4SJonathan Neuschäfer irq_set_chip_data(irq, aic); 126fead4dd4SJonathan Neuschäfer irq_set_probe(irq); 127fead4dd4SJonathan Neuschäfer 128fead4dd4SJonathan Neuschäfer return 0; 129fead4dd4SJonathan Neuschäfer } 130fead4dd4SJonathan Neuschäfer 131fead4dd4SJonathan Neuschäfer static const struct irq_domain_ops wpcm450_aic_ops = { 132fead4dd4SJonathan Neuschäfer .map = wpcm450_aic_map, 133fead4dd4SJonathan Neuschäfer .xlate = irq_domain_xlate_twocell, 134fead4dd4SJonathan Neuschäfer }; 135fead4dd4SJonathan Neuschäfer 136fead4dd4SJonathan Neuschäfer static int __init wpcm450_aic_of_init(struct device_node *node, 137fead4dd4SJonathan Neuschäfer struct device_node *parent) 138fead4dd4SJonathan Neuschäfer { 139fead4dd4SJonathan Neuschäfer if (parent) 140fead4dd4SJonathan Neuschäfer return -EINVAL; 141fead4dd4SJonathan Neuschäfer 142fead4dd4SJonathan Neuschäfer aic = kzalloc(sizeof(*aic), GFP_KERNEL); 143fead4dd4SJonathan Neuschäfer if (!aic) 144fead4dd4SJonathan Neuschäfer return -ENOMEM; 145fead4dd4SJonathan Neuschäfer 146fead4dd4SJonathan Neuschäfer aic->regs = of_iomap(node, 0); 147fead4dd4SJonathan Neuschäfer if (!aic->regs) { 148fead4dd4SJonathan Neuschäfer pr_err("Failed to map WPCM450 AIC registers\n"); 149fead4dd4SJonathan Neuschäfer return -ENOMEM; 150fead4dd4SJonathan Neuschäfer } 151fead4dd4SJonathan Neuschäfer 152fead4dd4SJonathan Neuschäfer wpcm450_aic_init_hw(); 153fead4dd4SJonathan Neuschäfer 154fead4dd4SJonathan Neuschäfer set_handle_irq(wpcm450_aic_handle_irq); 155fead4dd4SJonathan Neuschäfer 156fead4dd4SJonathan Neuschäfer aic->domain = irq_domain_add_linear(node, AIC_NUM_IRQS, &wpcm450_aic_ops, aic); 157fead4dd4SJonathan Neuschäfer 158fead4dd4SJonathan Neuschäfer return 0; 159fead4dd4SJonathan Neuschäfer } 160fead4dd4SJonathan Neuschäfer 161fead4dd4SJonathan Neuschäfer IRQCHIP_DECLARE(wpcm450_aic, "nuvoton,wpcm450-aic", wpcm450_aic_of_init); 162