Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *    Chassis LCD/LED driver for HP-PARISC workstations
0004  *
0005  *      (c) Copyright 2000 Red Hat Software
0006  *      (c) Copyright 2000 Helge Deller <hdeller@redhat.com>
0007  *      (c) Copyright 2001-2009 Helge Deller <deller@gmx.de>
0008  *      (c) Copyright 2001 Randolph Chung <tausq@debian.org>
0009  *
0010  * TODO:
0011  *  - speed-up calculations with inlined assembler
0012  *  - interface to write to second row of LCD from /proc (if technically possible)
0013  *
0014  * Changes:
0015  *      - Audit copy_from_user in led_proc_write.
0016  *                                Daniele Bellucci <bellucda@tiscali.it>
0017  *  - Switch from using a tasklet to a work queue, so the led_LCD_driver
0018  *      can sleep.
0019  *                David Pye <dmp@davidmpye.dyndns.org>
0020  */
0021 
0022 #include <linux/module.h>
0023 #include <linux/stddef.h>   /* for offsetof() */
0024 #include <linux/init.h>
0025 #include <linux/types.h>
0026 #include <linux/ioport.h>
0027 #include <linux/utsname.h>
0028 #include <linux/capability.h>
0029 #include <linux/delay.h>
0030 #include <linux/netdevice.h>
0031 #include <linux/inetdevice.h>
0032 #include <linux/in.h>
0033 #include <linux/interrupt.h>
0034 #include <linux/kernel_stat.h>
0035 #include <linux/reboot.h>
0036 #include <linux/proc_fs.h>
0037 #include <linux/seq_file.h>
0038 #include <linux/ctype.h>
0039 #include <linux/blkdev.h>
0040 #include <linux/workqueue.h>
0041 #include <linux/rcupdate.h>
0042 #include <asm/io.h>
0043 #include <asm/processor.h>
0044 #include <asm/hardware.h>
0045 #include <asm/param.h>      /* HZ */
0046 #include <asm/led.h>
0047 #include <asm/pdc.h>
0048 #include <linux/uaccess.h>
0049 
0050 /* The control of the LEDs and LCDs on PARISC-machines have to be done 
0051    completely in software. The necessary calculations are done in a work queue
0052    task which is scheduled regularly, and since the calculations may consume a 
0053    relatively large amount of CPU time, some of the calculations can be 
0054    turned off with the following variables (controlled via procfs) */
0055 
0056 static int led_type __read_mostly = -1;
0057 static unsigned char lastleds;  /* LED state from most recent update */
0058 static unsigned int led_heartbeat __read_mostly = 1;
0059 static unsigned int led_diskio    __read_mostly = 1;
0060 static unsigned int led_lanrxtx   __read_mostly = 1;
0061 static char lcd_text[32]          __read_mostly;
0062 static char lcd_text_default[32]  __read_mostly;
0063 static int  lcd_no_led_support    __read_mostly = 0; /* KittyHawk doesn't support LED on its LCD */
0064 
0065 
0066 static struct workqueue_struct *led_wq;
0067 static void led_work_func(struct work_struct *);
0068 static DECLARE_DELAYED_WORK(led_task, led_work_func);
0069 
0070 #if 0
0071 #define DPRINTK(x)  printk x
0072 #else
0073 #define DPRINTK(x)
0074 #endif
0075 
0076 struct lcd_block {
0077     unsigned char command;  /* stores the command byte      */
0078     unsigned char on;   /* value for turning LED on     */
0079     unsigned char off;  /* value for turning LED off    */
0080 };
0081 
0082 /* Structure returned by PDC_RETURN_CHASSIS_INFO */
0083 /* NOTE: we use unsigned long:16 two times, since the following member 
0084    lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */
0085 struct pdc_chassis_lcd_info_ret_block {
0086     unsigned long model:16;     /* DISPLAY_MODEL_XXXX */
0087     unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */
0088     unsigned long lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */
0089     unsigned long lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */
0090     unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */
0091     unsigned char reset_cmd1;   /* command #1 for writing LCD string (LCD only) */
0092     unsigned char reset_cmd2;   /* command #2 for writing LCD string (LCD only) */
0093     unsigned char act_enable;   /* 0 = no activity (LCD only) */
0094     struct lcd_block heartbeat;
0095     struct lcd_block disk_io;
0096     struct lcd_block lan_rcv;
0097     struct lcd_block lan_tx;
0098     char _pad;
0099 };
0100 
0101 
0102 /* LCD_CMD and LCD_DATA for KittyHawk machines */
0103 #define KITTYHAWK_LCD_CMD  F_EXTEND(0xf0190000UL) /* 64bit-ready */
0104 #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1)
0105 
0106 /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 
0107  * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */
0108 static struct pdc_chassis_lcd_info_ret_block
0109 lcd_info __attribute__((aligned(8))) __read_mostly =
0110 {
0111     .model =        DISPLAY_MODEL_LCD,
0112     .lcd_width =        16,
0113     .lcd_cmd_reg_addr = KITTYHAWK_LCD_CMD,
0114     .lcd_data_reg_addr =    KITTYHAWK_LCD_DATA,
0115     .min_cmd_delay =    80,
0116     .reset_cmd1 =       0x80,
0117     .reset_cmd2 =       0xc0,
0118 };
0119 
0120 
0121 /* direct access to some of the lcd_info variables */
0122 #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr    
0123 #define LCD_DATA_REG    lcd_info.lcd_data_reg_addr   
0124 #define LED_DATA_REG    lcd_info.lcd_cmd_reg_addr   /* LASI & ASP only */
0125 
0126 #define LED_HASLCD 1
0127 #define LED_NOLCD  0
0128 
0129 /* The workqueue must be created at init-time */
0130 static int start_task(void) 
0131 {   
0132     /* Display the default text now */
0133     if (led_type == LED_HASLCD) lcd_print( lcd_text_default );
0134 
0135     /* KittyHawk has no LED support on its LCD */
0136     if (lcd_no_led_support) return 0;
0137 
0138     /* Create the work queue and queue the LED task */
0139     led_wq = create_singlethread_workqueue("led_wq");   
0140     queue_delayed_work(led_wq, &led_task, 0);
0141 
0142     return 0;
0143 }
0144 
0145 device_initcall(start_task);
0146 
0147 /* ptr to LCD/LED-specific function */
0148 static void (*led_func_ptr) (unsigned char) __read_mostly;
0149 
0150 #ifdef CONFIG_PROC_FS
0151 static int led_proc_show(struct seq_file *m, void *v)
0152 {
0153     switch ((long)m->private)
0154     {
0155     case LED_NOLCD:
0156         seq_printf(m, "Heartbeat: %d\n", led_heartbeat);
0157         seq_printf(m, "Disk IO: %d\n", led_diskio);
0158         seq_printf(m, "LAN Rx/Tx: %d\n", led_lanrxtx);
0159         break;
0160     case LED_HASLCD:
0161         seq_printf(m, "%s\n", lcd_text);
0162         break;
0163     default:
0164         return 0;
0165     }
0166     return 0;
0167 }
0168 
0169 static int led_proc_open(struct inode *inode, struct file *file)
0170 {
0171     return single_open(file, led_proc_show, pde_data(inode));
0172 }
0173 
0174 
0175 static ssize_t led_proc_write(struct file *file, const char __user *buf,
0176     size_t count, loff_t *pos)
0177 {
0178     void *data = pde_data(file_inode(file));
0179     char *cur, lbuf[32];
0180     int d;
0181 
0182     if (!capable(CAP_SYS_ADMIN))
0183         return -EACCES;
0184 
0185     if (count >= sizeof(lbuf))
0186         count = sizeof(lbuf)-1;
0187 
0188     if (copy_from_user(lbuf, buf, count))
0189         return -EFAULT;
0190     lbuf[count] = 0;
0191 
0192     cur = lbuf;
0193 
0194     switch ((long)data)
0195     {
0196     case LED_NOLCD:
0197         d = *cur++ - '0';
0198         if (d != 0 && d != 1) goto parse_error;
0199         led_heartbeat = d;
0200 
0201         if (*cur++ != ' ') goto parse_error;
0202 
0203         d = *cur++ - '0';
0204         if (d != 0 && d != 1) goto parse_error;
0205         led_diskio = d;
0206 
0207         if (*cur++ != ' ') goto parse_error;
0208 
0209         d = *cur++ - '0';
0210         if (d != 0 && d != 1) goto parse_error;
0211         led_lanrxtx = d;
0212 
0213         break;
0214     case LED_HASLCD:
0215         if (*cur && cur[strlen(cur)-1] == '\n')
0216             cur[strlen(cur)-1] = 0;
0217         if (*cur == 0) 
0218             cur = lcd_text_default;
0219         lcd_print(cur);
0220         break;
0221     default:
0222         return 0;
0223     }
0224     
0225     return count;
0226 
0227 parse_error:
0228     if ((long)data == LED_NOLCD)
0229         printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n");
0230     return -EINVAL;
0231 }
0232 
0233 static const struct proc_ops led_proc_ops = {
0234     .proc_open  = led_proc_open,
0235     .proc_read  = seq_read,
0236     .proc_lseek = seq_lseek,
0237     .proc_release   = single_release,
0238     .proc_write = led_proc_write,
0239 };
0240 
0241 static int __init led_create_procfs(void)
0242 {
0243     struct proc_dir_entry *proc_pdc_root = NULL;
0244     struct proc_dir_entry *ent;
0245 
0246     if (led_type == -1) return -1;
0247 
0248     proc_pdc_root = proc_mkdir("pdc", NULL);
0249     if (!proc_pdc_root) return -1;
0250 
0251     if (!lcd_no_led_support)
0252     {
0253         ent = proc_create_data("led", 0644, proc_pdc_root,
0254                     &led_proc_ops, (void *)LED_NOLCD); /* LED */
0255         if (!ent) return -1;
0256     }
0257 
0258     if (led_type == LED_HASLCD)
0259     {
0260         ent = proc_create_data("lcd", 0644, proc_pdc_root,
0261                     &led_proc_ops, (void *)LED_HASLCD); /* LCD */
0262         if (!ent) return -1;
0263     }
0264 
0265     return 0;
0266 }
0267 #endif
0268 
0269 /*
0270    ** 
0271    ** led_ASP_driver()
0272    ** 
0273  */
0274 #define LED_DATA    0x01    /* data to shift (0:on 1:off) */
0275 #define LED_STROBE  0x02    /* strobe to clock data */
0276 static void led_ASP_driver(unsigned char leds)
0277 {
0278     int i;
0279 
0280     leds = ~leds;
0281     for (i = 0; i < 8; i++) {
0282         unsigned char value;
0283         value = (leds & 0x80) >> 7;
0284         gsc_writeb( value,       LED_DATA_REG );
0285         gsc_writeb( value | LED_STROBE,  LED_DATA_REG );
0286         leds <<= 1;
0287     }
0288 }
0289 
0290 
0291 /*
0292    ** 
0293    ** led_LASI_driver()
0294    ** 
0295  */
0296 static void led_LASI_driver(unsigned char leds)
0297 {
0298     leds = ~leds;
0299     gsc_writeb( leds, LED_DATA_REG );
0300 }
0301 
0302 
0303 /*
0304    ** 
0305    ** led_LCD_driver()
0306    **   
0307  */
0308 static void led_LCD_driver(unsigned char leds)
0309 {
0310     static int i;
0311     static unsigned char mask[4] = { LED_HEARTBEAT, LED_DISK_IO,
0312         LED_LAN_RCV, LED_LAN_TX };
0313     
0314     static struct lcd_block * blockp[4] = {
0315         &lcd_info.heartbeat,
0316         &lcd_info.disk_io,
0317         &lcd_info.lan_rcv,
0318         &lcd_info.lan_tx
0319     };
0320 
0321     /* Convert min_cmd_delay to milliseconds */
0322     unsigned int msec_cmd_delay = 1 + (lcd_info.min_cmd_delay / 1000);
0323     
0324     for (i=0; i<4; ++i) 
0325     {
0326         if ((leds & mask[i]) != (lastleds & mask[i])) 
0327         {
0328             gsc_writeb( blockp[i]->command, LCD_CMD_REG );
0329             msleep(msec_cmd_delay);
0330             
0331             gsc_writeb( leds & mask[i] ? blockp[i]->on : 
0332                     blockp[i]->off, LCD_DATA_REG );
0333             msleep(msec_cmd_delay);
0334         }
0335     }
0336 }
0337 
0338 
0339 /*
0340    ** 
0341    ** led_get_net_activity()
0342    ** 
0343    ** calculate if there was TX- or RX-throughput on the network interfaces
0344    ** (analog to dev_get_info() from net/core/dev.c)
0345    **   
0346  */
0347 static __inline__ int led_get_net_activity(void)
0348 { 
0349 #ifndef CONFIG_NET
0350     return 0;
0351 #else
0352     static u64 rx_total_last, tx_total_last;
0353     u64 rx_total, tx_total;
0354     struct net_device *dev;
0355     int retval;
0356 
0357     rx_total = tx_total = 0;
0358     
0359     /* we are running as a workqueue task, so we can use an RCU lookup */
0360     rcu_read_lock();
0361     for_each_netdev_rcu(&init_net, dev) {
0362         const struct rtnl_link_stats64 *stats;
0363         struct rtnl_link_stats64 temp;
0364         struct in_device *in_dev = __in_dev_get_rcu(dev);
0365         if (!in_dev || !in_dev->ifa_list)
0366         continue;
0367         if (ipv4_is_loopback(in_dev->ifa_list->ifa_local))
0368         continue;
0369         stats = dev_get_stats(dev, &temp);
0370         rx_total += stats->rx_packets;
0371         tx_total += stats->tx_packets;
0372     }
0373     rcu_read_unlock();
0374 
0375     retval = 0;
0376 
0377     if (rx_total != rx_total_last) {
0378         rx_total_last = rx_total;
0379         retval |= LED_LAN_RCV;
0380     }
0381 
0382     if (tx_total != tx_total_last) {
0383         tx_total_last = tx_total;
0384         retval |= LED_LAN_TX;
0385     }
0386 
0387     return retval;
0388 #endif
0389 }
0390 
0391 
0392 /*
0393    ** 
0394    ** led_get_diskio_activity()
0395    ** 
0396    ** calculate if there was disk-io in the system
0397    **   
0398  */
0399 static __inline__ int led_get_diskio_activity(void)
0400 {   
0401     static unsigned long last_pgpgin, last_pgpgout;
0402     unsigned long events[NR_VM_EVENT_ITEMS];
0403     int changed;
0404 
0405     all_vm_events(events);
0406 
0407     /* Just use a very simple calculation here. Do not care about overflow,
0408        since we only want to know if there was activity or not. */
0409     changed = (events[PGPGIN] != last_pgpgin) ||
0410           (events[PGPGOUT] != last_pgpgout);
0411     last_pgpgin  = events[PGPGIN];
0412     last_pgpgout = events[PGPGOUT];
0413 
0414     return (changed ? LED_DISK_IO : 0);
0415 }
0416 
0417 
0418 
0419 /*
0420    ** led_work_func()
0421    ** 
0422    ** manages when and which chassis LCD/LED gets updated
0423 
0424     TODO:
0425     - display load average (older machines like 715/64 have 4 "free" LED's for that)
0426     - optimizations
0427  */
0428 
0429 #define HEARTBEAT_LEN (HZ*10/100)
0430 #define HEARTBEAT_2ND_RANGE_START (HZ*28/100)
0431 #define HEARTBEAT_2ND_RANGE_END   (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN)
0432 
0433 #define LED_UPDATE_INTERVAL (1 + (HZ*19/1000))
0434 
0435 static void led_work_func (struct work_struct *unused)
0436 {
0437     static unsigned long last_jiffies;
0438     static unsigned long count_HZ; /* counter in range 0..HZ */
0439     unsigned char currentleds = 0; /* stores current value of the LEDs */
0440 
0441     /* exit if not initialized */
0442     if (!led_func_ptr)
0443         return;
0444 
0445     /* increment the heartbeat timekeeper */
0446     count_HZ += jiffies - last_jiffies;
0447     last_jiffies = jiffies;
0448     if (count_HZ >= HZ)
0449         count_HZ = 0;
0450 
0451     if (likely(led_heartbeat))
0452     {
0453         /* flash heartbeat-LED like a real heart
0454          * (2 x short then a long delay)
0455          */
0456         if (count_HZ < HEARTBEAT_LEN || 
0457                 (count_HZ >= HEARTBEAT_2ND_RANGE_START &&
0458                 count_HZ < HEARTBEAT_2ND_RANGE_END)) 
0459             currentleds |= LED_HEARTBEAT;
0460     }
0461 
0462     if (likely(led_lanrxtx))  currentleds |= led_get_net_activity();
0463     if (likely(led_diskio))   currentleds |= led_get_diskio_activity();
0464 
0465     /* blink LEDs if we got an Oops (HPMC) */
0466     if (unlikely(oops_in_progress)) {
0467         if (boot_cpu_data.cpu_type >= pcxl2) {
0468             /* newer machines don't have loadavg. LEDs, so we
0469              * let all LEDs blink twice per second instead */
0470             currentleds = (count_HZ <= (HZ/2)) ? 0 : 0xff;
0471         } else {
0472             /* old machines: blink loadavg. LEDs twice per second */
0473             if (count_HZ <= (HZ/2))
0474                 currentleds &= ~(LED4|LED5|LED6|LED7);
0475             else
0476                 currentleds |= (LED4|LED5|LED6|LED7);
0477         }
0478     }
0479 
0480     if (currentleds != lastleds)
0481     {
0482         led_func_ptr(currentleds);  /* Update the LCD/LEDs */
0483         lastleds = currentleds;
0484     }
0485 
0486     queue_delayed_work(led_wq, &led_task, LED_UPDATE_INTERVAL);
0487 }
0488 
0489 /*
0490    ** led_halt()
0491    ** 
0492    ** called by the reboot notifier chain at shutdown and stops all
0493    ** LED/LCD activities.
0494    ** 
0495  */
0496 
0497 static int led_halt(struct notifier_block *, unsigned long, void *);
0498 
0499 static struct notifier_block led_notifier = {
0500     .notifier_call = led_halt,
0501 };
0502 static int notifier_disabled = 0;
0503 
0504 static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) 
0505 {
0506     char *txt;
0507 
0508     if (notifier_disabled)
0509         return NOTIFY_OK;
0510 
0511     notifier_disabled = 1;
0512     switch (event) {
0513     case SYS_RESTART:   txt = "SYSTEM RESTART";
0514                 break;
0515     case SYS_HALT:      txt = "SYSTEM HALT";
0516                 break;
0517     case SYS_POWER_OFF: txt = "SYSTEM POWER OFF";
0518                 break;
0519     default:        return NOTIFY_DONE;
0520     }
0521     
0522     /* Cancel the work item and delete the queue */
0523     if (led_wq) {
0524         cancel_delayed_work_sync(&led_task);
0525         destroy_workqueue(led_wq);
0526         led_wq = NULL;
0527     }
0528  
0529     if (lcd_info.model == DISPLAY_MODEL_LCD)
0530         lcd_print(txt);
0531     else
0532         if (led_func_ptr)
0533             led_func_ptr(0xff); /* turn all LEDs ON */
0534     
0535     return NOTIFY_OK;
0536 }
0537 
0538 /*
0539    ** register_led_driver()
0540    ** 
0541    ** registers an external LED or LCD for usage by this driver.
0542    ** currently only LCD-, LASI- and ASP-style LCD/LED's are supported.
0543    ** 
0544  */
0545 
0546 int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg)
0547 {
0548     static int initialized;
0549     
0550     if (initialized || !data_reg)
0551         return 1;
0552     
0553     lcd_info.model = model;     /* store the values */
0554     LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg;
0555 
0556     switch (lcd_info.model) {
0557     case DISPLAY_MODEL_LCD:
0558         LCD_DATA_REG = data_reg;
0559         printk(KERN_INFO "LCD display at %lx,%lx registered\n", 
0560             LCD_CMD_REG , LCD_DATA_REG);
0561         led_func_ptr = led_LCD_driver;
0562         led_type = LED_HASLCD;
0563         break;
0564 
0565     case DISPLAY_MODEL_LASI:
0566         /* Skip to register LED in QEMU */
0567         if (running_on_qemu)
0568             return 1;
0569         LED_DATA_REG = data_reg;
0570         led_func_ptr = led_LASI_driver;
0571         printk(KERN_INFO "LED display at %lx registered\n", LED_DATA_REG);
0572         led_type = LED_NOLCD;
0573         break;
0574 
0575     case DISPLAY_MODEL_OLD_ASP:
0576         LED_DATA_REG = data_reg;
0577         led_func_ptr = led_ASP_driver;
0578         printk(KERN_INFO "LED (ASP-style) display at %lx registered\n", 
0579             LED_DATA_REG);
0580         led_type = LED_NOLCD;
0581         break;
0582 
0583     default:
0584         printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n",
0585                __func__, lcd_info.model);
0586         return 1;
0587     }
0588     
0589     /* mark the LCD/LED driver now as initialized and 
0590      * register to the reboot notifier chain */
0591     initialized++;
0592     register_reboot_notifier(&led_notifier);
0593 
0594     /* Ensure the work is queued */
0595     if (led_wq) {
0596         queue_delayed_work(led_wq, &led_task, 0);
0597     }
0598 
0599     return 0;
0600 }
0601 
0602 /*
0603    ** register_led_regions()
0604    ** 
0605    ** register_led_regions() registers the LCD/LED regions for /procfs.
0606    ** At bootup - where the initialisation of the LCD/LED normally happens - 
0607    ** not all internal structures of request_region() are properly set up,
0608    ** so that we delay the led-registration until after busdevices_init() 
0609    ** has been executed.
0610    **
0611  */
0612 
0613 void __init register_led_regions(void)
0614 {
0615     switch (lcd_info.model) {
0616     case DISPLAY_MODEL_LCD:
0617         request_mem_region((unsigned long)LCD_CMD_REG,  1, "lcd_cmd");
0618         request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");
0619         break;
0620     case DISPLAY_MODEL_LASI:
0621     case DISPLAY_MODEL_OLD_ASP:
0622         request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data");
0623         break;
0624     }
0625 }
0626 
0627 
0628 /*
0629    ** 
0630    ** lcd_print()
0631    ** 
0632    ** Displays the given string on the LCD-Display of newer machines.
0633    ** lcd_print() disables/enables the timer-based led work queue to
0634    ** avoid a race condition while writing the CMD/DATA register pair.
0635    **
0636  */
0637 int lcd_print( const char *str )
0638 {
0639     int i;
0640 
0641     if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD)
0642         return 0;
0643     
0644     /* temporarily disable the led work task */
0645     if (led_wq)
0646         cancel_delayed_work_sync(&led_task);
0647 
0648     /* copy display string to buffer for procfs */
0649     strscpy(lcd_text, str, sizeof(lcd_text));
0650 
0651     /* Set LCD Cursor to 1st character */
0652     gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG);
0653     udelay(lcd_info.min_cmd_delay);
0654 
0655     /* Print the string */
0656     for (i=0; i < lcd_info.lcd_width; i++) {
0657         if (str && *str)
0658         gsc_writeb(*str++, LCD_DATA_REG);
0659         else
0660         gsc_writeb(' ', LCD_DATA_REG);
0661         udelay(lcd_info.min_cmd_delay);
0662     }
0663     
0664     /* re-queue the work */
0665     if (led_wq) {
0666         queue_delayed_work(led_wq, &led_task, 0);
0667     }
0668 
0669     return lcd_info.lcd_width;
0670 }
0671 
0672 /*
0673    ** led_init()
0674    ** 
0675    ** led_init() is called very early in the bootup-process from setup.c 
0676    ** and asks the PDC for an usable chassis LCD or LED.
0677    ** If the PDC doesn't return any info, then the LED
0678    ** is detected by lasi.c or asp.c and registered with the
0679    ** above functions lasi_led_init() or asp_led_init().
0680    ** KittyHawk machines have often a buggy PDC, so that
0681    ** we explicitly check for those machines here.
0682  */
0683 
0684 int __init led_init(void)
0685 {
0686     struct pdc_chassis_info chassis_info;
0687     int ret;
0688 
0689     snprintf(lcd_text_default, sizeof(lcd_text_default),
0690         "Linux %s", init_utsname()->release);
0691 
0692     /* Work around the buggy PDC of KittyHawk-machines */
0693     switch (CPU_HVERSION) {
0694     case 0x580:     /* KittyHawk DC2-100 (K100) */
0695     case 0x581:     /* KittyHawk DC3-120 (K210) */
0696     case 0x582:     /* KittyHawk DC3 100 (K400) */
0697     case 0x583:     /* KittyHawk DC3 120 (K410) */
0698     case 0x58B:     /* KittyHawk DC2 100 (K200) */
0699         printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, "
0700                 "LED detection skipped.\n", __FILE__, CPU_HVERSION);
0701         lcd_no_led_support = 1;
0702         goto found; /* use the preinitialized values of lcd_info */
0703     }
0704 
0705     /* initialize the struct, so that we can check for valid return values */
0706     lcd_info.model = DISPLAY_MODEL_NONE;
0707     chassis_info.actcnt = chassis_info.maxcnt = 0;
0708 
0709     ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info));
0710     if (ret == PDC_OK) {
0711         DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), "
0712              "lcd_width=%d, cmd_delay=%u,\n"
0713              "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ld\n",
0714                  __FILE__, lcd_info.model,
0715              (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" :
0716               (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown",
0717              lcd_info.lcd_width, lcd_info.min_cmd_delay,
0718              __FILE__, sizeof(lcd_info), 
0719              chassis_info.actcnt, chassis_info.maxcnt));
0720         DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%d\n",
0721             __FILE__, lcd_info.lcd_cmd_reg_addr, 
0722             lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1,  
0723             lcd_info.reset_cmd2, lcd_info.act_enable ));
0724     
0725         /* check the results. Some machines have a buggy PDC */
0726         if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt)
0727             goto not_found;
0728 
0729         switch (lcd_info.model) {
0730         case DISPLAY_MODEL_LCD:     /* LCD display */
0731             if (chassis_info.actcnt < 
0732                 offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1)
0733                 goto not_found;
0734             if (!lcd_info.act_enable) {
0735                 DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.\n"));
0736                 goto not_found;
0737             }
0738             break;
0739 
0740         case DISPLAY_MODEL_NONE:    /* no LED or LCD available */
0741             printk(KERN_INFO "PDC reported no LCD or LED.\n");
0742             goto not_found;
0743 
0744         case DISPLAY_MODEL_LASI:    /* Lasi style 8 bit LED display */
0745             if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32)
0746                 goto not_found;
0747             break;
0748 
0749         default:
0750             printk(KERN_WARNING "PDC reported unknown LCD/LED model %d\n",
0751                    lcd_info.model);
0752             goto not_found;
0753         } /* switch() */
0754 
0755 found:
0756         /* register the LCD/LED driver */
0757         register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG);
0758         return 0;
0759 
0760     } else { /* if() */
0761         DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %d\n", ret));
0762     }
0763 
0764 not_found:
0765     lcd_info.model = DISPLAY_MODEL_NONE;
0766     return 1;
0767 }
0768 
0769 static void __exit led_exit(void)
0770 {
0771     unregister_reboot_notifier(&led_notifier);
0772     return;
0773 }
0774 
0775 #ifdef CONFIG_PROC_FS
0776 module_init(led_create_procfs)
0777 #endif