8060-irqchip-Add-Layerscape-SCFG-MSI-controller-support.patch 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. From 83ec4322b33e8d7908a3df0343246882e4e6b83a Mon Sep 17 00:00:00 2001
  2. From: Minghuan Lian <Minghuan.Lian@nxp.com>
  3. Date: Wed, 23 Mar 2016 19:08:20 +0800
  4. Subject: [PATCH 60/70] irqchip: Add Layerscape SCFG MSI controller support
  5. upstream b8f3ebe630a4f1b4ff9340103d3b565ad5d78d43 commit
  6. [context adjustment]
  7. Some kind of Freescale Layerscape SoC provides a MSI
  8. implementation which uses two SCFG registers MSIIR and
  9. MSIR to support 32 MSI interrupts for each PCIe controller.
  10. The patch is to support it.
  11. Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
  12. Tested-by: Alexander Stein <alexander.stein@systec-electronic.com>
  13. Acked-by: Marc Zyngier <marc.zyngier@arm.com>
  14. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
  15. ---
  16. drivers/irqchip/Kconfig | 5 +
  17. drivers/irqchip/Makefile | 1 +
  18. drivers/irqchip/irq-ls-scfg-msi.c | 240 +++++++++++++++++++++++++++++++++++++
  19. 3 files changed, 246 insertions(+)
  20. create mode 100644 drivers/irqchip/irq-ls-scfg-msi.c
  21. --- a/drivers/irqchip/Kconfig
  22. +++ b/drivers/irqchip/Kconfig
  23. @@ -193,3 +193,8 @@ config IRQ_MXS
  24. def_bool y if MACH_ASM9260 || ARCH_MXS
  25. select IRQ_DOMAIN
  26. select STMP_DEVICE
  27. +
  28. +config LS_SCFG_MSI
  29. + def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
  30. + depends on PCI && PCI_MSI
  31. + select PCI_MSI_IRQ_DOMAIN
  32. --- a/drivers/irqchip/Makefile
  33. +++ b/drivers/irqchip/Makefile
  34. @@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-r
  35. obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
  36. obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
  37. obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
  38. +obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
  39. --- /dev/null
  40. +++ b/drivers/irqchip/irq-ls-scfg-msi.c
  41. @@ -0,0 +1,240 @@
  42. +/*
  43. + * Freescale SCFG MSI(-X) support
  44. + *
  45. + * Copyright (C) 2016 Freescale Semiconductor.
  46. + *
  47. + * Author: Minghuan Lian <Minghuan.Lian@nxp.com>
  48. + *
  49. + * This program is free software; you can redistribute it and/or modify
  50. + * it under the terms of the GNU General Public License version 2 as
  51. + * published by the Free Software Foundation.
  52. + */
  53. +
  54. +#include <linux/kernel.h>
  55. +#include <linux/module.h>
  56. +#include <linux/msi.h>
  57. +#include <linux/interrupt.h>
  58. +#include <linux/irq.h>
  59. +#include <linux/irqchip/chained_irq.h>
  60. +#include <linux/irqdomain.h>
  61. +#include <linux/of_pci.h>
  62. +#include <linux/of_platform.h>
  63. +#include <linux/spinlock.h>
  64. +
  65. +#define MSI_MAX_IRQS 32
  66. +#define MSI_IBS_SHIFT 3
  67. +#define MSIR 4
  68. +
  69. +struct ls_scfg_msi {
  70. + spinlock_t lock;
  71. + struct platform_device *pdev;
  72. + struct irq_domain *parent;
  73. + struct irq_domain *msi_domain;
  74. + void __iomem *regs;
  75. + phys_addr_t msiir_addr;
  76. + int irq;
  77. + DECLARE_BITMAP(used, MSI_MAX_IRQS);
  78. +};
  79. +
  80. +static struct irq_chip ls_scfg_msi_irq_chip = {
  81. + .name = "MSI",
  82. + .irq_mask = pci_msi_mask_irq,
  83. + .irq_unmask = pci_msi_unmask_irq,
  84. +};
  85. +
  86. +static struct msi_domain_info ls_scfg_msi_domain_info = {
  87. + .flags = (MSI_FLAG_USE_DEF_DOM_OPS |
  88. + MSI_FLAG_USE_DEF_CHIP_OPS |
  89. + MSI_FLAG_PCI_MSIX),
  90. + .chip = &ls_scfg_msi_irq_chip,
  91. +};
  92. +
  93. +static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
  94. +{
  95. + struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
  96. +
  97. + msg->address_hi = upper_32_bits(msi_data->msiir_addr);
  98. + msg->address_lo = lower_32_bits(msi_data->msiir_addr);
  99. + msg->data = data->hwirq << MSI_IBS_SHIFT;
  100. +}
  101. +
  102. +static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
  103. + const struct cpumask *mask, bool force)
  104. +{
  105. + return -EINVAL;
  106. +}
  107. +
  108. +static struct irq_chip ls_scfg_msi_parent_chip = {
  109. + .name = "SCFG",
  110. + .irq_compose_msi_msg = ls_scfg_msi_compose_msg,
  111. + .irq_set_affinity = ls_scfg_msi_set_affinity,
  112. +};
  113. +
  114. +static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
  115. + unsigned int virq,
  116. + unsigned int nr_irqs,
  117. + void *args)
  118. +{
  119. + struct ls_scfg_msi *msi_data = domain->host_data;
  120. + int pos, err = 0;
  121. +
  122. + WARN_ON(nr_irqs != 1);
  123. +
  124. + spin_lock(&msi_data->lock);
  125. + pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
  126. + if (pos < MSI_MAX_IRQS)
  127. + __set_bit(pos, msi_data->used);
  128. + else
  129. + err = -ENOSPC;
  130. + spin_unlock(&msi_data->lock);
  131. +
  132. + if (err)
  133. + return err;
  134. +
  135. + irq_domain_set_info(domain, virq, pos,
  136. + &ls_scfg_msi_parent_chip, msi_data,
  137. + handle_simple_irq, NULL, NULL);
  138. +
  139. + return 0;
  140. +}
  141. +
  142. +static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
  143. + unsigned int virq, unsigned int nr_irqs)
  144. +{
  145. + struct irq_data *d = irq_domain_get_irq_data(domain, virq);
  146. + struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
  147. + int pos;
  148. +
  149. + pos = d->hwirq;
  150. + if (pos < 0 || pos >= MSI_MAX_IRQS) {
  151. + pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
  152. + return;
  153. + }
  154. +
  155. + spin_lock(&msi_data->lock);
  156. + __clear_bit(pos, msi_data->used);
  157. + spin_unlock(&msi_data->lock);
  158. +}
  159. +
  160. +static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
  161. + .alloc = ls_scfg_msi_domain_irq_alloc,
  162. + .free = ls_scfg_msi_domain_irq_free,
  163. +};
  164. +
  165. +static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
  166. +{
  167. + struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
  168. + unsigned long val;
  169. + int pos, virq;
  170. +
  171. + chained_irq_enter(irq_desc_get_chip(desc), desc);
  172. +
  173. + val = ioread32be(msi_data->regs + MSIR);
  174. + for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
  175. + virq = irq_find_mapping(msi_data->parent, (31 - pos));
  176. + if (virq)
  177. + generic_handle_irq(virq);
  178. + }
  179. +
  180. + chained_irq_exit(irq_desc_get_chip(desc), desc);
  181. +}
  182. +
  183. +static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
  184. +{
  185. + /* Initialize MSI domain parent */
  186. + msi_data->parent = irq_domain_add_linear(NULL,
  187. + MSI_MAX_IRQS,
  188. + &ls_scfg_msi_domain_ops,
  189. + msi_data);
  190. + if (!msi_data->parent) {
  191. + dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n");
  192. + return -ENOMEM;
  193. + }
  194. +
  195. + msi_data->msi_domain = pci_msi_create_irq_domain(
  196. + of_node_to_fwnode(msi_data->pdev->dev.of_node),
  197. + &ls_scfg_msi_domain_info,
  198. + msi_data->parent);
  199. + if (!msi_data->msi_domain) {
  200. + dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n");
  201. + irq_domain_remove(msi_data->parent);
  202. + return -ENOMEM;
  203. + }
  204. +
  205. + return 0;
  206. +}
  207. +
  208. +static int ls_scfg_msi_probe(struct platform_device *pdev)
  209. +{
  210. + struct ls_scfg_msi *msi_data;
  211. + struct resource *res;
  212. + int ret;
  213. +
  214. + msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
  215. + if (!msi_data)
  216. + return -ENOMEM;
  217. +
  218. + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  219. + msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
  220. + if (IS_ERR(msi_data->regs)) {
  221. + dev_err(&pdev->dev, "failed to initialize 'regs'\n");
  222. + return PTR_ERR(msi_data->regs);
  223. + }
  224. + msi_data->msiir_addr = res->start;
  225. +
  226. + msi_data->irq = platform_get_irq(pdev, 0);
  227. + if (msi_data->irq <= 0) {
  228. + dev_err(&pdev->dev, "failed to get MSI irq\n");
  229. + return -ENODEV;
  230. + }
  231. +
  232. + msi_data->pdev = pdev;
  233. + spin_lock_init(&msi_data->lock);
  234. +
  235. + ret = ls_scfg_msi_domains_init(msi_data);
  236. + if (ret)
  237. + return ret;
  238. +
  239. + irq_set_chained_handler_and_data(msi_data->irq,
  240. + ls_scfg_msi_irq_handler,
  241. + msi_data);
  242. +
  243. + platform_set_drvdata(pdev, msi_data);
  244. +
  245. + return 0;
  246. +}
  247. +
  248. +static int ls_scfg_msi_remove(struct platform_device *pdev)
  249. +{
  250. + struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
  251. +
  252. + irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
  253. +
  254. + irq_domain_remove(msi_data->msi_domain);
  255. + irq_domain_remove(msi_data->parent);
  256. +
  257. + platform_set_drvdata(pdev, NULL);
  258. +
  259. + return 0;
  260. +}
  261. +
  262. +static const struct of_device_id ls_scfg_msi_id[] = {
  263. + { .compatible = "fsl,1s1021a-msi", },
  264. + { .compatible = "fsl,1s1043a-msi", },
  265. + {},
  266. +};
  267. +
  268. +static struct platform_driver ls_scfg_msi_driver = {
  269. + .driver = {
  270. + .name = "ls-scfg-msi",
  271. + .of_match_table = ls_scfg_msi_id,
  272. + },
  273. + .probe = ls_scfg_msi_probe,
  274. + .remove = ls_scfg_msi_remove,
  275. +};
  276. +
  277. +module_platform_driver(ls_scfg_msi_driver);
  278. +
  279. +MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
  280. +MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver");
  281. +MODULE_LICENSE("GPL v2");