1 /*
2  * Copyright Altera Corporation (C) 2013-2015. All rights reserved
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <linux/interrupt.h>
18 #include <linux/irqchip/chained_irq.h>
19 #include <linux/module.h>
20 #include <linux/msi.h>
21 #include <linux/of_address.h>
22 #include <linux/of_irq.h>
23 #include <linux/of_pci.h>
24 #include <linux/pci.h>
25 #include <linux/platform_device.h>
26 #include <linux/slab.h>
27 
28 #define MSI_STATUS		0x0
29 #define MSI_ERROR		0x4
30 #define MSI_INTMASK		0x8
31 
32 #define MAX_MSI_VECTORS		32
33 
34 struct altera_msi {
35 	DECLARE_BITMAP(used, MAX_MSI_VECTORS);
36 	struct mutex		lock;	/* protect "used" bitmap */
37 	struct platform_device	*pdev;
38 	struct irq_domain	*msi_domain;
39 	struct irq_domain	*inner_domain;
40 	void __iomem		*csr_base;
41 	void __iomem		*vector_base;
42 	phys_addr_t		vector_phy;
43 	u32			num_of_vectors;
44 	int			irq;
45 };
46 
msi_writel(struct altera_msi * msi,const u32 value,const u32 reg)47 static inline void msi_writel(struct altera_msi *msi, const u32 value,
48 			      const u32 reg)
49 {
50 	writel_relaxed(value, msi->csr_base + reg);
51 }
52 
msi_readl(struct altera_msi * msi,const u32 reg)53 static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
54 {
55 	return readl_relaxed(msi->csr_base + reg);
56 }
57 
altera_msi_isr(struct irq_desc * desc)58 static void altera_msi_isr(struct irq_desc *desc)
59 {
60 	struct irq_chip *chip = irq_desc_get_chip(desc);
61 	struct altera_msi *msi;
62 	unsigned long status;
63 	u32 num_of_vectors;
64 	u32 bit;
65 	u32 virq;
66 
67 	chained_irq_enter(chip, desc);
68 	msi = irq_desc_get_handler_data(desc);
69 	num_of_vectors = msi->num_of_vectors;
70 
71 	while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
72 		for_each_set_bit(bit, &status, msi->num_of_vectors) {
73 			/* Dummy read from vector to clear the interrupt */
74 			readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
75 
76 			virq = irq_find_mapping(msi->inner_domain, bit);
77 			if (virq)
78 				generic_handle_irq(virq);
79 			else
80 				dev_err(&msi->pdev->dev, "unexpected MSI\n");
81 		}
82 	}
83 
84 	chained_irq_exit(chip, desc);
85 }
86 
87 static struct irq_chip altera_msi_irq_chip = {
88 	.name = "Altera PCIe MSI",
89 	.irq_mask = pci_msi_mask_irq,
90 	.irq_unmask = pci_msi_unmask_irq,
91 };
92 
93 static struct msi_domain_info altera_msi_domain_info = {
94 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
95 		     MSI_FLAG_PCI_MSIX),
96 	.chip	= &altera_msi_irq_chip,
97 };
98 
altera_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)99 static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
100 {
101 	struct altera_msi *msi = irq_data_get_irq_chip_data(data);
102 	phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
103 
104 	msg->address_lo = lower_32_bits(addr);
105 	msg->address_hi = upper_32_bits(addr);
106 	msg->data = data->hwirq;
107 
108 	dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
109 		(int)data->hwirq, msg->address_hi, msg->address_lo);
110 }
111 
altera_msi_set_affinity(struct irq_data * irq_data,const struct cpumask * mask,bool force)112 static int altera_msi_set_affinity(struct irq_data *irq_data,
113 				   const struct cpumask *mask, bool force)
114 {
115 	 return -EINVAL;
116 }
117 
118 static struct irq_chip altera_msi_bottom_irq_chip = {
119 	.name			= "Altera MSI",
120 	.irq_compose_msi_msg	= altera_compose_msi_msg,
121 	.irq_set_affinity	= altera_msi_set_affinity,
122 };
123 
altera_irq_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)124 static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
125 				   unsigned int nr_irqs, void *args)
126 {
127 	struct altera_msi *msi = domain->host_data;
128 	unsigned long bit;
129 	u32 mask;
130 
131 	WARN_ON(nr_irqs != 1);
132 	mutex_lock(&msi->lock);
133 
134 	bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
135 	if (bit >= msi->num_of_vectors) {
136 		mutex_unlock(&msi->lock);
137 		return -ENOSPC;
138 	}
139 
140 	set_bit(bit, msi->used);
141 
142 	mutex_unlock(&msi->lock);
143 
144 	irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
145 			    domain->host_data, handle_simple_irq,
146 			    NULL, NULL);
147 
148 	mask = msi_readl(msi, MSI_INTMASK);
149 	mask |= 1 << bit;
150 	msi_writel(msi, mask, MSI_INTMASK);
151 
152 	return 0;
153 }
154 
altera_irq_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)155 static void altera_irq_domain_free(struct irq_domain *domain,
156 				   unsigned int virq, unsigned int nr_irqs)
157 {
158 	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
159 	struct altera_msi *msi = irq_data_get_irq_chip_data(d);
160 	u32 mask;
161 
162 	mutex_lock(&msi->lock);
163 
164 	if (!test_bit(d->hwirq, msi->used)) {
165 		dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
166 			d->hwirq);
167 	} else {
168 		__clear_bit(d->hwirq, msi->used);
169 		mask = msi_readl(msi, MSI_INTMASK);
170 		mask &= ~(1 << d->hwirq);
171 		msi_writel(msi, mask, MSI_INTMASK);
172 	}
173 
174 	mutex_unlock(&msi->lock);
175 }
176 
177 static const struct irq_domain_ops msi_domain_ops = {
178 	.alloc	= altera_irq_domain_alloc,
179 	.free	= altera_irq_domain_free,
180 };
181 
altera_allocate_domains(struct altera_msi * msi)182 static int altera_allocate_domains(struct altera_msi *msi)
183 {
184 	struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
185 
186 	msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
187 					     &msi_domain_ops, msi);
188 	if (!msi->inner_domain) {
189 		dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
190 		return -ENOMEM;
191 	}
192 
193 	msi->msi_domain = pci_msi_create_irq_domain(fwnode,
194 				&altera_msi_domain_info, msi->inner_domain);
195 	if (!msi->msi_domain) {
196 		dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
197 		irq_domain_remove(msi->inner_domain);
198 		return -ENOMEM;
199 	}
200 
201 	return 0;
202 }
203 
altera_free_domains(struct altera_msi * msi)204 static void altera_free_domains(struct altera_msi *msi)
205 {
206 	irq_domain_remove(msi->msi_domain);
207 	irq_domain_remove(msi->inner_domain);
208 }
209 
altera_msi_remove(struct platform_device * pdev)210 static int altera_msi_remove(struct platform_device *pdev)
211 {
212 	struct altera_msi *msi = platform_get_drvdata(pdev);
213 
214 	msi_writel(msi, 0, MSI_INTMASK);
215 	irq_set_chained_handler(msi->irq, NULL);
216 	irq_set_handler_data(msi->irq, NULL);
217 
218 	altera_free_domains(msi);
219 
220 	platform_set_drvdata(pdev, NULL);
221 	return 0;
222 }
223 
altera_msi_probe(struct platform_device * pdev)224 static int altera_msi_probe(struct platform_device *pdev)
225 {
226 	struct altera_msi *msi;
227 	struct device_node *np = pdev->dev.of_node;
228 	struct resource *res;
229 	int ret;
230 
231 	msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
232 			   GFP_KERNEL);
233 	if (!msi)
234 		return -ENOMEM;
235 
236 	mutex_init(&msi->lock);
237 	msi->pdev = pdev;
238 
239 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
240 	if (!res) {
241 		dev_err(&pdev->dev, "no csr memory resource defined\n");
242 		return -ENODEV;
243 	}
244 
245 	msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
246 	if (IS_ERR(msi->csr_base)) {
247 		dev_err(&pdev->dev, "failed to map csr memory\n");
248 		return PTR_ERR(msi->csr_base);
249 	}
250 
251 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
252 					   "vector_slave");
253 	if (!res) {
254 		dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
255 		return -ENODEV;
256 	}
257 
258 	msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
259 	if (IS_ERR(msi->vector_base)) {
260 		dev_err(&pdev->dev, "failed to map vector_slave memory\n");
261 		return PTR_ERR(msi->vector_base);
262 	}
263 
264 	msi->vector_phy = res->start;
265 
266 	if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
267 		dev_err(&pdev->dev, "failed to parse the number of vectors\n");
268 		return -EINVAL;
269 	}
270 
271 	ret = altera_allocate_domains(msi);
272 	if (ret)
273 		return ret;
274 
275 	msi->irq = platform_get_irq(pdev, 0);
276 	if (msi->irq <= 0) {
277 		dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
278 		ret = -ENODEV;
279 		goto err;
280 	}
281 
282 	irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
283 	platform_set_drvdata(pdev, msi);
284 
285 	return 0;
286 
287 err:
288 	altera_msi_remove(pdev);
289 	return ret;
290 }
291 
292 static const struct of_device_id altera_msi_of_match[] = {
293 	{ .compatible = "altr,msi-1.0", NULL },
294 	{ },
295 };
296 
297 static struct platform_driver altera_msi_driver = {
298 	.driver = {
299 		.name = "altera-msi",
300 		.of_match_table = altera_msi_of_match,
301 	},
302 	.probe = altera_msi_probe,
303 	.remove = altera_msi_remove,
304 };
305 
altera_msi_init(void)306 static int __init altera_msi_init(void)
307 {
308 	return platform_driver_register(&altera_msi_driver);
309 }
310 subsys_initcall(altera_msi_init);
311 
312 MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
313 MODULE_DESCRIPTION("Altera PCIe MSI support");
314 MODULE_LICENSE("GPL v2");
315