1*acf9e575SChristophe Leroy // SPDX-License-Identifier: GPL-2.0 2*acf9e575SChristophe Leroy /* 3*acf9e575SChristophe Leroy * Interrupt controller for the 4*acf9e575SChristophe Leroy * Communication Processor Module. 5*acf9e575SChristophe Leroy * Copyright (c) 1997 Dan error_act (dmalek@jlc.net) 6*acf9e575SChristophe Leroy */ 7*acf9e575SChristophe Leroy #include <linux/kernel.h> 8*acf9e575SChristophe Leroy #include <linux/interrupt.h> 9*acf9e575SChristophe Leroy #include <linux/irqdomain.h> 10*acf9e575SChristophe Leroy #include <linux/of_irq.h> 11*acf9e575SChristophe Leroy #include <asm/cpm1.h> 12*acf9e575SChristophe Leroy 13*acf9e575SChristophe Leroy static cpic8xx_t __iomem *cpic_reg; 14*acf9e575SChristophe Leroy 15*acf9e575SChristophe Leroy static struct irq_domain *cpm_pic_host; 16*acf9e575SChristophe Leroy 17*acf9e575SChristophe Leroy static void cpm_mask_irq(struct irq_data *d) 18*acf9e575SChristophe Leroy { 19*acf9e575SChristophe Leroy unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 20*acf9e575SChristophe Leroy 21*acf9e575SChristophe Leroy clrbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec)); 22*acf9e575SChristophe Leroy } 23*acf9e575SChristophe Leroy 24*acf9e575SChristophe Leroy static void cpm_unmask_irq(struct irq_data *d) 25*acf9e575SChristophe Leroy { 26*acf9e575SChristophe Leroy unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 27*acf9e575SChristophe Leroy 28*acf9e575SChristophe Leroy setbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec)); 29*acf9e575SChristophe Leroy } 30*acf9e575SChristophe Leroy 31*acf9e575SChristophe Leroy static void cpm_end_irq(struct irq_data *d) 32*acf9e575SChristophe Leroy { 33*acf9e575SChristophe Leroy unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d); 34*acf9e575SChristophe Leroy 35*acf9e575SChristophe Leroy out_be32(&cpic_reg->cpic_cisr, (1 << cpm_vec)); 36*acf9e575SChristophe Leroy } 37*acf9e575SChristophe Leroy 38*acf9e575SChristophe Leroy static struct irq_chip cpm_pic = { 39*acf9e575SChristophe Leroy .name = "CPM PIC", 40*acf9e575SChristophe Leroy .irq_mask = cpm_mask_irq, 41*acf9e575SChristophe Leroy .irq_unmask = cpm_unmask_irq, 42*acf9e575SChristophe Leroy .irq_eoi = cpm_end_irq, 43*acf9e575SChristophe Leroy }; 44*acf9e575SChristophe Leroy 45*acf9e575SChristophe Leroy int cpm_get_irq(void) 46*acf9e575SChristophe Leroy { 47*acf9e575SChristophe Leroy int cpm_vec; 48*acf9e575SChristophe Leroy 49*acf9e575SChristophe Leroy /* 50*acf9e575SChristophe Leroy * Get the vector by setting the ACK bit and then reading 51*acf9e575SChristophe Leroy * the register. 52*acf9e575SChristophe Leroy */ 53*acf9e575SChristophe Leroy out_be16(&cpic_reg->cpic_civr, 1); 54*acf9e575SChristophe Leroy cpm_vec = in_be16(&cpic_reg->cpic_civr); 55*acf9e575SChristophe Leroy cpm_vec >>= 11; 56*acf9e575SChristophe Leroy 57*acf9e575SChristophe Leroy return irq_linear_revmap(cpm_pic_host, cpm_vec); 58*acf9e575SChristophe Leroy } 59*acf9e575SChristophe Leroy 60*acf9e575SChristophe Leroy static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq, 61*acf9e575SChristophe Leroy irq_hw_number_t hw) 62*acf9e575SChristophe Leroy { 63*acf9e575SChristophe Leroy pr_debug("cpm_pic_host_map(%d, 0x%lx)\n", virq, hw); 64*acf9e575SChristophe Leroy 65*acf9e575SChristophe Leroy irq_set_status_flags(virq, IRQ_LEVEL); 66*acf9e575SChristophe Leroy irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq); 67*acf9e575SChristophe Leroy return 0; 68*acf9e575SChristophe Leroy } 69*acf9e575SChristophe Leroy 70*acf9e575SChristophe Leroy /* 71*acf9e575SChristophe Leroy * The CPM can generate the error interrupt when there is a race condition 72*acf9e575SChristophe Leroy * between generating and masking interrupts. All we have to do is ACK it 73*acf9e575SChristophe Leroy * and return. This is a no-op function so we don't need any special 74*acf9e575SChristophe Leroy * tests in the interrupt handler. 75*acf9e575SChristophe Leroy */ 76*acf9e575SChristophe Leroy static irqreturn_t cpm_error_interrupt(int irq, void *dev) 77*acf9e575SChristophe Leroy { 78*acf9e575SChristophe Leroy return IRQ_HANDLED; 79*acf9e575SChristophe Leroy } 80*acf9e575SChristophe Leroy 81*acf9e575SChristophe Leroy static const struct irq_domain_ops cpm_pic_host_ops = { 82*acf9e575SChristophe Leroy .map = cpm_pic_host_map, 83*acf9e575SChristophe Leroy }; 84*acf9e575SChristophe Leroy 85*acf9e575SChristophe Leroy unsigned int __init cpm_pic_init(void) 86*acf9e575SChristophe Leroy { 87*acf9e575SChristophe Leroy struct device_node *np = NULL; 88*acf9e575SChristophe Leroy struct resource res; 89*acf9e575SChristophe Leroy unsigned int sirq = 0, hwirq, eirq; 90*acf9e575SChristophe Leroy int ret; 91*acf9e575SChristophe Leroy 92*acf9e575SChristophe Leroy pr_debug("cpm_pic_init\n"); 93*acf9e575SChristophe Leroy 94*acf9e575SChristophe Leroy np = of_find_compatible_node(NULL, NULL, "fsl,cpm1-pic"); 95*acf9e575SChristophe Leroy if (np == NULL) 96*acf9e575SChristophe Leroy np = of_find_compatible_node(NULL, "cpm-pic", "CPM"); 97*acf9e575SChristophe Leroy if (np == NULL) { 98*acf9e575SChristophe Leroy printk(KERN_ERR "CPM PIC init: can not find cpm-pic node\n"); 99*acf9e575SChristophe Leroy return sirq; 100*acf9e575SChristophe Leroy } 101*acf9e575SChristophe Leroy 102*acf9e575SChristophe Leroy ret = of_address_to_resource(np, 0, &res); 103*acf9e575SChristophe Leroy if (ret) 104*acf9e575SChristophe Leroy goto end; 105*acf9e575SChristophe Leroy 106*acf9e575SChristophe Leroy cpic_reg = ioremap(res.start, resource_size(&res)); 107*acf9e575SChristophe Leroy if (cpic_reg == NULL) 108*acf9e575SChristophe Leroy goto end; 109*acf9e575SChristophe Leroy 110*acf9e575SChristophe Leroy sirq = irq_of_parse_and_map(np, 0); 111*acf9e575SChristophe Leroy if (!sirq) 112*acf9e575SChristophe Leroy goto end; 113*acf9e575SChristophe Leroy 114*acf9e575SChristophe Leroy /* Initialize the CPM interrupt controller. */ 115*acf9e575SChristophe Leroy hwirq = (unsigned int)virq_to_hw(sirq); 116*acf9e575SChristophe Leroy out_be32(&cpic_reg->cpic_cicr, 117*acf9e575SChristophe Leroy (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | 118*acf9e575SChristophe Leroy ((hwirq/2) << 13) | CICR_HP_MASK); 119*acf9e575SChristophe Leroy 120*acf9e575SChristophe Leroy out_be32(&cpic_reg->cpic_cimr, 0); 121*acf9e575SChristophe Leroy 122*acf9e575SChristophe Leroy cpm_pic_host = irq_domain_add_linear(np, 64, &cpm_pic_host_ops, NULL); 123*acf9e575SChristophe Leroy if (cpm_pic_host == NULL) { 124*acf9e575SChristophe Leroy printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n"); 125*acf9e575SChristophe Leroy sirq = 0; 126*acf9e575SChristophe Leroy goto end; 127*acf9e575SChristophe Leroy } 128*acf9e575SChristophe Leroy 129*acf9e575SChristophe Leroy /* Install our own error handler. */ 130*acf9e575SChristophe Leroy np = of_find_compatible_node(NULL, NULL, "fsl,cpm1"); 131*acf9e575SChristophe Leroy if (np == NULL) 132*acf9e575SChristophe Leroy np = of_find_node_by_type(NULL, "cpm"); 133*acf9e575SChristophe Leroy if (np == NULL) { 134*acf9e575SChristophe Leroy printk(KERN_ERR "CPM PIC init: can not find cpm node\n"); 135*acf9e575SChristophe Leroy goto end; 136*acf9e575SChristophe Leroy } 137*acf9e575SChristophe Leroy 138*acf9e575SChristophe Leroy eirq = irq_of_parse_and_map(np, 0); 139*acf9e575SChristophe Leroy if (!eirq) 140*acf9e575SChristophe Leroy goto end; 141*acf9e575SChristophe Leroy 142*acf9e575SChristophe Leroy if (request_irq(eirq, cpm_error_interrupt, IRQF_NO_THREAD, "error", 143*acf9e575SChristophe Leroy NULL)) 144*acf9e575SChristophe Leroy printk(KERN_ERR "Could not allocate CPM error IRQ!"); 145*acf9e575SChristophe Leroy 146*acf9e575SChristophe Leroy setbits32(&cpic_reg->cpic_cicr, CICR_IEN); 147*acf9e575SChristophe Leroy 148*acf9e575SChristophe Leroy end: 149*acf9e575SChristophe Leroy of_node_put(np); 150*acf9e575SChristophe Leroy return sirq; 151*acf9e575SChristophe Leroy } 152