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/bitfield.h> 8 #include <linux/irqchip/riscv-aplic.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_irq.h> 12 #include <linux/platform_device.h> 13 #include <linux/printk.h> 14 15 #include "irq-riscv-aplic-main.h" 16 17 void aplic_irq_unmask(struct irq_data *d) 18 { 19 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 20 21 writel(d->hwirq, priv->regs + APLIC_SETIENUM); 22 } 23 24 void aplic_irq_mask(struct irq_data *d) 25 { 26 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 27 28 writel(d->hwirq, priv->regs + APLIC_CLRIENUM); 29 } 30 31 int aplic_irq_set_type(struct irq_data *d, unsigned int type) 32 { 33 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 34 void __iomem *sourcecfg; 35 u32 val = 0; 36 37 switch (type) { 38 case IRQ_TYPE_NONE: 39 val = APLIC_SOURCECFG_SM_INACTIVE; 40 break; 41 case IRQ_TYPE_LEVEL_LOW: 42 val = APLIC_SOURCECFG_SM_LEVEL_LOW; 43 break; 44 case IRQ_TYPE_LEVEL_HIGH: 45 val = APLIC_SOURCECFG_SM_LEVEL_HIGH; 46 break; 47 case IRQ_TYPE_EDGE_FALLING: 48 val = APLIC_SOURCECFG_SM_EDGE_FALL; 49 break; 50 case IRQ_TYPE_EDGE_RISING: 51 val = APLIC_SOURCECFG_SM_EDGE_RISE; 52 break; 53 default: 54 return -EINVAL; 55 } 56 57 sourcecfg = priv->regs + APLIC_SOURCECFG_BASE; 58 sourcecfg += (d->hwirq - 1) * sizeof(u32); 59 writel(val, sourcecfg); 60 61 return 0; 62 } 63 64 int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, 65 unsigned long *hwirq, unsigned int *type) 66 { 67 if (WARN_ON(fwspec->param_count < 2)) 68 return -EINVAL; 69 if (WARN_ON(!fwspec->param[0])) 70 return -EINVAL; 71 72 /* For DT, gsi_base is always zero. */ 73 *hwirq = fwspec->param[0] - gsi_base; 74 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 75 76 WARN_ON(*type == IRQ_TYPE_NONE); 77 78 return 0; 79 } 80 81 void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) 82 { 83 u32 val; 84 #ifdef CONFIG_RISCV_M_MODE 85 u32 valh; 86 87 if (msi_mode) { 88 val = lower_32_bits(priv->msicfg.base_ppn); 89 valh = FIELD_PREP(APLIC_xMSICFGADDRH_BAPPN, upper_32_bits(priv->msicfg.base_ppn)); 90 valh |= FIELD_PREP(APLIC_xMSICFGADDRH_LHXW, priv->msicfg.lhxw); 91 valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXW, priv->msicfg.hhxw); 92 valh |= FIELD_PREP(APLIC_xMSICFGADDRH_LHXS, priv->msicfg.lhxs); 93 valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); 94 writel(val, priv->regs + APLIC_xMSICFGADDR); 95 writel(valh, priv->regs + APLIC_xMSICFGADDRH); 96 } 97 #endif 98 99 /* Setup APLIC domaincfg register */ 100 val = readl(priv->regs + APLIC_DOMAINCFG); 101 val |= APLIC_DOMAINCFG_IE; 102 if (msi_mode) 103 val |= APLIC_DOMAINCFG_DM; 104 writel(val, priv->regs + APLIC_DOMAINCFG); 105 if (readl(priv->regs + APLIC_DOMAINCFG) != val) 106 dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); 107 } 108 109 static void aplic_init_hw_irqs(struct aplic_priv *priv) 110 { 111 int i; 112 113 /* Disable all interrupts */ 114 for (i = 0; i <= priv->nr_irqs; i += 32) 115 writel(-1U, priv->regs + APLIC_CLRIE_BASE + (i / 32) * sizeof(u32)); 116 117 /* Set interrupt type and default priority for all interrupts */ 118 for (i = 1; i <= priv->nr_irqs; i++) { 119 writel(0, priv->regs + APLIC_SOURCECFG_BASE + (i - 1) * sizeof(u32)); 120 writel(APLIC_DEFAULT_PRIORITY, 121 priv->regs + APLIC_TARGET_BASE + (i - 1) * sizeof(u32)); 122 } 123 124 /* Clear APLIC domaincfg */ 125 writel(0, priv->regs + APLIC_DOMAINCFG); 126 } 127 128 int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs) 129 { 130 struct device_node *np = to_of_node(dev->fwnode); 131 struct of_phandle_args parent; 132 int rc; 133 134 /* 135 * Currently, only OF fwnode is supported so extend this 136 * function for ACPI support. 137 */ 138 if (!np) 139 return -EINVAL; 140 141 /* Save device pointer and register base */ 142 priv->dev = dev; 143 priv->regs = regs; 144 145 /* Find out number of interrupt sources */ 146 rc = of_property_read_u32(np, "riscv,num-sources", &priv->nr_irqs); 147 if (rc) { 148 dev_err(dev, "failed to get number of interrupt sources\n"); 149 return rc; 150 } 151 152 /* 153 * Find out number of IDCs based on parent interrupts 154 * 155 * If "msi-parent" property is present then we ignore the 156 * APLIC IDCs which forces the APLIC driver to use MSI mode. 157 */ 158 if (!of_property_present(np, "msi-parent")) { 159 while (!of_irq_parse_one(np, priv->nr_idcs, &parent)) 160 priv->nr_idcs++; 161 } 162 163 /* Setup initial state APLIC interrupts */ 164 aplic_init_hw_irqs(priv); 165 166 return 0; 167 } 168 169 static int aplic_probe(struct platform_device *pdev) 170 { 171 struct device *dev = &pdev->dev; 172 bool msi_mode = false; 173 void __iomem *regs; 174 int rc; 175 176 /* Map the MMIO registers */ 177 regs = devm_platform_ioremap_resource(pdev, 0); 178 if (!regs) { 179 dev_err(dev, "failed map MMIO registers\n"); 180 return -ENOMEM; 181 } 182 183 /* 184 * If msi-parent property is present then setup APLIC MSI 185 * mode otherwise setup APLIC direct mode. 186 */ 187 msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); 188 if (msi_mode) 189 rc = aplic_msi_setup(dev, regs); 190 else 191 rc = aplic_direct_setup(dev, regs); 192 if (rc) 193 dev_err(dev, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); 194 195 return rc; 196 } 197 198 static const struct of_device_id aplic_match[] = { 199 { .compatible = "riscv,aplic" }, 200 {} 201 }; 202 203 static struct platform_driver aplic_driver = { 204 .driver = { 205 .name = "riscv-aplic", 206 .of_match_table = aplic_match, 207 }, 208 .probe = aplic_probe, 209 }; 210 builtin_platform_driver(aplic_driver); 211