aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/host/pci-exynos.c44
-rw-r--r--drivers/pci/host/pcie-designware.c240
-rw-r--r--drivers/pci/host/pcie-designware.h14
3 files changed, 298 insertions, 0 deletions
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 94e096bb2d0a..f062acaf052a 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -48,6 +48,7 @@ struct exynos_pcie {
48#define PCIE_IRQ_SPECIAL 0x008 48#define PCIE_IRQ_SPECIAL 0x008
49#define PCIE_IRQ_EN_PULSE 0x00c 49#define PCIE_IRQ_EN_PULSE 0x00c
50#define PCIE_IRQ_EN_LEVEL 0x010 50#define PCIE_IRQ_EN_LEVEL 0x010
51#define IRQ_MSI_ENABLE (0x1 << 2)
51#define PCIE_IRQ_EN_SPECIAL 0x014 52#define PCIE_IRQ_EN_SPECIAL 0x014
52#define PCIE_PWR_RESET 0x018 53#define PCIE_PWR_RESET 0x018
53#define PCIE_CORE_RESET 0x01c 54#define PCIE_CORE_RESET 0x01c
@@ -342,9 +343,36 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
342 return IRQ_HANDLED; 343 return IRQ_HANDLED;
343} 344}
344 345
346static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
347{
348 struct pcie_port *pp = arg;
349
350 dw_handle_msi_irq(pp);
351
352 return IRQ_HANDLED;
353}
354
355static void exynos_pcie_msi_init(struct pcie_port *pp)
356{
357 u32 val;
358 struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
359
360 dw_pcie_msi_init(pp);
361
362 /* enable MSI interrupt */
363 val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL);
364 val |= IRQ_MSI_ENABLE;
365 exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL);
366 return;
367}
368
345static void exynos_pcie_enable_interrupts(struct pcie_port *pp) 369static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
346{ 370{
347 exynos_pcie_enable_irq_pulse(pp); 371 exynos_pcie_enable_irq_pulse(pp);
372
373 if (IS_ENABLED(CONFIG_PCI_MSI))
374 exynos_pcie_msi_init(pp);
375
348 return; 376 return;
349} 377}
350 378
@@ -430,6 +458,22 @@ static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev)
430 return ret; 458 return ret;
431 } 459 }
432 460
461 if (IS_ENABLED(CONFIG_PCI_MSI)) {
462 pp->msi_irq = platform_get_irq(pdev, 0);
463 if (!pp->msi_irq) {
464 dev_err(&pdev->dev, "failed to get msi irq\n");
465 return -ENODEV;
466 }
467
468 ret = devm_request_irq(&pdev->dev, pp->msi_irq,
469 exynos_pcie_msi_irq_handler,
470 IRQF_SHARED, "exynos-pcie", pp);
471 if (ret) {
472 dev_err(&pdev->dev, "failed to request msi irq\n");
473 return ret;
474 }
475 }
476
433 pp->root_bus_nr = -1; 477 pp->root_bus_nr = -1;
434 pp->ops = &exynos_pcie_host_ops; 478 pp->ops = &exynos_pcie_host_ops;
435 479
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index c10e9ac9bbbc..896301788e9d 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -11,8 +11,11 @@
11 * published by the Free Software Foundation. 11 * published by the Free Software Foundation.
12 */ 12 */
13 13
14#include <linux/irq.h>
15#include <linux/irqdomain.h>
14#include <linux/kernel.h> 16#include <linux/kernel.h>
15#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/msi.h>
16#include <linux/of_address.h> 19#include <linux/of_address.h>
17#include <linux/pci.h> 20#include <linux/pci.h>
18#include <linux/pci_regs.h> 21#include <linux/pci_regs.h>
@@ -142,6 +145,204 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
142 return ret; 145 return ret;
143} 146}
144 147
148static struct irq_chip dw_msi_irq_chip = {
149 .name = "PCI-MSI",
150 .irq_enable = unmask_msi_irq,
151 .irq_disable = mask_msi_irq,
152 .irq_mask = mask_msi_irq,
153 .irq_unmask = unmask_msi_irq,
154};
155
156/* MSI int handler */
157void dw_handle_msi_irq(struct pcie_port *pp)
158{
159 unsigned long val;
160 int i, pos;
161
162 for (i = 0; i < MAX_MSI_CTRLS; i++) {
163 dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
164 (u32 *)&val);
165 if (val) {
166 pos = 0;
167 while ((pos = find_next_bit(&val, 32, pos)) != 32) {
168 generic_handle_irq(pp->msi_irq_start
169 + (i * 32) + pos);
170 pos++;
171 }
172 }
173 dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val);
174 }
175}
176
177void dw_pcie_msi_init(struct pcie_port *pp)
178{
179 pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
180
181 /* program the msi_data */
182 dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
183 virt_to_phys((void *)pp->msi_data));
184 dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
185}
186
187static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
188{
189 int flag = 1;
190
191 do {
192 pos = find_next_zero_bit(pp->msi_irq_in_use,
193 MAX_MSI_IRQS, pos);
194 /*if you have reached to the end then get out from here.*/
195 if (pos == MAX_MSI_IRQS)
196 return -ENOSPC;
197 /*
198 * Check if this position is at correct offset.nvec is always a
199 * power of two. pos0 must be nvec bit alligned.
200 */
201 if (pos % msgvec)
202 pos += msgvec - (pos % msgvec);
203 else
204 flag = 0;
205 } while (flag);
206
207 *pos0 = pos;
208 return 0;
209}
210
211static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
212{
213 int res, bit, irq, pos0, pos1, i;
214 u32 val;
215 struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata);
216
217 if (!pp) {
218 BUG();
219 return -EINVAL;
220 }
221
222 pos0 = find_first_zero_bit(pp->msi_irq_in_use,
223 MAX_MSI_IRQS);
224 if (pos0 % no_irqs) {
225 if (find_valid_pos0(pp, no_irqs, pos0, &pos0))
226 goto no_valid_irq;
227 }
228 if (no_irqs > 1) {
229 pos1 = find_next_bit(pp->msi_irq_in_use,
230 MAX_MSI_IRQS, pos0);
231 /* there must be nvec number of consecutive free bits */
232 while ((pos1 - pos0) < no_irqs) {
233 if (find_valid_pos0(pp, no_irqs, pos1, &pos0))
234 goto no_valid_irq;
235 pos1 = find_next_bit(pp->msi_irq_in_use,
236 MAX_MSI_IRQS, pos0);
237 }
238 }
239
240 irq = (pp->msi_irq_start + pos0);
241
242 if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1))
243 g