Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

parisc: led: Rewrite LED/LCD driver to utilizize Linux LED subsystem

Rewrite the whole driver and drop the own code to calculate load
average, disk and LAN load. Switch instead to use the in-kernel LED
subsystem, which gives us quite some advantages, e.g.
- existing triggers for heartbeat and disk/lan activity can be used
- users can configre the LEDs at will to any existing trigger via
/sys/class/leds
- less overhead since we don't need to run own timers
- fully integrated in Linux and as such cleaner code.

Note that the driver now depends on CONFIG_LEDS_CLASS which has to
be built-in and not as module.

Signed-off-by: Helge Deller <deller@gmx.de>

+389 -623
+3 -6
arch/parisc/include/asm/led.h
··· 25 25 #define LED_CMD_REG_NONE 0 /* NULL == no addr for the cmd register */ 26 26 27 27 /* register_led_driver() */ 28 - int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg); 28 + int register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg); 29 29 30 30 #ifdef CONFIG_CHASSIS_LCD_LED 31 31 /* writes a string to the LCD display (if possible on this h/w) */ 32 - int lcd_print(const char *str); 32 + void lcd_print(const char *str); 33 33 #else 34 - #define lcd_print(str) 34 + #define lcd_print(str) do { } while (0) 35 35 #endif 36 - 37 - /* main LED initialization function (uses PDC) */ 38 - int __init led_init(void); 39 36 40 37 #endif /* LED_H */
-5
arch/parisc/kernel/setup.c
··· 143 143 parisc_cache_init(); 144 144 paging_init(); 145 145 146 - #ifdef CONFIG_CHASSIS_LCD_LED 147 - /* initialize the LCD/LED after boot_cpu_data is available ! */ 148 - led_init(); /* LCD/LED initialization */ 149 - #endif 150 - 151 146 #ifdef CONFIG_PA11 152 147 dma_ops_init(); 153 148 #endif
+2 -1
drivers/parisc/Kconfig
··· 100 100 101 101 config CHASSIS_LCD_LED 102 102 bool "Chassis LCD and LED support" 103 + depends on LEDS_CLASS=y 103 104 default y 104 - select VM_EVENT_COUNTERS 105 + select LEDS_TRIGGERS 105 106 help 106 107 Say Y here if you want to enable support for the Heartbeat, 107 108 Disk/Network activities LEDs on some PA-RISC machines,
+384 -611
drivers/parisc/led.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * Chassis LCD/LED driver for HP-PARISC workstations 3 + * Chassis LCD/LED driver for HP-PARISC workstations 4 4 * 5 - * (c) Copyright 2000 Red Hat Software 6 - * (c) Copyright 2000 Helge Deller <hdeller@redhat.com> 7 - * (c) Copyright 2001-2009 Helge Deller <deller@gmx.de> 8 - * (c) Copyright 2001 Randolph Chung <tausq@debian.org> 5 + * (c) Copyright 2000 Red Hat Software 6 + * (c) Copyright 2000 Helge Deller <hdeller@redhat.com> 7 + * (c) Copyright 2001 Randolph Chung <tausq@debian.org> 8 + * (c) Copyright 2000-2023 Helge Deller <deller@gmx.de> 9 9 * 10 - * TODO: 11 - * - speed-up calculations with inlined assembler 12 - * - interface to write to second row of LCD from /proc (if technically possible) 10 + * The control of the LEDs and LCDs on PARISC machines has to be done 11 + * completely in software. 13 12 * 14 - * Changes: 15 - * - Audit copy_from_user in led_proc_write. 16 - * Daniele Bellucci <bellucda@tiscali.it> 17 - * - Switch from using a tasklet to a work queue, so the led_LCD_driver 18 - * can sleep. 19 - * David Pye <dmp@davidmpye.dyndns.org> 13 + * The LEDs can be configured at runtime in /sys/class/leds/ 20 14 */ 21 15 22 16 #include <linux/module.h> 23 - #include <linux/stddef.h> /* for offsetof() */ 24 17 #include <linux/init.h> 25 18 #include <linux/types.h> 26 19 #include <linux/ioport.h> 27 20 #include <linux/utsname.h> 28 21 #include <linux/capability.h> 29 22 #include <linux/delay.h> 30 - #include <linux/netdevice.h> 31 - #include <linux/inetdevice.h> 32 - #include <linux/in.h> 33 - #include <linux/interrupt.h> 34 - #include <linux/kernel_stat.h> 35 23 #include <linux/reboot.h> 36 - #include <linux/proc_fs.h> 37 - #include <linux/seq_file.h> 38 - #include <linux/ctype.h> 39 - #include <linux/blkdev.h> 40 - #include <linux/workqueue.h> 41 - #include <linux/rcupdate.h> 24 + #include <linux/uaccess.h> 25 + #include <linux/leds.h> 26 + #include <linux/platform_device.h> 27 + 42 28 #include <asm/io.h> 43 29 #include <asm/processor.h> 44 30 #include <asm/hardware.h> 45 31 #include <asm/param.h> /* HZ */ 46 32 #include <asm/led.h> 47 33 #include <asm/pdc.h> 48 - #include <linux/uaccess.h> 49 34 50 - /* The control of the LEDs and LCDs on PARISC-machines have to be done 51 - completely in software. The necessary calculations are done in a work queue 52 - task which is scheduled regularly, and since the calculations may consume a 53 - relatively large amount of CPU time, some of the calculations can be 54 - turned off with the following variables (controlled via procfs) */ 35 + #define LED_HAS_LCD 1 36 + #define LED_HAS_LED 2 55 37 56 - static int led_type __read_mostly = -1; 57 - static unsigned char lastleds; /* LED state from most recent update */ 58 - static unsigned int led_heartbeat __read_mostly = 1; 59 - static unsigned int led_diskio __read_mostly; 60 - static unsigned int led_lanrxtx __read_mostly; 61 - static char lcd_text[32] __read_mostly; 62 - static char lcd_text_default[32] __read_mostly; 63 - static int lcd_no_led_support __read_mostly = 0; /* KittyHawk doesn't support LED on its LCD */ 64 - 65 - 66 - static struct workqueue_struct *led_wq; 67 - static void led_work_func(struct work_struct *); 68 - static DECLARE_DELAYED_WORK(led_task, led_work_func); 69 - 70 - #if 0 71 - #define DPRINTK(x) printk x 72 - #else 73 - #define DPRINTK(x) 74 - #endif 38 + static unsigned char led_type; /* bitmask of LED_HAS_XXX */ 39 + static unsigned char lastleds; /* LED state from most recent update */ 40 + static unsigned char lcd_new_text; 41 + static unsigned char lcd_text[20]; 42 + static unsigned char lcd_text_default[20]; 43 + static unsigned char lcd_no_led_support; /* KittyHawk doesn't support LED on its LCD */ 75 44 76 45 struct lcd_block { 77 46 unsigned char command; /* stores the command byte */ ··· 49 80 }; 50 81 51 82 /* Structure returned by PDC_RETURN_CHASSIS_INFO */ 52 - /* NOTE: we use unsigned long:16 two times, since the following member 83 + /* NOTE: we use unsigned long:16 two times, since the following member 53 84 lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */ 54 85 struct pdc_chassis_lcd_info_ret_block { 55 86 unsigned long model:16; /* DISPLAY_MODEL_XXXX */ ··· 69 100 70 101 71 102 /* LCD_CMD and LCD_DATA for KittyHawk machines */ 72 - #define KITTYHAWK_LCD_CMD F_EXTEND(0xf0190000UL) /* 64bit-ready */ 73 - #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1) 103 + #define KITTYHAWK_LCD_CMD F_EXTEND(0xf0190000UL) 104 + #define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1) 74 105 75 - /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 106 + /* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's 76 107 * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */ 77 108 static struct pdc_chassis_lcd_info_ret_block 78 - lcd_info __attribute__((aligned(8))) __read_mostly = 109 + lcd_info __attribute__((aligned(8))) = 79 110 { 80 - .model = DISPLAY_MODEL_LCD, 111 + .model = DISPLAY_MODEL_NONE, 81 112 .lcd_width = 16, 82 113 .lcd_cmd_reg_addr = KITTYHAWK_LCD_CMD, 83 114 .lcd_data_reg_addr = KITTYHAWK_LCD_DATA, ··· 86 117 .reset_cmd2 = 0xc0, 87 118 }; 88 119 89 - 90 120 /* direct access to some of the lcd_info variables */ 91 - #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr 92 - #define LCD_DATA_REG lcd_info.lcd_data_reg_addr 121 + #define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr 122 + #define LCD_DATA_REG lcd_info.lcd_data_reg_addr 93 123 #define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */ 94 124 95 - #define LED_HASLCD 1 96 - #define LED_NOLCD 0 97 - 98 - /* The workqueue must be created at init-time */ 99 - static int start_task(void) 100 - { 101 - /* Display the default text now */ 102 - if (led_type == LED_HASLCD) lcd_print( lcd_text_default ); 103 - 104 - /* KittyHawk has no LED support on its LCD */ 105 - if (lcd_no_led_support) return 0; 106 - 107 - /* Create the work queue and queue the LED task */ 108 - led_wq = create_singlethread_workqueue("led_wq"); 109 - if (!led_wq) 110 - return -ENOMEM; 111 - 112 - queue_delayed_work(led_wq, &led_task, 0); 113 - 114 - return 0; 115 - } 116 - 117 - device_initcall(start_task); 118 - 119 125 /* ptr to LCD/LED-specific function */ 120 - static void (*led_func_ptr) (unsigned char) __read_mostly; 126 + static void (*led_func_ptr) (unsigned char); 121 127 122 - #ifdef CONFIG_PROC_FS 123 - static int led_proc_show(struct seq_file *m, void *v) 128 + 129 + static void lcd_print_now(void) 124 130 { 125 - switch ((long)m->private) 126 - { 127 - case LED_NOLCD: 128 - seq_printf(m, "Heartbeat: %d\n", led_heartbeat); 129 - seq_printf(m, "Disk IO: %d\n", led_diskio); 130 - seq_printf(m, "LAN Rx/Tx: %d\n", led_lanrxtx); 131 - break; 132 - case LED_HASLCD: 133 - seq_printf(m, "%s\n", lcd_text); 134 - break; 135 - default: 136 - return 0; 131 + int i; 132 + char *str = lcd_text; 133 + 134 + if (lcd_info.model != DISPLAY_MODEL_LCD) 135 + return; 136 + 137 + if (!lcd_new_text) 138 + return; 139 + lcd_new_text = 0; 140 + 141 + /* Set LCD Cursor to 1st character */ 142 + gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG); 143 + udelay(lcd_info.min_cmd_delay); 144 + 145 + /* Print the string */ 146 + for (i = 0; i < lcd_info.lcd_width; i++) { 147 + gsc_writeb(*str ? *str++ : ' ', LCD_DATA_REG); 148 + udelay(lcd_info.min_cmd_delay); 137 149 } 138 - return 0; 139 150 } 140 151 141 - static int led_proc_open(struct inode *inode, struct file *file) 142 - { 143 - return single_open(file, led_proc_show, pde_data(inode)); 144 - } 145 - 146 - 147 - static ssize_t led_proc_write(struct file *file, const char __user *buf, 148 - size_t count, loff_t *pos) 149 - { 150 - void *data = pde_data(file_inode(file)); 151 - char *cur, lbuf[32]; 152 - int d; 153 - 154 - if (!capable(CAP_SYS_ADMIN)) 155 - return -EACCES; 156 - 157 - if (count >= sizeof(lbuf)) 158 - count = sizeof(lbuf)-1; 159 - 160 - if (copy_from_user(lbuf, buf, count)) 161 - return -EFAULT; 162 - lbuf[count] = 0; 163 - 164 - cur = lbuf; 165 - 166 - switch ((long)data) 167 - { 168 - case LED_NOLCD: 169 - d = *cur++ - '0'; 170 - if (d != 0 && d != 1) goto parse_error; 171 - led_heartbeat = d; 172 - 173 - if (*cur++ != ' ') goto parse_error; 174 - 175 - d = *cur++ - '0'; 176 - if (d != 0 && d != 1) goto parse_error; 177 - led_diskio = d; 178 - 179 - if (*cur++ != ' ') goto parse_error; 180 - 181 - d = *cur++ - '0'; 182 - if (d != 0 && d != 1) goto parse_error; 183 - led_lanrxtx = d; 184 - 185 - break; 186 - case LED_HASLCD: 187 - if (*cur && cur[strlen(cur)-1] == '\n') 188 - cur[strlen(cur)-1] = 0; 189 - if (*cur == 0) 190 - cur = lcd_text_default; 191 - lcd_print(cur); 192 - break; 193 - default: 194 - return 0; 195 - } 196 - 197 - return count; 198 - 199 - parse_error: 200 - if ((long)data == LED_NOLCD) 201 - printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n"); 202 - return -EINVAL; 203 - } 204 - 205 - static const struct proc_ops led_proc_ops = { 206 - .proc_open = led_proc_open, 207 - .proc_read = seq_read, 208 - .proc_lseek = seq_lseek, 209 - .proc_release = single_release, 210 - .proc_write = led_proc_write, 211 - }; 212 - 213 - static int __init led_create_procfs(void) 214 - { 215 - struct proc_dir_entry *proc_pdc_root = NULL; 216 - struct proc_dir_entry *ent; 217 - 218 - if (led_type == -1) return -1; 219 - 220 - proc_pdc_root = proc_mkdir("pdc", NULL); 221 - if (!proc_pdc_root) return -1; 222 - 223 - if (!lcd_no_led_support) 224 - { 225 - ent = proc_create_data("led", 0644, proc_pdc_root, 226 - &led_proc_ops, (void *)LED_NOLCD); /* LED */ 227 - if (!ent) return -1; 228 - } 229 - 230 - if (led_type == LED_HASLCD) 231 - { 232 - ent = proc_create_data("lcd", 0644, proc_pdc_root, 233 - &led_proc_ops, (void *)LED_HASLCD); /* LCD */ 234 - if (!ent) return -1; 235 - } 236 - 237 - return 0; 238 - } 239 - #endif 240 - 241 - /* 242 - ** 243 - ** led_ASP_driver() 244 - ** 152 + /** 153 + * lcd_print() 154 + * 155 + * @str: string to show on the LCD. If NULL, print current string again. 156 + * 157 + * Displays the given string on the LCD-Display of newer machines. 245 158 */ 159 + void lcd_print(const char *str) 160 + { 161 + /* copy display string to buffer for procfs */ 162 + if (str) 163 + strscpy(lcd_text, str, sizeof(lcd_text)); 164 + lcd_new_text = 1; 165 + 166 + /* print now if LCD without any LEDs */ 167 + if (led_type == LED_HAS_LCD) 168 + lcd_print_now(); 169 + } 170 + 246 171 #define LED_DATA 0x01 /* data to shift (0:on 1:off) */ 247 172 #define LED_STROBE 0x02 /* strobe to clock data */ 173 + 174 + /** 175 + * led_ASP_driver() - LED driver for the ASP controller chip 176 + * 177 + * @leds: bitmap representing the LED status 178 + */ 248 179 static void led_ASP_driver(unsigned char leds) 249 180 { 250 181 int i; ··· 159 290 } 160 291 } 161 292 162 - 163 - /* 164 - ** 165 - ** led_LASI_driver() 166 - ** 293 + /** 294 + * led_LASI_driver() - LED driver for the LASI controller chip 295 + * 296 + * @leds: bitmap representing the LED status 167 297 */ 168 298 static void led_LASI_driver(unsigned char leds) 169 299 { ··· 170 302 gsc_writeb( leds, LED_DATA_REG ); 171 303 } 172 304 173 - 174 - /* 175 - ** 176 - ** led_LCD_driver() 177 - ** 305 + /** 306 + * led_LCD_driver() - LED & LCD driver for LCD chips 307 + * 308 + * @leds: bitmap representing the LED status 178 309 */ 179 310 static void led_LCD_driver(unsigned char leds) 180 311 { 181 - static int i; 182 - static unsigned char mask[4] = { LED_HEARTBEAT, LED_DISK_IO, 312 + static const unsigned char mask[4] = { 313 + LED_HEARTBEAT, LED_DISK_IO, 183 314 LED_LAN_RCV, LED_LAN_TX }; 184 - 185 - static struct lcd_block * blockp[4] = { 315 + 316 + static struct lcd_block * const blockp[4] = { 186 317 &lcd_info.heartbeat, 187 318 &lcd_info.disk_io, 188 319 &lcd_info.lan_rcv, 189 320 &lcd_info.lan_tx 190 321 }; 322 + static unsigned char latest_leds; 323 + int i; 191 324 192 - /* Convert min_cmd_delay to milliseconds */ 193 - unsigned int msec_cmd_delay = 1 + (lcd_info.min_cmd_delay / 1000); 194 - 195 - for (i=0; i<4; ++i) 196 - { 197 - if ((leds & mask[i]) != (lastleds & mask[i])) 198 - { 199 - gsc_writeb( blockp[i]->command, LCD_CMD_REG ); 200 - msleep(msec_cmd_delay); 201 - 202 - gsc_writeb( leds & mask[i] ? blockp[i]->on : 203 - blockp[i]->off, LCD_DATA_REG ); 204 - msleep(msec_cmd_delay); 205 - } 325 + for (i = 0; i < 4; ++i) { 326 + if ((leds & mask[i]) == (latest_leds & mask[i])) 327 + continue; 328 + 329 + gsc_writeb( blockp[i]->command, LCD_CMD_REG ); 330 + udelay(lcd_info.min_cmd_delay); 331 + 332 + gsc_writeb( leds & mask[i] ? blockp[i]->on : 333 + blockp[i]->off, LCD_DATA_REG ); 334 + udelay(lcd_info.min_cmd_delay); 206 335 } 336 + latest_leds = leds; 337 + 338 + lcd_print_now(); 207 339 } 208 340 209 341 210 - /* 211 - ** 212 - ** led_get_net_activity() 213 - ** 214 - ** calculate if there was TX- or RX-throughput on the network interfaces 215 - ** (analog to dev_get_info() from net/core/dev.c) 216 - ** 342 + /** 343 + * lcd_system_halt() 344 + * 345 + * @nb: pointer to the notifier_block structure 346 + * @event: the event (SYS_RESTART, SYS_HALT or SYS_POWER_OFF) 347 + * @buf: pointer to a buffer (not used) 348 + * 349 + * Called by the reboot notifier chain at shutdown. Stops all 350 + * LED/LCD activities. 217 351 */ 218 - static __inline__ int led_get_net_activity(void) 219 - { 220 - #ifndef CONFIG_NET 221 - return 0; 222 - #else 223 - static u64 rx_total_last, tx_total_last; 224 - u64 rx_total, tx_total; 225 - struct net_device *dev; 226 - int retval; 227 - 228 - rx_total = tx_total = 0; 229 - 230 - /* we are running as a workqueue task, so we can use an RCU lookup */ 231 - rcu_read_lock(); 232 - for_each_netdev_rcu(&init_net, dev) { 233 - const struct rtnl_link_stats64 *stats; 234 - struct rtnl_link_stats64 temp; 235 - struct in_device *in_dev = __in_dev_get_rcu(dev); 236 - if (!in_dev || !in_dev->ifa_list) 237 - continue; 238 - if (ipv4_is_loopback(in_dev->ifa_list->ifa_local)) 239 - continue; 240 - stats = dev_get_stats(dev, &temp); 241 - rx_total += stats->rx_packets; 242 - tx_total += stats->tx_packets; 243 - } 244 - rcu_read_unlock(); 245 - 246 - retval = 0; 247 - 248 - if (rx_total != rx_total_last) { 249 - rx_total_last = rx_total; 250 - retval |= LED_LAN_RCV; 251 - } 252 - 253 - if (tx_total != tx_total_last) { 254 - tx_total_last = tx_total; 255 - retval |= LED_LAN_TX; 256 - } 257 - 258 - return retval; 259 - #endif 260 - } 261 - 262 - 263 - /* 264 - ** 265 - ** led_get_diskio_activity() 266 - ** 267 - ** calculate if there was disk-io in the system 268 - ** 269 - */ 270 - static __inline__ int led_get_diskio_activity(void) 271 - { 272 - static unsigned long last_pgpgin, last_pgpgout; 273 - unsigned long events[NR_VM_EVENT_ITEMS]; 274 - int changed; 275 - 276 - all_vm_events(events); 277 - 278 - /* Just use a very simple calculation here. Do not care about overflow, 279 - since we only want to know if there was activity or not. */ 280 - changed = (events[PGPGIN] != last_pgpgin) || 281 - (events[PGPGOUT] != last_pgpgout); 282 - last_pgpgin = events[PGPGIN]; 283 - last_pgpgout = events[PGPGOUT]; 284 - 285 - return (changed ? LED_DISK_IO : 0); 286 - } 287 - 288 - 289 - 290 - /* 291 - ** led_work_func() 292 - ** 293 - ** manages when and which chassis LCD/LED gets updated 294 - 295 - TODO: 296 - - display load average (older machines like 715/64 have 4 "free" LED's for that) 297 - - optimizations 298 - */ 299 - 300 - #define HEARTBEAT_LEN (HZ*10/100) 301 - #define HEARTBEAT_2ND_RANGE_START (HZ*28/100) 302 - #define HEARTBEAT_2ND_RANGE_END (HEARTBEAT_2ND_RANGE_START + HEARTBEAT_LEN) 303 - 304 - #define LED_UPDATE_INTERVAL (1 + (HZ*19/1000)) 305 - 306 - static void led_work_func (struct work_struct *unused) 352 + static int lcd_system_halt(struct notifier_block *nb, unsigned long event, void *buf) 307 353 { 308 - static unsigned long last_jiffies; 309 - static unsigned long count_HZ; /* counter in range 0..HZ */ 310 - unsigned char currentleds = 0; /* stores current value of the LEDs */ 354 + const char *txt; 311 355 312 - /* exit if not initialized */ 313 - if (!led_func_ptr) 314 - return; 315 - 316 - /* increment the heartbeat timekeeper */ 317 - count_HZ += jiffies - last_jiffies; 318 - last_jiffies = jiffies; 319 - if (count_HZ >= HZ) 320 - count_HZ = 0; 321 - 322 - if (likely(led_heartbeat)) 323 - { 324 - /* flash heartbeat-LED like a real heart 325 - * (2 x short then a long delay) 326 - */ 327 - if (count_HZ < HEARTBEAT_LEN || 328 - (count_HZ >= HEARTBEAT_2ND_RANGE_START && 329 - count_HZ < HEARTBEAT_2ND_RANGE_END)) 330 - currentleds |= LED_HEARTBEAT; 331 - } 332 - 333 - if (likely(led_lanrxtx)) currentleds |= led_get_net_activity(); 334 - if (likely(led_diskio)) currentleds |= led_get_diskio_activity(); 335 - 336 - /* blink LEDs if we got an Oops (HPMC) */ 337 - if (unlikely(oops_in_progress)) { 338 - if (boot_cpu_data.cpu_type >= pcxl2) { 339 - /* newer machines don't have loadavg. LEDs, so we 340 - * let all LEDs blink twice per second instead */ 341 - currentleds = (count_HZ <= (HZ/2)) ? 0 : 0xff; 342 - } else { 343 - /* old machines: blink loadavg. LEDs twice per second */ 344 - if (count_HZ <= (HZ/2)) 345 - currentleds &= ~(LED4|LED5|LED6|LED7); 346 - else 347 - currentleds |= (LED4|LED5|LED6|LED7); 348 - } 349 - } 350 - 351 - if (currentleds != lastleds) 352 - { 353 - led_func_ptr(currentleds); /* Update the LCD/LEDs */ 354 - lastleds = currentleds; 355 - } 356 - 357 - queue_delayed_work(led_wq, &led_task, LED_UPDATE_INTERVAL); 358 - } 359 - 360 - /* 361 - ** led_halt() 362 - ** 363 - ** called by the reboot notifier chain at shutdown and stops all 364 - ** LED/LCD activities. 365 - ** 366 - */ 367 - 368 - static int led_halt(struct notifier_block *, unsigned long, void *); 369 - 370 - static struct notifier_block led_notifier = { 371 - .notifier_call = led_halt, 372 - }; 373 - static int notifier_disabled = 0; 374 - 375 - static int led_halt(struct notifier_block *nb, unsigned long event, void *buf) 376 - { 377 - char *txt; 378 - 379 - if (notifier_disabled) 380 - return NOTIFY_OK; 381 - 382 - notifier_disabled = 1; 383 356 switch (event) { 384 357 case SYS_RESTART: txt = "SYSTEM RESTART"; 385 358 break; ··· 230 521 break; 231 522 default: return NOTIFY_DONE; 232 523 } 233 - 234 - /* Cancel the work item and delete the queue */ 235 - if (led_wq) { 236 - cancel_delayed_work_sync(&led_task); 237 - destroy_workqueue(led_wq); 238 - led_wq = NULL; 239 - } 240 - 241 - if (lcd_info.model == DISPLAY_MODEL_LCD) 242 - lcd_print(txt); 243 - else 244 - if (led_func_ptr) 245 - led_func_ptr(0xff); /* turn all LEDs ON */ 246 - 524 + 525 + lcd_print(txt); 526 + 247 527 return NOTIFY_OK; 248 528 } 249 529 250 - /* 251 - ** register_led_driver() 252 - ** 253 - ** registers an external LED or LCD for usage by this driver. 254 - ** currently only LCD-, LASI- and ASP-style LCD/LED's are supported. 255 - ** 256 - */ 530 + static struct notifier_block lcd_system_halt_notifier = { 531 + .notifier_call = lcd_system_halt, 532 + }; 257 533 534 + static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness); 535 + 536 + struct hppa_led { 537 + struct led_classdev led_cdev; 538 + unsigned char led_bit; 539 + }; 540 + #define to_hppa_led(d) container_of(d, struct hppa_led, led_cdev) 541 + 542 + typedef void (*set_handler)(struct led_classdev *, enum led_brightness); 543 + struct led_type { 544 + const char *name; 545 + set_handler handler; 546 + const char *default_trigger; 547 + }; 548 + 549 + #define NUM_LEDS_PER_BOARD 8 550 + struct hppa_drvdata { 551 + struct hppa_led leds[NUM_LEDS_PER_BOARD]; 552 + }; 553 + 554 + static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness) 555 + { 556 + struct hppa_led *p = to_hppa_led(led_cdev); 557 + unsigned char led_bit = p->led_bit; 558 + 559 + if (brightness == LED_OFF) 560 + lastleds &= ~led_bit; 561 + else 562 + lastleds |= led_bit; 563 + 564 + if (led_func_ptr) 565 + led_func_ptr(lastleds); 566 + } 567 + 568 + 569 + static int hppa_led_generic_probe(struct platform_device *pdev, 570 + struct led_type *types) 571 + { 572 + struct hppa_drvdata *p; 573 + int i, err; 574 + 575 + p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); 576 + if (!p) 577 + return -ENOMEM; 578 + 579 + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { 580 + struct led_classdev *lp = &p->leds[i].led_cdev; 581 + 582 + p->leds[i].led_bit = BIT(i); 583 + lp->name = types[i].name; 584 + lp->brightness = LED_FULL; 585 + lp->brightness_set = types[i].handler; 586 + lp->default_trigger = types[i].default_trigger; 587 + err = led_classdev_register(&pdev->dev, lp); 588 + if (err) { 589 + dev_err(&pdev->dev, "Could not register %s LED\n", 590 + lp->name); 591 + for (i--; i >= 0; i--) 592 + led_classdev_unregister(&p->leds[i].led_cdev); 593 + return err; 594 + } 595 + } 596 + 597 + platform_set_drvdata(pdev, p); 598 + 599 + return 0; 600 + } 601 + 602 + static int platform_led_remove(struct platform_device *pdev) 603 + { 604 + struct hppa_drvdata *p = platform_get_drvdata(pdev); 605 + int i; 606 + 607 + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) 608 + led_classdev_unregister(&p->leds[i].led_cdev); 609 + 610 + return 0; 611 + } 612 + 613 + static struct led_type mainboard_led_types[NUM_LEDS_PER_BOARD] = { 614 + { 615 + .name = "platform-lan-tx", 616 + .handler = set_led, 617 + .default_trigger = "tx", 618 + }, 619 + { 620 + .name = "platform-lan-rx", 621 + .handler = set_led, 622 + .default_trigger = "rx", 623 + }, 624 + { 625 + .name = "platform-disk", 626 + .handler = set_led, 627 + .default_trigger = "disk-activity", 628 + }, 629 + { 630 + .name = "platform-heartbeat", 631 + .handler = set_led, 632 + .default_trigger = "heartbeat", 633 + }, 634 + { 635 + .name = "platform-LED4", 636 + .handler = set_led, 637 + .default_trigger = "panic", 638 + }, 639 + { 640 + .name = "platform-LED5", 641 + .handler = set_led, 642 + .default_trigger = "panic", 643 + }, 644 + { 645 + .name = "platform-LED6", 646 + .handler = set_led, 647 + .default_trigger = "panic", 648 + }, 649 + { 650 + .name = "platform-LED7", 651 + .handler = set_led, 652 + .default_trigger = "panic", 653 + }, 654 + }; 655 + 656 + static int platform_led_probe(struct platform_device *pdev) 657 + { 658 + return hppa_led_generic_probe(pdev, mainboard_led_types); 659 + } 660 + 661 + MODULE_ALIAS("platform:platform-leds"); 662 + 663 + static struct platform_driver hppa_mainboard_led_driver = { 664 + .probe = platform_led_probe, 665 + .remove = platform_led_remove, 666 + .driver = { 667 + .name = "platform-leds", 668 + }, 669 + }; 670 + 671 + static struct platform_driver * const drivers[] = { 672 + &hppa_mainboard_led_driver, 673 + }; 674 + 675 + static struct platform_device platform_leds = { 676 + .name = "platform-leds", 677 + }; 678 + 679 + /** 680 + * register_led_driver() 681 + * 682 + * @model: model type, one of the DISPLAY_MODEL_XXXX values 683 + * @cmd_reg: physical address of cmd register for the LED/LCD 684 + * @data_reg: physical address of data register for the LED/LCD 685 + * 686 + * Registers a chassis LED or LCD which should be driven by this driver. 687 + * Only PDC-based, LASI- or ASP-style LEDs and LCDs are supported. 688 + */ 258 689 int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg) 259 690 { 260 - static int initialized; 261 - 262 - if (initialized || !data_reg) 691 + if (led_func_ptr || !data_reg) 263 692 return 1; 264 - 693 + 694 + /* No LEDs when running in QEMU */ 695 + if (running_on_qemu) 696 + return 1; 697 + 265 698 lcd_info.model = model; /* store the values */ 266 699 LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg; 267 700 268 701 switch (lcd_info.model) { 269 702 case DISPLAY_MODEL_LCD: 270 703 LCD_DATA_REG = data_reg; 271 - printk(KERN_INFO "LCD display at %lx,%lx registered\n", 704 + pr_info("led: LCD display at %#lx and %#lx\n", 272 705 LCD_CMD_REG , LCD_DATA_REG); 273 706 led_func_ptr = led_LCD_driver; 274 - led_type = LED_HASLCD; 707 + if (lcd_no_led_support) 708 + led_type = LED_HAS_LCD; 709 + else 710 + led_type = LED_HAS_LCD | LED_HAS_LED; 275 711 break; 276 712 277 713 case DISPLAY_MODEL_LASI: 278 - /* Skip to register LED in QEMU */ 279 - if (running_on_qemu) 280 - return 1; 281 714 LED_DATA_REG = data_reg; 282 715 led_func_ptr = led_LASI_driver; 283 - printk(KERN_INFO "LED display at %lx registered\n", LED_DATA_REG); 284 - led_type = LED_NOLCD; 716 + pr_info("led: LED display at %#lx\n", LED_DATA_REG); 717 + led_type = LED_HAS_LED; 285 718 break; 286 719 287 720 case DISPLAY_MODEL_OLD_ASP: 288 721 LED_DATA_REG = data_reg; 289 722 led_func_ptr = led_ASP_driver; 290 - printk(KERN_INFO "LED (ASP-style) display at %lx registered\n", 723 + pr_info("led: LED (ASP-style) display at %#lx\n", 291 724 LED_DATA_REG); 292 - led_type = LED_NOLCD; 725 + led_type = LED_HAS_LED; 293 726 break; 294 727 295 728 default: 296 - printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n", 297 - __func__, lcd_info.model); 729 + pr_err("led: Unknown LCD/LED model type %d\n", lcd_info.model); 298 730 return 1; 299 731 } 300 - 301 - /* mark the LCD/LED driver now as initialized and 302 - * register to the reboot notifier chain */ 303 - initialized++; 304 - register_reboot_notifier(&led_notifier); 305 732 306 - /* Ensure the work is queued */ 307 - if (led_wq) { 308 - queue_delayed_work(led_wq, &led_task, 0); 309 - } 733 + platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 310 734 311 - return 0; 735 + return register_reboot_notifier(&lcd_system_halt_notifier); 312 736 } 313 737 314 - /* 315 - ** register_led_regions() 316 - ** 317 - ** register_led_regions() registers the LCD/LED regions for /procfs. 318 - ** At bootup - where the initialisation of the LCD/LED normally happens - 319 - ** not all internal structures of request_region() are properly set up, 320 - ** so that we delay the led-registration until after busdevices_init() 321 - ** has been executed. 322 - ** 738 + /** 739 + * early_led_init() 740 + * 741 + * early_led_init() is called early in the bootup-process and asks the 742 + * PDC for an usable chassis LCD or LED. If the PDC doesn't return any 743 + * info, then a LED might be detected by the LASI or ASP drivers later. 744 + * KittyHawk machines have often a buggy PDC, so that we explicitly check 745 + * for those machines here. 323 746 */ 747 + static int __init early_led_init(void) 748 + { 749 + struct pdc_chassis_info chassis_info; 750 + int ret; 324 751 325 - static int __init register_led_regions(void) 752 + snprintf(lcd_text_default, sizeof(lcd_text_default), 753 + "Linux %s", init_utsname()->release); 754 + strcpy(lcd_text, lcd_text_default); 755 + lcd_new_text = 1; 756 + 757 + /* Work around the buggy PDC of KittyHawk-machines */ 758 + switch (CPU_HVERSION) { 759 + case 0x580: /* KittyHawk DC2-100 (K100) */ 760 + case 0x581: /* KittyHawk DC3-120 (K210) */ 761 + case 0x582: /* KittyHawk DC3 100 (K400) */ 762 + case 0x583: /* KittyHawk DC3 120 (K410) */ 763 + case 0x58B: /* KittyHawk DC2 100 (K200) */ 764 + pr_info("LCD on KittyHawk-Machine found.\n"); 765 + lcd_info.model = DISPLAY_MODEL_LCD; 766 + /* KittyHawk has no LED support on its LCD, so skip LED detection */ 767 + lcd_no_led_support = 1; 768 + goto found; /* use the preinitialized values of lcd_info */ 769 + } 770 + 771 + /* initialize the struct, so that we can check for valid return values */ 772 + chassis_info.actcnt = chassis_info.maxcnt = 0; 773 + 774 + ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info)); 775 + if (ret != PDC_OK) { 776 + not_found: 777 + lcd_info.model = DISPLAY_MODEL_NONE; 778 + return 1; 779 + } 780 + 781 + /* check the results. Some machines have a buggy PDC */ 782 + if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt) 783 + goto not_found; 784 + 785 + switch (lcd_info.model) { 786 + case DISPLAY_MODEL_LCD: /* LCD display */ 787 + if (chassis_info.actcnt < 788 + offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1) 789 + goto not_found; 790 + if (!lcd_info.act_enable) { 791 + /* PDC tells LCD should not be used. */ 792 + goto not_found; 793 + } 794 + break; 795 + 796 + case DISPLAY_MODEL_NONE: /* no LED or LCD available */ 797 + goto not_found; 798 + 799 + case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */ 800 + if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32) 801 + goto not_found; 802 + break; 803 + 804 + default: 805 + pr_warn("PDC reported unknown LCD/LED model %d\n", 806 + lcd_info.model); 807 + goto not_found; 808 + } 809 + 810 + found: 811 + /* register the LCD/LED driver */ 812 + return register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG); 813 + } 814 + arch_initcall(early_led_init); 815 + 816 + /** 817 + * register_led_regions() 818 + * 819 + * Register_led_regions() registers the LCD/LED regions for /procfs. 820 + * At bootup - where the initialisation of the LCD/LED often happens 821 + * not all internal structures of request_region() are properly set up, 822 + * so that we delay the led-registration until after busdevices_init() 823 + * has been executed. 824 + */ 825 + static void __init register_led_regions(void) 326 826 { 327 827 switch (lcd_info.model) { 328 828 case DISPLAY_MODEL_LCD: ··· 543 625 request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data"); 544 626 break; 545 627 } 628 + } 629 + 630 + static int __init startup_leds(void) 631 + { 632 + if (platform_device_register(&platform_leds)) 633 + printk(KERN_INFO "LED: failed to register LEDs\n"); 634 + register_led_regions(); 546 635 return 0; 547 636 } 548 - late_initcall(register_led_regions); 549 - 550 - 551 - /* 552 - ** 553 - ** lcd_print() 554 - ** 555 - ** Displays the given string on the LCD-Display of newer machines. 556 - ** lcd_print() disables/enables the timer-based led work queue to 557 - ** avoid a race condition while writing the CMD/DATA register pair. 558 - ** 559 - */ 560 - int lcd_print( const char *str ) 561 - { 562 - int i; 563 - 564 - if (!led_func_ptr || lcd_info.model != DISPLAY_MODEL_LCD) 565 - return 0; 566 - 567 - /* temporarily disable the led work task */ 568 - if (led_wq) 569 - cancel_delayed_work_sync(&led_task); 570 - 571 - /* copy display string to buffer for procfs */ 572 - strscpy(lcd_text, str, sizeof(lcd_text)); 573 - 574 - /* Set LCD Cursor to 1st character */ 575 - gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG); 576 - udelay(lcd_info.min_cmd_delay); 577 - 578 - /* Print the string */ 579 - for (i=0; i < lcd_info.lcd_width; i++) { 580 - if (str && *str) 581 - gsc_writeb(*str++, LCD_DATA_REG); 582 - else 583 - gsc_writeb(' ', LCD_DATA_REG); 584 - udelay(lcd_info.min_cmd_delay); 585 - } 586 - 587 - /* re-queue the work */ 588 - if (led_wq) { 589 - queue_delayed_work(led_wq, &led_task, 0); 590 - } 591 - 592 - return lcd_info.lcd_width; 593 - } 594 - 595 - /* 596 - ** led_init() 597 - ** 598 - ** led_init() is called very early in the bootup-process from setup.c 599 - ** and asks the PDC for an usable chassis LCD or LED. 600 - ** If the PDC doesn't return any info, then the LED 601 - ** is detected by lasi.c or asp.c and registered with the 602 - ** above functions lasi_led_init() or asp_led_init(). 603 - ** KittyHawk machines have often a buggy PDC, so that 604 - ** we explicitly check for those machines here. 605 - */ 606 - 607 - int __init led_init(void) 608 - { 609 - struct pdc_chassis_info chassis_info; 610 - int ret; 611 - 612 - snprintf(lcd_text_default, sizeof(lcd_text_default), 613 - "Linux %s", init_utsname()->release); 614 - 615 - /* Work around the buggy PDC of KittyHawk-machines */ 616 - switch (CPU_HVERSION) { 617 - case 0x580: /* KittyHawk DC2-100 (K100) */ 618 - case 0x581: /* KittyHawk DC3-120 (K210) */ 619 - case 0x582: /* KittyHawk DC3 100 (K400) */ 620 - case 0x583: /* KittyHawk DC3 120 (K410) */ 621 - case 0x58B: /* KittyHawk DC2 100 (K200) */ 622 - printk(KERN_INFO "%s: KittyHawk-Machine (hversion 0x%x) found, " 623 - "LED detection skipped.\n", __FILE__, CPU_HVERSION); 624 - lcd_no_led_support = 1; 625 - goto found; /* use the preinitialized values of lcd_info */ 626 - } 627 - 628 - /* initialize the struct, so that we can check for valid return values */ 629 - lcd_info.model = DISPLAY_MODEL_NONE; 630 - chassis_info.actcnt = chassis_info.maxcnt = 0; 631 - 632 - ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info)); 633 - if (ret == PDC_OK) { 634 - DPRINTK((KERN_INFO "%s: chassis info: model=%d (%s), " 635 - "lcd_width=%d, cmd_delay=%u,\n" 636 - "%s: sizecnt=%d, actcnt=%ld, maxcnt=%ld\n", 637 - __FILE__, lcd_info.model, 638 - (lcd_info.model==DISPLAY_MODEL_LCD) ? "LCD" : 639 - (lcd_info.model==DISPLAY_MODEL_LASI) ? "LED" : "unknown", 640 - lcd_info.lcd_width, lcd_info.min_cmd_delay, 641 - __FILE__, sizeof(lcd_info), 642 - chassis_info.actcnt, chassis_info.maxcnt)); 643 - DPRINTK((KERN_INFO "%s: cmd=%p, data=%p, reset1=%x, reset2=%x, act_enable=%d\n", 644 - __FILE__, lcd_info.lcd_cmd_reg_addr, 645 - lcd_info.lcd_data_reg_addr, lcd_info.reset_cmd1, 646 - lcd_info.reset_cmd2, lcd_info.act_enable )); 647 - 648 - /* check the results. Some machines have a buggy PDC */ 649 - if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt) 650 - goto not_found; 651 - 652 - switch (lcd_info.model) { 653 - case DISPLAY_MODEL_LCD: /* LCD display */ 654 - if (chassis_info.actcnt < 655 - offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1) 656 - goto not_found; 657 - if (!lcd_info.act_enable) { 658 - DPRINTK((KERN_INFO "PDC prohibited usage of the LCD.\n")); 659 - goto not_found; 660 - } 661 - break; 662 - 663 - case DISPLAY_MODEL_NONE: /* no LED or LCD available */ 664 - printk(KERN_INFO "PDC reported no LCD or LED.\n"); 665 - goto not_found; 666 - 667 - case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */ 668 - if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32) 669 - goto not_found; 670 - break; 671 - 672 - default: 673 - printk(KERN_WARNING "PDC reported unknown LCD/LED model %d\n", 674 - lcd_info.model); 675 - goto not_found; 676 - } /* switch() */ 677 - 678 - found: 679 - /* register the LCD/LED driver */ 680 - register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG); 681 - return 0; 682 - 683 - } else { /* if() */ 684 - DPRINTK((KERN_INFO "pdc_chassis_info call failed with retval = %d\n", ret)); 685 - } 686 - 687 - not_found: 688 - lcd_info.model = DISPLAY_MODEL_NONE; 689 - return 1; 690 - } 691 - 692 - static void __exit led_exit(void) 693 - { 694 - unregister_reboot_notifier(&led_notifier); 695 - return; 696 - } 697 - 698 - #ifdef CONFIG_PROC_FS 699 - module_init(led_create_procfs) 700 - #endif 637 + device_initcall(startup_leds);