- Alan Cox: synch. PA-RISC arch and bitops cleanups
[davej-history.git] / arch / parisc / kernel / led.c
blob6e6ee77688240905ea7ac3ed63166e703fd6f753
1 /*
2 * Chassis LCD/LED driver for HP-PARISC workstations
4 * (c) Copyright 2000 Red Hat Software
5 * (c) Copyright 2000 Helge Deller <hdeller@redhat.com>
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.
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>
28 /* define to disable all LED functions */
29 #undef DISABLE_LEDS
32 #define CPU_HVERSION ((boot_cpu_data.hversion >> 4) & 0x0FFF)
35 struct lcd_block {
36 unsigned char command; /* stores the command byte */
37 unsigned char on; /* value for turning LED on */
38 unsigned char off; /* value for turning LED off */
41 /* Structure returned by PDC_RETURN_CHASSIS_INFO */
42 struct pdc_chassis_lcd_info_ret_block {
43 unsigned long model:16; /* DISPLAY_MODEL_XXXX (see below) */
44 unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */
45 char *lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */
46 char *lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */
47 unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */
48 unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */
49 unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */
50 unsigned char act_enable; /* 0 = no activity (LCD only) */
51 struct lcd_block heartbeat;
52 struct lcd_block disk_io;
53 struct lcd_block lan_rcv;
54 struct lcd_block lan_tx;
55 char _pad;
58 /* values for pdc_chassis_lcd_info_ret_block.model: */
59 #define DISPLAY_MODEL_LCD 0 /* KittyHawk LED or LCD */
60 #define DISPLAY_MODEL_NONE 1 /* no LED or LCD */
61 #define DISPLAY_MODEL_LASI 2 /* LASI style 8 bit LED */
62 #define DISPLAY_MODEL_OLD_ASP 0x7F /* faked: ASP style 8 x 1 bit LED (only very old ASP versions) */
65 /* LCD_CMD and LCD_DATA for KittyHawk machines */
66 #ifdef __LP64__
67 #define KITTYHAWK_LCD_CMD 0xfffffffff0190000L
68 #else
69 #define KITTYHAWK_LCD_CMD 0xf0190000
70 #endif
71 #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1)
74 /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's */
75 static struct pdc_chassis_lcd_info_ret_block
76 lcd_info __attribute__((aligned(8))) =
78 model:DISPLAY_MODEL_LCD,
79 lcd_width:16,
80 lcd_cmd_reg_addr:(char *) KITTYHAWK_LCD_CMD,
81 lcd_data_reg_addr:(char *) KITTYHAWK_LCD_DATA,
82 min_cmd_delay:40,
83 reset_cmd1:0x80,
84 reset_cmd2:0xc0,
88 /* direct access to some of the lcd_info variables */
89 #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr
90 #define LCD_DATA_REG lcd_info.lcd_data_reg_addr
91 #define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */
97 **
98 ** led_ASP_driver()
99 **
101 #define LED_DATA 0x01 /* data to shift (0:on 1:off) */
102 #define LED_STROBE 0x02 /* strobe to clock data */
103 static void led_ASP_driver(unsigned char leds)
105 int i;
107 leds = ~leds;
108 for (i = 0; i < 8; i++) {
109 unsigned char value;
110 value = (leds & 0x80) >> 7;
111 gsc_writeb( value, LED_DATA_REG );
112 gsc_writeb( value | LED_STROBE, LED_DATA_REG );
113 leds <<= 1;
120 ** led_LASI_driver()
123 static void led_LASI_driver(unsigned char leds)
125 leds = ~leds;
126 gsc_writeb( leds, LED_DATA_REG );
132 ** led_LCD_driver()
134 ** The logic of the LCD driver is, that we write at every interrupt
135 ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers.
136 ** That way we don't need to let this interrupt routine busywait
137 ** the "min_cmd_delay", since idlewaiting in an interrupt-routine is
138 ** allways a BAD IDEA !
140 ** TODO: check the value of "min_cmd_delay" against the value of HZ.
144 static void led_LCD_driver(unsigned char leds)
146 static int last_index; /* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */
147 static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */
148 struct lcd_block *block_ptr;
149 int value;
151 // leds = ~leds; /* needed ? */
153 switch (last_index) {
154 case 0: block_ptr = &lcd_info.heartbeat;
155 value = leds & LED_HEARTBEAT;
156 break;
157 case 1: block_ptr = &lcd_info.disk_io;
158 value = leds & LED_DISK_IO;
159 break;
160 case 2: block_ptr = &lcd_info.lan_rcv;
161 value = leds & LED_LAN_RCV;
162 break;
163 case 3: block_ptr = &lcd_info.lan_tx;
164 value = leds & LED_LAN_TX;
165 break;
166 default: /* should never happen: */
167 BUG();
168 return;
171 if (last_was_cmd) {
172 /* write the value to the LCD data port */
173 gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );
174 } else {
175 /* write the command-byte to the LCD command register */
176 gsc_writeb( block_ptr->command, LCD_CMD_REG );
179 /* now update the vars for the next interrupt iteration */
180 if (++last_was_cmd == 2) {
181 last_was_cmd = 0;
182 if (++last_index == 4)
183 last_index = 0;
189 static char currentleds; /* stores current value of the LEDs */
191 static void (*led_func_ptr) (unsigned char); /* ptr to LCD/LED-specific function */
194 ** led_interrupt_func()
196 ** is called at every timer interrupt from time.c,
197 ** updates the chassis LCD/LED
200 #define HEARTBEAT_LEN (HZ/16)
202 void led_interrupt_func(void)
204 #ifndef DISABLE_LEDS
205 static int count;
206 static int lastleds = -1;
207 static int nr;
209 /* exit, if not initialized */
210 if (!led_func_ptr)
211 return;
213 /* increment the local counter */
214 if (count == (HZ-1))
215 count = 0;
216 else
217 count++;
219 /* calculate the Heartbeat */
220 if ((count % (HZ/2)) < HEARTBEAT_LEN)
221 currentleds |= LED_HEARTBEAT;
222 else
223 currentleds &= ~LED_HEARTBEAT;
225 /* roll LEDs 0..2 */
226 if (count == 0) {
227 if (nr++ >= 2)
228 nr = 0;
229 currentleds &= ~7;
230 currentleds |= (1 << nr);
233 /* now update the LEDs */
234 if (currentleds != lastleds) {
235 led_func_ptr(currentleds);
236 lastleds = currentleds;
238 #endif
243 ** register_led_driver()
245 ** All information in lcd_info needs to be set up prior
246 ** calling this function.
249 static void __init register_led_driver(void)
251 #ifndef DISABLE_LEDS
252 switch (lcd_info.model) {
253 case DISPLAY_MODEL_LCD:
254 printk(KERN_INFO "LCD display at (%p,%p)\n",
255 LCD_CMD_REG , LCD_DATA_REG);
256 led_func_ptr = led_LCD_driver;
257 break;
259 case DISPLAY_MODEL_LASI:
260 printk(KERN_INFO "LED display at %p\n",
261 LED_DATA_REG);
262 led_func_ptr = led_LASI_driver;
263 break;
265 case DISPLAY_MODEL_OLD_ASP:
266 printk(KERN_INFO "LED (ASP-style) display at %p\n",
267 LED_DATA_REG);
268 led_func_ptr = led_ASP_driver;
269 break;
271 default:
272 printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n",
273 __FUNCTION__, lcd_info.model);
274 return;
276 #endif
280 * XXX - could this move to lasi.c ??
284 ** lasi_led_init()
286 ** lasi_led_init() is called from lasi.c with the base hpa
287 ** of the lasi controller chip.
288 ** Since Mirage and Electra machines use a different LED
289 ** address register, we need to check for these machines
290 ** explicitly.
293 #ifdef CONFIG_GSC_LASI
294 void __init lasi_led_init(unsigned long lasi_hpa)
296 if (lcd_info.model != DISPLAY_MODEL_NONE ||
297 lasi_hpa == 0)
298 return;
300 printk("%s: CPU_HVERSION %x\n", __FUNCTION__, CPU_HVERSION);
302 /* Mirage and Electra machines need special offsets */
303 switch (CPU_HVERSION) {
304 case 0x60A: /* Mirage Jr (715/64) */
305 case 0x60B: /* Mirage 100 */
306 case 0x60C: /* Mirage 100+ */
307 case 0x60D: /* Electra 100 */
308 case 0x60E: /* Electra 120 */
309 LED_DATA_REG = (char *) (lasi_hpa - 0x00020000);
310 break;
311 default:
312 LED_DATA_REG = (char *) (lasi_hpa + 0x0000C000);
313 break;
314 } /* switch() */
316 lcd_info.model = DISPLAY_MODEL_LASI;
317 register_led_driver();
319 #endif
323 ** asp_led_init()
325 ** asp_led_init() is called from asp.c with the ptr
326 ** to the LED display.
329 #ifdef CONFIG_GSC_LASI
330 void __init asp_led_init(unsigned long led_ptr)
332 if (lcd_info.model != DISPLAY_MODEL_NONE ||
333 led_ptr == 0)
334 return;
336 lcd_info.model = DISPLAY_MODEL_OLD_ASP;
337 LED_DATA_REG = (char *) led_ptr;
339 register_led_driver();
342 #endif
347 ** register_led_regions()
349 ** Simple function, which registers the LCD/LED regions for /procfs.
350 ** At bootup - where the initialisation of the LCD/LED normally happens -
351 ** not all internal structures of request_region() are properly set up,
352 ** so that we delay the registration until busdevice.c is executed.
356 void __init register_led_regions(void)
358 switch (lcd_info.model) {
359 case DISPLAY_MODEL_LCD:
360 request_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd");
361 request_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
362 break;
363 case DISPLAY_MODEL_LASI:
364 case DISPLAY_MODEL_OLD_ASP:
365 request_region((unsigned long)LED_DATA_REG, 1, "led_data");
366 break;
373 ** led_init()
375 ** led_init() is called very early in the bootup-process from setup.c
376 ** and asks the PDC for an usable chassis LCD or LED.
377 ** If the PDC doesn't return any info, then the LED
378 ** is detected by lasi.c or asp.c and registered with the
379 ** above functions lasi_led_init() or asp_led_init().
380 ** KittyHawk machines have often a buggy PDC, so that
381 ** we explicitly check for those machines here.
384 int __init led_init(void)
386 #ifndef DISABLE_LEDS
387 long pdc_result[32];
389 printk("%s: CPU_HVERSION %x\n", __FUNCTION__, CPU_HVERSION);
391 /* Work around the buggy PDC of KittyHawk-machines */
392 switch (CPU_HVERSION) {
393 case 0x580: /* KittyHawk DC2-100 (K100) */
394 case 0x581: /* KittyHawk DC3-120 (K210) */
395 case 0x582: /* KittyHawk DC3 100 (K400) */
396 case 0x583: /* KittyHawk DC3 120 (K410) */
397 case 0x58B: /* KittyHawk DC2 100 (K200) */
398 printk("%s: KittyHawk-Machine found !!\n", __FUNCTION__);
399 goto found; /* use the preinitialized values of lcd_info */
401 default:
402 break;
405 /* initialize pdc_result, so we can check the return values of pdc_chassis_info() */
406 pdc_result[0] = pdc_result[1] = 0;
408 if (pdc_chassis_info(&pdc_result, &lcd_info, sizeof(lcd_info)) == PDC_OK) {
409 printk("%s: chassis info: model %d, ret0=%d, ret1=%d\n",
410 __FUNCTION__, lcd_info.model, pdc_result[0], pdc_result[1]);
412 /* check the results. Some machines have a buggy PDC */
413 if (pdc_result[0] <= 0 || pdc_result[0] != pdc_result[1])
414 goto not_found;
416 switch (lcd_info.model) {
417 case DISPLAY_MODEL_LCD: /* LCD display */
418 if (pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block)
419 && pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block) - 1)
420 goto not_found;
421 printk("%s: min_cmd_delay = %d uS\n",
422 __FUNCTION__, lcd_info.min_cmd_delay);
423 break;
425 case DISPLAY_MODEL_NONE: /* no LED or LCD available */
426 goto not_found;
428 case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */
429 if (pdc_result[0] != 8 && pdc_result[0] != 32)
430 goto not_found;
431 break;
433 default:
434 printk(KERN_WARNING "Unknown LCD/LED model %d\n",
435 lcd_info.model);
436 goto not_found;
437 } /* switch() */
439 found:
440 /* register the LCD/LED driver */
441 register_led_driver();
442 return 0;
444 } /* if() */
446 not_found:
447 lcd_info.model = DISPLAY_MODEL_NONE;
448 return 1;
449 #endif