aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Makefile2
-rw-r--r--arch/powerpc/sysdev/mv64x60.h11
-rw-r--r--arch/powerpc/sysdev/mv64x60_dev.c422
-rw-r--r--arch/powerpc/sysdev/mv64x60_pci.c172
-rw-r--r--arch/powerpc/sysdev/mv64x60_pic.c305
5 files changed, 912 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 9ce775c38ab7..c3ce0bd12c0b 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o
16obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o 16obj-$(CONFIG_FSL_PCIE) += fsl_pcie.o
17obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o 17obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
18obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ 18obj-$(CONFIG_QUICC_ENGINE) += qe_lib/
19mv64x60-$(CONFIG_PCI) += mv64x60_pci.o
20obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o
19 21
20# contains only the suspend handler for time 22# contains only the suspend handler for time
21obj-$(CONFIG_PM) += timer.o 23obj-$(CONFIG_PM) += timer.o
diff --git a/arch/powerpc/sysdev/mv64x60.h b/arch/powerpc/sysdev/mv64x60.h
new file mode 100644
index 000000000000..2ff0b4ef2681
--- /dev/null
+++ b/arch/powerpc/sysdev/mv64x60.h
@@ -0,0 +1,11 @@
1#ifndef __MV64X60_H__
2#define __MV64X60_H__
3
4#include <linux/init.h>
5
6extern void __init mv64x60_init_irq(void);
7extern unsigned int mv64x60_get_irq(void);
8
9extern void __init mv64x60_pci_init(void);
10
11#endif /* __MV64X60_H__ */
diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c
new file mode 100644
index 000000000000..4b0a9c88eeb3
--- /dev/null
+++ b/arch/powerpc/sysdev/mv64x60_dev.c
@@ -0,0 +1,422 @@
1/*
2 * Platform device setup for Marvell mv64360/mv64460 host bridges (Discovery)
3 *
4 * Author: Dale Farnsworth <dale@farnsworth.org>
5 *
6 * 2007 (c) MontaVista, Software, Inc. This file is licensed under
7 * the terms of the GNU General Public License version 2. This program
8 * is licensed "as is" without any warranty of any kind, whether express
9 * or implied.
10 */
11
12#include <linux/stddef.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/mv643xx.h>
16#include <linux/platform_device.h>
17
18#include <asm/prom.h>
19
20/*
21 * These functions provide the necessary setup for the mv64x60 drivers.
22 * These drivers are unusual in that they work on both the MIPS and PowerPC
23 * architectures. Because of that, the drivers do not support the normal
24 * PowerPC of_platform_bus_type. They support platform_bus_type instead.
25 */
26
27/*
28 * Create MPSC platform devices
29 */
30static int __init mv64x60_mpsc_register_shared_pdev(struct device_node *np)
31{
32 struct platform_device *pdev;
33 struct resource r[2];
34 struct mpsc_shared_pdata pdata;
35 const phandle *ph;
36 struct device_node *mpscrouting, *mpscintr;
37 int err;
38
39 ph = of_get_property(np, "mpscrouting", NULL);
40 mpscrouting = of_find_node_by_phandle(*ph);
41 if (!mpscrouting)
42 return -ENODEV;
43
44 err = of_address_to_resource(mpscrouting, 0, &r[0]);
45 of_node_put(mpscrouting);
46 if (err)
47 return err;
48
49 ph = of_get_property(np, "mpscintr", NULL);
50 mpscintr = of_find_node_by_phandle(*ph);
51 if (!mpscintr)
52 return -ENODEV;
53
54 err = of_address_to_resource(mpscintr, 0, &r[1]);
55 of_node_put(mpscintr);
56 if (err)
57 return err;
58
59 memset(&pdata, 0, sizeof(pdata));
60
61 pdev = platform_device_alloc(MPSC_SHARED_NAME, 0);
62 if (!pdev)
63 return -ENOMEM;
64
65 err = platform_device_add_resources(pdev, r, 2);
66 if (err)
67 goto error;
68
69 err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
70 if (err)
71 goto error;
72
73 err = platform_device_add(pdev);
74 if (err)
75 goto error;
76
77 return 0;
78
79error:
80 platform_device_put(pdev);
81 return err;
82}
83
84
85static int __init mv64x60_mpsc_device_setup(struct device_node *np, int id)
86{
87 struct resource r[5];
88 struct mpsc_pdata pdata;
89 struct platform_device *pdev;
90 const unsigned int *prop;
91 const phandle *ph;
92 struct device_node *sdma, *brg;
93 int err;
94 int port_number;
95
96 /* only register the shared platform device the first time through */
97 if (id == 0 && (err = mv64x60_mpsc_register_shared_pdev(np)))
98 return err;
99
100 memset(r, 0, sizeof(r));
101
102 err = of_address_to_resource(np, 0, &r[0]);
103 if (err)
104 return err;
105
106 of_irq_to_resource(np, 0, &r[4]);
107
108 ph = of_get_property(np, "sdma", NULL);
109 sdma = of_find_node_by_phandle(*ph);
110 if (!sdma)
111 return -ENODEV;
112
113 of_irq_to_resource(sdma, 0, &r[3]);
114 err = of_address_to_resource(sdma, 0, &r[1]);
115 of_node_put(sdma);
116 if (err)
117 return err;
118
119 ph = of_get_property(np, "brg", NULL);
120 brg = of_find_node_by_phandle(*ph);
121 if (!brg)
122 return -ENODEV;
123
124 err = of_address_to_resource(brg, 0, &r[2]);
125 of_node_put(brg);
126 if (err)
127 return err;
128
129 prop = of_get_property(np, "block-index", NULL);
130 if (!prop)
131 return -ENODEV;
132 port_number = *(int *)prop;
133
134 memset(&pdata, 0, sizeof(pdata));
135
136 pdata.cache_mgmt = 1; /* All current revs need this set */
137
138 prop = of_get_property(np, "max_idle", NULL);
139 if (prop)
140 pdata.max_idle = *prop;
141
142 prop = of_get_property(brg, "current-speed", NULL);
143 if (prop)
144 pdata.default_baud = *prop;
145
146 /* Default is 8 bits, no parity, no flow control */
147 pdata.default_bits = 8;
148 pdata.default_parity = 'n';
149 pdata.default_flow = 'n';
150
151 prop = of_get_property(np, "chr_1", NULL);
152 if (prop)
153 pdata.chr_1_val = *prop;
154
155 prop = of_get_property(np, "chr_2", NULL);
156 if (prop)
157 pdata.chr_2_val = *prop;
158
159 prop = of_get_property(np, "chr_10", NULL);
160 if (prop)
161 pdata.chr_10_val = *prop;
162
163 prop = of_get_property(np, "mpcr", NULL);
164 if (prop)
165 pdata.mpcr_val = *prop;
166
167 prop = of_get_property(brg, "bcr", NULL);
168 if (prop)
169 pdata.bcr_val = *prop;
170
171 pdata.brg_can_tune = 1; /* All current revs need this set */
172
173 prop = of_get_property(brg, "clock-src", NULL);
174 if (prop)
175 pdata.brg_clk_src = *prop;
176
177 prop = of_get_property(brg, "clock-frequency", NULL);
178 if (prop)
179 pdata.brg_clk_freq = *prop;
180
181 pdev = platform_device_alloc(MPSC_CTLR_NAME, port_number);
182 if (!pdev)
183 return -ENOMEM;
184
185 err = platform_device_add_resources(pdev, r, 5);
186 if (err)
187 goto error;
188
189 err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
190 if (err)
191 goto error;
192
193 err = platform_device_add(pdev);
194 if (err)
195 goto error;
196
197 return 0;
198
199error:
200 platform_device_put(pdev);
201 return err;
202}
203
204/*
205 * Create mv64x60_eth platform devices
206 */
207static int __init eth_register_shared_pdev(struct device_node *np)
208{
209 struct platform_device *pdev;
210 struct resource r[1];
211 int err;
212
213 np = of_get_parent(np);
214 if (!np)
215 return -ENODEV;
216
217 err = of_address_to_resource(np, 0, &r[0]);
218 of_node_put(np);
219 if (err)
220 return err;
221
222 pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, 0,
223 r, 1);
224 if (IS_ERR(pdev))
225 return PTR_ERR(pdev);
226
227 return 0;
228}
229
230static int __init mv64x60_eth_device_setup(struct device_node *np, int id)
231{
232 struct resource r[1];
233 struct mv643xx_eth_platform_data pdata;
234 struct platform_device *pdev;
235 struct device_node *phy;
236 const u8 *mac_addr;
237 const int *prop;
238 const phandle *ph;
239 int err;
240
241 /* only register the shared platform device the first time through */
242 if (id == 0 && (err = eth_register_shared_pdev(np)))
243 return err;;
244
245 memset(r, 0, sizeof(r));
246 of_irq_to_resource(np, 0, &r[0]);
247
248 memset(&pdata, 0, sizeof(pdata));
249
250 prop = of_get_property(np, "block-index", NULL);
251 if (!prop)
252 return -ENODEV;
253 pdata.port_number = *prop;
254
255 mac_addr = of_get_mac_address(np);
256 if (mac_addr)
257 memcpy(pdata.mac_addr, mac_addr, 6);
258
259 prop = of_get_property(np, "speed", NULL);
260 if (prop)
261 pdata.speed = *prop;
262
263 prop = of_get_property(np, "tx_queue_size", NULL);
264 if (prop)
265 pdata.tx_queue_size = *prop;
266
267 prop = of_get_property(np, "rx_queue_size", NULL);
268 if (prop)
269 pdata.rx_queue_size = *prop;
270
271 prop = of_get_property(np, "tx_sram_addr", NULL);
272 if (prop)
273 pdata.tx_sram_addr = *prop;
274
275 prop = of_get_property(np, "tx_sram_size", NULL);
276 if (prop)
277 pdata.tx_sram_size = *prop;
278
279 prop = of_get_property(np, "rx_sram_addr", NULL);
280 if (prop)
281 pdata.rx_sram_addr = *prop;
282
283 prop = of_get_property(np, "rx_sram_size", NULL);
284 if (prop)
285 pdata.rx_sram_size = *prop;
286
287 ph = of_get_property(np, "phy", NULL);
288 if (!ph)
289 return -ENODEV;
290
291 phy = of_find_node_by_phandle(*ph);
292 if (phy == NULL)
293 return -ENODEV;
294
295 prop = of_get_property(phy, "reg", NULL);
296 if (prop) {
297 pdata.force_phy_addr = 1;
298 pdata.phy_addr = *prop;
299 }
300
301 of_node_put(phy);
302
303 pdev = platform_device_alloc(MV643XX_ETH_NAME, pdata.port_number);
304 if (!pdev)
305 return -ENOMEM;
306
307 err = platform_device_add_resources(pdev, r, 1);
308 if (err)
309 goto error;
310
311 err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
312 if (err)
313 goto error;
314
315 err = platform_device_add(pdev);
316 if (err)
317 goto error;
318
319 return 0;
320
321error:
322 platform_device_put(pdev);
323 return err;
324}
325
326/*
327 * Create mv64x60_i2c platform devices
328 */
329static int __init mv64x60_i2c_device_setup(struct device_node *np, int id)
330{
331 struct resource r[2];
332 struct platform_device *pdev;
333 struct mv64xxx_i2c_pdata pdata;
334 const unsigned int *prop;
335 int err;
336
337 memset(r, 0, sizeof(r));
338
339 err = of_address_to_resource(np, 0, &r[0]);
340 if (err)
341 return err;
342
343 of_irq_to_resource(np, 0, &r[1]);
344
345 memset(&pdata, 0, sizeof(pdata));
346
347 prop = of_get_property(np, "freq_m", NULL);
348 if (!prop)
349 return -ENODEV;
350 pdata.freq_m = *prop;
351
352 prop = of_get_property(np, "freq_n", NULL);
353 if (!prop)
354 return -ENODEV;
355 pdata.freq_n = *prop;
356
357 prop = of_get_property(np, "timeout", NULL);
358 if (prop)
359 pdata.timeout = *prop;
360 else
361 pdata.timeout = 1000; /* 1 second */
362
363 prop = of_get_property(np, "retries", NULL);
364 if (prop)
365 pdata.retries = *prop;
366 else
367 pdata.retries = 1;
368
369 pdev = platform_device_alloc(MV64XXX_I2C_CTLR_NAME, id);
370 if (!pdev)
371 return -ENOMEM;
372
373 err = platform_device_add_resources(pdev, r, 2);
374 if (err)
375 goto error;
376
377 err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
378 if (err)
379 goto error;
380
381 err = platform_device_add(pdev);
382 if (err)
383 goto error;
384
385 return 0;
386
387error:
388 platform_device_put(pdev);
389 return err;
390}
391
392static int __init mv64x60_device_setup(void)
393{
394 struct device_node *np = NULL;
395 int id;
396 int err;
397
398 for (id = 0;
399 (np = of_find_compatible_node(np, "serial", "marvell,mpsc")); id++)
400 if ((err = mv64x60_mpsc_device_setup(np, id)))
401 goto error;
402
403 for (id = 0;
404 (np = of_find_compatible_node(np, "network",
405 "marvell,mv64x60-eth"));
406 id++)
407 if ((err = mv64x60_eth_device_setup(np, id)))
408 goto error;
409
410 for (id = 0;
411 (np = of_find_compatible_node(np, "i2c", "marvell,mv64x60-i2c"));
412 id++)
413 if ((err = mv64x60_i2c_device_setup(np, id)))
414 goto error;
415
416 return 0;
417
418error:
419 of_node_put(np);
420 return err;
421}
422arch_initcall(mv64x60_device_setup);
diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c
new file mode 100644
index 000000000000..b5aef4cbc8d2
--- /dev/null
+++ b/arch/powerpc/sysdev/mv64x60_pci.c
@@ -0,0 +1,172 @@
1/*
2 * PCI bus setup for Marvell mv64360/mv64460 host bridges (Discovery)
3 *
4 * Author: Dale Farnsworth <dale@farnsworth.org>
5 *
6 * 2007 (c) MontaVista, Software, Inc. This file is licensed under
7 * the terms of the GNU General Public License version 2. This program
8 * is licensed "as is" without any warranty of any kind, whether express
9 * or implied.
10 */
11
12#include <linux/stddef.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/pci.h>
16
17#include <asm/prom.h>
18#include <asm/pci-bridge.h>
19
20#define PCI_HEADER_TYPE_INVALID 0x7f /* Invalid PCI header type */
21
22#ifdef CONFIG_SYSFS
23/* 32-bit hex or dec stringified number + '\n' */
24#define MV64X60_VAL_LEN_MAX 11
25#define MV64X60_PCICFG_CPCI_HOTSWAP 0x68
26
27static ssize_t mv64x60_hs_reg_read(struct kobject *kobj, char *buf, loff_t off,
28 size_t count)
29{
30 struct pci_dev *phb;
31 u32 v;
32
33 if (off > 0)
34 return 0;
35 if (count < MV64X60_VAL_LEN_MAX)
36 return -EINVAL;
37
38 phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
39 if (!phb)
40 return -ENODEV;
41 pci_read_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, &v);
42 pci_dev_put(phb);
43
44 return sprintf(buf, "0x%08x\n", v);
45}
46
47static ssize_t mv64x60_hs_reg_write(struct kobject *kobj, char *buf, loff_t off,
48 size_t count)
49{
50 struct pci_dev *phb;
51 u32 v;
52
53 if (off > 0)
54 return 0;
55 if (count <= 0)
56 return -EINVAL;
57
58 if (sscanf(buf, "%i", &v) != 1)
59 return -EINVAL;
60
61 phb = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
62 if (!phb)
63 return -ENODEV;
64 pci_write_config_dword(phb, MV64X60_PCICFG_CPCI_HOTSWAP, v);
65 pci_dev_put(phb);
66
67 return count;
68}
69
70static struct bin_attribute mv64x60_hs_reg_attr = { /* Hotswap register */
71 .attr = {
72 .name = "hs_reg",
73 .mode = S_IRUGO | S_IWUSR,
74 .owner = THIS_MODULE,
75 },
76 .size = MV64X60_VAL_LEN_MAX,
77 .read = mv64x60_hs_reg_read,
78 .write = mv64x60_hs_reg_write,
79};
80
81static int __init mv64x60_sysfs_init(void)
82{
83 struct device_node *np;
84 struct platform_device *pdev;
85 const unsigned int *prop;
86
87 np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60");
88 if (!np)
89 return 0;
90
91 prop = of_get_property(np, "hs_reg_valid", NULL);
92 of_node_put(np);
93
94 pdev = platform_device_register_simple("marvell,mv64x60", 0, NULL, 0);
95 if (IS_ERR(pdev))
96 return PTR_ERR(pdev);
97
98 return sysfs_create_bin_file(&pdev->dev.kobj, &mv64x60_hs_reg_attr);
99}
100
101subsys_initcall(mv64x60_sysfs_init);
102
103#endif /* CONFIG_SYSFS */
104
105static void __init mv64x60_pci_fixup_early(struct pci_dev *dev)
106{
107 /*
108 * Set the host bridge hdr_type to an invalid value so that
109 * pci_setup_device() will ignore the host bridge.
110 */
111 dev->hdr_type = PCI_HEADER_TYPE_INVALID;
112}
113DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360,
114 mv64x60_pci_fixup_early);
115DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64460,
116 mv64x60_pci_fixup_early);
117
118static int __init mv64x60_add_bridge(struct device_node *dev)
119{
120 int len;
121 struct pci_controller *hose;
122 struct resource rsrc;
123 const int *bus_range;
124 int primary;
125
126 memset(&rsrc, 0, sizeof(rsrc));
127
128 /* Fetch host bridge registers address */
129 if (of_address_to_resource(dev, 0, &rsrc)) {
130 printk(KERN_ERR "No PCI reg property in device tree\n");
131 return -ENODEV;
132 }
133
134 /* Get bus range if any */
135 bus_range = of_get_property(dev, "bus-range", &len);
136 if (bus_range == NULL || len < 2 * sizeof(int))
137 printk(KERN_WARNING "Can't get bus-range for %s, assume"
138 " bus 0\n", dev->full_name);
139
140 hose = pcibios_alloc_controller();
141 if (!hose)
142 return -ENOMEM;
143
144 hose->arch_data = dev;
145 hose->set_cfg_type = 1;
146
147 hose->first_busno = bus_range ? bus_range[0] : 0;
148 hose->last_busno = bus_range ? bus_range[1] : 0xff;
149
150 setup_indirect_pci(hose, rsrc.start, rsrc.start + 4);
151 hose->bus_offset = hose->first_busno;
152
153 printk(KERN_INFO "Found MV64x60 PCI host bridge at 0x%016llx. "
154 "Firmware bus number: %d->%d\n",
155 (unsigned long long)rsrc.start, hose->first_busno,
156 hose->last_busno);
157
158 /* Interpret the "ranges" property */
159 /* This also maps the I/O region and sets isa_io/mem_base */
160 primary = (hose->first_busno == 0);
161 pci_process_bridge_OF_ranges(hose, dev, primary);
162
163 return 0;
164}
165
166void __init mv64x60_pci_init(void)
167{
168 struct device_node *np = NULL;
169
170 while ((np = of_find_compatible_node(np, "pci", "marvell,mv64x60-pci")))
171 mv64x60_add_bridge(np);
172}
diff --git a/arch/powerpc/sysdev/mv64x60_pic.c b/arch/powerpc/sysdev/mv64x60_pic.c
new file mode 100644
index 000000000000..01d316287772
--- /dev/null
+++ b/arch/powerpc/sysdev/mv64x60_pic.c
@@ -0,0 +1,305 @@
1/*
2 * Interrupt handling for Marvell mv64360/mv64460 host bridges (Discovery)
3 *
4 * Author: Dale Farnsworth <dale@farnsworth.org>
5 *
6 * 2007 (c) MontaVista, Software, Inc. This file is licensed under
7 * the terms of the GNU General Public License version 2. This program
8 * is licensed "as is" without any warranty of any kind, whether express
9 * or implied.
10 */
11
12#include <linux/stddef.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/irq.h>
16#include <linux/interrupt.h>
17#include <linux/spinlock.h>
18
19#include <asm/byteorder.h>
20#include <asm/io.h>
21#include <asm/prom.h>
22#include <asm/irq.h>
23
24#include "mv64x60.h"
25
26/* Interrupt Controller Interface Registers */
27#define MV64X60_IC_MAIN_CAUSE_LO 0x0004
28#define MV64X60_IC_MAIN_CAUSE_HI 0x000c
29#define MV64X60_IC_CPU0_INTR_MASK_LO 0x0014
30#define MV64X60_IC_CPU0_INTR_MASK_HI 0x001c
31#define MV64X60_IC_CPU0_SELECT_CAUSE 0x0024
32
33#define MV64X60_HIGH_GPP_GROUPS 0x0f000000
34#define MV64X60_SELECT_CAUSE_HIGH 0x40000000
35
36/* General Purpose Pins Controller Interface Registers */
37#define MV64x60_GPP_INTR_CAUSE 0x0008
38#define MV64x60_GPP_INTR_MASK 0x000c
39
40#define MV64x60_LEVEL1_LOW 0
41#define MV64x60_LEVEL1_HIGH 1
42#define MV64x60_LEVEL1_GPP 2
43
44#define MV64x60_LEVEL1_MASK 0x00000060
45#define MV64x60_LEVEL1_OFFSET 5
46
47#define MV64x60_LEVEL2_MASK 0x0000001f
48
49#define MV64x60_NUM_IRQS 96
50
51static DEFINE_SPINLOCK(mv64x60_lock);
52
53static void __iomem *mv64x60_irq_reg_base;
54static void __iomem *mv64x60_gpp_reg_base;
55
56/*
57 * Interrupt Controller Handling
58 *
59 * The interrupt controller handles three groups of interrupts:
60 * main low: IRQ0-IRQ31
61 * main high: IRQ32-IRQ63
62 * gpp: IRQ64-IRQ95
63 *
64 * This code handles interrupts in two levels. Level 1 selects the
65 * interrupt group, and level 2 selects an IRQ within that group.
66 * Each group has its own irq_chip structure.
67 */
68
69static u32 mv64x60_cached_low_mask;
70static u32 mv64x60_cached_high_mask = MV64X60_HIGH_GPP_GROUPS;
71static u32 mv64x60_cached_gpp_mask;
72
73static struct irq_host *mv64x60_irq_host;
74
75/*
76 * mv64x60_chip_low functions
77 */
78
79static void mv64x60_mask_low(unsigned int virq)
80{
81 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
82 unsigned long flags;
83
84 spin_lock_irqsave(&mv64x60_lock, flags);
85 mv64x60_cached_low_mask &= ~(1 << level2);
86 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO,
87 mv64x60_cached_low_mask);
88 spin_unlock_irqrestore(&mv64x60_lock, flags);
89 (void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO);
90}
91
92static void mv64x60_unmask_low(unsigned int virq)
93{
94 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
95 unsigned long flags;
96
97 spin_lock_irqsave(&mv64x60_lock, flags);
98 mv64x60_cached_low_mask |= 1 << level2;
99 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO,
100 mv64x60_cached_low_mask);
101 spin_unlock_irqrestore(&mv64x60_lock, flags);
102 (void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO);
103}
104
105static struct irq_chip mv64x60_chip_low = {
106 .name = "mv64x60_low",
107 .mask = mv64x60_mask_low,
108 .mask_ack = mv64x60_mask_low,
109 .unmask = mv64x60_unmask_low,
110};
111
112/*
113 * mv64x60_chip_high functions
114 */
115
116static void mv64x60_mask_high(unsigned int virq)
117{
118 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
119 unsigned long flags;
120
121 spin_lock_irqsave(&mv64x60_lock, flags);
122 mv64x60_cached_high_mask &= ~(1 << level2);
123 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI,
124 mv64x60_cached_high_mask);
125 spin_unlock_irqrestore(&mv64x60_lock, flags);
126 (void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI);
127}
128
129static void mv64x60_unmask_high(unsigned int virq)
130{
131 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
132 unsigned long flags;
133
134 spin_lock_irqsave(&mv64x60_lock, flags);
135 mv64x60_cached_high_mask |= 1 << level2;
136 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI,
137 mv64x60_cached_high_mask);
138 spin_unlock_irqrestore(&mv64x60_lock, flags);
139 (void)in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI);
140}
141
142static struct irq_chip mv64x60_chip_high = {
143 .name = "mv64x60_high",
144 .mask = mv64x60_mask_high,
145 .mask_ack = mv64x60_mask_high,
146 .unmask = mv64x60_unmask_high,
147};
148
149/*
150 * mv64x60_chip_gpp functions
151 */
152
153static void mv64x60_mask_gpp(unsigned int virq)
154{
155 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
156 unsigned long flags;
157
158 spin_lock_irqsave(&mv64x60_lock, flags);
159 mv64x60_cached_gpp_mask &= ~(1 << level2);
160 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
161 mv64x60_cached_gpp_mask);
162 spin_unlock_irqrestore(&mv64x60_lock, flags);
163 (void)in_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK);
164}
165
166static void mv64x60_mask_ack_gpp(unsigned int virq)
167{
168 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
169 unsigned long flags;
170
171 spin_lock_irqsave(&mv64x60_lock, flags);
172 mv64x60_cached_gpp_mask &= ~(1 << level2);
173 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
174 mv64x60_cached_gpp_mask);
175 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_CAUSE,
176 ~(1 << level2));
177 spin_unlock_irqrestore(&mv64x60_lock, flags);
178 (void)in_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_CAUSE);
179}
180
181static void mv64x60_unmask_gpp(unsigned int virq)
182{
183 int level2 = irq_map[virq].hwirq & MV64x60_LEVEL2_MASK;
184 unsigned long flags;
185
186 spin_lock_irqsave(&mv64x60_lock, flags);
187 mv64x60_cached_gpp_mask |= 1 << level2;
188 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
189 mv64x60_cached_gpp_mask);
190 spin_unlock_irqrestore(&mv64x60_lock, flags);
191 (void)in_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK);
192}
193
194static struct irq_chip mv64x60_chip_gpp = {
195 .name = "mv64x60_gpp",
196 .mask = mv64x60_mask_gpp,
197 .mask_ack = mv64x60_mask_ack_gpp,
198 .unmask = mv64x60_unmask_gpp,
199};
200
201/*
202 * mv64x60_host_ops functions
203 */
204
205static int mv64x60_host_match(struct irq_host *h, struct device_node *np)
206{
207 return mv64x60_irq_host->host_data == np;
208}
209
210static struct irq_chip *mv64x60_chips[] = {
211 [MV64x60_LEVEL1_LOW] = &mv64x60_chip_low,
212 [MV64x60_LEVEL1_HIGH] = &mv64x60_chip_high,
213 [MV64x60_LEVEL1_GPP] = &mv64x60_chip_gpp,
214};
215
216static int mv64x60_host_map(struct irq_host *h, unsigned int virq,
217 irq_hw_number_t hwirq)
218{
219 int level1;
220
221 get_irq_desc(virq)->status |= IRQ_LEVEL;
222
223 level1 = (hwirq & MV64x60_LEVEL1_MASK) >> MV64x60_LEVEL1_OFFSET;
224 BUG_ON(level1 > MV64x60_LEVEL1_GPP);
225 set_irq_chip_and_handler(virq, mv64x60_chips[level1], handle_level_irq);
226
227 return 0;
228}
229
230static struct irq_host_ops mv64x60_host_ops = {
231 .match = mv64x60_host_match,
232 .map = mv64x60_host_map,
233};
234
235/*
236 * Global functions
237 */
238
239void __init mv64x60_init_irq(void)
240{
241 struct device_node *np;
242 phys_addr_t paddr;
243 unsigned int size;
244 const unsigned int *reg;
245 unsigned long flags;
246
247 np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60-gpp");
248 reg = of_get_property(np, "reg", &size);
249 paddr = of_translate_address(np, reg);
250 mv64x60_gpp_reg_base = ioremap(paddr, reg[1]);
251 of_node_put(np);
252
253 np = of_find_compatible_node(NULL, NULL, "marvell,mv64x60-pic");
254 reg = of_get_property(np, "reg", &size);
255 paddr = of_translate_address(np, reg);
256 of_node_put(np);
257 mv64x60_irq_reg_base = ioremap(paddr, reg[1]);
258
259 mv64x60_irq_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, MV64x60_NUM_IRQS,
260 &mv64x60_host_ops, MV64x60_NUM_IRQS);
261
262 mv64x60_irq_host->host_data = np;
263
264 spin_lock_irqsave(&mv64x60_lock, flags);
265 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_MASK,
266 mv64x60_cached_gpp_mask);
267 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_LO,
268 mv64x60_cached_low_mask);
269 out_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_INTR_MASK_HI,
270 mv64x60_cached_high_mask);
271
272 out_le32(mv64x60_gpp_reg_base + MV64x60_GPP_INTR_CAUSE, 0);
273 out_le32(mv64x60_irq_reg_base + MV64X60_IC_MAIN_CAUSE_LO, 0);
274 out_le32(mv64x60_irq_reg_base + MV64X60_IC_MAIN_CAUSE_HI, 0);
275 spin_unlock_irqrestore(&mv64x60_lock, flags);
276}
277
278unsigned int mv64x60_get_irq(void)
279{
280 u32 cause;
281 int level1;
282 irq_hw_number_t hwirq;
283 int virq = NO_IRQ;
284
285 cause = in_le32(mv64x60_irq_reg_base + MV64X60_IC_CPU0_SELECT_CAUSE);
286 if (cause & MV64X60_SELECT_CAUSE_HIGH) {
287 cause &= mv64x60_cached_high_mask;
288 level1 = MV64x60_LEVEL1_HIGH;
289 if (cause & MV64X60_HIGH_GPP_GROUPS) {
290 cause = in_le32(mv64x60_gpp_reg_base +
291 MV64x60_GPP_INTR_CAUSE);
292 cause &= mv64x60_cached_gpp_mask;
293 level1 = MV64x60_LEVEL1_GPP;
294 }
295 } else {
296 cause &= mv64x60_cached_low_mask;
297 level1 = MV64x60_LEVEL1_LOW;
298 }
299 if (cause) {
300 hwirq = (level1 << MV64x60_LEVEL1_OFFSET) | __ilog2(cause);
301 virq = irq_linear_revmap(mv64x60_irq_host, hwirq);
302 }
303
304 return virq;
305}