Merge branch 'fix/hda' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[pandora-kernel.git] / arch / mips / ar7 / platform.c
1 /*
2  * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include <linux/init.h>
21 #include <linux/types.h>
22 #include <linux/module.h>
23 #include <linux/delay.h>
24 #include <linux/dma-mapping.h>
25 #include <linux/platform_device.h>
26 #include <linux/mtd/physmap.h>
27 #include <linux/serial.h>
28 #include <linux/serial_8250.h>
29 #include <linux/ioport.h>
30 #include <linux/io.h>
31 #include <linux/vlynq.h>
32 #include <linux/leds.h>
33 #include <linux/string.h>
34 #include <linux/etherdevice.h>
35
36 #include <asm/addrspace.h>
37 #include <asm/mach-ar7/ar7.h>
38 #include <asm/mach-ar7/gpio.h>
39 #include <asm/mach-ar7/prom.h>
40
41 struct plat_vlynq_data {
42         struct plat_vlynq_ops ops;
43         int gpio_bit;
44         int reset_bit;
45 };
46
47
48 static int vlynq_on(struct vlynq_device *dev)
49 {
50         int result;
51         struct plat_vlynq_data *pdata = dev->dev.platform_data;
52
53         result = gpio_request(pdata->gpio_bit, "vlynq");
54         if (result)
55                 goto out;
56
57         ar7_device_reset(pdata->reset_bit);
58
59         result = ar7_gpio_disable(pdata->gpio_bit);
60         if (result)
61                 goto out_enabled;
62
63         result = ar7_gpio_enable(pdata->gpio_bit);
64         if (result)
65                 goto out_enabled;
66
67         result = gpio_direction_output(pdata->gpio_bit, 0);
68         if (result)
69                 goto out_gpio_enabled;
70
71         msleep(50);
72
73         gpio_set_value(pdata->gpio_bit, 1);
74         msleep(50);
75
76         return 0;
77
78 out_gpio_enabled:
79         ar7_gpio_disable(pdata->gpio_bit);
80 out_enabled:
81         ar7_device_disable(pdata->reset_bit);
82         gpio_free(pdata->gpio_bit);
83 out:
84         return result;
85 }
86
87 static void vlynq_off(struct vlynq_device *dev)
88 {
89         struct plat_vlynq_data *pdata = dev->dev.platform_data;
90         ar7_gpio_disable(pdata->gpio_bit);
91         gpio_free(pdata->gpio_bit);
92         ar7_device_disable(pdata->reset_bit);
93 }
94
95 static struct resource physmap_flash_resource = {
96         .name = "mem",
97         .flags = IORESOURCE_MEM,
98         .start = 0x10000000,
99         .end = 0x107fffff,
100 };
101
102 static struct resource cpmac_low_res[] = {
103         {
104                 .name = "regs",
105                 .flags = IORESOURCE_MEM,
106                 .start = AR7_REGS_MAC0,
107                 .end = AR7_REGS_MAC0 + 0x7ff,
108         },
109         {
110                 .name = "irq",
111                 .flags = IORESOURCE_IRQ,
112                 .start = 27,
113                 .end = 27,
114         },
115 };
116
117 static struct resource cpmac_high_res[] = {
118         {
119                 .name = "regs",
120                 .flags = IORESOURCE_MEM,
121                 .start = AR7_REGS_MAC1,
122                 .end = AR7_REGS_MAC1 + 0x7ff,
123         },
124         {
125                 .name = "irq",
126                 .flags = IORESOURCE_IRQ,
127                 .start = 41,
128                 .end = 41,
129         },
130 };
131
132 static struct resource vlynq_low_res[] = {
133         {
134                 .name = "regs",
135                 .flags = IORESOURCE_MEM,
136                 .start = AR7_REGS_VLYNQ0,
137                 .end = AR7_REGS_VLYNQ0 + 0xff,
138         },
139         {
140                 .name = "irq",
141                 .flags = IORESOURCE_IRQ,
142                 .start = 29,
143                 .end = 29,
144         },
145         {
146                 .name = "mem",
147                 .flags = IORESOURCE_MEM,
148                 .start = 0x04000000,
149                 .end = 0x04ffffff,
150         },
151         {
152                 .name = "devirq",
153                 .flags = IORESOURCE_IRQ,
154                 .start = 80,
155                 .end = 111,
156         },
157 };
158
159 static struct resource vlynq_high_res[] = {
160         {
161                 .name = "regs",
162                 .flags = IORESOURCE_MEM,
163                 .start = AR7_REGS_VLYNQ1,
164                 .end = AR7_REGS_VLYNQ1 + 0xff,
165         },
166         {
167                 .name = "irq",
168                 .flags = IORESOURCE_IRQ,
169                 .start = 33,
170                 .end = 33,
171         },
172         {
173                 .name = "mem",
174                 .flags = IORESOURCE_MEM,
175                 .start = 0x0c000000,
176                 .end = 0x0cffffff,
177         },
178         {
179                 .name = "devirq",
180                 .flags = IORESOURCE_IRQ,
181                 .start = 112,
182                 .end = 143,
183         },
184 };
185
186 static struct resource usb_res[] = {
187         {
188                 .name = "regs",
189                 .flags = IORESOURCE_MEM,
190                 .start = AR7_REGS_USB,
191                 .end = AR7_REGS_USB + 0xff,
192         },
193         {
194                 .name = "irq",
195                 .flags = IORESOURCE_IRQ,
196                 .start = 32,
197                 .end = 32,
198         },
199         {
200                 .name = "mem",
201                 .flags = IORESOURCE_MEM,
202                 .start = 0x03400000,
203                 .end = 0x034001fff,
204         },
205 };
206
207 static struct physmap_flash_data physmap_flash_data = {
208         .width = 2,
209 };
210
211 static struct plat_cpmac_data cpmac_low_data = {
212         .reset_bit = 17,
213         .power_bit = 20,
214         .phy_mask = 0x80000000,
215 };
216
217 static struct plat_cpmac_data cpmac_high_data = {
218         .reset_bit = 21,
219         .power_bit = 22,
220         .phy_mask = 0x7fffffff,
221 };
222
223 static struct plat_vlynq_data vlynq_low_data = {
224         .ops.on = vlynq_on,
225         .ops.off = vlynq_off,
226         .reset_bit = 20,
227         .gpio_bit = 18,
228 };
229
230 static struct plat_vlynq_data vlynq_high_data = {
231         .ops.on = vlynq_on,
232         .ops.off = vlynq_off,
233         .reset_bit = 16,
234         .gpio_bit = 19,
235 };
236
237 static struct platform_device physmap_flash = {
238         .id = 0,
239         .name = "physmap-flash",
240         .dev.platform_data = &physmap_flash_data,
241         .resource = &physmap_flash_resource,
242         .num_resources = 1,
243 };
244
245 static u64 cpmac_dma_mask = DMA_BIT_MASK(32);
246 static struct platform_device cpmac_low = {
247         .id = 0,
248         .name = "cpmac",
249         .dev = {
250                 .dma_mask = &cpmac_dma_mask,
251                 .coherent_dma_mask = DMA_BIT_MASK(32),
252                 .platform_data = &cpmac_low_data,
253         },
254         .resource = cpmac_low_res,
255         .num_resources = ARRAY_SIZE(cpmac_low_res),
256 };
257
258 static struct platform_device cpmac_high = {
259         .id = 1,
260         .name = "cpmac",
261         .dev = {
262                 .dma_mask = &cpmac_dma_mask,
263                 .coherent_dma_mask = DMA_BIT_MASK(32),
264                 .platform_data = &cpmac_high_data,
265         },
266         .resource = cpmac_high_res,
267         .num_resources = ARRAY_SIZE(cpmac_high_res),
268 };
269
270 static struct platform_device vlynq_low = {
271         .id = 0,
272         .name = "vlynq",
273         .dev.platform_data = &vlynq_low_data,
274         .resource = vlynq_low_res,
275         .num_resources = ARRAY_SIZE(vlynq_low_res),
276 };
277
278 static struct platform_device vlynq_high = {
279         .id = 1,
280         .name = "vlynq",
281         .dev.platform_data = &vlynq_high_data,
282         .resource = vlynq_high_res,
283         .num_resources = ARRAY_SIZE(vlynq_high_res),
284 };
285
286
287 static struct gpio_led default_leds[] = {
288         {
289                 .name = "status",
290                 .gpio = 8,
291                 .active_low = 1,
292         },
293 };
294
295 static struct gpio_led dsl502t_leds[] = {
296         {
297                 .name = "status",
298                 .gpio = 9,
299                 .active_low = 1,
300         },
301         {
302                 .name = "ethernet",
303                 .gpio = 7,
304                 .active_low = 1,
305         },
306         {
307                 .name = "usb",
308                 .gpio = 12,
309                 .active_low = 1,
310         },
311 };
312
313 static struct gpio_led dg834g_leds[] = {
314         {
315                 .name = "ppp",
316                 .gpio = 6,
317                 .active_low = 1,
318         },
319         {
320                 .name = "status",
321                 .gpio = 7,
322                 .active_low = 1,
323         },
324         {
325                 .name = "adsl",
326                 .gpio = 8,
327                 .active_low = 1,
328         },
329         {
330                 .name = "wifi",
331                 .gpio = 12,
332                 .active_low = 1,
333         },
334         {
335                 .name = "power",
336                 .gpio = 14,
337                 .active_low = 1,
338                 .default_trigger = "default-on",
339         },
340 };
341
342 static struct gpio_led fb_sl_leds[] = {
343         {
344                 .name = "1",
345                 .gpio = 7,
346         },
347         {
348                 .name = "2",
349                 .gpio = 13,
350                 .active_low = 1,
351         },
352         {
353                 .name = "3",
354                 .gpio = 10,
355                 .active_low = 1,
356         },
357         {
358                 .name = "4",
359                 .gpio = 12,
360                 .active_low = 1,
361         },
362         {
363                 .name = "5",
364                 .gpio = 9,
365                 .active_low = 1,
366         },
367 };
368
369 static struct gpio_led fb_fon_leds[] = {
370         {
371                 .name = "1",
372                 .gpio = 8,
373         },
374         {
375                 .name = "2",
376                 .gpio = 3,
377                 .active_low = 1,
378         },
379         {
380                 .name = "3",
381                 .gpio = 5,
382         },
383         {
384                 .name = "4",
385                 .gpio = 4,
386                 .active_low = 1,
387         },
388         {
389                 .name = "5",
390                 .gpio = 11,
391                 .active_low = 1,
392         },
393 };
394
395 static struct gpio_led_platform_data ar7_led_data;
396
397 static struct platform_device ar7_gpio_leds = {
398         .name = "leds-gpio",
399         .id = -1,
400         .dev = {
401                 .platform_data = &ar7_led_data,
402         }
403 };
404
405 static struct platform_device ar7_udc = {
406         .id = -1,
407         .name = "ar7_udc",
408         .resource = usb_res,
409         .num_resources = ARRAY_SIZE(usb_res),
410 };
411
412 static inline unsigned char char2hex(char h)
413 {
414         switch (h) {
415         case '0': case '1': case '2': case '3': case '4':
416         case '5': case '6': case '7': case '8': case '9':
417                 return h - '0';
418         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
419                 return h - 'A' + 10;
420         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
421                 return h - 'a' + 10;
422         default:
423                 return 0;
424         }
425 }
426
427 static void cpmac_get_mac(int instance, unsigned char *dev_addr)
428 {
429         int i;
430         char name[5], default_mac[ETH_ALEN], *mac;
431
432         mac = NULL;
433         sprintf(name, "mac%c", 'a' + instance);
434         mac = prom_getenv(name);
435         if (!mac) {
436                 sprintf(name, "mac%c", 'a');
437                 mac = prom_getenv(name);
438         }
439         if (!mac) {
440                 random_ether_addr(default_mac);
441                 mac = default_mac;
442         }
443         for (i = 0; i < 6; i++)
444                 dev_addr[i] = (char2hex(mac[i * 3]) << 4) +
445                         char2hex(mac[i * 3 + 1]);
446 }
447
448 static void __init detect_leds(void)
449 {
450         char *prid, *usb_prod;
451
452         /* Default LEDs */
453         ar7_led_data.num_leds = ARRAY_SIZE(default_leds);
454         ar7_led_data.leds = default_leds;
455
456         /* FIXME: the whole thing is unreliable */
457         prid = prom_getenv("ProductID");
458         usb_prod = prom_getenv("usb_prod");
459
460         /* If we can't get the product id from PROM, use the default LEDs */
461         if (!prid)
462                 return;
463
464         if (strstr(prid, "Fritz_Box_FON")) {
465                 ar7_led_data.num_leds = ARRAY_SIZE(fb_fon_leds);
466                 ar7_led_data.leds = fb_fon_leds;
467         } else if (strstr(prid, "Fritz_Box_")) {
468                 ar7_led_data.num_leds = ARRAY_SIZE(fb_sl_leds);
469                 ar7_led_data.leds = fb_sl_leds;
470         } else if ((!strcmp(prid, "AR7RD") || !strcmp(prid, "AR7DB"))
471                 && usb_prod != NULL && strstr(usb_prod, "DSL-502T")) {
472                 ar7_led_data.num_leds = ARRAY_SIZE(dsl502t_leds);
473                 ar7_led_data.leds = dsl502t_leds;
474         } else if (strstr(prid, "DG834")) {
475                 ar7_led_data.num_leds = ARRAY_SIZE(dg834g_leds);
476                 ar7_led_data.leds = dg834g_leds;
477         }
478 }
479
480 static int __init ar7_register_devices(void)
481 {
482         int res;
483 #ifdef CONFIG_SERIAL_8250
484         static struct uart_port uart_port[2];
485
486         memset(uart_port, 0, sizeof(struct uart_port) * 2);
487
488         uart_port[0].type = PORT_16550A;
489         uart_port[0].line = 0;
490         uart_port[0].irq = AR7_IRQ_UART0;
491         uart_port[0].uartclk = ar7_bus_freq() / 2;
492         uart_port[0].iotype = UPIO_MEM32;
493         uart_port[0].mapbase = AR7_REGS_UART0;
494         uart_port[0].membase = ioremap(uart_port[0].mapbase, 256);
495         uart_port[0].regshift = 2;
496         res = early_serial_setup(&uart_port[0]);
497         if (res)
498                 return res;
499
500
501         /* Only TNETD73xx have a second serial port */
502         if (ar7_has_second_uart()) {
503                 uart_port[1].type = PORT_16550A;
504                 uart_port[1].line = 1;
505                 uart_port[1].irq = AR7_IRQ_UART1;
506                 uart_port[1].uartclk = ar7_bus_freq() / 2;
507                 uart_port[1].iotype = UPIO_MEM32;
508                 uart_port[1].mapbase = UR8_REGS_UART1;
509                 uart_port[1].membase = ioremap(uart_port[1].mapbase, 256);
510                 uart_port[1].regshift = 2;
511                 res = early_serial_setup(&uart_port[1]);
512                 if (res)
513                         return res;
514         }
515 #endif /* CONFIG_SERIAL_8250 */
516         res = platform_device_register(&physmap_flash);
517         if (res)
518                 return res;
519
520         ar7_device_disable(vlynq_low_data.reset_bit);
521         res = platform_device_register(&vlynq_low);
522         if (res)
523                 return res;
524
525         if (ar7_has_high_vlynq()) {
526                 ar7_device_disable(vlynq_high_data.reset_bit);
527                 res = platform_device_register(&vlynq_high);
528                 if (res)
529                         return res;
530         }
531
532         if (ar7_has_high_cpmac()) {
533                 cpmac_get_mac(1, cpmac_high_data.dev_addr);
534                 res = platform_device_register(&cpmac_high);
535                 if (res)
536                         return res;
537         } else {
538                 cpmac_low_data.phy_mask = 0xffffffff;
539         }
540
541         cpmac_get_mac(0, cpmac_low_data.dev_addr);
542         res = platform_device_register(&cpmac_low);
543         if (res)
544                 return res;
545
546         detect_leds();
547         res = platform_device_register(&ar7_gpio_leds);
548         if (res)
549                 return res;
550
551         res = platform_device_register(&ar7_udc);
552
553         return res;
554 }
555 arch_initcall(ar7_register_devices);