2 * Copyright Altera Corporation (C) 2013-2015. All rights reserved
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.
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
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/>.
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>
28 #define MSI_STATUS 0x0
30 #define MSI_INTMASK 0x8
32 #define MAX_MSI_VECTORS 32
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
;
47 static inline void msi_writel(struct altera_msi
*msi
, const u32 value
,
50 writel_relaxed(value
, msi
->csr_base
+ reg
);
53 static inline u32
msi_readl(struct altera_msi
*msi
, const u32 reg
)
55 return readl_relaxed(msi
->csr_base
+ reg
);
58 static void altera_msi_isr(struct irq_desc
*desc
)
60 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
61 struct altera_msi
*msi
;
67 chained_irq_enter(chip
, desc
);
68 msi
= irq_desc_get_handler_data(desc
);
69 num_of_vectors
= msi
->num_of_vectors
;
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
)));
76 virq
= irq_find_mapping(msi
->inner_domain
, bit
);
78 generic_handle_irq(virq
);
80 dev_err(&msi
->pdev
->dev
, "unexpected MSI\n");
84 chained_irq_exit(chip
, desc
);
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
,
93 static struct msi_domain_info altera_msi_domain_info
= {
94 .flags
= (MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
|
96 .chip
= &altera_msi_irq_chip
,
99 static void altera_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
101 struct altera_msi
*msi
= irq_data_get_irq_chip_data(data
);
102 phys_addr_t addr
= msi
->vector_phy
+ (data
->hwirq
* sizeof(u32
));
104 msg
->address_lo
= lower_32_bits(addr
);
105 msg
->address_hi
= upper_32_bits(addr
);
106 msg
->data
= data
->hwirq
;
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
);
112 static int altera_msi_set_affinity(struct irq_data
*irq_data
,
113 const struct cpumask
*mask
, bool force
)
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
,
124 static int altera_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
125 unsigned int nr_irqs
, void *args
)
127 struct altera_msi
*msi
= domain
->host_data
;
131 WARN_ON(nr_irqs
!= 1);
132 mutex_lock(&msi
->lock
);
134 bit
= find_first_zero_bit(msi
->used
, msi
->num_of_vectors
);
135 if (bit
>= msi
->num_of_vectors
) {
136 mutex_unlock(&msi
->lock
);
140 set_bit(bit
, msi
->used
);
142 mutex_unlock(&msi
->lock
);
144 irq_domain_set_info(domain
, virq
, bit
, &altera_msi_bottom_irq_chip
,
145 domain
->host_data
, handle_simple_irq
,
148 mask
= msi_readl(msi
, MSI_INTMASK
);
150 msi_writel(msi
, mask
, MSI_INTMASK
);
155 static void altera_irq_domain_free(struct irq_domain
*domain
,
156 unsigned int virq
, unsigned int nr_irqs
)
158 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
159 struct altera_msi
*msi
= irq_data_get_irq_chip_data(d
);
162 mutex_lock(&msi
->lock
);
164 if (!test_bit(d
->hwirq
, msi
->used
)) {
165 dev_err(&msi
->pdev
->dev
, "trying to free unused MSI#%lu\n",
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
);
174 mutex_unlock(&msi
->lock
);
177 static const struct irq_domain_ops msi_domain_ops
= {
178 .alloc
= altera_irq_domain_alloc
,
179 .free
= altera_irq_domain_free
,
182 static int altera_allocate_domains(struct altera_msi
*msi
)
184 struct fwnode_handle
*fwnode
= of_node_to_fwnode(msi
->pdev
->dev
.of_node
);
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");
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
);
204 static void altera_free_domains(struct altera_msi
*msi
)
206 irq_domain_remove(msi
->msi_domain
);
207 irq_domain_remove(msi
->inner_domain
);
210 static int altera_msi_remove(struct platform_device
*pdev
)
212 struct altera_msi
*msi
= platform_get_drvdata(pdev
);
214 msi_writel(msi
, 0, MSI_INTMASK
);
215 irq_set_chained_handler(msi
->irq
, NULL
);
216 irq_set_handler_data(msi
->irq
, NULL
);
218 altera_free_domains(msi
);
220 platform_set_drvdata(pdev
, NULL
);
224 static int altera_msi_probe(struct platform_device
*pdev
)
226 struct altera_msi
*msi
;
227 struct device_node
*np
= pdev
->dev
.of_node
;
228 struct resource
*res
;
231 msi
= devm_kzalloc(&pdev
->dev
, sizeof(struct altera_msi
),
236 mutex_init(&msi
->lock
);
239 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "csr");
241 dev_err(&pdev
->dev
, "no csr memory resource defined\n");
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
);
251 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
254 dev_err(&pdev
->dev
, "no vector_slave memory resource defined\n");
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
);
264 msi
->vector_phy
= res
->start
;
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");
271 ret
= altera_allocate_domains(msi
);
275 msi
->irq
= platform_get_irq(pdev
, 0);
277 dev_err(&pdev
->dev
, "failed to map IRQ: %d\n", msi
->irq
);
282 irq_set_chained_handler_and_data(msi
->irq
, altera_msi_isr
, msi
);
283 platform_set_drvdata(pdev
, msi
);
288 altera_msi_remove(pdev
);
292 static const struct of_device_id altera_msi_of_match
[] = {
293 { .compatible
= "altr,msi-1.0", NULL
},
297 static struct platform_driver altera_msi_driver
= {
299 .name
= "altera-msi",
300 .of_match_table
= altera_msi_of_match
,
302 .probe
= altera_msi_probe
,
303 .remove
= altera_msi_remove
,
306 static int __init
altera_msi_init(void)
308 return platform_driver_register(&altera_msi_driver
);
310 subsys_initcall(altera_msi_init
);
312 MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
313 MODULE_DESCRIPTION("Altera PCIe MSI support");
314 MODULE_LICENSE("GPL v2");