aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/thinkpad_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r--drivers/misc/thinkpad_acpi.c136
1 files changed, 135 insertions, 1 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 38a119bd931e..2ab3633f4e56 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -277,6 +277,7 @@ struct tpacpi_led_classdev {
277 struct led_classdev led_classdev; 277 struct led_classdev led_classdev;
278 struct work_struct work; 278 struct work_struct work;
279 enum led_brightness new_brightness; 279 enum led_brightness new_brightness;
280 unsigned int led;
280}; 281};
281 282
282/**************************************************************************** 283/****************************************************************************
@@ -3814,20 +3815,38 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
3814 "LED", /* all others */ 3815 "LED", /* all others */
3815 ); /* R30, R31 */ 3816 ); /* R30, R31 */
3816 3817
3818#define TPACPI_LED_NUMLEDS 8
3819static struct tpacpi_led_classdev *tpacpi_leds;
3820static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
3821static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
3822 /* there's a limit of 19 chars + NULL before 2.6.26 */
3823 "tpacpi::power",
3824 "tpacpi:orange:batt",
3825 "tpacpi:green:batt",
3826 "tpacpi::dock_active",
3827 "tpacpi::bay_active",
3828 "tpacpi::dock_batt",
3829 "tpacpi::unknown_led",
3830 "tpacpi::standby",
3831};
3832
3817static int led_get_status(unsigned int led) 3833static int led_get_status(unsigned int led)
3818{ 3834{
3819 int status; 3835 int status;
3836 enum led_status_t led_s;
3820 3837
3821 switch (led_supported) { 3838 switch (led_supported) {
3822 case TPACPI_LED_570: 3839 case TPACPI_LED_570:
3823 if (!acpi_evalf(ec_handle, 3840 if (!acpi_evalf(ec_handle,
3824 &status, "GLED", "dd", 1 << led)) 3841 &status, "GLED", "dd", 1 << led))
3825 return -EIO; 3842 return -EIO;
3826 return (status == 0)? 3843 led_s = (status == 0)?
3827 TPACPI_LED_OFF : 3844 TPACPI_LED_OFF :
3828 ((status == 1)? 3845 ((status == 1)?
3829 TPACPI_LED_ON : 3846 TPACPI_LED_ON :
3830 TPACPI_LED_BLINK); 3847 TPACPI_LED_BLINK);
3848 tpacpi_led_state_cache[led] = led_s;
3849 return led_s;
3831 default: 3850 default:
3832 return -ENXIO; 3851 return -ENXIO;
3833 } 3852 }
@@ -3874,11 +3893,96 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus)
3874 rc = -ENXIO; 3893 rc = -ENXIO;
3875 } 3894 }
3876 3895
3896 if (!rc)
3897 tpacpi_led_state_cache[led] = ledstatus;
3898
3877 return rc; 3899 return rc;
3878} 3900}
3879 3901
3902static void led_sysfs_set_status(unsigned int led,
3903 enum led_brightness brightness)
3904{
3905 led_set_status(led,
3906 (brightness == LED_OFF) ?
3907 TPACPI_LED_OFF :
3908 (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
3909 TPACPI_LED_BLINK : TPACPI_LED_ON);
3910}
3911
3912static void led_set_status_worker(struct work_struct *work)
3913{
3914 struct tpacpi_led_classdev *data =
3915 container_of(work, struct tpacpi_led_classdev, work);
3916
3917 if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
3918 led_sysfs_set_status(data->led, data->new_brightness);
3919}
3920
3921static void led_sysfs_set(struct led_classdev *led_cdev,
3922 enum led_brightness brightness)
3923{
3924 struct tpacpi_led_classdev *data = container_of(led_cdev,
3925 struct tpacpi_led_classdev, led_classdev);
3926
3927 data->new_brightness = brightness;
3928 schedule_work(&data->work);
3929}
3930
3931static int led_sysfs_blink_set(struct led_classdev *led_cdev,
3932 unsigned long *delay_on, unsigned long *delay_off)
3933{
3934 struct tpacpi_led_classdev *data = container_of(led_cdev,
3935 struct tpacpi_led_classdev, led_classdev);
3936
3937 /* Can we choose the flash rate? */
3938 if (*delay_on == 0 && *delay_off == 0) {
3939 /* yes. set them to the hardware blink rate (1 Hz) */
3940 *delay_on = 500; /* ms */
3941 *delay_off = 500; /* ms */
3942 } else if ((*delay_on != 500) || (*delay_off != 500))
3943 return -EINVAL;
3944
3945 data->new_brightness = TPACPI_LED_BLINK;
3946 schedule_work(&data->work);
3947
3948 return 0;
3949}
3950
3951static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
3952{
3953 int rc;
3954
3955 struct tpacpi_led_classdev *data = container_of(led_cdev,
3956 struct tpacpi_led_classdev, led_classdev);
3957
3958 rc = led_get_status(data->led);
3959
3960 if (rc == TPACPI_LED_OFF || rc < 0)
3961 rc = LED_OFF; /* no error handling in led class :( */
3962 else
3963 rc = LED_FULL;
3964
3965 return rc;
3966}
3967
3968static void led_exit(void)
3969{
3970 unsigned int i;
3971
3972 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
3973 if (tpacpi_leds[i].led_classdev.name)
3974 led_classdev_unregister(&tpacpi_leds[i].led_classdev);
3975 }
3976
3977 kfree(tpacpi_leds);
3978 tpacpi_leds = NULL;
3979}
3980
3880static int __init led_init(struct ibm_init_struct *iibm) 3981static int __init led_init(struct ibm_init_struct *iibm)
3881{ 3982{
3983 unsigned int i;
3984 int rc;
3985
3882 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); 3986 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
3883 3987
3884 TPACPI_ACPIHANDLE_INIT(led); 3988 TPACPI_ACPIHANDLE_INIT(led);
@@ -3899,6 +4003,35 @@ static int __init led_init(struct ibm_init_struct *iibm)
3899 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", 4003 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
3900 str_supported(led_supported), led_supported); 4004 str_supported(led_supported), led_supported);
3901 4005
4006 tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
4007 GFP_KERNEL);
4008 if (!tpacpi_leds) {
4009 printk(TPACPI_ERR "Out of memory for LED data\n");
4010 return -ENOMEM;
4011 }
4012
4013 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4014 tpacpi_leds[i].led = i;
4015
4016 tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
4017 tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
4018 if (led_supported == TPACPI_LED_570)
4019 tpacpi_leds[i].led_classdev.brightness_get =
4020 &led_sysfs_get;
4021
4022 tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
4023
4024 INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
4025
4026 rc = led_classdev_register(&tpacpi_pdev->dev,
4027 &tpacpi_leds[i].led_classdev);
4028 if (rc < 0) {
4029 tpacpi_leds[i].led_classdev.name = NULL;
4030 led_exit();
4031 return rc;
4032 }
4033 }
4034
3902 return (led_supported != TPACPI_LED_NONE)? 0 : 1; 4035 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
3903} 4036}
3904 4037
@@ -3969,6 +4102,7 @@ static struct ibm_struct led_driver_data = {
3969 .name = "led", 4102 .name = "led",
3970 .read = led_read, 4103 .read = led_read,
3971 .write = led_write, 4104 .write = led_write,
4105 .exit = led_exit,
3972}; 4106};
3973 4107
3974/************************************************************************* 4108/*************************************************************************