554 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Chassis LCD/LED driver for HP-PARISC workstations4 *5 * (c) Copyright 2000 Red Hat Software6 * (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 *10 * The control of the LEDs and LCDs on PARISC machines has to be done11 * completely in software.12 *13 * The LEDs can be configured at runtime in /sys/class/leds/14 */15 16#include <linux/module.h>17#include <linux/init.h>18#include <linux/types.h>19#include <linux/ioport.h>20#include <linux/utsname.h>21#include <linux/capability.h>22#include <linux/delay.h>23#include <linux/reboot.h>24#include <linux/uaccess.h>25#include <linux/leds.h>26#include <linux/platform_device.h>27 28#include <asm/io.h>29#include <asm/processor.h>30#include <asm/hardware.h>31#include <asm/param.h> /* HZ */32#include <asm/led.h>33#include <asm/pdc.h>34 35#define LED_HAS_LCD 136#define LED_HAS_LED 237 38static unsigned char led_type; /* bitmask of LED_HAS_XXX */39static unsigned char lastleds; /* LED state from most recent update */40static unsigned char lcd_new_text;41static unsigned char lcd_text[20];42static unsigned char lcd_text_default[20];43static unsigned char lcd_no_led_support; /* KittyHawk doesn't support LED on its LCD */44 45struct lcd_block {46 unsigned char command; /* stores the command byte */47 unsigned char on; /* value for turning LED on */48 unsigned char off; /* value for turning LED off */49};50 51/* Structure returned by PDC_RETURN_CHASSIS_INFO */52/* NOTE: we use unsigned long:16 two times, since the following member53 lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */54struct pdc_chassis_lcd_info_ret_block {55 unsigned long model:16; /* DISPLAY_MODEL_XXXX */56 unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */57 unsigned long lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */58 unsigned long lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */59 unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */60 unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */61 unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */62 unsigned char act_enable; /* 0 = no activity (LCD only) */63 struct lcd_block heartbeat;64 struct lcd_block disk_io;65 struct lcd_block lan_rcv;66 struct lcd_block lan_tx;67 char _pad;68};69 70 71/* LCD_CMD and LCD_DATA for KittyHawk machines */72#define KITTYHAWK_LCD_CMD F_EXTEND(0xf0190000UL)73#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1)74 75/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's76 * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */77static struct pdc_chassis_lcd_info_ret_block78lcd_info __attribute__((aligned(8))) =79{80 .model = DISPLAY_MODEL_NONE,81 .lcd_width = 16,82 .lcd_cmd_reg_addr = KITTYHAWK_LCD_CMD,83 .lcd_data_reg_addr = KITTYHAWK_LCD_DATA,84 .min_cmd_delay = 80,85 .reset_cmd1 = 0x80,86 .reset_cmd2 = 0xc0,87};88 89/* direct access to some of the lcd_info variables */90#define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr91#define LCD_DATA_REG lcd_info.lcd_data_reg_addr92#define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */93 94/* ptr to LCD/LED-specific function */95static void (*led_func_ptr) (unsigned char);96 97 98static void lcd_print_now(void)99{100 int i;101 char *str = lcd_text;102 103 if (lcd_info.model != DISPLAY_MODEL_LCD)104 return;105 106 if (!lcd_new_text)107 return;108 lcd_new_text = 0;109 110 /* Set LCD Cursor to 1st character */111 gsc_writeb(lcd_info.reset_cmd1, LCD_CMD_REG);112 udelay(lcd_info.min_cmd_delay);113 114 /* Print the string */115 for (i = 0; i < lcd_info.lcd_width; i++) {116 gsc_writeb(*str ? *str++ : ' ', LCD_DATA_REG);117 udelay(lcd_info.min_cmd_delay);118 }119}120 121/**122 * lcd_print()123 *124 * @str: string to show on the LCD. If NULL, print current string again.125 *126 * Displays the given string on the LCD-Display of newer machines.127 */128void lcd_print(const char *str)129{130 /* copy display string to buffer for procfs */131 if (str)132 strscpy(lcd_text, str, sizeof(lcd_text));133 lcd_new_text = 1;134 135 /* print now if LCD without any LEDs */136 if (led_type == LED_HAS_LCD)137 lcd_print_now();138}139 140#define LED_DATA 0x01 /* data to shift (0:on 1:off) */141#define LED_STROBE 0x02 /* strobe to clock data */142 143/**144 * led_ASP_driver() - LED driver for the ASP controller chip145 *146 * @leds: bitmap representing the LED status147 */148static void led_ASP_driver(unsigned char leds)149{150 int i;151 152 leds = ~leds;153 for (i = 0; i < 8; i++) {154 unsigned char value;155 value = (leds & 0x80) >> 7;156 gsc_writeb( value, LED_DATA_REG );157 gsc_writeb( value | LED_STROBE, LED_DATA_REG );158 leds <<= 1;159 }160}161 162/**163 * led_LASI_driver() - LED driver for the LASI controller chip164 *165 * @leds: bitmap representing the LED status166 */167static void led_LASI_driver(unsigned char leds)168{169 leds = ~leds;170 gsc_writeb( leds, LED_DATA_REG );171}172 173/**174 * led_LCD_driver() - LED & LCD driver for LCD chips175 *176 * @leds: bitmap representing the LED status177 */178static void led_LCD_driver(unsigned char leds)179{180 static const unsigned char mask[4] = {181 LED_HEARTBEAT, LED_DISK_IO,182 LED_LAN_RCV, LED_LAN_TX };183 184 static struct lcd_block * const blockp[4] = {185 &lcd_info.heartbeat,186 &lcd_info.disk_io,187 &lcd_info.lan_rcv,188 &lcd_info.lan_tx189 };190 static unsigned char latest_leds;191 int i;192 193 for (i = 0; i < 4; ++i) {194 if ((leds & mask[i]) == (latest_leds & mask[i]))195 continue;196 197 gsc_writeb( blockp[i]->command, LCD_CMD_REG );198 udelay(lcd_info.min_cmd_delay);199 200 gsc_writeb( leds & mask[i] ? blockp[i]->on :201 blockp[i]->off, LCD_DATA_REG );202 udelay(lcd_info.min_cmd_delay);203 }204 latest_leds = leds;205 206 lcd_print_now();207}208 209 210/**211 * lcd_system_halt()212 *213 * @nb: pointer to the notifier_block structure214 * @event: the event (SYS_RESTART, SYS_HALT or SYS_POWER_OFF)215 * @buf: pointer to a buffer (not used)216 *217 * Called by the reboot notifier chain at shutdown. Stops all218 * LED/LCD activities.219 */220static int lcd_system_halt(struct notifier_block *nb, unsigned long event, void *buf)221{222 const char *txt;223 224 switch (event) {225 case SYS_RESTART: txt = "SYSTEM RESTART";226 break;227 case SYS_HALT: txt = "SYSTEM HALT";228 break;229 case SYS_POWER_OFF: txt = "SYSTEM POWER OFF";230 break;231 default: return NOTIFY_DONE;232 }233 234 lcd_print(txt);235 236 return NOTIFY_OK;237}238 239static struct notifier_block lcd_system_halt_notifier = {240 .notifier_call = lcd_system_halt,241};242 243static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness);244 245struct hppa_led {246 struct led_classdev led_cdev;247 unsigned char led_bit;248};249#define to_hppa_led(d) container_of(d, struct hppa_led, led_cdev)250 251typedef void (*set_handler)(struct led_classdev *, enum led_brightness);252struct led_type {253 const char *name;254 set_handler handler;255 const char *default_trigger;256};257 258#define NUM_LEDS_PER_BOARD 8259struct hppa_drvdata {260 struct hppa_led leds[NUM_LEDS_PER_BOARD];261};262 263static void set_led(struct led_classdev *led_cdev, enum led_brightness brightness)264{265 struct hppa_led *p = to_hppa_led(led_cdev);266 unsigned char led_bit = p->led_bit;267 268 if (brightness == LED_OFF)269 lastleds &= ~led_bit;270 else271 lastleds |= led_bit;272 273 if (led_func_ptr)274 led_func_ptr(lastleds);275}276 277 278static int hppa_led_generic_probe(struct platform_device *pdev,279 struct led_type *types)280{281 struct hppa_drvdata *p;282 int i, err;283 284 p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);285 if (!p)286 return -ENOMEM;287 288 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {289 struct led_classdev *lp = &p->leds[i].led_cdev;290 291 p->leds[i].led_bit = BIT(i);292 lp->name = types[i].name;293 lp->brightness = LED_FULL;294 lp->brightness_set = types[i].handler;295 lp->default_trigger = types[i].default_trigger;296 err = led_classdev_register(&pdev->dev, lp);297 if (err) {298 dev_err(&pdev->dev, "Could not register %s LED\n",299 lp->name);300 for (i--; i >= 0; i--)301 led_classdev_unregister(&p->leds[i].led_cdev);302 return err;303 }304 }305 306 platform_set_drvdata(pdev, p);307 308 return 0;309}310 311static void platform_led_remove(struct platform_device *pdev)312{313 struct hppa_drvdata *p = platform_get_drvdata(pdev);314 int i;315 316 for (i = 0; i < NUM_LEDS_PER_BOARD; i++)317 led_classdev_unregister(&p->leds[i].led_cdev);318}319 320static struct led_type mainboard_led_types[NUM_LEDS_PER_BOARD] = {321 {322 .name = "platform-lan-tx",323 .handler = set_led,324 .default_trigger = "tx",325 },326 {327 .name = "platform-lan-rx",328 .handler = set_led,329 .default_trigger = "rx",330 },331 {332 .name = "platform-disk",333 .handler = set_led,334 .default_trigger = "disk-activity",335 },336 {337 .name = "platform-heartbeat",338 .handler = set_led,339 .default_trigger = "heartbeat",340 },341 {342 .name = "platform-LED4",343 .handler = set_led,344 .default_trigger = "panic",345 },346 {347 .name = "platform-LED5",348 .handler = set_led,349 .default_trigger = "panic",350 },351 {352 .name = "platform-LED6",353 .handler = set_led,354 .default_trigger = "panic",355 },356 {357 .name = "platform-LED7",358 .handler = set_led,359 .default_trigger = "panic",360 },361};362 363static int platform_led_probe(struct platform_device *pdev)364{365 return hppa_led_generic_probe(pdev, mainboard_led_types);366}367 368MODULE_ALIAS("platform:platform-leds");369 370static struct platform_driver hppa_mainboard_led_driver = {371 .probe = platform_led_probe,372 .remove_new = platform_led_remove,373 .driver = {374 .name = "platform-leds",375 },376};377 378static struct platform_driver * const drivers[] = {379 &hppa_mainboard_led_driver,380};381 382static struct platform_device platform_leds = {383 .name = "platform-leds",384};385 386/**387 * register_led_driver()388 *389 * @model: model type, one of the DISPLAY_MODEL_XXXX values390 * @cmd_reg: physical address of cmd register for the LED/LCD391 * @data_reg: physical address of data register for the LED/LCD392 *393 * Registers a chassis LED or LCD which should be driven by this driver.394 * Only PDC-based, LASI- or ASP-style LEDs and LCDs are supported.395 */396int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long data_reg)397{398 if (led_func_ptr || !data_reg)399 return 1;400 401 /* No LEDs when running in QEMU */402 if (running_on_qemu)403 return 1;404 405 lcd_info.model = model; /* store the values */406 LCD_CMD_REG = (cmd_reg == LED_CMD_REG_NONE) ? 0 : cmd_reg;407 408 switch (lcd_info.model) {409 case DISPLAY_MODEL_LCD:410 LCD_DATA_REG = data_reg;411 pr_info("led: LCD display at %#lx and %#lx\n",412 LCD_CMD_REG , LCD_DATA_REG);413 led_func_ptr = led_LCD_driver;414 if (lcd_no_led_support)415 led_type = LED_HAS_LCD;416 else417 led_type = LED_HAS_LCD | LED_HAS_LED;418 break;419 420 case DISPLAY_MODEL_LASI:421 LED_DATA_REG = data_reg;422 led_func_ptr = led_LASI_driver;423 pr_info("led: LED display at %#lx\n", LED_DATA_REG);424 led_type = LED_HAS_LED;425 break;426 427 case DISPLAY_MODEL_OLD_ASP:428 LED_DATA_REG = data_reg;429 led_func_ptr = led_ASP_driver;430 pr_info("led: LED (ASP-style) display at %#lx\n",431 LED_DATA_REG);432 led_type = LED_HAS_LED;433 break;434 435 default:436 pr_err("led: Unknown LCD/LED model type %d\n", lcd_info.model);437 return 1;438 }439 440 platform_register_drivers(drivers, ARRAY_SIZE(drivers));441 442 return register_reboot_notifier(&lcd_system_halt_notifier);443}444 445/**446 * early_led_init()447 *448 * early_led_init() is called early in the bootup-process and asks the449 * PDC for an usable chassis LCD or LED. If the PDC doesn't return any450 * info, then a LED might be detected by the LASI or ASP drivers later.451 * KittyHawk machines have often a buggy PDC, so that we explicitly check452 * for those machines here.453 */454static int __init early_led_init(void)455{456 struct pdc_chassis_info chassis_info;457 int ret;458 459 snprintf(lcd_text_default, sizeof(lcd_text_default),460 "Linux %s", init_utsname()->release);461 strcpy(lcd_text, lcd_text_default);462 lcd_new_text = 1;463 464 /* Work around the buggy PDC of KittyHawk-machines */465 switch (CPU_HVERSION) {466 case 0x580: /* KittyHawk DC2-100 (K100) */467 case 0x581: /* KittyHawk DC3-120 (K210) */468 case 0x582: /* KittyHawk DC3 100 (K400) */469 case 0x583: /* KittyHawk DC3 120 (K410) */470 case 0x58B: /* KittyHawk DC2 100 (K200) */471 pr_info("LCD on KittyHawk-Machine found.\n");472 lcd_info.model = DISPLAY_MODEL_LCD;473 /* KittyHawk has no LED support on its LCD, so skip LED detection */474 lcd_no_led_support = 1;475 goto found; /* use the preinitialized values of lcd_info */476 }477 478 /* initialize the struct, so that we can check for valid return values */479 chassis_info.actcnt = chassis_info.maxcnt = 0;480 481 ret = pdc_chassis_info(&chassis_info, &lcd_info, sizeof(lcd_info));482 if (ret != PDC_OK) {483not_found:484 lcd_info.model = DISPLAY_MODEL_NONE;485 return 1;486 }487 488 /* check the results. Some machines have a buggy PDC */489 if (chassis_info.actcnt <= 0 || chassis_info.actcnt != chassis_info.maxcnt)490 goto not_found;491 492 switch (lcd_info.model) {493 case DISPLAY_MODEL_LCD: /* LCD display */494 if (chassis_info.actcnt <495 offsetof(struct pdc_chassis_lcd_info_ret_block, _pad)-1)496 goto not_found;497 if (!lcd_info.act_enable) {498 /* PDC tells LCD should not be used. */499 goto not_found;500 }501 break;502 503 case DISPLAY_MODEL_NONE: /* no LED or LCD available */504 goto not_found;505 506 case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */507 if (chassis_info.actcnt != 8 && chassis_info.actcnt != 32)508 goto not_found;509 break;510 511 default:512 pr_warn("PDC reported unknown LCD/LED model %d\n",513 lcd_info.model);514 goto not_found;515 }516 517found:518 /* register the LCD/LED driver */519 return register_led_driver(lcd_info.model, LCD_CMD_REG, LCD_DATA_REG);520}521arch_initcall(early_led_init);522 523/**524 * register_led_regions()525 *526 * Register_led_regions() registers the LCD/LED regions for /procfs.527 * At bootup - where the initialisation of the LCD/LED often happens528 * not all internal structures of request_region() are properly set up,529 * so that we delay the led-registration until after busdevices_init()530 * has been executed.531 */532static void __init register_led_regions(void)533{534 switch (lcd_info.model) {535 case DISPLAY_MODEL_LCD:536 request_mem_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd");537 request_mem_region((unsigned long)LCD_DATA_REG, 1, "lcd_data");538 break;539 case DISPLAY_MODEL_LASI:540 case DISPLAY_MODEL_OLD_ASP:541 request_mem_region((unsigned long)LED_DATA_REG, 1, "led_data");542 break;543 }544}545 546static int __init startup_leds(void)547{548 if (platform_device_register(&platform_leds))549 printk(KERN_INFO "LED: failed to register LEDs\n");550 register_led_regions();551 return 0;552}553device_initcall(startup_leds);554