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/bitops.h> 9 #include <linux/cpu.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> 20 21 #include "irq-riscv-aplic-main.h" 22 23 static void aplic_msi_irq_mask(struct irq_data *d) 24 { 25 aplic_irq_mask(d); 26 irq_chip_mask_parent(d); 27 } 28 29 static void aplic_msi_irq_unmask(struct irq_data *d) 30 { 31 irq_chip_unmask_parent(d); 32 aplic_irq_unmask(d); 33 } 34 35 static void aplic_msi_irq_eoi(struct irq_data *d) 36 { 37 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 38 39 /* 40 * EOI handling is required only for level-triggered interrupts 41 * when APLIC is in MSI mode. 42 */ 43 44 switch (irqd_get_trigger_type(d)) { 45 case IRQ_TYPE_LEVEL_LOW: 46 case IRQ_TYPE_LEVEL_HIGH: 47 /* 48 * The section "4.9.2 Special consideration for level-sensitive interrupt 49 * sources" of the RISC-V AIA specification says: 50 * 51 * A second option is for the interrupt service routine to write the 52 * APLIC’s source identity number for the interrupt to the domain’s 53 * setipnum register just before exiting. This will cause the interrupt’s 54 * pending bit to be set to one again if the source is still asserting 55 * an interrupt, but not if the source is not asserting an interrupt. 56 */ 57 writel(d->hwirq, priv->regs + APLIC_SETIPNUM_LE); 58 break; 59 } 60 } 61 62 static void aplic_msi_write_msg(struct irq_data *d, struct msi_msg *msg) 63 { 64 unsigned int group_index, hart_index, guest_index, val; 65 struct aplic_priv *priv = irq_data_get_irq_chip_data(d); 66 struct aplic_msicfg *mc = &priv->msicfg; 67 phys_addr_t tppn, tbppn, msg_addr; 68 void __iomem *target; 69 70 /* For zeroed MSI, simply write zero into the target register */ 71 if (!msg->address_hi && !msg->address_lo && !msg->data) { 72 target = priv->regs + APLIC_TARGET_BASE; 73 target += (d->hwirq - 1) * sizeof(u32); 74 writel(0, target); 75 return; 76 } 77 78 /* Sanity check on message data */ 79 WARN_ON(msg->data > APLIC_TARGET_EIID_MASK); 80 81 /* Compute target MSI address */ 82 msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo; 83 tppn = msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT; 84 85 /* Compute target HART Base PPN */ 86 tbppn = tppn; 87 tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs); 88 tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs); 89 tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs); 90 WARN_ON(tbppn != mc->base_ppn); 91 92 /* Compute target group and hart indexes */ 93 group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) & 94 APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw); 95 hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) & 96 APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw); 97 hart_index |= (group_index << mc->lhxw); 98 WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK); 99 100 /* Compute target guest index */ 101 guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs); 102 WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK); 103 104 /* Update IRQ TARGET register */ 105 target = priv->regs + APLIC_TARGET_BASE; 106 target += (d->hwirq - 1) * sizeof(u32); 107 val = FIELD_PREP(APLIC_TARGET_HART_IDX, hart_index); 108 val |= FIELD_PREP(APLIC_TARGET_GUEST_IDX, guest_index); 109 val |= FIELD_PREP(APLIC_TARGET_EIID, msg->data); 110 writel(val, target); 111 } 112 113 static void aplic_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) 114 { 115 arg->desc = desc; 116 arg->hwirq = (u32)desc->data.icookie.value; 117 } 118 119 static int aplic_msi_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 120 unsigned long *hwirq, unsigned int *type) 121 { 122 struct msi_domain_info *info = d->host_data; 123 struct aplic_priv *priv = info->data; 124 125 return aplic_irqdomain_translate(fwspec, priv->gsi_base, hwirq, type); 126 } 127 128 static const struct msi_domain_template aplic_msi_template = { 129 .chip = { 130 .name = "APLIC-MSI", 131 .irq_mask = aplic_msi_irq_mask, 132 .irq_unmask = aplic_msi_irq_unmask, 133 .irq_set_type = aplic_irq_set_type, 134 .irq_eoi = aplic_msi_irq_eoi, 135 #ifdef CONFIG_SMP 136 .irq_set_affinity = irq_chip_set_affinity_parent, 137 #endif 138 .irq_write_msi_msg = aplic_msi_write_msg, 139 .flags = IRQCHIP_SET_TYPE_MASKED | 140 IRQCHIP_SKIP_SET_WAKE | 141 IRQCHIP_MASK_ON_SUSPEND, 142 }, 143 144 .ops = { 145 .set_desc = aplic_msi_set_desc, 146 .msi_translate = aplic_msi_translate, 147 }, 148 149 .info = { 150 .bus_token = DOMAIN_BUS_WIRED_TO_MSI, 151 .flags = MSI_FLAG_USE_DEV_FWNODE, 152 .handler = handle_fasteoi_irq, 153 .handler_name = "fasteoi", 154 }, 155 }; 156 157 int aplic_msi_setup(struct device *dev, void __iomem *regs) 158 { 159 const struct imsic_global_config *imsic_global; 160 struct aplic_priv *priv; 161 struct aplic_msicfg *mc; 162 phys_addr_t pa; 163 int rc; 164 165 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 166 if (!priv) 167 return -ENOMEM; 168 169 rc = aplic_setup_priv(priv, dev, regs); 170 if (rc) { 171 dev_err(dev, "failed to create APLIC context\n"); 172 return rc; 173 } 174 mc = &priv->msicfg; 175 176 /* 177 * The APLIC outgoing MSI config registers assume target MSI 178 * controller to be RISC-V AIA IMSIC controller. 179 */ 180 imsic_global = imsic_get_global_config(); 181 if (!imsic_global) { 182 dev_err(dev, "IMSIC global config not found\n"); 183 return -ENODEV; 184 } 185 186 /* Find number of guest index bits (LHXS) */ 187 mc->lhxs = imsic_global->guest_index_bits; 188 if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) { 189 dev_err(dev, "IMSIC guest index bits big for APLIC LHXS\n"); 190 return -EINVAL; 191 } 192 193 /* Find number of HART index bits (LHXW) */ 194 mc->lhxw = imsic_global->hart_index_bits; 195 if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) { 196 dev_err(dev, "IMSIC hart index bits big for APLIC LHXW\n"); 197 return -EINVAL; 198 } 199 200 /* Find number of group index bits (HHXW) */ 201 mc->hhxw = imsic_global->group_index_bits; 202 if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) { 203 dev_err(dev, "IMSIC group index bits big for APLIC HHXW\n"); 204 return -EINVAL; 205 } 206 207 /* Find first bit position of group index (HHXS) */ 208 mc->hhxs = imsic_global->group_index_shift; 209 if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) { 210 dev_err(dev, "IMSIC group index shift should be >= %d\n", 211 (2 * APLIC_xMSICFGADDR_PPN_SHIFT)); 212 return -EINVAL; 213 } 214 mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT); 215 if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) { 216 dev_err(dev, "IMSIC group index shift big for APLIC HHXS\n"); 217 return -EINVAL; 218 } 219 220 /* Compute PPN base */ 221 mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT; 222 mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs); 223 mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs); 224 mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs); 225 226 /* Setup global config and interrupt delivery */ 227 aplic_init_hw_global(priv, true); 228 229 /* Set the APLIC device MSI domain if not available */ 230 if (!dev_get_msi_domain(dev)) { 231 /* 232 * The device MSI domain for OF devices is only set at the 233 * time of populating/creating OF device. If the device MSI 234 * domain is discovered later after the OF device is created 235 * then we need to set it explicitly before using any platform 236 * MSI functions. 237 * 238 * In case of APLIC device, the parent MSI domain is always 239 * IMSIC and the IMSIC MSI domains are created later through 240 * the platform driver probing so we set it explicitly here. 241 */ 242 if (is_of_node(dev->fwnode)) 243 of_msi_configure(dev, to_of_node(dev->fwnode)); 244 } 245 246 if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, &aplic_msi_template, 247 priv->nr_irqs + 1, priv, priv)) { 248 dev_err(dev, "failed to create MSI irq domain\n"); 249 return -ENOMEM; 250 } 251 252 /* Advertise the interrupt controller */ 253 pa = priv->msicfg.base_ppn << APLIC_xMSICFGADDR_PPN_SHIFT; 254 dev_info(dev, "%d interrupts forwarded to MSI base %pa\n", priv->nr_irqs, &pa); 255 256 return 0; 257 } 258