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>
22 #include <asm/processor.h>
23 #include <asm/hardware.h>
24 #include <asm/param.h> /* HZ */
28 /* define to disable all LED functions */
32 #define CPU_HVERSION ((boot_cpu_data.hversion >> 4) & 0x0FFF)
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
;
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 */
67 #define KITTYHAWK_LCD_CMD 0xfffffffff0190000L
69 #define KITTYHAWK_LCD_CMD 0xf0190000
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
,
80 lcd_cmd_reg_addr
:(char *) KITTYHAWK_LCD_CMD
,
81 lcd_data_reg_addr
:(char *) KITTYHAWK_LCD_DATA
,
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 */
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
)
108 for (i
= 0; i
< 8; i
++) {
110 value
= (leds
& 0x80) >> 7;
111 gsc_writeb( value
, LED_DATA_REG
);
112 gsc_writeb( value
| LED_STROBE
, LED_DATA_REG
);
123 static void led_LASI_driver(unsigned char leds
)
126 gsc_writeb( leds
, LED_DATA_REG
);
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
;
151 // leds = ~leds; /* needed ? */
153 switch (last_index
) {
154 case 0: block_ptr
= &lcd_info
.heartbeat
;
155 value
= leds
& LED_HEARTBEAT
;
157 case 1: block_ptr
= &lcd_info
.disk_io
;
158 value
= leds
& LED_DISK_IO
;
160 case 2: block_ptr
= &lcd_info
.lan_rcv
;
161 value
= leds
& LED_LAN_RCV
;
163 case 3: block_ptr
= &lcd_info
.lan_tx
;
164 value
= leds
& LED_LAN_TX
;
166 default: /* should never happen: */
172 /* write the value to the LCD data port */
173 gsc_writeb( value
? block_ptr
->on
: block_ptr
->off
, LCD_DATA_REG
);
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) {
182 if (++last_index
== 4)
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)
206 static int lastleds
= -1;
209 /* exit, if not initialized */
213 /* increment the local counter */
219 /* calculate the Heartbeat */
220 if ((count
% (HZ
/2)) < HEARTBEAT_LEN
)
221 currentleds
|= LED_HEARTBEAT
;
223 currentleds
&= ~LED_HEARTBEAT
;
230 currentleds
|= (1 << nr
);
233 /* now update the LEDs */
234 if (currentleds
!= lastleds
) {
235 led_func_ptr(currentleds
);
236 lastleds
= currentleds
;
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)
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
;
259 case DISPLAY_MODEL_LASI
:
260 printk(KERN_INFO
"LED display at %p\n",
262 led_func_ptr
= led_LASI_driver
;
265 case DISPLAY_MODEL_OLD_ASP
:
266 printk(KERN_INFO
"LED (ASP-style) display at %p\n",
268 led_func_ptr
= led_ASP_driver
;
272 printk(KERN_ERR
"%s: Wrong LCD/LED model %d !\n",
273 __FUNCTION__
, lcd_info
.model
);
280 * XXX - could this move to lasi.c ??
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
293 #ifdef CONFIG_GSC_LASI
294 void __init
lasi_led_init(unsigned long lasi_hpa
)
296 if (lcd_info
.model
!= DISPLAY_MODEL_NONE
||
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);
312 LED_DATA_REG
= (char *) (lasi_hpa
+ 0x0000C000);
316 lcd_info
.model
= DISPLAY_MODEL_LASI
;
317 register_led_driver();
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
||
336 lcd_info
.model
= DISPLAY_MODEL_OLD_ASP
;
337 LED_DATA_REG
= (char *) led_ptr
;
339 register_led_driver();
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");
363 case DISPLAY_MODEL_LASI
:
364 case DISPLAY_MODEL_OLD_ASP
:
365 request_region((unsigned long)LED_DATA_REG
, 1, "led_data");
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)
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 */
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])
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)
421 printk("%s: min_cmd_delay = %d uS\n",
422 __FUNCTION__
, lcd_info
.min_cmd_delay
);
425 case DISPLAY_MODEL_NONE
: /* no LED or LCD available */
428 case DISPLAY_MODEL_LASI
: /* Lasi style 8 bit LED display */
429 if (pdc_result
[0] != 8 && pdc_result
[0] != 32)
434 printk(KERN_WARNING
"Unknown LCD/LED model %d\n",
440 /* register the LCD/LED driver */
441 register_led_driver();
447 lcd_info
.model
= DISPLAY_MODEL_NONE
;