1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MSI framework for platform devices 4 * 5 * Copyright (C) 2015 ARM Limited, All Rights Reserved. 6 * Author: Marc Zyngier <marc.zyngier@arm.com> 7 */ 8 9 #include <linux/device.h> 10 #include <linux/idr.h> 11 #include <linux/irq.h> 12 #include <linux/irqdomain.h> 13 #include <linux/msi.h> 14 #include <linux/slab.h> 15 16 #define DEV_ID_SHIFT 21 17 #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT)) 18 19 /* 20 * Internal data structure containing a (made up, but unique) devid 21 * and the callback to write the MSI message. 22 */ 23 struct platform_msi_priv_data { 24 struct device *dev; 25 void *host_data; 26 msi_alloc_info_t arg; 27 irq_write_msi_msg_t write_msg; 28 int devid; 29 }; 30 31 /* The devid allocator */ 32 static DEFINE_IDA(platform_msi_devid_ida); 33 34 #ifdef GENERIC_MSI_DOMAIN_OPS 35 /* 36 * Convert an msi_desc to a globaly unique identifier (per-device 37 * devid + msi_desc position in the msi_list). 38 */ 39 static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc) 40 { 41 u32 devid = desc->dev->msi.data->platform_data->devid; 42 43 return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index; 44 } 45 46 static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) 47 { 48 arg->desc = desc; 49 arg->hwirq = platform_msi_calc_hwirq(desc); 50 } 51 52 static int platform_msi_init(struct irq_domain *domain, 53 struct msi_domain_info *info, 54 unsigned int virq, irq_hw_number_t hwirq, 55 msi_alloc_info_t *arg) 56 { 57 return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 58 info->chip, info->chip_data); 59 } 60 61 static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg) 62 { 63 arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; 64 } 65 #else 66 #define platform_msi_set_desc NULL 67 #define platform_msi_init NULL 68 #define platform_msi_set_proxy_dev(x) do {} while(0) 69 #endif 70 71 static void platform_msi_update_dom_ops(struct msi_domain_info *info) 72 { 73 struct msi_domain_ops *ops = info->ops; 74 75 BUG_ON(!ops); 76 77 if (ops->msi_init == NULL) 78 ops->msi_init = platform_msi_init; 79 if (ops->set_desc == NULL) 80 ops->set_desc = platform_msi_set_desc; 81 } 82 83 static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) 84 { 85 struct msi_desc *desc = irq_data_get_msi_desc(data); 86 87 desc->dev->msi.data->platform_data->write_msg(desc, msg); 88 } 89 90 static void platform_msi_update_chip_ops(struct msi_domain_info *info) 91 { 92 struct irq_chip *chip = info->chip; 93 94 BUG_ON(!chip); 95 if (!chip->irq_mask) 96 chip->irq_mask = irq_chip_mask_parent; 97 if (!chip->irq_unmask) 98 chip->irq_unmask = irq_chip_unmask_parent; 99 if (!chip->irq_eoi) 100 chip->irq_eoi = irq_chip_eoi_parent; 101 if (!chip->irq_set_affinity) 102 chip->irq_set_affinity = msi_domain_set_affinity; 103 if (!chip->irq_write_msi_msg) 104 chip->irq_write_msi_msg = platform_msi_write_msg; 105 if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) && 106 !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI))) 107 info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; 108 } 109 110 /** 111 * platform_msi_create_irq_domain - Create a platform MSI interrupt domain 112 * @fwnode: Optional fwnode of the interrupt controller 113 * @info: MSI domain info 114 * @parent: Parent irq domain 115 * 116 * Updates the domain and chip ops and creates a platform MSI 117 * interrupt domain. 118 * 119 * Returns: 120 * A domain pointer or NULL in case of failure. 121 */ 122 struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, 123 struct msi_domain_info *info, 124 struct irq_domain *parent) 125 { 126 struct irq_domain *domain; 127 128 if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) 129 platform_msi_update_dom_ops(info); 130 if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) 131 platform_msi_update_chip_ops(info); 132 info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | 133 MSI_FLAG_FREE_MSI_DESCS; 134 135 domain = msi_create_irq_domain(fwnode, info, parent); 136 if (domain) 137 irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI); 138 139 return domain; 140 } 141 EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain); 142 143 static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, 144 irq_write_msi_msg_t write_msi_msg) 145 { 146 struct platform_msi_priv_data *datap; 147 int err; 148 149 /* 150 * Limit the number of interrupts to 2048 per device. Should we 151 * need to bump this up, DEV_ID_SHIFT should be adjusted 152 * accordingly (which would impact the max number of MSI 153 * capable devices). 154 */ 155 if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS) 156 return -EINVAL; 157 158 if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { 159 dev_err(dev, "Incompatible msi_domain, giving up\n"); 160 return -EINVAL; 161 } 162 163 err = msi_setup_device_data(dev); 164 if (err) 165 return err; 166 167 /* Already initialized? */ 168 if (dev->msi.data->platform_data) 169 return -EBUSY; 170 171 datap = kzalloc(sizeof(*datap), GFP_KERNEL); 172 if (!datap) 173 return -ENOMEM; 174 175 datap->devid = ida_simple_get(&platform_msi_devid_ida, 176 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); 177 if (datap->devid < 0) { 178 err = datap->devid; 179 kfree(datap); 180 return err; 181 } 182 183 datap->write_msg = write_msi_msg; 184 datap->dev = dev; 185 dev->msi.data->platform_data = datap; 186 return 0; 187 } 188 189 static void platform_msi_free_priv_data(struct device *dev) 190 { 191 struct platform_msi_priv_data *data = dev->msi.data->platform_data; 192 193 dev->msi.data->platform_data = NULL; 194 ida_simple_remove(&platform_msi_devid_ida, data->devid); 195 kfree(data); 196 } 197 198 /** 199 * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev 200 * @dev: The device for which to allocate interrupts 201 * @nvec: The number of interrupts to allocate 202 * @write_msi_msg: Callback to write an interrupt message for @dev 203 * 204 * Returns: 205 * Zero for success, or an error code in case of failure 206 */ 207 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, 208 irq_write_msi_msg_t write_msi_msg) 209 { 210 int err; 211 212 err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 213 if (err) 214 return err; 215 216 err = msi_domain_alloc_irqs(dev->msi.domain, dev, nvec); 217 if (err) 218 platform_msi_free_priv_data(dev); 219 220 return err; 221 } 222 EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs); 223 224 /** 225 * platform_msi_domain_free_irqs - Free MSI interrupts for @dev 226 * @dev: The device for which to free interrupts 227 */ 228 void platform_msi_domain_free_irqs(struct device *dev) 229 { 230 msi_domain_free_irqs(dev->msi.domain, dev); 231 platform_msi_free_priv_data(dev); 232 } 233 EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs); 234 235 /** 236 * platform_msi_get_host_data - Query the private data associated with 237 * a platform-msi domain 238 * @domain: The platform-msi domain 239 * 240 * Return: The private data provided when calling 241 * platform_msi_create_device_domain(). 242 */ 243 void *platform_msi_get_host_data(struct irq_domain *domain) 244 { 245 struct platform_msi_priv_data *data = domain->host_data; 246 247 return data->host_data; 248 } 249 250 static struct lock_class_key platform_device_msi_lock_class; 251 252 /** 253 * __platform_msi_create_device_domain - Create a platform-msi device domain 254 * 255 * @dev: The device generating the MSIs 256 * @nvec: The number of MSIs that need to be allocated 257 * @is_tree: flag to indicate tree hierarchy 258 * @write_msi_msg: Callback to write an interrupt message for @dev 259 * @ops: The hierarchy domain operations to use 260 * @host_data: Private data associated to this domain 261 * 262 * Return: An irqdomain for @nvec interrupts on success, NULL in case of error. 263 * 264 * This is for interrupt domains which stack on a platform-msi domain 265 * created by platform_msi_create_irq_domain(). @dev->msi.domain points to 266 * that platform-msi domain which is the parent for the new domain. 267 */ 268 struct irq_domain * 269 __platform_msi_create_device_domain(struct device *dev, 270 unsigned int nvec, 271 bool is_tree, 272 irq_write_msi_msg_t write_msi_msg, 273 const struct irq_domain_ops *ops, 274 void *host_data) 275 { 276 struct platform_msi_priv_data *data; 277 struct irq_domain *domain; 278 int err; 279 280 err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 281 if (err) 282 return NULL; 283 284 /* 285 * Use a separate lock class for the MSI descriptor mutex on 286 * platform MSI device domains because the descriptor mutex nests 287 * into the domain mutex. See alloc/free below. 288 */ 289 lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class); 290 291 data = dev->msi.data->platform_data; 292 data->host_data = host_data; 293 domain = irq_domain_create_hierarchy(dev->msi.domain, 0, 294 is_tree ? 0 : nvec, 295 dev->fwnode, ops, data); 296 if (!domain) 297 goto free_priv; 298 299 platform_msi_set_proxy_dev(&data->arg); 300 err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg); 301 if (err) 302 goto free_domain; 303 304 return domain; 305 306 free_domain: 307 irq_domain_remove(domain); 308 free_priv: 309 platform_msi_free_priv_data(dev); 310 return NULL; 311 } 312 313 /** 314 * platform_msi_device_domain_free - Free interrupts associated with a platform-msi 315 * device domain 316 * 317 * @domain: The platform-msi device domain 318 * @virq: The base irq from which to perform the free operation 319 * @nr_irqs: How many interrupts to free from @virq 320 */ 321 void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq, 322 unsigned int nr_irqs) 323 { 324 struct platform_msi_priv_data *data = domain->host_data; 325 326 msi_lock_descs(data->dev); 327 irq_domain_free_irqs_common(domain, virq, nr_irqs); 328 msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1); 329 msi_unlock_descs(data->dev); 330 } 331 332 /** 333 * platform_msi_device_domain_alloc - Allocate interrupts associated with 334 * a platform-msi device domain 335 * 336 * @domain: The platform-msi device domain 337 * @virq: The base irq from which to perform the allocate operation 338 * @nr_irqs: How many interrupts to allocate from @virq 339 * 340 * Return 0 on success, or an error code on failure. Must be called 341 * with irq_domain_mutex held (which can only be done as part of a 342 * top-level interrupt allocation). 343 */ 344 int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq, 345 unsigned int nr_irqs) 346 { 347 struct platform_msi_priv_data *data = domain->host_data; 348 struct device *dev = data->dev; 349 350 return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg); 351 } 352