Skip to content

Commit

Permalink
ACPI: thinkpad-acpi: prepare light and LED for sysfs support
Browse files Browse the repository at this point in the history
Do some preparatory work to add sysfs support to the thinklight and
thinkpad leds driver.

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 Apr 29, 2008
1 parent 95e57ab commit 4fa6811
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 55 deletions.
2 changes: 2 additions & 0 deletions drivers/misc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ config THINKPAD_ACPI
select HWMON
select NVRAM
depends on INPUT
select NEW_LEDS
select LEDS_CLASS
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
Expand Down
191 changes: 136 additions & 55 deletions drivers/misc/thinkpad_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <asm/uaccess.h>

#include <linux/dmi.h>
Expand All @@ -85,6 +86,8 @@
#define TP_CMOS_VOLUME_MUTE 2
#define TP_CMOS_BRIGHTNESS_UP 4
#define TP_CMOS_BRIGHTNESS_DOWN 5
#define TP_CMOS_THINKLIGHT_ON 12
#define TP_CMOS_THINKLIGHT_OFF 13

/* NVRAM Addresses */
enum tp_nvram_addr {
Expand Down Expand Up @@ -269,6 +272,13 @@ static enum {
static int experimental;
static u32 dbg_level;

/* Special LED class that can defer work */
struct tpacpi_led_classdev {
struct led_classdev led_classdev;
struct work_struct work;
enum led_brightness new_brightness;
};

/****************************************************************************
****************************************************************************
*
Expand Down Expand Up @@ -3237,6 +3247,39 @@ static struct ibm_struct video_driver_data = {
TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */

static int light_get_status(void)
{
int status = 0;

if (tp_features.light_status) {
if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
return -EIO;
return (!!status);
}

return -ENXIO;
}

static int light_set_status(int status)
{
int rc;

if (tp_features.light) {
if (cmos_handle) {
rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
(status)?
TP_CMOS_THINKLIGHT_ON :
TP_CMOS_THINKLIGHT_OFF);
} else {
rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
(status)? 1 : 0);
}
return (rc)? 0 : -EIO;
}

return -ENXIO;
}

static int __init light_init(struct ibm_init_struct *iibm)
{
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
Expand All @@ -3263,16 +3306,17 @@ static int __init light_init(struct ibm_init_struct *iibm)
static int light_read(char *p)
{
int len = 0;
int status = 0;
int status;

if (!tp_features.light) {
len += sprintf(p + len, "status:\t\tnot supported\n");
} else if (!tp_features.light_status) {
len += sprintf(p + len, "status:\t\tunknown\n");
len += sprintf(p + len, "commands:\ton, off\n");
} else {
if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
return -EIO;
status = light_get_status();
if (status < 0)
return status;
len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
len += sprintf(p + len, "commands:\ton, off\n");
}
Expand All @@ -3282,31 +3326,22 @@ static int light_read(char *p)

static int light_write(char *buf)
{
int cmos_cmd, lght_cmd;
char *cmd;
int success;
int newstatus = 0;

if (!tp_features.light)
return -ENODEV;

while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "on") == 0) {
cmos_cmd = 0x0c;
lght_cmd = 1;
newstatus = 1;
} else if (strlencmp(cmd, "off") == 0) {
cmos_cmd = 0x0d;
lght_cmd = 0;
newstatus = 0;
} else
return -EINVAL;

success = cmos_handle ?
acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
if (!success)
return -EIO;
}

return 0;
return light_set_status(newstatus);
}

