Loading Documentation/devicetree/bindings/arm/omap/crossbar.txt +36 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ Required properties: - compatible : Should be "ti,irq-crossbar" - reg: Base address and the size of the crossbar registers. - ti,max-irqs: Total number of irqs available at the interrupt controller. - ti,max-crossbar-sources: Maximum number of crossbar sources that can be routed. - ti,reg-size: Size of a individual register in bytes. Every individual register is assumed to be of same size. Valid sizes are 1, 2, 4. - ti,irqs-reserved: List of the reserved irq lines that are not muxed using Loading @@ -17,11 +18,46 @@ Required properties: so crossbar bar driver should not consider them as free lines. Optional properties: - ti,irqs-skip: This is similar to "ti,irqs-reserved", but these are for SOC-specific hard-wiring of those irqs which unexpectedly bypasses the crossbar. These irqs have a crossbar register, but still cannot be used. - ti,irqs-safe-map: integer which maps to a safe configuration to use when the interrupt controller irq is unused (when not provided, default is 0) Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; reg = <0x4a002a48 0x130>; ti,max-irqs = <160>; ti,max-crossbar-sources = <400>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; }; Consumer: ======== See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt and Documentation/devicetree/bindings/arm/gic.txt for further details. An interrupt consumer on an SoC using crossbar will use: interrupts = <GIC_SPI request_number interrupt_level> When the request number is between 0 to that described by "ti,max-crossbar-sources", it is assumed to be a crossbar mapping. If the request_number is greater than "ti,max-crossbar-sources", then it is mapped as a quirky hardware mapping direct to GIC. Example: device_x@0x4a023000 { /* Crossbar 8 used */ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; ... }; device_y@0x4a033000 { /* Direct mapped GIC SPI 1 used */ interrupts = <GIC_SPI DIRECT_IRQ(1) IRQ_TYPE_LEVEL_HIGH>; ... }; drivers/irqchip/irq-crossbar.c +143 −25 Original line number Diff line number Diff line Loading @@ -15,18 +15,27 @@ #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/irqchip/arm-gic.h> #include <linux/irqchip/irq-crossbar.h> #define IRQ_FREE -1 #define IRQ_RESERVED -2 #define IRQ_SKIP -3 #define GIC_IRQ_START 32 /* /** * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @max_crossbar_sources: Maximum number of crossbar sources * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number * @write: register write function pointer */ struct crossbar_device { uint int_max; uint safe_map; uint max_crossbar_sources; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; Loading @@ -50,11 +59,22 @@ static inline void crossbar_writeb(int irq_no, int cb_no) writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } static inline int get_prev_map_irq(int cb_no) { int i; for (i = cb->int_max - 1; i >= 0; i--) if (cb->irq_map[i] == cb_no) return i; return -ENODEV; } static inline int allocate_free_irq(int cb_no) { int i; for (i = 0; i < cb->int_max; i++) { for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { cb->irq_map[i] = cb_no; return i; Loading @@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no) return -ENODEV; } static inline bool needs_crossbar_write(irq_hw_number_t hw) { int cb_no; if (hw > GIC_IRQ_START) { cb_no = cb->irq_map[hw - GIC_IRQ_START]; if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) return true; } return false; } static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { if (needs_crossbar_write(hw)) cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); return 0; } /** * crossbar_domain_unmap - unmap a crossbar<->irq connection * @d: domain of irq to unmap * @irq: virq number * * We do not maintain a use count of total number of map/unmap * calls for a particular irq to find out if a irq can be really * unmapped. This is because unmap is called during irq_dispose_mapping(irq), * after which irq is anyways unusable. So an explicit map has to be called * after that. */ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; if (hw > GIC_IRQ_START) if (needs_crossbar_write(hw)) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; cb->write(hw - GIC_IRQ_START, cb->safe_map); } } static int crossbar_domain_xlate(struct irq_domain *d, Loading @@ -85,18 +133,41 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { unsigned long ret; int ret; int req_num = intspec[1]; int direct_map_num; if (req_num >= cb->max_crossbar_sources) { direct_map_num = req_num - cb->max_crossbar_sources; if (direct_map_num < cb->int_max) { ret = cb->irq_map[direct_map_num]; if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { /* We use the interrupt num as h/w irq num */ ret = direct_map_num; goto found; } } pr_err("%s: requested crossbar number %d > max %d\n", __func__, req_num, cb->max_crossbar_sources); return -EINVAL; } ret = allocate_free_irq(intspec[1]); ret = get_prev_map_irq(req_num); if (ret >= 0) goto found; if (IS_ERR_VALUE(ret)) ret = allocate_free_irq(req_num); if (ret < 0) return ret; found: *out_hwirq = ret + GIC_IRQ_START; return 0; } const struct irq_domain_ops routable_irq_domain_ops = { static const struct irq_domain_ops routable_irq_domain_ops = { .map = crossbar_domain_map, .unmap = crossbar_domain_unmap, .xlate = crossbar_domain_xlate Loading @@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { int i, size, max, reserved = 0, entry; int i, size, max = 0, reserved = 0, entry; const __be32 *irqsr; int ret = -ENOMEM; cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) return -ENOMEM; return ret; cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) goto err1; goto err_cb; of_property_read_u32(node, "ti,max-crossbar-sources", &cb->max_crossbar_sources); if (!cb->max_crossbar_sources) { pr_err("missing 'ti,max-crossbar-sources' property\n"); ret = -EINVAL; goto err_base; } of_property_read_u32(node, "ti,max-irqs", &max); cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); ret = -EINVAL; goto err_base; } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; goto err_base; cb->int_max = max; Loading @@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node) i, &entry); if (entry > max) { pr_err("Invalid reserved entry\n"); goto err3; ret = -EINVAL; goto err_irq_map; } cb->irq_map[entry] = 0; cb->irq_map[entry] = IRQ_RESERVED; } } cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); /* Skip irqs hardwired to bypass the crossbar */ irqsr = of_get_property(node, "ti,irqs-skip", &size); if (irqsr) { size /= sizeof(__be32); for (i = 0; i < size; i++) { of_property_read_u32_index(node, "ti,irqs-skip", i, &entry); if (entry > max) { pr_err("Invalid skip entry\n"); ret = -EINVAL; goto err_irq_map; } cb->irq_map[entry] = IRQ_SKIP; } } cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; goto err_irq_map; of_property_read_u32(node, "ti,reg-size", &size); Loading @@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node) break; default: pr_err("Invalid reg-size property\n"); goto err4; ret = -EINVAL; goto err_reg_offset; break; } Loading @@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node) * reserved irqs. so find and store the offsets once. */ for (i = 0; i < max; i++) { if (!cb->irq_map[i]) if (cb->irq_map[i] == IRQ_RESERVED) continue; cb->register_offsets[i] = reserved; reserved += size; } of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); /* Initialize the crossbar with safe map to start with */ for (i = 0; i < max; i++) { if (cb->irq_map[i] == IRQ_RESERVED || cb->irq_map[i] == IRQ_SKIP) continue; cb->write(i, cb->safe_map); } register_routable_domain_ops(&routable_irq_domain_ops); return 0; err4: err_reg_offset: kfree(cb->register_offsets); err3: err_irq_map: kfree(cb->irq_map); err2: err_base: iounmap(cb->crossbar_base); err1: err_cb: kfree(cb); return -ENOMEM; cb = NULL; return ret; } static const struct of_device_id crossbar_match[] __initconst = { Loading Loading
Documentation/devicetree/bindings/arm/omap/crossbar.txt +36 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ Required properties: - compatible : Should be "ti,irq-crossbar" - reg: Base address and the size of the crossbar registers. - ti,max-irqs: Total number of irqs available at the interrupt controller. - ti,max-crossbar-sources: Maximum number of crossbar sources that can be routed. - ti,reg-size: Size of a individual register in bytes. Every individual register is assumed to be of same size. Valid sizes are 1, 2, 4. - ti,irqs-reserved: List of the reserved irq lines that are not muxed using Loading @@ -17,11 +18,46 @@ Required properties: so crossbar bar driver should not consider them as free lines. Optional properties: - ti,irqs-skip: This is similar to "ti,irqs-reserved", but these are for SOC-specific hard-wiring of those irqs which unexpectedly bypasses the crossbar. These irqs have a crossbar register, but still cannot be used. - ti,irqs-safe-map: integer which maps to a safe configuration to use when the interrupt controller irq is unused (when not provided, default is 0) Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; reg = <0x4a002a48 0x130>; ti,max-irqs = <160>; ti,max-crossbar-sources = <400>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; }; Consumer: ======== See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt and Documentation/devicetree/bindings/arm/gic.txt for further details. An interrupt consumer on an SoC using crossbar will use: interrupts = <GIC_SPI request_number interrupt_level> When the request number is between 0 to that described by "ti,max-crossbar-sources", it is assumed to be a crossbar mapping. If the request_number is greater than "ti,max-crossbar-sources", then it is mapped as a quirky hardware mapping direct to GIC. Example: device_x@0x4a023000 { /* Crossbar 8 used */ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; ... }; device_y@0x4a033000 { /* Direct mapped GIC SPI 1 used */ interrupts = <GIC_SPI DIRECT_IRQ(1) IRQ_TYPE_LEVEL_HIGH>; ... };
drivers/irqchip/irq-crossbar.c +143 −25 Original line number Diff line number Diff line Loading @@ -15,18 +15,27 @@ #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/irqchip/arm-gic.h> #include <linux/irqchip/irq-crossbar.h> #define IRQ_FREE -1 #define IRQ_RESERVED -2 #define IRQ_SKIP -3 #define GIC_IRQ_START 32 /* /** * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @max_crossbar_sources: Maximum number of crossbar sources * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number * @write: register write function pointer */ struct crossbar_device { uint int_max; uint safe_map; uint max_crossbar_sources; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; Loading @@ -50,11 +59,22 @@ static inline void crossbar_writeb(int irq_no, int cb_no) writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } static inline int get_prev_map_irq(int cb_no) { int i; for (i = cb->int_max - 1; i >= 0; i--) if (cb->irq_map[i] == cb_no) return i; return -ENODEV; } static inline int allocate_free_irq(int cb_no) { int i; for (i = 0; i < cb->int_max; i++) { for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { cb->irq_map[i] = cb_no; return i; Loading @@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no) return -ENODEV; } static inline bool needs_crossbar_write(irq_hw_number_t hw) { int cb_no; if (hw > GIC_IRQ_START) { cb_no = cb->irq_map[hw - GIC_IRQ_START]; if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) return true; } return false; } static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { if (needs_crossbar_write(hw)) cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); return 0; } /** * crossbar_domain_unmap - unmap a crossbar<->irq connection * @d: domain of irq to unmap * @irq: virq number * * We do not maintain a use count of total number of map/unmap * calls for a particular irq to find out if a irq can be really * unmapped. This is because unmap is called during irq_dispose_mapping(irq), * after which irq is anyways unusable. So an explicit map has to be called * after that. */ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; if (hw > GIC_IRQ_START) if (needs_crossbar_write(hw)) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; cb->write(hw - GIC_IRQ_START, cb->safe_map); } } static int crossbar_domain_xlate(struct irq_domain *d, Loading @@ -85,18 +133,41 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { unsigned long ret; int ret; int req_num = intspec[1]; int direct_map_num; if (req_num >= cb->max_crossbar_sources) { direct_map_num = req_num - cb->max_crossbar_sources; if (direct_map_num < cb->int_max) { ret = cb->irq_map[direct_map_num]; if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { /* We use the interrupt num as h/w irq num */ ret = direct_map_num; goto found; } } pr_err("%s: requested crossbar number %d > max %d\n", __func__, req_num, cb->max_crossbar_sources); return -EINVAL; } ret = allocate_free_irq(intspec[1]); ret = get_prev_map_irq(req_num); if (ret >= 0) goto found; if (IS_ERR_VALUE(ret)) ret = allocate_free_irq(req_num); if (ret < 0) return ret; found: *out_hwirq = ret + GIC_IRQ_START; return 0; } const struct irq_domain_ops routable_irq_domain_ops = { static const struct irq_domain_ops routable_irq_domain_ops = { .map = crossbar_domain_map, .unmap = crossbar_domain_unmap, .xlate = crossbar_domain_xlate Loading @@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { int i, size, max, reserved = 0, entry; int i, size, max = 0, reserved = 0, entry; const __be32 *irqsr; int ret = -ENOMEM; cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) return -ENOMEM; return ret; cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) goto err1; goto err_cb; of_property_read_u32(node, "ti,max-crossbar-sources", &cb->max_crossbar_sources); if (!cb->max_crossbar_sources) { pr_err("missing 'ti,max-crossbar-sources' property\n"); ret = -EINVAL; goto err_base; } of_property_read_u32(node, "ti,max-irqs", &max); cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); ret = -EINVAL; goto err_base; } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; goto err_base; cb->int_max = max; Loading @@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node) i, &entry); if (entry > max) { pr_err("Invalid reserved entry\n"); goto err3; ret = -EINVAL; goto err_irq_map; } cb->irq_map[entry] = 0; cb->irq_map[entry] = IRQ_RESERVED; } } cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); /* Skip irqs hardwired to bypass the crossbar */ irqsr = of_get_property(node, "ti,irqs-skip", &size); if (irqsr) { size /= sizeof(__be32); for (i = 0; i < size; i++) { of_property_read_u32_index(node, "ti,irqs-skip", i, &entry); if (entry > max) { pr_err("Invalid skip entry\n"); ret = -EINVAL; goto err_irq_map; } cb->irq_map[entry] = IRQ_SKIP; } } cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; goto err_irq_map; of_property_read_u32(node, "ti,reg-size", &size); Loading @@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node) break; default: pr_err("Invalid reg-size property\n"); goto err4; ret = -EINVAL; goto err_reg_offset; break; } Loading @@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node) * reserved irqs. so find and store the offsets once. */ for (i = 0; i < max; i++) { if (!cb->irq_map[i]) if (cb->irq_map[i] == IRQ_RESERVED) continue; cb->register_offsets[i] = reserved; reserved += size; } of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); /* Initialize the crossbar with safe map to start with */ for (i = 0; i < max; i++) { if (cb->irq_map[i] == IRQ_RESERVED || cb->irq_map[i] == IRQ_SKIP) continue; cb->write(i, cb->safe_map); } register_routable_domain_ops(&routable_irq_domain_ops); return 0; err4: err_reg_offset: kfree(cb->register_offsets); err3: err_irq_map: kfree(cb->irq_map); err2: err_base: iounmap(cb->crossbar_base); err1: err_cb: kfree(cb); return -ENOMEM; cb = NULL; return ret; } static const struct of_device_id crossbar_match[] __initconst = { Loading