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