2 * Altera PCIe MSI support
4 * Author: Ley Foon Tan <lftan@altera.com>
6 * Copyright Altera Corporation (C) 2013-2015. All rights reserved
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <linux/interrupt.h>
22 #include <linux/irqchip/chained_irq.h>
23 #include <linux/init.h>
24 #include <linux/msi.h>
25 #include <linux/of_address.h>
26 #include <linux/of_irq.h>
27 #include <linux/of_pci.h>
28 #include <linux/pci.h>
29 #include <linux/platform_device.h>
30 #include <linux/slab.h>
32 #define MSI_STATUS 0x0
34 #define MSI_INTMASK 0x8
36 #define MAX_MSI_VECTORS 32
39 DECLARE_BITMAP(used
, MAX_MSI_VECTORS
);
40 struct mutex lock
; /* protect "used" bitmap */
41 struct platform_device
*pdev
;
42 struct irq_domain
*msi_domain
;
43 struct irq_domain
*inner_domain
;
44 void __iomem
*csr_base
;
45 void __iomem
*vector_base
;
46 phys_addr_t vector_phy
;
51 static inline void msi_writel(struct altera_msi
*msi
, const u32 value
,
54 writel_relaxed(value
, msi
->csr_base
+ reg
);
57 static inline u32
msi_readl(struct altera_msi
*msi
, const u32 reg
)
59 return readl_relaxed(msi
->csr_base
+ reg
);
62 static void altera_msi_isr(struct irq_desc
*desc
)
64 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
65 struct altera_msi
*msi
;
71 chained_irq_enter(chip
, desc
);
72 msi
= irq_desc_get_handler_data(desc
);
73 num_of_vectors
= msi
->num_of_vectors
;
75 while ((status
= msi_readl(msi
, MSI_STATUS
)) != 0) {
76 for_each_set_bit(bit
, &status
, msi
->num_of_vectors
) {
77 /* Dummy read from vector to clear the interrupt */
78 readl_relaxed(msi
->vector_base
+ (bit
* sizeof(u32
)));
80 virq
= irq_find_mapping(msi
->inner_domain
, bit
);
82 generic_handle_irq(virq
);
84 dev_err(&msi
->pdev
->dev
, "unexpected MSI\n");
88 chained_irq_exit(chip
, desc
);
91 static struct irq_chip altera_msi_irq_chip
= {
92 .name
= "Altera PCIe MSI",
93 .irq_mask
= pci_msi_mask_irq
,
94 .irq_unmask
= pci_msi_unmask_irq
,
97 static struct msi_domain_info altera_msi_domain_info
= {
98 .flags
= (MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
|
100 .chip
= &altera_msi_irq_chip
,
103 static void altera_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
105 struct altera_msi
*msi
= irq_data_get_irq_chip_data(data
);
106 phys_addr_t addr
= msi
->vector_phy
+ (data
->hwirq
* sizeof(u32
));
108 msg
->address_lo
= lower_32_bits(addr
);
109 msg
->address_hi
= upper_32_bits(addr
);
110 msg
->data
= data
->hwirq
;
112 dev_dbg(&msi
->pdev
->dev
, "msi#%d address_hi %#x address_lo %#x\n",
113 (int)data
->hwirq
, msg
->address_hi
, msg
->address_lo
);
116 static int altera_msi_set_affinity(struct irq_data
*irq_data
,
117 const struct cpumask
*mask
, bool force
)
122 static struct irq_chip altera_msi_bottom_irq_chip
= {
123 .name
= "Altera MSI",
124 .irq_compose_msi_msg
= altera_compose_msi_msg
,
125 .irq_set_affinity
= altera_msi_set_affinity
,
128 static int altera_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
129 unsigned int nr_irqs
, void *args
)
131 struct altera_msi
*msi
= domain
->host_data
;
135 WARN_ON(nr_irqs
!= 1);
136 mutex_lock(&msi
->lock
);
138 bit
= find_first_zero_bit(msi
->used
, msi
->num_of_vectors
);
139 if (bit
>= msi
->num_of_vectors
) {
140 mutex_unlock(&msi
->lock
);
144 set_bit(bit
, msi
->used
);
146 mutex_unlock(&msi
->lock
);
148 irq_domain_set_info(domain
, virq
, bit
, &altera_msi_bottom_irq_chip
,
149 domain
->host_data
, handle_simple_irq
,
152 mask
= msi_readl(msi
, MSI_INTMASK
);
154 msi_writel(msi
, mask
, MSI_INTMASK
);
159 static void altera_irq_domain_free(struct irq_domain
*domain
,
160 unsigned int virq
, unsigned int nr_irqs
)
162 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
163 struct altera_msi
*msi
= irq_data_get_irq_chip_data(d
);
166 mutex_lock(&msi
->lock
);
168 if (!test_bit(d
->hwirq
, msi
->used
)) {
169 dev_err(&msi
->pdev
->dev
, "trying to free unused MSI#%lu\n",
172 __clear_bit(d
->hwirq
, msi
->used
);
173 mask
= msi_readl(msi
, MSI_INTMASK
);
174 mask
&= ~(1 << d
->hwirq
);
175 msi_writel(msi
, mask
, MSI_INTMASK
);
178 mutex_unlock(&msi
->lock
);
181 static const struct irq_domain_ops msi_domain_ops
= {
182 .alloc
= altera_irq_domain_alloc
,
183 .free
= altera_irq_domain_free
,
186 static int altera_allocate_domains(struct altera_msi
*msi
)
188 struct fwnode_handle
*fwnode
= of_node_to_fwnode(msi
->pdev
->dev
.of_node
);
190 msi
->inner_domain
= irq_domain_add_linear(NULL
, msi
->num_of_vectors
,
191 &msi_domain_ops
, msi
);
192 if (!msi
->inner_domain
) {
193 dev_err(&msi
->pdev
->dev
, "failed to create IRQ domain\n");
197 msi
->msi_domain
= pci_msi_create_irq_domain(fwnode
,
198 &altera_msi_domain_info
, msi
->inner_domain
);
199 if (!msi
->msi_domain
) {
200 dev_err(&msi
->pdev
->dev
, "failed to create MSI domain\n");
201 irq_domain_remove(msi
->inner_domain
);
208 static void altera_free_domains(struct altera_msi
*msi
)
210 irq_domain_remove(msi
->msi_domain
);
211 irq_domain_remove(msi
->inner_domain
);
214 static int altera_msi_remove(struct platform_device
*pdev
)
216 struct altera_msi
*msi
= platform_get_drvdata(pdev
);
218 msi_writel(msi
, 0, MSI_INTMASK
);
219 irq_set_chained_handler(msi
->irq
, NULL
);
220 irq_set_handler_data(msi
->irq
, NULL
);
222 altera_free_domains(msi
);
224 platform_set_drvdata(pdev
, NULL
);
228 static int altera_msi_probe(struct platform_device
*pdev
)
230 struct altera_msi
*msi
;
231 struct device_node
*np
= pdev
->dev
.of_node
;
232 struct resource
*res
;
235 msi
= devm_kzalloc(&pdev
->dev
, sizeof(struct altera_msi
),
240 mutex_init(&msi
->lock
);
243 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "csr");
244 msi
->csr_base
= devm_ioremap_resource(&pdev
->dev
, res
);
245 if (IS_ERR(msi
->csr_base
)) {
246 dev_err(&pdev
->dev
, "failed to map csr memory\n");
247 return PTR_ERR(msi
->csr_base
);
250 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
252 msi
->vector_base
= devm_ioremap_resource(&pdev
->dev
, res
);
253 if (IS_ERR(msi
->vector_base
)) {
254 dev_err(&pdev
->dev
, "failed to map vector_slave memory\n");
255 return PTR_ERR(msi
->vector_base
);
258 msi
->vector_phy
= res
->start
;
260 if (of_property_read_u32(np
, "num-vectors", &msi
->num_of_vectors
)) {
261 dev_err(&pdev
->dev
, "failed to parse the number of vectors\n");
265 ret
= altera_allocate_domains(msi
);
269 msi
->irq
= platform_get_irq(pdev
, 0);
271 dev_err(&pdev
->dev
, "failed to map IRQ: %d\n", msi
->irq
);
276 irq_set_chained_handler_and_data(msi
->irq
, altera_msi_isr
, msi
);
277 platform_set_drvdata(pdev
, msi
);
282 altera_msi_remove(pdev
);
286 static const struct of_device_id altera_msi_of_match
[] = {
287 { .compatible
= "altr,msi-1.0", NULL
},
291 static struct platform_driver altera_msi_driver
= {
293 .name
= "altera-msi",
294 .of_match_table
= altera_msi_of_match
,
296 .probe
= altera_msi_probe
,
297 .remove
= altera_msi_remove
,
300 static int __init
altera_msi_init(void)
302 return platform_driver_register(&altera_msi_driver
);
304 subsys_initcall(altera_msi_init
);