1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
4 * Loongson PCH MSI support
7 #define pr_fmt(fmt) "pch-msi: " fmt
9 #include <linux/irqchip.h>
10 #include <linux/msi.h>
12 #include <linux/of_address.h>
13 #include <linux/of_irq.h>
14 #include <linux/of_pci.h>
15 #include <linux/pci.h>
16 #include <linux/slab.h>
18 #include "irq-msi-lib.h"
19 #include "irq-loongson.h"
24 struct mutex msi_map_lock
;
26 u32 irq_first
; /* The vector number that MSIs starts */
27 u32 num_irqs
; /* The number of vectors for MSIs */
28 unsigned long *msi_map
;
31 static struct fwnode_handle
*pch_msi_handle
[MAX_IO_PICS
];
33 static int pch_msi_allocate_hwirq(struct pch_msi_data
*priv
, int num_req
)
37 mutex_lock(&priv
->msi_map_lock
);
39 first
= bitmap_find_free_region(priv
->msi_map
, priv
->num_irqs
,
40 get_count_order(num_req
));
42 mutex_unlock(&priv
->msi_map_lock
);
46 mutex_unlock(&priv
->msi_map_lock
);
48 return priv
->irq_first
+ first
;
51 static void pch_msi_free_hwirq(struct pch_msi_data
*priv
,
52 int hwirq
, int num_req
)
54 int first
= hwirq
- priv
->irq_first
;
56 mutex_lock(&priv
->msi_map_lock
);
57 bitmap_release_region(priv
->msi_map
, first
, get_count_order(num_req
));
58 mutex_unlock(&priv
->msi_map_lock
);
61 static void pch_msi_compose_msi_msg(struct irq_data
*data
,
64 struct pch_msi_data
*priv
= irq_data_get_irq_chip_data(data
);
66 msg
->address_hi
= upper_32_bits(priv
->doorbell
);
67 msg
->address_lo
= lower_32_bits(priv
->doorbell
);
68 msg
->data
= data
->hwirq
;
71 static struct irq_chip middle_irq_chip
= {
73 .irq_mask
= irq_chip_mask_parent
,
74 .irq_unmask
= irq_chip_unmask_parent
,
75 .irq_ack
= irq_chip_ack_parent
,
76 .irq_set_affinity
= irq_chip_set_affinity_parent
,
77 .irq_compose_msi_msg
= pch_msi_compose_msi_msg
,
80 static int pch_msi_parent_domain_alloc(struct irq_domain
*domain
,
81 unsigned int virq
, int hwirq
)
83 struct irq_fwspec fwspec
;
85 fwspec
.fwnode
= domain
->parent
->fwnode
;
86 fwspec
.param_count
= 1;
87 fwspec
.param
[0] = hwirq
;
89 return irq_domain_alloc_irqs_parent(domain
, virq
, 1, &fwspec
);
92 static int pch_msi_middle_domain_alloc(struct irq_domain
*domain
,
94 unsigned int nr_irqs
, void *args
)
96 struct pch_msi_data
*priv
= domain
->host_data
;
99 hwirq
= pch_msi_allocate_hwirq(priv
, nr_irqs
);
103 for (i
= 0; i
< nr_irqs
; i
++) {
104 err
= pch_msi_parent_domain_alloc(domain
, virq
+ i
, hwirq
+ i
);
108 irq_domain_set_hwirq_and_chip(domain
, virq
+ i
, hwirq
+ i
,
109 &middle_irq_chip
, priv
);
115 pch_msi_free_hwirq(priv
, hwirq
, nr_irqs
);
116 irq_domain_free_irqs_parent(domain
, virq
, i
);
121 static void pch_msi_middle_domain_free(struct irq_domain
*domain
,
123 unsigned int nr_irqs
)
125 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
126 struct pch_msi_data
*priv
= irq_data_get_irq_chip_data(d
);
128 irq_domain_free_irqs_parent(domain
, virq
, nr_irqs
);
129 pch_msi_free_hwirq(priv
, d
->hwirq
, nr_irqs
);
132 static const struct irq_domain_ops pch_msi_middle_domain_ops
= {
133 .alloc
= pch_msi_middle_domain_alloc
,
134 .free
= pch_msi_middle_domain_free
,
135 .select
= msi_lib_irq_domain_select
,
138 #define PCH_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
139 MSI_FLAG_USE_DEF_CHIP_OPS | \
140 MSI_FLAG_PCI_MSI_MASK_PARENT)
142 #define PCH_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
143 MSI_FLAG_PCI_MSIX | \
144 MSI_FLAG_MULTI_PCI_MSI)
146 static struct msi_parent_ops pch_msi_parent_ops
= {
147 .required_flags
= PCH_MSI_FLAGS_REQUIRED
,
148 .supported_flags
= PCH_MSI_FLAGS_SUPPORTED
,
149 .bus_select_mask
= MATCH_PCI_MSI
,
150 .bus_select_token
= DOMAIN_BUS_NEXUS
,
152 .init_dev_msi_info
= msi_lib_init_dev_msi_info
,
155 static int pch_msi_init_domains(struct pch_msi_data
*priv
,
156 struct irq_domain
*parent
,
157 struct fwnode_handle
*domain_handle
)
159 struct irq_domain
*middle_domain
;
161 middle_domain
= irq_domain_create_hierarchy(parent
, 0, priv
->num_irqs
,
163 &pch_msi_middle_domain_ops
,
165 if (!middle_domain
) {
166 pr_err("Failed to create the MSI middle domain\n");
170 irq_domain_update_bus_token(middle_domain
, DOMAIN_BUS_NEXUS
);
172 middle_domain
->flags
|= IRQ_DOMAIN_FLAG_MSI_PARENT
;
173 middle_domain
->msi_parent_ops
= &pch_msi_parent_ops
;
178 static int pch_msi_init(phys_addr_t msg_address
, int irq_base
, int irq_count
,
179 struct irq_domain
*parent_domain
, struct fwnode_handle
*domain_handle
)
182 struct pch_msi_data
*priv
;
184 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
188 mutex_init(&priv
->msi_map_lock
);
190 priv
->doorbell
= msg_address
;
191 priv
->irq_first
= irq_base
;
192 priv
->num_irqs
= irq_count
;
194 priv
->msi_map
= bitmap_zalloc(priv
->num_irqs
, GFP_KERNEL
);
198 pr_debug("Registering %d MSIs, starting at %d\n",
199 priv
->num_irqs
, priv
->irq_first
);
201 ret
= pch_msi_init_domains(priv
, parent_domain
, domain_handle
);
205 pch_msi_handle
[nr_pics
++] = domain_handle
;
209 bitmap_free(priv
->msi_map
);
217 static int pch_msi_of_init(struct device_node
*node
, struct device_node
*parent
)
220 int irq_base
, irq_count
;
222 struct irq_domain
*parent_domain
;
224 parent_domain
= irq_find_host(parent
);
225 if (!parent_domain
) {
226 pr_err("Failed to find the parent domain\n");
230 if (of_address_to_resource(node
, 0, &res
)) {
231 pr_err("Failed to allocate resource\n");
235 if (of_property_read_u32(node
, "loongson,msi-base-vec", &irq_base
)) {
236 pr_err("Unable to parse MSI vec base\n");
240 if (of_property_read_u32(node
, "loongson,msi-num-vecs", &irq_count
)) {
241 pr_err("Unable to parse MSI vec number\n");
245 err
= pch_msi_init(res
.start
, irq_base
, irq_count
, parent_domain
, of_node_to_fwnode(node
));
252 IRQCHIP_DECLARE(pch_msi
, "loongson,pch-msi-1.0", pch_msi_of_init
);
256 struct fwnode_handle
*get_pch_msi_handle(int pci_segment
)
259 return pch_msi_handle
[0];
261 for (int i
= 0; i
< MAX_IO_PICS
; i
++) {
262 if (msi_group
[i
].pci_segment
== pci_segment
)
263 return pch_msi_handle
[i
];
265 return pch_msi_handle
[0];
268 int __init
pch_msi_acpi_init(struct irq_domain
*parent
, struct acpi_madt_msi_pic
*acpi_pchmsi
)
271 struct fwnode_handle
*domain_handle
;
273 domain_handle
= irq_domain_alloc_fwnode(&acpi_pchmsi
->msg_address
);
274 ret
= pch_msi_init(acpi_pchmsi
->msg_address
, acpi_pchmsi
->start
,
275 acpi_pchmsi
->count
, parent
, domain_handle
);
277 irq_domain_free_fwnode(domain_handle
);
282 int __init
pch_msi_acpi_init_avec(struct irq_domain
*parent
)
284 if (pch_msi_handle
[0])
287 pch_msi_handle
[0] = parent
->fwnode
;
288 irq_domain_update_bus_token(parent
, DOMAIN_BUS_NEXUS
);
290 parent
->flags
|= IRQ_DOMAIN_FLAG_MSI_PARENT
;
291 parent
->msi_parent_ops
= &pch_msi_parent_ops
;