1 /* 2 * Freescale SCFG MSI(-X) support 3 * 4 * Copyright (C) 2016 Freescale Semiconductor. 5 * 6 * Author: Minghuan Lian <Minghuan.Lian@nxp.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/msi.h> 16 #include <linux/interrupt.h> 17 #include <linux/irq.h> 18 #include <linux/irqchip/chained_irq.h> 19 #include <linux/irqdomain.h> 20 #include <linux/of_irq.h> 21 #include <linux/of_pci.h> 22 #include <linux/of_platform.h> 23 #include <linux/spinlock.h> 24 #include <linux/dma-iommu.h> 25 26 #define MSI_IRQS_PER_MSIR 32 27 #define MSI_MSIR_OFFSET 4 28 29 #define MSI_LS1043V1_1_IRQS_PER_MSIR 8 30 #define MSI_LS1043V1_1_MSIR_OFFSET 0x10 31 32 struct ls_scfg_msi_cfg { 33 u32 ibs_shift; /* Shift of interrupt bit select */ 34 u32 msir_irqs; /* The irq number per MSIR */ 35 u32 msir_base; /* The base address of MSIR */ 36 }; 37 38 struct ls_scfg_msir { 39 struct ls_scfg_msi *msi_data; 40 unsigned int index; 41 unsigned int gic_irq; 42 unsigned int bit_start; 43 unsigned int bit_end; 44 unsigned int srs; /* Shared interrupt register select */ 45 void __iomem *reg; 46 }; 47 48 struct ls_scfg_msi { 49 spinlock_t lock; 50 struct platform_device *pdev; 51 struct irq_domain *parent; 52 struct irq_domain *msi_domain; 53 void __iomem *regs; 54 phys_addr_t msiir_addr; 55 struct ls_scfg_msi_cfg *cfg; 56 u32 msir_num; 57 struct ls_scfg_msir *msir; 58 u32 irqs_num; 59 unsigned long *used; 60 }; 61 62 static struct irq_chip ls_scfg_msi_irq_chip = { 63 .name = "MSI", 64 .irq_mask = pci_msi_mask_irq, 65 .irq_unmask = pci_msi_unmask_irq, 66 }; 67 68 static struct msi_domain_info ls_scfg_msi_domain_info = { 69 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | 70 MSI_FLAG_USE_DEF_CHIP_OPS | 71 MSI_FLAG_PCI_MSIX), 72 .chip = &ls_scfg_msi_irq_chip, 73 }; 74 75 static int msi_affinity_flag = 1; 76 77 static int __init early_parse_ls_scfg_msi(char *p) 78 { 79 if (p && strncmp(p, "no-affinity", 11) == 0) 80 msi_affinity_flag = 0; 81 else 82 msi_affinity_flag = 1; 83 84 return 0; 85 } 86 early_param("lsmsi", early_parse_ls_scfg_msi); 87 88 static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) 89 { 90 struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); 91 92 msg->address_hi = upper_32_bits(msi_data->msiir_addr); 93 msg->address_lo = lower_32_bits(msi_data->msiir_addr); 94 msg->data = data->hwirq; 95 96 if (msi_affinity_flag) { 97 const struct cpumask *mask; 98 99 mask = irq_data_get_effective_affinity_mask(data); 100 msg->data |= cpumask_first(mask); 101 } 102 103 iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); 104 } 105 106 static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, 107 const struct cpumask *mask, bool force) 108 { 109 struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data); 110 u32 cpu; 111 112 if (!msi_affinity_flag) 113 return -EINVAL; 114 115 if (!force) 116 cpu = cpumask_any_and(mask, cpu_online_mask); 117 else 118 cpu = cpumask_first(mask); 119 120 if (cpu >= msi_data->msir_num) 121 return -EINVAL; 122 123 if (msi_data->msir[cpu].gic_irq <= 0) { 124 pr_warn("cannot bind the irq to cpu%d\n", cpu); 125 return -EINVAL; 126 } 127 128 irq_data_update_effective_affinity(irq_data, cpumask_of(cpu)); 129 130 return IRQ_SET_MASK_OK; 131 } 132 133 static struct irq_chip ls_scfg_msi_parent_chip = { 134 .name = "SCFG", 135 .irq_compose_msi_msg = ls_scfg_msi_compose_msg, 136 .irq_set_affinity = ls_scfg_msi_set_affinity, 137 }; 138 139 static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, 140 unsigned int virq, 141 unsigned int nr_irqs, 142 void *args) 143 { 144 msi_alloc_info_t *info = args; 145 struct ls_scfg_msi *msi_data = domain->host_data; 146 int pos, err = 0; 147 148 WARN_ON(nr_irqs != 1); 149 150 spin_lock(&msi_data->lock); 151 pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num); 152 if (pos < msi_data->irqs_num) 153 __set_bit(pos, msi_data->used); 154 else 155 err = -ENOSPC; 156 spin_unlock(&msi_data->lock); 157 158 if (err) 159 return err; 160 161 err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr); 162 if (err) 163 return err; 164 165 irq_domain_set_info(domain, virq, pos, 166 &ls_scfg_msi_parent_chip, msi_data, 167 handle_simple_irq, NULL, NULL); 168 169 return 0; 170 } 171 172 static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, 173 unsigned int virq, unsigned int nr_irqs) 174 { 175 struct irq_data *d = irq_domain_get_irq_data(domain, virq); 176 struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d); 177 int pos; 178 179 pos = d->hwirq; 180 if (pos < 0 || pos >= msi_data->irqs_num) { 181 pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); 182 return; 183 } 184 185 spin_lock(&msi_data->lock); 186 __clear_bit(pos, msi_data->used); 187 spin_unlock(&msi_data->lock); 188 } 189 190 static const struct irq_domain_ops ls_scfg_msi_domain_ops = { 191 .alloc = ls_scfg_msi_domain_irq_alloc, 192 .free = ls_scfg_msi_domain_irq_free, 193 }; 194 195 static void ls_scfg_msi_irq_handler(struct irq_desc *desc) 196 { 197 struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc); 198 struct ls_scfg_msi *msi_data = msir->msi_data; 199 unsigned long val; 200 int pos, size, virq, hwirq; 201 202 chained_irq_enter(irq_desc_get_chip(desc), desc); 203 204 val = ioread32be(msir->reg); 205 206 pos = msir->bit_start; 207 size = msir->bit_end + 1; 208 209 for_each_set_bit_from(pos, &val, size) { 210 hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) | 211 msir->srs; 212 virq = irq_find_mapping(msi_data->parent, hwirq); 213 if (virq) 214 generic_handle_irq(virq); 215 } 216 217 chained_irq_exit(irq_desc_get_chip(desc), desc); 218 } 219 220 static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) 221 { 222 /* Initialize MSI domain parent */ 223 msi_data->parent = irq_domain_add_linear(NULL, 224 msi_data->irqs_num, 225 &ls_scfg_msi_domain_ops, 226 msi_data); 227 if (!msi_data->parent) { 228 dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); 229 return -ENOMEM; 230 } 231 232 msi_data->msi_domain = pci_msi_create_irq_domain( 233 of_node_to_fwnode(msi_data->pdev->dev.of_node), 234 &ls_scfg_msi_domain_info, 235 msi_data->parent); 236 if (!msi_data->msi_domain) { 237 dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n"); 238 irq_domain_remove(msi_data->parent); 239 return -ENOMEM; 240 } 241 242 return 0; 243 } 244 245 static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index) 246 { 247 struct ls_scfg_msir *msir; 248 int virq, i, hwirq; 249 250 virq = platform_get_irq(msi_data->pdev, index); 251 if (virq <= 0) 252 return -ENODEV; 253 254 msir = &msi_data->msir[index]; 255 msir->index = index; 256 msir->msi_data = msi_data; 257 msir->gic_irq = virq; 258 msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index; 259 260 if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) { 261 msir->bit_start = 32 - ((msir->index + 1) * 262 MSI_LS1043V1_1_IRQS_PER_MSIR); 263 msir->bit_end = msir->bit_start + 264 MSI_LS1043V1_1_IRQS_PER_MSIR - 1; 265 } else { 266 msir->bit_start = 0; 267 msir->bit_end = msi_data->cfg->msir_irqs - 1; 268 } 269 270 irq_set_chained_handler_and_data(msir->gic_irq, 271 ls_scfg_msi_irq_handler, 272 msir); 273 274 if (msi_affinity_flag) { 275 /* Associate MSIR interrupt to the cpu */ 276 irq_set_affinity(msir->gic_irq, get_cpu_mask(index)); 277 msir->srs = 0; /* This value is determined by the CPU */ 278 } else 279 msir->srs = index; 280 281 /* Release the hwirqs corresponding to this MSIR */ 282 if (!msi_affinity_flag || msir->index == 0) { 283 for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 284 hwirq = i << msi_data->cfg->ibs_shift | msir->index; 285 bitmap_clear(msi_data->used, hwirq, 1); 286 } 287 } 288 289 return 0; 290 } 291 292 static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir) 293 { 294 struct ls_scfg_msi *msi_data = msir->msi_data; 295 int i, hwirq; 296 297 if (msir->gic_irq > 0) 298 irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL); 299 300 for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 301 hwirq = i << msi_data->cfg->ibs_shift | msir->index; 302 bitmap_set(msi_data->used, hwirq, 1); 303 } 304 305 return 0; 306 } 307 308 static struct ls_scfg_msi_cfg ls1021_msi_cfg = { 309 .ibs_shift = 3, 310 .msir_irqs = MSI_IRQS_PER_MSIR, 311 .msir_base = MSI_MSIR_OFFSET, 312 }; 313 314 static struct ls_scfg_msi_cfg ls1046_msi_cfg = { 315 .ibs_shift = 2, 316 .msir_irqs = MSI_IRQS_PER_MSIR, 317 .msir_base = MSI_MSIR_OFFSET, 318 }; 319 320 static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = { 321 .ibs_shift = 2, 322 .msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR, 323 .msir_base = MSI_LS1043V1_1_MSIR_OFFSET, 324 }; 325 326 static const struct of_device_id ls_scfg_msi_id[] = { 327 /* The following two misspelled compatibles are obsolete */ 328 { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg}, 329 { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg}, 330 331 { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg }, 332 { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg }, 333 { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg }, 334 { .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg }, 335 { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg }, 336 {}, 337 }; 338 MODULE_DEVICE_TABLE(of, ls_scfg_msi_id); 339 340 static int ls_scfg_msi_probe(struct platform_device *pdev) 341 { 342 const struct of_device_id *match; 343 struct ls_scfg_msi *msi_data; 344 struct resource *res; 345 int i, ret; 346 347 match = of_match_device(ls_scfg_msi_id, &pdev->dev); 348 if (!match) 349 return -ENODEV; 350 351 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 352 if (!msi_data) 353 return -ENOMEM; 354 355 msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data; 356 357 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 358 msi_data->regs = devm_ioremap_resource(&pdev->dev, res); 359 if (IS_ERR(msi_data->regs)) { 360 dev_err(&pdev->dev, "failed to initialize 'regs'\n"); 361 return PTR_ERR(msi_data->regs); 362 } 363 msi_data->msiir_addr = res->start; 364 365 msi_data->pdev = pdev; 366 spin_lock_init(&msi_data->lock); 367 368 msi_data->irqs_num = MSI_IRQS_PER_MSIR * 369 (1 << msi_data->cfg->ibs_shift); 370 msi_data->used = devm_kcalloc(&pdev->dev, 371 BITS_TO_LONGS(msi_data->irqs_num), 372 sizeof(*msi_data->used), 373 GFP_KERNEL); 374 if (!msi_data->used) 375 return -ENOMEM; 376 /* 377 * Reserve all the hwirqs 378 * The available hwirqs will be released in ls1_msi_setup_hwirq() 379 */ 380 bitmap_set(msi_data->used, 0, msi_data->irqs_num); 381 382 msi_data->msir_num = of_irq_count(pdev->dev.of_node); 383 384 if (msi_affinity_flag) { 385 u32 cpu_num; 386 387 cpu_num = num_possible_cpus(); 388 if (msi_data->msir_num >= cpu_num) 389 msi_data->msir_num = cpu_num; 390 else 391 msi_affinity_flag = 0; 392 } 393 394 msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num, 395 sizeof(*msi_data->msir), 396 GFP_KERNEL); 397 if (!msi_data->msir) 398 return -ENOMEM; 399 400 for (i = 0; i < msi_data->msir_num; i++) 401 ls_scfg_msi_setup_hwirq(msi_data, i); 402 403 ret = ls_scfg_msi_domains_init(msi_data); 404 if (ret) 405 return ret; 406 407 platform_set_drvdata(pdev, msi_data); 408 409 return 0; 410 } 411 412 static int ls_scfg_msi_remove(struct platform_device *pdev) 413 { 414 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); 415 int i; 416 417 for (i = 0; i < msi_data->msir_num; i++) 418 ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]); 419 420 irq_domain_remove(msi_data->msi_domain); 421 irq_domain_remove(msi_data->parent); 422 423 platform_set_drvdata(pdev, NULL); 424 425 return 0; 426 } 427 428 static struct platform_driver ls_scfg_msi_driver = { 429 .driver = { 430 .name = "ls-scfg-msi", 431 .of_match_table = ls_scfg_msi_id, 432 }, 433 .probe = ls_scfg_msi_probe, 434 .remove = ls_scfg_msi_remove, 435 }; 436 437 module_platform_driver(ls_scfg_msi_driver); 438 439 MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>"); 440 MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver"); 441 MODULE_LICENSE("GPL v2"); 442