[POWERPC] Rewrite the PPC 86xx IRQ handling to use Flat Device Tree
[linux-2.6.git] / arch / powerpc / sysdev / fsl_soc.c
1 /*
2  * FSL SoC setup code
3  *
4  * Maintained by Kumar Gala (see MAINTAINERS for contact information)
5  *
6  * This program is free software; you can redistribute  it and/or modify it
7  * under  the terms of  the GNU General  Public License as published by the
8  * Free Software Foundation;  either version 2 of the  License, or (at your
9  * option) any later version.
10  */
11
12 #include <linux/stddef.h>
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/errno.h>
16 #include <linux/major.h>
17 #include <linux/delay.h>
18 #include <linux/irq.h>
19 #include <linux/module.h>
20 #include <linux/device.h>
21 #include <linux/platform_device.h>
22 #include <linux/fsl_devices.h>
23
24 #include <asm/system.h>
25 #include <asm/atomic.h>
26 #include <asm/io.h>
27 #include <asm/irq.h>
28 #include <asm/prom.h>
29 #include <sysdev/fsl_soc.h>
30 #include <mm/mmu_decl.h>
31
32 static phys_addr_t immrbase = -1;
33
34 phys_addr_t get_immrbase(void)
35 {
36         struct device_node *soc;
37
38         if (immrbase != -1)
39                 return immrbase;
40
41         soc = of_find_node_by_type(NULL, "soc");
42         if (soc) {
43                 unsigned int size;
44                 const void *prop = get_property(soc, "reg", &size);
45                 immrbase = of_translate_address(soc, prop);
46                 of_node_put(soc);
47         };
48
49         return immrbase;
50 }
51
52 EXPORT_SYMBOL(get_immrbase);
53
54 static int __init gfar_mdio_of_init(void)
55 {
56         struct device_node *np;
57         unsigned int i;
58         struct platform_device *mdio_dev;
59         struct resource res;
60         int ret;
61
62         for (np = NULL, i = 0;
63              (np = of_find_compatible_node(np, "mdio", "gianfar")) != NULL;
64              i++) {
65                 int k;
66                 struct device_node *child = NULL;
67                 struct gianfar_mdio_data mdio_data;
68
69                 memset(&res, 0, sizeof(res));
70                 memset(&mdio_data, 0, sizeof(mdio_data));
71
72                 ret = of_address_to_resource(np, 0, &res);
73                 if (ret)
74                         goto err;
75
76                 mdio_dev =
77                     platform_device_register_simple("fsl-gianfar_mdio",
78                                                     res.start, &res, 1);
79                 if (IS_ERR(mdio_dev)) {
80                         ret = PTR_ERR(mdio_dev);
81                         goto err;
82                 }
83
84                 for (k = 0; k < 32; k++)
85                         mdio_data.irq[k] = -1;
86
87                 while ((child = of_get_next_child(np, child)) != NULL) {
88                         const u32 *id = get_property(child, "reg", NULL);
89                         mdio_data.irq[*id] = irq_of_parse_and_map(child, 0);
90                 }
91
92                 ret =
93                     platform_device_add_data(mdio_dev, &mdio_data,
94                                              sizeof(struct gianfar_mdio_data));
95                 if (ret)
96                         goto unreg;
97         }
98
99         return 0;
100
101 unreg:
102         platform_device_unregister(mdio_dev);
103 err:
104         return ret;
105 }
106
107 arch_initcall(gfar_mdio_of_init);
108
109 static const char *gfar_tx_intr = "tx";
110 static const char *gfar_rx_intr = "rx";
111 static const char *gfar_err_intr = "error";
112
113 static int __init gfar_of_init(void)
114 {
115         struct device_node *np;
116         unsigned int i;
117         struct platform_device *gfar_dev;
118         struct resource res;
119         int ret;
120
121         for (np = NULL, i = 0;
122              (np = of_find_compatible_node(np, "network", "gianfar")) != NULL;
123              i++) {
124                 struct resource r[4];
125                 struct device_node *phy, *mdio;
126                 struct gianfar_platform_data gfar_data;
127                 const unsigned int *id;
128                 const char *model;
129                 const void *mac_addr;
130                 const phandle *ph;
131                 int n_res = 1;
132
133                 memset(r, 0, sizeof(r));
134                 memset(&gfar_data, 0, sizeof(gfar_data));
135
136                 ret = of_address_to_resource(np, 0, &r[0]);
137                 if (ret)
138                         goto err;
139
140                 r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
141                 r[1].flags = IORESOURCE_IRQ;
142
143                 model = get_property(np, "model", NULL);
144
145                 /* If we aren't the FEC we have multiple interrupts */
146                 if (model && strcasecmp(model, "FEC")) {
147                         r[1].name = gfar_tx_intr;
148
149                         r[2].name = gfar_rx_intr;
150                         r[2].start = r[2].end = irq_of_parse_and_map(np, 1);
151                         r[2].flags = IORESOURCE_IRQ;
152
153                         r[3].name = gfar_err_intr;
154                         r[3].start = r[3].end = irq_of_parse_and_map(np, 2);
155                         r[3].flags = IORESOURCE_IRQ;
156
157                         n_res += 2;
158                 }
159
160                 gfar_dev =
161                     platform_device_register_simple("fsl-gianfar", i, &r[0],
162                                                     n_res + 1);
163
164                 if (IS_ERR(gfar_dev)) {
165                         ret = PTR_ERR(gfar_dev);
166                         goto err;
167                 }
168
169                 mac_addr = get_property(np, "address", NULL);
170                 memcpy(gfar_data.mac_addr, mac_addr, 6);
171
172                 if (model && !strcasecmp(model, "TSEC"))
173                         gfar_data.device_flags =
174                             FSL_GIANFAR_DEV_HAS_GIGABIT |
175                             FSL_GIANFAR_DEV_HAS_COALESCE |
176                             FSL_GIANFAR_DEV_HAS_RMON |
177                             FSL_GIANFAR_DEV_HAS_MULTI_INTR;
178                 if (model && !strcasecmp(model, "eTSEC"))
179                         gfar_data.device_flags =
180                             FSL_GIANFAR_DEV_HAS_GIGABIT |
181                             FSL_GIANFAR_DEV_HAS_COALESCE |
182                             FSL_GIANFAR_DEV_HAS_RMON |
183                             FSL_GIANFAR_DEV_HAS_MULTI_INTR |
184                             FSL_GIANFAR_DEV_HAS_CSUM |
185                             FSL_GIANFAR_DEV_HAS_VLAN |
186                             FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
187
188                 ph = get_property(np, "phy-handle", NULL);
189                 phy = of_find_node_by_phandle(*ph);
190
191                 if (phy == NULL) {
192                         ret = -ENODEV;
193                         goto unreg;
194                 }
195
196                 mdio = of_get_parent(phy);
197
198                 id = get_property(phy, "reg", NULL);
199                 ret = of_address_to_resource(mdio, 0, &res);
200                 if (ret) {
201                         of_node_put(phy);
202                         of_node_put(mdio);
203                         goto unreg;
204                 }
205
206                 gfar_data.phy_id = *id;
207                 gfar_data.bus_id = res.start;
208
209                 of_node_put(phy);
210                 of_node_put(mdio);
211
212                 ret =
213                     platform_device_add_data(gfar_dev, &gfar_data,
214                                              sizeof(struct
215                                                     gianfar_platform_data));
216                 if (ret)
217                         goto unreg;
218         }
219
220         return 0;
221
222 unreg:
223         platform_device_unregister(gfar_dev);
224 err:
225         return ret;
226 }
227
228 arch_initcall(gfar_of_init);
229
230 static int __init fsl_i2c_of_init(void)
231 {
232         struct device_node *np;
233         unsigned int i;
234         struct platform_device *i2c_dev;
235         int ret;
236
237         for (np = NULL, i = 0;
238              (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL;
239              i++) {
240                 struct resource r[2];
241                 struct fsl_i2c_platform_data i2c_data;
242                 const unsigned char *flags = NULL;
243
244                 memset(&r, 0, sizeof(r));
245                 memset(&i2c_data, 0, sizeof(i2c_data));
246
247                 ret = of_address_to_resource(np, 0, &r[0]);
248                 if (ret)
249                         goto err;
250
251                 r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
252                 r[1].flags = IORESOURCE_IRQ;
253
254                 i2c_dev = platform_device_register_simple("fsl-i2c", i, r, 2);
255                 if (IS_ERR(i2c_dev)) {
256                         ret = PTR_ERR(i2c_dev);
257                         goto err;
258                 }
259
260                 i2c_data.device_flags = 0;
261                 flags = get_property(np, "dfsrr", NULL);
262                 if (flags)
263                         i2c_data.device_flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
264
265                 flags = get_property(np, "fsl5200-clocking", NULL);
266                 if (flags)
267                         i2c_data.device_flags |= FSL_I2C_DEV_CLOCK_5200;
268
269                 ret =
270                     platform_device_add_data(i2c_dev, &i2c_data,
271                                              sizeof(struct
272                                                     fsl_i2c_platform_data));
273                 if (ret)
274                         goto unreg;
275         }
276
277         return 0;
278
279 unreg:
280         platform_device_unregister(i2c_dev);
281 err:
282         return ret;
283 }
284
285 arch_initcall(fsl_i2c_of_init);
286
287 #ifdef CONFIG_PPC_83xx
288 static int __init mpc83xx_wdt_init(void)
289 {
290         struct resource r;
291         struct device_node *soc, *np;
292         struct platform_device *dev;
293         const unsigned int *freq;
294         int ret;
295
296         np = of_find_compatible_node(NULL, "watchdog", "mpc83xx_wdt");
297
298         if (!np) {
299                 ret = -ENODEV;
300                 goto nodev;
301         }
302
303         soc = of_find_node_by_type(NULL, "soc");
304
305         if (!soc) {
306                 ret = -ENODEV;
307                 goto nosoc;
308         }
309
310         freq = get_property(soc, "bus-frequency", NULL);
311         if (!freq) {
312                 ret = -ENODEV;
313                 goto err;
314         }
315
316         memset(&r, 0, sizeof(r));
317
318         ret = of_address_to_resource(np, 0, &r);
319         if (ret)
320                 goto err;
321
322         dev = platform_device_register_simple("mpc83xx_wdt", 0, &r, 1);
323         if (IS_ERR(dev)) {
324                 ret = PTR_ERR(dev);
325                 goto err;
326         }
327
328         ret = platform_device_add_data(dev, freq, sizeof(int));
329         if (ret)
330                 goto unreg;
331
332         of_node_put(soc);
333         of_node_put(np);
334
335         return 0;
336
337 unreg:
338         platform_device_unregister(dev);
339 err:
340         of_node_put(soc);
341 nosoc:
342         of_node_put(np);
343 nodev:
344         return ret;
345 }
346
347 arch_initcall(mpc83xx_wdt_init);
348 #endif
349
350 static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
351 {
352         if (!phy_type)
353                 return FSL_USB2_PHY_NONE;
354         if (!strcasecmp(phy_type, "ulpi"))
355                 return FSL_USB2_PHY_ULPI;
356         if (!strcasecmp(phy_type, "utmi"))
357                 return FSL_USB2_PHY_UTMI;
358         if (!strcasecmp(phy_type, "utmi_wide"))
359                 return FSL_USB2_PHY_UTMI_WIDE;
360         if (!strcasecmp(phy_type, "serial"))
361                 return FSL_USB2_PHY_SERIAL;
362
363         return FSL_USB2_PHY_NONE;
364 }
365
366 static int __init fsl_usb_of_init(void)
367 {
368         struct device_node *np;
369         unsigned int i;
370         struct platform_device *usb_dev_mph = NULL, *usb_dev_dr = NULL;
371         int ret;
372
373         for (np = NULL, i = 0;
374              (np = of_find_compatible_node(np, "usb", "fsl-usb2-mph")) != NULL;
375              i++) {
376                 struct resource r[2];
377                 struct fsl_usb2_platform_data usb_data;
378                 const unsigned char *prop = NULL;
379
380                 memset(&r, 0, sizeof(r));
381                 memset(&usb_data, 0, sizeof(usb_data));
382
383                 ret = of_address_to_resource(np, 0, &r[0]);
384                 if (ret)
385                         goto err;
386
387                 r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
388                 r[1].flags = IORESOURCE_IRQ;
389
390                 usb_dev_mph =
391                     platform_device_register_simple("fsl-ehci", i, r, 2);
392                 if (IS_ERR(usb_dev_mph)) {
393                         ret = PTR_ERR(usb_dev_mph);
394                         goto err;
395                 }
396
397                 usb_dev_mph->dev.coherent_dma_mask = 0xffffffffUL;
398                 usb_dev_mph->dev.dma_mask = &usb_dev_mph->dev.coherent_dma_mask;
399
400                 usb_data.operating_mode = FSL_USB2_MPH_HOST;
401
402                 prop = get_property(np, "port0", NULL);
403                 if (prop)
404                         usb_data.port_enables |= FSL_USB2_PORT0_ENABLED;
405
406                 prop = get_property(np, "port1", NULL);
407                 if (prop)
408                         usb_data.port_enables |= FSL_USB2_PORT1_ENABLED;
409
410                 prop = get_property(np, "phy_type", NULL);
411                 usb_data.phy_mode = determine_usb_phy(prop);
412
413                 ret =
414                     platform_device_add_data(usb_dev_mph, &usb_data,
415                                              sizeof(struct
416                                                     fsl_usb2_platform_data));
417                 if (ret)
418                         goto unreg_mph;
419         }
420
421         for (np = NULL;
422              (np = of_find_compatible_node(np, "usb", "fsl-usb2-dr")) != NULL;
423              i++) {
424                 struct resource r[2];
425                 struct fsl_usb2_platform_data usb_data;
426                 const unsigned char *prop = NULL;
427
428                 memset(&r, 0, sizeof(r));
429                 memset(&usb_data, 0, sizeof(usb_data));
430
431                 ret = of_address_to_resource(np, 0, &r[0]);
432                 if (ret)
433                         goto unreg_mph;
434
435                 r[1].start = r[1].end = irq_of_parse_and_map(np, 0);
436                 r[1].flags = IORESOURCE_IRQ;
437
438                 usb_dev_dr =
439                     platform_device_register_simple("fsl-ehci", i, r, 2);
440                 if (IS_ERR(usb_dev_dr)) {
441                         ret = PTR_ERR(usb_dev_dr);
442                         goto err;
443                 }
444
445                 usb_dev_dr->dev.coherent_dma_mask = 0xffffffffUL;
446                 usb_dev_dr->dev.dma_mask = &usb_dev_dr->dev.coherent_dma_mask;
447
448                 usb_data.operating_mode = FSL_USB2_DR_HOST;
449
450                 prop = get_property(np, "phy_type", NULL);
451                 usb_data.phy_mode = determine_usb_phy(prop);
452
453                 ret =
454                     platform_device_add_data(usb_dev_dr, &usb_data,
455                                              sizeof(struct
456                                                     fsl_usb2_platform_data));
457                 if (ret)
458                         goto unreg_dr;
459         }
460         return 0;
461
462 unreg_dr:
463         if (usb_dev_dr)
464                 platform_device_unregister(usb_dev_dr);
465 unreg_mph:
466         if (usb_dev_mph)
467                 platform_device_unregister(usb_dev_mph);
468 err:
469         return ret;
470 }
471
472 arch_initcall(fsl_usb_of_init);