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/acpi.h>
8 #include <linux/bitfield.h>
9 #include <linux/irqchip/riscv-aplic.h>
10 #include <linux/irqchip/riscv-imsic.h>
11 #include <linux/module.h>
13 #include <linux/of_irq.h>
14 #include <linux/platform_device.h>
15 #include <linux/printk.h>
17 #include "irq-riscv-aplic-main.h"
19 void aplic_irq_unmask(struct irq_data
*d
)
21 struct aplic_priv
*priv
= irq_data_get_irq_chip_data(d
);
23 writel(d
->hwirq
, priv
->regs
+ APLIC_SETIENUM
);
26 void aplic_irq_mask(struct irq_data
*d
)
28 struct aplic_priv
*priv
= irq_data_get_irq_chip_data(d
);
30 writel(d
->hwirq
, priv
->regs
+ APLIC_CLRIENUM
);
33 int aplic_irq_set_type(struct irq_data
*d
, unsigned int type
)
35 struct aplic_priv
*priv
= irq_data_get_irq_chip_data(d
);
36 void __iomem
*sourcecfg
;
41 val
= APLIC_SOURCECFG_SM_INACTIVE
;
43 case IRQ_TYPE_LEVEL_LOW
:
44 val
= APLIC_SOURCECFG_SM_LEVEL_LOW
;
46 case IRQ_TYPE_LEVEL_HIGH
:
47 val
= APLIC_SOURCECFG_SM_LEVEL_HIGH
;
49 case IRQ_TYPE_EDGE_FALLING
:
50 val
= APLIC_SOURCECFG_SM_EDGE_FALL
;
52 case IRQ_TYPE_EDGE_RISING
:
53 val
= APLIC_SOURCECFG_SM_EDGE_RISE
;
59 sourcecfg
= priv
->regs
+ APLIC_SOURCECFG_BASE
;
60 sourcecfg
+= (d
->hwirq
- 1) * sizeof(u32
);
61 writel(val
, sourcecfg
);
66 int aplic_irqdomain_translate(struct irq_fwspec
*fwspec
, u32 gsi_base
,
67 unsigned long *hwirq
, unsigned int *type
)
69 if (WARN_ON(fwspec
->param_count
< 2))
71 if (WARN_ON(!fwspec
->param
[0]))
74 /* For DT, gsi_base is always zero. */
75 *hwirq
= fwspec
->param
[0] - gsi_base
;
76 *type
= fwspec
->param
[1] & IRQ_TYPE_SENSE_MASK
;
78 WARN_ON(*type
== IRQ_TYPE_NONE
);
83 void aplic_init_hw_global(struct aplic_priv
*priv
, bool msi_mode
)
86 #ifdef CONFIG_RISCV_M_MODE
90 val
= lower_32_bits(priv
->msicfg
.base_ppn
);
91 valh
= FIELD_PREP(APLIC_xMSICFGADDRH_BAPPN
, upper_32_bits(priv
->msicfg
.base_ppn
));
92 valh
|= FIELD_PREP(APLIC_xMSICFGADDRH_LHXW
, priv
->msicfg
.lhxw
);
93 valh
|= FIELD_PREP(APLIC_xMSICFGADDRH_HHXW
, priv
->msicfg
.hhxw
);
94 valh
|= FIELD_PREP(APLIC_xMSICFGADDRH_LHXS
, priv
->msicfg
.lhxs
);
95 valh
|= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS
, priv
->msicfg
.hhxs
);
96 writel(val
, priv
->regs
+ APLIC_xMSICFGADDR
);
97 writel(valh
, priv
->regs
+ APLIC_xMSICFGADDRH
);
101 /* Setup APLIC domaincfg register */
102 val
= readl(priv
->regs
+ APLIC_DOMAINCFG
);
103 val
|= APLIC_DOMAINCFG_IE
;
105 val
|= APLIC_DOMAINCFG_DM
;
106 writel(val
, priv
->regs
+ APLIC_DOMAINCFG
);
107 if (readl(priv
->regs
+ APLIC_DOMAINCFG
) != val
)
108 dev_warn(priv
->dev
, "unable to write 0x%x in domaincfg\n", val
);
111 static void aplic_init_hw_irqs(struct aplic_priv
*priv
)
115 /* Disable all interrupts */
116 for (i
= 0; i
<= priv
->nr_irqs
; i
+= 32)
117 writel(-1U, priv
->regs
+ APLIC_CLRIE_BASE
+ (i
/ 32) * sizeof(u32
));
119 /* Set interrupt type and default priority for all interrupts */
120 for (i
= 1; i
<= priv
->nr_irqs
; i
++) {
121 writel(0, priv
->regs
+ APLIC_SOURCECFG_BASE
+ (i
- 1) * sizeof(u32
));
122 writel(APLIC_DEFAULT_PRIORITY
,
123 priv
->regs
+ APLIC_TARGET_BASE
+ (i
- 1) * sizeof(u32
));
126 /* Clear APLIC domaincfg */
127 writel(0, priv
->regs
+ APLIC_DOMAINCFG
);
131 static const struct acpi_device_id aplic_acpi_match
[] = {
135 MODULE_DEVICE_TABLE(acpi
, aplic_acpi_match
);
139 int aplic_setup_priv(struct aplic_priv
*priv
, struct device
*dev
, void __iomem
*regs
)
141 struct device_node
*np
= to_of_node(dev
->fwnode
);
142 struct of_phandle_args parent
;
145 /* Save device pointer and register base */
150 /* Find out number of interrupt sources */
151 rc
= of_property_read_u32(np
, "riscv,num-sources", &priv
->nr_irqs
);
153 dev_err(dev
, "failed to get number of interrupt sources\n");
158 * Find out number of IDCs based on parent interrupts
160 * If "msi-parent" property is present then we ignore the
161 * APLIC IDCs which forces the APLIC driver to use MSI mode.
163 if (!of_property_present(np
, "msi-parent")) {
164 while (!of_irq_parse_one(np
, priv
->nr_idcs
, &parent
))
168 rc
= riscv_acpi_get_gsi_info(dev
->fwnode
, &priv
->gsi_base
, &priv
->acpi_aplic_id
,
169 &priv
->nr_irqs
, &priv
->nr_idcs
);
171 dev_err(dev
, "failed to find GSI mapping\n");
176 /* Setup initial state APLIC interrupts */
177 aplic_init_hw_irqs(priv
);
182 static int aplic_probe(struct platform_device
*pdev
)
184 struct device
*dev
= &pdev
->dev
;
185 bool msi_mode
= false;
189 /* Map the MMIO registers */
190 regs
= devm_platform_ioremap_resource(pdev
, 0);
192 dev_err(dev
, "failed map MMIO registers\n");
193 return PTR_ERR(regs
);
197 * If msi-parent property is present then setup APLIC MSI
198 * mode otherwise setup APLIC direct mode.
200 if (is_of_node(dev
->fwnode
))
201 msi_mode
= of_property_present(to_of_node(dev
->fwnode
), "msi-parent");
203 msi_mode
= imsic_acpi_get_fwnode(NULL
) ? 1 : 0;
206 rc
= aplic_msi_setup(dev
, regs
);
208 rc
= aplic_direct_setup(dev
, regs
);
210 dev_err_probe(dev
, rc
, "failed to setup APLIC in %s mode\n",
211 msi_mode
? "MSI" : "direct");
215 acpi_dev_clear_dependencies(ACPI_COMPANION(dev
));
221 static const struct of_device_id aplic_match
[] = {
222 { .compatible
= "riscv,aplic" },
226 static struct platform_driver aplic_driver
= {
228 .name
= "riscv-aplic",
229 .of_match_table
= aplic_match
,
230 .acpi_match_table
= ACPI_PTR(aplic_acpi_match
),
232 .probe
= aplic_probe
,
234 builtin_platform_driver(aplic_driver
);