Skip to content

Commit

Permalink
thinkpad-acpi: enhance led support
Browse files Browse the repository at this point in the history
Add support for extra LEDs on recent ThinkPads, and avoid registering
with the led class the LEDs which are not available for a given
ThinkPad model.

All non-restricted LEDs are always available through the procfs
interface, as the firmware doesn't care if an attempt is made to
access an invalid LED.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Henrique de Moraes Holschuh authored and Len Brown committed Jun 18, 2009
1 parent 6020173 commit f21179a
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 15 deletions.
23 changes: 20 additions & 3 deletions Documentation/laptops/thinkpad-acpi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ The available commands are:
echo '<LED number> off' >/proc/acpi/ibm/led
echo '<LED number> blink' >/proc/acpi/ibm/led

The <LED number> range is 0 to 7. The set of LEDs that can be
The <LED number> range is 0 to 15. The set of LEDs that can be
controlled varies from model to model. Here is the common ThinkPad
mapping:

Expand All @@ -932,6 +932,11 @@ mapping:
5 - UltraBase battery slot
6 - (unknown)
7 - standby
8 - dock status 1
9 - dock status 2
10, 11 - (unknown)
12 - thinkvantage
13, 14, 15 - (unknown)

All of the above can be turned on and off and can be made to blink.

Expand All @@ -940,10 +945,12 @@ sysfs notes:
The ThinkPad LED sysfs interface is described in detail by the LED class
documentation, in Documentation/leds-class.txt.

The leds are named (in LED ID order, from 0 to 7):
The LEDs are named (in LED ID order, from 0 to 12):
"tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
"tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
"tpacpi::unknown_led", "tpacpi::standby".
"tpacpi::unknown_led", "tpacpi::standby", "tpacpi::dock_status1",
"tpacpi::dock_status2", "tpacpi::unknown_led2", "tpacpi::unknown_led3",
"tpacpi::thinkvantage".

Due to limitations in the sysfs LED class, if the status of the LED
indicators cannot be read due to an error, thinkpad-acpi will report it as
Expand All @@ -958,6 +965,12 @@ ThinkPad indicator LED should blink in hardware accelerated mode, use the
"timer" trigger, and leave the delay_on and delay_off parameters set to
zero (to request hardware acceleration autodetection).

LEDs that are known not to exist in a given ThinkPad model are not
made available through the sysfs interface. If you have a dock and you
notice there are LEDs listed for your ThinkPad that do not exist (and
are not in the dock), or if you notice that there are missing LEDs,
a report to ibm-acpi-devel@lists.sourceforge.net is appreciated.


ACPI sounds -- /proc/acpi/ibm/beep
----------------------------------
Expand Down Expand Up @@ -1555,3 +1568,7 @@ Sysfs interface changelog:
0x020300: hotkey enable/disable support removed, attributes
hotkey_bios_enabled and hotkey_enable deprecated and
marked for removal.

0x020400: Marker for 16 LEDs support. Also, LEDs that are known
to not exist in a given model are not registered with
the LED sysfs class anymore.
88 changes: 76 additions & 12 deletions drivers/platform/x86/thinkpad_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
*/

#define TPACPI_VERSION "0.23"
#define TPACPI_SYSFS_VERSION 0x020300
#define TPACPI_SYSFS_VERSION 0x020400

/*
* Changelog:
Expand Down Expand Up @@ -4815,7 +4815,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
"LED", /* all others */
); /* R30, R31 */

#define TPACPI_LED_NUMLEDS 8
#define TPACPI_LED_NUMLEDS 16
static struct tpacpi_led_classdev *tpacpi_leds;
static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
Expand All @@ -4828,15 +4828,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
"tpacpi::dock_batt",
"tpacpi::unknown_led",
"tpacpi::standby",
"tpacpi::dock_status1",
"tpacpi::dock_status2",
"tpacpi::unknown_led2",
"tpacpi::unknown_led3",
"tpacpi::thinkvantage",
};
#define TPACPI_SAFE_LEDS 0x0081U
#define TPACPI_SAFE_LEDS 0x1081U

static inline bool tpacpi_is_led_restricted(const unsigned int led)
{
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
return false;
#else
return (TPACPI_SAFE_LEDS & (1 << led)) == 0;
return (1U & (TPACPI_SAFE_LEDS >> led)) == 0;
#endif
}

Expand Down Expand Up @@ -4998,6 +5003,10 @@ static int __init tpacpi_init_led(unsigned int led)

tpacpi_leds[led].led = led;

/* LEDs with no name don't get registered */
if (!tpacpi_led_names[led])
return 0;

tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
if (led_supported == TPACPI_LED_570)
Expand All @@ -5016,10 +5025,59 @@ static int __init tpacpi_init_led(unsigned int led)
return rc;
}

static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */
TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */
TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */

TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */
TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */
TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */
TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */
TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */
TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */
TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */
TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */

TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */
TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */
TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */
TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */
TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */

TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */
TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */
TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */
TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */

/* (1) - may have excess leds enabled on MSB */

/* Defaults (order matters, keep last, don't reorder!) */
{ /* Lenovo */
.vendor = PCI_VENDOR_ID_LENOVO,
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
.quirks = 0x1fffU,
},
{ /* IBM ThinkPads with no EC version string */
.vendor = PCI_VENDOR_ID_IBM,
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN,
.quirks = 0x00ffU,
},
{ /* IBM ThinkPads with EC version string */
.vendor = PCI_VENDOR_ID_IBM,
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
.quirks = 0x00bfU,
},
};

#undef TPACPI_LEDQ_IBM
#undef TPACPI_LEDQ_LNV

static int __init led_init(struct ibm_init_struct *iibm)
{
unsigned int i;
int rc;
unsigned long useful_leds;

vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");

Expand All @@ -5041,15 +5099,22 @@ static int __init led_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
str_supported(led_supported), led_supported);

if (led_supported == TPACPI_LED_NONE)
return 1;

tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
GFP_KERNEL);
if (!tpacpi_leds) {
printk(TPACPI_ERR "Out of memory for LED data\n");
return -ENOMEM;
}

useful_leds = tpacpi_check_quirks(led_useful_qtable,
ARRAY_SIZE(led_useful_qtable));

for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
if (!tpacpi_is_led_restricted(i)) {
if (!tpacpi_is_led_restricted(i) &&
test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i);
if (rc < 0) {
led_exit();
Expand All @@ -5059,12 +5124,11 @@ static int __init led_init(struct ibm_init_struct *iibm)
}

#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
if (led_supported != TPACPI_LED_NONE)
printk(TPACPI_NOTICE
"warning: userspace override of important "
"firmware LEDs is enabled\n");
printk(TPACPI_NOTICE
"warning: userspace override of important "
"firmware LEDs is enabled\n");
#endif
return (led_supported != TPACPI_LED_NONE)? 0 : 1;
return 0;
}

#define str_led_status(s) \
Expand Down Expand Up @@ -5094,7 +5158,7 @@ static int led_read(char *p)
}

len += sprintf(p + len, "commands:\t"
"<led> on, <led> off, <led> blink (<led> is 0-7)\n");
"<led> on, <led> off, <led> blink (<led> is 0-15)\n");

return len;
}
Expand All @@ -5109,7 +5173,7 @@ static int led_write(char *buf)
return -ENODEV;

while ((cmd = next_cmd(&buf))) {
if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15)
return -EINVAL;

if (strstr(cmd, "off")) {
Expand Down

0 comments on commit f21179a

Please sign in to comment.