led.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:11k
源码类别:

嵌入式Linux

开发平台:

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.  *
  7.  *      This program is free software; you can redistribute it and/or modify
  8.  *      it under the terms of the GNU General Public License as published by
  9.  *      the Free Software Foundation; either version 2 of the License, or
  10.  *      (at your option) any later version.
  11.  *
  12.  */
  13. #include <linux/config.h>
  14. #include <linux/init.h>
  15. #include <linux/kernel_stat.h>
  16. #include <linux/types.h>
  17. #include <linux/interrupt.h>
  18. #include <linux/ioport.h>
  19. #include <linux/bitops.h>
  20. #include <asm/io.h>
  21. #include <asm/gsc.h>
  22. #include <asm/processor.h>
  23. #include <asm/hardware.h>
  24. #include <asm/param.h> /* HZ */
  25. #include <asm/led.h>
  26. /* define to disable all LED functions */
  27. #undef  DISABLE_LEDS
  28. #define CPU_HVERSION ((boot_cpu_data.hversion >> 4) & 0x0FFF)
  29. struct lcd_block {
  30. unsigned char command; /* stores the command byte      */
  31. unsigned char on; /* value for turning LED on     */
  32. unsigned char off; /* value for turning LED off    */
  33. };
  34. /* Structure returned by PDC_RETURN_CHASSIS_INFO */
  35. struct pdc_chassis_lcd_info_ret_block {
  36. unsigned long model:16; /* DISPLAY_MODEL_XXXX (see below)  */
  37. unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */
  38. char *lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */
  39. char *lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */
  40. unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */
  41. unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */
  42. unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */
  43. unsigned char act_enable; /* 0 = no activity (LCD only) */
  44. struct lcd_block heartbeat;
  45. struct lcd_block disk_io;
  46. struct lcd_block lan_rcv;
  47. struct lcd_block lan_tx;
  48. char _pad;
  49. };
  50. /* values for pdc_chassis_lcd_info_ret_block.model: */
  51. #define DISPLAY_MODEL_LCD  0 /* KittyHawk LED or LCD */
  52. #define DISPLAY_MODEL_NONE 1 /* no LED or LCD */
  53. #define DISPLAY_MODEL_LASI 2 /* LASI style 8 bit LED */
  54. #define DISPLAY_MODEL_OLD_ASP 0x7F /* faked: ASP style 8 x 1 bit LED (only very old ASP versions) */
  55. /* LCD_CMD and LCD_DATA for KittyHawk machines */
  56. #ifdef __LP64__
  57. #define KITTYHAWK_LCD_CMD 0xfffffffff0190000L
  58. #else
  59. #define KITTYHAWK_LCD_CMD 0xf0190000
  60. #endif
  61. #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1)
  62. /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's */
  63. static struct pdc_chassis_lcd_info_ret_block
  64. lcd_info __attribute__((aligned(8))) =
  65. {
  66.       model:DISPLAY_MODEL_LCD,
  67.       lcd_width:16,
  68.       lcd_cmd_reg_addr:(char *) KITTYHAWK_LCD_CMD,
  69.       lcd_data_reg_addr:(char *) KITTYHAWK_LCD_DATA,
  70.       min_cmd_delay:40,
  71.       reset_cmd1:0x80,
  72.       reset_cmd2:0xc0,
  73. };
  74. /* direct access to some of the lcd_info variables */
  75. #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr  
  76. #define LCD_DATA_REG lcd_info.lcd_data_reg_addr  
  77. #define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */
  78. /*
  79.    ** 
  80.    ** led_ASP_driver()
  81.    ** 
  82.  */
  83. #define LED_DATA 0x01 /* data to shift (0:on 1:off) */
  84. #define LED_STROBE 0x02 /* strobe to clock data */
  85. static void led_ASP_driver(unsigned char leds)
  86. {
  87. int i;
  88. leds = ~leds;
  89. for (i = 0; i < 8; i++) {
  90. unsigned char value;
  91. value = (leds & 0x80) >> 7;
  92. gsc_writeb( value,  LED_DATA_REG );
  93. gsc_writeb( value | LED_STROBE,  LED_DATA_REG );
  94. leds <<= 1;
  95. }
  96. }
  97. /*
  98.    ** 
  99.    ** led_LASI_driver()
  100.    ** 
  101.  */
  102. static void led_LASI_driver(unsigned char leds)
  103. {
  104. leds = ~leds;
  105. gsc_writeb( leds, LED_DATA_REG );
  106. }
  107. /*
  108.    ** 
  109.    ** led_LCD_driver()
  110.    ** 
  111.    ** The logic of the LCD driver is, that we write at every interrupt
  112.    ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers.
  113.    ** That way we don't need to let this interrupt routine busywait
  114.    ** the "min_cmd_delay", since idlewaiting in an interrupt-routine is
  115.    ** allways a BAD IDEA !
  116.    **
  117.    ** TODO: check the value of "min_cmd_delay" against the value of HZ.
  118.    **   
  119.  */
  120. static void led_LCD_driver(unsigned char leds)
  121. {
  122. static int last_index; /* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */
  123. static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */
  124. struct lcd_block *block_ptr;
  125. int value;
  126.   // leds = ~leds; /* needed ? */ 
  127. switch (last_index) {
  128.     case 0: block_ptr = &lcd_info.heartbeat;
  129. value = leds & LED_HEARTBEAT;
  130. break;
  131.     case 1: block_ptr = &lcd_info.disk_io;
  132. value = leds & LED_DISK_IO;
  133. break;
  134.     case 2: block_ptr = &lcd_info.lan_rcv;
  135. value = leds & LED_LAN_RCV;
  136. break;
  137.     case 3: block_ptr = &lcd_info.lan_tx;
  138. value = leds & LED_LAN_TX;
  139. break;
  140.     default: /* should never happen: */
  141. BUG();
  142. return;
  143. }
  144. if (last_was_cmd) {
  145.     /* write the value to the LCD data port */
  146.          gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );
  147. } else {
  148.     /* write the command-byte to the LCD command register */
  149.          gsc_writeb( block_ptr->command, LCD_CMD_REG );
  150. }    
  151. /* now update the vars for the next interrupt iteration */ 
  152. if (++last_was_cmd == 2) {
  153.     last_was_cmd = 0;
  154.     if (++last_index == 4)
  155. last_index = 0;
  156. }
  157. }
  158. static char currentleds; /* stores current value of the LEDs */
  159. static void (*led_func_ptr) (unsigned char); /* ptr to LCD/LED-specific function */
  160. /*
  161.    ** led_interrupt_func()
  162.    ** 
  163.    ** is called at every timer interrupt from time.c,
  164.    ** updates the chassis LCD/LED 
  165.  */
  166. #define HEARTBEAT_LEN (HZ/16)
  167. void led_interrupt_func(void)
  168. {
  169. #ifndef DISABLE_LEDS
  170. static int count;
  171. static int lastleds = -1;
  172. static int nr;
  173. /* exit, if not initialized */
  174. if (!led_func_ptr)
  175.     return;
  176. /* increment the local counter */
  177. if (count == (HZ-1))
  178.     count = 0;
  179. else
  180.     count++;
  181. /* calculate the Heartbeat */
  182. if ((count % (HZ/2)) < HEARTBEAT_LEN) 
  183.     currentleds |= LED_HEARTBEAT;
  184. else
  185.     currentleds &= ~LED_HEARTBEAT;
  186. /* roll LEDs 0..2 */
  187. if (count == 0) {
  188.     if (nr++ >= 2) 
  189. nr = 0;
  190.     currentleds &= ~7;
  191.     currentleds |= (1 << nr);
  192. }
  193. /* now update the LEDs */
  194. if (currentleds != lastleds) {
  195.     led_func_ptr(currentleds);
  196.     lastleds = currentleds;
  197. }
  198. #endif
  199. }
  200. /*
  201.    ** register_led_driver()
  202.    ** 
  203.    ** All information in lcd_info needs to be set up prior
  204.    ** calling this function. 
  205.  */
  206. static void __init register_led_driver(void)
  207. {
  208. #ifndef DISABLE_LEDS
  209. switch (lcd_info.model) {
  210. case DISPLAY_MODEL_LCD:
  211. printk(KERN_INFO "LCD display at (%p,%p)n",
  212.   LCD_CMD_REG , LCD_DATA_REG);
  213. led_func_ptr = led_LCD_driver;
  214. break;
  215. case DISPLAY_MODEL_LASI:
  216. printk(KERN_INFO "LED display at %pn",
  217.        LED_DATA_REG);
  218. led_func_ptr = led_LASI_driver;
  219. break;
  220. case DISPLAY_MODEL_OLD_ASP:
  221. printk(KERN_INFO "LED (ASP-style) display at %pn",
  222.        LED_DATA_REG);
  223. led_func_ptr = led_ASP_driver;
  224. break;
  225. default:
  226. printk(KERN_ERR "%s: Wrong LCD/LED model %d !n",
  227.        __FUNCTION__, lcd_info.model);
  228. return;
  229. }
  230. #endif
  231. }
  232. /*
  233.  * XXX - could this move to lasi.c ??
  234.  */
  235. /*
  236.    ** lasi_led_init()
  237.    ** 
  238.    ** lasi_led_init() is called from lasi.c with the base hpa  
  239.    ** of the lasi controller chip. 
  240.    ** Since Mirage and Electra machines use a different LED
  241.    ** address register, we need to check for these machines 
  242.    ** explicitly.
  243.  */
  244. #ifdef CONFIG_GSC_LASI
  245. void __init lasi_led_init(unsigned long lasi_hpa)
  246. {
  247. if (lcd_info.model != DISPLAY_MODEL_NONE ||
  248.     lasi_hpa == 0)
  249. return;
  250. printk("%s: CPU_HVERSION %xn", __FUNCTION__, CPU_HVERSION);
  251. /* Mirage and Electra machines need special offsets */
  252. switch (CPU_HVERSION) {
  253. case 0x60A: /* Mirage Jr (715/64) */
  254. case 0x60B: /* Mirage 100 */
  255. case 0x60C: /* Mirage 100+ */
  256. case 0x60D: /* Electra 100 */
  257. case 0x60E: /* Electra 120 */
  258. LED_DATA_REG = (char *) (lasi_hpa - 0x00020000);
  259. break;
  260. default:
  261. LED_DATA_REG = (char *) (lasi_hpa + 0x0000C000);
  262. break;
  263. } /* switch() */
  264. lcd_info.model = DISPLAY_MODEL_LASI;
  265. register_led_driver();
  266. }
  267. #endif
  268. /*
  269.    ** asp_led_init()
  270.    ** 
  271.    ** asp_led_init() is called from asp.c with the ptr 
  272.    ** to the LED display.
  273.  */
  274. #ifdef CONFIG_GSC_LASI
  275. void __init asp_led_init(unsigned long led_ptr)
  276. {
  277. if (lcd_info.model != DISPLAY_MODEL_NONE ||
  278.     led_ptr == 0)
  279. return;
  280. lcd_info.model = DISPLAY_MODEL_OLD_ASP;
  281. LED_DATA_REG = (char *) led_ptr;
  282. register_led_driver();
  283. }
  284. #endif
  285. /*
  286.    ** register_led_regions()
  287.    ** 
  288.    ** Simple function, which registers the LCD/LED regions for /procfs.
  289.    ** At bootup - where the initialisation of the LCD/LED normally happens - 
  290.    ** not all internal structures of request_region() are properly set up,
  291.    ** so that we delay the registration until busdevice.c is executed.
  292.    **
  293.  */
  294. void __init register_led_regions(void)
  295. {
  296. switch (lcd_info.model) {
  297. case DISPLAY_MODEL_LCD:
  298. request_region((unsigned long)LCD_CMD_REG,  1, "lcd_cmd");
  299. request_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
  300. break;
  301. case DISPLAY_MODEL_LASI:
  302. case DISPLAY_MODEL_OLD_ASP:
  303. request_region((unsigned long)LED_DATA_REG, 1, "led_data");
  304. break;
  305. }
  306. }
  307. /*
  308.    ** led_init()
  309.    ** 
  310.    ** led_init() is called very early in the bootup-process from setup.c 
  311.    ** and asks the PDC for an usable chassis LCD or LED.
  312.    ** If the PDC doesn't return any info, then the LED
  313.    ** is detected by lasi.c or asp.c and registered with the
  314.    ** above functions lasi_led_init() or asp_led_init().
  315.    ** KittyHawk machines have often a buggy PDC, so that
  316.    ** we explicitly check for those machines here.
  317.  */
  318. int __init led_init(void)
  319. {
  320. #ifndef DISABLE_LEDS
  321. long pdc_result[32];
  322. printk("%s: CPU_HVERSION %xn", __FUNCTION__, CPU_HVERSION);
  323. /* Work around the buggy PDC of KittyHawk-machines */
  324. switch (CPU_HVERSION) {
  325. case 0x580: /* KittyHawk DC2-100 (K100) */
  326. case 0x581: /* KittyHawk DC3-120 (K210) */
  327. case 0x582: /* KittyHawk DC3 100 (K400) */
  328. case 0x583: /* KittyHawk DC3 120 (K410) */
  329. case 0x58B: /* KittyHawk DC2 100 (K200) */
  330. printk("%s: KittyHawk-Machine found !!n", __FUNCTION__);
  331. goto found; /* use the preinitialized values of lcd_info */
  332. default:
  333. break;
  334. }
  335. /* initialize pdc_result, so we can check the return values of pdc_chassis_info() */
  336. pdc_result[0] = pdc_result[1] = 0;
  337. if (pdc_chassis_info(&pdc_result, &lcd_info, sizeof(lcd_info)) == PDC_OK) {
  338. printk("%s: chassis info: model %d, ret0=%d, ret1=%dn",
  339.  __FUNCTION__, lcd_info.model, pdc_result[0], pdc_result[1]);
  340. /* check the results. Some machines have a buggy PDC */
  341. if (pdc_result[0] <= 0 || pdc_result[0] != pdc_result[1])
  342. goto not_found;
  343. switch (lcd_info.model) {
  344. case DISPLAY_MODEL_LCD: /* LCD display */
  345. if (pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block)
  346.     && pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block) - 1)
  347.  goto not_found;
  348. printk("%s: min_cmd_delay = %d uSn",
  349.              __FUNCTION__, lcd_info.min_cmd_delay);
  350. break;
  351. case DISPLAY_MODEL_NONE: /* no LED or LCD available */
  352. goto not_found;
  353. case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */
  354. if (pdc_result[0] != 8 && pdc_result[0] != 32)
  355. goto not_found;
  356. break;
  357. default:
  358. printk(KERN_WARNING "Unknown LCD/LED model %dn",
  359.        lcd_info.model);
  360. goto not_found;
  361. } /* switch() */
  362. found:
  363. /* register the LCD/LED driver */
  364. register_led_driver();
  365. return 0;
  366. } /* if() */
  367. not_found:
  368. lcd_info.model = DISPLAY_MODEL_NONE;
  369. return 1;
  370. #endif
  371. }