1*96ca848eSSricharan R /* 2*96ca848eSSricharan R * drivers/irqchip/irq-crossbar.c 3*96ca848eSSricharan R * 4*96ca848eSSricharan R * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com 5*96ca848eSSricharan R * Author: Sricharan R <r.sricharan@ti.com> 6*96ca848eSSricharan R * 7*96ca848eSSricharan R * This program is free software; you can redistribute it and/or modify 8*96ca848eSSricharan R * it under the terms of the GNU General Public License version 2 as 9*96ca848eSSricharan R * published by the Free Software Foundation. 10*96ca848eSSricharan R * 11*96ca848eSSricharan R */ 12*96ca848eSSricharan R #include <linux/err.h> 13*96ca848eSSricharan R #include <linux/io.h> 14*96ca848eSSricharan R #include <linux/of_address.h> 15*96ca848eSSricharan R #include <linux/of_irq.h> 16*96ca848eSSricharan R #include <linux/slab.h> 17*96ca848eSSricharan R #include <linux/irqchip/arm-gic.h> 18*96ca848eSSricharan R 19*96ca848eSSricharan R #define IRQ_FREE -1 20*96ca848eSSricharan R #define GIC_IRQ_START 32 21*96ca848eSSricharan R 22*96ca848eSSricharan R /* 23*96ca848eSSricharan R * @int_max: maximum number of supported interrupts 24*96ca848eSSricharan R * @irq_map: array of interrupts to crossbar number mapping 25*96ca848eSSricharan R * @crossbar_base: crossbar base address 26*96ca848eSSricharan R * @register_offsets: offsets for each irq number 27*96ca848eSSricharan R */ 28*96ca848eSSricharan R struct crossbar_device { 29*96ca848eSSricharan R uint int_max; 30*96ca848eSSricharan R uint *irq_map; 31*96ca848eSSricharan R void __iomem *crossbar_base; 32*96ca848eSSricharan R int *register_offsets; 33*96ca848eSSricharan R void (*write) (int, int); 34*96ca848eSSricharan R }; 35*96ca848eSSricharan R 36*96ca848eSSricharan R static struct crossbar_device *cb; 37*96ca848eSSricharan R 38*96ca848eSSricharan R static inline void crossbar_writel(int irq_no, int cb_no) 39*96ca848eSSricharan R { 40*96ca848eSSricharan R writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 41*96ca848eSSricharan R } 42*96ca848eSSricharan R 43*96ca848eSSricharan R static inline void crossbar_writew(int irq_no, int cb_no) 44*96ca848eSSricharan R { 45*96ca848eSSricharan R writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 46*96ca848eSSricharan R } 47*96ca848eSSricharan R 48*96ca848eSSricharan R static inline void crossbar_writeb(int irq_no, int cb_no) 49*96ca848eSSricharan R { 50*96ca848eSSricharan R writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 51*96ca848eSSricharan R } 52*96ca848eSSricharan R 53*96ca848eSSricharan R static inline int allocate_free_irq(int cb_no) 54*96ca848eSSricharan R { 55*96ca848eSSricharan R int i; 56*96ca848eSSricharan R 57*96ca848eSSricharan R for (i = 0; i < cb->int_max; i++) { 58*96ca848eSSricharan R if (cb->irq_map[i] == IRQ_FREE) { 59*96ca848eSSricharan R cb->irq_map[i] = cb_no; 60*96ca848eSSricharan R return i; 61*96ca848eSSricharan R } 62*96ca848eSSricharan R } 63*96ca848eSSricharan R 64*96ca848eSSricharan R return -ENODEV; 65*96ca848eSSricharan R } 66*96ca848eSSricharan R 67*96ca848eSSricharan R static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, 68*96ca848eSSricharan R irq_hw_number_t hw) 69*96ca848eSSricharan R { 70*96ca848eSSricharan R cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); 71*96ca848eSSricharan R return 0; 72*96ca848eSSricharan R } 73*96ca848eSSricharan R 74*96ca848eSSricharan R static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) 75*96ca848eSSricharan R { 76*96ca848eSSricharan R irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; 77*96ca848eSSricharan R 78*96ca848eSSricharan R if (hw > GIC_IRQ_START) 79*96ca848eSSricharan R cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; 80*96ca848eSSricharan R } 81*96ca848eSSricharan R 82*96ca848eSSricharan R static int crossbar_domain_xlate(struct irq_domain *d, 83*96ca848eSSricharan R struct device_node *controller, 84*96ca848eSSricharan R const u32 *intspec, unsigned int intsize, 85*96ca848eSSricharan R unsigned long *out_hwirq, 86*96ca848eSSricharan R unsigned int *out_type) 87*96ca848eSSricharan R { 88*96ca848eSSricharan R unsigned long ret; 89*96ca848eSSricharan R 90*96ca848eSSricharan R ret = allocate_free_irq(intspec[1]); 91*96ca848eSSricharan R 92*96ca848eSSricharan R if (IS_ERR_VALUE(ret)) 93*96ca848eSSricharan R return ret; 94*96ca848eSSricharan R 95*96ca848eSSricharan R *out_hwirq = ret + GIC_IRQ_START; 96*96ca848eSSricharan R return 0; 97*96ca848eSSricharan R } 98*96ca848eSSricharan R 99*96ca848eSSricharan R const struct irq_domain_ops routable_irq_domain_ops = { 100*96ca848eSSricharan R .map = crossbar_domain_map, 101*96ca848eSSricharan R .unmap = crossbar_domain_unmap, 102*96ca848eSSricharan R .xlate = crossbar_domain_xlate 103*96ca848eSSricharan R }; 104*96ca848eSSricharan R 105*96ca848eSSricharan R static int __init crossbar_of_init(struct device_node *node) 106*96ca848eSSricharan R { 107*96ca848eSSricharan R int i, size, max, reserved = 0, entry; 108*96ca848eSSricharan R const __be32 *irqsr; 109*96ca848eSSricharan R 110*96ca848eSSricharan R cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL); 111*96ca848eSSricharan R 112*96ca848eSSricharan R if (!cb) 113*96ca848eSSricharan R return -ENOMEM; 114*96ca848eSSricharan R 115*96ca848eSSricharan R cb->crossbar_base = of_iomap(node, 0); 116*96ca848eSSricharan R if (!cb->crossbar_base) 117*96ca848eSSricharan R goto err1; 118*96ca848eSSricharan R 119*96ca848eSSricharan R of_property_read_u32(node, "ti,max-irqs", &max); 120*96ca848eSSricharan R cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); 121*96ca848eSSricharan R if (!cb->irq_map) 122*96ca848eSSricharan R goto err2; 123*96ca848eSSricharan R 124*96ca848eSSricharan R cb->int_max = max; 125*96ca848eSSricharan R 126*96ca848eSSricharan R for (i = 0; i < max; i++) 127*96ca848eSSricharan R cb->irq_map[i] = IRQ_FREE; 128*96ca848eSSricharan R 129*96ca848eSSricharan R /* Get and mark reserved irqs */ 130*96ca848eSSricharan R irqsr = of_get_property(node, "ti,irqs-reserved", &size); 131*96ca848eSSricharan R if (irqsr) { 132*96ca848eSSricharan R size /= sizeof(__be32); 133*96ca848eSSricharan R 134*96ca848eSSricharan R for (i = 0; i < size; i++) { 135*96ca848eSSricharan R of_property_read_u32_index(node, 136*96ca848eSSricharan R "ti,irqs-reserved", 137*96ca848eSSricharan R i, &entry); 138*96ca848eSSricharan R if (entry > max) { 139*96ca848eSSricharan R pr_err("Invalid reserved entry\n"); 140*96ca848eSSricharan R goto err3; 141*96ca848eSSricharan R } 142*96ca848eSSricharan R cb->irq_map[entry] = 0; 143*96ca848eSSricharan R } 144*96ca848eSSricharan R } 145*96ca848eSSricharan R 146*96ca848eSSricharan R cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); 147*96ca848eSSricharan R if (!cb->register_offsets) 148*96ca848eSSricharan R goto err3; 149*96ca848eSSricharan R 150*96ca848eSSricharan R of_property_read_u32(node, "ti,reg-size", &size); 151*96ca848eSSricharan R 152*96ca848eSSricharan R switch (size) { 153*96ca848eSSricharan R case 1: 154*96ca848eSSricharan R cb->write = crossbar_writeb; 155*96ca848eSSricharan R break; 156*96ca848eSSricharan R case 2: 157*96ca848eSSricharan R cb->write = crossbar_writew; 158*96ca848eSSricharan R break; 159*96ca848eSSricharan R case 4: 160*96ca848eSSricharan R cb->write = crossbar_writel; 161*96ca848eSSricharan R break; 162*96ca848eSSricharan R default: 163*96ca848eSSricharan R pr_err("Invalid reg-size property\n"); 164*96ca848eSSricharan R goto err4; 165*96ca848eSSricharan R break; 166*96ca848eSSricharan R } 167*96ca848eSSricharan R 168*96ca848eSSricharan R /* 169*96ca848eSSricharan R * Register offsets are not linear because of the 170*96ca848eSSricharan R * reserved irqs. so find and store the offsets once. 171*96ca848eSSricharan R */ 172*96ca848eSSricharan R for (i = 0; i < max; i++) { 173*96ca848eSSricharan R if (!cb->irq_map[i]) 174*96ca848eSSricharan R continue; 175*96ca848eSSricharan R 176*96ca848eSSricharan R cb->register_offsets[i] = reserved; 177*96ca848eSSricharan R reserved += size; 178*96ca848eSSricharan R } 179*96ca848eSSricharan R 180*96ca848eSSricharan R register_routable_domain_ops(&routable_irq_domain_ops); 181*96ca848eSSricharan R return 0; 182*96ca848eSSricharan R 183*96ca848eSSricharan R err4: 184*96ca848eSSricharan R kfree(cb->register_offsets); 185*96ca848eSSricharan R err3: 186*96ca848eSSricharan R kfree(cb->irq_map); 187*96ca848eSSricharan R err2: 188*96ca848eSSricharan R iounmap(cb->crossbar_base); 189*96ca848eSSricharan R err1: 190*96ca848eSSricharan R kfree(cb); 191*96ca848eSSricharan R return -ENOMEM; 192*96ca848eSSricharan R } 193*96ca848eSSricharan R 194*96ca848eSSricharan R static const struct of_device_id crossbar_match[] __initconst = { 195*96ca848eSSricharan R { .compatible = "ti,irq-crossbar" }, 196*96ca848eSSricharan R {} 197*96ca848eSSricharan R }; 198*96ca848eSSricharan R 199*96ca848eSSricharan R int __init irqcrossbar_init(void) 200*96ca848eSSricharan R { 201*96ca848eSSricharan R struct device_node *np; 202*96ca848eSSricharan R np = of_find_matching_node(NULL, crossbar_match); 203*96ca848eSSricharan R if (!np) 204*96ca848eSSricharan R return -ENODEV; 205*96ca848eSSricharan R 206*96ca848eSSricharan R crossbar_of_init(np); 207*96ca848eSSricharan R return 0; 208*96ca848eSSricharan R } 209