reverse power led, fixes #891
[openwrt/svn-archive/openwrt.git] / openwrt / target / linux / package / diag / src / diag.c
1 /*
2 * diag.c - GPIO interface driver for Broadcom boards
3 *
4 * Copyright (C) 2006 Mike Baker <mbm@openwrt.org>,
5 * Felix Fietkau <nbd@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id:$
22 */
23 #include <linux/module.h>
24 #include <linux/pci.h>
25 #include <linux/kmod.h>
26 #include <linux/proc_fs.h>
27 #include <linux/moduleparam.h>
28 #include <linux/tqueue.h>
29 #include <linux/timer.h>
30 #include <asm/uaccess.h>
31
32 #include <osl.h>
33 #include <bcmdevs.h>
34 #include <sbutils.h>
35 #include <sbconfig.h>
36 #include <sbchipc.h>
37
38 #define MODULE_NAME "diag"
39
40 #define MAX_GPIO 8
41 #define FLASH_TIME HZ/6
42
43 static unsigned int gpiomask = 0;
44 module_param(gpiomask, int, 0644);
45
46 enum polarity_t {
47 REVERSE = 0,
48 NORMAL = 1,
49 };
50
51 enum {
52 PROC_BUTTON,
53 PROC_LED,
54 PROC_MODEL,
55 PROC_GPIOMASK
56 };
57
58 struct prochandler_t {
59 int type;
60 void *ptr;
61 };
62
63 struct button_t {
64 struct prochandler_t proc;
65 char *name;
66 u16 gpio;
67 u8 polarity;
68 u8 pressed;
69 unsigned long seen;
70 };
71
72 struct led_t {
73 struct prochandler_t proc;
74 char *name;
75 u16 gpio;
76 u8 polarity;
77 u8 flash;
78 };
79
80 struct platform_t {
81 char *name;
82 struct button_t buttons[MAX_GPIO];
83 struct led_t leds[MAX_GPIO];
84 };
85
86 enum {
87 /* Linksys */
88 WAP54GV1,
89 WAP54GV3,
90 WRT54GV1,
91 WRT54G,
92 WRTSL54GS,
93 WRT54G3G,
94
95 /* ASUS */
96 WLHDD,
97 WL300G,
98 WL500G,
99 WL500GD,
100 WL500GP,
101 ASUS_4702,
102
103 /* Buffalo */
104 WBR2_G54,
105 WHR_G54S,
106 WHR_HP_G54,
107 WLA2_G54L,
108 BUFFALO_UNKNOWN,
109 BUFFALO_UNKNOWN_4710,
110
111 /* Siemens */
112 SE505V1,
113 SE505V2,
114
115 /* US Robotics */
116 USR5461,
117
118 /* Dell */
119 TM2300,
120
121 /* Motorola */
122 WR850GV1,
123 WR850GV2,
124 };
125
126 static struct platform_t __init platforms[] = {
127 /* Linksys */
128 [WAP54GV1] = {
129 .name = "Linksys WAP54G V1",
130 .buttons = {
131 { .name = "reset", .gpio = 1 << 0 },
132 },
133 .leds = {
134 { .name = "diag", .gpio = 1 << 3 },
135 { .name = "wlan", .gpio = 1 << 4 },
136 },
137 },
138 [WAP54GV3] = {
139 .name = "Linksys WAP54G V3",
140 .buttons = {
141 /* FIXME: verify this */
142 { .name = "reset", .gpio = 1 << 7 },
143 { .name = "ses", .gpio = 1 << 0 },
144 },
145 .leds = {
146 /* FIXME: diag? */
147 { .name = "ses", .gpio = 1 << 1 },
148 },
149 },
150 [WRT54GV1] = {
151 .name = "Linksys WRT54G V1.x",
152 .buttons = {
153 { .name = "reset", .gpio = 1 << 6 },
154 },
155 .leds = {
156 /* FIXME */
157 { .name = "diag", .gpio = 1 << 1 },
158 { .name = "dmz", .gpio = 1 << 7 },
159 { .name = "wlan", .gpio = 1 << 0 },
160 },
161 },
162 [WRT54G] = {
163 .name = "Linksys WRT54G*",
164 .buttons = {
165 { .name = "reset", .gpio = 1 << 6 },
166 { .name = "ses", .gpio = 1 << 4 },
167 },
168 .leds = {
169 { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
170 { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE },
171 { .name = "ses_white", .gpio = 1 << 2, .polarity = REVERSE },
172 { .name = "ses_orange", .gpio = 1 << 3, .polarity = REVERSE },
173 { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
174 },
175 },
176 [WRTSL54GS] = {
177 .name = "Linksys WRTSL54GS",
178 .buttons = {
179 { .name = "reset", .gpio = 1 << 6 },
180 { .name = "ses", .gpio = 1 << 4 },
181 },
182 .leds = {
183 { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
184 { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE },
185 { .name = "ses_white", .gpio = 1 << 5, .polarity = REVERSE },
186 { .name = "ses_orange", .gpio = 1 << 7, .polarity = REVERSE },
187 },
188 },
189 [WRT54G3G] = {
190 .name = "Linksys WRT54G3G",
191 .buttons = {
192 { .name = "reset", .gpio = 1 << 6 },
193 { .name = "3g", .gpio = 1 << 4 },
194 },
195 .leds = {
196 { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
197 { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE },
198 { .name = "3g_green", .gpio = 1 << 2, .polarity = NORMAL },
199 { .name = "3g_blue", .gpio = 1 << 3, .polarity = NORMAL },
200 { .name = "3g_blink", .gpio = 1 << 5, .polarity = NORMAL },
201 },
202 },
203 /* Asus */
204 [WLHDD] = {
205 .name = "ASUS WL-HDD",
206 .buttons = {
207 { .name = "reset", .gpio = 1 << 6 },
208 },
209 .leds = {
210 { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
211 },
212 },
213 [WL300G] = {
214 .name = "ASUS WL-500g",
215 .buttons = {
216 { .name = "reset", .gpio = 1 << 6 },
217 },
218 .leds = {
219 { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
220 },
221 },
222 [WL500G] = {
223 .name = "ASUS WL-500g",
224 .buttons = {
225 { .name = "reset", .gpio = 1 << 6 },
226 },
227 .leds = {
228 { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
229 },
230 },
231 [WL500GD] = {
232 .name = "ASUS WL-500g Deluxe",
233 .buttons = {
234 { .name = "reset", .gpio = 1 << 6 },
235 },
236 .leds = {
237 { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
238 },
239 },
240 [WL500GP] = {
241 .name = "ASUS WL-500g Premium",
242 .buttons = {
243 { .name = "reset", .gpio = 1 << 0 },
244 },
245 .leds = {
246 { .name = "power", .gpio = 1 << 1, .polarity = REVERSE },
247 { .name = "ses", .gpio = 1 << 4, .polarity = REVERSE },
248 },
249 },
250 [ASUS_4702] = {
251 .name = "ASUS (unknown, BCM4702)",
252 .buttons = {
253 { .name = "reset", .gpio = 1 << 6 },
254 },
255 .leds = {
256 { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
257 },
258 },
259 /* Buffalo */
260 [WHR_G54S] = {
261 .name = "Buffalo WHR-G54S",
262 .buttons = {
263 { .name = "reset", .gpio = 1 << 4 },
264 { .name = "ses", .gpio = 1 << 0 },
265 },
266 .leds = {
267 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
268 { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE },
269 { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
270 },
271 },
272 [WBR2_G54] = {
273 .name = "Buffalo WBR2-G54",
274 /* FIXME: verify */
275 .buttons = {
276 { .name = "reset", .gpio = 1 << 7 },
277 },
278 .leds = {
279 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
280 },
281 },
282 [WHR_HP_G54] = {
283 .name = "Buffalo WHR-HP-G54",
284 /* FIXME: verify */
285 .buttons = {
286 { .name = "reset", .gpio = 1 << 4 },
287 { .name = "ses", .gpio = 1 << 0 },
288 },
289 .leds = {
290 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
291 { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE },
292 { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
293 },
294 },
295 [WLA2_G54L] = {
296 .name = "Buffalo WLA2-G54L",
297 /* FIXME: verify */
298 .buttons = {
299 { .name = "reset", .gpio = 1 << 7 },
300 },
301 .leds = {
302 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
303 },
304 },
305 [BUFFALO_UNKNOWN] = {
306 .name = "Buffalo (unknown)",
307 .buttons = {
308 { .name = "reset", .gpio = 1 << 7 },
309 },
310 .leds = {
311 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
312 },
313 },
314 [BUFFALO_UNKNOWN_4710] = {
315 .name = "Buffalo (unknown, BCM4710)",
316 .buttons = {
317 { .name = "reset", .gpio = 1 << 4 },
318 },
319 .leds = {
320 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
321 },
322 },
323 /* Siemens */
324 [SE505V1] = {
325 .name = "Siemens SE505 V1",
326 .buttons = {
327 /* No usable buttons */
328 },
329 .leds = {
330 { .name = "dmz", .gpio = 1 << 4, .polarity = REVERSE },
331 { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE },
332 },
333 },
334 [SE505V2] = {
335 .name = "Siemens SE505 V2",
336 .buttons = {
337 /* No usable buttons */
338 },
339 .leds = {
340 { .name = "power", .gpio = 1 << 5, .polarity = REVERSE },
341 { .name = "dmz", .gpio = 1 << 0, .polarity = REVERSE },
342 { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE },
343 },
344 },
345 /* US Robotics */
346 [USR5461] = {
347 .name = "U.S. Robotics USR5461",
348 .buttons = {
349 /* No usable buttons */
350 },
351 .leds = {
352 { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
353 { .name = "printer", .gpio = 1 << 1, .polarity = REVERSE },
354 },
355 },
356 /* Dell */
357 [TM2300] = {
358 .name = "Dell TrueMobile 2300",
359 .buttons = {
360 { .name = "reset", .gpio = 1 << 0 },
361 },
362 .leds = {
363 { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
364 },
365 },
366 /* Motorola */
367 [WR850GV1] = {
368 .name = "Motorola WR850G V1",
369 .buttons = {
370 { .name = "reset", .gpio = 1 << 0 },
371 },
372 .leds = {
373 { .name = "diag", .gpio = 1 << 3, .polarity = REVERSE },
374 { .name = "wlan_red", .gpio = 1 << 5, .polarity = NORMAL },
375 { .name = "wlan_green", .gpio = 1 << 7, .polarity = NORMAL },
376 },
377 },
378 [WR850GV2] = {
379 .name = "Motorola WR850G V2",
380 .buttons = {
381 { .name = "reset", .gpio = 1 << 5 },
382 },
383 .leds = {
384 { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
385 { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
386 { .name = "modem", .gpio = 1 << 7, .polarity = NORMAL },
387 },
388 },
389 };
390
391 static struct proc_dir_entry *diag, *leds;
392
393 extern void *bcm947xx_sbh;
394 #define sbh bcm947xx_sbh
395
396 static int sb_irq(void *sbh);
397 static struct platform_t platform;
398
399 extern char *nvram_get(char *str);
400
401 static void led_flash(unsigned long dummy);
402
403 static inline char __init *getvar(char *str)
404 {
405 return nvram_get(str)?:"";
406 }
407
408 static struct platform_t __init *platform_detect(void)
409 {
410 char *boardnum, *boardtype, *buf;
411
412 boardnum = getvar("boardnum");
413 boardtype = getvar("boardtype");
414 if (strncmp(getvar("pmon_ver"), "CFE", 3) == 0) {
415 /* CFE based - newer hardware */
416 if (!strcmp(boardnum, "42")) { /* Linksys */
417 if (!strcmp(boardtype, "0x0101"))
418 return &platforms[WRT54G3G];
419
420 if (!strcmp(getvar("et1phyaddr"),"5") && !strcmp(getvar("et1mdcport"), "1"))
421 return &platforms[WRTSL54GS];
422
423 /* default to WRT54G */
424 return &platforms[WRT54G];
425 }
426
427 if (!strcmp(boardnum, "45")) { /* ASUS */
428 if (!strcmp(boardtype,"0x042f"))
429 return &platforms[WL500GP];
430 else
431 return &platforms[WL500GD];
432 }
433
434 if (!strcmp(boardnum, "10496"))
435 return &platforms[USR5461];
436 } else { /* PMON based - old stuff */
437 if (!strncmp(boardtype, "bcm94710dev", 11)) {
438 if (!strcmp(boardtype, "42"))
439 return &platforms[WRT54GV1];
440 if (simple_strtoul(boardnum, NULL, 9) == 2)
441 return &platforms[WAP54GV1];
442 }
443 if (!strncmp(getvar("hardware_version"), "WL500-", 6))
444 return &platforms[WL500G];
445 if (!strncmp(getvar("hardware_version"), "WL300-", 6)) {
446 /* Either WL-300g or WL-HDD, do more extensive checks */
447 if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
448 (simple_strtoul(getvar("et1phyaddr"), NULL, 9) == 1))
449 return &platforms[WLHDD];
450 if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
451 (simple_strtoul(getvar("et1phyaddr"), NULL, 9) == 10))
452 return &platforms[WL300G];
453 }
454
455 /* unknown asus stuff, probably bcm4702 */
456 if (!strncmp(boardnum, "asusX", 5))
457 return &platforms[ASUS_4702];
458
459 if ((simple_strtoul(getvar("GemtekPmonVer"), NULL, 0) == 9) &&
460 (simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 30))
461 return &platforms[WR850GV1];
462 }
463
464 if ((buf = (nvram_get("melco_id") ?: nvram_get("buffalo_id")))) {
465 /* Buffalo hardware, check id for specific hardware matches */
466 if (!strcmp(buf, "29bb0332"))
467 return &platforms[WBR2_G54];
468 if (!strcmp(buf, "29129"))
469 return &platforms[WLA2_G54L];
470 if (!strcmp(buf, "30189"))
471 return &platforms[WHR_HP_G54];
472 if (!strcmp(buf, "30182"))
473 return &platforms[WHR_G54S];
474 }
475
476 if (buf || !strcmp(boardnum, "00")) {/* probably buffalo */
477 if (!strncmp(boardtype, "bcm94710ap", 10))
478 return &platforms[BUFFALO_UNKNOWN_4710];
479 else
480 return &platforms[BUFFALO_UNKNOWN];
481 }
482
483 if (!strcmp(getvar("CFEver"), "MotoWRv203"))
484 return &platforms[WR850GV2];
485
486 /* not found */
487 return NULL;
488 }
489
490 static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos);
491 static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, void *data);
492 static struct file_operations diag_proc_fops = {
493 read: diag_proc_read,
494 write: diag_proc_write
495 };
496
497
498
499 static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
500 {
501 #ifdef LINUX_2_4
502 struct inode *inode = file->f_dentry->d_inode;
503 struct proc_dir_entry *dent = inode->u.generic_ip;
504 #else
505 struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
506 #endif
507 char *page;
508 int len = 0;
509
510 if ((page = kmalloc(1024, GFP_KERNEL)) == NULL)
511 return -ENOBUFS;
512
513 if (dent->data != NULL) {
514 struct prochandler_t *handler = (struct prochandler_t *) dent->data;
515 switch (handler->type) {
516 case PROC_LED: {
517 struct led_t * led = (struct led_t *) handler->ptr;
518 if (led->flash) {
519 len = sprintf(page, "f\n");
520 } else {
521 int in = (sb_gpioin(sbh) & led->gpio ? 1 : 0);
522 int p = (led->polarity == NORMAL ? 0 : 1);
523 len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0));
524 }
525 break;
526 }
527 case PROC_MODEL:
528 len = sprintf(page, "%s\n", platform.name);
529 break;
530 case PROC_GPIOMASK:
531 len = sprintf(page, "%d\n", gpiomask);
532 break;
533 }
534 }
535 len += 1;
536
537 if (*ppos < len) {
538 len = min_t(int, len - *ppos, count);
539 if (copy_to_user(buf, (page + *ppos), len)) {
540 kfree(page);
541 return -EFAULT;
542 }
543 *ppos += len;
544 } else {
545 len = 0;
546 }
547
548 return len;
549 }
550
551
552 static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, void *data)
553 {
554 #ifdef LINUX_2_4
555 struct inode *inode = file->f_dentry->d_inode;
556 struct proc_dir_entry *dent = inode->u.generic_ip;
557 #else
558 struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
559 #endif
560 char *page;
561 int ret = -EINVAL;
562
563 if ((page = kmalloc(count + 1, GFP_KERNEL)) == NULL)
564 return -ENOBUFS;
565
566 if (copy_from_user(page, buf, count)) {
567 kfree(page);
568 return -EINVAL;
569 }
570 page[count] = 0;
571
572 if (dent->data != NULL) {
573 struct prochandler_t *handler = (struct prochandler_t *) dent->data;
574 switch (handler->type) {
575 case PROC_LED: {
576 struct led_t *led = (struct led_t *) handler->ptr;
577 int p = (led->polarity == NORMAL ? 0 : 1);
578
579 if (led->gpio & gpiomask)
580 break;
581
582 if (page[0] == 'f') {
583 led->flash = 1;
584 led_flash(0);
585 } else {
586 led->flash = 0;
587 sb_gpioouten(sbh, led->gpio, led->gpio);
588 sb_gpiocontrol(sbh, led->gpio, 0);
589 sb_gpioout(sbh, led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0));
590 }
591 break;
592 }
593 case PROC_GPIOMASK:
594 gpiomask = simple_strtoul(page, NULL, 16);
595 break;
596 }
597 ret = count;
598 }
599
600 kfree(page);
601 return ret;
602 }
603
604 struct event_t {
605 struct tq_struct tq;
606 char buf[256];
607 char *argv[3];
608 char *envp[6];
609 };
610
611 static void hotplug_button(struct event_t *event)
612 {
613 call_usermodehelper (event->argv[0], event->argv, event->envp);
614 kfree(event);
615 }
616
617 static void button_handler(int irq, void *dev_id, struct pt_regs *regs)
618 {
619 struct button_t *b;
620 int in = sb_gpioin(sbh);
621 struct event_t *event;
622
623 for (b = platform.buttons; b->name; b++) {
624 if (b->gpio & gpiomask)
625 continue;
626
627 if (b->polarity != (in & b->gpio)) {
628
629 b->pressed ^= 1;
630
631 if ((event = (struct event_t *)kmalloc (256, GFP_KERNEL))) {
632 int i;
633 char *scratch = event->buf;
634
635 i = 0;
636 event->argv[i++] = hotplug_path;
637 event->argv[i++] = "button";
638 event->argv[i] = 0;
639
640 i = 0;
641 event->envp[i++] = "HOME=/";
642 event->envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
643 event->envp[i++] = scratch;
644 scratch += sprintf (scratch, "ACTION=%s", b->pressed?"pressed":"released") + 1;
645 event->envp[i++] = scratch;
646 scratch += sprintf (scratch, "BUTTON=%s", b->name) + 1;
647 event->envp[i++] = scratch;
648 scratch += sprintf (scratch, "SEEN=%ld", (jiffies - b->seen)/HZ) + 1;
649 event->envp[i] = 0;
650
651 INIT_TQUEUE(&event->tq, (void *)(void *)hotplug_button, (void *)event);
652 schedule_task(&event->tq);
653 }
654
655 b->seen = jiffies;
656 b->polarity ^= b->gpio;
657 sb_gpiointpolarity(sbh, b->gpio, b->polarity);
658 }
659 }
660 }
661
662 static struct timer_list led_timer = {
663 function: &led_flash
664 };
665
666 static void led_flash(unsigned long dummy) {
667 struct led_t *l;
668 unsigned mask = 0;
669
670 for (l = platform.leds; l->name; l++) {
671 if (l->flash)
672 mask |= l->gpio;
673 }
674
675 mask &= ~gpiomask;
676
677 if (mask) {
678 unsigned val;
679
680 val = ~sb_gpioin(sbh);
681 val &= mask;
682
683 sb_gpioouten(sbh, mask, mask);
684 sb_gpiocontrol(sbh, mask, 0);
685 sb_gpioout(sbh, mask, val);
686
687 mod_timer(&led_timer, jiffies + FLASH_TIME);
688 }
689 }
690
691 static void __init register_buttons(struct button_t *b)
692 {
693 int irq = sb_irq(sbh) + 2;
694 chipcregs_t *cc;
695
696 request_irq(irq, button_handler, SA_SHIRQ | SA_SAMPLE_RANDOM, "gpio", button_handler);
697
698 for (; b->name; b++) {
699 if (b->gpio & gpiomask)
700 continue;
701
702 sb_gpioouten(sbh, b->gpio,0);
703 sb_gpiocontrol(sbh, b->gpio,0);
704 b->polarity = sb_gpioin(sbh) & b->gpio;
705 sb_gpiointpolarity(sbh, b->gpio, b->polarity);
706 sb_gpiointmask(sbh, b->gpio, b->gpio);
707 }
708
709 if ((cc = sb_setcore(sbh, SB_CC, 0))) {
710 int intmask;
711
712 intmask = readl(&cc->intmask);
713 intmask |= CI_GPIO;
714 writel(intmask, &cc->intmask);
715 }
716 }
717
718 static void __exit unregister_buttons(struct button_t *b)
719 {
720 int irq = sb_irq(sbh) + 2;
721
722 for (; b->name; b++)
723 sb_gpiointmask(sbh, b->gpio, 0);
724
725 free_irq(irq, button_handler);
726 }
727
728 static void __init register_leds(struct led_t *l)
729 {
730 struct proc_dir_entry *p;
731
732 leds = proc_mkdir("led", diag);
733 if (!leds)
734 return;
735
736 for(; l->name; l++) {
737 if (l->gpio & gpiomask)
738 continue;
739
740 sb_gpioouten(sbh, l->gpio, l->gpio);
741 sb_gpiocontrol(sbh, l->gpio, 0);
742 sb_gpioout(sbh, l->gpio, (l->polarity == NORMAL)?0:l->gpio);
743
744 if ((p = create_proc_entry(l->name, S_IRUSR, leds))) {
745 l->proc.type = PROC_LED;
746 l->proc.ptr = l;
747 p->data = (void *) &l->proc;
748 p->proc_fops = &diag_proc_fops;
749 }
750 }
751 }
752
753 static void __exit unregister_leds(struct led_t *l)
754 {
755 for(; l->name; l++)
756 remove_proc_entry(l->name, leds);
757
758 remove_proc_entry("led", diag);
759 }
760
761 static void __exit diag_exit(void)
762 {
763
764 del_timer(&led_timer);
765
766 if (platform.buttons)
767 unregister_buttons(platform.buttons);
768
769 if (platform.leds)
770 unregister_leds(platform.leds);
771
772 remove_proc_entry("model", diag);
773 remove_proc_entry("gpiomask", diag);
774 remove_proc_entry("diag", NULL);
775 }
776
777 static struct prochandler_t proc_model = { .type = PROC_MODEL };
778 static struct prochandler_t proc_gpiomask = { .type = PROC_GPIOMASK };
779
780 static int __init diag_init(void)
781 {
782 static struct proc_dir_entry *p;
783 static struct platform_t *detected;
784
785 detected = platform_detect();
786 if (!detected) {
787 printk(MODULE_NAME ": Router model not detected.\n");
788 return -ENODEV;
789 }
790 memcpy(&platform, detected, sizeof(struct platform_t));
791
792 printk(MODULE_NAME ": Detected '%s'\n", platform.name);
793
794 if (!(diag = proc_mkdir("diag", NULL))) {
795 printk(MODULE_NAME ": proc_mkdir on /proc/diag failed\n");
796 return -EINVAL;
797 }
798
799 if ((p = create_proc_entry("model", S_IRUSR, diag))) {
800 p->data = (void *) &proc_model;
801 p->proc_fops = &diag_proc_fops;
802 }
803
804 if ((p = create_proc_entry("gpiomask", S_IRUSR | S_IWUSR, diag))) {
805 p->data = (void *) &proc_gpiomask;
806 p->proc_fops = &diag_proc_fops;
807 }
808
809 if (platform.buttons)
810 register_buttons(platform.buttons);
811
812 if (platform.leds)
813 register_leds(platform.leds);
814
815 return 0;
816 }
817
818 EXPORT_NO_SYMBOLS;
819
820 module_init(diag_init);
821 module_exit(diag_exit);
822
823 MODULE_AUTHOR("Mike Baker, Felix Fietkau / OpenWrt.org");
824 MODULE_LICENSE("GPL");
825
826 /* TODO: export existing sb_irq instead */
827 static int sb_irq(void *sbh)
828 {
829 uint idx;
830 void *regs;
831 sbconfig_t *sb;
832 uint32 flag, sbipsflag;
833 uint irq = 0;
834
835 regs = sb_coreregs(sbh);
836 sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
837 flag = (R_REG(&sb->sbtpsflag) & SBTPS_NUM0_MASK);
838
839 idx = sb_coreidx(sbh);
840
841 if ((regs = sb_setcore(sbh, SB_MIPS, 0)) ||
842 (regs = sb_setcore(sbh, SB_MIPS33, 0))) {
843 sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
844
845 /* sbipsflag specifies which core is routed to interrupts 1 to 4 */
846 sbipsflag = R_REG(&sb->sbipsflag);
847 for (irq = 1; irq <= 4; irq++, sbipsflag >>= 8) {
848 if ((sbipsflag & 0x3f) == flag)
849 break;
850 }
851 if (irq == 5)
852 irq = 0;
853 }
854
855 sb_setcoreidx(sbh, idx);
856
857 return irq;
858 }