1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2021 Western Digital Corporation or its affiliates. 4 * Copyright (C) 2022 Ventana Micro Systems Inc. 5 */ 6 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> 12 #include <linux/of.h> 13 #include <linux/of_irq.h> 14 #include <linux/platform_device.h> 15 #include <linux/printk.h> 16 17 #include "irq-riscv-aplic-main.h" 18 19 void aplic_irq_unmask(struct irq_data *d) 20 { 21 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 22 23 writel(d->hwirq, priv->regs + APLIC_SETIENUM); 24 } 25 26 void aplic_irq_mask(struct irq_data *d) 27 { 28 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 29 30 writel(d->hwirq, priv->regs + APLIC_CLRIENUM); 31 } 32 33 int aplic_irq_set_type(struct irq_data *d, unsigned int type) 34 { 35 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 36 void __iomem *sourcecfg; 37 u32 val = 0; 38 39 switch (type) { 40 case IRQ_TYPE_NONE: 41 val = APLIC_SOURCECFG_SM_INACTIVE; 42 break; 43 case IRQ_TYPE_LEVEL_LOW: 44 val = APLIC_SOURCECFG_SM_LEVEL_LOW; 45 break; 46 case IRQ_TYPE_LEVEL_HIGH: 47 val = APLIC_SOURCECFG_SM_LEVEL_HIGH; 48 break; 49 case IRQ_TYPE_EDGE_FALLING: 50 val = APLIC_SOURCECFG_SM_EDGE_FALL; 51 break; 52 case IRQ_TYPE_EDGE_RISING: 53 val = APLIC_SOURCECFG_SM_EDGE_RISE; 54 break; 55 default: 56 return -EINVAL; 57 } 58 59 sourcecfg = priv->regs + APLIC_SOURCECFG_BASE; 60 sourcecfg += (d->hwirq - 1) * sizeof(u32); 61 writel(val, sourcecfg); 62 63 return 0; 64 } 65 66 int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, 67 unsigned long *hwirq, unsigned int *type) 68 { 69 if (WARN_ON(fwspec->param_count < 2)) 70 return -EINVAL; 71 if (WARN_ON(!fwspec->param[0])) 72 return -EINVAL; 73 74 /* For DT, gsi_base is always zero. */ 75 *hwirq = fwspec->param[0] - gsi_base; 76 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 77 78 WARN_ON(*type == IRQ_TYPE_NONE); 79 80 return 0; 81 } 82 83 void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) 84 { 85 u32 val; 86 #ifdef CONFIG_RISCV_M_MODE 87 u32 valh; 88 89 if (msi_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); 98 } 99 #endif 100 101 /* Setup APLIC domaincfg register */ 102 val = readl(priv->regs + APLIC_DOMAINCFG); 103 val |= APLIC_DOMAINCFG_IE; 104 if (msi_mode) 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); 109 } 110 111 static void aplic_init_hw_irqs(struct aplic_priv *priv) 112 { 113 int i; 114 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)); 118 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)); 124 } 125 126 /* Clear APLIC domaincfg */ 127 writel(0, priv->regs + APLIC_DOMAINCFG); 128 } 129 130 #ifdef CONFIG_ACPI 131 static const struct acpi_device_id aplic_acpi_match[] = { 132 { "RSCV0002", 0 }, 133 {} 134 }; 135 MODULE_DEVICE_TABLE(acpi, aplic_acpi_match); 136 137 #endif 138 139 int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs) 140 { 141 struct device_node *np = to_of_node(dev->fwnode); 142 struct of_phandle_args parent; 143 int rc; 144 145 /* Save device pointer and register base */ 146 priv->dev = dev; 147 priv->regs = regs; 148 149 if (np) { 150 /* Find out number of interrupt sources */ 151 rc = of_property_read_u32(np, "riscv,num-sources", &priv->nr_irqs); 152 if (rc) { 153 dev_err(dev, "failed to get number of interrupt sources\n"); 154 return rc; 155 } 156 157 /* 158 * Find out number of IDCs based on parent interrupts 159 * 160 * If "msi-parent" property is present then we ignore the 161 * APLIC IDCs which forces the APLIC driver to use MSI mode. 162 */ 163 if (!of_property_present(np, "msi-parent")) { 164 while (!of_irq_parse_one(np, priv->nr_idcs, &parent)) 165 priv->nr_idcs++; 166 } 167 } else { 168 rc = riscv_acpi_get_gsi_info(dev->fwnode, &priv->gsi_base, &priv->acpi_aplic_id, 169 &priv->nr_irqs, &priv->nr_idcs); 170 if (rc) { 171 dev_err(dev, "failed to find GSI mapping\n"); 172 return rc; 173 } 174 } 175 176 /* Setup initial state APLIC interrupts */ 177 aplic_init_hw_irqs(priv); 178 179 return 0; 180 } 181 182 static int aplic_probe(struct platform_device *pdev) 183 { 184 struct device *dev = &pdev->dev; 185 bool msi_mode = false; 186 void __iomem *regs; 187 int rc; 188 189 /* Map the MMIO registers */ 190 regs = devm_platform_ioremap_resource(pdev, 0); 191 if (IS_ERR(regs)) { 192 dev_err(dev, "failed map MMIO registers\n"); 193 return PTR_ERR(regs); 194 } 195 196 /* 197 * If msi-parent property is present then setup APLIC MSI 198 * mode otherwise setup APLIC direct mode. 199 */ 200 if (is_of_node(dev->fwnode)) 201 msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); 202 else 203 msi_mode = imsic_acpi_get_fwnode(NULL) ? 1 : 0; 204 205 if (msi_mode) 206 rc = aplic_msi_setup(dev, regs); 207 else 208 rc = aplic_direct_setup(dev, regs); 209 if (rc) 210 dev_err(dev, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); 211 212 #ifdef CONFIG_ACPI 213 if (!acpi_disabled) 214 acpi_dev_clear_dependencies(ACPI_COMPANION(dev)); 215 #endif 216 217 return rc; 218 } 219 220 static const struct of_device_id aplic_match[] = { 221 { .compatible = "riscv,aplic" }, 222 {} 223 }; 224 225 static struct platform_driver aplic_driver = { 226 .driver = { 227 .name = "riscv-aplic", 228 .of_match_table = aplic_match, 229 .acpi_match_table = ACPI_PTR(aplic_acpi_match), 230 }, 231 .probe = aplic_probe, 232 }; 233 builtin_platform_driver(aplic_driver); 234