platform-msi.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * MSI framework for platform devices
  3. *
  4. * Copyright (C) 2015 ARM Limited, All Rights Reserved.
  5. * Author: Marc Zyngier <marc.zyngier@arm.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <linux/device.h>
  20. #include <linux/idr.h>
  21. #include <linux/irq.h>
  22. #include <linux/irqdomain.h>
  23. #include <linux/msi.h>
  24. #include <linux/slab.h>
  25. #define DEV_ID_SHIFT 21
  26. #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
  27. /*
  28. * Internal data structure containing a (made up, but unique) devid
  29. * and the callback to write the MSI message.
  30. */
  31. struct platform_msi_priv_data {
  32. struct device *dev;
  33. void *host_data;
  34. msi_alloc_info_t arg;
  35. irq_write_msi_msg_t write_msg;
  36. int devid;
  37. };
  38. /* The devid allocator */
  39. static DEFINE_IDA(platform_msi_devid_ida);
  40. #ifdef GENERIC_MSI_DOMAIN_OPS
  41. /*
  42. * Convert an msi_desc to a globaly unique identifier (per-device
  43. * devid + msi_desc position in the msi_list).
  44. */
  45. static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc)
  46. {
  47. u32 devid;
  48. devid = desc->platform.msi_priv_data->devid;
  49. return (devid << (32 - DEV_ID_SHIFT)) | desc->platform.msi_index;
  50. }
  51. static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
  52. {
  53. arg->desc = desc;
  54. arg->hwirq = platform_msi_calc_hwirq(desc);
  55. }
  56. static int platform_msi_init(struct irq_domain *domain,
  57. struct msi_domain_info *info,
  58. unsigned int virq, irq_hw_number_t hwirq,
  59. msi_alloc_info_t *arg)
  60. {
  61. return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  62. info->chip, info->chip_data);
  63. }
  64. #else
  65. #define platform_msi_set_desc NULL
  66. #define platform_msi_init NULL
  67. #endif
  68. static void platform_msi_update_dom_ops(struct msi_domain_info *info)
  69. {
  70. struct msi_domain_ops *ops = info->ops;
  71. BUG_ON(!ops);
  72. if (ops->msi_init == NULL)
  73. ops->msi_init = platform_msi_init;
  74. if (ops->set_desc == NULL)
  75. ops->set_desc = platform_msi_set_desc;
  76. }
  77. static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
  78. {
  79. struct msi_desc *desc = irq_data_get_msi_desc(data);
  80. struct platform_msi_priv_data *priv_data;
  81. priv_data = desc->platform.msi_priv_data;
  82. priv_data->write_msg(desc, msg);
  83. }
  84. static void platform_msi_update_chip_ops(struct msi_domain_info *info)
  85. {
  86. struct irq_chip *chip = info->chip;
  87. BUG_ON(!chip);
  88. if (!chip->irq_mask)
  89. chip->irq_mask = irq_chip_mask_parent;
  90. if (!chip->irq_unmask)
  91. chip->irq_unmask = irq_chip_unmask_parent;
  92. if (!chip->irq_eoi)
  93. chip->irq_eoi = irq_chip_eoi_parent;
  94. if (!chip->irq_set_affinity)
  95. chip->irq_set_affinity = msi_domain_set_affinity;
  96. if (!chip->irq_write_msi_msg)
  97. chip->irq_write_msi_msg = platform_msi_write_msg;
  98. }
  99. static void platform_msi_free_descs(struct device *dev, int base, int nvec)
  100. {
  101. struct msi_desc *desc, *tmp;
  102. list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
  103. if (desc->platform.msi_index >= base &&
  104. desc->platform.msi_index < (base + nvec)) {
  105. list_del(&desc->list);
  106. free_msi_entry(desc);
  107. }
  108. }
  109. }
  110. static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
  111. int nvec,
  112. struct platform_msi_priv_data *data)
  113. {
  114. struct msi_desc *desc;
  115. int i, base = 0;
  116. if (!list_empty(dev_to_msi_list(dev))) {
  117. desc = list_last_entry(dev_to_msi_list(dev),
  118. struct msi_desc, list);
  119. base = desc->platform.msi_index + 1;
  120. }
  121. for (i = 0; i < nvec; i++) {
  122. desc = alloc_msi_entry(dev);
  123. if (!desc)
  124. break;
  125. desc->platform.msi_priv_data = data;
  126. desc->platform.msi_index = base + i;
  127. desc->nvec_used = 1;
  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. domain->bus_token = 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 256 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. of_node_to_fwnode(dev->of_node),
  294. ops, data);
  295. if (!domain)
  296. goto free_priv;
  297. err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
  298. if (err)
  299. goto free_domain;
  300. return domain;
  301. free_domain:
  302. irq_domain_remove(domain);
  303. free_priv:
  304. platform_msi_free_priv_data(data);
  305. return NULL;
  306. }
  307. /**
  308. * platform_msi_domain_free - Free interrupts associated with a platform-msi
  309. * domain
  310. *
  311. * @domain: The platform-msi domain
  312. * @virq: The base irq from which to perform the free operation
  313. * @nvec: How many interrupts to free from @virq
  314. */
  315. void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
  316. unsigned int nvec)
  317. {
  318. struct platform_msi_priv_data *data = domain->host_data;
  319. struct msi_desc *desc;
  320. for_each_msi_entry(desc, data->dev) {
  321. if (WARN_ON(!desc->irq || desc->nvec_used != 1))
  322. return;
  323. if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
  324. continue;
  325. irq_domain_free_irqs_common(domain, desc->irq, 1);
  326. }
  327. }
  328. /**
  329. * platform_msi_domain_alloc - Allocate interrupts associated with
  330. * a platform-msi domain
  331. *
  332. * @domain: The platform-msi domain
  333. * @virq: The base irq from which to perform the allocate operation
  334. * @nvec: How many interrupts to free from @virq
  335. *
  336. * Return 0 on success, or an error code on failure. Must be called
  337. * with irq_domain_mutex held (which can only be done as part of a
  338. * top-level interrupt allocation).
  339. */
  340. int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
  341. unsigned int nr_irqs)
  342. {
  343. struct platform_msi_priv_data *data = domain->host_data;
  344. int err;
  345. err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
  346. if (err)
  347. return err;
  348. err = msi_domain_populate_irqs(domain->parent, data->dev,
  349. virq, nr_irqs, &data->arg);
  350. if (err)
  351. platform_msi_domain_free(domain, virq, nr_irqs);
  352. return err;
  353. }