1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b8f3ebe6SMinghuan Lian /* 3b8f3ebe6SMinghuan Lian * Freescale SCFG MSI(-X) support 4b8f3ebe6SMinghuan Lian * 5b8f3ebe6SMinghuan Lian * Copyright (C) 2016 Freescale Semiconductor. 6b8f3ebe6SMinghuan Lian * 7b8f3ebe6SMinghuan Lian * Author: Minghuan Lian <Minghuan.Lian@nxp.com> 8b8f3ebe6SMinghuan Lian */ 9b8f3ebe6SMinghuan Lian 10b8f3ebe6SMinghuan Lian #include <linux/kernel.h> 11b8f3ebe6SMinghuan Lian #include <linux/module.h> 12b8f3ebe6SMinghuan Lian #include <linux/msi.h> 13b8f3ebe6SMinghuan Lian #include <linux/interrupt.h> 14b8f3ebe6SMinghuan Lian #include <linux/irq.h> 15b8f3ebe6SMinghuan Lian #include <linux/irqchip/chained_irq.h> 16b8f3ebe6SMinghuan Lian #include <linux/irqdomain.h> 174dd5da65SMinghuan Lian #include <linux/of_irq.h> 18b8f3ebe6SMinghuan Lian #include <linux/of_pci.h> 19b8f3ebe6SMinghuan Lian #include <linux/of_platform.h> 20b8f3ebe6SMinghuan Lian #include <linux/spinlock.h> 210cdd431cSLaurentiu Tudor #include <linux/dma-iommu.h> 22b8f3ebe6SMinghuan Lian 234dd5da65SMinghuan Lian #define MSI_IRQS_PER_MSIR 32 244dd5da65SMinghuan Lian #define MSI_MSIR_OFFSET 4 254dd5da65SMinghuan Lian 26fd100dabSMinghuan Lian #define MSI_LS1043V1_1_IRQS_PER_MSIR 8 27fd100dabSMinghuan Lian #define MSI_LS1043V1_1_MSIR_OFFSET 0x10 28fd100dabSMinghuan Lian 294dd5da65SMinghuan Lian struct ls_scfg_msi_cfg { 304dd5da65SMinghuan Lian u32 ibs_shift; /* Shift of interrupt bit select */ 31fd100dabSMinghuan Lian u32 msir_irqs; /* The irq number per MSIR */ 32fd100dabSMinghuan Lian u32 msir_base; /* The base address of MSIR */ 334dd5da65SMinghuan Lian }; 344dd5da65SMinghuan Lian 354dd5da65SMinghuan Lian struct ls_scfg_msir { 364dd5da65SMinghuan Lian struct ls_scfg_msi *msi_data; 374dd5da65SMinghuan Lian unsigned int index; 384dd5da65SMinghuan Lian unsigned int gic_irq; 39fd100dabSMinghuan Lian unsigned int bit_start; 40fd100dabSMinghuan Lian unsigned int bit_end; 41ae3efabfSMinghuan Lian unsigned int srs; /* Shared interrupt register select */ 424dd5da65SMinghuan Lian void __iomem *reg; 434dd5da65SMinghuan Lian }; 44b8f3ebe6SMinghuan Lian 45b8f3ebe6SMinghuan Lian struct ls_scfg_msi { 46b8f3ebe6SMinghuan Lian spinlock_t lock; 47b8f3ebe6SMinghuan Lian struct platform_device *pdev; 48b8f3ebe6SMinghuan Lian struct irq_domain *parent; 49b8f3ebe6SMinghuan Lian struct irq_domain *msi_domain; 50b8f3ebe6SMinghuan Lian void __iomem *regs; 51b8f3ebe6SMinghuan Lian phys_addr_t msiir_addr; 524dd5da65SMinghuan Lian struct ls_scfg_msi_cfg *cfg; 534dd5da65SMinghuan Lian u32 msir_num; 544dd5da65SMinghuan Lian struct ls_scfg_msir *msir; 554dd5da65SMinghuan Lian u32 irqs_num; 564dd5da65SMinghuan Lian unsigned long *used; 57b8f3ebe6SMinghuan Lian }; 58b8f3ebe6SMinghuan Lian 59b8f3ebe6SMinghuan Lian static struct irq_chip ls_scfg_msi_irq_chip = { 60b8f3ebe6SMinghuan Lian .name = "MSI", 61b8f3ebe6SMinghuan Lian .irq_mask = pci_msi_mask_irq, 62b8f3ebe6SMinghuan Lian .irq_unmask = pci_msi_unmask_irq, 63b8f3ebe6SMinghuan Lian }; 64b8f3ebe6SMinghuan Lian 65b8f3ebe6SMinghuan Lian static struct msi_domain_info ls_scfg_msi_domain_info = { 66b8f3ebe6SMinghuan Lian .flags = (MSI_FLAG_USE_DEF_DOM_OPS | 67b8f3ebe6SMinghuan Lian MSI_FLAG_USE_DEF_CHIP_OPS | 68b8f3ebe6SMinghuan Lian MSI_FLAG_PCI_MSIX), 69b8f3ebe6SMinghuan Lian .chip = &ls_scfg_msi_irq_chip, 70b8f3ebe6SMinghuan Lian }; 71b8f3ebe6SMinghuan Lian 72ae3efabfSMinghuan Lian static int msi_affinity_flag = 1; 73ae3efabfSMinghuan Lian 74ae3efabfSMinghuan Lian static int __init early_parse_ls_scfg_msi(char *p) 75ae3efabfSMinghuan Lian { 76ae3efabfSMinghuan Lian if (p && strncmp(p, "no-affinity", 11) == 0) 77ae3efabfSMinghuan Lian msi_affinity_flag = 0; 78ae3efabfSMinghuan Lian else 79ae3efabfSMinghuan Lian msi_affinity_flag = 1; 80ae3efabfSMinghuan Lian 81ae3efabfSMinghuan Lian return 0; 82ae3efabfSMinghuan Lian } 83ae3efabfSMinghuan Lian early_param("lsmsi", early_parse_ls_scfg_msi); 84ae3efabfSMinghuan Lian 85b8f3ebe6SMinghuan Lian static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) 86b8f3ebe6SMinghuan Lian { 87b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); 88b8f3ebe6SMinghuan Lian 89b8f3ebe6SMinghuan Lian msg->address_hi = upper_32_bits(msi_data->msiir_addr); 90b8f3ebe6SMinghuan Lian msg->address_lo = lower_32_bits(msi_data->msiir_addr); 914dd5da65SMinghuan Lian msg->data = data->hwirq; 92ae3efabfSMinghuan Lian 93893fbfffSMarc Zyngier if (msi_affinity_flag) { 94893fbfffSMarc Zyngier const struct cpumask *mask; 95893fbfffSMarc Zyngier 96893fbfffSMarc Zyngier mask = irq_data_get_effective_affinity_mask(data); 97893fbfffSMarc Zyngier msg->data |= cpumask_first(mask); 98893fbfffSMarc Zyngier } 990cdd431cSLaurentiu Tudor 1002cb3b165SJulien Grall iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); 101b8f3ebe6SMinghuan Lian } 102b8f3ebe6SMinghuan Lian 103b8f3ebe6SMinghuan Lian static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, 104b8f3ebe6SMinghuan Lian const struct cpumask *mask, bool force) 105b8f3ebe6SMinghuan Lian { 106ae3efabfSMinghuan Lian struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data); 107ae3efabfSMinghuan Lian u32 cpu; 108ae3efabfSMinghuan Lian 109ae3efabfSMinghuan Lian if (!msi_affinity_flag) 110b8f3ebe6SMinghuan Lian return -EINVAL; 111ae3efabfSMinghuan Lian 112ae3efabfSMinghuan Lian if (!force) 113ae3efabfSMinghuan Lian cpu = cpumask_any_and(mask, cpu_online_mask); 114ae3efabfSMinghuan Lian else 115ae3efabfSMinghuan Lian cpu = cpumask_first(mask); 116ae3efabfSMinghuan Lian 117ae3efabfSMinghuan Lian if (cpu >= msi_data->msir_num) 118ae3efabfSMinghuan Lian return -EINVAL; 119ae3efabfSMinghuan Lian 120ae3efabfSMinghuan Lian if (msi_data->msir[cpu].gic_irq <= 0) { 121ae3efabfSMinghuan Lian pr_warn("cannot bind the irq to cpu%d\n", cpu); 122ae3efabfSMinghuan Lian return -EINVAL; 123ae3efabfSMinghuan Lian } 124ae3efabfSMinghuan Lian 125893fbfffSMarc Zyngier irq_data_update_effective_affinity(irq_data, cpumask_of(cpu)); 126ae3efabfSMinghuan Lian 127ae3efabfSMinghuan Lian return IRQ_SET_MASK_OK; 128b8f3ebe6SMinghuan Lian } 129b8f3ebe6SMinghuan Lian 130b8f3ebe6SMinghuan Lian static struct irq_chip ls_scfg_msi_parent_chip = { 131b8f3ebe6SMinghuan Lian .name = "SCFG", 132b8f3ebe6SMinghuan Lian .irq_compose_msi_msg = ls_scfg_msi_compose_msg, 133b8f3ebe6SMinghuan Lian .irq_set_affinity = ls_scfg_msi_set_affinity, 134b8f3ebe6SMinghuan Lian }; 135b8f3ebe6SMinghuan Lian 136b8f3ebe6SMinghuan Lian static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, 137b8f3ebe6SMinghuan Lian unsigned int virq, 138b8f3ebe6SMinghuan Lian unsigned int nr_irqs, 139b8f3ebe6SMinghuan Lian void *args) 140b8f3ebe6SMinghuan Lian { 1412cb3b165SJulien Grall msi_alloc_info_t *info = args; 142b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = domain->host_data; 143b8f3ebe6SMinghuan Lian int pos, err = 0; 144b8f3ebe6SMinghuan Lian 145b8f3ebe6SMinghuan Lian WARN_ON(nr_irqs != 1); 146b8f3ebe6SMinghuan Lian 147b8f3ebe6SMinghuan Lian spin_lock(&msi_data->lock); 1484dd5da65SMinghuan Lian pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num); 1494dd5da65SMinghuan Lian if (pos < msi_data->irqs_num) 150b8f3ebe6SMinghuan Lian __set_bit(pos, msi_data->used); 151b8f3ebe6SMinghuan Lian else 152b8f3ebe6SMinghuan Lian err = -ENOSPC; 153b8f3ebe6SMinghuan Lian spin_unlock(&msi_data->lock); 154b8f3ebe6SMinghuan Lian 155b8f3ebe6SMinghuan Lian if (err) 156b8f3ebe6SMinghuan Lian return err; 157b8f3ebe6SMinghuan Lian 1582cb3b165SJulien Grall err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr); 1592cb3b165SJulien Grall if (err) 1602cb3b165SJulien Grall return err; 1612cb3b165SJulien Grall 162b8f3ebe6SMinghuan Lian irq_domain_set_info(domain, virq, pos, 163b8f3ebe6SMinghuan Lian &ls_scfg_msi_parent_chip, msi_data, 164b8f3ebe6SMinghuan Lian handle_simple_irq, NULL, NULL); 165b8f3ebe6SMinghuan Lian 166b8f3ebe6SMinghuan Lian return 0; 167b8f3ebe6SMinghuan Lian } 168b8f3ebe6SMinghuan Lian 169b8f3ebe6SMinghuan Lian static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, 170b8f3ebe6SMinghuan Lian unsigned int virq, unsigned int nr_irqs) 171b8f3ebe6SMinghuan Lian { 172b8f3ebe6SMinghuan Lian struct irq_data *d = irq_domain_get_irq_data(domain, virq); 173b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d); 174b8f3ebe6SMinghuan Lian int pos; 175b8f3ebe6SMinghuan Lian 176b8f3ebe6SMinghuan Lian pos = d->hwirq; 1774dd5da65SMinghuan Lian if (pos < 0 || pos >= msi_data->irqs_num) { 178b8f3ebe6SMinghuan Lian pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); 179b8f3ebe6SMinghuan Lian return; 180b8f3ebe6SMinghuan Lian } 181b8f3ebe6SMinghuan Lian 182b8f3ebe6SMinghuan Lian spin_lock(&msi_data->lock); 183b8f3ebe6SMinghuan Lian __clear_bit(pos, msi_data->used); 184b8f3ebe6SMinghuan Lian spin_unlock(&msi_data->lock); 185b8f3ebe6SMinghuan Lian } 186b8f3ebe6SMinghuan Lian 187b8f3ebe6SMinghuan Lian static const struct irq_domain_ops ls_scfg_msi_domain_ops = { 188b8f3ebe6SMinghuan Lian .alloc = ls_scfg_msi_domain_irq_alloc, 189b8f3ebe6SMinghuan Lian .free = ls_scfg_msi_domain_irq_free, 190b8f3ebe6SMinghuan Lian }; 191b8f3ebe6SMinghuan Lian 192b8f3ebe6SMinghuan Lian static void ls_scfg_msi_irq_handler(struct irq_desc *desc) 193b8f3ebe6SMinghuan Lian { 1944dd5da65SMinghuan Lian struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc); 1954dd5da65SMinghuan Lian struct ls_scfg_msi *msi_data = msir->msi_data; 196b8f3ebe6SMinghuan Lian unsigned long val; 197046a6ee2SMarc Zyngier int pos, size, hwirq; 198b8f3ebe6SMinghuan Lian 199b8f3ebe6SMinghuan Lian chained_irq_enter(irq_desc_get_chip(desc), desc); 200b8f3ebe6SMinghuan Lian 2014dd5da65SMinghuan Lian val = ioread32be(msir->reg); 202fd100dabSMinghuan Lian 203fd100dabSMinghuan Lian pos = msir->bit_start; 204fd100dabSMinghuan Lian size = msir->bit_end + 1; 205fd100dabSMinghuan Lian 206fd100dabSMinghuan Lian for_each_set_bit_from(pos, &val, size) { 207fd100dabSMinghuan Lian hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) | 208ae3efabfSMinghuan Lian msir->srs; 209046a6ee2SMarc Zyngier generic_handle_domain_irq(msi_data->parent, hwirq); 210b8f3ebe6SMinghuan Lian } 211b8f3ebe6SMinghuan Lian 212b8f3ebe6SMinghuan Lian chained_irq_exit(irq_desc_get_chip(desc), desc); 213b8f3ebe6SMinghuan Lian } 214b8f3ebe6SMinghuan Lian 215b8f3ebe6SMinghuan Lian static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) 216b8f3ebe6SMinghuan Lian { 217b8f3ebe6SMinghuan Lian /* Initialize MSI domain parent */ 218b8f3ebe6SMinghuan Lian msi_data->parent = irq_domain_add_linear(NULL, 2194dd5da65SMinghuan Lian msi_data->irqs_num, 220b8f3ebe6SMinghuan Lian &ls_scfg_msi_domain_ops, 221b8f3ebe6SMinghuan Lian msi_data); 222b8f3ebe6SMinghuan Lian if (!msi_data->parent) { 223b8f3ebe6SMinghuan Lian dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); 224b8f3ebe6SMinghuan Lian return -ENOMEM; 225b8f3ebe6SMinghuan Lian } 226b8f3ebe6SMinghuan Lian 227b8f3ebe6SMinghuan Lian msi_data->msi_domain = pci_msi_create_irq_domain( 228b8f3ebe6SMinghuan Lian of_node_to_fwnode(msi_data->pdev->dev.of_node), 229b8f3ebe6SMinghuan Lian &ls_scfg_msi_domain_info, 230b8f3ebe6SMinghuan Lian msi_data->parent); 231b8f3ebe6SMinghuan Lian if (!msi_data->msi_domain) { 232b8f3ebe6SMinghuan Lian dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n"); 233b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->parent); 234b8f3ebe6SMinghuan Lian return -ENOMEM; 235b8f3ebe6SMinghuan Lian } 236b8f3ebe6SMinghuan Lian 237b8f3ebe6SMinghuan Lian return 0; 238b8f3ebe6SMinghuan Lian } 239b8f3ebe6SMinghuan Lian 2404dd5da65SMinghuan Lian static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index) 2414dd5da65SMinghuan Lian { 2424dd5da65SMinghuan Lian struct ls_scfg_msir *msir; 2434dd5da65SMinghuan Lian int virq, i, hwirq; 2444dd5da65SMinghuan Lian 2454dd5da65SMinghuan Lian virq = platform_get_irq(msi_data->pdev, index); 2464dd5da65SMinghuan Lian if (virq <= 0) 2474dd5da65SMinghuan Lian return -ENODEV; 2484dd5da65SMinghuan Lian 2494dd5da65SMinghuan Lian msir = &msi_data->msir[index]; 2504dd5da65SMinghuan Lian msir->index = index; 2514dd5da65SMinghuan Lian msir->msi_data = msi_data; 2524dd5da65SMinghuan Lian msir->gic_irq = virq; 253fd100dabSMinghuan Lian msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index; 254fd100dabSMinghuan Lian 255fd100dabSMinghuan Lian if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) { 256fd100dabSMinghuan Lian msir->bit_start = 32 - ((msir->index + 1) * 257fd100dabSMinghuan Lian MSI_LS1043V1_1_IRQS_PER_MSIR); 258fd100dabSMinghuan Lian msir->bit_end = msir->bit_start + 259fd100dabSMinghuan Lian MSI_LS1043V1_1_IRQS_PER_MSIR - 1; 260fd100dabSMinghuan Lian } else { 261fd100dabSMinghuan Lian msir->bit_start = 0; 262fd100dabSMinghuan Lian msir->bit_end = msi_data->cfg->msir_irqs - 1; 263fd100dabSMinghuan Lian } 2644dd5da65SMinghuan Lian 2654dd5da65SMinghuan Lian irq_set_chained_handler_and_data(msir->gic_irq, 2664dd5da65SMinghuan Lian ls_scfg_msi_irq_handler, 2674dd5da65SMinghuan Lian msir); 2684dd5da65SMinghuan Lian 269ae3efabfSMinghuan Lian if (msi_affinity_flag) { 270ae3efabfSMinghuan Lian /* Associate MSIR interrupt to the cpu */ 271ae3efabfSMinghuan Lian irq_set_affinity(msir->gic_irq, get_cpu_mask(index)); 272ae3efabfSMinghuan Lian msir->srs = 0; /* This value is determined by the CPU */ 273ae3efabfSMinghuan Lian } else 274ae3efabfSMinghuan Lian msir->srs = index; 275ae3efabfSMinghuan Lian 2764dd5da65SMinghuan Lian /* Release the hwirqs corresponding to this MSIR */ 277ae3efabfSMinghuan Lian if (!msi_affinity_flag || msir->index == 0) { 278fd100dabSMinghuan Lian for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 2794dd5da65SMinghuan Lian hwirq = i << msi_data->cfg->ibs_shift | msir->index; 2804dd5da65SMinghuan Lian bitmap_clear(msi_data->used, hwirq, 1); 2814dd5da65SMinghuan Lian } 282ae3efabfSMinghuan Lian } 2834dd5da65SMinghuan Lian 2844dd5da65SMinghuan Lian return 0; 2854dd5da65SMinghuan Lian } 2864dd5da65SMinghuan Lian 2874dd5da65SMinghuan Lian static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir) 2884dd5da65SMinghuan Lian { 2894dd5da65SMinghuan Lian struct ls_scfg_msi *msi_data = msir->msi_data; 2904dd5da65SMinghuan Lian int i, hwirq; 2914dd5da65SMinghuan Lian 2924dd5da65SMinghuan Lian if (msir->gic_irq > 0) 2934dd5da65SMinghuan Lian irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL); 2944dd5da65SMinghuan Lian 295fd100dabSMinghuan Lian for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 2964dd5da65SMinghuan Lian hwirq = i << msi_data->cfg->ibs_shift | msir->index; 2974dd5da65SMinghuan Lian bitmap_set(msi_data->used, hwirq, 1); 2984dd5da65SMinghuan Lian } 2994dd5da65SMinghuan Lian 3004dd5da65SMinghuan Lian return 0; 3014dd5da65SMinghuan Lian } 3024dd5da65SMinghuan Lian 3034dd5da65SMinghuan Lian static struct ls_scfg_msi_cfg ls1021_msi_cfg = { 3044dd5da65SMinghuan Lian .ibs_shift = 3, 305fd100dabSMinghuan Lian .msir_irqs = MSI_IRQS_PER_MSIR, 306fd100dabSMinghuan Lian .msir_base = MSI_MSIR_OFFSET, 3074dd5da65SMinghuan Lian }; 3084dd5da65SMinghuan Lian 3094dd5da65SMinghuan Lian static struct ls_scfg_msi_cfg ls1046_msi_cfg = { 3104dd5da65SMinghuan Lian .ibs_shift = 2, 311fd100dabSMinghuan Lian .msir_irqs = MSI_IRQS_PER_MSIR, 312fd100dabSMinghuan Lian .msir_base = MSI_MSIR_OFFSET, 313fd100dabSMinghuan Lian }; 314fd100dabSMinghuan Lian 315fd100dabSMinghuan Lian static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = { 316fd100dabSMinghuan Lian .ibs_shift = 2, 317fd100dabSMinghuan Lian .msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR, 318fd100dabSMinghuan Lian .msir_base = MSI_LS1043V1_1_MSIR_OFFSET, 3194dd5da65SMinghuan Lian }; 3204dd5da65SMinghuan Lian 3214dd5da65SMinghuan Lian static const struct of_device_id ls_scfg_msi_id[] = { 3224dd5da65SMinghuan Lian /* The following two misspelled compatibles are obsolete */ 3234dd5da65SMinghuan Lian { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg}, 3244dd5da65SMinghuan Lian { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg}, 3254dd5da65SMinghuan Lian 32668ace22eSHou Zhiqiang { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg }, 3274dd5da65SMinghuan Lian { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg }, 3284dd5da65SMinghuan Lian { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg }, 329fd100dabSMinghuan Lian { .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg }, 3304dd5da65SMinghuan Lian { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg }, 3314dd5da65SMinghuan Lian {}, 3324dd5da65SMinghuan Lian }; 3334dd5da65SMinghuan Lian MODULE_DEVICE_TABLE(of, ls_scfg_msi_id); 3344dd5da65SMinghuan Lian 335b8f3ebe6SMinghuan Lian static int ls_scfg_msi_probe(struct platform_device *pdev) 336b8f3ebe6SMinghuan Lian { 3374dd5da65SMinghuan Lian const struct of_device_id *match; 338b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data; 339b8f3ebe6SMinghuan Lian struct resource *res; 3404dd5da65SMinghuan Lian int i, ret; 3414dd5da65SMinghuan Lian 3424dd5da65SMinghuan Lian match = of_match_device(ls_scfg_msi_id, &pdev->dev); 3434dd5da65SMinghuan Lian if (!match) 3444dd5da65SMinghuan Lian return -ENODEV; 345b8f3ebe6SMinghuan Lian 346b8f3ebe6SMinghuan Lian msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 347b8f3ebe6SMinghuan Lian if (!msi_data) 348b8f3ebe6SMinghuan Lian return -ENOMEM; 349b8f3ebe6SMinghuan Lian 3504dd5da65SMinghuan Lian msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data; 3514dd5da65SMinghuan Lian 352b8f3ebe6SMinghuan Lian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 353b8f3ebe6SMinghuan Lian msi_data->regs = devm_ioremap_resource(&pdev->dev, res); 354b8f3ebe6SMinghuan Lian if (IS_ERR(msi_data->regs)) { 355b8f3ebe6SMinghuan Lian dev_err(&pdev->dev, "failed to initialize 'regs'\n"); 356b8f3ebe6SMinghuan Lian return PTR_ERR(msi_data->regs); 357b8f3ebe6SMinghuan Lian } 358b8f3ebe6SMinghuan Lian msi_data->msiir_addr = res->start; 359b8f3ebe6SMinghuan Lian 360b8f3ebe6SMinghuan Lian msi_data->pdev = pdev; 361b8f3ebe6SMinghuan Lian spin_lock_init(&msi_data->lock); 362b8f3ebe6SMinghuan Lian 3634dd5da65SMinghuan Lian msi_data->irqs_num = MSI_IRQS_PER_MSIR * 3644dd5da65SMinghuan Lian (1 << msi_data->cfg->ibs_shift); 36543a1965fSAndy Shevchenko msi_data->used = devm_bitmap_zalloc(&pdev->dev, msi_data->irqs_num, GFP_KERNEL); 3664dd5da65SMinghuan Lian if (!msi_data->used) 3674dd5da65SMinghuan Lian return -ENOMEM; 3684dd5da65SMinghuan Lian /* 3694dd5da65SMinghuan Lian * Reserve all the hwirqs 3704dd5da65SMinghuan Lian * The available hwirqs will be released in ls1_msi_setup_hwirq() 3714dd5da65SMinghuan Lian */ 3724dd5da65SMinghuan Lian bitmap_set(msi_data->used, 0, msi_data->irqs_num); 3734dd5da65SMinghuan Lian 3744dd5da65SMinghuan Lian msi_data->msir_num = of_irq_count(pdev->dev.of_node); 375ae3efabfSMinghuan Lian 376ae3efabfSMinghuan Lian if (msi_affinity_flag) { 377ae3efabfSMinghuan Lian u32 cpu_num; 378ae3efabfSMinghuan Lian 379ae3efabfSMinghuan Lian cpu_num = num_possible_cpus(); 380ae3efabfSMinghuan Lian if (msi_data->msir_num >= cpu_num) 381ae3efabfSMinghuan Lian msi_data->msir_num = cpu_num; 382ae3efabfSMinghuan Lian else 383ae3efabfSMinghuan Lian msi_affinity_flag = 0; 384ae3efabfSMinghuan Lian } 385ae3efabfSMinghuan Lian 3864dd5da65SMinghuan Lian msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num, 3874dd5da65SMinghuan Lian sizeof(*msi_data->msir), 3884dd5da65SMinghuan Lian GFP_KERNEL); 3894dd5da65SMinghuan Lian if (!msi_data->msir) 3904dd5da65SMinghuan Lian return -ENOMEM; 3914dd5da65SMinghuan Lian 3924dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++) 3934dd5da65SMinghuan Lian ls_scfg_msi_setup_hwirq(msi_data, i); 3944dd5da65SMinghuan Lian 395b8f3ebe6SMinghuan Lian ret = ls_scfg_msi_domains_init(msi_data); 396b8f3ebe6SMinghuan Lian if (ret) 397b8f3ebe6SMinghuan Lian return ret; 398b8f3ebe6SMinghuan Lian 399b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, msi_data); 400b8f3ebe6SMinghuan Lian 401b8f3ebe6SMinghuan Lian return 0; 402b8f3ebe6SMinghuan Lian } 403b8f3ebe6SMinghuan Lian 404b8f3ebe6SMinghuan Lian static int ls_scfg_msi_remove(struct platform_device *pdev) 405b8f3ebe6SMinghuan Lian { 406b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); 4074dd5da65SMinghuan Lian int i; 408b8f3ebe6SMinghuan Lian 4094dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++) 4104dd5da65SMinghuan Lian ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]); 411b8f3ebe6SMinghuan Lian 412b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->msi_domain); 413b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->parent); 414b8f3ebe6SMinghuan Lian 415b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, NULL); 416b8f3ebe6SMinghuan Lian 417b8f3ebe6SMinghuan Lian return 0; 418b8f3ebe6SMinghuan Lian } 419b8f3ebe6SMinghuan Lian 420b8f3ebe6SMinghuan Lian static struct platform_driver ls_scfg_msi_driver = { 421b8f3ebe6SMinghuan Lian .driver = { 422b8f3ebe6SMinghuan Lian .name = "ls-scfg-msi", 423b8f3ebe6SMinghuan Lian .of_match_table = ls_scfg_msi_id, 424b8f3ebe6SMinghuan Lian }, 425b8f3ebe6SMinghuan Lian .probe = ls_scfg_msi_probe, 426b8f3ebe6SMinghuan Lian .remove = ls_scfg_msi_remove, 427b8f3ebe6SMinghuan Lian }; 428b8f3ebe6SMinghuan Lian 429b8f3ebe6SMinghuan Lian module_platform_driver(ls_scfg_msi_driver); 430b8f3ebe6SMinghuan Lian 431b8f3ebe6SMinghuan Lian MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>"); 432b8f3ebe6SMinghuan Lian MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver"); 433b8f3ebe6SMinghuan Lian MODULE_LICENSE("GPL v2"); 434