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/msi.h>
14 #include <linux/of_address.h>
15 #include <linux/of_irq.h>
16 #include <linux/of_pci.h>
17 #include <linux/pci.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
21 #define MSI_STATUS 0x0
23 #define MSI_INTMASK 0x8
25 #define MAX_MSI_VECTORS 32
28 DECLARE_BITMAP(used
, MAX_MSI_VECTORS
);
29 struct mutex lock
; /* protect "used" bitmap */
30 struct platform_device
*pdev
;
31 struct irq_domain
*msi_domain
;
32 struct irq_domain
*inner_domain
;
33 void __iomem
*csr_base
;
34 void __iomem
*vector_base
;
35 phys_addr_t vector_phy
;
40 static inline void msi_writel(struct altera_msi
*msi
, const u32 value
,
43 writel_relaxed(value
, msi
->csr_base
+ reg
);
46 static inline u32
msi_readl(struct altera_msi
*msi
, const u32 reg
)
48 return readl_relaxed(msi
->csr_base
+ reg
);
51 static void altera_msi_isr(struct irq_desc
*desc
)
53 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
54 struct altera_msi
*msi
;
59 chained_irq_enter(chip
, desc
);
60 msi
= irq_desc_get_handler_data(desc
);
62 while ((status
= msi_readl(msi
, MSI_STATUS
)) != 0) {
63 for_each_set_bit(bit
, &status
, msi
->num_of_vectors
) {
64 /* Dummy read from vector to clear the interrupt */
65 readl_relaxed(msi
->vector_base
+ (bit
* sizeof(u32
)));
67 virq
= irq_find_mapping(msi
->inner_domain
, bit
);
69 generic_handle_irq(virq
);
71 dev_err(&msi
->pdev
->dev
, "unexpected MSI\n");
75 chained_irq_exit(chip
, desc
);
78 static struct irq_chip altera_msi_irq_chip
= {
79 .name
= "Altera PCIe MSI",
80 .irq_mask
= pci_msi_mask_irq
,
81 .irq_unmask
= pci_msi_unmask_irq
,
84 static struct msi_domain_info altera_msi_domain_info
= {
85 .flags
= (MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
|
87 .chip
= &altera_msi_irq_chip
,
90 static void altera_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
92 struct altera_msi
*msi
= irq_data_get_irq_chip_data(data
);
93 phys_addr_t addr
= msi
->vector_phy
+ (data
->hwirq
* sizeof(u32
));
95 msg
->address_lo
= lower_32_bits(addr
);
96 msg
->address_hi
= upper_32_bits(addr
);
97 msg
->data
= data
->hwirq
;
99 dev_dbg(&msi
->pdev
->dev
, "msi#%d address_hi %#x address_lo %#x\n",
100 (int)data
->hwirq
, msg
->address_hi
, msg
->address_lo
);
103 static int altera_msi_set_affinity(struct irq_data
*irq_data
,
104 const struct cpumask
*mask
, bool force
)
109 static struct irq_chip altera_msi_bottom_irq_chip
= {
110 .name
= "Altera MSI",
111 .irq_compose_msi_msg
= altera_compose_msi_msg
,
112 .irq_set_affinity
= altera_msi_set_affinity
,
115 static int altera_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
116 unsigned int nr_irqs
, void *args
)
118 struct altera_msi
*msi
= domain
->host_data
;
122 WARN_ON(nr_irqs
!= 1);
123 mutex_lock(&msi
->lock
);
125 bit
= find_first_zero_bit(msi
->used
, msi
->num_of_vectors
);
126 if (bit
>= msi
->num_of_vectors
) {
127 mutex_unlock(&msi
->lock
);
131 set_bit(bit
, msi
->used
);
133 mutex_unlock(&msi
->lock
);
135 irq_domain_set_info(domain
, virq
, bit
, &altera_msi_bottom_irq_chip
,
136 domain
->host_data
, handle_simple_irq
,
139 mask
= msi_readl(msi
, MSI_INTMASK
);
141 msi_writel(msi
, mask
, MSI_INTMASK
);
146 static void altera_irq_domain_free(struct irq_domain
*domain
,
147 unsigned int virq
, unsigned int nr_irqs
)
149 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
150 struct altera_msi
*msi
= irq_data_get_irq_chip_data(d
);
153 mutex_lock(&msi
->lock
);
155 if (!test_bit(d
->hwirq
, msi
->used
)) {
156 dev_err(&msi
->pdev
->dev
, "trying to free unused MSI#%lu\n",
159 __clear_bit(d
->hwirq
, msi
->used
);
160 mask
= msi_readl(msi
, MSI_INTMASK
);
161 mask
&= ~(1 << d
->hwirq
);
162 msi_writel(msi
, mask
, MSI_INTMASK
);
165 mutex_unlock(&msi
->lock
);
168 static const struct irq_domain_ops msi_domain_ops
= {
169 .alloc
= altera_irq_domain_alloc
,
170 .free
= altera_irq_domain_free
,
173 static int altera_allocate_domains(struct altera_msi
*msi
)
175 struct fwnode_handle
*fwnode
= of_node_to_fwnode(msi
->pdev
->dev
.of_node
);
177 msi
->inner_domain
= irq_domain_add_linear(NULL
, msi
->num_of_vectors
,
178 &msi_domain_ops
, msi
);
179 if (!msi
->inner_domain
) {
180 dev_err(&msi
->pdev
->dev
, "failed to create IRQ domain\n");
184 msi
->msi_domain
= pci_msi_create_irq_domain(fwnode
,
185 &altera_msi_domain_info
, msi
->inner_domain
);
186 if (!msi
->msi_domain
) {
187 dev_err(&msi
->pdev
->dev
, "failed to create MSI domain\n");
188 irq_domain_remove(msi
->inner_domain
);
195 static void altera_free_domains(struct altera_msi
*msi
)
197 irq_domain_remove(msi
->msi_domain
);
198 irq_domain_remove(msi
->inner_domain
);
201 static int altera_msi_remove(struct platform_device
*pdev
)
203 struct altera_msi
*msi
= platform_get_drvdata(pdev
);
205 msi_writel(msi
, 0, MSI_INTMASK
);
206 irq_set_chained_handler(msi
->irq
, NULL
);
207 irq_set_handler_data(msi
->irq
, NULL
);
209 altera_free_domains(msi
);
211 platform_set_drvdata(pdev
, NULL
);
215 static int altera_msi_probe(struct platform_device
*pdev
)
217 struct altera_msi
*msi
;
218 struct device_node
*np
= pdev
->dev
.of_node
;
219 struct resource
*res
;
222 msi
= devm_kzalloc(&pdev
->dev
, sizeof(struct altera_msi
),
227 mutex_init(&msi
->lock
);
230 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "csr");
231 msi
->csr_base
= devm_ioremap_resource(&pdev
->dev
, res
);
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);
258 dev_err(&pdev
->dev
, "failed to map IRQ: %d\n", msi
->irq
);
263 irq_set_chained_handler_and_data(msi
->irq
, altera_msi_isr
, msi
);
264 platform_set_drvdata(pdev
, msi
);
269 altera_msi_remove(pdev
);
273 static const struct of_device_id altera_msi_of_match
[] = {
274 { .compatible
= "altr,msi-1.0", NULL
},
278 static struct platform_driver altera_msi_driver
= {
280 .name
= "altera-msi",
281 .of_match_table
= altera_msi_of_match
,
283 .probe
= altera_msi_probe
,
284 .remove
= altera_msi_remove
,
287 static int __init
altera_msi_init(void)
289 return platform_driver_register(&altera_msi_driver
);
291 subsys_initcall(altera_msi_init
);