1*d2912cb1SThomas 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; 197fd100dabSMinghuan Lian int pos, size, virq, 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; 2094dd5da65SMinghuan Lian virq = irq_find_mapping(msi_data->parent, hwirq); 210b8f3ebe6SMinghuan Lian if (virq) 211b8f3ebe6SMinghuan Lian generic_handle_irq(virq); 212b8f3ebe6SMinghuan Lian } 213b8f3ebe6SMinghuan Lian 214b8f3ebe6SMinghuan Lian chained_irq_exit(irq_desc_get_chip(desc), desc); 215b8f3ebe6SMinghuan Lian } 216b8f3ebe6SMinghuan Lian 217b8f3ebe6SMinghuan Lian static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) 218b8f3ebe6SMinghuan Lian { 219b8f3ebe6SMinghuan Lian /* Initialize MSI domain parent */ 220b8f3ebe6SMinghuan Lian msi_data->parent = irq_domain_add_linear(NULL, 2214dd5da65SMinghuan Lian msi_data->irqs_num, 222b8f3ebe6SMinghuan Lian &ls_scfg_msi_domain_ops, 223b8f3ebe6SMinghuan Lian msi_data); 224b8f3ebe6SMinghuan Lian if (!msi_data->parent) { 225b8f3ebe6SMinghuan Lian dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); 226b8f3ebe6SMinghuan Lian return -ENOMEM; 227b8f3ebe6SMinghuan Lian } 228b8f3ebe6SMinghuan Lian 229b8f3ebe6SMinghuan Lian msi_data->msi_domain = pci_msi_create_irq_domain( 230b8f3ebe6SMinghuan Lian of_node_to_fwnode(msi_data->pdev->dev.of_node), 231b8f3ebe6SMinghuan Lian &ls_scfg_msi_domain_info, 232b8f3ebe6SMinghuan Lian msi_data->parent); 233b8f3ebe6SMinghuan Lian if (!msi_data->msi_domain) { 234b8f3ebe6SMinghuan Lian dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n"); 235b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->parent); 236b8f3ebe6SMinghuan Lian return -ENOMEM; 237b8f3ebe6SMinghuan Lian } 238b8f3ebe6SMinghuan Lian 239b8f3ebe6SMinghuan Lian return 0; 240b8f3ebe6SMinghuan Lian } 241b8f3ebe6SMinghuan Lian 2424dd5da65SMinghuan Lian static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index) 2434dd5da65SMinghuan Lian { 2444dd5da65SMinghuan Lian struct ls_scfg_msir *msir; 2454dd5da65SMinghuan Lian int virq, i, hwirq; 2464dd5da65SMinghuan Lian 2474dd5da65SMinghuan Lian virq = platform_get_irq(msi_data->pdev, index); 2484dd5da65SMinghuan Lian if (virq <= 0) 2494dd5da65SMinghuan Lian return -ENODEV; 2504dd5da65SMinghuan Lian 2514dd5da65SMinghuan Lian msir = &msi_data->msir[index]; 2524dd5da65SMinghuan Lian msir->index = index; 2534dd5da65SMinghuan Lian msir->msi_data = msi_data; 2544dd5da65SMinghuan Lian msir->gic_irq = virq; 255fd100dabSMinghuan Lian msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index; 256fd100dabSMinghuan Lian 257fd100dabSMinghuan Lian if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) { 258fd100dabSMinghuan Lian msir->bit_start = 32 - ((msir->index + 1) * 259fd100dabSMinghuan Lian MSI_LS1043V1_1_IRQS_PER_MSIR); 260fd100dabSMinghuan Lian msir->bit_end = msir->bit_start + 261fd100dabSMinghuan Lian MSI_LS1043V1_1_IRQS_PER_MSIR - 1; 262fd100dabSMinghuan Lian } else { 263fd100dabSMinghuan Lian msir->bit_start = 0; 264fd100dabSMinghuan Lian msir->bit_end = msi_data->cfg->msir_irqs - 1; 265fd100dabSMinghuan Lian } 2664dd5da65SMinghuan Lian 2674dd5da65SMinghuan Lian irq_set_chained_handler_and_data(msir->gic_irq, 2684dd5da65SMinghuan Lian ls_scfg_msi_irq_handler, 2694dd5da65SMinghuan Lian msir); 2704dd5da65SMinghuan Lian 271ae3efabfSMinghuan Lian if (msi_affinity_flag) { 272ae3efabfSMinghuan Lian /* Associate MSIR interrupt to the cpu */ 273ae3efabfSMinghuan Lian irq_set_affinity(msir->gic_irq, get_cpu_mask(index)); 274ae3efabfSMinghuan Lian msir->srs = 0; /* This value is determined by the CPU */ 275ae3efabfSMinghuan Lian } else 276ae3efabfSMinghuan Lian msir->srs = index; 277ae3efabfSMinghuan Lian 2784dd5da65SMinghuan Lian /* Release the hwirqs corresponding to this MSIR */ 279ae3efabfSMinghuan Lian if (!msi_affinity_flag || msir->index == 0) { 280fd100dabSMinghuan Lian for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 2814dd5da65SMinghuan Lian hwirq = i << msi_data->cfg->ibs_shift | msir->index; 2824dd5da65SMinghuan Lian bitmap_clear(msi_data->used, hwirq, 1); 2834dd5da65SMinghuan Lian } 284ae3efabfSMinghuan Lian } 2854dd5da65SMinghuan Lian 2864dd5da65SMinghuan Lian return 0; 2874dd5da65SMinghuan Lian } 2884dd5da65SMinghuan Lian 2894dd5da65SMinghuan Lian static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir) 2904dd5da65SMinghuan Lian { 2914dd5da65SMinghuan Lian struct ls_scfg_msi *msi_data = msir->msi_data; 2924dd5da65SMinghuan Lian int i, hwirq; 2934dd5da65SMinghuan Lian 2944dd5da65SMinghuan Lian if (msir->gic_irq > 0) 2954dd5da65SMinghuan Lian irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL); 2964dd5da65SMinghuan Lian 297fd100dabSMinghuan Lian for (i = 0; i < msi_data->cfg->msir_irqs; i++) { 2984dd5da65SMinghuan Lian hwirq = i << msi_data->cfg->ibs_shift | msir->index; 2994dd5da65SMinghuan Lian bitmap_set(msi_data->used, hwirq, 1); 3004dd5da65SMinghuan Lian } 3014dd5da65SMinghuan Lian 3024dd5da65SMinghuan Lian return 0; 3034dd5da65SMinghuan Lian } 3044dd5da65SMinghuan Lian 3054dd5da65SMinghuan Lian static struct ls_scfg_msi_cfg ls1021_msi_cfg = { 3064dd5da65SMinghuan Lian .ibs_shift = 3, 307fd100dabSMinghuan Lian .msir_irqs = MSI_IRQS_PER_MSIR, 308fd100dabSMinghuan Lian .msir_base = MSI_MSIR_OFFSET, 3094dd5da65SMinghuan Lian }; 3104dd5da65SMinghuan Lian 3114dd5da65SMinghuan Lian static struct ls_scfg_msi_cfg ls1046_msi_cfg = { 3124dd5da65SMinghuan Lian .ibs_shift = 2, 313fd100dabSMinghuan Lian .msir_irqs = MSI_IRQS_PER_MSIR, 314fd100dabSMinghuan Lian .msir_base = MSI_MSIR_OFFSET, 315fd100dabSMinghuan Lian }; 316fd100dabSMinghuan Lian 317fd100dabSMinghuan Lian static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = { 318fd100dabSMinghuan Lian .ibs_shift = 2, 319fd100dabSMinghuan Lian .msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR, 320fd100dabSMinghuan Lian .msir_base = MSI_LS1043V1_1_MSIR_OFFSET, 3214dd5da65SMinghuan Lian }; 3224dd5da65SMinghuan Lian 3234dd5da65SMinghuan Lian static const struct of_device_id ls_scfg_msi_id[] = { 3244dd5da65SMinghuan Lian /* The following two misspelled compatibles are obsolete */ 3254dd5da65SMinghuan Lian { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg}, 3264dd5da65SMinghuan Lian { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg}, 3274dd5da65SMinghuan Lian 32868ace22eSHou Zhiqiang { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg }, 3294dd5da65SMinghuan Lian { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg }, 3304dd5da65SMinghuan Lian { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg }, 331fd100dabSMinghuan Lian { .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg }, 3324dd5da65SMinghuan Lian { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg }, 3334dd5da65SMinghuan Lian {}, 3344dd5da65SMinghuan Lian }; 3354dd5da65SMinghuan Lian MODULE_DEVICE_TABLE(of, ls_scfg_msi_id); 3364dd5da65SMinghuan Lian 337b8f3ebe6SMinghuan Lian static int ls_scfg_msi_probe(struct platform_device *pdev) 338b8f3ebe6SMinghuan Lian { 3394dd5da65SMinghuan Lian const struct of_device_id *match; 340b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data; 341b8f3ebe6SMinghuan Lian struct resource *res; 3424dd5da65SMinghuan Lian int i, ret; 3434dd5da65SMinghuan Lian 3444dd5da65SMinghuan Lian match = of_match_device(ls_scfg_msi_id, &pdev->dev); 3454dd5da65SMinghuan Lian if (!match) 3464dd5da65SMinghuan Lian return -ENODEV; 347b8f3ebe6SMinghuan Lian 348b8f3ebe6SMinghuan Lian msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 349b8f3ebe6SMinghuan Lian if (!msi_data) 350b8f3ebe6SMinghuan Lian return -ENOMEM; 351b8f3ebe6SMinghuan Lian 3524dd5da65SMinghuan Lian msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data; 3534dd5da65SMinghuan Lian 354b8f3ebe6SMinghuan Lian res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 355b8f3ebe6SMinghuan Lian msi_data->regs = devm_ioremap_resource(&pdev->dev, res); 356b8f3ebe6SMinghuan Lian if (IS_ERR(msi_data->regs)) { 357b8f3ebe6SMinghuan Lian dev_err(&pdev->dev, "failed to initialize 'regs'\n"); 358b8f3ebe6SMinghuan Lian return PTR_ERR(msi_data->regs); 359b8f3ebe6SMinghuan Lian } 360b8f3ebe6SMinghuan Lian msi_data->msiir_addr = res->start; 361b8f3ebe6SMinghuan Lian 362b8f3ebe6SMinghuan Lian msi_data->pdev = pdev; 363b8f3ebe6SMinghuan Lian spin_lock_init(&msi_data->lock); 364b8f3ebe6SMinghuan Lian 3654dd5da65SMinghuan Lian msi_data->irqs_num = MSI_IRQS_PER_MSIR * 3664dd5da65SMinghuan Lian (1 << msi_data->cfg->ibs_shift); 3674dd5da65SMinghuan Lian msi_data->used = devm_kcalloc(&pdev->dev, 3684dd5da65SMinghuan Lian BITS_TO_LONGS(msi_data->irqs_num), 3694dd5da65SMinghuan Lian sizeof(*msi_data->used), 3704dd5da65SMinghuan Lian GFP_KERNEL); 3714dd5da65SMinghuan Lian if (!msi_data->used) 3724dd5da65SMinghuan Lian return -ENOMEM; 3734dd5da65SMinghuan Lian /* 3744dd5da65SMinghuan Lian * Reserve all the hwirqs 3754dd5da65SMinghuan Lian * The available hwirqs will be released in ls1_msi_setup_hwirq() 3764dd5da65SMinghuan Lian */ 3774dd5da65SMinghuan Lian bitmap_set(msi_data->used, 0, msi_data->irqs_num); 3784dd5da65SMinghuan Lian 3794dd5da65SMinghuan Lian msi_data->msir_num = of_irq_count(pdev->dev.of_node); 380ae3efabfSMinghuan Lian 381ae3efabfSMinghuan Lian if (msi_affinity_flag) { 382ae3efabfSMinghuan Lian u32 cpu_num; 383ae3efabfSMinghuan Lian 384ae3efabfSMinghuan Lian cpu_num = num_possible_cpus(); 385ae3efabfSMinghuan Lian if (msi_data->msir_num >= cpu_num) 386ae3efabfSMinghuan Lian msi_data->msir_num = cpu_num; 387ae3efabfSMinghuan Lian else 388ae3efabfSMinghuan Lian msi_affinity_flag = 0; 389ae3efabfSMinghuan Lian } 390ae3efabfSMinghuan Lian 3914dd5da65SMinghuan Lian msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num, 3924dd5da65SMinghuan Lian sizeof(*msi_data->msir), 3934dd5da65SMinghuan Lian GFP_KERNEL); 3944dd5da65SMinghuan Lian if (!msi_data->msir) 3954dd5da65SMinghuan Lian return -ENOMEM; 3964dd5da65SMinghuan Lian 3974dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++) 3984dd5da65SMinghuan Lian ls_scfg_msi_setup_hwirq(msi_data, i); 3994dd5da65SMinghuan Lian 400b8f3ebe6SMinghuan Lian ret = ls_scfg_msi_domains_init(msi_data); 401b8f3ebe6SMinghuan Lian if (ret) 402b8f3ebe6SMinghuan Lian return ret; 403b8f3ebe6SMinghuan Lian 404b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, msi_data); 405b8f3ebe6SMinghuan Lian 406b8f3ebe6SMinghuan Lian return 0; 407b8f3ebe6SMinghuan Lian } 408b8f3ebe6SMinghuan Lian 409b8f3ebe6SMinghuan Lian static int ls_scfg_msi_remove(struct platform_device *pdev) 410b8f3ebe6SMinghuan Lian { 411b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); 4124dd5da65SMinghuan Lian int i; 413b8f3ebe6SMinghuan Lian 4144dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++) 4154dd5da65SMinghuan Lian ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]); 416b8f3ebe6SMinghuan Lian 417b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->msi_domain); 418b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->parent); 419b8f3ebe6SMinghuan Lian 420b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, NULL); 421b8f3ebe6SMinghuan Lian 422b8f3ebe6SMinghuan Lian return 0; 423b8f3ebe6SMinghuan Lian } 424b8f3ebe6SMinghuan Lian 425b8f3ebe6SMinghuan Lian static struct platform_driver ls_scfg_msi_driver = { 426b8f3ebe6SMinghuan Lian .driver = { 427b8f3ebe6SMinghuan Lian .name = "ls-scfg-msi", 428b8f3ebe6SMinghuan Lian .of_match_table = ls_scfg_msi_id, 429b8f3ebe6SMinghuan Lian }, 430b8f3ebe6SMinghuan Lian .probe = ls_scfg_msi_probe, 431b8f3ebe6SMinghuan Lian .remove = ls_scfg_msi_remove, 432b8f3ebe6SMinghuan Lian }; 433b8f3ebe6SMinghuan Lian 434b8f3ebe6SMinghuan Lian module_platform_driver(ls_scfg_msi_driver); 435b8f3ebe6SMinghuan Lian 436b8f3ebe6SMinghuan Lian MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>"); 437b8f3ebe6SMinghuan Lian MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver"); 438b8f3ebe6SMinghuan Lian MODULE_LICENSE("GPL v2"); 439