1 // SPDX-License-Identifier: GPL-2.0
3 * Altera PCIe MSI support
5 * Author: Ley Foon Tan <lftan@altera.com>
7 * Copyright Altera Corporation (C) 2013-2015. All rights reserved
10 #include <linux/interrupt.h>
11 #include <linux/irqchip/chained_irq.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/msi.h>
15 #include <linux/of_address.h>
16 #include <linux/of_irq.h>
17 #include <linux/of_pci.h>
18 #include <linux/pci.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
22 #define MSI_STATUS 0x0
24 #define MSI_INTMASK 0x8
26 #define MAX_MSI_VECTORS 32
29 DECLARE_BITMAP(used
, MAX_MSI_VECTORS
);
30 struct mutex lock
; /* protect "used" bitmap */
31 struct platform_device
*pdev
;
32 struct irq_domain
*msi_domain
;
33 struct irq_domain
*inner_domain
;
34 void __iomem
*csr_base
;
35 void __iomem
*vector_base
;
36 phys_addr_t vector_phy
;
41 static inline void msi_writel(struct altera_msi
*msi
, const u32 value
,
44 writel_relaxed(value
, msi
->csr_base
+ reg
);
47 static inline u32
msi_readl(struct altera_msi
*msi
, const u32 reg
)
49 return readl_relaxed(msi
->csr_base
+ reg
);
52 static void altera_msi_isr(struct irq_desc
*desc
)
54 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
55 struct altera_msi
*msi
;
60 chained_irq_enter(chip
, desc
);
61 msi
= irq_desc_get_handler_data(desc
);
63 while ((status
= msi_readl(msi
, MSI_STATUS
)) != 0) {
64 for_each_set_bit(bit
, &status
, msi
->num_of_vectors
) {
65 /* Dummy read from vector to clear the interrupt */
66 readl_relaxed(msi
->vector_base
+ (bit
* sizeof(u32
)));
68 virq
= irq_find_mapping(msi
->inner_domain
, bit
);
70 generic_handle_irq(virq
);
72 dev_err(&msi
->pdev
->dev
, "unexpected MSI\n");
76 chained_irq_exit(chip
, desc
);
79 static struct irq_chip altera_msi_irq_chip
= {
80 .name
= "Altera PCIe MSI",
81 .irq_mask
= pci_msi_mask_irq
,
82 .irq_unmask
= pci_msi_unmask_irq
,
85 static struct msi_domain_info altera_msi_domain_info
= {
86 .flags
= (MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
|
88 .chip
= &altera_msi_irq_chip
,
91 static void altera_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
93 struct altera_msi
*msi
= irq_data_get_irq_chip_data(data
);
94 phys_addr_t addr
= msi
->vector_phy
+ (data
->hwirq
* sizeof(u32
));
96 msg
->address_lo
= lower_32_bits(addr
);
97 msg
->address_hi
= upper_32_bits(addr
);
98 msg
->data
= data
->hwirq
;
100 dev_dbg(&msi
->pdev
->dev
, "msi#%d address_hi %#x address_lo %#x\n",
101 (int)data
->hwirq
, msg
->address_hi
, msg
->address_lo
);
104 static int altera_msi_set_affinity(struct irq_data
*irq_data
,
105 const struct cpumask
*mask
, bool force
)
110 static struct irq_chip altera_msi_bottom_irq_chip
= {
111 .name
= "Altera MSI",
112 .irq_compose_msi_msg
= altera_compose_msi_msg
,
113 .irq_set_affinity
= altera_msi_set_affinity
,
116 static int altera_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
117 unsigned int nr_irqs
, void *args
)
119 struct altera_msi
*msi
= domain
->host_data
;
123 WARN_ON(nr_irqs
!= 1);
124 mutex_lock(&msi
->lock
);
126 bit
= find_first_zero_bit(msi
->used
, msi
->num_of_vectors
);
127 if (bit
>= msi
->num_of_vectors
) {
128 mutex_unlock(&msi
->lock
);
132 set_bit(bit
, msi
->used
);
134 mutex_unlock(&msi
->lock
);
136 irq_domain_set_info(domain
, virq
, bit
, &altera_msi_bottom_irq_chip
,
137 domain
->host_data
, handle_simple_irq
,
140 mask
= msi_readl(msi
, MSI_INTMASK
);
142 msi_writel(msi
, mask
, MSI_INTMASK
);
147 static void altera_irq_domain_free(struct irq_domain
*domain
,
148 unsigned int virq
, unsigned int nr_irqs
)
150 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
151 struct altera_msi
*msi
= irq_data_get_irq_chip_data(d
);
154 mutex_lock(&msi
->lock
);
156 if (!test_bit(d
->hwirq
, msi
->used
)) {
157 dev_err(&msi
->pdev
->dev
, "trying to free unused MSI#%lu\n",
160 __clear_bit(d
->hwirq
, msi
->used
);
161 mask
= msi_readl(msi
, MSI_INTMASK
);
162 mask
&= ~(1 << d
->hwirq
);
163 msi_writel(msi
, mask
, MSI_INTMASK
);
166 mutex_unlock(&msi
->lock
);
169 static const struct irq_domain_ops msi_domain_ops
= {
170 .alloc
= altera_irq_domain_alloc
,
171 .free
= altera_irq_domain_free
,
174 static int altera_allocate_domains(struct altera_msi
*msi
)
176 struct fwnode_handle
*fwnode
= of_node_to_fwnode(msi
->pdev
->dev
.of_node
);
178 msi
->inner_domain
= irq_domain_add_linear(NULL
, msi
->num_of_vectors
,
179 &msi_domain_ops
, msi
);
180 if (!msi
->inner_domain
) {
181 dev_err(&msi
->pdev
->dev
, "failed to create IRQ domain\n");
185 msi
->msi_domain
= pci_msi_create_irq_domain(fwnode
,
186 &altera_msi_domain_info
, msi
->inner_domain
);
187 if (!msi
->msi_domain
) {
188 dev_err(&msi
->pdev
->dev
, "failed to create MSI domain\n");
189 irq_domain_remove(msi
->inner_domain
);
196 static void altera_free_domains(struct altera_msi
*msi
)
198 irq_domain_remove(msi
->msi_domain
);
199 irq_domain_remove(msi
->inner_domain
);
202 static int altera_msi_remove(struct platform_device
*pdev
)
204 struct altera_msi
*msi
= platform_get_drvdata(pdev
);
206 msi_writel(msi
, 0, MSI_INTMASK
);
207 irq_set_chained_handler(msi
->irq
, NULL
);
208 irq_set_handler_data(msi
->irq
, NULL
);
210 altera_free_domains(msi
);
212 platform_set_drvdata(pdev
, NULL
);
216 static int altera_msi_probe(struct platform_device
*pdev
)
218 struct altera_msi
*msi
;
219 struct device_node
*np
= pdev
->dev
.of_node
;
220 struct resource
*res
;
223 msi
= devm_kzalloc(&pdev
->dev
, sizeof(struct altera_msi
),
228 mutex_init(&msi
->lock
);
231 msi
->csr_base
= devm_platform_ioremap_resource_byname(pdev
, "csr");
232 if (IS_ERR(msi
->csr_base
)) {
233 dev_err(&pdev
->dev
, "failed to map csr memory\n");
234 return PTR_ERR(msi
->csr_base
);
237 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
239 msi
->vector_base
= devm_ioremap_resource(&pdev
->dev
, res
);
240 if (IS_ERR(msi
->vector_base
)) {
241 dev_err(&pdev
->dev
, "failed to map vector_slave memory\n");
242 return PTR_ERR(msi
->vector_base
);
245 msi
->vector_phy
= res
->start
;
247 if (of_property_read_u32(np
, "num-vectors", &msi
->num_of_vectors
)) {
248 dev_err(&pdev
->dev
, "failed to parse the number of vectors\n");
252 ret
= altera_allocate_domains(msi
);
256 msi
->irq
= platform_get_irq(pdev
, 0);
262 irq_set_chained_handler_and_data(msi
->irq
, altera_msi_isr
, msi
);
263 platform_set_drvdata(pdev
, msi
);
268 altera_msi_remove(pdev
);
272 static const struct of_device_id altera_msi_of_match
[] = {
273 { .compatible
= "altr,msi-1.0", NULL
},
277 static struct platform_driver altera_msi_driver
= {
279 .name
= "altera-msi",
280 .of_match_table
= altera_msi_of_match
,
282 .probe
= altera_msi_probe
,
283 .remove
= altera_msi_remove
,
286 static int __init
altera_msi_init(void)
288 return platform_driver_register(&altera_msi_driver
);
291 static void __exit
altera_msi_exit(void)
293 platform_driver_unregister(&altera_msi_driver
);
296 subsys_initcall(altera_msi_init
);
297 MODULE_DEVICE_TABLE(of
, altera_msi_of_match
);
298 module_exit(altera_msi_exit
);
299 MODULE_LICENSE("GPL v2");