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