18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Chassis LCD/LED driver for HP-PARISC workstations 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 2000 Red Hat Software 68c2ecf20Sopenharmony_ci * (c) Copyright 2000 Helge Deller <hdeller@redhat.com> 78c2ecf20Sopenharmony_ci * (c) Copyright 2001-2009 Helge Deller <deller@gmx.de> 88c2ecf20Sopenharmony_ci * (c) Copyright 2001 Randolph Chung <tausq@debian.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * TODO: 118c2ecf20Sopenharmony_ci * - speed-up calculations with inlined assembler 128c2ecf20Sopenharmony_ci * - interface to write to second row of LCD from /proc (if technically possible) 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Changes: 158c2ecf20Sopenharmony_ci * - Audit copy_from_user in led_proc_write. 168c2ecf20Sopenharmony_ci * Daniele Bellucci <bellucda@tiscali.it> 178c2ecf20Sopenharmony_ci * - Switch from using a tasklet to a work queue, so the led_LCD_driver 188c2ecf20Sopenharmony_ci * can sleep. 198c2ecf20Sopenharmony_ci * David Pye <dmp@davidmpye.dyndns.org> 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/stddef.h> /* for offsetof() */ 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/types.h> 268c2ecf20Sopenharmony_ci#include <linux/ioport.h> 278c2ecf20Sopenharmony_ci#include <linux/utsname.h> 288c2ecf20Sopenharmony_ci#include <linux/capability.h> 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 318c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 328c2ecf20Sopenharmony_ci#include <linux/in.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 358c2ecf20Sopenharmony_ci#include <linux/reboot.h> 368c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 378c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 388c2ecf20Sopenharmony_ci#include <linux/ctype.h> 398c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 408c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 418c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 428c2ecf20Sopenharmony_ci#include <asm/io.h> 438c2ecf20Sopenharmony_ci#include <asm/processor.h> 448c2ecf20Sopenharmony_ci#include <asm/hardware.h> 458c2ecf20Sopenharmony_ci#include <asm/param.h> /* HZ */ 468c2ecf20Sopenharmony_ci#include <asm/led.h> 478c2ecf20Sopenharmony_ci#include <asm/pdc.h> 488c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* The control of the LEDs and LCDs on PARISC-machines have to be done 518c2ecf20Sopenharmony_ci completely in software. The necessary calculations are done in a work queue 528c2ecf20Sopenharmony_ci task which is scheduled regularly, and since the calculations may consume a 538c2ecf20Sopenharmony_ci relatively large amount of CPU time, some of the calculations can be 548c2ecf20Sopenharmony_ci turned off with the following variables (controlled via procfs) */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int led_type __read_mostly = -1; 578c2ecf20Sopenharmony_cistatic unsigned char lastleds; /* LED state from most recent update */ 588c2ecf20Sopenharmony_cistatic unsigned int led_heartbeat __read_mostly = 1; 598c2ecf20Sopenharmony_cistatic unsigned int led_diskio __read_mostly; 608c2ecf20Sopenharmony_cistatic unsigned int led_lanrxtx __read_mostly; 618c2ecf20Sopenharmony_cistatic char lcd_text[32] __read_mostly; 628c2ecf20Sopenharmony_cistatic char lcd_text_default[32] __read_mostly; 638c2ecf20Sopenharmony_cistatic int lcd_no_led_support __read_mostly = 0; /* KittyHawk doesn't support LED on its LCD */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct workqueue_struct *led_wq; 678c2ecf20Sopenharmony_cistatic void led_work_func(struct work_struct *); 688c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(led_task, led_work_func); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#if 0 718c2ecf20Sopenharmony_ci#define DPRINTK(x) printk x 728c2ecf20Sopenharmony_ci#else 738c2ecf20Sopenharmony_ci#define DPRINTK(x) 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct lcd_block { 778c2ecf20Sopenharmony_ci unsigned char command; /* stores the command byte */ 788c2ecf20Sopenharmony_ci unsigned char on; /* value for turning LED on */ 798c2ecf20Sopenharmony_ci unsigned char off; /* value for turning LED off */ 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Structure returned by PDC_RETURN_CHASSIS_INFO */ 838c2ecf20Sopenharmony_ci/* NOTE: we use unsigned long:16 two times, since the following member 848c2ecf20Sopenharmony_ci lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */ 858c2ecf20Sopenharmony_cistruct pdc_chassis_lcd_info_ret_block { 868c2ecf20Sopenharmony_ci unsigned long model:16; /* DISPLAY_MODEL_XXXX */ 878c2ecf20Sopenharmony_ci unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */ 888c2ecf20Sopenharmony_ci unsigned long lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */ 898c2ecf20Sopenharmony_ci unsigned long lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */ 908c2ecf20Sopenharmony_ci unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */ 918c2ecf20Sopenharmony_ci unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */ 928c2ecf20Sopenharmony_ci unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */ 938c2ecf20Sopenharmony_ci unsigned char act_enable; /* 0 = no activity (LCD only) */ 948c2ecf20Sopenharmony_ci struct lcd_block heartbeat; 958c2ecf20Sopenharmony_ci struct lcd_block disk_io; 968c2ecf20Sopenharmony_ci struct lcd_block lan_rcv; 978c2ecf20Sopenharmony_ci struct lcd_block lan_tx; 988c2ecf20Sopenharmony_ci char _pad; 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* LCD_CMD and LCD_DATA for KittyHawk machines */ 1038c2ecf20Sopenharmony_ci#define KITTYHAWK_LCD_CMD F_EXTEND(0xf0190000UL) /* 64bit-ready */ 1048c2ecf20Sopenharmony_ci#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 1078c2ecf20Sopenharmony_ci * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */ 1088c2ecf20Sopenharmony_cistatic struct pdc_chassis_lcd_info_ret_block 1098c2ecf20Sopenharmony_cilcd_info __attribute__((aligned(8))) __read_mostly = 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci .model = DISPLAY_MODEL_LCD, 1128c2ecf20Sopenharmony_ci .lcd_width = 16, 1138c2ecf20Sopenharmony_ci .lcd_cmd_reg_addr = KITTYHAWK_LCD_CMD, 1148c2ecf20Sopenharmony_ci .lcd_data_reg_addr = KITTYHAWK_LCD_DATA, 1158c2ecf20Sopenharmony_ci .min_cmd_delay = 80, 1168c2ecf20Sopenharmony_ci .reset_cmd1 = 0x80, 1178c2ecf20Sopenharmony_ci .reset_cmd2 = 0xc0, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* direct access to some of the lcd_info variables */ 1228c2ecf20Sopenharmony_ci#define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr 1238c2ecf20Sopenharmony_ci#define LCD_DATA_REG lcd_info.lcd_data_reg_addr 1248c2ecf20Sopenharmony_ci#define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define LED_HASLCD 1 1278c2ecf20Sopenharmony_ci#define LED_NOLCD 0 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* The workqueue must be created at init-time */ 1308c2ecf20Sopenharmony_cistatic int start_task(void) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci /* Display the default text now */ 1338c2ecf20Sopenharmony_ci if (led_type == LED_HASLCD) lcd_print( lcd_text_default ); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* KittyHawk has no LED support on its LCD */ 1368c2ecf20Sopenharmony_ci if (lcd_no_led_support) return 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Create the work queue and queue the LED task */ 1398c2ecf20Sopenharmony_ci led_wq = create_singlethread_workqueue("led_wq"); 1408c2ecf20Sopenharmony_ci if (!led_wq) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci queue_delayed_work(led_wq, &led_task, 0); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cidevice_initcall(start_task); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* ptr to LCD/LED-specific function */ 1518c2ecf20Sopenharmony_cistatic void (*led_func_ptr) (unsigned char) __read_mostly; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1548c2ecf20Sopenharmony_cistatic int led_proc_show(struct seq_file *m, void *v) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci switch ((long)m->private) 1578c2ecf20Sopenharmony_ci { 1588c2ecf20Sopenharmony_ci case LED_NOLCD: 1598c2ecf20Sopenharmony_ci seq_printf(m, "Heartbeat: %d\n", led_heartbeat); 1608c2ecf20Sopenharmony_ci seq_printf(m, "Disk IO: %d\n", led_diskio); 1618c2ecf20Sopenharmony_ci seq_printf(m, "LAN Rx/Tx: %d\n", led_lanrxtx); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case LED_HASLCD: 1648c2ecf20Sopenharmony_ci seq_printf(m, "%s\n", lcd_text); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci default: 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int led_proc_open(struct inode *inode, struct file *file) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return single_open(file, led_proc_show, PDE_DATA(inode)); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic ssize_t led_proc_write(struct file *file, const char __user *buf, 1798c2ecf20Sopenharmony_ci size_t count, loff_t *pos) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci void *data = PDE_DATA(file_inode(file)); 1828c2ecf20Sopenharmony_ci char *cur, lbuf[32]; 1838c2ecf20Sopenharmony_ci int d; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 1868c2ecf20Sopenharmony_ci return -EACCES; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (count >= sizeof(lbuf)) 1898c2ecf20Sopenharmony_ci count = sizeof(lbuf)-1; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (copy_from_user(lbuf, buf, count)) 1928c2ecf20Sopenharmony_ci return -EFAULT; 1938c2ecf20Sopenharmony_ci lbuf[count] = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci cur = lbuf; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch ((long)data) 1988c2ecf20Sopenharmony_ci { 1998c2ecf20Sopenharmony_ci case LED_NOLCD: 2008c2ecf20Sopenharmony_ci d = *cur++ - '0'; 2018c2ecf20Sopenharmony_ci if (d != 0 && d != 1) goto parse_error; 2028c2ecf20Sopenharmony_ci led_heartbeat = d; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (*cur++ != ' ') goto parse_error; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci d = *cur++ - '0'; 2078c2ecf20Sopenharmony_ci if (d != 0 && d != 1) goto parse_error; 2088c2ecf20Sopenharmony_ci led_diskio = d; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (*cur++ != ' ') goto parse_error; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci d = *cur++ - '0'; 2138c2ecf20Sopenharmony_ci if (d != 0 && d != 1) goto parse_error; 2148c2ecf20Sopenharmony_ci led_lanrxtx = d; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case LED_HASLCD: 2188c2ecf20Sopenharmony_ci if (*cur && cur[strlen(cur)-1] == '\n') 2198c2ecf20Sopenharmony_ci cur[strlen(cur)-1] = 0; 2208c2ecf20Sopenharmony_ci if (*cur == 0) 2218c2ecf20Sopenharmony_ci cur = lcd_text_default; 2228c2ecf20Sopenharmony_ci lcd_print(cur); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci default: 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return count; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciparse_error: 2318c2ecf20Sopenharmony_ci if ((long)data == LED_NOLCD) 2328c2ecf20Sopenharmony_ci printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n"); 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic const struct proc_ops led_proc_ops = { 2378c2ecf20Sopenharmony_ci .proc_open = led_proc_open, 2388c2ecf20Sopenharmony_ci .proc_read = seq_read, 2398c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 2408c2ecf20Sopenharmony_ci .proc_release = single_release, 2418c2ecf20Sopenharmony_ci .proc_write = led_proc_write, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int __init led_create_procfs(void) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct proc_dir_entry *proc_pdc_root = NULL; 2478c2ecf20Sopenharmony_ci struct proc_dir_entry *ent; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (led_type == -1) return -1; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci proc_pdc_root = proc_mkdir("pdc", NULL); 2528c2ecf20Sopenharmony_ci if (!proc_pdc_root) return -1; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!lcd_no_led_support) 2558c2ecf20Sopenharmony_ci { 2568c2ecf20Sopenharmony_ci ent = proc_create_data("led", S_IRUGO|S_IWUSR, proc_pdc_root, 2578c2ecf20Sopenharmony_ci &led_proc_ops, (void *)LED_NOLCD); /* LED */ 2588c2ecf20Sopenharmony_ci if (!ent) return -1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (led_type == LED_HASLCD) 2628c2ecf20Sopenharmony_ci { 2638c2ecf20Sopenharmony_ci ent = proc_create_data("lcd", S_IRUGO|S_IWUSR, proc_pdc_root, 2648c2ecf20Sopenharmony_ci &led_proc_ops, (void *)LED_HASLCD); /* LCD */ 2658c2ecf20Sopenharmony_ci if (!ent) return -1; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci#endif 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci ** 2748c2ecf20Sopenharmony_ci ** led_ASP_driver() 2758c2ecf20Sopenharmony_ci ** 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci#define LED_DATA 0x01 /* data to shift (0:on 1:off) */ 2788c2ecf20Sopenharmony_ci#define LED_STROBE 0x02 /* strobe to clock data */ 2798c2ecf20Sopenharmony_cistatic void led_ASP_driver(unsigned char leds) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci leds = ~leds; 2848c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 2858c2ecf20Sopenharmony_ci unsigned char value; 2868c2ecf20Sopenharmony_ci value = (leds & 0x80) >> 7; 2878c2ecf20Sopenharmony_ci gsc_writeb( value, LED_DATA_REG ); 2888c2ecf20Sopenharmony_ci gsc_writeb( value | LED_STROBE, LED_DATA_REG ); 2898c2ecf20Sopenharmony_ci leds <<= 1; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci ** 2968c2ecf20Sopenharmony_ci ** led_LASI_driver() 2978c2ecf20Sopenharmony_ci ** 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_cistatic void led_LASI_driver(unsigned char leds) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci leds = ~leds; 3028c2ecf20Sopenharmony_ci gsc_writeb( leds, LED_DATA_REG ); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci ** 3088c2ecf20Sopenharmony_ci ** led_LCD_driver() 3098c2ecf20Sopenharmony_ci ** 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic void led_LCD_driver(unsigned char leds) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci static int i; 3148c2ecf20Sopenharmony_ci static unsigned char mask[4] = { LED_HEARTBEAT, LED_DISK_IO, 3158c2ecf20Sopenharmony_ci LED_LAN_RCV, LED_LAN_TX }; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci static struct lcd_block * blockp[4] = { 3188c2ecf20Sopenharmony_ci &lcd_info.heartbeat, 3198c2ecf20Sopenharmony_ci &lcd_info.disk_io, 3208c2ecf20Sopenharmony_ci &lcd_info.lan_rcv, 3218c2ecf20Sopenharmony_ci &lcd_info.lan_tx 3228c2ecf20Sopenharmony_ci }; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Convert min_cmd_delay to milliseconds */ 3258c2ecf20Sopenharmony_ci unsigned int msec_cmd_delay = 1 + (lcd_info.min_cmd_delay / 1000); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i=0; i<4; ++i) 3288c2ecf20Sopenharmony_ci { 3298c2ecf20Sopenharmony_ci if ((leds & mask[i]) != (lastleds & mask[i])) 3308c2ecf20Sopenharmony_ci { 3318c2ecf20Sopenharmony_ci gsc_writeb( blockp[i]->command, LCD_CMD_REG ); 3328c2ecf20Sopenharmony_ci msleep(msec_cmd_delay); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci gsc_writeb( leds & mask[i] ? blockp[i]->on : 3358c2ecf20Sopenharmony_ci blockp[i]->off, LCD_DATA_REG ); 3368c2ecf20Sopenharmony_ci msleep(msec_cmd_delay); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* 3438c2ecf20Sopenharmony_ci ** 3448c2ecf20Sopenharmony_ci ** led_get_net_activity() 3458c2ecf20Sopenharmony_ci ** 3468c2ecf20Sopenharmony_ci ** calculate if there was TX- or RX-throughput on the network interfaces 3478c2ecf20Sopenharmony_ci ** (analog to dev_get_info() from net/core/dev.c) 3488c2ecf20Sopenharmony_ci ** 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_cistatic __inline__ int led_get_net_activity(void) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci#ifndef CONFIG_NET 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci#else 3558c2ecf20Sopenharmony_ci static u64 rx_total_last, tx_total_last; 3568c2ecf20Sopenharmony_ci u64 rx_total, tx_total; 3578c2ecf20Sopenharmony_ci struct net_device *dev; 3588c2ecf20Sopenharmony_ci int retval; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci rx_total = tx_total = 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* we are running as a workqueue task, so we can use an RCU lookup */ 3638c2ecf20Sopenharmony_ci rcu_read_lock(); 3648c2ecf20Sopenharmony_ci for_each_netdev_rcu(&init_net, dev) { 3658c2ecf20Sopenharmony_ci const struct rtnl_link_stats64 *stats; 3668c2ecf20Sopenharmony_ci struct rtnl_link_stats64 temp; 3678c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 3688c2ecf20Sopenharmony_ci if (!in_dev || !in_dev->ifa_list) 3698c2ecf20Sopenharmony_ci continue; 3708c2ecf20Sopenharmony_ci if (ipv4_is_loopback(in_dev->ifa_list->ifa_local)) 3718c2ecf20Sopenharmony_ci continue; 3728c2ecf20Sopenharmony_ci stats = dev_get_stats(dev, &temp); 3738c2ecf20Sopenharmony_ci rx_total += stats->rx_packets; 3748c2ecf20Sopenharmony_ci tx_total += stats->tx_packets; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci rcu_read_unlock(); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci retval = 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (rx_total != rx_total_last) { 3818c2ecf20Sopenharmony_ci rx_total_last = rx_total; 3828c2ecf20Sopenharmony_ci retval |= LED_LAN_RCV; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (tx_total != tx_total_last) { 3868c2ecf20Sopenharmony_ci tx_total_last = tx_total; 3878c2ecf20Sopenharmony_ci retval |= LED_LAN_TX; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return retval; 3918c2ecf20Sopenharmony_ci#endif 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci ** 3978c2ecf20Sopenharmony_ci ** led_get_diskio_activity() 3988c2ecf20Sopenharmony_ci ** 3998c2ecf20Sopenharmony_ci ** calculate if there was disk-io in the system 4008c2ecf20Sopenharmony_ci ** 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic __inline__ int led_get_diskio_activity(void) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci static unsigned long last_pgpgin, last_pgpgout; 4058c2ecf20Sopenharmony_ci unsigned long events[NR_VM_EVENT_ITEMS]; 4068c2ecf20Sopenharmony_ci int changed; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci all_vm_events(events); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Just use a very simple calculation here. Do not care about overflow, 4118c2ecf20Sopenharmony_ci since we only want to know if there was activity or not. */ 4128c2ecf20Sopenharmony_ci changed = (events[PGPGIN] != last_pgpgin) || 4138c2ecf20Sopenharmony_ci (events[PGPGOUT] != last_pgpgout); 4148c2ecf20Sopenharmony_ci last_pgpgin = events[PGPGIN]; 4158c2ecf20Sopenharmony_ci last_pgpgout = events[PGPGOUT]; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return (changed ? LED_DISK_IO : 0); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* 4238c2ecf20Sopenharmony_ci ** led_work_func() 4248c2ecf20Sopenharmony_ci ** 4258c2ecf20Sopenharmony_ci ** manages when and which chassis LCD/LED gets updated 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci TODO: 4288c2ecf20Sopenharmony_ci - display load average (older machines like 715/64 have 4 "free" LED's for that) 4298c2ecf20Sopenharmony_ci - optimizations 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci#define HEARTBEAT_LEN (HZ*10/100) 4338c2ecf20Sopenharmony_ci#define HEARTBEAT_2ND_RANGE_START (HZ*28/100) 4348c2ecf20Sopenharmony_ci#define HEARTBEAT_2ND_RANGE_END (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN) 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci#define LED_UPDATE_INTERVAL (1 + (HZ*19/1000)) 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void led_work_func (struct work_struct *unused) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci static unsigned long last_jiffies; 4418c2ecf20Sopenharmony_ci static unsigned long count_HZ; /* counter in range 0..HZ */ 4428c2ecf20Sopenharmony_ci unsigned char currentleds = 0; /* stores current value of the LEDs */ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* exit if not initialized */ 4458c2ecf20Sopenharmony_ci if (!led_func_ptr) 4468c2ecf20Sopenharmony_ci return; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* increment the heartbeat timekeeper */ 4498c2ecf20Sopenharmony_ci count_HZ += jiffies - last_jiffies; 4508c2ecf20Sopenharmony_ci last_jiffies = jiffies; 4518c2ecf20Sopenharmony_ci if (count_HZ >= HZ) 4528c2ecf20Sopenharmony_ci count_HZ = 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (likely(led_heartbeat)) 4558c2ecf20Sopenharmony_ci { 4568c2ecf20Sopenharmony_ci /* flash heartbeat-LED like a real heart 4578c2ecf20Sopenharmony_ci * (2 x short then a long delay) 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci if (count_HZ < HEARTBEAT_LEN || 4608c2ecf20Sopenharmony_ci (count_HZ >= HEARTBEAT_2ND_RANGE_START && 4618c2ecf20Sopenharmony_ci count_HZ < HEARTBEAT_2ND_RANGE_END)) 4628c2ecf20Sopenharmony_ci currentleds |= LED_HEARTBEAT; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (likely(led_lanrxtx)) currentleds |= led_get_net_activity(); 4668c2ecf20Sopenharmony_ci if (likely(led_diskio)) currentleds |= led_get_diskio_activity(); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* blink LEDs if we got an Oops (HPMC) */ 4698c2ecf20Sopenharmony_ci if (unlikely(oops_in_progress)) { 4708c2ecf20Sopenharmony_ci if (boot_cpu_data.cpu_type >= pcxl2) { 4718c2ecf20Sopenharmony_ci /* newer machines don't have loadavg. LEDs, so we 4728c2ecf20Sopenharmony_ci * let all LEDs blink twice per second instead */ 4738c2ecf20Sopenharmony_ci currentleds = (count_HZ <= (HZ/2)) ? 0 : 0xff; 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci /* old machines: blink loadavg. LEDs twice per second */ 4768c2ecf20Sopenharmony_ci if (count_HZ <= (HZ/2)) 4778c2ecf20Sopenharmony_ci currentleds &= ~(LED4|LED5|LED6|LED7); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci currentleds |= (LED4|LED5|LED6|LED7); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (currentleds != lastleds) 4848c2ecf20Sopenharmony_ci { 4858c2ecf20Sopenharmony_ci led_func_ptr(currentleds); /* Update the LCD/LEDs */ 4868c2ecf20Sopenharmony_ci lastleds = currentleds; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci queue_delayed_work(led_wq, &led_task, LED_UPDATE_INTERVAL); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci ** led_halt() 4948c2ecf20Sopenharmony_ci ** 4958c2ecf20Sopenharmony_ci ** called by the reboot notifier chain at shutdown and stops all 4968c2ecf20Sopenharmony_ci ** LED/LCD activities. 4978c2ecf20Sopenharmony_ci ** 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int led_halt(struct notifier_block *, unsigned long, void *); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct notifier_block led_notifier = { 5038c2ecf20Sopenharmony_ci .notifier_call = led_halt, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_cistatic int notifier_disabled = 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int led_halt(struct notifier_block *nb, unsigned long event, void *buf) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci char *txt; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (notifier_disabled) 5128c2ecf20Sopenharmony_ci return NOTIFY_OK; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci notifier_disabled = 1; 5158c2ecf20Sopenharmony_ci switch (event) { 5168c2ecf20Sopenharmony_ci case SYS_RESTART: txt = "SYSTEM RESTART"; 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci case SYS_HALT: txt = "SYSTEM HALT"; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case SYS_POWER_OFF: txt = "SYSTEM POWER OFF"; 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci default: return NOTIFY_DONE; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Cancel the work item and delete the queue */ 5268c2ecf20Sopenharmony_ci if (led_wq) { 5278c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&led_task); 5288c2ecf20Sopenharmony_ci destroy_workqueue(led_wq); 5298c2ecf20Sopenharmony_ci led_wq = NULL; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (lcd_info.model == DISPLAY_MODEL_LCD) 5338c2ecf20Sopenharmony_ci lcd_print(txt); 5348c2ecf20Sopenharmony_ci else 5358c2ecf20Sopenharmony_ci if (led_func_ptr) 5368c2ecf20Sopenharmony_ci led_func_ptr(0xff); /* turn all LEDs ON */ 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return NOTIFY_OK; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/* 5428c2ecf20Sopenharmony_ci ** register_led_driver() 5438c2ecf20Sopenharmony_ci ** 5448c2ecf20Sopenharmony_ci ** registers an external LED or LCD for usage by this driver. 5458c2ecf20Sopenharmony_ci ** currently only LCD-, LASI- and ASP-style LCD/LED's are supported. 5468c2ecf20Sopenharmony_ci ** 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ciint __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci static int initialized; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (initialized || !data_reg) 5548c2ecf20Sopenharmony_ci return 1; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci lcd_info.model = model; /* store the values */ 5578c2ecf20Sopenharmony_ci LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci switch (lcd_info.model) { 5608c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LCD: 5618c2ecf20Sopenharmony_ci LCD_DATA_REG = data_reg; 5628c2ecf20Sopenharmony_ci printk(KERN_INFO "LCD display at %lx,%lx registered\n", 5638c2ecf20Sopenharmony_ci LCD_CMD_REG , LCD_DATA_REG); 5648c2ecf20Sopenharmony_ci led_func_ptr = led_LCD_driver; 5658c2ecf20Sopenharmony_ci led_type = LED_HASLCD; 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LASI: 5698c2ecf20Sopenharmony_ci /* Skip to register LED in QEMU */ 5708c2ecf20Sopenharmony_ci if (running_on_qemu) 5718c2ecf20Sopenharmony_ci return 1; 5728c2ecf20Sopenharmony_ci LED_DATA_REG = data_reg; 5738c2ecf20Sopenharmony_ci led_func_ptr = led_LASI_driver; 5748c2ecf20Sopenharmony_ci printk(KERN_INFO "LED display at %lx registered\n", LED_DATA_REG); 5758c2ecf20Sopenharmony_ci led_type = LED_NOLCD; 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci case DISPLAY_MODEL_OLD_ASP: 5798c2ecf20Sopenharmony_ci LED_DATA_REG = data_reg; 5808c2ecf20Sopenharmony_ci led_func_ptr = led_ASP_driver; 5818c2ecf20Sopenharmony_ci printk(KERN_INFO "LED (ASP-style) display at %lx registered\n", 5828c2ecf20Sopenharmony_ci LED_DATA_REG); 5838c2ecf20Sopenharmony_ci led_type = LED_NOLCD; 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci default: 5878c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n", 5888c2ecf20Sopenharmony_ci __func__, lcd_info.model); 5898c2ecf20Sopenharmony_ci return 1; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* mark the LCD/LED driver now as initialized and 5938c2ecf20Sopenharmony_ci * register to the reboot notifier chain */ 5948c2ecf20Sopenharmony_ci initialized++; 5958c2ecf20Sopenharmony_ci register_reboot_notifier(&led_notifier); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Ensure the work is queued */ 5988c2ecf20Sopenharmony_ci if (led_wq) { 5998c2ecf20Sopenharmony_ci queue_delayed_work(led_wq, &led_task, 0); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci/* 6068c2ecf20Sopenharmony_ci ** register_led_regions() 6078c2ecf20Sopenharmony_ci ** 6088c2ecf20Sopenharmony_ci ** register_led_regions() registers the LCD/LED regions for /procfs. 6098c2ecf20Sopenharmony_ci ** At bootup - where the initialisation of the LCD/LED normally happens - 6108c2ecf20Sopenharmony_ci ** not all internal structures of request_region() are properly set up, 6118c2ecf20Sopenharmony_ci ** so that we delay the led-registration until after busdevices_init() 6128c2ecf20Sopenharmony_ci ** has been executed. 6138c2ecf20Sopenharmony_ci ** 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_civoid __init register_led_regions(void) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci switch (lcd_info.model) { 6198c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LCD: 6208c2ecf20Sopenharmony_ci request_mem_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd"); 6218c2ecf20Sopenharmony_ci request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data"); 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LASI: 6248c2ecf20Sopenharmony_ci case DISPLAY_MODEL_OLD_ASP: 6258c2ecf20Sopenharmony_ci request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data"); 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci ** 6338c2ecf20Sopenharmony_ci ** lcd_print() 6348c2ecf20Sopenharmony_ci ** 6358c2ecf20Sopenharmony_ci ** Displays the given string on the LCD-Display of newer machines. 6368c2ecf20Sopenharmony_ci ** lcd_print() disables/enables the timer-based led work queue to 6378c2ecf20Sopenharmony_ci ** avoid a race condition while writing the CMD/DATA register pair. 6388c2ecf20Sopenharmony_ci ** 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ciint lcd_print( const char *str ) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci int i; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD) 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* temporarily disable the led work task */ 6488c2ecf20Sopenharmony_ci if (led_wq) 6498c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&led_task); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* copy display string to buffer for procfs */ 6528c2ecf20Sopenharmony_ci strlcpy(lcd_text, str, sizeof(lcd_text)); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Set LCD Cursor to 1st character */ 6558c2ecf20Sopenharmony_ci gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG); 6568c2ecf20Sopenharmony_ci udelay(lcd_info.min_cmd_delay); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Print the string */ 6598c2ecf20Sopenharmony_ci for (i=0; i < lcd_info.lcd_width; i++) { 6608c2ecf20Sopenharmony_ci if (str && *str) 6618c2ecf20Sopenharmony_ci gsc_writeb(*str++, LCD_DATA_REG); 6628c2ecf20Sopenharmony_ci else 6638c2ecf20Sopenharmony_ci gsc_writeb(' ', LCD_DATA_REG); 6648c2ecf20Sopenharmony_ci udelay(lcd_info.min_cmd_delay); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* re-queue the work */ 6688c2ecf20Sopenharmony_ci if (led_wq) { 6698c2ecf20Sopenharmony_ci queue_delayed_work(led_wq, &led_task, 0); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return lcd_info.lcd_width; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* 6768c2ecf20Sopenharmony_ci ** led_init() 6778c2ecf20Sopenharmony_ci ** 6788c2ecf20Sopenharmony_ci ** led_init() is called very early in the bootup-process from setup.c 6798c2ecf20Sopenharmony_ci ** and asks the PDC for an usable chassis LCD or LED. 6808c2ecf20Sopenharmony_ci ** If the PDC doesn't return any info, then the LED 6818c2ecf20Sopenharmony_ci ** is detected by lasi.c or asp.c and registered with the 6828c2ecf20Sopenharmony_ci ** above functions lasi_led_init() or asp_led_init(). 6838c2ecf20Sopenharmony_ci ** KittyHawk machines have often a buggy PDC, so that 6848c2ecf20Sopenharmony_ci ** we explicitly check for those machines here. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ciint __init led_init(void) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct pdc_chassis_info chassis_info; 6908c2ecf20Sopenharmony_ci int ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci snprintf(lcd_text_default, sizeof(lcd_text_default), 6938c2ecf20Sopenharmony_ci "Linux %s", init_utsname()->release); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* Work around the buggy PDC of KittyHawk-machines */ 6968c2ecf20Sopenharmony_ci switch (CPU_HVERSION) { 6978c2ecf20Sopenharmony_ci case 0x580: /* KittyHawk DC2-100 (K100) */ 6988c2ecf20Sopenharmony_ci case 0x581: /* KittyHawk DC3-120 (K210) */ 6998c2ecf20Sopenharmony_ci case 0x582: /* KittyHawk DC3 100 (K400) */ 7008c2ecf20Sopenharmony_ci case 0x583: /* KittyHawk DC3 120 (K410) */ 7018c2ecf20Sopenharmony_ci case 0x58B: /* KittyHawk DC2 100 (K200) */ 7028c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, " 7038c2ecf20Sopenharmony_ci "LED detection skipped.\n", __FILE__, CPU_HVERSION); 7048c2ecf20Sopenharmony_ci lcd_no_led_support = 1; 7058c2ecf20Sopenharmony_ci goto found; /* use the preinitialized values of lcd_info */ 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* initialize the struct, so that we can check for valid return values */ 7098c2ecf20Sopenharmony_ci lcd_info.model = DISPLAY_MODEL_NONE; 7108c2ecf20Sopenharmony_ci chassis_info.actcnt = chassis_info.maxcnt = 0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info)); 7138c2ecf20Sopenharmony_ci if (ret == PDC_OK) { 7148c2ecf20Sopenharmony_ci DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), " 7158c2ecf20Sopenharmony_ci "lcd_width=%d, cmd_delay=%u,\n" 7168c2ecf20Sopenharmony_ci "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ld\n", 7178c2ecf20Sopenharmony_ci __FILE__, lcd_info.model, 7188c2ecf20Sopenharmony_ci (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" : 7198c2ecf20Sopenharmony_ci (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown", 7208c2ecf20Sopenharmony_ci lcd_info.lcd_width, lcd_info.min_cmd_delay, 7218c2ecf20Sopenharmony_ci __FILE__, sizeof(lcd_info), 7228c2ecf20Sopenharmony_ci chassis_info.actcnt, chassis_info.maxcnt)); 7238c2ecf20Sopenharmony_ci DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%d\n", 7248c2ecf20Sopenharmony_ci __FILE__, lcd_info.lcd_cmd_reg_addr, 7258c2ecf20Sopenharmony_ci lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1, 7268c2ecf20Sopenharmony_ci lcd_info.reset_cmd2, lcd_info.act_enable )); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* check the results. Some machines have a buggy PDC */ 7298c2ecf20Sopenharmony_ci if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt) 7308c2ecf20Sopenharmony_ci goto not_found; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci switch (lcd_info.model) { 7338c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LCD: /* LCD display */ 7348c2ecf20Sopenharmony_ci if (chassis_info.actcnt < 7358c2ecf20Sopenharmony_ci offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1) 7368c2ecf20Sopenharmony_ci goto not_found; 7378c2ecf20Sopenharmony_ci if (!lcd_info.act_enable) { 7388c2ecf20Sopenharmony_ci DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.\n")); 7398c2ecf20Sopenharmony_ci goto not_found; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci case DISPLAY_MODEL_NONE: /* no LED or LCD available */ 7448c2ecf20Sopenharmony_ci printk(KERN_INFO "PDC reported no LCD or LED.\n"); 7458c2ecf20Sopenharmony_ci goto not_found; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */ 7488c2ecf20Sopenharmony_ci if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32) 7498c2ecf20Sopenharmony_ci goto not_found; 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci default: 7538c2ecf20Sopenharmony_ci printk(KERN_WARNING "PDC reported unknown LCD/LED model %d\n", 7548c2ecf20Sopenharmony_ci lcd_info.model); 7558c2ecf20Sopenharmony_ci goto not_found; 7568c2ecf20Sopenharmony_ci } /* switch() */ 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cifound: 7598c2ecf20Sopenharmony_ci /* register the LCD/LED driver */ 7608c2ecf20Sopenharmony_ci register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG); 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci } else { /* if() */ 7648c2ecf20Sopenharmony_ci DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %d\n", ret)); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cinot_found: 7688c2ecf20Sopenharmony_ci lcd_info.model = DISPLAY_MODEL_NONE; 7698c2ecf20Sopenharmony_ci return 1; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic void __exit led_exit(void) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci unregister_reboot_notifier(&led_notifier); 7758c2ecf20Sopenharmony_ci return; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 7798c2ecf20Sopenharmony_cimodule_init(led_create_procfs) 7808c2ecf20Sopenharmony_ci#endif 781