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>
14fa49364cSRobin Murphy #include <linux/iommu.h>
15b8f3ebe6SMinghuan Lian #include <linux/irq.h>
16b8f3ebe6SMinghuan Lian #include <linux/irqchip/chained_irq.h>
17b8f3ebe6SMinghuan Lian #include <linux/irqdomain.h>
184dd5da65SMinghuan Lian #include <linux/of_irq.h>
19b8f3ebe6SMinghuan Lian #include <linux/of_pci.h>
20b8f3ebe6SMinghuan Lian #include <linux/of_platform.h>
21b8f3ebe6SMinghuan Lian #include <linux/spinlock.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
early_parse_ls_scfg_msi(char * p)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
ls_scfg_msi_compose_msg(struct irq_data * data,struct msi_msg * msg)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
ls_scfg_msi_set_affinity(struct irq_data * irq_data,const struct cpumask * mask,bool force)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
ls_scfg_msi_domain_irq_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)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
ls_scfg_msi_domain_irq_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)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
ls_scfg_msi_irq_handler(struct irq_desc * desc)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
ls_scfg_msi_domains_init(struct ls_scfg_msi * msi_data)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
ls_scfg_msi_setup_hwirq(struct ls_scfg_msi * msi_data,int index)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
ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir * msir)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
ls_scfg_msi_probe(struct platform_device * pdev)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
352*32b5f8a6SYangtao Li msi_data->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
353b8f3ebe6SMinghuan Lian if (IS_ERR(msi_data->regs)) {
354b8f3ebe6SMinghuan Lian dev_err(&pdev->dev, "failed to initialize 'regs'\n");
355b8f3ebe6SMinghuan Lian return PTR_ERR(msi_data->regs);
356b8f3ebe6SMinghuan Lian }
357b8f3ebe6SMinghuan Lian msi_data->msiir_addr = res->start;
358b8f3ebe6SMinghuan Lian
359b8f3ebe6SMinghuan Lian msi_data->pdev = pdev;
360b8f3ebe6SMinghuan Lian spin_lock_init(&msi_data->lock);
361b8f3ebe6SMinghuan Lian
3624dd5da65SMinghuan Lian msi_data->irqs_num = MSI_IRQS_PER_MSIR *
3634dd5da65SMinghuan Lian (1 << msi_data->cfg->ibs_shift);
36443a1965fSAndy Shevchenko msi_data->used = devm_bitmap_zalloc(&pdev->dev, msi_data->irqs_num, GFP_KERNEL);
3654dd5da65SMinghuan Lian if (!msi_data->used)
3664dd5da65SMinghuan Lian return -ENOMEM;
3674dd5da65SMinghuan Lian /*
3684dd5da65SMinghuan Lian * Reserve all the hwirqs
3694dd5da65SMinghuan Lian * The available hwirqs will be released in ls1_msi_setup_hwirq()
3704dd5da65SMinghuan Lian */
3714dd5da65SMinghuan Lian bitmap_set(msi_data->used, 0, msi_data->irqs_num);
3724dd5da65SMinghuan Lian
3734dd5da65SMinghuan Lian msi_data->msir_num = of_irq_count(pdev->dev.of_node);
374ae3efabfSMinghuan Lian
375ae3efabfSMinghuan Lian if (msi_affinity_flag) {
376ae3efabfSMinghuan Lian u32 cpu_num;
377ae3efabfSMinghuan Lian
378ae3efabfSMinghuan Lian cpu_num = num_possible_cpus();
379ae3efabfSMinghuan Lian if (msi_data->msir_num >= cpu_num)
380ae3efabfSMinghuan Lian msi_data->msir_num = cpu_num;
381ae3efabfSMinghuan Lian else
382ae3efabfSMinghuan Lian msi_affinity_flag = 0;
383ae3efabfSMinghuan Lian }
384ae3efabfSMinghuan Lian
3854dd5da65SMinghuan Lian msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
3864dd5da65SMinghuan Lian sizeof(*msi_data->msir),
3874dd5da65SMinghuan Lian GFP_KERNEL);
3884dd5da65SMinghuan Lian if (!msi_data->msir)
3894dd5da65SMinghuan Lian return -ENOMEM;
3904dd5da65SMinghuan Lian
3914dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++)
3924dd5da65SMinghuan Lian ls_scfg_msi_setup_hwirq(msi_data, i);
3934dd5da65SMinghuan Lian
394b8f3ebe6SMinghuan Lian ret = ls_scfg_msi_domains_init(msi_data);
395b8f3ebe6SMinghuan Lian if (ret)
396b8f3ebe6SMinghuan Lian return ret;
397b8f3ebe6SMinghuan Lian
398b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, msi_data);
399b8f3ebe6SMinghuan Lian
400b8f3ebe6SMinghuan Lian return 0;
401b8f3ebe6SMinghuan Lian }
402b8f3ebe6SMinghuan Lian
ls_scfg_msi_remove(struct platform_device * pdev)403b8f3ebe6SMinghuan Lian static int ls_scfg_msi_remove(struct platform_device *pdev)
404b8f3ebe6SMinghuan Lian {
405b8f3ebe6SMinghuan Lian struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
4064dd5da65SMinghuan Lian int i;
407b8f3ebe6SMinghuan Lian
4084dd5da65SMinghuan Lian for (i = 0; i < msi_data->msir_num; i++)
4094dd5da65SMinghuan Lian ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
410b8f3ebe6SMinghuan Lian
411b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->msi_domain);
412b8f3ebe6SMinghuan Lian irq_domain_remove(msi_data->parent);
413b8f3ebe6SMinghuan Lian
414b8f3ebe6SMinghuan Lian platform_set_drvdata(pdev, NULL);
415b8f3ebe6SMinghuan Lian
416b8f3ebe6SMinghuan Lian return 0;
417b8f3ebe6SMinghuan Lian }
418b8f3ebe6SMinghuan Lian
419b8f3ebe6SMinghuan Lian static struct platform_driver ls_scfg_msi_driver = {
420b8f3ebe6SMinghuan Lian .driver = {
421b8f3ebe6SMinghuan Lian .name = "ls-scfg-msi",
422b8f3ebe6SMinghuan Lian .of_match_table = ls_scfg_msi_id,
423b8f3ebe6SMinghuan Lian },
424b8f3ebe6SMinghuan Lian .probe = ls_scfg_msi_probe,
425b8f3ebe6SMinghuan Lian .remove = ls_scfg_msi_remove,
426b8f3ebe6SMinghuan Lian };
427b8f3ebe6SMinghuan Lian
428b8f3ebe6SMinghuan Lian module_platform_driver(ls_scfg_msi_driver);
429b8f3ebe6SMinghuan Lian
430b8f3ebe6SMinghuan Lian MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
431b8f3ebe6SMinghuan Lian MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver");
432