diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2009-05-30 12:25:08 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-06-17 22:48:22 -0400 |
commit | f21179a47ff8d1046a61c1cf5920244997a4a7bb (patch) | |
tree | f90635e4dc7f5602d960a199877a4bb20c6b7be6 | |
parent | 60201732f03c1231742e5872abe55a3bf59849a5 (diff) |
thinkpad-acpi: enhance led support
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>
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 23 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 88 |
2 files changed, 96 insertions, 15 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index e7e9a69069e1..88fc0661de56 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -920,7 +920,7 @@ The available commands are: | |||
920 | echo '<LED number> off' >/proc/acpi/ibm/led | 920 | echo '<LED number> off' >/proc/acpi/ibm/led |
921 | echo '<LED number> blink' >/proc/acpi/ibm/led | 921 | echo '<LED number> blink' >/proc/acpi/ibm/led |
922 | 922 | ||
923 | The <LED number> range is 0 to 7. The set of LEDs that can be | 923 | The <LED number> range is 0 to 15. The set of LEDs that can be |
924 | controlled varies from model to model. Here is the common ThinkPad | 924 | controlled varies from model to model. Here is the common ThinkPad |
925 | mapping: | 925 | mapping: |
926 | 926 | ||
@@ -932,6 +932,11 @@ mapping: | |||
932 | 5 - UltraBase battery slot | 932 | 5 - UltraBase battery slot |
933 | 6 - (unknown) | 933 | 6 - (unknown) |
934 | 7 - standby | 934 | 7 - standby |
935 | 8 - dock status 1 | ||
936 | 9 - dock status 2 | ||
937 | 10, 11 - (unknown) | ||
938 | 12 - thinkvantage | ||
939 | 13, 14, 15 - (unknown) | ||
935 | 940 | ||
936 | All of the above can be turned on and off and can be made to blink. | 941 | All of the above can be turned on and off and can be made to blink. |
937 | 942 | ||
@@ -940,10 +945,12 @@ sysfs notes: | |||
940 | The ThinkPad LED sysfs interface is described in detail by the LED class | 945 | The ThinkPad LED sysfs interface is described in detail by the LED class |
941 | documentation, in Documentation/leds-class.txt. | 946 | documentation, in Documentation/leds-class.txt. |
942 | 947 | ||
943 | The leds are named (in LED ID order, from 0 to 7): | 948 | The LEDs are named (in LED ID order, from 0 to 12): |
944 | "tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt", | 949 | "tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt", |
945 | "tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt", | 950 | "tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt", |
946 | "tpacpi::unknown_led", "tpacpi::standby". | 951 | "tpacpi::unknown_led", "tpacpi::standby", "tpacpi::dock_status1", |
952 | "tpacpi::dock_status2", "tpacpi::unknown_led2", "tpacpi::unknown_led3", | ||
953 | "tpacpi::thinkvantage". | ||
947 | 954 | ||
948 | Due to limitations in the sysfs LED class, if the status of the LED | 955 | Due to limitations in the sysfs LED class, if the status of the LED |
949 | indicators cannot be read due to an error, thinkpad-acpi will report it as | 956 | indicators cannot be read due to an error, thinkpad-acpi will report it as |
@@ -958,6 +965,12 @@ ThinkPad indicator LED should blink in hardware accelerated mode, use the | |||
958 | "timer" trigger, and leave the delay_on and delay_off parameters set to | 965 | "timer" trigger, and leave the delay_on and delay_off parameters set to |
959 | zero (to request hardware acceleration autodetection). | 966 | zero (to request hardware acceleration autodetection). |
960 | 967 | ||
968 | LEDs that are known not to exist in a given ThinkPad model are not | ||
969 | made available through the sysfs interface. If you have a dock and you | ||
970 | notice there are LEDs listed for your ThinkPad that do not exist (and | ||
971 | are not in the dock), or if you notice that there are missing LEDs, | ||
972 | a report to ibm-acpi-devel@lists.sourceforge.net is appreciated. | ||
973 | |||
961 | 974 | ||
962 | ACPI sounds -- /proc/acpi/ibm/beep | 975 | ACPI sounds -- /proc/acpi/ibm/beep |
963 | ---------------------------------- | 976 | ---------------------------------- |
@@ -1555,3 +1568,7 @@ Sysfs interface changelog: | |||
1555 | 0x020300: hotkey enable/disable support removed, attributes | 1568 | 0x020300: hotkey enable/disable support removed, attributes |
1556 | hotkey_bios_enabled and hotkey_enable deprecated and | 1569 | hotkey_bios_enabled and hotkey_enable deprecated and |
1557 | marked for removal. | 1570 | marked for removal. |
1571 | |||
1572 | 0x020400: Marker for 16 LEDs support. Also, LEDs that are known | ||
1573 | to not exist in a given model are not registered with | ||
1574 | the LED sysfs class anymore. | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index da739d5c9210..06c7c03c8f2f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -22,7 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.23" |
25 | #define TPACPI_SYSFS_VERSION 0x020300 | 25 | #define TPACPI_SYSFS_VERSION 0x020400 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -4815,7 +4815,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
4815 | "LED", /* all others */ | 4815 | "LED", /* all others */ |
4816 | ); /* R30, R31 */ | 4816 | ); /* R30, R31 */ |
4817 | 4817 | ||
4818 | #define TPACPI_LED_NUMLEDS 8 | 4818 | #define TPACPI_LED_NUMLEDS 16 |
4819 | static struct tpacpi_led_classdev *tpacpi_leds; | 4819 | static struct tpacpi_led_classdev *tpacpi_leds; |
4820 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | 4820 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; |
4821 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | 4821 | static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { |
@@ -4828,15 +4828,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | |||
4828 | "tpacpi::dock_batt", | 4828 | "tpacpi::dock_batt", |
4829 | "tpacpi::unknown_led", | 4829 | "tpacpi::unknown_led", |
4830 | "tpacpi::standby", | 4830 | "tpacpi::standby", |
4831 | "tpacpi::dock_status1", | ||
4832 | "tpacpi::dock_status2", | ||
4833 | "tpacpi::unknown_led2", | ||
4834 | "tpacpi::unknown_led3", | ||
4835 | "tpacpi::thinkvantage", | ||
4831 | }; | 4836 | }; |
4832 | #define TPACPI_SAFE_LEDS 0x0081U | 4837 | #define TPACPI_SAFE_LEDS 0x1081U |
4833 | 4838 | ||
4834 | static inline bool tpacpi_is_led_restricted(const unsigned int led) | 4839 | static inline bool tpacpi_is_led_restricted(const unsigned int led) |
4835 | { | 4840 | { |
4836 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 4841 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
4837 | return false; | 4842 | return false; |
4838 | #else | 4843 | #else |
4839 | return (TPACPI_SAFE_LEDS & (1 << led)) == 0; | 4844 | return (1U & (TPACPI_SAFE_LEDS >> led)) == 0; |
4840 | #endif | 4845 | #endif |
4841 | } | 4846 | } |
4842 | 4847 | ||
@@ -4998,6 +5003,10 @@ static int __init tpacpi_init_led(unsigned int led) | |||
4998 | 5003 | ||
4999 | tpacpi_leds[led].led = led; | 5004 | tpacpi_leds[led].led = led; |
5000 | 5005 | ||
5006 | /* LEDs with no name don't get registered */ | ||
5007 | if (!tpacpi_led_names[led]) | ||
5008 | return 0; | ||
5009 | |||
5001 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; | 5010 | tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; |
5002 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; | 5011 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; |
5003 | if (led_supported == TPACPI_LED_570) | 5012 | if (led_supported == TPACPI_LED_570) |
@@ -5016,10 +5025,59 @@ static int __init tpacpi_init_led(unsigned int led) | |||
5016 | return rc; | 5025 | return rc; |
5017 | } | 5026 | } |
5018 | 5027 | ||
5028 | static const struct tpacpi_quirk led_useful_qtable[] __initconst = { | ||
5029 | TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */ | ||
5030 | TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */ | ||
5031 | TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */ | ||
5032 | |||
5033 | TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */ | ||
5034 | TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */ | ||
5035 | TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */ | ||
5036 | TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */ | ||
5037 | TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */ | ||
5038 | TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */ | ||
5039 | TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */ | ||
5040 | TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */ | ||
5041 | |||
5042 | TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */ | ||
5043 | TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */ | ||
5044 | TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */ | ||
5045 | TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */ | ||
5046 | TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */ | ||
5047 | |||
5048 | TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */ | ||
5049 | TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */ | ||
5050 | TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */ | ||
5051 | TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */ | ||
5052 | |||
5053 | /* (1) - may have excess leds enabled on MSB */ | ||
5054 | |||
5055 | /* Defaults (order matters, keep last, don't reorder!) */ | ||
5056 | { /* Lenovo */ | ||
5057 | .vendor = PCI_VENDOR_ID_LENOVO, | ||
5058 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5059 | .quirks = 0x1fffU, | ||
5060 | }, | ||
5061 | { /* IBM ThinkPads with no EC version string */ | ||
5062 | .vendor = PCI_VENDOR_ID_IBM, | ||
5063 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN, | ||
5064 | .quirks = 0x00ffU, | ||
5065 | }, | ||
5066 | { /* IBM ThinkPads with EC version string */ | ||
5067 | .vendor = PCI_VENDOR_ID_IBM, | ||
5068 | .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, | ||
5069 | .quirks = 0x00bfU, | ||
5070 | }, | ||
5071 | }; | ||
5072 | |||
5073 | #undef TPACPI_LEDQ_IBM | ||
5074 | #undef TPACPI_LEDQ_LNV | ||
5075 | |||
5019 | static int __init led_init(struct ibm_init_struct *iibm) | 5076 | static int __init led_init(struct ibm_init_struct *iibm) |
5020 | { | 5077 | { |
5021 | unsigned int i; | 5078 | unsigned int i; |
5022 | int rc; | 5079 | int rc; |
5080 | unsigned long useful_leds; | ||
5023 | 5081 | ||
5024 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 5082 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
5025 | 5083 | ||
@@ -5041,6 +5099,9 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5041 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 5099 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
5042 | str_supported(led_supported), led_supported); | 5100 | str_supported(led_supported), led_supported); |
5043 | 5101 | ||
5102 | if (led_supported == TPACPI_LED_NONE) | ||
5103 | return 1; | ||
5104 | |||
5044 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | 5105 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, |
5045 | GFP_KERNEL); | 5106 | GFP_KERNEL); |
5046 | if (!tpacpi_leds) { | 5107 | if (!tpacpi_leds) { |
@@ -5048,8 +5109,12 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5048 | return -ENOMEM; | 5109 | return -ENOMEM; |
5049 | } | 5110 | } |
5050 | 5111 | ||
5112 | useful_leds = tpacpi_check_quirks(led_useful_qtable, | ||
5113 | ARRAY_SIZE(led_useful_qtable)); | ||
5114 | |||
5051 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | 5115 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
5052 | if (!tpacpi_is_led_restricted(i)) { | 5116 | if (!tpacpi_is_led_restricted(i) && |
5117 | test_bit(i, &useful_leds)) { | ||
5053 | rc = tpacpi_init_led(i); | 5118 | rc = tpacpi_init_led(i); |
5054 | if (rc < 0) { | 5119 | if (rc < 0) { |
5055 | led_exit(); | 5120 | led_exit(); |
@@ -5059,12 +5124,11 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5059 | } | 5124 | } |
5060 | 5125 | ||
5061 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 5126 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
5062 | if (led_supported != TPACPI_LED_NONE) | 5127 | printk(TPACPI_NOTICE |
5063 | printk(TPACPI_NOTICE | 5128 | "warning: userspace override of important " |
5064 | "warning: userspace override of important " | 5129 | "firmware LEDs is enabled\n"); |
5065 | "firmware LEDs is enabled\n"); | ||
5066 | #endif | 5130 | #endif |
5067 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 5131 | return 0; |
5068 | } | 5132 | } |
5069 | 5133 | ||
5070 | #define str_led_status(s) \ | 5134 | #define str_led_status(s) \ |
@@ -5094,7 +5158,7 @@ static int led_read(char *p) | |||
5094 | } | 5158 | } |
5095 | 5159 | ||
5096 | len += sprintf(p + len, "commands:\t" | 5160 | len += sprintf(p + len, "commands:\t" |
5097 | "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); | 5161 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5098 | 5162 | ||
5099 | return len; | 5163 | return len; |
5100 | } | 5164 | } |
@@ -5109,7 +5173,7 @@ static int led_write(char *buf) | |||
5109 | return -ENODEV; | 5173 | return -ENODEV; |
5110 | 5174 | ||
5111 | while ((cmd = next_cmd(&buf))) { | 5175 | while ((cmd = next_cmd(&buf))) { |
5112 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) | 5176 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15) |
5113 | return -EINVAL; | 5177 | return -EINVAL; |
5114 | 5178 | ||
5115 | if (strstr(cmd, "off")) { | 5179 | if (strstr(cmd, "off")) { |