led.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:20k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *    Chassis LCD/LED driver for HP-PARISC workstations
  3.  *
  4.  *      (c) Copyright 2000 Red Hat Software
  5.  *      (c) Copyright 2000 Helge Deller <hdeller@redhat.com>
  6.  *      (c) Copyright 2001-2002 Helge Deller <deller@gmx.de>
  7.  *      (c) Copyright 2001 Randolph Chung <tausq@debian.org>
  8.  *
  9.  *      This program is free software; you can redistribute it and/or modify
  10.  *      it under the terms of the GNU General Public License as published by
  11.  *      the Free Software Foundation; either version 2 of the License, or
  12.  *      (at your option) any later version.
  13.  *
  14.  * TODO:
  15.  * - speed-up calculations with inlined assembler
  16.  * - interface to write to second row of LCD from /proc
  17.  */
  18. #include <linux/config.h>
  19. #include <linux/module.h>
  20. #include <linux/stddef.h> /* for offsetof() */
  21. #include <linux/init.h>
  22. #include <linux/types.h>
  23. #include <linux/ioport.h>
  24. #include <linux/bitops.h>
  25. #include <linux/version.h>
  26. #include <linux/delay.h>
  27. #include <linux/netdevice.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/kernel_stat.h>
  30. #include <linux/reboot.h>
  31. #include <linux/proc_fs.h>
  32. #include <linux/ctype.h>
  33. #include <asm/io.h>
  34. #include <asm/gsc.h>
  35. #include <asm/processor.h>
  36. #include <asm/hardware.h>
  37. #include <asm/param.h> /* HZ */
  38. #include <asm/led.h>
  39. #include <asm/pdc.h>
  40. #include <asm/uaccess.h>
  41. /* The control of the LEDs and LCDs on PARISC-machines have to be done 
  42.    completely in software. The necessary calculations are done in a tasklet
  43.    which is scheduled at every timer interrupt and since the calculations 
  44.    may consume relatively much CPU-time some of the calculations can be 
  45.    turned off with the following variables (controlled via procfs) */
  46. static int led_type = -1;
  47. static int led_heartbeat = 1;
  48. static int led_diskio = 1;
  49. static int led_lanrxtx = 1;
  50. static char lcd_text[32];
  51. #if 0
  52. #define DPRINTK(x) printk x
  53. #else
  54. #define DPRINTK(x)
  55. #endif
  56. #define CALC_ADD(val, comp, add) 
  57.  (val<=(comp/8) ? add/16 : val<=(comp/4) ? add/8 : val<=(comp/2) ? add/4 : add)
  58. struct lcd_block {
  59. unsigned char command; /* stores the command byte      */
  60. unsigned char on; /* value for turning LED on     */
  61. unsigned char off; /* value for turning LED off    */
  62. };
  63. /* Structure returned by PDC_RETURN_CHASSIS_INFO */
  64. /* NOTE: we use unsigned long:16 two times, since the following member 
  65.    lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */
  66. struct pdc_chassis_lcd_info_ret_block {
  67. unsigned long model:16; /* DISPLAY_MODEL_XXXX */
  68. unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */
  69. char *lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */
  70. char *lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */
  71. unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */
  72. unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */
  73. unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */
  74. unsigned char act_enable; /* 0 = no activity (LCD only) */
  75. struct lcd_block heartbeat;
  76. struct lcd_block disk_io;
  77. struct lcd_block lan_rcv;
  78. struct lcd_block lan_tx;
  79. char _pad;
  80. };
  81. /* LCD_CMD and LCD_DATA for KittyHawk machines */
  82. #define KITTYHAWK_LCD_CMD  (0xfffffffff0190000UL) /* 64bit-ready */
  83. #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1)
  84. /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 
  85.  * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */
  86. static struct pdc_chassis_lcd_info_ret_block
  87. lcd_info __attribute__((aligned(8))) =
  88. {
  89.       model: DISPLAY_MODEL_LCD,
  90.       lcd_width: 16,
  91.       lcd_cmd_reg_addr: (char *) KITTYHAWK_LCD_CMD,
  92.       lcd_data_reg_addr:(char *) KITTYHAWK_LCD_DATA,
  93.       min_cmd_delay: 40,
  94.       reset_cmd1: 0x80,
  95.       reset_cmd2: 0xc0,
  96. };
  97. /* direct access to some of the lcd_info variables */
  98. #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr  
  99. #define LCD_DATA_REG lcd_info.lcd_data_reg_addr  
  100. #define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */
  101. /* ptr to LCD/LED-specific function */
  102. static void (*led_func_ptr) (unsigned char);
  103. #define LED_HASLCD 1
  104. #define LED_NOLCD  0
  105. #ifdef CONFIG_PROC_FS
  106. static int led_proc_read(char *page, char **start, off_t off, int count, 
  107. int *eof, void *data)
  108. {
  109. char *out = page;
  110. int len;
  111. switch ((long)data)
  112. {
  113. case LED_NOLCD:
  114. out += sprintf(out, "Heartbeat: %dn", led_heartbeat);
  115. out += sprintf(out, "Disk IO: %dn", led_diskio);
  116. out += sprintf(out, "LAN Rx/Tx: %dn", led_lanrxtx);
  117. break;
  118. case LED_HASLCD:
  119. out += sprintf(out, "%sn", lcd_text);
  120. break;
  121. default:
  122. *eof = 1;
  123. return 0;
  124. }
  125. len = out - page - off;
  126. if (len < count) {
  127. *eof = 1;
  128. if (len <= 0) return 0;
  129. } else {
  130. len = count;
  131. }
  132. *start = page + off;
  133. return len;
  134. }
  135. static int led_proc_write(struct file *file, const char *buf, 
  136. unsigned long count, void *data)
  137. {
  138. char *cur, lbuf[count];
  139. int d;
  140. if (!capable(CAP_SYS_ADMIN))
  141. return -EACCES;
  142. memset(lbuf, 0, count);
  143. copy_from_user(lbuf, buf, count);
  144. cur = lbuf;
  145. /* skip initial spaces */
  146. while (*cur && isspace(*cur))
  147. {
  148. cur++;
  149. }
  150. switch ((long)data)
  151. {
  152. case LED_NOLCD:
  153. d = *cur++ - '0';
  154. if (d != 0 && d != 1) goto parse_error;
  155. led_heartbeat = d;
  156. if (*cur++ != ' ') goto parse_error;
  157. d = *cur++ - '0';
  158. if (d != 0 && d != 1) goto parse_error;
  159. led_diskio = d;
  160. if (*cur++ != ' ') goto parse_error;
  161. d = *cur++ - '0';
  162. if (d != 0 && d != 1) goto parse_error;
  163. led_lanrxtx = d;
  164. break;
  165. case LED_HASLCD:
  166. if (*cur == 0) 
  167. {
  168. /* reset to default */
  169. lcd_print("Linux " UTS_RELEASE);
  170. }
  171. else
  172. {
  173. /* chop off trailing n.. if the user gives multiple
  174.  * n then it's all their fault.. */
  175. if (*cur && cur[strlen(cur)-1] == 'n')
  176. cur[strlen(cur)-1] = 0;
  177. lcd_print(cur);
  178. }
  179. break;
  180. default:
  181. return 0;
  182. }
  183. return count;
  184. parse_error:
  185. if ((long)data == LED_NOLCD)
  186. printk(KERN_CRIT "Parse error: expect "n n n" (n == 0 or 1) for heartbeat,ndisk io and lan tx/rx indicatorsn");
  187. return -EINVAL;
  188. }
  189. static int __init led_create_procfs(void)
  190. {
  191. struct proc_dir_entry *proc_pdc_root = NULL;
  192. struct proc_dir_entry *ent;
  193. if (led_type == -1) return -1;
  194. proc_pdc_root = proc_mkdir("pdc", 0);
  195. if (!proc_pdc_root) return -1;
  196. proc_pdc_root->owner = THIS_MODULE;
  197. ent = create_proc_entry("led", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
  198. if (!ent) return -1;
  199. ent->nlink = 1;
  200. ent->data = (void *)LED_NOLCD; /* LED */
  201. ent->read_proc = led_proc_read;
  202. ent->write_proc = led_proc_write;
  203. ent->owner = THIS_MODULE;
  204. if (led_type == LED_HASLCD)
  205. {
  206. ent = create_proc_entry("lcd", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
  207. if (!ent) return -1;
  208. ent->nlink = 1;
  209. ent->data = (void *)LED_HASLCD; /* LCD */
  210. ent->read_proc = led_proc_read;
  211. ent->write_proc = led_proc_write;
  212. ent->owner = THIS_MODULE;
  213. }
  214. return 0;
  215. }
  216. #endif
  217. /*
  218.    ** 
  219.    ** led_ASP_driver()
  220.    ** 
  221.  */
  222. #define LED_DATA 0x01 /* data to shift (0:on 1:off) */
  223. #define LED_STROBE 0x02 /* strobe to clock data */
  224. static void led_ASP_driver(unsigned char leds)
  225. {
  226. int i;
  227. leds = ~leds;
  228. for (i = 0; i < 8; i++) {
  229. unsigned char value;
  230. value = (leds & 0x80) >> 7;
  231. gsc_writeb( value,  LED_DATA_REG );
  232. gsc_writeb( value | LED_STROBE,  LED_DATA_REG );
  233. leds <<= 1;
  234. }
  235. }
  236. /*
  237.    ** 
  238.    ** led_LASI_driver()
  239.    ** 
  240.  */
  241. static void led_LASI_driver(unsigned char leds)
  242. {
  243. leds = ~leds;
  244. gsc_writeb( leds, LED_DATA_REG );
  245. }
  246. /*
  247.    ** 
  248.    ** led_LCD_driver()
  249.    ** 
  250.    ** The logic of the LCD driver is, that we write at every scheduled call
  251.    ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers.
  252.    ** That way we don't need to let this tasklet busywait for min_cmd_delay
  253.    ** milliseconds.
  254.    **
  255.    ** TODO: check the value of "min_cmd_delay" against the value of HZ.
  256.    **   
  257.  */
  258. static void led_LCD_driver(unsigned char leds)
  259. {
  260. static int last_index; /* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */
  261. static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */
  262. struct lcd_block *block_ptr;
  263. int value;
  264. switch (last_index) {
  265.     case 0: block_ptr = &lcd_info.heartbeat;
  266. value = leds & LED_HEARTBEAT;
  267. break;
  268.     case 1: block_ptr = &lcd_info.disk_io;
  269. value = leds & LED_DISK_IO;
  270. break;
  271.     case 2: block_ptr = &lcd_info.lan_rcv;
  272. value = leds & LED_LAN_RCV;
  273. break;
  274.     case 3: block_ptr = &lcd_info.lan_tx;
  275. value = leds & LED_LAN_TX;
  276. break;
  277.     default: /* should never happen: */
  278. return;
  279. }
  280. if (last_was_cmd) {
  281.     /* write the value to the LCD data port */
  282.          gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );
  283. } else {
  284.     /* write the command-byte to the LCD command register */
  285.          gsc_writeb( block_ptr->command, LCD_CMD_REG );
  286. }    
  287. /* now update the vars for the next interrupt iteration */ 
  288. if (++last_was_cmd == 2) { /* switch between cmd & data */
  289.     last_was_cmd = 0;
  290.     if (++last_index == 4) 
  291. last_index = 0;  /* switch back to heartbeat index */
  292. }
  293. }
  294. /*
  295.    ** 
  296.    ** led_get_net_stats()
  297.    ** 
  298.    ** calculate the TX- & RX-troughput on the network interfaces in
  299.    ** the system for usage in the LED code
  300.    **
  301.    ** (analog to dev_get_info() from net/core/dev.c)
  302.    **   
  303.  */
  304. static unsigned long led_net_rx_counter, led_net_tx_counter;
  305. static void led_get_net_stats(int addvalue)
  306. {
  307. static unsigned long rx_total_last, tx_total_last;
  308. unsigned long rx_total, tx_total;
  309. struct net_device *dev;
  310. struct net_device_stats *stats;
  311. rx_total = tx_total = 0;
  312. /* we are running as a tasklet, so locking dev_base 
  313.  * for reading should be OK */
  314. read_lock(&dev_base_lock);
  315. for (dev = dev_base; dev != NULL; dev = dev->next) {
  316.     if (dev->get_stats) { 
  317.         stats = dev->get_stats(dev);
  318. rx_total += stats->rx_packets;
  319. tx_total += stats->tx_packets;
  320.     }
  321. }
  322. read_unlock(&dev_base_lock);
  323. rx_total -= rx_total_last;
  324. tx_total -= tx_total_last;
  325. if (rx_total)
  326.     led_net_rx_counter += CALC_ADD(rx_total, tx_total, addvalue);
  327. if (tx_total)
  328.     led_net_tx_counter += CALC_ADD(tx_total, rx_total, addvalue);
  329.         
  330. rx_total_last += rx_total;
  331.         tx_total_last += tx_total;
  332. }
  333. /*
  334.    ** 
  335.    ** led_get_diskio_stats()
  336.    ** 
  337.    ** calculate the disk-io througput in the system
  338.    ** (analog to linux/fs/proc/proc_misc.c)
  339.    **   
  340.  */
  341. static unsigned long led_diskio_counter;
  342. static void led_get_diskio_stats(int addvalue)
  343. {
  344. static unsigned int diskio_total_last, diskio_max;
  345. int major, disk, total;
  346. total = 0;
  347. for (major = 0; major < DK_MAX_MAJOR; major++) {
  348.     for (disk = 0; disk < DK_MAX_DISK; disk++)
  349. total += kstat.dk_drive[major][disk];
  350. }
  351. total -= diskio_total_last;
  352. if (total) {
  353.     if (total >= diskio_max) {
  354. led_diskio_counter += addvalue;
  355.         diskio_max = total; /* new maximum value found */ 
  356.     } else
  357. led_diskio_counter += CALC_ADD(total, diskio_max, addvalue);
  358. }
  359. diskio_total_last += total; 
  360. }
  361. /*
  362.    ** led_tasklet_func()
  363.    ** 
  364.    ** is scheduled at every timer interrupt from time.c and
  365.    ** updates the chassis LCD/LED 
  366.     TODO:
  367.     - display load average (older machines like 715/64 have 4 "free" LED's for that)
  368.     - optimizations
  369.  */
  370. static unsigned char currentleds; /* stores current value of the LEDs */
  371. #define HEARTBEAT_LEN (HZ*6/100)
  372. #define HEARTBEAT_2ND_RANGE_START (HZ*22/100)
  373. #define HEARTBEAT_2ND_RANGE_END   (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN)
  374. static void led_tasklet_func(unsigned long unused)
  375. {
  376. static unsigned int count, count_HZ;
  377. static unsigned char lastleds;
  378. /* exit if not initialized */
  379. if (!led_func_ptr)
  380.     return;
  381. /* increment the local counters */
  382. ++count;
  383. if (++count_HZ == HZ)
  384.     count_HZ = 0;
  385. if (led_heartbeat)
  386. {
  387. /* flash heartbeat-LED like a real heart (2 x short then a long delay) */
  388. if (count_HZ<HEARTBEAT_LEN || 
  389.     (count_HZ>=HEARTBEAT_2ND_RANGE_START && count_HZ<HEARTBEAT_2ND_RANGE_END)) 
  390.     currentleds |= LED_HEARTBEAT;
  391. else
  392.     currentleds &= ~LED_HEARTBEAT;
  393. }
  394. /* gather network and diskio statistics and flash LEDs respectively */
  395. if (led_lanrxtx)
  396. {
  397. if ((count & 31) == 0)
  398. led_get_net_stats(30);
  399. if (led_net_rx_counter) {
  400. led_net_rx_counter--;
  401. currentleds |= LED_LAN_RCV;
  402. }
  403. else    
  404. currentleds &= ~LED_LAN_RCV;
  405. if (led_net_tx_counter) {
  406. led_net_tx_counter--;
  407. currentleds |= LED_LAN_TX;
  408. }
  409. else    
  410. currentleds &= ~LED_LAN_TX;
  411. }
  412. if (led_diskio)
  413. {
  414. /* avoid to calculate diskio-stats at same irq as netio-stats ! */
  415. if ((count & 31) == 15) 
  416. led_get_diskio_stats(30);
  417. if (led_diskio_counter) {
  418. led_diskio_counter--;
  419. currentleds |= LED_DISK_IO;
  420. }
  421. else    
  422. currentleds &= ~LED_DISK_IO;
  423. }
  424. /* update the LCD/LEDs */
  425. if (currentleds != lastleds) {
  426.     led_func_ptr(currentleds);
  427.     lastleds = currentleds;
  428. }
  429. }
  430. /* main led tasklet struct (scheduled from time.c) */
  431. DECLARE_TASKLET_DISABLED(led_tasklet, led_tasklet_func, 0);
  432. /*
  433.    ** led_halt()
  434.    ** 
  435.    ** called by the reboot notifier chain at shutdown and stops all
  436.    ** LED/LCD activities.
  437.    ** 
  438.  */
  439. static int led_halt(struct notifier_block *, unsigned long, void *);
  440. static struct notifier_block led_notifier = {
  441. notifier_call: led_halt,
  442. };
  443. static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) 
  444. {
  445. char *txt;
  446. switch (event) {
  447. case SYS_RESTART: txt = "SYSTEM RESTART";
  448. break;
  449. case SYS_HALT: txt = "SYSTEM HALT";
  450. break;
  451. case SYS_POWER_OFF: txt = "SYSTEM POWER OFF";
  452. break;
  453. default: return NOTIFY_DONE;
  454. }
  455. /* completely stop the LED/LCD tasklet */
  456. tasklet_disable(&led_tasklet);
  457. if (lcd_info.model == DISPLAY_MODEL_LCD)
  458. lcd_print(txt);
  459. else
  460. if (led_func_ptr)
  461. led_func_ptr(0xff); /* turn all LEDs ON */
  462. unregister_reboot_notifier(&led_notifier);
  463. return NOTIFY_OK;
  464. }
  465. /*
  466.    ** register_led_driver()
  467.    ** 
  468.    ** registers an external LED or LCD for usage by this driver.
  469.    ** currently only LCD-, LASI- and ASP-style LCD/LED's are supported.
  470.    ** 
  471.  */
  472. int __init register_led_driver(int model, char *cmd_reg, char *data_reg)
  473. {
  474. static int initialized;
  475. if (initialized || !data_reg)
  476.     return 1;
  477. lcd_info.model = model; /* store the values */
  478. LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? NULL : cmd_reg;
  479. switch (lcd_info.model) {
  480. case DISPLAY_MODEL_LCD:
  481. LCD_DATA_REG = data_reg;
  482. printk(KERN_INFO "LCD display at %p,%p registeredn", 
  483. LCD_CMD_REG , LCD_DATA_REG);
  484. led_func_ptr = led_LCD_driver;
  485. lcd_print( "Linux " UTS_RELEASE );
  486. led_type = LED_HASLCD;
  487. break;
  488. case DISPLAY_MODEL_LASI:
  489. LED_DATA_REG = data_reg;
  490. led_func_ptr = led_LASI_driver;
  491. printk(KERN_INFO "LED display at %p registeredn", LED_DATA_REG);
  492. led_type = LED_NOLCD;
  493. break;
  494. case DISPLAY_MODEL_OLD_ASP:
  495. LED_DATA_REG = data_reg;
  496. led_func_ptr = led_ASP_driver;
  497. printk(KERN_INFO "LED (ASP-style) display at %p registeredn", 
  498.     LED_DATA_REG);
  499. led_type = LED_NOLCD;
  500. break;
  501. default:
  502. printk(KERN_ERR "%s: Wrong LCD/LED model %d !n",
  503.        __FUNCTION__, lcd_info.model);
  504. return 1;
  505. }
  506. /* mark the LCD/LED driver now as initialized and 
  507.  * register to the reboot notifier chain */
  508. initialized++;
  509. register_reboot_notifier(&led_notifier);
  510. /* start the led tasklet for the first time */
  511. tasklet_enable(&led_tasklet);
  512. return 0;
  513. }
  514. /*
  515.    ** register_led_regions()
  516.    ** 
  517.    ** register_led_regions() registers the LCD/LED regions for /procfs.
  518.    ** At bootup - where the initialisation of the LCD/LED normally happens - 
  519.    ** not all internal structures of request_region() are properly set up,
  520.    ** so that we delay the led-registration until after busdevices_init() 
  521.    ** has been executed.
  522.    **
  523.  */
  524. void __init register_led_regions(void)
  525. {
  526. switch (lcd_info.model) {
  527. case DISPLAY_MODEL_LCD:
  528. request_mem_region((unsigned long)LCD_CMD_REG,  1, "lcd_cmd");
  529. request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
  530. break;
  531. case DISPLAY_MODEL_LASI:
  532. case DISPLAY_MODEL_OLD_ASP:
  533. request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data");
  534. break;
  535. }
  536. }
  537. /*
  538.    ** 
  539.    ** lcd_print()
  540.    ** 
  541.    ** Displays the given string on the LCD-Display of newer machines.
  542.    ** lcd_print() disables the timer-based led tasklet during its 
  543.    ** execution and enables it afterwards again.
  544.    **
  545.  */
  546. int lcd_print( char *str )
  547. {
  548. int i;
  549. if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD)
  550.     return 0;
  551. /* temporarily disable the led tasklet */
  552. tasklet_disable(&led_tasklet);
  553. /* copy display string to buffer for procfs */
  554. strncpy(lcd_text, str, sizeof(lcd_text)-1);
  555. /* Set LCD Cursor to 1st character */
  556. gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG);
  557. udelay(lcd_info.min_cmd_delay);
  558. /* Print the string */
  559. for (i=0; i < lcd_info.lcd_width; i++) {
  560.     if (str && *str)
  561. gsc_writeb(*str++, LCD_DATA_REG);
  562.     else
  563. gsc_writeb(' ', LCD_DATA_REG);
  564.     udelay(lcd_info.min_cmd_delay);
  565. }
  566. /* re-enable the led tasklet */
  567. tasklet_enable(&led_tasklet);
  568. return lcd_info.lcd_width;
  569. }
  570. /*
  571.    ** led_init()
  572.    ** 
  573.    ** led_init() is called very early in the bootup-process from setup.c 
  574.    ** and asks the PDC for an usable chassis LCD or LED.
  575.    ** If the PDC doesn't return any info, then the LED
  576.    ** is detected by lasi.c or asp.c and registered with the
  577.    ** above functions lasi_led_init() or asp_led_init().
  578.    ** KittyHawk machines have often a buggy PDC, so that
  579.    ** we explicitly check for those machines here.
  580.  */
  581. int __init led_init(void)
  582. {
  583. struct pdc_chassis_info chassis_info;
  584. int ret;
  585. /* Work around the buggy PDC of KittyHawk-machines */
  586. switch (CPU_HVERSION) {
  587. case 0x580: /* KittyHawk DC2-100 (K100) */
  588. case 0x581: /* KittyHawk DC3-120 (K210) */
  589. case 0x582: /* KittyHawk DC3 100 (K400) */
  590. case 0x583: /* KittyHawk DC3 120 (K410) */
  591. case 0x58B: /* KittyHawk DC2 100 (K200) */
  592. printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, "
  593. "LED detection skipped.n", __FILE__, CPU_HVERSION);
  594. goto found; /* use the preinitialized values of lcd_info */
  595. }
  596. /* initialize the struct, so that we can check for valid return values */
  597. lcd_info.model = DISPLAY_MODEL_NONE;
  598. chassis_info.actcnt = chassis_info.maxcnt = 0;
  599. if ((ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info))) == PDC_OK) {
  600. DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), "
  601.  "lcd_width=%d, cmd_delay=%u,n"
  602.  "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ldn",
  603.          __FILE__, lcd_info.model,
  604.  (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" :
  605.   (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown",
  606.  lcd_info.lcd_width, lcd_info.min_cmd_delay,
  607.  __FILE__, sizeof(lcd_info), 
  608.  chassis_info.actcnt, chassis_info.maxcnt));
  609. DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%dn",
  610. __FILE__, lcd_info.lcd_cmd_reg_addr, 
  611. lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1,  
  612. lcd_info.reset_cmd2, lcd_info.act_enable ));
  613. /* check the results. Some machines have a buggy PDC */
  614. if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt)
  615. goto not_found;
  616. switch (lcd_info.model) {
  617. case DISPLAY_MODEL_LCD: /* LCD display */
  618. if (chassis_info.actcnt < 
  619. offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1)
  620. goto not_found;
  621. if (!lcd_info.act_enable) {
  622. DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.n"));
  623. goto not_found;
  624. }
  625. break;
  626. case DISPLAY_MODEL_NONE: /* no LED or LCD available */
  627. printk(KERN_INFO "PDC reported no LCD or LED.n");
  628. goto not_found;
  629. case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */
  630. if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32)
  631. goto not_found;
  632. break;
  633. default:
  634. printk(KERN_WARNING "PDC reported unknown LCD/LED model %dn",
  635.        lcd_info.model);
  636. goto not_found;
  637. } /* switch() */
  638. found:
  639. /* register the LCD/LED driver */
  640. register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG);
  641. return 0;
  642. } else { /* if() */
  643. DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %dn", ret));
  644. }
  645. not_found:
  646. lcd_info.model = DISPLAY_MODEL_NONE;
  647. return 1;
  648. }
  649. #ifdef CONFIG_PROC_FS
  650. module_init(led_create_procfs)
  651. #endif