1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/config.h>
20#include <linux/module.h>
21#include <linux/stddef.h>
22#include <linux/init.h>
23#include <linux/types.h>
24#include <linux/ioport.h>
25#include <linux/bitops.h>
26#include <linux/version.h>
27#include <linux/delay.h>
28#include <linux/netdevice.h>
29#include <linux/interrupt.h>
30#include <linux/kernel_stat.h>
31#include <linux/reboot.h>
32#include <linux/proc_fs.h>
33#include <linux/ctype.h>
34#include <asm/io.h>
35#include <asm/gsc.h>
36#include <asm/processor.h>
37#include <asm/hardware.h>
38#include <asm/param.h>
39#include <asm/led.h>
40#include <asm/pdc.h>
41#include <asm/uaccess.h>
42
43
44
45
46
47
48
49static int led_type = -1;
50static int led_heartbeat = 1;
51static int led_diskio = 1;
52static int led_lanrxtx = 1;
53static char lcd_text[32];
54
55#if 0
56#define DPRINTK(x) printk x
57#else
58#define DPRINTK(x)
59#endif
60
61
62#define CALC_ADD(val, comp, add) \
63 (val<=(comp/8) ? add/16 : val<=(comp/4) ? add/8 : val<=(comp/2) ? add/4 : add)
64
65
66struct lcd_block {
67 unsigned char command;
68 unsigned char on;
69 unsigned char off;
70};
71
72
73
74
75struct pdc_chassis_lcd_info_ret_block {
76 unsigned long model:16;
77 unsigned long lcd_width:16;
78 char *lcd_cmd_reg_addr;
79 char *lcd_data_reg_addr;
80 unsigned int min_cmd_delay;
81 unsigned char reset_cmd1;
82 unsigned char reset_cmd2;
83 unsigned char act_enable;
84 struct lcd_block heartbeat;
85 struct lcd_block disk_io;
86 struct lcd_block lan_rcv;
87 struct lcd_block lan_tx;
88 char _pad;
89};
90
91
92
93#define KITTYHAWK_LCD_CMD (0xfffffffff0190000UL)
94#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1)
95
96
97
98static struct pdc_chassis_lcd_info_ret_block
99lcd_info __attribute__((aligned(8))) =
100{
101 model: DISPLAY_MODEL_LCD,
102 lcd_width: 16,
103 lcd_cmd_reg_addr: (char *) KITTYHAWK_LCD_CMD,
104 lcd_data_reg_addr:(char *) KITTYHAWK_LCD_DATA,
105 min_cmd_delay: 40,
106 reset_cmd1: 0x80,
107 reset_cmd2: 0xc0,
108};
109
110
111
112#define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr
113#define LCD_DATA_REG lcd_info.lcd_data_reg_addr
114#define LED_DATA_REG lcd_info.lcd_cmd_reg_addr
115
116
117
118static void (*led_func_ptr) (unsigned char);
119
120#define LED_HASLCD 1
121#define LED_NOLCD 0
122#ifdef CONFIG_PROC_FS
123static int led_proc_read(char *page, char **start, off_t off, int count,
124 int *eof, void *data)
125{
126 char *out = page;
127 int len;
128
129 switch ((long)data)
130 {
131 case LED_NOLCD:
132 out += sprintf(out, "Heartbeat: %d\n", led_heartbeat);
133 out += sprintf(out, "Disk IO: %d\n", led_diskio);
134 out += sprintf(out, "LAN Rx/Tx: %d\n", led_lanrxtx);
135 break;
136 case LED_HASLCD:
137 out += sprintf(out, "%s\n", lcd_text);
138 break;
139 default:
140 *eof = 1;
141 return 0;
142 }
143
144 len = out - page - off;
145 if (len < count) {
146 *eof = 1;
147 if (len <= 0) return 0;
148 } else {
149 len = count;
150 }
151 *start = page + off;
152 return len;
153}
154
155static int led_proc_write(struct file *file, const char *buf,
156 unsigned long count, void *data)
157{
158 char *cur, lbuf[count];
159 int d;
160
161 if (!capable(CAP_SYS_ADMIN))
162 return -EACCES;
163
164 memset(lbuf, 0, count);
165
166 copy_from_user(lbuf, buf, count);
167 cur = lbuf;
168
169
170 while (*cur && isspace(*cur))
171 {
172 cur++;
173 }
174
175 switch ((long)data)
176 {
177 case LED_NOLCD:
178 d = *cur++ - '0';
179 if (d != 0 && d != 1) goto parse_error;
180 led_heartbeat = d;
181
182 if (*cur++ != ' ') goto parse_error;
183
184 d = *cur++ - '0';
185 if (d != 0 && d != 1) goto parse_error;
186 led_diskio = d;
187
188 if (*cur++ != ' ') goto parse_error;
189
190 d = *cur++ - '0';
191 if (d != 0 && d != 1) goto parse_error;
192 led_lanrxtx = d;
193
194 break;
195 case LED_HASLCD:
196 if (*cur == 0)
197 {
198
199 lcd_print("Linux " UTS_RELEASE);
200 }
201 else
202 {
203
204
205 if (*cur && cur[strlen(cur)-1] == '\n')
206 cur[strlen(cur)-1] = 0;
207 lcd_print(cur);
208 }
209 break;
210 default:
211 return 0;
212 }
213
214 return count;
215
216parse_error:
217 if ((long)data == LED_NOLCD)
218 printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n");
219 return -EINVAL;
220}
221
222static int __init led_create_procfs(void)
223{
224 struct proc_dir_entry *proc_pdc_root = NULL;
225 struct proc_dir_entry *ent;
226
227 if (led_type == -1) return -1;
228
229 proc_pdc_root = proc_mkdir("pdc", 0);
230 if (!proc_pdc_root) return -1;
231 proc_pdc_root->owner = THIS_MODULE;
232 ent = create_proc_entry("led", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
233 if (!ent) return -1;
234 ent->nlink = 1;
235 ent->data = (void *)LED_NOLCD;
236 ent->read_proc = led_proc_read;
237 ent->write_proc = led_proc_write;
238 ent->owner = THIS_MODULE;
239
240 if (led_type == LED_HASLCD)
241 {
242 ent = create_proc_entry("lcd", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
243 if (!ent) return -1;
244 ent->nlink = 1;
245 ent->data = (void *)LED_HASLCD;
246 ent->read_proc = led_proc_read;
247 ent->write_proc = led_proc_write;
248 ent->owner = THIS_MODULE;
249 }
250
251 return 0;
252}
253#endif
254
255
256
257
258
259
260#define LED_DATA 0x01
261#define LED_STROBE 0x02
262static void led_ASP_driver(unsigned char leds)
263{
264 int i;
265
266 leds = ~leds;
267 for (i = 0; i < 8; i++) {
268 unsigned char value;
269 value = (leds & 0x80) >> 7;
270 gsc_writeb( value, LED_DATA_REG );
271 gsc_writeb( value | LED_STROBE, LED_DATA_REG );
272 leds <<= 1;
273 }
274}
275
276
277
278
279
280
281
282static void led_LASI_driver(unsigned char leds)
283{
284 leds = ~leds;
285 gsc_writeb( leds, LED_DATA_REG );
286}
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301static void led_LCD_driver(unsigned char leds)
302{
303 static int last_index;
304 static int last_was_cmd;
305 struct lcd_block *block_ptr;
306 int value;
307
308 switch (last_index) {
309 case 0: block_ptr = &lcd_info.heartbeat;
310 value = leds & LED_HEARTBEAT;
311 break;
312 case 1: block_ptr = &lcd_info.disk_io;
313 value = leds & LED_DISK_IO;
314 break;
315 case 2: block_ptr = &lcd_info.lan_rcv;
316 value = leds & LED_LAN_RCV;
317 break;
318 case 3: block_ptr = &lcd_info.lan_tx;
319 value = leds & LED_LAN_TX;
320 break;
321 default:
322 return;
323 }
324
325 if (last_was_cmd) {
326
327 gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );
328 } else {
329
330 gsc_writeb( block_ptr->command, LCD_CMD_REG );
331 }
332
333
334 if (++last_was_cmd == 2) {
335 last_was_cmd = 0;
336 if (++last_index == 4)
337 last_index = 0;
338 }
339}
340
341
342
343
344
345
346
347
348
349
350
351
352static unsigned long led_net_rx_counter, led_net_tx_counter;
353
354static void led_get_net_stats(int addvalue)
355{
356#ifdef CONFIG_NET
357 static unsigned long rx_total_last, tx_total_last;
358 unsigned long rx_total, tx_total;
359 struct net_device *dev;
360 struct net_device_stats *stats;
361
362 rx_total = tx_total = 0;
363
364
365
366 read_lock(&dev_base_lock);
367 for (dev = dev_base; dev != NULL; dev = dev->next) {
368 if (dev->get_stats) {
369 stats = dev->get_stats(dev);
370 rx_total += stats->rx_packets;
371 tx_total += stats->tx_packets;
372 }
373 }
374 read_unlock(&dev_base_lock);
375
376 rx_total -= rx_total_last;
377 tx_total -= tx_total_last;
378
379 if (rx_total)
380 led_net_rx_counter += CALC_ADD(rx_total, tx_total, addvalue);
381
382 if (tx_total)
383 led_net_tx_counter += CALC_ADD(tx_total, rx_total, addvalue);
384
385 rx_total_last += rx_total;
386 tx_total_last += tx_total;
387#endif
388}
389
390
391
392
393
394
395
396
397
398
399static unsigned long led_diskio_counter;
400
401static void led_get_diskio_stats(int addvalue)
402{
403 static unsigned int diskio_total_last, diskio_max;
404 int major, disk, total;
405
406 total = 0;
407 for (major = 0; major < DK_MAX_MAJOR; major++) {
408 for (disk = 0; disk < DK_MAX_DISK; disk++)
409 total += kstat.dk_drive[major][disk];
410 }
411 total -= diskio_total_last;
412
413 if (total) {
414 if (total >= diskio_max) {
415 led_diskio_counter += addvalue;
416 diskio_max = total;
417 } else
418 led_diskio_counter += CALC_ADD(total, diskio_max, addvalue);
419 }
420
421 diskio_total_last += total;
422}
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437static unsigned char currentleds;
438
439#define HEARTBEAT_LEN (HZ*6/100)
440#define HEARTBEAT_2ND_RANGE_START (HZ*22/100)
441#define HEARTBEAT_2ND_RANGE_END (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN)
442
443static void led_tasklet_func(unsigned long unused)
444{
445 static unsigned int count, count_HZ;
446 static unsigned char lastleds;
447
448
449 if (!led_func_ptr)
450 return;
451
452
453 ++count;
454 if (++count_HZ == HZ)
455 count_HZ = 0;
456
457 if (led_heartbeat)
458 {
459
460 if (count_HZ<HEARTBEAT_LEN ||
461 (count_HZ>=HEARTBEAT_2ND_RANGE_START && count_HZ<HEARTBEAT_2ND_RANGE_END))
462 currentleds |= LED_HEARTBEAT;
463 else
464 currentleds &= ~LED_HEARTBEAT;
465 }
466
467
468
469 if (led_lanrxtx)
470 {
471 if ((count & 31) == 0)
472 led_get_net_stats(30);
473
474 if (led_net_rx_counter) {
475 led_net_rx_counter--;
476 currentleds |= LED_LAN_RCV;
477 }
478 else
479 currentleds &= ~LED_LAN_RCV;
480
481 if (led_net_tx_counter) {
482 led_net_tx_counter--;
483 currentleds |= LED_LAN_TX;
484 }
485 else
486 currentleds &= ~LED_LAN_TX;
487 }
488
489 if (led_diskio)
490 {
491
492 if ((count & 31) == 15)
493 led_get_diskio_stats(30);
494
495 if (led_diskio_counter) {
496 led_diskio_counter--;
497 currentleds |= LED_DISK_IO;
498 }
499 else
500 currentleds &= ~LED_DISK_IO;
501 }
502
503
504 if (currentleds != lastleds) {
505 led_func_ptr(currentleds);
506 lastleds = currentleds;
507 }
508}
509
510
511DECLARE_TASKLET_DISABLED(led_tasklet, led_tasklet_func, 0);
512
513
514
515
516
517
518
519
520
521
522static int led_halt(struct notifier_block *, unsigned long, void *);
523
524static struct notifier_block led_notifier = {
525 notifier_call: led_halt,
526};
527
528static int led_halt(struct notifier_block *nb, unsigned long event, void *buf)
529{
530 char *txt;
531
532 switch (event) {
533 case SYS_RESTART: txt = "SYSTEM RESTART";
534 break;
535 case SYS_HALT: txt = "SYSTEM HALT";
536 break;
537 case SYS_POWER_OFF: txt = "SYSTEM POWER OFF";
538 break;
539 default: return NOTIFY_DONE;
540 }
541
542
543 tasklet_disable(&led_tasklet);
544
545 if (lcd_info.model == DISPLAY_MODEL_LCD)
546 lcd_print(txt);
547 else
548 if (led_func_ptr)
549 led_func_ptr(0xff);
550
551 unregister_reboot_notifier(&led_notifier);
552 return NOTIFY_OK;
553}
554
555
556
557
558
559
560
561
562
563int __init register_led_driver(int model, char *cmd_reg, char *data_reg)
564{
565 static int initialized;
566
567 if (initialized || !data_reg)
568 return 1;
569
570 lcd_info.model = model;
571 LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? NULL : cmd_reg;
572
573 switch (lcd_info.model) {
574 case DISPLAY_MODEL_LCD:
575 LCD_DATA_REG = data_reg;
576 printk(KERN_INFO "LCD display at %p,%p registered\n",
577 LCD_CMD_REG , LCD_DATA_REG);
578 led_func_ptr = led_LCD_driver;
579 lcd_print( "Linux " UTS_RELEASE );
580 led_type = LED_HASLCD;
581 break;
582
583 case DISPLAY_MODEL_LASI:
584 LED_DATA_REG = data_reg;
585 led_func_ptr = led_LASI_driver;
586 printk(KERN_INFO "LED display at %p registered\n", LED_DATA_REG);
587 led_type = LED_NOLCD;
588 break;
589
590 case DISPLAY_MODEL_OLD_ASP:
591 LED_DATA_REG = data_reg;
592 led_func_ptr = led_ASP_driver;
593 printk(KERN_INFO "LED (ASP-style) display at %p registered\n",
594 LED_DATA_REG);
595 led_type = LED_NOLCD;
596 break;
597
598 default:
599 printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n",
600 __FUNCTION__, lcd_info.model);
601 return 1;
602 }
603
604
605
606 initialized++;
607 register_reboot_notifier(&led_notifier);
608
609
610 tasklet_enable(&led_tasklet);
611
612 return 0;
613}
614
615
616
617
618
619
620
621
622
623
624
625
626void __init register_led_regions(void)
627{
628 switch (lcd_info.model) {
629 case DISPLAY_MODEL_LCD:
630 request_mem_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd");
631 request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
632 break;
633 case DISPLAY_MODEL_LASI:
634 case DISPLAY_MODEL_OLD_ASP:
635 request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data");
636 break;
637 }
638}
639
640
641
642
643
644
645
646
647
648
649
650int lcd_print( char *str )
651{
652 int i;
653
654 if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD)
655 return 0;
656
657
658 tasklet_disable(&led_tasklet);
659
660
661 strncpy(lcd_text, str, sizeof(lcd_text)-1);
662
663
664 gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG);
665 udelay(lcd_info.min_cmd_delay);
666
667
668 for (i=0; i < lcd_info.lcd_width; i++) {
669 if (str && *str)
670 gsc_writeb(*str++, LCD_DATA_REG);
671 else
672 gsc_writeb(' ', LCD_DATA_REG);
673 udelay(lcd_info.min_cmd_delay);
674 }
675
676
677 tasklet_enable(&led_tasklet);
678
679 return lcd_info.lcd_width;
680}
681
682
683
684
685
686
687
688
689
690
691
692
693
694int __init led_init(void)
695{
696 struct pdc_chassis_info chassis_info;
697 int ret;
698
699
700 switch (CPU_HVERSION) {
701 case 0x580:
702 case 0x581:
703 case 0x582:
704 case 0x583:
705 case 0x58B:
706 printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, "
707 "LED detection skipped.\n", __FILE__, CPU_HVERSION);
708 goto found;
709 }
710
711
712 lcd_info.model = DISPLAY_MODEL_NONE;
713 chassis_info.actcnt = chassis_info.maxcnt = 0;
714
715 if ((ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info))) == PDC_OK) {
716 DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), "
717 "lcd_width=%d, cmd_delay=%u,\n"
718 "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ld\n",
719 __FILE__, lcd_info.model,
720 (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" :
721 (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown",
722 lcd_info.lcd_width, lcd_info.min_cmd_delay,
723 __FILE__, sizeof(lcd_info),
724 chassis_info.actcnt, chassis_info.maxcnt));
725 DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%d\n",
726 __FILE__, lcd_info.lcd_cmd_reg_addr,
727 lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1,
728 lcd_info.reset_cmd2, lcd_info.act_enable ));
729
730
731 if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt)
732 goto not_found;
733
734 switch (lcd_info.model) {
735 case DISPLAY_MODEL_LCD:
736 if (chassis_info.actcnt <
737 offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1)
738 goto not_found;
739 if (!lcd_info.act_enable) {
740 DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.\n"));
741 goto not_found;
742 }
743 break;
744
745 case DISPLAY_MODEL_NONE:
746 printk(KERN_INFO "PDC reported no LCD or LED.\n");
747 goto not_found;
748
749 case DISPLAY_MODEL_LASI:
750 if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32)
751 goto not_found;
752 break;
753
754 default:
755 printk(KERN_WARNING "PDC reported unknown LCD/LED model %d\n",
756 lcd_info.model);
757 goto not_found;
758 }
759
760found:
761
762 register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG);
763 return 0;
764
765 } else {
766 DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %d\n", ret));
767 }
768
769not_found:
770 lcd_info.model = DISPLAY_MODEL_NONE;
771 return 1;
772}
773
774#ifdef CONFIG_PROC_FS
775module_init(led_create_procfs)
776#endif
777