1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
4 * Copyright (C) 2022 Ventana Micro Systems Inc.
7 #include <linux/bitfield.h>
8 #include <linux/bitops.h>
10 #include <linux/interrupt.h>
11 #include <linux/irqchip.h>
12 #include <linux/irqchip/riscv-aplic.h>
13 #include <linux/irqchip/riscv-imsic.h>
14 #include <linux/module.h>
15 #include <linux/msi.h>
16 #include <linux/of_irq.h>
17 #include <linux/platform_device.h>
18 #include <linux/printk.h>
19 #include <linux/smp.h>
21 #include "irq-riscv-aplic-main.h"
23 static void aplic_msi_irq_mask(struct irq_data
*d
)
26 irq_chip_mask_parent(d
);
29 static void aplic_msi_irq_unmask(struct irq_data
*d
)
31 irq_chip_unmask_parent(d
);
35 static void aplic_msi_irq_retrigger_level(struct irq_data
*d
)
37 struct aplic_priv
*priv
= irq_data_get_irq_chip_data(d
);
39 switch (irqd_get_trigger_type(d
)) {
40 case IRQ_TYPE_LEVEL_LOW
:
41 case IRQ_TYPE_LEVEL_HIGH
:
43 * The section "4.9.2 Special consideration for level-sensitive interrupt
44 * sources" of the RISC-V AIA specification says:
46 * A second option is for the interrupt service routine to write the
47 * APLIC’s source identity number for the interrupt to the domain’s
48 * setipnum register just before exiting. This will cause the interrupt’s
49 * pending bit to be set to one again if the source is still asserting
50 * an interrupt, but not if the source is not asserting an interrupt.
52 writel(d
->hwirq
, priv
->regs
+ APLIC_SETIPNUM_LE
);
57 static void aplic_msi_irq_eoi(struct irq_data
*d
)
60 * EOI handling is required only for level-triggered interrupts
61 * when APLIC is in MSI mode.
63 aplic_msi_irq_retrigger_level(d
);
66 static int aplic_msi_irq_set_type(struct irq_data
*d
, unsigned int type
)
68 int rc
= aplic_irq_set_type(d
, type
);
73 * Updating sourcecfg register for level-triggered interrupts
74 * requires interrupt retriggering when APLIC is in MSI mode.
76 aplic_msi_irq_retrigger_level(d
);
80 static void aplic_msi_write_msg(struct irq_data
*d
, struct msi_msg
*msg
)
82 unsigned int group_index
, hart_index
, guest_index
, val
;
83 struct aplic_priv
*priv
= irq_data_get_irq_chip_data(d
);
84 struct aplic_msicfg
*mc
= &priv
->msicfg
;
85 phys_addr_t tppn
, tbppn
, msg_addr
;
88 /* For zeroed MSI, simply write zero into the target register */
89 if (!msg
->address_hi
&& !msg
->address_lo
&& !msg
->data
) {
90 target
= priv
->regs
+ APLIC_TARGET_BASE
;
91 target
+= (d
->hwirq
- 1) * sizeof(u32
);
96 /* Sanity check on message data */
97 WARN_ON(msg
->data
> APLIC_TARGET_EIID_MASK
);
99 /* Compute target MSI address */
100 msg_addr
= (((u64
)msg
->address_hi
) << 32) | msg
->address_lo
;
101 tppn
= msg_addr
>> APLIC_xMSICFGADDR_PPN_SHIFT
;
103 /* Compute target HART Base PPN */
105 tbppn
&= ~APLIC_xMSICFGADDR_PPN_HART(mc
->lhxs
);
106 tbppn
&= ~APLIC_xMSICFGADDR_PPN_LHX(mc
->lhxw
, mc
->lhxs
);
107 tbppn
&= ~APLIC_xMSICFGADDR_PPN_HHX(mc
->hhxw
, mc
->hhxs
);
108 WARN_ON(tbppn
!= mc
->base_ppn
);
110 /* Compute target group and hart indexes */
111 group_index
= (tppn
>> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc
->hhxs
)) &
112 APLIC_xMSICFGADDR_PPN_HHX_MASK(mc
->hhxw
);
113 hart_index
= (tppn
>> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc
->lhxs
)) &
114 APLIC_xMSICFGADDR_PPN_LHX_MASK(mc
->lhxw
);
115 hart_index
|= (group_index
<< mc
->lhxw
);
116 WARN_ON(hart_index
> APLIC_TARGET_HART_IDX_MASK
);
118 /* Compute target guest index */
119 guest_index
= tppn
& APLIC_xMSICFGADDR_PPN_HART(mc
->lhxs
);
120 WARN_ON(guest_index
> APLIC_TARGET_GUEST_IDX_MASK
);
122 /* Update IRQ TARGET register */
123 target
= priv
->regs
+ APLIC_TARGET_BASE
;
124 target
+= (d
->hwirq
- 1) * sizeof(u32
);
125 val
= FIELD_PREP(APLIC_TARGET_HART_IDX
, hart_index
);
126 val
|= FIELD_PREP(APLIC_TARGET_GUEST_IDX
, guest_index
);
127 val
|= FIELD_PREP(APLIC_TARGET_EIID
, msg
->data
);
131 static void aplic_msi_set_desc(msi_alloc_info_t
*arg
, struct msi_desc
*desc
)
134 arg
->hwirq
= (u32
)desc
->data
.icookie
.value
;
137 static int aplic_msi_translate(struct irq_domain
*d
, struct irq_fwspec
*fwspec
,
138 unsigned long *hwirq
, unsigned int *type
)
140 struct msi_domain_info
*info
= d
->host_data
;
141 struct aplic_priv
*priv
= info
->data
;
143 return aplic_irqdomain_translate(fwspec
, priv
->gsi_base
, hwirq
, type
);
146 static const struct msi_domain_template aplic_msi_template
= {
149 .irq_mask
= aplic_msi_irq_mask
,
150 .irq_unmask
= aplic_msi_irq_unmask
,
151 .irq_set_type
= aplic_msi_irq_set_type
,
152 .irq_eoi
= aplic_msi_irq_eoi
,
154 .irq_set_affinity
= irq_chip_set_affinity_parent
,
156 .irq_write_msi_msg
= aplic_msi_write_msg
,
157 .flags
= IRQCHIP_SET_TYPE_MASKED
|
158 IRQCHIP_SKIP_SET_WAKE
|
159 IRQCHIP_MASK_ON_SUSPEND
,
163 .set_desc
= aplic_msi_set_desc
,
164 .msi_translate
= aplic_msi_translate
,
168 .bus_token
= DOMAIN_BUS_WIRED_TO_MSI
,
169 .flags
= MSI_FLAG_USE_DEV_FWNODE
,
170 .handler
= handle_fasteoi_irq
,
171 .handler_name
= "fasteoi",
175 int aplic_msi_setup(struct device
*dev
, void __iomem
*regs
)
177 const struct imsic_global_config
*imsic_global
;
178 struct irq_domain
*msi_domain
;
179 struct aplic_priv
*priv
;
180 struct aplic_msicfg
*mc
;
184 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
188 rc
= aplic_setup_priv(priv
, dev
, regs
);
190 dev_err(dev
, "failed to create APLIC context\n");
196 * The APLIC outgoing MSI config registers assume target MSI
197 * controller to be RISC-V AIA IMSIC controller.
199 imsic_global
= imsic_get_global_config();
201 dev_err(dev
, "IMSIC global config not found\n");
205 /* Find number of guest index bits (LHXS) */
206 mc
->lhxs
= imsic_global
->guest_index_bits
;
207 if (APLIC_xMSICFGADDRH_LHXS_MASK
< mc
->lhxs
) {
208 dev_err(dev
, "IMSIC guest index bits big for APLIC LHXS\n");
212 /* Find number of HART index bits (LHXW) */
213 mc
->lhxw
= imsic_global
->hart_index_bits
;
214 if (APLIC_xMSICFGADDRH_LHXW_MASK
< mc
->lhxw
) {
215 dev_err(dev
, "IMSIC hart index bits big for APLIC LHXW\n");
219 /* Find number of group index bits (HHXW) */
220 mc
->hhxw
= imsic_global
->group_index_bits
;
221 if (APLIC_xMSICFGADDRH_HHXW_MASK
< mc
->hhxw
) {
222 dev_err(dev
, "IMSIC group index bits big for APLIC HHXW\n");
226 /* Find first bit position of group index (HHXS) */
227 mc
->hhxs
= imsic_global
->group_index_shift
;
228 if (mc
->hhxs
< (2 * APLIC_xMSICFGADDR_PPN_SHIFT
)) {
229 dev_err(dev
, "IMSIC group index shift should be >= %d\n",
230 (2 * APLIC_xMSICFGADDR_PPN_SHIFT
));
233 mc
->hhxs
-= (2 * APLIC_xMSICFGADDR_PPN_SHIFT
);
234 if (APLIC_xMSICFGADDRH_HHXS_MASK
< mc
->hhxs
) {
235 dev_err(dev
, "IMSIC group index shift big for APLIC HHXS\n");
239 /* Compute PPN base */
240 mc
->base_ppn
= imsic_global
->base_addr
>> APLIC_xMSICFGADDR_PPN_SHIFT
;
241 mc
->base_ppn
&= ~APLIC_xMSICFGADDR_PPN_HART(mc
->lhxs
);
242 mc
->base_ppn
&= ~APLIC_xMSICFGADDR_PPN_LHX(mc
->lhxw
, mc
->lhxs
);
243 mc
->base_ppn
&= ~APLIC_xMSICFGADDR_PPN_HHX(mc
->hhxw
, mc
->hhxs
);
245 /* Setup global config and interrupt delivery */
246 aplic_init_hw_global(priv
, true);
248 /* Set the APLIC device MSI domain if not available */
249 if (!dev_get_msi_domain(dev
)) {
251 * The device MSI domain for OF devices is only set at the
252 * time of populating/creating OF device. If the device MSI
253 * domain is discovered later after the OF device is created
254 * then we need to set it explicitly before using any platform
257 * In case of APLIC device, the parent MSI domain is always
258 * IMSIC and the IMSIC MSI domains are created later through
259 * the platform driver probing so we set it explicitly here.
261 if (is_of_node(dev
->fwnode
)) {
262 of_msi_configure(dev
, to_of_node(dev
->fwnode
));
264 msi_domain
= irq_find_matching_fwnode(imsic_acpi_get_fwnode(dev
),
265 DOMAIN_BUS_PLATFORM_MSI
);
267 dev_set_msi_domain(dev
, msi_domain
);
270 if (!dev_get_msi_domain(dev
))
271 return -EPROBE_DEFER
;
274 if (!msi_create_device_irq_domain(dev
, MSI_DEFAULT_DOMAIN
, &aplic_msi_template
,
275 priv
->nr_irqs
+ 1, priv
, priv
)) {
276 dev_err(dev
, "failed to create MSI irq domain\n");
280 /* Advertise the interrupt controller */
281 pa
= priv
->msicfg
.base_ppn
<< APLIC_xMSICFGADDR_PPN_SHIFT
;
282 dev_info(dev
, "%d interrupts forwarded to MSI base %pa\n", priv
->nr_irqs
, &pa
);