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