platform-msi.c (9941B)
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 */ 23struct 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 */ 32static 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 */ 39static 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 46static 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 52static 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 61static 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 71static 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 83static 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 90static 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 */ 122struct 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 142static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, 143 irq_write_msi_msg_t write_msi_msg) 144{ 145 struct platform_msi_priv_data *datap; 146 int err; 147 148 /* 149 * Limit the number of interrupts to 2048 per device. Should we 150 * need to bump this up, DEV_ID_SHIFT should be adjusted 151 * accordingly (which would impact the max number of MSI 152 * capable devices). 153 */ 154 if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS) 155 return -EINVAL; 156 157 if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { 158 dev_err(dev, "Incompatible msi_domain, giving up\n"); 159 return -EINVAL; 160 } 161 162 err = msi_setup_device_data(dev); 163 if (err) 164 return err; 165 166 /* Already initialized? */ 167 if (dev->msi.data->platform_data) 168 return -EBUSY; 169 170 datap = kzalloc(sizeof(*datap), GFP_KERNEL); 171 if (!datap) 172 return -ENOMEM; 173 174 datap->devid = ida_simple_get(&platform_msi_devid_ida, 175 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); 176 if (datap->devid < 0) { 177 err = datap->devid; 178 kfree(datap); 179 return err; 180 } 181 182 datap->write_msg = write_msi_msg; 183 datap->dev = dev; 184 dev->msi.data->platform_data = datap; 185 return 0; 186} 187 188static void platform_msi_free_priv_data(struct device *dev) 189{ 190 struct platform_msi_priv_data *data = dev->msi.data->platform_data; 191 192 dev->msi.data->platform_data = NULL; 193 ida_simple_remove(&platform_msi_devid_ida, data->devid); 194 kfree(data); 195} 196 197/** 198 * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev 199 * @dev: The device for which to allocate interrupts 200 * @nvec: The number of interrupts to allocate 201 * @write_msi_msg: Callback to write an interrupt message for @dev 202 * 203 * Returns: 204 * Zero for success, or an error code in case of failure 205 */ 206int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, 207 irq_write_msi_msg_t write_msi_msg) 208{ 209 int err; 210 211 err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 212 if (err) 213 return err; 214 215 err = msi_domain_alloc_irqs(dev->msi.domain, dev, nvec); 216 if (err) 217 platform_msi_free_priv_data(dev); 218 219 return err; 220} 221EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs); 222 223/** 224 * platform_msi_domain_free_irqs - Free MSI interrupts for @dev 225 * @dev: The device for which to free interrupts 226 */ 227void platform_msi_domain_free_irqs(struct device *dev) 228{ 229 msi_domain_free_irqs(dev->msi.domain, dev); 230 platform_msi_free_priv_data(dev); 231} 232EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs); 233 234/** 235 * platform_msi_get_host_data - Query the private data associated with 236 * a platform-msi domain 237 * @domain: The platform-msi domain 238 * 239 * Return: The private data provided when calling 240 * platform_msi_create_device_domain(). 241 */ 242void *platform_msi_get_host_data(struct irq_domain *domain) 243{ 244 struct platform_msi_priv_data *data = domain->host_data; 245 246 return data->host_data; 247} 248 249static struct lock_class_key platform_device_msi_lock_class; 250 251/** 252 * __platform_msi_create_device_domain - Create a platform-msi device domain 253 * 254 * @dev: The device generating the MSIs 255 * @nvec: The number of MSIs that need to be allocated 256 * @is_tree: flag to indicate tree hierarchy 257 * @write_msi_msg: Callback to write an interrupt message for @dev 258 * @ops: The hierarchy domain operations to use 259 * @host_data: Private data associated to this domain 260 * 261 * Return: An irqdomain for @nvec interrupts on success, NULL in case of error. 262 * 263 * This is for interrupt domains which stack on a platform-msi domain 264 * created by platform_msi_create_irq_domain(). @dev->msi.domain points to 265 * that platform-msi domain which is the parent for the new domain. 266 */ 267struct irq_domain * 268__platform_msi_create_device_domain(struct device *dev, 269 unsigned int nvec, 270 bool is_tree, 271 irq_write_msi_msg_t write_msi_msg, 272 const struct irq_domain_ops *ops, 273 void *host_data) 274{ 275 struct platform_msi_priv_data *data; 276 struct irq_domain *domain; 277 int err; 278 279 err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); 280 if (err) 281 return NULL; 282 283 /* 284 * Use a separate lock class for the MSI descriptor mutex on 285 * platform MSI device domains because the descriptor mutex nests 286 * into the domain mutex. See alloc/free below. 287 */ 288 lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class); 289 290 data = dev->msi.data->platform_data; 291 data->host_data = host_data; 292 domain = irq_domain_create_hierarchy(dev->msi.domain, 0, 293 is_tree ? 0 : nvec, 294 dev->fwnode, ops, data); 295 if (!domain) 296 goto free_priv; 297 298 platform_msi_set_proxy_dev(&data->arg); 299 err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg); 300 if (err) 301 goto free_domain; 302 303 return domain; 304 305free_domain: 306 irq_domain_remove(domain); 307free_priv: 308 platform_msi_free_priv_data(dev); 309 return NULL; 310} 311 312/** 313 * platform_msi_device_domain_free - Free interrupts associated with a platform-msi 314 * device domain 315 * 316 * @domain: The platform-msi device domain 317 * @virq: The base irq from which to perform the free operation 318 * @nr_irqs: How many interrupts to free from @virq 319 */ 320void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq, 321 unsigned int nr_irqs) 322{ 323 struct platform_msi_priv_data *data = domain->host_data; 324 325 msi_lock_descs(data->dev); 326 irq_domain_free_irqs_common(domain, virq, nr_irqs); 327 msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1); 328 msi_unlock_descs(data->dev); 329} 330 331/** 332 * platform_msi_device_domain_alloc - Allocate interrupts associated with 333 * a platform-msi device domain 334 * 335 * @domain: The platform-msi device domain 336 * @virq: The base irq from which to perform the allocate operation 337 * @nr_irqs: How many interrupts to allocate from @virq 338 * 339 * Return 0 on success, or an error code on failure. Must be called 340 * with irq_domain_mutex held (which can only be done as part of a 341 * top-level interrupt allocation). 342 */ 343int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq, 344 unsigned int nr_irqs) 345{ 346 struct platform_msi_priv_data *data = domain->host_data; 347 struct device *dev = data->dev; 348 349 return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg); 350}