static struct ibm_struct light_driver_data = {
Expand Down Expand Up @@ -3710,6 +3745,12 @@ enum { /* For TPACPI_LED_OLD */
TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
};

enum led_status_t {
TPACPI_LED_OFF = 0,
TPACPI_LED_ON,
TPACPI_LED_BLINK,
};

static enum led_access_mode led_supported;

TPACPI_HANDLE(led, ec, "SLED", /* 570 */
Expand All @@ -3718,6 +3759,69 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
"LED", /* all others */
); /* R30, R31 */

static int led_get_status(unsigned int led)
{
int status;

switch (led_supported) {
case TPACPI_LED_570:
if (!acpi_evalf(ec_handle,
&status, "GLED", "dd", 1 << led))
return -EIO;
return (status == 0)?
TPACPI_LED_OFF :
((status == 1)?
TPACPI_LED_ON :
TPACPI_LED_BLINK);
default:
return -ENXIO;
}

/* not reached */
}

static int led_set_status(unsigned int led, enum led_status_t ledstatus)
{
/* off, on, blink. Index is led_status_t */
static const int const led_sled_arg1[] = { 0, 1, 3 };
static const int const led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
static const int const led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
static const int const led_led_arg1[] = { 0, 0x80, 0xc0 };

int rc = 0;

switch (led_supported) {
case TPACPI_LED_570:
/* 570 */
led = 1 << led;
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_sled_arg1[ledstatus]))
rc = -EIO;
break;
case TPACPI_LED_OLD:
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
led = 1 << led;
rc = ec_write(TPACPI_LED_EC_HLMS, led);
if (rc >= 0)
rc = ec_write(TPACPI_LED_EC_HLBL,
led * led_exp_hlbl[ledstatus]);
if (rc >= 0)
rc = ec_write(TPACPI_LED_EC_HLCL,
led * led_exp_hlcl[ledstatus]);
break;
case TPACPI_LED_NEW:
/* all others */
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_led_arg1[ledstatus]))
rc = -EIO;
break;
default:
rc = -ENXIO;
}

return rc;
}

static int __init led_init(struct ibm_init_struct *iibm)
{
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
Expand All @@ -3743,7 +3847,9 @@ static int __init led_init(struct ibm_init_struct *iibm)
return (led_supported != TPACPI_LED_NONE)? 0 : 1;
}

#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
#define str_led_status(s) \
((s) == TPACPI_LED_OFF ? "off" : \
((s) == TPACPI_LED_ON ? "on" : "blinking"))

static int led_read(char *p)
{
Expand All @@ -3759,11 +3865,11 @@ static int led_read(char *p)
/* 570 */
int i, status;
for (i = 0; i < 8; i++) {
if (!acpi_evalf(ec_handle,
&status, "GLED", "dd", 1 << i))
status = led_get_status(i);
if (status < 0)
return -EIO;
len += sprintf(p + len, "%d:\t\t%s\n",
i, led_status(status));
i, str_led_status(status));
}
}

Expand All @@ -3773,16 +3879,11 @@ static int led_read(char *p)
return len;
}

/* off, on, blink */
static const int led_sled_arg1[] = { 0, 1, 3 };
static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
static const int led_led_arg1[] = { 0, 0x80, 0xc0 };

static int led_write(char *buf)
{
char *cmd;
int led, ind, ret;
int led, rc;
enum led_status_t s;

if (!led_supported)
return -ENODEV;
Expand All @@ -3792,38 +3893,18 @@ static int led_write(char *buf)
return -EINVAL;

if (strstr(cmd, "off")) {
ind = 0;
s = TPACPI_LED_OFF;
} else if (strstr(cmd, "on")) {
ind = 1;
s = TPACPI_LED_ON;
} else if (strstr(cmd, "blink")) {
ind = 2;
} else
return -EINVAL;

if (led_supported == TPACPI_LED_570) {
/* 570 */
led = 1 << led;
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_sled_arg1[ind]))
return -EIO;
} else if (led_supported == TPACPI_LED_OLD) {
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
led = 1 << led;
ret = ec_write(TPACPI_LED_EC_HLMS, led);
if (ret >= 0)
ret = ec_write(TPACPI_LED_EC_HLBL,
led * led_exp_hlbl[ind]);
if (ret >= 0)
ret = ec_write(TPACPI_LED_EC_HLCL,
led * led_exp_hlcl[ind]);
if (ret < 0)
return ret;
s = TPACPI_LED_BLINK;
} else {
/* all others */
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_led_arg1[ind]))
return -EIO;
return -EINVAL;
}

rc = led_set_status(led, s);
if (rc < 0)
return rc;
}

return 0;
Expand Down

0 comments on commit 4fa6811

Please sign in to comment.