platform-msi.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <linux/device.h>
  21. #include <linux/idr.h>
  22. #include <linux/irq.h>
  23. #include <linux/irqdomain.h>
  24. #include <linux/msi.h>
  25. #include <linux/slab.h>
  26. #define DEV_ID_SHIFT 21
  27. #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
  28. /*
  29. * Internal data structure containing a (made up, but unique) devid
  30. * and the callback to write the MSI message.
  31. */
  32. struct platform_msi_priv_data {
  33. struct device *dev;
  34. void *host_data;
  35. msi_alloc_info_t arg;
  36. irq_write_msi_msg_t write_msg;
  37. int devid;
  38. };
  39. /* The devid allocator */
  40. static DEFINE_IDA(platform_msi_devid_ida);
  41. #ifdef GENERIC_MSI_DOMAIN_OPS
  42. /*
  43. * Convert an msi_desc to a globaly unique identifier (per-device
  44. * devid + msi_desc position in the msi_list).
  45. */
  46. static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
  47. {
  48. u32 devid;
  49. devid = desc->platform.msi_priv_data->devid;
  50. return (devid << (32 - DEV_ID_SHIFT)) | desc->platform.msi_index;
  51. }
  52. static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
  53. {
  54. arg->desc = desc;
  55. arg->hwirq = platform_msi_calc_hwirq(desc);
  56. }
  57. static int platform_msi_init(struct irq_domain *domain,
  58. struct msi_domain_info *info,
  59. unsigned int virq, irq_hw_number_t hwirq,
  60. msi_alloc_info_t *arg)
  61. {
  62. return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  63. info->chip, info->chip_data);
  64. }
  65. #else
  66. #define platform_msi_set_desc NULL
  67. #define platform_msi_init NULL
  68. #endif
  69. static void platform_msi_update_dom_ops(struct msi_domain_info *info)
  70. {
  71. struct msi_domain_ops *ops = info->ops;
  72. BUG_ON(!ops);
  73. if (ops->msi_init == NULL)
  74. ops->msi_init = platform_msi_init;
  75. if (ops->set_desc == NULL)
  76. ops->set_desc = platform_msi_set_desc;
  77. }
  78. static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
  79. {
  80. struct msi_desc *desc = irq_data_get_msi_desc(data);
  81. struct platform_msi_priv_data *priv_data;
  82. priv_data = desc->platform.msi_priv_data;
  83. priv_data->write_msg(desc, msg);
  84. }
  85. static void platform_msi_update_chip_ops(struct msi_domain_info *info)
  86. {
  87. struct irq_chip *chip = info->chip;
  88. BUG_ON(!chip);
  89. if (!chip->irq_mask)
  90. chip->irq_mask = irq_chip_mask_parent;
  91. if (!chip->irq_unmask)
  92. chip->irq_unmask = irq_chip_unmask_parent;
  93. if (!chip->irq_eoi)
  94. chip->irq_eoi = irq_chip_eoi_parent;
  95. if (!chip->irq_set_affinity)
  96. chip->irq_set_affinity = msi_domain_set_affinity;
  97. if (!chip->irq_write_msi_msg)
  98. chip->irq_write_msi_msg = platform_msi_write_msg;
  99. }
  100. static void platform_msi_free_descs(struct device *dev, int base, int nvec)
  101. {
  102. struct msi_desc *desc, *tmp;
  103. list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
  104. if (desc->platform.msi_index >= base &&
  105. desc->platform.msi_index < (base + nvec)) {
  106. list_del(&desc->list);
  107. free_msi_entry(desc);
  108. }
  109. }
  110. }
  111. static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
  112. int nvec,
  113. struct platform_msi_priv_data *data)
  114. {
  115. struct msi_desc *desc;
  116. int i, base = 0;
  117. if (!list_empty(dev_to_msi_list(dev))) {
  118. desc = list_last_entry(dev_to_msi_list(dev),
  119. struct msi_desc, list);
  120. base = desc->platform.msi_index + 1;
  121. }
  122. for (i = 0; i < nvec; i++) {
  123. desc = alloc_msi_entry(dev, 1, NULL);
  124. if (!desc)
  125. break;
  126. desc->platform.msi_priv_data = data;
  127. desc->platform.msi_index = base + i;
  128. desc->irq = virq ? virq + i : 0;
  129. list_add_tail(&desc->list, dev_to_msi_list(dev));
  130. }
  131. if (i != nvec) {
  132. /* Clean up the mess */
  133. platform_msi_free_descs(dev, base, nvec);
  134. return -ENOMEM;
  135. }
  136. return 0;
  137. }
  138. static int platform_msi_alloc_descs(struct device *dev, int nvec,
  139. struct platform_msi_priv_data *data)
  140. {
  141. return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
  142. }
  143. /**
  144. * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
  145. * @fwnode: Optional fwnode of the interrupt controller
  146. * @info: MSI domain info
  147. * @parent: Parent irq domain
  148. *
  149. * Updates the domain and chip ops and creates a platform MSI
  150. * interrupt domain.
  151. *
  152. * Returns:
  153. * A domain pointer or NULL in case of failure.
  154. */
  155. struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
  156. struct msi_domain_info *info,
  157. struct irq_domain *parent)
  158. {
  159. struct irq_domain *domain;
  160. if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
  161. platform_msi_update_dom_ops(info);
  162. if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
  163. platform_msi_update_chip_ops(info);
  164. domain = msi_create_irq_domain(fwnode, info, parent);
  165. if (domain)
  166. irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI);
  167. return domain;
  168. }
  169. static struct platform_msi_priv_data *
  170. platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
  171. irq_write_msi_msg_t write_msi_msg)
  172. {
  173. struct platform_msi_priv_data *datap;
  174. /*
  175. * Limit the number of interrupts to 2048 per device. Should we
  176. * need to bump this up, DEV_ID_SHIFT should be adjusted
  177. * accordingly (which would impact the max number of MSI
  178. * capable devices).
  179. */
  180. if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
  181. return ERR_PTR(-EINVAL);
  182. if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
  183. dev_err(dev, "Incompatible msi_domain, giving up\n");
  184. return ERR_PTR(-EINVAL);
  185. }
  186. /* Already had a helping of MSI? Greed... */
  187. if (!list_empty(dev_to_msi_list(dev)))
  188. return ERR_PTR(-EBUSY);
  189. datap = kzalloc(sizeof(*datap), GFP_KERNEL);
  190. if (!datap)
  191. return ERR_PTR(-ENOMEM);
  192. datap->devid = ida_simple_get(&platform_msi_devid_ida,
  193. 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
  194. if (datap->devid < 0) {
  195. int err = datap->devid;
  196. kfree(datap);
  197. return ERR_PTR(err);
  198. }
  199. datap->write_msg = write_msi_msg;
  200. datap->dev = dev;
  201. return datap;
  202. }
  203. static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
  204. {
  205. ida_simple_remove(&platform_msi_devid_ida, data->devid);
  206. kfree(data);
  207. }
  208. /**
  209. * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
  210. * @dev: The device for which to allocate interrupts
  211. * @nvec: The number of interrupts to allocate
  212. * @write_msi_msg: Callback to write an interrupt message for @dev
  213. *
  214. * Returns:
  215. * Zero for success, or an error code in case of failure
  216. */
  217. int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
  218. irq_write_msi_msg_t write_msi_msg)
  219. {
  220. struct platform_msi_priv_data *priv_data;
  221. int err;
  222. priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
  223. if (IS_ERR(priv_data))
  224. return PTR_ERR(priv_data);
  225. err = platform_msi_alloc_descs(dev, nvec, priv_data);
  226. if (err)
  227. goto out_free_priv_data;
  228. err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
  229. if (err)
  230. goto out_free_desc;
  231. return 0;
  232. out_free_desc:
  233. platform_msi_free_descs(dev, 0, nvec);
  234. out_free_priv_data:
  235. platform_msi_free_priv_data(priv_data);
  236. return err;
  237. }
  238. EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
  239. /**
  240. * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
  241. * @dev: The device for which to free interrupts
  242. */
  243. void platform_msi_domain_free_irqs(struct device *dev)
  244. {
  245. if (!list_empty(dev_to_msi_list(dev))) {
  246. struct msi_desc *desc;
  247. desc = first_msi_entry(dev);
  248. platform_msi_free_priv_data(desc->platform.msi_priv_data);
  249. }
  250. msi_domain_free_irqs(dev->msi_domain, dev);
  251. platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
  252. }
  253. EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
  254. /**
  255. * platform_msi_get_host_data - Query the private data associated with
  256. * a platform-msi domain
  257. * @domain: The platform-msi domain
  258. *
  259. * Returns the private data provided when calling
  260. * platform_msi_create_device_domain.
  261. */
  262. void *platform_msi_get_host_data(struct irq_domain *domain)
  263. {
  264. struct platform_msi_priv_data *data = domain->host_data;
  265. return data->host_data;
  266. }
  267. /**
  268. * platform_msi_create_device_domain - Create a platform-msi domain
  269. *
  270. * @dev: The device generating the MSIs
  271. * @nvec: The number of MSIs that need to be allocated
  272. * @write_msi_msg: Callback to write an interrupt message for @dev
  273. * @ops: The hierarchy domain operations to use
  274. * @host_data: Private data associated to this domain
  275. *
  276. * Returns an irqdomain for @nvec interrupts
  277. */
  278. struct irq_domain *
  279. platform_msi_create_device_domain(struct device *dev,
  280. unsigned int nvec,
  281. irq_write_msi_msg_t write_msi_msg,
  282. const struct irq_domain_ops *ops,
  283. void *host_data)
  284. {
  285. struct platform_msi_priv_data *data;
  286. struct irq_domain *domain;
  287. int err;
  288. data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
  289. if (IS_ERR(data))
  290. return NULL;
  291. data->host_data = host_data;
  292. domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
  293. dev->fwnode, ops, data);
  294. if (!domain)
  295. goto free_priv;
  296. err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
  297. if (err)
  298. goto free_domain;
  299. return domain;
  300. free_domain:
  301. irq_domain_remove(domain);
  302. free_priv:
  303. platform_msi_free_priv_data(data);
  304. return NULL;
  305. }
  306. /**
  307. * platform_msi_domain_free - Free interrupts associated with a platform-msi
  308. * domain
  309. *
  310. * @domain: The platform-msi domain
  311. * @virq: The base irq from which to perform the free operation
  312. * @nvec: How many interrupts to free from @virq
  313. */
  314. void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
  315. unsigned int nvec)
  316. {
  317. struct platform_msi_priv_data *data = domain->host_data;
  318. struct msi_desc *desc;
  319. for_each_msi_entry(desc, data->dev) {
  320. if (WARN_ON(!desc->irq || desc->nvec_used != 1))
  321. return;
  322. if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
  323. continue;
  324. irq_domain_free_irqs_common(domain, desc->irq, 1);
  325. }
  326. }
  327. /**
  328. * platform_msi_domain_alloc - Allocate interrupts associated with
  329. * a platform-msi domain
  330. *
  331. * @domain: The platform-msi domain
  332. * @virq: The base irq from which to perform the allocate operation
  333. * @nvec: How many interrupts to free from @virq
  334. *
  335. * Return 0 on success, or an error code on failure. Must be called
  336. * with irq_domain_mutex held (which can only be done as part of a
  337. * top-level interrupt allocation).
  338. */
  339. int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
  340. unsigned int nr_irqs)
  341. {
  342. struct platform_msi_priv_data *data = domain->host_data;
  343. int err;
  344. err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
  345. if (err)
  346. return err;
  347. err = msi_domain_populate_irqs(domain->parent, data->dev,
  348. virq, nr_irqs, &data->arg);
  349. if (err)
  350. platform_msi_domain_free(domain, virq, nr_irqs);
  351. return err;
  352. }