1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com> 4 * Loongson PCH MSI support 5 */ 6 7 #define pr_fmt(fmt) "pch-msi: " fmt 8 9 #include <linux/irqchip.h> 10 #include <linux/msi.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/of_irq.h> 14 #include <linux/of_pci.h> 15 #include <linux/pci.h> 16 #include <linux/slab.h> 17 18 struct pch_msi_data { 19 struct mutex msi_map_lock; 20 phys_addr_t doorbell; 21 u32 irq_first; /* The vector number that MSIs starts */ 22 u32 num_irqs; /* The number of vectors for MSIs */ 23 unsigned long *msi_map; 24 }; 25 26 static void pch_msi_mask_msi_irq(struct irq_data *d) 27 { 28 pci_msi_mask_irq(d); 29 irq_chip_mask_parent(d); 30 } 31 32 static void pch_msi_unmask_msi_irq(struct irq_data *d) 33 { 34 irq_chip_unmask_parent(d); 35 pci_msi_unmask_irq(d); 36 } 37 38 static struct irq_chip pch_msi_irq_chip = { 39 .name = "PCH PCI MSI", 40 .irq_mask = pch_msi_mask_msi_irq, 41 .irq_unmask = pch_msi_unmask_msi_irq, 42 .irq_ack = irq_chip_ack_parent, 43 .irq_set_affinity = irq_chip_set_affinity_parent, 44 }; 45 46 static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req) 47 { 48 int first; 49 50 mutex_lock(&priv->msi_map_lock); 51 52 first = bitmap_find_free_region(priv->msi_map, priv->num_irqs, 53 get_count_order(num_req)); 54 if (first < 0) { 55 mutex_unlock(&priv->msi_map_lock); 56 return -ENOSPC; 57 } 58 59 mutex_unlock(&priv->msi_map_lock); 60 61 return priv->irq_first + first; 62 } 63 64 static void pch_msi_free_hwirq(struct pch_msi_data *priv, 65 int hwirq, int num_req) 66 { 67 int first = hwirq - priv->irq_first; 68 69 mutex_lock(&priv->msi_map_lock); 70 bitmap_release_region(priv->msi_map, first, get_count_order(num_req)); 71 mutex_unlock(&priv->msi_map_lock); 72 } 73 74 static void pch_msi_compose_msi_msg(struct irq_data *data, 75 struct msi_msg *msg) 76 { 77 struct pch_msi_data *priv = irq_data_get_irq_chip_data(data); 78 79 msg->address_hi = upper_32_bits(priv->doorbell); 80 msg->address_lo = lower_32_bits(priv->doorbell); 81 msg->data = data->hwirq; 82 } 83 84 static struct msi_domain_info pch_msi_domain_info = { 85 .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 86 MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, 87 .chip = &pch_msi_irq_chip, 88 }; 89 90 static struct irq_chip middle_irq_chip = { 91 .name = "PCH MSI", 92 .irq_mask = irq_chip_mask_parent, 93 .irq_unmask = irq_chip_unmask_parent, 94 .irq_ack = irq_chip_ack_parent, 95 .irq_set_affinity = irq_chip_set_affinity_parent, 96 .irq_compose_msi_msg = pch_msi_compose_msi_msg, 97 }; 98 99 static int pch_msi_parent_domain_alloc(struct irq_domain *domain, 100 unsigned int virq, int hwirq) 101 { 102 struct irq_fwspec fwspec; 103 int ret; 104 105 fwspec.fwnode = domain->parent->fwnode; 106 fwspec.param_count = 1; 107 fwspec.param[0] = hwirq; 108 109 ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 110 if (ret) 111 return ret; 112 113 return 0; 114 } 115 116 static int pch_msi_middle_domain_alloc(struct irq_domain *domain, 117 unsigned int virq, 118 unsigned int nr_irqs, void *args) 119 { 120 struct pch_msi_data *priv = domain->host_data; 121 int hwirq, err, i; 122 123 hwirq = pch_msi_allocate_hwirq(priv, nr_irqs); 124 if (hwirq < 0) 125 return hwirq; 126 127 for (i = 0; i < nr_irqs; i++) { 128 err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i); 129 if (err) 130 goto err_hwirq; 131 132 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 133 &middle_irq_chip, priv); 134 } 135 136 return 0; 137 138 err_hwirq: 139 pch_msi_free_hwirq(priv, hwirq, nr_irqs); 140 irq_domain_free_irqs_parent(domain, virq, i - 1); 141 142 return err; 143 } 144 145 static void pch_msi_middle_domain_free(struct irq_domain *domain, 146 unsigned int virq, 147 unsigned int nr_irqs) 148 { 149 struct irq_data *d = irq_domain_get_irq_data(domain, virq); 150 struct pch_msi_data *priv = irq_data_get_irq_chip_data(d); 151 152 irq_domain_free_irqs_parent(domain, virq, nr_irqs); 153 pch_msi_free_hwirq(priv, d->hwirq, nr_irqs); 154 } 155 156 static const struct irq_domain_ops pch_msi_middle_domain_ops = { 157 .alloc = pch_msi_middle_domain_alloc, 158 .free = pch_msi_middle_domain_free, 159 }; 160 161 static int pch_msi_init_domains(struct pch_msi_data *priv, 162 struct device_node *node, 163 struct irq_domain *parent) 164 { 165 struct irq_domain *middle_domain, *msi_domain; 166 167 middle_domain = irq_domain_create_linear(of_node_to_fwnode(node), 168 priv->num_irqs, 169 &pch_msi_middle_domain_ops, 170 priv); 171 if (!middle_domain) { 172 pr_err("Failed to create the MSI middle domain\n"); 173 return -ENOMEM; 174 } 175 176 middle_domain->parent = parent; 177 irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS); 178 179 msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), 180 &pch_msi_domain_info, 181 middle_domain); 182 if (!msi_domain) { 183 pr_err("Failed to create PCI MSI domain\n"); 184 irq_domain_remove(middle_domain); 185 return -ENOMEM; 186 } 187 188 return 0; 189 } 190 191 static int pch_msi_init(struct device_node *node, 192 struct device_node *parent) 193 { 194 struct pch_msi_data *priv; 195 struct irq_domain *parent_domain; 196 struct resource res; 197 int ret; 198 199 parent_domain = irq_find_host(parent); 200 if (!parent_domain) { 201 pr_err("Failed to find the parent domain\n"); 202 return -ENXIO; 203 } 204 205 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 206 if (!priv) 207 return -ENOMEM; 208 209 mutex_init(&priv->msi_map_lock); 210 211 ret = of_address_to_resource(node, 0, &res); 212 if (ret) { 213 pr_err("Failed to allocate resource\n"); 214 goto err_priv; 215 } 216 217 priv->doorbell = res.start; 218 219 if (of_property_read_u32(node, "loongson,msi-base-vec", 220 &priv->irq_first)) { 221 pr_err("Unable to parse MSI vec base\n"); 222 ret = -EINVAL; 223 goto err_priv; 224 } 225 226 if (of_property_read_u32(node, "loongson,msi-num-vecs", 227 &priv->num_irqs)) { 228 pr_err("Unable to parse MSI vec number\n"); 229 ret = -EINVAL; 230 goto err_priv; 231 } 232 233 priv->msi_map = bitmap_alloc(priv->num_irqs, GFP_KERNEL); 234 if (!priv->msi_map) { 235 ret = -ENOMEM; 236 goto err_priv; 237 } 238 239 pr_debug("Registering %d MSIs, starting at %d\n", 240 priv->num_irqs, priv->irq_first); 241 242 ret = pch_msi_init_domains(priv, node, parent_domain); 243 if (ret) 244 goto err_map; 245 246 return 0; 247 248 err_map: 249 kfree(priv->msi_map); 250 err_priv: 251 kfree(priv); 252 return ret; 253 } 254 255 IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init); 256