1 /* 2 * drivers/irqchip/irq-crossbar.c 3 * 4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com 5 * Author: Sricharan R <r.sricharan@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 */ 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/of_address.h> 15 #include <linux/of_irq.h> 16 #include <linux/slab.h> 17 #include <linux/irqchip/arm-gic.h> 18 #include <linux/irqchip/irq-crossbar.h> 19 20 #define IRQ_FREE -1 21 #define IRQ_RESERVED -2 22 #define IRQ_SKIP -3 23 #define GIC_IRQ_START 32 24 25 /** 26 * struct crossbar_device - crossbar device description 27 * @int_max: maximum number of supported interrupts 28 * @safe_map: safe default value to initialize the crossbar 29 * @max_crossbar_sources: Maximum number of crossbar sources 30 * @irq_map: array of interrupts to crossbar number mapping 31 * @crossbar_base: crossbar base address 32 * @register_offsets: offsets for each irq number 33 * @write: register write function pointer 34 */ 35 struct crossbar_device { 36 uint int_max; 37 uint safe_map; 38 uint max_crossbar_sources; 39 uint *irq_map; 40 void __iomem *crossbar_base; 41 int *register_offsets; 42 void (*write)(int, int); 43 }; 44 45 static struct crossbar_device *cb; 46 47 static inline void crossbar_writel(int irq_no, int cb_no) 48 { 49 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 50 } 51 52 static inline void crossbar_writew(int irq_no, int cb_no) 53 { 54 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 55 } 56 57 static inline void crossbar_writeb(int irq_no, int cb_no) 58 { 59 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 60 } 61 62 static inline int get_prev_map_irq(int cb_no) 63 { 64 int i; 65 66 for (i = cb->int_max - 1; i >= 0; i--) 67 if (cb->irq_map[i] == cb_no) 68 return i; 69 70 return -ENODEV; 71 } 72 73 static inline int allocate_free_irq(int cb_no) 74 { 75 int i; 76 77 for (i = cb->int_max - 1; i >= 0; i--) { 78 if (cb->irq_map[i] == IRQ_FREE) { 79 cb->irq_map[i] = cb_no; 80 return i; 81 } 82 } 83 84 return -ENODEV; 85 } 86 87 static inline bool needs_crossbar_write(irq_hw_number_t hw) 88 { 89 int cb_no; 90 91 if (hw > GIC_IRQ_START) { 92 cb_no = cb->irq_map[hw - GIC_IRQ_START]; 93 if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) 94 return true; 95 } 96 97 return false; 98 } 99 100 static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, 101 irq_hw_number_t hw) 102 { 103 if (needs_crossbar_write(hw)) 104 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); 105 106 return 0; 107 } 108 109 /** 110 * crossbar_domain_unmap - unmap a crossbar<->irq connection 111 * @d: domain of irq to unmap 112 * @irq: virq number 113 * 114 * We do not maintain a use count of total number of map/unmap 115 * calls for a particular irq to find out if a irq can be really 116 * unmapped. This is because unmap is called during irq_dispose_mapping(irq), 117 * after which irq is anyways unusable. So an explicit map has to be called 118 * after that. 119 */ 120 static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) 121 { 122 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; 123 124 if (needs_crossbar_write(hw)) { 125 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; 126 cb->write(hw - GIC_IRQ_START, cb->safe_map); 127 } 128 } 129 130 static int crossbar_domain_xlate(struct irq_domain *d, 131 struct device_node *controller, 132 const u32 *intspec, unsigned int intsize, 133 unsigned long *out_hwirq, 134 unsigned int *out_type) 135 { 136 int ret; 137 int req_num = intspec[1]; 138 int direct_map_num; 139 140 if (req_num >= cb->max_crossbar_sources) { 141 direct_map_num = req_num - cb->max_crossbar_sources; 142 if (direct_map_num < cb->int_max) { 143 ret = cb->irq_map[direct_map_num]; 144 if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { 145 /* We use the interrupt num as h/w irq num */ 146 ret = direct_map_num; 147 goto found; 148 } 149 } 150 151 pr_err("%s: requested crossbar number %d > max %d\n", 152 __func__, req_num, cb->max_crossbar_sources); 153 return -EINVAL; 154 } 155 156 ret = get_prev_map_irq(req_num); 157 if (ret >= 0) 158 goto found; 159 160 ret = allocate_free_irq(req_num); 161 162 if (ret < 0) 163 return ret; 164 165 found: 166 *out_hwirq = ret + GIC_IRQ_START; 167 return 0; 168 } 169 170 static const struct irq_domain_ops routable_irq_domain_ops = { 171 .map = crossbar_domain_map, 172 .unmap = crossbar_domain_unmap, 173 .xlate = crossbar_domain_xlate 174 }; 175 176 static int __init crossbar_of_init(struct device_node *node) 177 { 178 int i, size, max = 0, reserved = 0, entry; 179 const __be32 *irqsr; 180 int ret = -ENOMEM; 181 182 cb = kzalloc(sizeof(*cb), GFP_KERNEL); 183 184 if (!cb) 185 return ret; 186 187 cb->crossbar_base = of_iomap(node, 0); 188 if (!cb->crossbar_base) 189 goto err_cb; 190 191 of_property_read_u32(node, "ti,max-crossbar-sources", 192 &cb->max_crossbar_sources); 193 if (!cb->max_crossbar_sources) { 194 pr_err("missing 'ti,max-crossbar-sources' property\n"); 195 ret = -EINVAL; 196 goto err_base; 197 } 198 199 of_property_read_u32(node, "ti,max-irqs", &max); 200 if (!max) { 201 pr_err("missing 'ti,max-irqs' property\n"); 202 ret = -EINVAL; 203 goto err_base; 204 } 205 cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); 206 if (!cb->irq_map) 207 goto err_base; 208 209 cb->int_max = max; 210 211 for (i = 0; i < max; i++) 212 cb->irq_map[i] = IRQ_FREE; 213 214 /* Get and mark reserved irqs */ 215 irqsr = of_get_property(node, "ti,irqs-reserved", &size); 216 if (irqsr) { 217 size /= sizeof(__be32); 218 219 for (i = 0; i < size; i++) { 220 of_property_read_u32_index(node, 221 "ti,irqs-reserved", 222 i, &entry); 223 if (entry > max) { 224 pr_err("Invalid reserved entry\n"); 225 ret = -EINVAL; 226 goto err_irq_map; 227 } 228 cb->irq_map[entry] = IRQ_RESERVED; 229 } 230 } 231 232 /* Skip irqs hardwired to bypass the crossbar */ 233 irqsr = of_get_property(node, "ti,irqs-skip", &size); 234 if (irqsr) { 235 size /= sizeof(__be32); 236 237 for (i = 0; i < size; i++) { 238 of_property_read_u32_index(node, 239 "ti,irqs-skip", 240 i, &entry); 241 if (entry > max) { 242 pr_err("Invalid skip entry\n"); 243 ret = -EINVAL; 244 goto err_irq_map; 245 } 246 cb->irq_map[entry] = IRQ_SKIP; 247 } 248 } 249 250 251 cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); 252 if (!cb->register_offsets) 253 goto err_irq_map; 254 255 of_property_read_u32(node, "ti,reg-size", &size); 256 257 switch (size) { 258 case 1: 259 cb->write = crossbar_writeb; 260 break; 261 case 2: 262 cb->write = crossbar_writew; 263 break; 264 case 4: 265 cb->write = crossbar_writel; 266 break; 267 default: 268 pr_err("Invalid reg-size property\n"); 269 ret = -EINVAL; 270 goto err_reg_offset; 271 break; 272 } 273 274 /* 275 * Register offsets are not linear because of the 276 * reserved irqs. so find and store the offsets once. 277 */ 278 for (i = 0; i < max; i++) { 279 if (cb->irq_map[i] == IRQ_RESERVED) 280 continue; 281 282 cb->register_offsets[i] = reserved; 283 reserved += size; 284 } 285 286 of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); 287 /* Initialize the crossbar with safe map to start with */ 288 for (i = 0; i < max; i++) { 289 if (cb->irq_map[i] == IRQ_RESERVED || 290 cb->irq_map[i] == IRQ_SKIP) 291 continue; 292 293 cb->write(i, cb->safe_map); 294 } 295 296 register_routable_domain_ops(&routable_irq_domain_ops); 297 return 0; 298 299 err_reg_offset: 300 kfree(cb->register_offsets); 301 err_irq_map: 302 kfree(cb->irq_map); 303 err_base: 304 iounmap(cb->crossbar_base); 305 err_cb: 306 kfree(cb); 307 308 cb = NULL; 309 return ret; 310 } 311 312 static const struct of_device_id crossbar_match[] __initconst = { 313 { .compatible = "ti,irq-crossbar" }, 314 {} 315 }; 316 317 int __init irqcrossbar_init(void) 318 { 319 struct device_node *np; 320 np = of_find_matching_node(NULL, crossbar_match); 321 if (!np) 322 return -ENODEV; 323 324 crossbar_of_init(np); 325 return 0; 326 } 327