Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/ti-sci-intr: Add support for INTR being a parent to INTR

Driver assumes that Interrupt parent to Interrupt router is always GIC.
This is not true always and an Interrupt Router can be a parent to
Interrupt Router. Update the driver to detect the parent and request the
parent irqs accordingly.

Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20200806074826.24607-7-lokeshvutla@ti.com

authored by

Lokesh Vutla and committed by
Marc Zyngier
a5b659bd b8713af8

+93 -59
+93 -59
drivers/irqchip/irq-ti-sci-intr.c
··· 17 17 #include <linux/of_irq.h> 18 18 #include <linux/soc/ti/ti_sci_protocol.h> 19 19 20 - #define TI_SCI_DEV_ID_MASK 0xffff 21 - #define TI_SCI_DEV_ID_SHIFT 16 22 - #define TI_SCI_IRQ_ID_MASK 0xffff 23 - #define TI_SCI_IRQ_ID_SHIFT 0 24 - #define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ 25 - (TI_SCI_DEV_ID_MASK)) 26 - #define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) 27 - #define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ 28 - TI_SCI_DEV_ID_SHIFT) | \ 29 - ((index) & TI_SCI_IRQ_ID_MASK)) 30 - 31 20 /** 32 21 * struct ti_sci_intr_irq_domain - Structure representing a TISCI based 33 22 * Interrupt Router IRQ domain. 34 23 * @sci: Pointer to TISCI handle 35 - * @dst_irq: TISCI resource pointer representing GIC irq controller. 36 - * @dst_id: TISCI device ID of the GIC irq controller. 24 + * @out_irqs: TISCI resource pointer representing INTR irqs. 25 + * @dev: Struct device pointer. 26 + * @ti_sci_id: TI-SCI device identifier 37 27 * @type: Specifies the trigger type supported by this Interrupt Router 38 28 */ 39 29 struct ti_sci_intr_irq_domain { 40 30 const struct ti_sci_handle *sci; 41 - struct ti_sci_resource *dst_irq; 42 - u32 dst_id; 31 + struct ti_sci_resource *out_irqs; 32 + struct device *dev; 33 + u32 ti_sci_id; 43 34 u32 type; 44 35 }; 45 36 ··· 61 70 { 62 71 struct ti_sci_intr_irq_domain *intr = domain->host_data; 63 72 64 - if (fwspec->param_count != 2) 73 + if (fwspec->param_count != 1) 65 74 return -EINVAL; 66 75 67 - *hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]); 76 + *hwirq = fwspec->param[0]; 68 77 *type = intr->type; 69 78 70 79 return 0; 80 + } 81 + 82 + /** 83 + * ti_sci_intr_xlate_irq() - Translate hwirq to parent's hwirq. 84 + * @intr: IRQ domain corresponding to Interrupt Router 85 + * @irq: Hardware irq corresponding to the above irq domain 86 + * 87 + * Return parent irq number if translation is available else -ENOENT. 88 + */ 89 + static int ti_sci_intr_xlate_irq(struct ti_sci_intr_irq_domain *intr, u32 irq) 90 + { 91 + struct device_node *np = dev_of_node(intr->dev); 92 + u32 base, pbase, size, len; 93 + const __be32 *range; 94 + 95 + range = of_get_property(np, "ti,interrupt-ranges", &len); 96 + if (!range) 97 + return irq; 98 + 99 + for (len /= sizeof(*range); len >= 3; len -= 3) { 100 + base = be32_to_cpu(*range++); 101 + pbase = be32_to_cpu(*range++); 102 + size = be32_to_cpu(*range++); 103 + 104 + if (base <= irq && irq < base + size) 105 + return irq - base + pbase; 106 + } 107 + 108 + return -ENOENT; 71 109 } 72 110 73 111 /** ··· 109 89 unsigned int virq, unsigned int nr_irqs) 110 90 { 111 91 struct ti_sci_intr_irq_domain *intr = domain->host_data; 112 - struct irq_data *data, *parent_data; 113 - u16 dev_id, irq_index; 92 + struct irq_data *data; 93 + int out_irq; 114 94 115 - parent_data = irq_domain_get_irq_data(domain->parent, virq); 116 95 data = irq_domain_get_irq_data(domain, virq); 117 - irq_index = HWIRQ_TO_IRQID(data->hwirq); 118 - dev_id = HWIRQ_TO_DEVID(data->hwirq); 96 + out_irq = (uintptr_t)data->chip_data; 119 97 120 - intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index, 121 - intr->dst_id, parent_data->hwirq); 122 - ti_sci_release_resource(intr->dst_irq, parent_data->hwirq); 98 + intr->sci->ops.rm_irq_ops.free_irq(intr->sci, 99 + intr->ti_sci_id, data->hwirq, 100 + intr->ti_sci_id, out_irq); 101 + ti_sci_release_resource(intr->out_irqs, out_irq); 123 102 irq_domain_free_irqs_parent(domain, virq, 1); 124 103 irq_domain_reset_irq_data(data); 125 104 } 126 105 127 106 /** 128 - * ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ 107 + * ti_sci_intr_alloc_parent_irq() - Allocate parent IRQ 129 108 * @domain: Pointer to the interrupt router IRQ domain 130 109 * @virq: Corresponding Linux virtual IRQ number 131 110 * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain 132 111 * 133 - * Returns 0 if all went well else appropriate error pointer. 112 + * Returns parent irq if all went well else appropriate error pointer. 134 113 */ 135 - static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain, 136 - unsigned int virq, u32 hwirq) 114 + static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, 115 + unsigned int virq, u32 hwirq) 137 116 { 138 117 struct ti_sci_intr_irq_domain *intr = domain->host_data; 118 + struct device_node *parent_node; 139 119 struct irq_fwspec fwspec; 140 - u16 dev_id, irq_index; 141 - u16 dst_irq; 142 - int err; 120 + u16 out_irq, p_hwirq; 121 + int err = 0; 143 122 144 - dev_id = HWIRQ_TO_DEVID(hwirq); 145 - irq_index = HWIRQ_TO_IRQID(hwirq); 146 - 147 - dst_irq = ti_sci_get_free_resource(intr->dst_irq); 148 - if (dst_irq == TI_SCI_RESOURCE_NULL) 123 + out_irq = ti_sci_get_free_resource(intr->out_irqs); 124 + if (out_irq == TI_SCI_RESOURCE_NULL) 149 125 return -EINVAL; 150 126 151 - fwspec.fwnode = domain->parent->fwnode; 152 - fwspec.param_count = 3; 153 - fwspec.param[0] = 0; /* SPI */ 154 - fwspec.param[1] = dst_irq - 32; /* SPI offset */ 155 - fwspec.param[2] = intr->type; 127 + p_hwirq = ti_sci_intr_xlate_irq(intr, out_irq); 128 + if (p_hwirq < 0) 129 + goto err_irqs; 130 + 131 + parent_node = of_irq_find_parent(dev_of_node(intr->dev)); 132 + fwspec.fwnode = of_node_to_fwnode(parent_node); 133 + 134 + if (of_device_is_compatible(parent_node, "arm,gic-v3")) { 135 + /* Parent is GIC */ 136 + fwspec.param_count = 3; 137 + fwspec.param[0] = 0; /* SPI */ 138 + fwspec.param[1] = p_hwirq - 32; /* SPI offset */ 139 + fwspec.param[2] = intr->type; 140 + } else { 141 + /* Parent is Interrupt Router */ 142 + fwspec.param_count = 1; 143 + fwspec.param[0] = p_hwirq; 144 + } 156 145 157 146 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 158 147 if (err) 159 148 goto err_irqs; 160 149 161 - err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index, 162 - intr->dst_id, dst_irq); 150 + err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, 151 + intr->ti_sci_id, hwirq, 152 + intr->ti_sci_id, out_irq); 163 153 if (err) 164 154 goto err_msg; 165 155 166 - return 0; 156 + return p_hwirq; 167 157 168 158 err_msg: 169 159 irq_domain_free_irqs_parent(domain, virq, 1); 170 160 err_irqs: 171 - ti_sci_release_resource(intr->dst_irq, dst_irq); 161 + ti_sci_release_resource(intr->out_irqs, out_irq); 172 162 return err; 173 163 } 174 164 ··· 198 168 struct irq_fwspec *fwspec = data; 199 169 unsigned long hwirq; 200 170 unsigned int flags; 201 - int err; 171 + int err, p_hwirq; 202 172 203 173 err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); 204 174 if (err) 205 175 return err; 206 176 207 - err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq); 208 - if (err) 209 - return err; 177 + p_hwirq = ti_sci_intr_alloc_parent_irq(domain, virq, hwirq); 178 + if (p_hwirq < 0) 179 + return p_hwirq; 210 180 211 181 irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 212 - &ti_sci_intr_irq_chip, NULL); 182 + &ti_sci_intr_irq_chip, 183 + (void *)(uintptr_t)p_hwirq); 213 184 214 185 return 0; 215 186 } ··· 245 214 if (!intr) 246 215 return -ENOMEM; 247 216 217 + intr->dev = dev; 248 218 ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", 249 219 &intr->type); 250 220 if (ret) { ··· 262 230 return ret; 263 231 } 264 232 265 - ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id", 266 - &intr->dst_id); 233 + ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dev-id", 234 + &intr->ti_sci_id); 267 235 if (ret) { 268 - dev_err(dev, "missing 'ti,sci-dst-id' property\n"); 236 + dev_err(dev, "missing 'ti,sci-dev-id' property\n"); 269 237 return -EINVAL; 270 238 } 271 239 272 - intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev, 273 - intr->dst_id, 274 - "ti,sci-rm-range-girq"); 275 - if (IS_ERR(intr->dst_irq)) { 240 + intr->out_irqs = devm_ti_sci_get_resource(intr->sci, dev, 241 + intr->ti_sci_id, 242 + TI_SCI_RESASG_SUBTYPE_IR_OUTPUT); 243 + if (IS_ERR(intr->out_irqs)) { 276 244 dev_err(dev, "Destination irq resource allocation failed\n"); 277 - return PTR_ERR(intr->dst_irq); 245 + return PTR_ERR(intr->out_irqs); 278 246 } 279 247 280 248 domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev), ··· 283 251 dev_err(dev, "Failed to allocate IRQ domain\n"); 284 252 return -ENOMEM; 285 253 } 254 + 255 + dev_info(dev, "Interrupt Router %d domain created\n", intr->ti_sci_id); 286 256 287 257 return 0; 288 258 }