imx6: add support for gw5400-a
[openwrt/svn-archive/archive.git] / target / linux / imx6 / files-3.10 / arch / arm / mach-imx / msi.c
1 /*
2 * arch/arm/mach-mx6/msi.c
3 *
4 * PCI MSI support for the imx processor
5 *
6 * Copyright (c) 2013, Boundary Devices.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place - Suite 330, Boston, MA 02111-1307 USA.
20 *
21 */
22 #include <linux/module.h>
23 #include <linux/pci.h>
24 #include <linux/msi.h>
25 #include <asm/bitops.h>
26 #include <asm/mach/irq.h>
27 #include <asm/irq.h>
28 #include <linux/irqchip/chained_irq.h>
29
30 #include "hardware.h"
31 #include "msi.h"
32
33 #define IMX_NUM_MSI_IRQS 128
34 static DECLARE_BITMAP(msi_irq_in_use, IMX_NUM_MSI_IRQS);
35 static int irq_base;
36
37 static void imx_msi_handler(unsigned int irq, struct irq_desc *desc)
38 {
39 int i, j;
40 unsigned status;
41 struct irq_chip *chip = irq_get_chip(irq);
42
43 irq_base = irq_alloc_descs(-1, 0, IMX_NUM_MSI_IRQS, 0);
44 if (irq_base < 0) {
45 printk(KERN_ERR "%s: could not allocate IRQ numbers\n", __func__);
46 return;
47 }
48
49 chained_irq_enter(chip, desc);
50 for (i = 0; i < 8; i++) {
51 status = imx_pcie_msi_pending(i);
52 while (status) {
53 j = __fls(status);
54 generic_handle_irq(irq_base + j);
55 status &= ~(1 << j);
56 }
57 irq_base += 32;
58 }
59 chained_irq_exit(chip, desc);
60 }
61
62 /*
63 * Dynamic irq allocate and deallocation
64 */
65 int create_irq(void)
66 {
67 int irq, pos;
68
69 do {
70 pos = find_first_zero_bit(msi_irq_in_use, IMX_NUM_MSI_IRQS);
71 if ((unsigned)pos >= IMX_NUM_MSI_IRQS)
72 return -ENOSPC;
73 /* test_and_set_bit operates on 32-bits at a time */
74 } while (test_and_set_bit(pos, msi_irq_in_use));
75
76 irq = irq_base + pos;
77 dynamic_irq_init(irq);
78 return irq;
79 }
80
81 void destroy_irq(unsigned int irq)
82 {
83 int pos = irq - irq_base;
84
85 dynamic_irq_cleanup(irq);
86 clear_bit(pos, msi_irq_in_use);
87 }
88
89 void arch_teardown_msi_irq(unsigned int irq)
90 {
91 destroy_irq(irq);
92 }
93
94 static void imx_msi_irq_ack(struct irq_data *d)
95 {
96 return;
97 }
98
99 static void imx_msi_irq_enable(struct irq_data *d)
100 {
101 imx_pcie_enable_irq(d->irq - irq_base, 1);
102 return unmask_msi_irq(d);
103 }
104
105 static void imx_msi_irq_disable(struct irq_data *d)
106 {
107 imx_pcie_enable_irq(d->irq - irq_base, 0);
108 return mask_msi_irq(d);
109 }
110
111 static void imx_msi_irq_mask(struct irq_data *d)
112 {
113 imx_pcie_mask_irq(d->irq - irq_base, 1);
114 return mask_msi_irq(d);
115 }
116
117 static void imx_msi_irq_unmask(struct irq_data *d)
118 {
119 imx_pcie_mask_irq(d->irq - irq_base, 0);
120 return unmask_msi_irq(d);
121 }
122
123 static struct irq_chip imx_msi_chip = {
124 .name = "PCIe-MSI",
125 .irq_ack = imx_msi_irq_ack,
126 .irq_enable = imx_msi_irq_enable,
127 .irq_disable = imx_msi_irq_disable,
128 .irq_mask = imx_msi_irq_mask,
129 .irq_unmask = imx_msi_irq_unmask,
130 };
131
132 int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
133 {
134 int irq = create_irq();
135 struct msi_msg msg;
136
137 if (irq < 0)
138 return irq;
139
140 irq_set_msi_desc(irq, desc);
141
142 msg.address_hi = 0x0;
143 msg.address_lo = MSI_MATCH_ADDR;
144 msg.data = (mxc_cpu_type << 15) | ((irq - irq_base) & 0xff);
145
146 write_msi_msg(irq, &msg);
147 irq_set_chip_and_handler(irq, &imx_msi_chip, handle_simple_irq);
148 set_irq_flags(irq, IRQF_VALID);
149 pr_info("%s: %d of %d\n", __func__, irq, NR_IRQS);
150 return 0;
151 }
152
153 void imx_msi_init(void)
154 {
155 irq_set_chained_handler(MXC_INT_PCIE_0, imx_msi_handler);
156 }