* ACPI patches for 2.6.26
@ 2008-04-29 8:43 Len Brown
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
0 siblings, 1 reply; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi
Here is the current queue for 2.6.26.
It is available in the git tree:
git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git release
as well as a consolidated plain patch:
ftp://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/patches/release/2.6.25/acpi-release-20080321-2.6.25.diff.gz
I've excluded the ACPICA series and the PNP series
from this patch-bomb, as you've recently seen them
and they're unchanged since you did.
thanks,
-Len
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes
2008-04-29 8:43 ACPI patches for 2.6.26 Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 002/171] ACPI: EC: Restore udelay in poll mode Len Brown
` (42 more replies)
0 siblings, 43 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: David Brownell, David Brownell, Zhang Rui, Len Brown
From: David Brownell <david-b@pacbell.net>
Add cross-links between ACPI device and "real" devices in sysfs,
exposing otherwise-hidden interrelationships between the various
device nodes for ACPI stuff. As a representative example, one
hardware device is exposed as two logical devices (PNP and ACPI):
.../pnp0/00:06/
.../LNXSYSTM:00/device:00/PNP0A03:00/device:15/PNP0B00:00/
The PNP device gets a "firmware_node" link pointing to the ACPI device,
and is what a Linux device driver binds to. The ACPI device has instead
a "physical_node" link pointing back to the PNP device. Other firmware
frameworks, like OpenFirmware, could do the same thing to couple their
firmware tables to the rest of the system.
(Based on a patch from Zhang Rui. This version is modified to not
depend on the patch makig ACPI initialize driver model wakeup flags.)
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/glue.c | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index eda0978..06f8634 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -142,6 +142,7 @@ EXPORT_SYMBOL(acpi_get_physical_device);
static int acpi_bind_one(struct device *dev, acpi_handle handle)
{
+ struct acpi_device *acpi_dev;
acpi_status status;
if (dev->archdata.acpi_handle) {
@@ -157,6 +158,16 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
}
dev->archdata.acpi_handle = handle;
+ status = acpi_bus_get_device(handle, &acpi_dev);
+ if (!ACPI_FAILURE(status)) {
+ int ret;
+
+ ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
+ "firmware_node");
+ ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
+ "physical_node");
+ }
+
return 0;
}
@@ -165,8 +176,17 @@ static int acpi_unbind_one(struct device *dev)
if (!dev->archdata.acpi_handle)
return 0;
if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
+ struct acpi_device *acpi_dev;
+
/* acpi_get_physical_device increase refcnt by one */
put_device(dev);
+
+ if (!acpi_bus_get_device(dev->archdata.acpi_handle,
+ &acpi_dev)) {
+ sysfs_remove_link(&dev->kobj, "firmware_node");
+ sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
+ }
+
acpi_detach_data(dev->archdata.acpi_handle,
acpi_glue_data_handler);
dev->archdata.acpi_handle = NULL;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 002/171] ACPI: EC: Restore udelay in poll mode
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 003/171] ACPI: EC: Add poll timer Len Brown
` (41 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
This fixes keyboard event handling on some systems.
Note that this delay was thought unnecessary, and removed
from linux-2.6.20 with 50c1e1138cb94f6aca0f8555777edbcefe0324e2
'ACPI: ec: Drop udelay() from poll mode. Loop by reading status field instead.'
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 7222a18..828c752 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -73,6 +73,7 @@ enum ec_event {
#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
+#define ACPI_EC_UDELAY 100 /* Wait 100us before polling EC again */
enum {
EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */
@@ -227,6 +228,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
while (time_before(jiffies, delay)) {
if (acpi_ec_check_status(ec, event))
goto end;
+ udelay(ACPI_EC_UDELAY);
}
}
pr_err(PREFIX "acpi_ec_wait timeout,"
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 003/171] ACPI: EC: Add poll timer
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
2008-04-29 8:43 ` [PATCH 002/171] ACPI: EC: Restore udelay in poll mode Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 004/171] ACPI: EC: Improve debug output Len Brown
` (40 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
If we can not use interrupt mode of EC for some reason, start polling
EC for events periodically.
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 43 +++++++++++++++++++++++++++++++++++++++----
1 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 828c752..63e0ac2 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -84,6 +84,7 @@ enum {
EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */
EC_FLAGS_WDATA, /* Data is being written */
EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */
+ EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */
};
static int acpi_ec_remove(struct acpi_device *device, int type);
@@ -130,6 +131,7 @@ static struct acpi_ec {
struct mutex lock;
wait_queue_head_t wait;
struct list_head list;
+ struct delayed_work work;
u8 handlers_installed;
} *boot_ec, *first_ec;
@@ -178,6 +180,20 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event)
return 0;
}
+static void ec_schedule_ec_poll(struct acpi_ec *ec)
+{
+ if (test_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags))
+ schedule_delayed_work(&ec->work,
+ msecs_to_jiffies(ACPI_EC_DELAY));
+}
+
+static void ec_switch_to_poll_mode(struct acpi_ec *ec)
+{
+ clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
+ acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
+ set_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
+}
+
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
{
int ret = 0;
@@ -218,7 +234,8 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
if (printk_ratelimit())
pr_info(PREFIX "missing confirmations, "
"switch off interrupt mode.\n");
- clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
+ ec_switch_to_poll_mode(ec);
+ ec_schedule_ec_poll(ec);
}
goto end;
}
@@ -529,28 +546,37 @@ static u32 acpi_ec_gpe_handler(void *data)
{
acpi_status status = AE_OK;
struct acpi_ec *ec = data;
+ u8 state = acpi_ec_read_status(ec);
pr_debug(PREFIX "~~~> interrupt\n");
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
wake_up(&ec->wait);
- if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI) {
+ if (state & ACPI_EC_FLAG_SCI) {
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
status = acpi_os_execute(OSL_EC_BURST_HANDLER,
acpi_ec_gpe_query, ec);
- } else if (unlikely(!test_bit(EC_FLAGS_GPE_MODE, &ec->flags))) {
+ } else if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) &&
+ in_interrupt()) {
/* this is non-query, must be confirmation */
if (printk_ratelimit())
pr_info(PREFIX "non-query interrupt received,"
" switching to interrupt mode\n");
set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
+ clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
}
-
+ ec_schedule_ec_poll(ec);
return ACPI_SUCCESS(status) ?
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
}
+static void do_ec_poll(struct work_struct *work)
+{
+ struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work);
+ (void)acpi_ec_gpe_handler(ec);
+}
+
/* --------------------------------------------------------------------------
Address Space Management
-------------------------------------------------------------------------- */
@@ -711,6 +737,7 @@ static struct acpi_ec *make_acpi_ec(void)
mutex_init(&ec->lock);
init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list);
+ INIT_DELAYED_WORK_DEFERRABLE(&ec->work, do_ec_poll);
return ec;
}
@@ -752,8 +779,15 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
return AE_CTRL_TERMINATE;
}
+static void ec_poll_stop(struct acpi_ec *ec)
+{
+ clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
+ cancel_delayed_work(&ec->work);
+}
+
static void ec_remove_handlers(struct acpi_ec *ec)
{
+ ec_poll_stop(ec);
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
pr_err(PREFIX "failed to remove space handler\n");
@@ -899,6 +933,7 @@ static int acpi_ec_start(struct acpi_device *device)
/* EC is fully operational, allow queries */
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
+ ec_schedule_ec_poll(ec);
return ret;
}
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 004/171] ACPI: EC: Improve debug output
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
2008-04-29 8:43 ` [PATCH 002/171] ACPI: EC: Restore udelay in poll mode Len Brown
2008-04-29 8:43 ` [PATCH 003/171] ACPI: EC: Add poll timer Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 005/171] ACPI: EC: Replace broken controller workarounds with poll mode Len Brown
` (39 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 63e0ac2..1a7949c 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -248,9 +248,9 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
udelay(ACPI_EC_UDELAY);
}
}
- pr_err(PREFIX "acpi_ec_wait timeout,"
- " status = %d, expect_event = %d\n",
- acpi_ec_read_status(ec), event);
+ pr_err(PREFIX "acpi_ec_wait timeout, status = 0x%2.2x, event = %s\n",
+ acpi_ec_read_status(ec),
+ (event == ACPI_EC_EVENT_OBF_1) ? "\"b0=1\"" : "\"b1=0\"");
ret = -ETIME;
end:
clear_bit(EC_FLAGS_ADDRESS, &ec->flags);
@@ -264,8 +264,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
{
int result = 0;
set_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
- acpi_ec_write_cmd(ec, command);
pr_debug(PREFIX "transaction start\n");
+ acpi_ec_write_cmd(ec, command);
for (; wdata_len > 0; --wdata_len) {
result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll);
if (result) {
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 005/171] ACPI: EC: Replace broken controller workarounds with poll mode.
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (2 preceding siblings ...)
2008-04-29 8:43 ` [PATCH 004/171] ACPI: EC: Improve debug output Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 006/171] ACPI: EC: Switch off GPE mode during suspend/resume Len Brown
` (38 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 61 ++++++++++------------------------------------------
1 files changed, 12 insertions(+), 49 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 1a7949c..7f07b68 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -79,11 +79,7 @@ enum {
EC_FLAGS_WAIT_GPE = 0, /* Don't check status until GPE arrives */
EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_GPE_MODE, /* Expect GPE to be sent for status change */
- EC_FLAGS_NO_ADDRESS_GPE, /* Expect GPE only for non-address event */
- EC_FLAGS_ADDRESS, /* Address is being written */
- EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */
- EC_FLAGS_WDATA, /* Data is being written */
- EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */
+ EC_FLAGS_NO_GPE, /* Don't use GPE mode */
EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */
};
@@ -189,6 +185,7 @@ static void ec_schedule_ec_poll(struct acpi_ec *ec)
static void ec_switch_to_poll_mode(struct acpi_ec *ec)
{
+ set_bit(EC_FLAGS_NO_GPE, &ec->flags);
clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
set_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
@@ -196,65 +193,34 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
{
- int ret = 0;
-
- if (unlikely(event == ACPI_EC_EVENT_OBF_1 &&
- test_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags)))
- force_poll = 1;
- if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) &&
- test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags)))
- force_poll = 1;
- if (unlikely(test_bit(EC_FLAGS_WDATA, &ec->flags) &&
- test_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags)))
- force_poll = 1;
if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) &&
likely(!force_poll)) {
if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
msecs_to_jiffies(ACPI_EC_DELAY)))
- goto end;
+ return 0;
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
if (acpi_ec_check_status(ec, event)) {
- if (event == ACPI_EC_EVENT_OBF_1) {
- /* miss OBF_1 GPE, don't expect it */
- pr_info(PREFIX "missing OBF confirmation, "
- "don't expect it any longer.\n");
- set_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags);
- } else if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) {
- /* miss address GPE, don't expect it anymore */
- pr_info(PREFIX "missing address confirmation, "
- "don't expect it any longer.\n");
- set_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags);
- } else if (test_bit(EC_FLAGS_WDATA, &ec->flags)) {
- /* miss write data GPE, don't expect it */
- pr_info(PREFIX "missing write data confirmation, "
- "don't expect it any longer.\n");
- set_bit(EC_FLAGS_NO_WDATA_GPE, &ec->flags);
- } else {
- /* missing GPEs, switch back to poll mode */
- if (printk_ratelimit())
- pr_info(PREFIX "missing confirmations, "
+ /* missing GPEs, switch back to poll mode */
+ if (printk_ratelimit())
+ pr_info(PREFIX "missing confirmations, "
"switch off interrupt mode.\n");
- ec_switch_to_poll_mode(ec);
- ec_schedule_ec_poll(ec);
- }
- goto end;
+ ec_switch_to_poll_mode(ec);
+ ec_schedule_ec_poll(ec);
+ return 0;
}
} else {
unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
while (time_before(jiffies, delay)) {
if (acpi_ec_check_status(ec, event))
- goto end;
+ return 0;
udelay(ACPI_EC_UDELAY);
}
}
pr_err(PREFIX "acpi_ec_wait timeout, status = 0x%2.2x, event = %s\n",
acpi_ec_read_status(ec),
(event == ACPI_EC_EVENT_OBF_1) ? "\"b0=1\"" : "\"b1=0\"");
- ret = -ETIME;
- end:
- clear_bit(EC_FLAGS_ADDRESS, &ec->flags);
- return ret;
+ return -ETIME;
}
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
@@ -273,15 +239,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
"write_cmd timeout, command = %d\n", command);
goto end;
}
- /* mark the address byte written to EC */
- if (rdata_len + wdata_len > 1)
- set_bit(EC_FLAGS_ADDRESS, &ec->flags);
set_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
acpi_ec_write_data(ec, *(wdata++));
}
if (!rdata_len) {
- set_bit(EC_FLAGS_WDATA, &ec->flags);
result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, force_poll);
if (result) {
pr_err(PREFIX
@@ -558,6 +520,7 @@ static u32 acpi_ec_gpe_handler(void *data)
status = acpi_os_execute(OSL_EC_BURST_HANDLER,
acpi_ec_gpe_query, ec);
} else if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) &&
+ !test_bit(EC_FLAGS_NO_GPE, &ec->flags) &&
in_interrupt()) {
/* this is non-query, must be confirmation */
if (printk_ratelimit())
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 006/171] ACPI: EC: Switch off GPE mode during suspend/resume
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (3 preceding siblings ...)
2008-04-29 8:43 ` [PATCH 005/171] ACPI: EC: Replace broken controller workarounds with poll mode Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 007/171] ACPI: EC: Detect irq storm Len Brown
` (37 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 60 +++++++++++++++++++++++++++++++++-------------------
1 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 7f07b68..da67d22 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -83,28 +83,6 @@ enum {
EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */
};
-static int acpi_ec_remove(struct acpi_device *device, int type);
-static int acpi_ec_start(struct acpi_device *device);
-static int acpi_ec_stop(struct acpi_device *device, int type);
-static int acpi_ec_add(struct acpi_device *device);
-
-static const struct acpi_device_id ec_device_ids[] = {
- {"PNP0C09", 0},
- {"", 0},
-};
-
-static struct acpi_driver acpi_ec_driver = {
- .name = "ec",
- .class = ACPI_EC_CLASS,
- .ids = ec_device_ids,
- .ops = {
- .add = acpi_ec_add,
- .remove = acpi_ec_remove,
- .start = acpi_ec_start,
- .stop = acpi_ec_stop,
- },
-};
-
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
/* External interfaces use first EC only, so remember */
typedef int (*acpi_ec_query_func) (void *data);
@@ -924,6 +902,11 @@ int __init acpi_boot_ec_enable(void)
return -EFAULT;
}
+static const struct acpi_device_id ec_device_ids[] = {
+ {"PNP0C09", 0},
+ {"", 0},
+};
+
int __init acpi_ec_ecdt_probe(void)
{
int ret;
@@ -973,6 +956,39 @@ int __init acpi_ec_ecdt_probe(void)
return -ENODEV;
}
+static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state)
+{
+ struct acpi_ec *ec = acpi_driver_data(device);
+ /* Stop using GPE */
+ set_bit(EC_FLAGS_NO_GPE, &ec->flags);
+ clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
+ acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
+ return 0;
+}
+
+static int acpi_ec_resume(struct acpi_device *device)
+{
+ struct acpi_ec *ec = acpi_driver_data(device);
+ /* Enable use of GPE back */
+ clear_bit(EC_FLAGS_NO_GPE, &ec->flags);
+ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
+ return 0;
+}
+
+static struct acpi_driver acpi_ec_driver = {
+ .name = "ec",
+ .class = ACPI_EC_CLASS,
+ .ids = ec_device_ids,
+ .ops = {
+ .add = acpi_ec_add,
+ .remove = acpi_ec_remove,
+ .start = acpi_ec_start,
+ .stop = acpi_ec_stop,
+ .suspend = acpi_ec_suspend,
+ .resume = acpi_ec_resume,
+ },
+};
+
static int __init acpi_ec_init(void)
{
int result = 0;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 007/171] ACPI: EC: Detect irq storm
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (4 preceding siblings ...)
2008-04-29 8:43 ` [PATCH 006/171] ACPI: EC: Switch off GPE mode during suspend/resume Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:43 ` [PATCH 008/171] ACPI: EC: Use default setup handler Len Brown
` (36 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Problem seems to be that hw fails to clear GPE after we service it and write 1
into corresponding bit. Thus, as soon as we get interrupts enabled again, we
receive a new one. Google gives too many results for "acer interrupt storm" for
this being one-broken-machine case.
Reference: http://bugzilla.kernel.org/show_bug.cgi?id=9998
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index da67d22..f0e8216 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -106,6 +106,7 @@ static struct acpi_ec {
wait_queue_head_t wait;
struct list_head list;
struct delayed_work work;
+ atomic_t irq_count;
u8 handlers_installed;
} *boot_ec, *first_ec;
@@ -171,6 +172,7 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
{
+ atomic_set(&ec->irq_count, 0);
if (likely(test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) &&
likely(!force_poll)) {
if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
@@ -489,6 +491,12 @@ static u32 acpi_ec_gpe_handler(void *data)
u8 state = acpi_ec_read_status(ec);
pr_debug(PREFIX "~~~> interrupt\n");
+ atomic_inc(&ec->irq_count);
+ if (atomic_read(&ec->irq_count) > 5) {
+ pr_err(PREFIX "GPE storm detected, disabling EC GPE\n");
+ ec_switch_to_poll_mode(ec);
+ goto end;
+ }
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
wake_up(&ec->wait);
@@ -507,6 +515,7 @@ static u32 acpi_ec_gpe_handler(void *data)
set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
clear_bit(EC_FLAGS_RESCHEDULE_POLL, &ec->flags);
}
+end:
ec_schedule_ec_poll(ec);
return ACPI_SUCCESS(status) ?
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
@@ -515,6 +524,7 @@ static u32 acpi_ec_gpe_handler(void *data)
static void do_ec_poll(struct work_struct *work)
{
struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work);
+ atomic_set(&ec->irq_count, 0);
(void)acpi_ec_gpe_handler(ec);
}
@@ -679,6 +689,7 @@ static struct acpi_ec *make_acpi_ec(void)
init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list);
INIT_DELAYED_WORK_DEFERRABLE(&ec->work, do_ec_poll);
+ atomic_set(&ec->irq_count, 0);
return ec;
}
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 008/171] ACPI: EC: Use default setup handler
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (5 preceding siblings ...)
2008-04-29 8:43 ` [PATCH 007/171] ACPI: EC: Detect irq storm Len Brown
@ 2008-04-29 8:43 ` Len Brown
2008-04-29 8:44 ` [PATCH 009/171] ACPI: EC: Don't delete boot EC Len Brown
` (35 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:43 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 16 +---------------
1 files changed, 1 insertions(+), 15 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index f0e8216..167af37 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -533,20 +533,6 @@ static void do_ec_poll(struct work_struct *work)
-------------------------------------------------------------------------- */
static acpi_status
-acpi_ec_space_setup(acpi_handle region_handle,
- u32 function, void *handler_context, void **return_context)
-{
- /*
- * The EC object is in the handler context and is needed
- * when calling the acpi_ec_space_handler.
- */
- *return_context = (function != ACPI_REGION_DEACTIVATE) ?
- handler_context : NULL;
-
- return AE_OK;
-}
-
-static acpi_status
acpi_ec_space_handler(u32 function, acpi_physical_address address,
u32 bits, acpi_integer *value,
void *handler_context, void *region_context)
@@ -858,7 +844,7 @@ static int ec_install_handlers(struct acpi_ec *ec)
status = acpi_install_address_space_handler(ec->handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
- &acpi_ec_space_setup, ec);
+ NULL, ec);
if (ACPI_FAILURE(status)) {
acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
return -ENODEV;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 009/171] ACPI: EC: Don't delete boot EC
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (6 preceding siblings ...)
2008-04-29 8:43 ` [PATCH 008/171] ACPI: EC: Use default setup handler Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 010/171] ACPI : Disable the device's ability to wake the sleeping system in the boot phase Len Brown
` (34 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/ec.c | 43 +++++++++++++++++++------------------------
1 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 167af37..3d93621 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -708,9 +708,6 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
if (ACPI_FAILURE(status))
return status;
- /* Find and register all query methods */
- acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1,
- acpi_ec_register_query_methods, ec, NULL);
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);
ec->handle = handle;
@@ -745,31 +742,28 @@ static int acpi_ec_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
/* Check for boot EC */
- if (boot_ec) {
- if (boot_ec->handle == device->handle) {
- /* Pre-loaded EC from DSDT, just move pointer */
- ec = boot_ec;
- boot_ec = NULL;
- goto end;
- } else if (boot_ec->handle == ACPI_ROOT_OBJECT) {
- /* ECDT-based EC, time to shut it down */
- ec_remove_handlers(boot_ec);
- kfree(boot_ec);
- first_ec = boot_ec = NULL;
+ if (boot_ec &&
+ (boot_ec->handle == device->handle ||
+ boot_ec->handle == ACPI_ROOT_OBJECT)) {
+ ec = boot_ec;
+ boot_ec = NULL;
+ } else {
+ ec = make_acpi_ec();
+ if (!ec)
+ return -ENOMEM;
+ if (ec_parse_device(device->handle, 0, ec, NULL) !=
+ AE_CTRL_TERMINATE) {
+ kfree(ec);
+ return -EINVAL;
}
}
- ec = make_acpi_ec();
- if (!ec)
- return -ENOMEM;
-
- if (ec_parse_device(device->handle, 0, ec, NULL) !=
- AE_CTRL_TERMINATE) {
- kfree(ec);
- return -EINVAL;
- }
ec->handle = device->handle;
- end:
+
+ /* Find and register all query methods */
+ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
+ acpi_ec_register_query_methods, ec, NULL);
+
if (!first_ec)
first_ec = ec;
acpi_driver_data(device) = ec;
@@ -924,6 +918,7 @@ int __init acpi_ec_ecdt_probe(void)
boot_ec->data_addr = ecdt_ptr->data.address;
boot_ec->gpe = ecdt_ptr->gpe;
boot_ec->handle = ACPI_ROOT_OBJECT;
+ acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle);
} else {
/* This workaround is needed only on some broken machines,
* which require early EC, but fail to provide ECDT */
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 010/171] ACPI : Disable the device's ability to wake the sleeping system in the boot phase
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (7 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 009/171] ACPI: EC: Don't delete boot EC Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 011/171] #if 0 acpi/bay.c:eject_removable_drive() Len Brown
` (33 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhao Yakui, Zhang Rui, Len Brown
From: Zhao Yakui <yakui.zhao@intel.com>
In some machines some GPE is shared by several ACPI devices, for example:
sleep button, keyboard, mouse. At the same time one of them is
non-wake(runtime) device and the other are wake devices. In such case OSPM
should call the _PSW object to disable the device's ability to
wake the sleeping system in the boot phase.
Otherwise there will be ACPI interrupt flood triggered by the GPE input.
The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
So it is necessary to call _DSW object first. Only when it is not
present will the _PSW object used.
http://bugzilla.kernel.org/show_bug.cgi?id=10224
Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/scan.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 42 insertions(+), 1 deletions(-)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e6ce262..bd32351 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -692,6 +692,9 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
acpi_status status = 0;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package = NULL;
+ union acpi_object in_arg[3];
+ struct acpi_object_list arg_list = { 3, in_arg };
+ acpi_status psw_status = AE_OK;
struct acpi_device_id button_device_ids[] = {
{"PNP0C0D", 0},
@@ -700,7 +703,6 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
{"", 0},
};
-
/* _PRW */
status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
if (ACPI_FAILURE(status)) {
@@ -718,6 +720,45 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
kfree(buffer.pointer);
device->wakeup.flags.valid = 1;
+ /* Call _PSW/_DSW object to disable its ability to wake the sleeping
+ * system for the ACPI device with the _PRW object.
+ * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
+ * So it is necessary to call _DSW object first. Only when it is not
+ * present will the _PSW object used.
+ */
+ /*
+ * Three agruments are needed for the _DSW object.
+ * Argument 0: enable/disable the wake capabilities
+ * When _DSW object is called to disable the wake capabilities, maybe
+ * the first argument is filled. The value of the other two agruments
+ * is meaningless.
+ */
+ in_arg[0].type = ACPI_TYPE_INTEGER;
+ in_arg[0].integer.value = 0;
+ in_arg[1].type = ACPI_TYPE_INTEGER;
+ in_arg[1].integer.value = 0;
+ in_arg[2].type = ACPI_TYPE_INTEGER;
+ in_arg[2].integer.value = 0;
+ psw_status = acpi_evaluate_object(device->handle, "_DSW",
+ &arg_list, NULL);
+ if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND))
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in evaluate _DSW\n"));
+ /*
+ * When the _DSW object is not present, OSPM will call _PSW object.
+ */
+ if (psw_status == AE_NOT_FOUND) {
+ /*
+ * Only one agruments is required for the _PSW object.
+ * agrument 0: enable/disable the wake capabilities
+ */
+ arg_list.count = 1;
+ in_arg[0].integer.value = 0;
+ psw_status = acpi_evaluate_object(device->handle, "_PSW",
+ &arg_list, NULL);
+ if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND))
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in "
+ "evaluate _PSW\n"));
+ }
/* Power button, Lid switch always enable wakeup */
if (!acpi_match_device_ids(device, button_device_ids))
device->wakeup.flags.run_wake = 1;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 011/171] #if 0 acpi/bay.c:eject_removable_drive()
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (8 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 010/171] ACPI : Disable the device's ability to wake the sleeping system in the boot phase Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 012/171] PM: Remove legacy PM Len Brown
` (32 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Adrian Bunk, Len Brown
From: Adrian Bunk <bunk@kernel.org>
This patch #if 0's the unused eject_removable_drive().
Signed-off-by: Adrian Bunk <bunk@kernel.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/bay.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c
index 1fa8681..d2fc941 100644
--- a/drivers/acpi/bay.c
+++ b/drivers/acpi/bay.c
@@ -201,6 +201,7 @@ static int is_ejectable_bay(acpi_handle handle)
return 0;
}
+#if 0
/**
* eject_removable_drive - try to eject this drive
* @dev : the device structure of the drive
@@ -225,6 +226,7 @@ int eject_removable_drive(struct device *dev)
return 0;
}
EXPORT_SYMBOL_GPL(eject_removable_drive);
+#endif /* 0 */
static int acpi_bay_add_fs(struct bay *bay)
{
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 012/171] PM: Remove legacy PM
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (9 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 011/171] #if 0 acpi/bay.c:eject_removable_drive() Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 013/171] PM: arch/x86/kernel/apm_32.c: fix build warning Len Brown
` (31 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Pavel Machek, Rafael J. Wysocki, Len Brown
From: Pavel Machek <pavel@suse.cz>
AFAICT pm_send_all is a nop when noone uses pm_register...
Hmm.. can we just force CONFIG_PM_LEGACY=n, and see what happens?
Or maybe this is better idea? It may break build somewhere, but it
should be easy to fix... (it builds here, i386 and x86-64).
Signed-off-by: Pavel Machek <pavel@suse.cz>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
---
arch/frv/kernel/pm.c | 8 --
arch/mips/au1000/common/power.c | 17 +---
arch/x86/kernel/apm_32.c | 15 ---
kernel/power/Kconfig | 10 --
kernel/power/Makefile | 1 -
kernel/power/pm.c | 205 ---------------------------------------
6 files changed, 2 insertions(+), 254 deletions(-)
delete mode 100644 kernel/power/pm.c
diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c
index c57ce3f..73f3aee 100644
--- a/arch/frv/kernel/pm.c
+++ b/arch/frv/kernel/pm.c
@@ -163,14 +163,11 @@ static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp,
if ((mode != 1) && (mode != 5))
return -EINVAL;
- retval = pm_send_all(PM_SUSPEND, (void *)3);
-
if (retval == 0) {
if (mode == 5)
retval = pm_do_bus_sleep();
else
retval = pm_do_suspend();
- pm_send_all(PM_RESUME, (void *)0);
}
return retval;
@@ -183,9 +180,6 @@ static int try_set_cmode(int new_cmode)
if (!(clock_cmodes_permitted & (1<<new_cmode)))
return -EINVAL;
- /* tell all the drivers we're suspending */
- pm_send_all(PM_SUSPEND, (void *)3);
-
/* now change cmode */
local_irq_disable();
frv_dma_pause_all();
@@ -201,8 +195,6 @@ static int try_set_cmode(int new_cmode)
frv_dma_resume_all();
local_irq_enable();
- /* tell all the drivers we're resuming */
- pm_send_all(PM_RESUME, (void *)0);
return 0;
}
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index 54047d6..a9f7f63 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -258,7 +258,6 @@ int au_sleep(void)
static int pm_do_sleep(ctl_table * ctl, int write, struct file *file,
void __user *buffer, size_t * len, loff_t *ppos)
{
- int retval = 0;
#ifdef SLEEP_TEST_TIMEOUT
#define TMPBUFLEN2 16
char buf[TMPBUFLEN2], *p;
@@ -278,33 +277,21 @@ static int pm_do_sleep(ctl_table * ctl, int write, struct file *file,
p = buf;
sleep_ticks = simple_strtoul(p, &p, 0);
#endif
- retval = pm_send_all(PM_SUSPEND, (void *) 2);
-
- if (retval)
- return retval;
au_sleep();
- retval = pm_send_all(PM_RESUME, (void *) 0);
}
- return retval;
+ return 0;
}
static int pm_do_suspend(ctl_table * ctl, int write, struct file *file,
void __user *buffer, size_t * len, loff_t *ppos)
{
- int retval = 0;
-
if (!write) {
*len = 0;
} else {
- retval = pm_send_all(PM_SUSPEND, (void *) 2);
- if (retval)
- return retval;
suspend_mode = 1;
-
- retval = pm_send_all(PM_RESUME, (void *) 0);
}
- return retval;
+ return 0;
}
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index d4438ef..d7e92bf 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -1189,19 +1189,6 @@ static int suspend(int vetoable)
int err;
struct apm_user *as;
- if (pm_send_all(PM_SUSPEND, (void *)3)) {
- /* Vetoed */
- if (vetoable) {
- if (apm_info.connection_version > 0x100)
- set_system_power_state(APM_STATE_REJECT);
- err = -EBUSY;
- ignore_sys_suspend = 0;
- printk(KERN_WARNING "apm: suspend was vetoed.\n");
- goto out;
- }
- printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
- }
-
device_suspend(PMSG_SUSPEND);
local_irq_disable();
device_power_down(PMSG_SUSPEND);
@@ -1224,7 +1211,6 @@ static int suspend(int vetoable)
device_power_up();
local_irq_enable();
device_resume();
- pm_send_all(PM_RESUME, (void *)0);
queue_event(APM_NORMAL_RESUME, NULL);
out:
spin_lock(&user_list_lock);
@@ -1337,7 +1323,6 @@ static void check_events(void)
if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) {
device_resume();
- pm_send_all(PM_RESUME, (void *)0);
queue_event(event, NULL);
}
ignore_normal_resume = 0;
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 6233f3b..b45da40 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -19,16 +19,6 @@ config PM
will issue the hlt instruction if nothing is to be done, thereby
sending the processor to sleep and saving power.
-config PM_LEGACY
- bool "Legacy Power Management API (DEPRECATED)"
- depends on PM
- default n
- ---help---
- Support for pm_register() and friends. This old API is obsoleted
- by the driver model.
-
- If unsure, say N.
-
config PM_DEBUG
bool "Power Management Debug Support"
depends on PM
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index f7dfff2..597823b 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -4,7 +4,6 @@ EXTRA_CFLAGS += -DDEBUG
endif
obj-y := main.o
-obj-$(CONFIG_PM_LEGACY) += pm.o
obj-$(CONFIG_PM_SLEEP) += process.o console.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
diff --git a/kernel/power/pm.c b/kernel/power/pm.c
deleted file mode 100644
index 60c73fa..0000000
--- a/kernel/power/pm.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * pm.c - Power management interface
- *
- * Copyright (C) 2000 Andrew Henroid
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/pm.h>
-#include <linux/pm_legacy.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-
-/*
- * Locking notes:
- * pm_devs_lock can be a semaphore providing pm ops are not called
- * from an interrupt handler (already a bad idea so no change here). Each
- * change must be protected so that an unlink of an entry doesn't clash
- * with a pm send - which is permitted to sleep in the current architecture
- *
- * Module unloads clashing with pm events now work out safely, the module
- * unload path will block until the event has been sent. It may well block
- * until a resume but that will be fine.
- */
-
-static DEFINE_MUTEX(pm_devs_lock);
-static LIST_HEAD(pm_devs);
-
-/**
- * pm_register - register a device with power management
- * @type: device type
- * @id: device ID
- * @callback: callback function
- *
- * Add a device to the list of devices that wish to be notified about
- * power management events. A &pm_dev structure is returned on success,
- * on failure the return is %NULL.
- *
- * The callback function will be called in process context and
- * it may sleep.
- */
-
-struct pm_dev *pm_register(pm_dev_t type,
- unsigned long id,
- pm_callback callback)
-{
- struct pm_dev *dev = kzalloc(sizeof(struct pm_dev), GFP_KERNEL);
- if (dev) {
- dev->type = type;
- dev->id = id;
- dev->callback = callback;
-
- mutex_lock(&pm_devs_lock);
- list_add(&dev->entry, &pm_devs);
- mutex_unlock(&pm_devs_lock);
- }
- return dev;
-}
-
-/**
- * pm_send - send request to a single device
- * @dev: device to send to
- * @rqst: power management request
- * @data: data for the callback
- *
- * Issue a power management request to a given device. The
- * %PM_SUSPEND and %PM_RESUME events are handled specially. The
- * data field must hold the intended next state. No call is made
- * if the state matches.
- *
- * BUGS: what stops two power management requests occurring in parallel
- * and conflicting.
- *
- * WARNING: Calling pm_send directly is not generally recommended, in
- * particular there is no locking against the pm_dev going away. The
- * caller must maintain all needed locking or have 'inside knowledge'
- * on the safety. Also remember that this function is not locked against
- * pm_unregister. This means that you must handle SMP races on callback
- * execution and unload yourself.
- */
-
-static int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
-{
- int status = 0;
- unsigned long prev_state, next_state;
-
- if (in_interrupt())
- BUG();
-
- switch (rqst) {
- case PM_SUSPEND:
- case PM_RESUME:
- prev_state = dev->state;
- next_state = (unsigned long) data;
- if (prev_state != next_state) {
- if (dev->callback)
- status = (*dev->callback)(dev, rqst, data);
- if (!status) {
- dev->state = next_state;
- dev->prev_state = prev_state;
- }
- }
- else {
- dev->prev_state = prev_state;
- }
- break;
- default:
- if (dev->callback)
- status = (*dev->callback)(dev, rqst, data);
- break;
- }
- return status;
-}
-
-/*
- * Undo incomplete request
- */
-static void pm_undo_all(struct pm_dev *last)
-{
- struct list_head *entry = last->entry.prev;
- while (entry != &pm_devs) {
- struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
- if (dev->state != dev->prev_state) {
- /* previous state was zero (running) resume or
- * previous state was non-zero (suspended) suspend
- */
- pm_request_t undo = (dev->prev_state
- ? PM_SUSPEND:PM_RESUME);
- pm_send(dev, undo, (void*) dev->prev_state);
- }
- entry = entry->prev;
- }
-}
-
-/**
- * pm_send_all - send request to all managed devices
- * @rqst: power management request
- * @data: data for the callback
- *
- * Issue a power management request to a all devices. The
- * %PM_SUSPEND events are handled specially. Any device is
- * permitted to fail a suspend by returning a non zero (error)
- * value from its callback function. If any device vetoes a
- * suspend request then all other devices that have suspended
- * during the processing of this request are restored to their
- * previous state.
- *
- * WARNING: This function takes the pm_devs_lock. The lock is not dropped until
- * the callbacks have completed. This prevents races against pm locking
- * functions, races against module unload pm_unregister code. It does
- * mean however that you must not issue pm_ functions within the callback
- * or you will deadlock and users will hate you.
- *
- * Zero is returned on success. If a suspend fails then the status
- * from the device that vetoes the suspend is returned.
- *
- * BUGS: what stops two power management requests occurring in parallel
- * and conflicting.
- */
-
-int pm_send_all(pm_request_t rqst, void *data)
-{
- struct list_head *entry;
-
- mutex_lock(&pm_devs_lock);
- entry = pm_devs.next;
- while (entry != &pm_devs) {
- struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
- if (dev->callback) {
- int status = pm_send(dev, rqst, data);
- if (status) {
- /* return devices to previous state on
- * failed suspend request
- */
- if (rqst == PM_SUSPEND)
- pm_undo_all(dev);
- mutex_unlock(&pm_devs_lock);
- return status;
- }
- }
- entry = entry->next;
- }
- mutex_unlock(&pm_devs_lock);
- return 0;
-}
-
-EXPORT_SYMBOL(pm_register);
-EXPORT_SYMBOL(pm_send_all);
-
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 013/171] PM: arch/x86/kernel/apm_32.c: fix build warning
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (10 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 012/171] PM: Remove legacy PM Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 014/171] MIPS Alchemy: Crapectomy after removal of pm_send_all calls Len Brown
` (30 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Andrew Morton, Len Brown
From: Andrew Morton <akpm@linux-foundation.org>
arch/x86/kernel/apm_32.c:1215: warning: label 'out' defined but not used
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
arch/x86/kernel/apm_32.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index d7e92bf..bc8b57d 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -1212,7 +1212,6 @@ static int suspend(int vetoable)
local_irq_enable();
device_resume();
queue_event(APM_NORMAL_RESUME, NULL);
- out:
spin_lock(&user_list_lock);
for (as = user_list; as != NULL; as = as->next) {
as->suspend_wait = 0;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 014/171] MIPS Alchemy: Crapectomy after removal of pm_send_all calls.
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (11 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 013/171] PM: arch/x86/kernel/apm_32.c: fix build warning Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 085/171] eeepc-laptop: add base driver Len Brown
` (29 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Ralf Baechle, Rafael J. Wysocki, Len Brown
From: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
---
arch/mips/au1000/common/power.c | 20 --------------------
1 files changed, 0 insertions(+), 20 deletions(-)
diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c
index a9f7f63..725a52f 100644
--- a/arch/mips/au1000/common/power.c
+++ b/arch/mips/au1000/common/power.c
@@ -283,18 +283,6 @@ static int pm_do_sleep(ctl_table * ctl, int write, struct file *file,
return 0;
}
-static int pm_do_suspend(ctl_table * ctl, int write, struct file *file,
- void __user *buffer, size_t * len, loff_t *ppos)
-{
- if (!write) {
- *len = 0;
- } else {
- suspend_mode = 1;
- }
- return 0;
-}
-
-
static int pm_do_freq(ctl_table * ctl, int write, struct file *file,
void __user *buffer, size_t * len, loff_t *ppos)
{
@@ -408,14 +396,6 @@ static int pm_do_freq(ctl_table * ctl, int write, struct file *file,
static struct ctl_table pm_table[] = {
{
- .ctl_name = CTL_UNNUMBERED,
- .procname = "suspend",
- .data = NULL,
- .maxlen = 0,
- .mode = 0600,
- .proc_handler = &pm_do_suspend
- },
- {
.ctl_name = CTL_UNNUMBERED,
.procname = "sleep",
.data = NULL,
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 085/171] eeepc-laptop: add base driver
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (12 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 014/171] MIPS Alchemy: Crapectomy after removal of pm_send_all calls Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 086/171] eeepc-laptop: add backlight Len Brown
` (28 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Eric Cooper, Corentin Chary, Len Brown
From: Eric Cooper <ecc@cmu.edu>
This patch is based on Eric Cooper's work to clean the original asus_acpi
given by Asus. It's a platform driver (/sys/devices/platform/eeepc/)
wich support:
- hotkeys - wlan on/off - camera on/off - cardr on/off
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/Kconfig | 11 +
drivers/misc/Makefile | 3 +-
drivers/misc/eeepc-laptop.c | 442 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 455 insertions(+), 1 deletions(-)
create mode 100644 drivers/misc/eeepc-laptop.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 962817e..10c5ecf 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -327,4 +327,15 @@ config ENCLOSURE_SERVICES
driver (SCSI/ATA) which supports enclosures
or a SCSI enclosure device (SES) to use these services.
+config EEEPC_LAPTOP
+ tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
+ depends on X86
+ depends on ACPI
+ depends on EXPERIMENTAL
+ ---help---
+ This driver supports the Fn-Fx keys on Eee PC laptops.
+ It also adds the ability to switch camera/wlan on/off.
+
+ If you have an Eee PC laptop, say Y or M here.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3b12f5d..ef53a92 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -7,7 +7,8 @@ obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
-obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
+obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
+obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c
new file mode 100644
index 0000000..e34ff97
--- /dev/null
+++ b/drivers/misc/eeepc-laptop.c
@@ -0,0 +1,442 @@
+/*
+ * eepc-laptop.c - Asus Eee PC extras
+ *
+ * Based on asus_acpi.c as patched for the Eee PC by Asus:
+ * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
+ * Based on eee.c from eeepc-linux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <linux/uaccess.h>
+
+#define EEEPC_LAPTOP_VERSION "0.1"
+
+#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
+#define EEEPC_HOTK_FILE "eeepc"
+#define EEEPC_HOTK_CLASS "hotkey"
+#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
+#define EEEPC_HOTK_HID "ASUS010"
+
+#define EEEPC_LOG EEEPC_HOTK_FILE ": "
+#define EEEPC_ERR KERN_ERR EEEPC_LOG
+#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
+#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
+#define EEEPC_INFO KERN_INFO EEEPC_LOG
+
+/*
+ * Definitions for Asus EeePC
+ */
+#define NOTIFY_WLAN_ON 0x10
+
+enum {
+ DISABLE_ASL_WLAN = 0x0001,
+ DISABLE_ASL_BLUETOOTH = 0x0002,
+ DISABLE_ASL_IRDA = 0x0004,
+ DISABLE_ASL_CAMERA = 0x0008,
+ DISABLE_ASL_TV = 0x0010,
+ DISABLE_ASL_GPS = 0x0020,
+ DISABLE_ASL_DISPLAYSWITCH = 0x0040,
+ DISABLE_ASL_MODEM = 0x0080,
+ DISABLE_ASL_CARDREADER = 0x0100
+};
+
+enum {
+ CM_ASL_WLAN = 0,
+ CM_ASL_BLUETOOTH,
+ CM_ASL_IRDA,
+ CM_ASL_1394,
+ CM_ASL_CAMERA,
+ CM_ASL_TV,
+ CM_ASL_GPS,
+ CM_ASL_DVDROM,
+ CM_ASL_DISPLAYSWITCH,
+ CM_ASL_PANELBRIGHT,
+ CM_ASL_BIOSFLASH,
+ CM_ASL_ACPIFLASH,
+ CM_ASL_CPUFV,
+ CM_ASL_CPUTEMPERATURE,
+ CM_ASL_FANCPU,
+ CM_ASL_FANCHASSIS,
+ CM_ASL_USBPORT1,
+ CM_ASL_USBPORT2,
+ CM_ASL_USBPORT3,
+ CM_ASL_MODEM,
+ CM_ASL_CARDREADER,
+ CM_ASL_LID
+};
+
+const char *cm_getv[] = {
+ "WLDG", NULL, NULL, NULL,
+ "CAMG", NULL, NULL, NULL,
+ NULL, "PBLG", NULL, NULL,
+ "CFVG", NULL, NULL, NULL,
+ "USBG", NULL, NULL, "MODG",
+ "CRDG", "LIDG"
+};
+
+const char *cm_setv[] = {
+ "WLDS", NULL, NULL, NULL,
+ "CAMS", NULL, NULL, NULL,
+ "SDSP", "PBLS", "HDPS", NULL,
+ "CFVS", NULL, NULL, NULL,
+ "USBG", NULL, NULL, "MODS",
+ "CRDS", NULL
+};
+
+/*
+ * This is the main structure, we can use it to store useful information
+ * about the hotk device
+ */
+struct eeepc_hotk {
+ struct acpi_device *device; /* the device we are in */
+ acpi_handle handle; /* the handle of the hotk device */
+ u32 cm_supported; /* the control methods supported
+ by this BIOS */
+ uint init_flag; /* Init flags */
+ u16 event_count[128]; /* count for each event */
+};
+
+/* The actual device the driver binds to */
+static struct eeepc_hotk *ehotk;
+
+/* Platform device/driver */
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = EEEPC_HOTK_FILE,
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct platform_device *platform_device;
+
+/*
+ * The hotkey driver declaration
+ */
+static int eeepc_hotk_add(struct acpi_device *device);
+static int eeepc_hotk_remove(struct acpi_device *device, int type);
+
+static const struct acpi_device_id eeepc_device_ids[] = {
+ {EEEPC_HOTK_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
+
+static struct acpi_driver eeepc_hotk_driver = {
+ .name = EEEPC_HOTK_NAME,
+ .class = EEEPC_HOTK_CLASS,
+ .ids = eeepc_device_ids,
+ .ops = {
+ .add = eeepc_hotk_add,
+ .remove = eeepc_hotk_remove,
+ },
+};
+
+MODULE_AUTHOR("Corentin Chary, Eric Cooper");
+MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * ACPI Helpers
+ */
+static int write_acpi_int(acpi_handle handle, const char *method, int val,
+ struct acpi_buffer *output)
+{
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ acpi_status status;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = val;
+
+ status = acpi_evaluate_object(handle, (char *)method, ¶ms, output);
+ return (status == AE_OK ? 0 : -1);
+}
+
+static int read_acpi_int(acpi_handle handle, const char *method, int *val)
+{
+ acpi_status status;
+ ulong result;
+
+ status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
+ if (ACPI_FAILURE(status)) {
+ *val = -1;
+ return -1;
+ } else {
+ *val = result;
+ return 0;
+ }
+}
+
+static int set_acpi(int cm, int value)
+{
+ if (ehotk->cm_supported & (0x1 << cm)) {
+ const char *method = cm_setv[cm];
+ if (method == NULL)
+ return -ENODEV;
+ if (write_acpi_int(ehotk->handle, method, value, NULL))
+ printk(EEEPC_WARNING "Error writing %s\n", method);
+ }
+ return 0;
+}
+
+static int get_acpi(int cm)
+{
+ int value = -1;
+ if ((ehotk->cm_supported & (0x1 << cm))) {
+ const char *method = cm_getv[cm];
+ if (method == NULL)
+ return -ENODEV;
+ if (read_acpi_int(ehotk->handle, method, &value))
+ printk(EEEPC_WARNING "Error reading %s\n", method);
+ }
+ return value;
+}
+
+/*
+ * Sys helpers
+ */
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", val) != 1)
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
+{
+ int rv, value;
+
+ rv = parse_arg(buf, count, &value);
+ if (rv > 0)
+ set_acpi(cm, value);
+ return rv;
+}
+
+static ssize_t show_sys_acpi(int cm, char *buf)
+{
+ return sprintf(buf, "%d\n", get_acpi(cm));
+}
+
+#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
+ static ssize_t show_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return show_sys_acpi(_cm, buf); \
+ } \
+ static ssize_t store_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return store_sys_acpi(_cm, buf, count); \
+ } \
+ static struct device_attribute dev_attr_##_name = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0644 }, \
+ .show = show_##_name, \
+ .store = store_##_name, \
+ }
+
+EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
+EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
+EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
+EEEPC_CREATE_DEVICE_ATTR(wlan, CM_ASL_WLAN);
+
+static struct attribute *platform_attributes[] = {
+ &dev_attr_camera.attr,
+ &dev_attr_cardr.attr,
+ &dev_attr_disp.attr,
+ &dev_attr_wlan.attr,
+ NULL
+};
+
+static struct attribute_group platform_attribute_group = {
+ .attrs = platform_attributes
+};
+
+/*
+ * Hotkey functions
+ */
+static int eeepc_hotk_check(void)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ int result;
+
+ result = acpi_bus_get_status(ehotk->device);
+ if (result)
+ return result;
+ if (ehotk->device->status.present) {
+ if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
+ &buffer)) {
+ printk(EEEPC_ERR "Hotkey initialization failed\n");
+ return -ENODEV;
+ } else {
+ printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
+ ehotk->init_flag);
+ }
+ /* get control methods supported */
+ if (read_acpi_int(ehotk->handle, "CMSG"
+ , &ehotk->cm_supported)) {
+ printk(EEEPC_ERR
+ "Get control methods supported failed\n");
+ return -ENODEV;
+ } else {
+ printk(EEEPC_INFO
+ "Get control methods supported: 0x%x\n",
+ ehotk->cm_supported);
+ }
+ } else {
+ printk(EEEPC_ERR "Hotkey device not present, aborting\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void notify_wlan(u32 *event)
+{
+ /* if DISABLE_ASL_WLAN is set, the notify code for fn+f2
+ will always be 0x10 */
+ if (ehotk->cm_supported & (0x1 << CM_ASL_WLAN)) {
+ const char *method = cm_getv[CM_ASL_WLAN];
+ int value;
+ if (read_acpi_int(ehotk->handle, method, &value))
+ printk(EEEPC_WARNING "Error reading %s\n",
+ method);
+ else if (value == 1)
+ *event = 0x11;
+ }
+}
+
+static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
+{
+ if (!ehotk)
+ return;
+ if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag))
+ notify_wlan(&event);
+ acpi_bus_generate_proc_event(ehotk->device, event,
+ ehotk->event_count[event % 128]++);
+}
+
+static int eeepc_hotk_add(struct acpi_device *device)
+{
+ acpi_status status = AE_OK;
+ int result;
+
+ if (!device)
+ return -EINVAL;
+ printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
+ ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
+ if (!ehotk)
+ return -ENOMEM;
+ ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
+ ehotk->handle = device->handle;
+ strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
+ strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
+ acpi_driver_data(device) = ehotk;
+ ehotk->device = device;
+ result = eeepc_hotk_check();
+ if (result)
+ goto end;
+ status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
+ eeepc_hotk_notify, ehotk);
+ if (ACPI_FAILURE(status))
+ printk(EEEPC_ERR "Error installing notify handler\n");
+ end:
+ if (result) {
+ kfree(ehotk);
+ ehotk = NULL;
+ }
+ return result;
+}
+
+static int eeepc_hotk_remove(struct acpi_device *device, int type)
+{
+ acpi_status status = 0;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+ status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
+ eeepc_hotk_notify);
+ if (ACPI_FAILURE(status))
+ printk(EEEPC_ERR "Error removing notify handler\n");
+ kfree(ehotk);
+ return 0;
+}
+
+/*
+ * exit/init
+ */
+static void __exit eeepc_laptop_exit(void)
+{
+ acpi_bus_unregister_driver(&eeepc_hotk_driver);
+ sysfs_remove_group(&platform_device->dev.kobj,
+ &platform_attribute_group);
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+}
+
+static int __init eeepc_laptop_init(void)
+{
+ struct device *dev;
+ int result;
+
+ if (acpi_disabled)
+ return -ENODEV;
+ result = acpi_bus_register_driver(&eeepc_hotk_driver);
+ if (result < 0)
+ return result;
+ if (!ehotk) {
+ acpi_bus_unregister_driver(&eeepc_hotk_driver);
+ return -ENODEV;
+ }
+ dev = acpi_get_physical_device(ehotk->device->handle);
+ /* Register platform stuff */
+ result = platform_driver_register(&platform_driver);
+ if (result)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
+ if (!platform_device) {
+ result = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ result = platform_device_add(platform_device);
+ if (result)
+ goto fail_platform_device2;
+ result = sysfs_create_group(&platform_device->dev.kobj,
+ &platform_attribute_group);
+ if (result)
+ goto fail_sysfs;
+ return 0;
+fail_sysfs:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+ return result;
+}
+
+module_init(eeepc_laptop_init);
+module_exit(eeepc_laptop_exit);
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 086/171] eeepc-laptop: add backlight
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (13 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 085/171] eeepc-laptop: add base driver Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 087/171] eeepc-laptop: add hwmon fan control Len Brown
` (27 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Corentin Chary, Len Brown
From: Corentin Chary <corentincj@iksaif.net>
Add backlight class support to the eeepc-laptop driver.
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/Kconfig | 1 +
drivers/misc/eeepc-laptop.c | 77 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 0 deletions(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 10c5ecf..e9ab291 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -331,6 +331,7 @@ config EEEPC_LAPTOP
tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
depends on X86
depends on ACPI
+ depends on BACKLIGHT_CLASS_DEVICE
depends on EXPERIMENTAL
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c
index e34ff97..2b59b7e 100644
--- a/drivers/misc/eeepc-laptop.c
+++ b/drivers/misc/eeepc-laptop.c
@@ -21,6 +21,8 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
@@ -43,6 +45,8 @@
* Definitions for Asus EeePC
*/
#define NOTIFY_WLAN_ON 0x10
+#define NOTIFY_BRN_MIN 0x20
+#define NOTIFY_BRN_MAX 0x2f
enum {
DISABLE_ASL_WLAN = 0x0001,
@@ -147,6 +151,19 @@ static struct acpi_driver eeepc_hotk_driver = {
},
};
+/* The backlight device /sys/class/backlight */
+static struct backlight_device *eeepc_backlight_device;
+
+/*
+ * The backlight class declaration
+ */
+static int read_brightness(struct backlight_device *bd);
+static int update_bl_status(struct backlight_device *bd);
+static struct backlight_ops eeepcbl_ops = {
+ .get_brightness = read_brightness,
+ .update_status = update_bl_status,
+};
+
MODULE_AUTHOR("Corentin Chary, Eric Cooper");
MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
MODULE_LICENSE("GPL");
@@ -211,6 +228,25 @@ static int get_acpi(int cm)
}
/*
+ * Backlight
+ */
+static int read_brightness(struct backlight_device *bd)
+{
+ return get_acpi(CM_ASL_PANELBRIGHT);
+}
+
+static int set_brightness(struct backlight_device *bd, int value)
+{
+ value = max(0, min(15, value));
+ return set_acpi(CM_ASL_PANELBRIGHT, value);
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+ return set_brightness(bd, bd->props.brightness);
+}
+
+/*
* Sys helpers
*/
static int parse_arg(const char *buf, unsigned long count, int *val)
@@ -328,12 +364,20 @@ static void notify_wlan(u32 *event)
}
}
+static void notify_brn(void)
+{
+ struct backlight_device *bd = eeepc_backlight_device;
+ bd->props.brightness = read_brightness(bd);
+}
+
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
if (!ehotk)
return;
if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag))
notify_wlan(&event);
+ if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
+ notify_brn();
acpi_bus_generate_proc_event(ehotk->device, event,
ehotk->event_count[event % 128]++);
}
@@ -387,8 +431,16 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type)
/*
* exit/init
*/
+static void eeepc_backlight_exit(void)
+{
+ if (eeepc_backlight_device)
+ backlight_device_unregister(eeepc_backlight_device);
+ eeepc_backlight_device = NULL;
+}
+
static void __exit eeepc_laptop_exit(void)
{
+ eeepc_backlight_exit();
acpi_bus_unregister_driver(&eeepc_hotk_driver);
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
@@ -396,6 +448,26 @@ static void __exit eeepc_laptop_exit(void)
platform_driver_unregister(&platform_driver);
}
+static int eeepc_backlight_init(struct device *dev)
+{
+ struct backlight_device *bd;
+
+ bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
+ NULL, &eeepcbl_ops);
+ if (IS_ERR(bd)) {
+ printk(EEEPC_ERR
+ "Could not register eeepc backlight device\n");
+ eeepc_backlight_device = NULL;
+ return PTR_ERR(bd);
+ }
+ eeepc_backlight_device = bd;
+ bd->props.max_brightness = 15;
+ bd->props.brightness = read_brightness(NULL);
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+ return 0;
+}
+
static int __init eeepc_laptop_init(void)
{
struct device *dev;
@@ -411,6 +483,9 @@ static int __init eeepc_laptop_init(void)
return -ENODEV;
}
dev = acpi_get_physical_device(ehotk->device->handle);
+ result = eeepc_backlight_init(dev);
+ if (result)
+ goto fail_backlight;
/* Register platform stuff */
result = platform_driver_register(&platform_driver);
if (result)
@@ -435,6 +510,8 @@ fail_platform_device2:
fail_platform_device1:
platform_driver_unregister(&platform_driver);
fail_platform_driver:
+ eeepc_backlight_exit();
+fail_backlight:
return result;
}
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 087/171] eeepc-laptop: add hwmon fan control
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (14 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 086/171] eeepc-laptop: add backlight Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 088/171] eeepc-laptop: MAINTAINERS entry Len Brown
` (26 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Corentin Chary, Len Brown
From: Corentin Chary <corentincj@iksaif.net>
Adds an hwmon interface to control the fan.
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/Kconfig | 1 +
drivers/misc/eeepc-laptop.c | 147 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+), 0 deletions(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index e9ab291..7547523 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -332,6 +332,7 @@ config EEEPC_LAPTOP
depends on X86
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ depends on HWMON
depends on EXPERIMENTAL
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c
index 2b59b7e..6d72760 100644
--- a/drivers/misc/eeepc-laptop.c
+++ b/drivers/misc/eeepc-laptop.c
@@ -23,6 +23,8 @@
#include <linux/platform_device.h>
#include <linux/backlight.h>
#include <linux/fb.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
@@ -103,6 +105,15 @@ const char *cm_setv[] = {
"CRDS", NULL
};
+#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
+
+#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
+#define EEEPC_EC_SC02 0x63
+#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
+#define EEEPC_EC_SFB3 0xD3
+
/*
* This is the main structure, we can use it to store useful information
* about the hotk device
@@ -154,6 +165,9 @@ static struct acpi_driver eeepc_hotk_driver = {
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;
+/* The hwmon device */
+static struct device *eeepc_hwmon_device;
+
/*
* The backlight class declaration
*/
@@ -429,6 +443,100 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type)
}
/*
+ * Hwmon
+ */
+static int eeepc_get_fan_pwm(void)
+{
+ int value = 0;
+
+ read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
+ return (value);
+}
+
+static void eeepc_set_fan_pwm(int value)
+{
+ value = SENSORS_LIMIT(value, 0, 100);
+ ec_write(EEEPC_EC_SC02, value);
+}
+
+static int eeepc_get_fan_rpm(void)
+{
+ int high = 0;
+ int low = 0;
+
+ read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
+ read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
+ return (high << 8 | low);
+}
+
+static int eeepc_get_fan_ctrl(void)
+{
+ int value = 0;
+
+ read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
+ return ((value & 0x02 ? 1 : 0));
+}
+
+static void eeepc_set_fan_ctrl(int manual)
+{
+ int value = 0;
+
+ read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
+ if (manual)
+ value |= 0x02;
+ else
+ value &= ~0x02;
+ ec_write(EEEPC_EC_SFB3, value);
+}
+
+static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
+{
+ int rv, value;
+
+ rv = parse_arg(buf, count, &value);
+ if (rv > 0)
+ set(value);
+ return rv;
+}
+
+static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
+{
+ return sprintf(buf, "%d\n", get());
+}
+
+#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
+ static ssize_t show_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return show_sys_hwmon(_set, buf); \
+ } \
+ static ssize_t store_##_name(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return store_sys_hwmon(_get, buf, count); \
+ } \
+ static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
+
+EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
+EEEPC_CREATE_SENSOR_ATTR(fan1_pwm, S_IRUGO | S_IWUSR,
+ eeepc_get_fan_pwm, eeepc_set_fan_pwm);
+EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+ eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
+
+static struct attribute *hwmon_attributes[] = {
+ &sensor_dev_attr_fan1_pwm.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group hwmon_attribute_group = {
+ .attrs = hwmon_attributes
+};
+
+/*
* exit/init
*/
static void eeepc_backlight_exit(void)
@@ -438,9 +546,23 @@ static void eeepc_backlight_exit(void)
eeepc_backlight_device = NULL;
}
+static void eeepc_hwmon_exit(void)
+{
+ struct device *hwmon;
+
+ hwmon = eeepc_hwmon_device;
+ if (!hwmon)
+ return ;
+ hwmon_device_unregister(hwmon);
+ sysfs_remove_group(&hwmon->kobj,
+ &hwmon_attribute_group);
+ eeepc_hwmon_device = NULL;
+}
+
static void __exit eeepc_laptop_exit(void)
{
eeepc_backlight_exit();
+ eeepc_hwmon_exit();
acpi_bus_unregister_driver(&eeepc_hotk_driver);
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
@@ -468,6 +590,26 @@ static int eeepc_backlight_init(struct device *dev)
return 0;
}
+static int eeepc_hwmon_init(struct device *dev)
+{
+ struct device *hwmon;
+ int result;
+
+ hwmon = hwmon_device_register(dev);
+ if (IS_ERR(hwmon)) {
+ printk(EEEPC_ERR
+ "Could not register eeepc hwmon device\n");
+ eeepc_hwmon_device = NULL;
+ return PTR_ERR(hwmon);
+ }
+ eeepc_hwmon_device = hwmon;
+ result = sysfs_create_group(&hwmon->kobj,
+ &hwmon_attribute_group);
+ if (result)
+ eeepc_hwmon_exit();
+ return result;
+}
+
static int __init eeepc_laptop_init(void)
{
struct device *dev;
@@ -486,6 +628,9 @@ static int __init eeepc_laptop_init(void)
result = eeepc_backlight_init(dev);
if (result)
goto fail_backlight;
+ result = eeepc_hwmon_init(dev);
+ if (result)
+ goto fail_hwmon;
/* Register platform stuff */
result = platform_driver_register(&platform_driver);
if (result)
@@ -510,6 +655,8 @@ fail_platform_device2:
fail_platform_device1:
platform_driver_unregister(&platform_driver);
fail_platform_driver:
+ eeepc_hwmon_exit();
+fail_hwmon:
eeepc_backlight_exit();
fail_backlight:
return result;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 088/171] eeepc-laptop: MAINTAINERS entry
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (15 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 087/171] eeepc-laptop: add hwmon fan control Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 089/171] ACPI: re-name acpi_pm_ops to acpi_suspend_ops Len Brown
` (25 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Corentin CHARY, Len Brown
From: Corentin CHARY <corentincj@iksaif.net>
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
MAINTAINERS | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index e467758..3d9886d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1523,6 +1523,13 @@ L: bluesmoke-devel@lists.sourceforge.net
W: bluesmoke.sourceforge.net
S: Maintained
+EEEPC LAPTOP EXTRAS DRIVER
+P: Corentin Chary
+M: corentincj@iksaif.net
+L: acpi4asus-user@lists.sourceforge.net
+W: http://sourceforge.net/projects/acpi4asus
+S: Maintained
+
EEPRO100 NETWORK DRIVER
P: Andrey V. Savochkin
M: saw@saw.sw.com.sg
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 089/171] ACPI: re-name acpi_pm_ops to acpi_suspend_ops
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (16 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 088/171] eeepc-laptop: MAINTAINERS entry Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 091/171] ACPI: GPE enabling should happen after EC installation Len Brown
` (24 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Len Brown
From: Len Brown <len.brown@intel.com>
... as they are platform_suspend_ops after all.
cosmetic re-name only, no functional change.
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/sleep/main.c | 42 +++++++++++++++++++++---------------------
1 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 71183ee..c3b0cd8 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -51,7 +51,7 @@ static int acpi_sleep_prepare(u32 acpi_state)
}
#ifdef CONFIG_SUSPEND
-static struct platform_suspend_ops acpi_pm_ops;
+static struct platform_suspend_ops acpi_suspend_ops;
extern void do_suspend_lowlevel(void);
@@ -65,11 +65,11 @@ static u32 acpi_suspend_states[] = {
static int init_8259A_after_S1;
/**
- * acpi_pm_begin - Set the target system sleep state to the state
+ * acpi_suspend_begin - Set the target system sleep state to the state
* associated with given @pm_state, if supported.
*/
-static int acpi_pm_begin(suspend_state_t pm_state)
+static int acpi_suspend_begin(suspend_state_t pm_state)
{
u32 acpi_state = acpi_suspend_states[pm_state];
int error = 0;
@@ -85,13 +85,13 @@ static int acpi_pm_begin(suspend_state_t pm_state)
}
/**
- * acpi_pm_prepare - Do preliminary suspend work.
+ * acpi_suspend_prepare - Do preliminary suspend work.
*
* If necessary, set the firmware waking vector and do arch-specific
* nastiness to get the wakeup code to the waking vector.
*/
-static int acpi_pm_prepare(void)
+static int acpi_suspend_prepare(void)
{
int error = acpi_sleep_prepare(acpi_target_sleep_state);
@@ -104,7 +104,7 @@ static int acpi_pm_prepare(void)
}
/**
- * acpi_pm_enter - Actually enter a sleep state.
+ * acpi_suspend_enter - Actually enter a sleep state.
* @pm_state: ignored
*
* Flush caches and go to sleep. For STR we have to call arch-specific
@@ -112,7 +112,7 @@ static int acpi_pm_prepare(void)
* It's unfortunate, but it works. Please fix if you're feeling frisky.
*/
-static int acpi_pm_enter(suspend_state_t pm_state)
+static int acpi_suspend_enter(suspend_state_t pm_state)
{
acpi_status status = AE_OK;
unsigned long flags = 0;
@@ -169,13 +169,13 @@ static int acpi_pm_enter(suspend_state_t pm_state)
}
/**
- * acpi_pm_finish - Instruct the platform to leave a sleep state.
+ * acpi_suspend_finish - Instruct the platform to leave a sleep state.
*
* This is called after we wake back up (or if entering the sleep state
* failed).
*/
-static void acpi_pm_finish(void)
+static void acpi_suspend_finish(void)
{
u32 acpi_state = acpi_target_sleep_state;
@@ -196,19 +196,19 @@ static void acpi_pm_finish(void)
}
/**
- * acpi_pm_end - Finish up suspend sequence.
+ * acpi_suspend_end - Finish up suspend sequence.
*/
-static void acpi_pm_end(void)
+static void acpi_suspend_end(void)
{
/*
- * This is necessary in case acpi_pm_finish() is not called during a
+ * This is necessary in case acpi_suspend_finish() is not called during a
* failing transition to a sleep state.
*/
acpi_target_sleep_state = ACPI_STATE_S0;
}
-static int acpi_pm_state_valid(suspend_state_t pm_state)
+static int acpi_suspend_state_valid(suspend_state_t pm_state)
{
u32 acpi_state;
@@ -224,13 +224,13 @@ static int acpi_pm_state_valid(suspend_state_t pm_state)
}
}
-static struct platform_suspend_ops acpi_pm_ops = {
- .valid = acpi_pm_state_valid,
- .begin = acpi_pm_begin,
- .prepare = acpi_pm_prepare,
- .enter = acpi_pm_enter,
- .finish = acpi_pm_finish,
- .end = acpi_pm_end,
+static struct platform_suspend_ops acpi_suspend_ops = {
+ .valid = acpi_suspend_state_valid,
+ .begin = acpi_suspend_begin,
+ .prepare = acpi_suspend_prepare,
+ .enter = acpi_suspend_enter,
+ .finish = acpi_suspend_finish,
+ .end = acpi_suspend_end,
};
/*
@@ -492,7 +492,7 @@ int __init acpi_sleep_init(void)
}
}
- suspend_set_ops(&acpi_pm_ops);
+ suspend_set_ops(&acpi_suspend_ops);
#endif
#ifdef CONFIG_HIBERNATION
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (17 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 089/171] ACPI: re-name acpi_pm_ops to acpi_suspend_ops Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-30 10:25 ` Zhao Yakui
2008-04-29 8:44 ` [PATCH 092/171] 2.6.25 regression: powertop says 120K wakeups/sec Len Brown
` (23 subsequent siblings)
42 siblings, 1 reply; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Alexey Starikovskiy, Len Brown
From: Alexey Starikovskiy <astarikovskiy@suse.de>
GPE could try to access EC region, so should not be enabled before
EC is installed
http://bugzilla.kernel.org/show_bug.cgi?id=9916
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/utilities/utxface.c | 35 +++++++++++++++++------------------
1 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c
index 2d49691..df41312 100644
--- a/drivers/acpi/utilities/utxface.c
+++ b/drivers/acpi/utilities/utxface.c
@@ -192,24 +192,6 @@ acpi_status acpi_enable_subsystem(u32 flags)
}
}
- /*
- * Complete the GPE initialization for the GPE blocks defined in the FADT
- * (GPE block 0 and 1).
- *
- * Note1: This is where the _PRW methods are executed for the GPEs. These
- * methods can only be executed after the SCI and Global Lock handlers are
- * installed and initialized.
- *
- * Note2: Currently, there seems to be no need to run the _REG methods
- * before execution of the _PRW methods and enabling of the GPEs.
- */
- if (!(flags & ACPI_NO_EVENT_INIT)) {
- status = acpi_ev_install_fadt_gpes();
- if (ACPI_FAILURE(status)) {
- return (status);
- }
- }
-
return_ACPI_STATUS(status);
}
@@ -280,6 +262,23 @@ acpi_status acpi_initialize_objects(u32 flags)
}
/*
+ * Complete the GPE initialization for the GPE blocks defined in the FADT
+ * (GPE block 0 and 1).
+ *
+ * Note1: This is where the _PRW methods are executed for the GPEs. These
+ * methods can only be executed after the SCI and Global Lock handlers are
+ * installed and initialized.
+ *
+ * Note2: Currently, there seems to be no need to run the _REG methods
+ * before execution of the _PRW methods and enabling of the GPEs.
+ */
+ if (!(flags & ACPI_NO_EVENT_INIT)) {
+ status = acpi_ev_install_fadt_gpes();
+ if (ACPI_FAILURE(status))
+ return (status);
+ }
+
+ /*
* Empty the caches (delete the cached objects) on the assumption that
* the table load filled them up more than they will be at runtime --
* thus wasting non-paged memory.
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 092/171] 2.6.25 regression: powertop says 120K wakeups/sec
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (18 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 091/171] ACPI: GPE enabling should happen after EC installation Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 093/171] flush kacpi_notify_wq before removing notify handler Len Brown
` (22 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Venkatesh Pallipadi, Len Brown
From: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Patch to fix huge number of wakeups reported due to recent changes in
processor_idle.c. The problem was that the entry_method determination was
broken due to one of the recent commits (bc71bec91f987) causing
C1 entry to not to go to halt.
http://lkml.org/lkml/2008/3/22/124
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/processor_idle.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 788da97..836362b 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -848,6 +848,7 @@ static int acpi_processor_get_power_info_default(struct acpi_processor *pr)
/* all processors need to support C1 */
pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1;
pr->power.states[ACPI_STATE_C1].valid = 1;
+ pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT;
}
/* the C0 state only exists as a filler in our array */
pr->power.states[ACPI_STATE_C0].valid = 1;
@@ -960,6 +961,9 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
cx.address);
}
+ if (cx.type == ACPI_STATE_C1) {
+ cx.valid = 1;
+ }
obj = &(element->package.elements[2]);
if (obj->type != ACPI_TYPE_INTEGER)
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 093/171] flush kacpi_notify_wq before removing notify handler
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (19 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 092/171] 2.6.25 regression: powertop says 120K wakeups/sec Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 094/171] thermal: add the support for building the generic thermal as a module Len Brown
` (21 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang Rui, Len Brown
From: Zhang Rui <rui.zhang@intel.com>
Flush kacpi_notify_wq before notify handler is removed,
this can fix a bug which the deferred notify handler is executed
after the notify_handler has already been removed.
http://bugzilla.kernel.org/show_bug.cgi?id=9772
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/osl.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index a498a6c..235a138 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -742,6 +742,7 @@ EXPORT_SYMBOL(acpi_os_execute);
void acpi_os_wait_events_complete(void *context)
{
flush_workqueue(kacpid_wq);
+ flush_workqueue(kacpi_notify_wq);
}
EXPORT_SYMBOL(acpi_os_wait_events_complete);
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 094/171] thermal: add the support for building the generic thermal as a module
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (20 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 093/171] flush kacpi_notify_wq before removing notify handler Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 095/171] thermal: add new get_crit_temp callback Len Brown
` (20 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang Rui, Len Brown
From: Zhang Rui <rui.zhang@intel.com>
Build the generic thermal driver as module "thermal_sys".
Make ACPI thermal, video, processor and fan SELECT the generic
thermal driver, as these drivers rely on it to build the sysfs I/F.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/Kconfig | 3 +++
drivers/misc/Kconfig | 1 +
drivers/thermal/Kconfig | 4 ++--
drivers/thermal/Makefile | 3 ++-
drivers/thermal/thermal.c | 2 +-
include/linux/thermal.h | 14 --------------
6 files changed, 9 insertions(+), 18 deletions(-)
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index b4f5e85..c52fca8 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -140,6 +140,7 @@ config ACPI_VIDEO
tristate "Video"
depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL
depends on INPUT
+ select THERMAL
help
This driver implement the ACPI Extensions For Display Adapters
for integrated graphics devices on motherboard, as specified in
@@ -151,6 +152,7 @@ config ACPI_VIDEO
config ACPI_FAN
tristate "Fan"
+ select THERMAL
default y
help
This driver adds support for ACPI fan devices, allowing user-mode
@@ -172,6 +174,7 @@ config ACPI_BAY
config ACPI_PROCESSOR
tristate "Processor"
+ select THERMAL
default y
help
This driver installs ACPI as the idle handler for Linux, and uses
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 297a48f..08f35d7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -344,6 +344,7 @@ config ATMEL_SSC
config INTEL_MENLOW
tristate "Thermal Management driver for Intel menlow platform"
depends on ACPI_THERMAL
+ select THERMAL
depends on X86
---help---
ACPI thermal management enhancement driver on
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 17e71d5..4b62852 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig THERMAL
- bool "Generic Thermal sysfs driver"
+ tristate "Generic Thermal sysfs driver"
help
Generic Thermal Sysfs driver offers a generic mechanism for
thermal management. Usually it's made up of one or more thermal
@@ -11,4 +11,4 @@ menuconfig THERMAL
Each thermal zone contains its own temperature, trip points,
cooling devices.
All platforms with ACPI thermal support can use this driver.
- If you want this support, you should say Y here.
+ If you want this support, you should say Y or M here.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 8ef1232..02b6451 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -2,4 +2,5 @@
# Makefile for sensor chip drivers.
#
-obj-$(CONFIG_THERMAL) += thermal.o
+thermal_sys-objs += thermal.o
+obj-$(CONFIG_THERMAL) += thermal_sys.o
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c
index 7f79bbf..cf56af4 100644
--- a/drivers/thermal/thermal.c
+++ b/drivers/thermal/thermal.c
@@ -31,7 +31,7 @@
#include <linux/thermal.h>
#include <linux/spinlock.h>
-MODULE_AUTHOR("Zhang Rui")
+MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Generic thermal management sysfs support");
MODULE_LICENSE("GPL");
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 90c1c19..3ff680b 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -88,24 +88,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
-
-#ifdef CONFIG_THERMAL
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
struct
thermal_cooling_device_ops
*);
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
-#else
-static inline struct thermal_cooling_device
-*thermal_cooling_device_register(char *c, void *v,
- struct thermal_cooling_device_ops *t)
-{
- return NULL;
-}
-static inline
- void thermal_cooling_device_unregister(struct thermal_cooling_device *t)
-{
-};
-#endif
#endif /* __THERMAL_H__ */
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 095/171] thermal: add new get_crit_temp callback
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (21 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 094/171] thermal: add the support for building the generic thermal as a module Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 096/171] thermal: add hwmon sysfs I/F Len Brown
` (19 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang, Rui, Len Brown
From: Zhang, Rui <rui.zhang@intel.com>
Add a new callback so that the generic thermal can get
the critical trip point info of a thermal zone,
which is needed for building the tempX_crit hwmon sysfs attribute.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/thermal.c | 13 +++++++++++++
include/linux/thermal.h | 1 +
2 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 766bd25..ec707ed 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -1012,6 +1012,18 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
return -EINVAL;
}
+static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temperature) {
+ struct acpi_thermal *tz = thermal->devdata;
+
+ if (tz->trips.critical.flags.valid) {
+ *temperature = KELVIN_TO_MILLICELSIUS(
+ tz->trips.critical.temperature);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
typedef int (*cb)(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
@@ -1103,6 +1115,7 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
.set_mode = thermal_set_mode,
.get_trip_type = thermal_get_trip_type,
.get_trip_temp = thermal_get_trip_temp,
+ .get_crit_temp = thermal_get_crit_temp,
};
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 3ff680b..16e6a8b 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -41,6 +41,7 @@ struct thermal_zone_device_ops {
int (*set_mode) (struct thermal_zone_device *, const char *);
int (*get_trip_type) (struct thermal_zone_device *, int, char *);
int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
+ int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
};
struct thermal_cooling_device_ops {
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 096/171] thermal: add hwmon sysfs I/F
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (22 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 095/171] thermal: add new get_crit_temp callback Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 097/171] thermal: update the documentation Len Brown
` (18 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang Rui, Len Brown
From: Zhang Rui <rui.zhang@intel.com>
Add hwmon sys I/F for generic thermal driver.
Note: we have one hwmon class device for EACH TYPE of the thermal zone device.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/thermal/thermal.c | 163 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 24 +++++++
2 files changed, 187 insertions(+), 0 deletions(-)
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c
index cf56af4..6098787 100644
--- a/drivers/thermal/thermal.c
+++ b/drivers/thermal/thermal.c
@@ -295,6 +295,164 @@ thermal_cooling_device_trip_point_show(struct device *dev,
/* Device management */
+#if defined(CONFIG_HWMON) || \
+ (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE))
+/* hwmon sys I/F */
+#include <linux/hwmon.h>
+static LIST_HEAD(thermal_hwmon_list);
+
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_hwmon_device *hwmon = dev->driver_data;
+ return sprintf(buf, "%s\n", hwmon->type);
+}
+static DEVICE_ATTR(name, 0444, name_show, NULL);
+
+static ssize_t
+temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_zone_device *tz
+ = container_of(hwmon_attr, struct thermal_zone_device,
+ temp_input);
+
+ return tz->ops->get_temp(tz, buf);
+}
+
+static ssize_t
+temp_crit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_zone_device *tz
+ = container_of(hwmon_attr, struct thermal_zone_device,
+ temp_crit);
+
+ return tz->ops->get_trip_temp(tz, 0, buf);
+}
+
+
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon;
+ int new_hwmon_device = 1;
+ int result;
+
+ mutex_lock(&thermal_list_lock);
+ list_for_each_entry(hwmon, &thermal_hwmon_list, node)
+ if (!strcmp(hwmon->type, tz->type)) {
+ new_hwmon_device = 0;
+ mutex_unlock(&thermal_list_lock);
+ goto register_sys_interface;
+ }
+ mutex_unlock(&thermal_list_lock);
+
+ hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&hwmon->tz_list);
+ strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
+ hwmon->device = hwmon_device_register(NULL);
+ if (IS_ERR(hwmon->device)) {
+ result = PTR_ERR(hwmon->device);
+ goto free_mem;
+ }
+ hwmon->device->driver_data = hwmon;
+ result = device_create_file(hwmon->device, &dev_attr_name);
+ if (result)
+ goto unregister_hwmon_device;
+
+ register_sys_interface:
+ tz->hwmon = hwmon;
+ hwmon->count++;
+
+ snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
+ "temp%d_input", hwmon->count);
+ tz->temp_input.attr.attr.name = tz->temp_input.name;
+ tz->temp_input.attr.attr.mode = 0444;
+ tz->temp_input.attr.show = temp_input_show;
+ result = device_create_file(hwmon->device, &tz->temp_input.attr);
+ if (result)
+ goto unregister_hwmon_device;
+
+ if (tz->ops->get_crit_temp) {
+ unsigned long temperature;
+ if (!tz->ops->get_crit_temp(tz, &temperature)) {
+ snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
+ "temp%d_crit", hwmon->count);
+ tz->temp_crit.attr.attr.name = tz->temp_crit.name;
+ tz->temp_crit.attr.attr.mode = 0444;
+ tz->temp_crit.attr.show = temp_crit_show;
+ result = device_create_file(hwmon->device,
+ &tz->temp_crit.attr);
+ if (result)
+ goto unregister_hwmon_device;
+ }
+ }
+
+ mutex_lock(&thermal_list_lock);
+ if (new_hwmon_device)
+ list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
+ mutex_unlock(&thermal_list_lock);
+
+ return 0;
+
+ unregister_hwmon_device:
+ device_remove_file(hwmon->device, &tz->temp_crit.attr);
+ device_remove_file(hwmon->device, &tz->temp_input.attr);
+ if (new_hwmon_device) {
+ device_remove_file(hwmon->device, &dev_attr_name);
+ hwmon_device_unregister(hwmon->device);
+ }
+ free_mem:
+ if (new_hwmon_device)
+ kfree(hwmon);
+
+ return result;
+}
+
+static void
+thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon = tz->hwmon;
+
+ tz->hwmon = NULL;
+ device_remove_file(hwmon->device, &tz->temp_input.attr);
+ device_remove_file(hwmon->device, &tz->temp_crit.attr);
+
+ mutex_lock(&thermal_list_lock);
+ list_del(&tz->hwmon_node);
+ if (!list_empty(&hwmon->tz_list)) {
+ mutex_unlock(&thermal_list_lock);
+ return;
+ }
+ list_del(&hwmon->node);
+ mutex_unlock(&thermal_list_lock);
+
+ device_remove_file(hwmon->device, &dev_attr_name);
+ hwmon_device_unregister(hwmon->device);
+ kfree(hwmon);
+}
+#else
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ return 0;
+}
+
+static void
+thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+}
+#endif
+
+
/**
* thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
* @tz: thermal zone device
@@ -642,6 +800,10 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
goto unregister;
}
+ result = thermal_add_hwmon_sysfs(tz);
+ if (result)
+ goto unregister;
+
mutex_lock(&thermal_list_lock);
list_add_tail(&tz->node, &thermal_tz_list);
if (ops->bind)
@@ -700,6 +862,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
for (count = 0; count < tz->trips; count++)
TRIP_POINT_ATTR_REMOVE(&tz->device, count);
+ thermal_remove_hwmon_sysfs(tz);
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
idr_destroy(&tz->idr);
mutex_destroy(&tz->lock);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 16e6a8b..06d3e6e 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -66,6 +66,23 @@ struct thermal_cooling_device {
((long)t-2732+5)/10 : ((long)t-2732-5)/10)
#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
+#if defined(CONFIG_HWMON) || \
+ (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE))
+/* thermal zone devices with the same type share one hwmon device */
+struct thermal_hwmon_device {
+ char type[THERMAL_NAME_LENGTH];
+ struct device *device;
+ int count;
+ struct list_head tz_list;
+ struct list_head node;
+};
+
+struct thermal_hwmon_attr {
+ struct device_attribute attr;
+ char name[16];
+};
+#endif
+
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -77,6 +94,13 @@ struct thermal_zone_device {
struct idr idr;
struct mutex lock; /* protect cooling devices list */
struct list_head node;
+#if defined(CONFIG_HWMON) || \
+ (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE))
+ struct list_head hwmon_node;
+ struct thermal_hwmon_device *hwmon;
+ struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
+ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
+#endif
};
struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 097/171] thermal: update the documentation
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (23 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 096/171] thermal: add hwmon sysfs I/F Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 098/171] ACPI: elide a non-zero test on a result that is never 0 Len Brown
` (17 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang Rui, Len Brown
From: Zhang Rui <rui.zhang@intel.com>
Update the documentation for the thermal driver hwmon sys I/F.
Change the ACPI thermal zone type to be consistent with hwmon.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/thermal/sysfs-api.txt | 33 +++++++++++++++++++++++++++------
drivers/acpi/thermal.c | 2 +-
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
index d9f28be..70d68ce 100644
--- a/Documentation/thermal/sysfs-api.txt
+++ b/Documentation/thermal/sysfs-api.txt
@@ -108,10 +108,12 @@ and throttle appropriate devices.
RO read only value
RW read/write value
-All thermal sysfs attributes will be represented under /sys/class/thermal
+Thermal sysfs attributes will be represented under /sys/class/thermal.
+Hwmon sysfs I/F extension is also available under /sys/class/hwmon
+if hwmon is compiled in or built as a module.
Thermal zone device sys I/F, created once it's registered:
-|thermal_zone[0-*]:
+/sys/class/thermal/thermal_zone[0-*]:
|-----type: Type of the thermal zone
|-----temp: Current temperature
|-----mode: Working mode of the thermal zone
@@ -119,7 +121,7 @@ Thermal zone device sys I/F, created once it's registered:
|-----trip_point_[0-*]_type: Trip point type
Thermal cooling device sys I/F, created once it's registered:
-|cooling_device[0-*]:
+/sys/class/thermal/cooling_device[0-*]:
|-----type : Type of the cooling device(processor/fan/...)
|-----max_state: Maximum cooling state of the cooling device
|-----cur_state: Current cooling state of the cooling device
@@ -130,10 +132,19 @@ They represent the relationship between a thermal zone and its associated coolin
They are created/removed for each
thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful execution.
-|thermal_zone[0-*]
+/sys/class/thermal/thermal_zone[0-*]
|-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone
|-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with
+Besides the thermal zone device sysfs I/F and cooling device sysfs I/F,
+the generic thermal driver also creates a hwmon sysfs I/F for each _type_ of
+thermal zone device. E.g. the generic thermal driver registers one hwmon class device
+and build the associated hwmon sysfs I/F for all the registered ACPI thermal zones.
+/sys/class/hwmon/hwmon[0-*]:
+ |-----name: The type of the thermal zone devices.
+ |-----temp[1-*]_input: The current temperature of thermal zone [1-*].
+ |-----temp[1-*]_critical: The critical trip point of thermal zone [1-*].
+Please read Documentation/hwmon/sysfs-interface for additional information.
***************************
* Thermal zone attributes *
@@ -141,7 +152,10 @@ thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful e
type Strings which represent the thermal zone type.
This is given by thermal zone driver as part of registration.
- Eg: "ACPI thermal zone" indicates it's a ACPI thermal device
+ Eg: "acpitz" indicates it's an ACPI thermal device.
+ In order to keep it consistent with hwmon sys attribute,
+ this should be a short, lowercase string,
+ not containing spaces nor dashes.
RO
Required
@@ -218,7 +232,7 @@ the sys I/F structure will be built like this:
/sys/class/thermal:
|thermal_zone1:
- |-----type: ACPI thermal zone
+ |-----type: acpitz
|-----temp: 37000
|-----mode: kernel
|-----trip_point_0_temp: 100000
@@ -243,3 +257,10 @@ the sys I/F structure will be built like this:
|-----type: Fan
|-----max_state: 2
|-----cur_state: 0
+
+/sys/class/hwmon:
+
+|hwmon0:
+ |-----name: acpitz
+ |-----temp1_input: 37000
+ |-----temp1_crit: 100000
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index ec707ed..5e9641c 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -1136,7 +1136,7 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
tz->trips.active[i].flags.valid; i++, trips++);
- tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
+ tz->thermal_zone = thermal_zone_device_register("acpitz",
trips, tz, &acpi_thermal_zone_ops);
if (IS_ERR(tz->thermal_zone))
return -ENODEV;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 098/171] ACPI: elide a non-zero test on a result that is never 0
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (24 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 097/171] thermal: update the documentation Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 099/171] ACPI: update thermal temperature Len Brown
` (16 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Julia Lawall, Zhang Rui, Len Brown
From: Julia Lawall <julia@diku.dk>
thermal_cooling_device_register used to return NULL if THERMAL is "n".
As the ACPI fan, processor and video drivers SELECT the generic
thermal in PATCH 01, this is not a problem any more.
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/fan.c | 35 +++++++++++++++++------------------
drivers/acpi/processor_core.c | 31 +++++++++++++++----------------
drivers/acpi/video.c | 28 +++++++++++++---------------
drivers/misc/intel_menlow.c | 23 +++++++++--------------
4 files changed, 54 insertions(+), 63 deletions(-)
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index c8e3cba..cf635cd 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -260,24 +260,23 @@ static int acpi_fan_add(struct acpi_device *device)
result = PTR_ERR(cdev);
goto end;
}
- if (cdev) {
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev.bus_id, cdev->id);
-
- acpi_driver_data(device) = cdev;
- result = sysfs_create_link(&device->dev.kobj,
- &cdev->device.kobj,
- "thermal_cooling");
- if (result)
- return result;
-
- result = sysfs_create_link(&cdev->device.kobj,
- &device->dev.kobj,
- "device");
- if (result)
- return result;
- }
+
+ printk(KERN_INFO PREFIX
+ "%s is registered as cooling_device%d\n",
+ device->dev.bus_id, cdev->id);
+
+ acpi_driver_data(device) = cdev;
+ result = sysfs_create_link(&device->dev.kobj,
+ &cdev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+
+ result = sysfs_create_link(&cdev->device.kobj,
+ &device->dev.kobj,
+ "device");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
result = acpi_fan_add_fs(device);
if (result)
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index a825b43..ea5f628 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -674,22 +674,21 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
result = PTR_ERR(pr->cdev);
goto end;
}
- if (pr->cdev) {
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev.bus_id, pr->cdev->id);
-
- result = sysfs_create_link(&device->dev.kobj,
- &pr->cdev->device.kobj,
- "thermal_cooling");
- if (result)
- return result;
- result = sysfs_create_link(&pr->cdev->device.kobj,
- &device->dev.kobj,
- "device");
- if (result)
- return result;
- }
+
+ printk(KERN_INFO PREFIX
+ "%s is registered as cooling_device%d\n",
+ device->dev.bus_id, pr->cdev->id);
+
+ result = sysfs_create_link(&device->dev.kobj,
+ &pr->cdev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+ result = sysfs_create_link(&pr->cdev->device.kobj,
+ &device->dev.kobj,
+ "device");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
if (pr->flags.throttling) {
printk(KERN_INFO PREFIX "%s [%s] (supports",
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 980a741..55c0956 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -734,21 +734,19 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
if (IS_ERR(device->cdev))
return;
- if (device->cdev) {
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev->dev.bus_id, device->cdev->id);
- result = sysfs_create_link(&device->dev->dev.kobj,
- &device->cdev->device.kobj,
- "thermal_cooling");
- if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
- result = sysfs_create_link(&device->cdev->device.kobj,
- &device->dev->dev.kobj,
- "device");
- if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
- }
+ printk(KERN_INFO PREFIX
+ "%s is registered as cooling_device%d\n",
+ device->dev->dev.bus_id, device->cdev->id);
+ result = sysfs_create_link(&device->dev->dev.kobj,
+ &device->cdev->device.kobj,
+ "thermal_cooling");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+ result = sysfs_create_link(&device->cdev->device.kobj,
+ &device->dev->dev.kobj, "device");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+
}
if (device->cap._DCS && device->cap._DSS){
static int count = 0;
diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c
index 0c0bb30..3db83a2 100644
--- a/drivers/misc/intel_menlow.c
+++ b/drivers/misc/intel_menlow.c
@@ -175,20 +175,15 @@ static int intel_menlow_memory_add(struct acpi_device *device)
goto end;
}
- if (cdev) {
- acpi_driver_data(device) = cdev;
- result = sysfs_create_link(&device->dev.kobj,
- &cdev->device.kobj, "thermal_cooling");
- if (result)
- goto unregister;
-
- result = sysfs_create_link(&cdev->device.kobj,
- &device->dev.kobj, "device");
- if (result) {
- sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
- goto unregister;
- }
- }
+ acpi_driver_data(device) = cdev;
+ result = sysfs_create_link(&device->dev.kobj,
+ &cdev->device.kobj, "thermal_cooling");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
+ result = sysfs_create_link(&cdev->device.kobj,
+ &device->dev.kobj, "device");
+ if (result)
+ printk(KERN_ERR PREFIX "Create sysfs link\n");
end:
return result;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 099/171] ACPI: update thermal temperature
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (25 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 098/171] ACPI: elide a non-zero test on a result that is never 0 Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 100/171] thermal: re-name thermal.c to thermal_sys.c Len Brown
` (15 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Zhang, Rui, Len Brown
From: Zhang, Rui <rui.zhang@intel.com>
Fix the problem that thermal_get_temp returns the cached value,
which causes the temperature in generic thermal never updates.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/thermal.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 5e9641c..782c225 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -884,10 +884,15 @@ static void acpi_thermal_check(void *data)
static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
{
struct acpi_thermal *tz = thermal->devdata;
+ int result;
if (!tz)
return -EINVAL;
+ result = acpi_thermal_get_temperature(tz);
+ if (result)
+ return result;
+
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(tz->temperature));
}
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 100/171] thermal: re-name thermal.c to thermal_sys.c
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (26 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 099/171] ACPI: update thermal temperature Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 156/171] ACPI: thinkpad-acpi: BIOS backlight mode helper (v2) Len Brown
` (14 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Len Brown
From: Len Brown <len.brown@intel.com>
thermal_sys was already the name of the resulting module,
and it is built from this one source file.
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/thermal/Makefile | 1 -
drivers/thermal/thermal.c | 899 -----------------------------------------
drivers/thermal/thermal_sys.c | 899 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 899 insertions(+), 900 deletions(-)
delete mode 100644 drivers/thermal/thermal.c
create mode 100644 drivers/thermal/thermal_sys.c
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 02b6451..31108a0 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -2,5 +2,4 @@
# Makefile for sensor chip drivers.
#
-thermal_sys-objs += thermal.o
obj-$(CONFIG_THERMAL) += thermal_sys.o
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c
deleted file mode 100644
index 6098787..0000000
--- a/drivers/thermal/thermal.c
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * thermal.c - Generic Thermal Management Sysfs support.
- *
- * Copyright (C) 2008 Intel Corp
- * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
- * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/kdev_t.h>
-#include <linux/idr.h>
-#include <linux/thermal.h>
-#include <linux/spinlock.h>
-
-MODULE_AUTHOR("Zhang Rui");
-MODULE_DESCRIPTION("Generic thermal management sysfs support");
-MODULE_LICENSE("GPL");
-
-#define PREFIX "Thermal: "
-
-struct thermal_cooling_device_instance {
- int id;
- char name[THERMAL_NAME_LENGTH];
- struct thermal_zone_device *tz;
- struct thermal_cooling_device *cdev;
- int trip;
- char attr_name[THERMAL_NAME_LENGTH];
- struct device_attribute attr;
- struct list_head node;
-};
-
-static DEFINE_IDR(thermal_tz_idr);
-static DEFINE_IDR(thermal_cdev_idr);
-static DEFINE_MUTEX(thermal_idr_lock);
-
-static LIST_HEAD(thermal_tz_list);
-static LIST_HEAD(thermal_cdev_list);
-static DEFINE_MUTEX(thermal_list_lock);
-
-static int get_idr(struct idr *idr, struct mutex *lock, int *id)
-{
- int err;
-
- again:
- if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
- return -ENOMEM;
-
- if (lock)
- mutex_lock(lock);
- err = idr_get_new(idr, NULL, id);
- if (lock)
- mutex_unlock(lock);
- if (unlikely(err == -EAGAIN))
- goto again;
- else if (unlikely(err))
- return err;
-
- *id = *id & MAX_ID_MASK;
- return 0;
-}
-
-static void release_idr(struct idr *idr, struct mutex *lock, int id)
-{
- if (lock)
- mutex_lock(lock);
- idr_remove(idr, id);
- if (lock)
- mutex_unlock(lock);
-}
-
-/* sys I/F for thermal zone */
-
-#define to_thermal_zone(_dev) \
- container_of(_dev, struct thermal_zone_device, device)
-
-static ssize_t
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
-
- return sprintf(buf, "%s\n", tz->type);
-}
-
-static ssize_t
-temp_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
-
- if (!tz->ops->get_temp)
- return -EPERM;
-
- return tz->ops->get_temp(tz, buf);
-}
-
-static ssize_t
-mode_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
-
- if (!tz->ops->get_mode)
- return -EPERM;
-
- return tz->ops->get_mode(tz, buf);
-}
-
-static ssize_t
-mode_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
- int result;
-
- if (!tz->ops->set_mode)
- return -EPERM;
-
- result = tz->ops->set_mode(tz, buf);
- if (result)
- return result;
-
- return count;
-}
-
-static ssize_t
-trip_point_type_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
- int trip;
-
- if (!tz->ops->get_trip_type)
- return -EPERM;
-
- if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
- return -EINVAL;
-
- return tz->ops->get_trip_type(tz, trip, buf);
-}
-
-static ssize_t
-trip_point_temp_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct thermal_zone_device *tz = to_thermal_zone(dev);
- int trip;
-
- if (!tz->ops->get_trip_temp)
- return -EPERM;
-
- if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
- return -EINVAL;
-
- return tz->ops->get_trip_temp(tz, trip, buf);
-}
-
-static DEVICE_ATTR(type, 0444, type_show, NULL);
-static DEVICE_ATTR(temp, 0444, temp_show, NULL);
-static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
-
-static struct device_attribute trip_point_attrs[] = {
- __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_10_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_10_temp, 0444, trip_point_temp_show, NULL),
- __ATTR(trip_point_11_type, 0444, trip_point_type_show, NULL),
- __ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
-};
-
-#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
-do { \
- result = device_create_file(_dev, \
- &trip_point_attrs[_index * 2]); \
- if (result) \
- break; \
- result = device_create_file(_dev, \
- &trip_point_attrs[_index * 2 + 1]); \
-} while (0)
-
-#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
-do { \
- device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
- device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
-} while (0)
-
-/* sys I/F for cooling device */
-#define to_cooling_device(_dev) \
- container_of(_dev, struct thermal_cooling_device, device)
-
-static ssize_t
-thermal_cooling_device_type_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
-
- return sprintf(buf, "%s\n", cdev->type);
-}
-
-static ssize_t
-thermal_cooling_device_max_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
-
- return cdev->ops->get_max_state(cdev, buf);
-}
-
-static ssize_t
-thermal_cooling_device_cur_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
-
- return cdev->ops->get_cur_state(cdev, buf);
-}
-
-static ssize_t
-thermal_cooling_device_cur_state_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
- int state;
- int result;
-
- if (!sscanf(buf, "%d\n", &state))
- return -EINVAL;
-
- if (state < 0)
- return -EINVAL;
-
- result = cdev->ops->set_cur_state(cdev, state);
- if (result)
- return result;
- return count;
-}
-
-static struct device_attribute dev_attr_cdev_type =
-__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
-static DEVICE_ATTR(max_state, 0444,
- thermal_cooling_device_max_state_show, NULL);
-static DEVICE_ATTR(cur_state, 0644,
- thermal_cooling_device_cur_state_show,
- thermal_cooling_device_cur_state_store);
-
-static ssize_t
-thermal_cooling_device_trip_point_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct thermal_cooling_device_instance *instance;
-
- instance =
- container_of(attr, struct thermal_cooling_device_instance, attr);
-
- if (instance->trip == THERMAL_TRIPS_NONE)
- return sprintf(buf, "-1\n");
- else
- return sprintf(buf, "%d\n", instance->trip);
-}
-
-/* Device management */
-
-#if defined(CONFIG_HWMON) || \
- (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE))
-/* hwmon sys I/F */
-#include <linux/hwmon.h>
-static LIST_HEAD(thermal_hwmon_list);
-
-static ssize_t
-name_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct thermal_hwmon_device *hwmon = dev->driver_data;
- return sprintf(buf, "%s\n", hwmon->type);
-}
-static DEVICE_ATTR(name, 0444, name_show, NULL);
-
-static ssize_t
-temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct thermal_hwmon_attr *hwmon_attr
- = container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_zone_device *tz
- = container_of(hwmon_attr, struct thermal_zone_device,
- temp_input);
-
- return tz->ops->get_temp(tz, buf);
-}
-
-static ssize_t
-temp_crit_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct thermal_hwmon_attr *hwmon_attr
- = container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_zone_device *tz
- = container_of(hwmon_attr, struct thermal_zone_device,
- temp_crit);
-
- return tz->ops->get_trip_temp(tz, 0, buf);
-}
-
-
-static int
-thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
-{
- struct thermal_hwmon_device *hwmon;
- int new_hwmon_device = 1;
- int result;
-
- mutex_lock(&thermal_list_lock);
- list_for_each_entry(hwmon, &thermal_hwmon_list, node)
- if (!strcmp(hwmon->type, tz->type)) {
- new_hwmon_device = 0;
- mutex_unlock(&thermal_list_lock);
- goto register_sys_interface;
- }
- mutex_unlock(&thermal_list_lock);
-
- hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
- if (!hwmon)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&hwmon->tz_list);
- strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
- hwmon->device = hwmon_device_register(NULL);
- if (IS_ERR(hwmon->device)) {
- result = PTR_ERR(hwmon->device);
- goto free_mem;
- }
- hwmon->device->driver_data = hwmon;
- result = device_create_file(hwmon->device, &dev_attr_name);
- if (result)
- goto unregister_hwmon_device;
-
- register_sys_interface:
- tz->hwmon = hwmon;
- hwmon->count++;
-
- snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
- "temp%d_input", hwmon->count);
- tz->temp_input.attr.attr.name = tz->temp_input.name;
- tz->temp_input.attr.attr.mode = 0444;
- tz->temp_input.attr.show = temp_input_show;
- result = device_create_file(hwmon->device, &tz->temp_input.attr);
- if (result)
- goto unregister_hwmon_device;
-
- if (tz->ops->get_crit_temp) {
- unsigned long temperature;
- if (!tz->ops->get_crit_temp(tz, &temperature)) {
- snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
- "temp%d_crit", hwmon->count);
- tz->temp_crit.attr.attr.name = tz->temp_crit.name;
- tz->temp_crit.attr.attr.mode = 0444;
- tz->temp_crit.attr.show = temp_crit_show;
- result = device_create_file(hwmon->device,
- &tz->temp_crit.attr);
- if (result)
- goto unregister_hwmon_device;
- }
- }
-
- mutex_lock(&thermal_list_lock);
- if (new_hwmon_device)
- list_add_tail(&hwmon->node, &thermal_hwmon_list);
- list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
- mutex_unlock(&thermal_list_lock);
-
- return 0;
-
- unregister_hwmon_device:
- device_remove_file(hwmon->device, &tz->temp_crit.attr);
- device_remove_file(hwmon->device, &tz->temp_input.attr);
- if (new_hwmon_device) {
- device_remove_file(hwmon->device, &dev_attr_name);
- hwmon_device_unregister(hwmon->device);
- }
- free_mem:
- if (new_hwmon_device)
- kfree(hwmon);
-
- return result;
-}
-
-static void
-thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
-{
- struct thermal_hwmon_device *hwmon = tz->hwmon;
-
- tz->hwmon = NULL;
- device_remove_file(hwmon->device, &tz->temp_input.attr);
- device_remove_file(hwmon->device, &tz->temp_crit.attr);
-
- mutex_lock(&thermal_list_lock);
- list_del(&tz->hwmon_node);
- if (!list_empty(&hwmon->tz_list)) {
- mutex_unlock(&thermal_list_lock);
- return;
- }
- list_del(&hwmon->node);
- mutex_unlock(&thermal_list_lock);
-
- device_remove_file(hwmon->device, &dev_attr_name);
- hwmon_device_unregister(hwmon->device);
- kfree(hwmon);
-}
-#else
-static int
-thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
-{
- return 0;
-}
-
-static void
-thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
-{
-}
-#endif
-
-
-/**
- * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
- * @tz: thermal zone device
- * @trip: indicates which trip point the cooling devices is
- * associated with in this thermal zone.
- * @cdev: thermal cooling device
- *
- * This function is usually called in the thermal zone device .bind callback.
- */
-int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
- int trip,
- struct thermal_cooling_device *cdev)
-{
- struct thermal_cooling_device_instance *dev;
- struct thermal_cooling_device_instance *pos;
- struct thermal_zone_device *pos1;
- struct thermal_cooling_device *pos2;
- int result;
-
- if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
- return -EINVAL;
-
- list_for_each_entry(pos1, &thermal_tz_list, node) {
- if (pos1 == tz)
- break;
- }
- list_for_each_entry(pos2, &thermal_cdev_list, node) {
- if (pos2 == cdev)
- break;
- }
-
- if (tz != pos1 || cdev != pos2)
- return -EINVAL;
-
- dev =
- kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- dev->tz = tz;
- dev->cdev = cdev;
- dev->trip = trip;
- result = get_idr(&tz->idr, &tz->lock, &dev->id);
- if (result)
- goto free_mem;
-
- sprintf(dev->name, "cdev%d", dev->id);
- result =
- sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
- if (result)
- goto release_idr;
-
- sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
- dev->attr.attr.name = dev->attr_name;
- dev->attr.attr.mode = 0444;
- dev->attr.show = thermal_cooling_device_trip_point_show;
- result = device_create_file(&tz->device, &dev->attr);
- if (result)
- goto remove_symbol_link;
-
- mutex_lock(&tz->lock);
- list_for_each_entry(pos, &tz->cooling_devices, node)
- if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
- result = -EEXIST;
- break;
- }
- if (!result)
- list_add_tail(&dev->node, &tz->cooling_devices);
- mutex_unlock(&tz->lock);
-
- if (!result)
- return 0;
-
- device_remove_file(&tz->device, &dev->attr);
- remove_symbol_link:
- sysfs_remove_link(&tz->device.kobj, dev->name);
- release_idr:
- release_idr(&tz->idr, &tz->lock, dev->id);
- free_mem:
- kfree(dev);
- return result;
-}
-
-EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
-
-/**
- * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
- * @tz: thermal zone device
- * @trip: indicates which trip point the cooling devices is
- * associated with in this thermal zone.
- * @cdev: thermal cooling device
- *
- * This function is usually called in the thermal zone device .unbind callback.
- */
-int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
- int trip,
- struct thermal_cooling_device *cdev)
-{
- struct thermal_cooling_device_instance *pos, *next;
-
- mutex_lock(&tz->lock);
- list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
- if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
- list_del(&pos->node);
- mutex_unlock(&tz->lock);
- goto unbind;
- }
- }
- mutex_unlock(&tz->lock);
-
- return -ENODEV;
-
- unbind:
- device_remove_file(&tz->device, &pos->attr);
- sysfs_remove_link(&tz->device.kobj, pos->name);
- release_idr(&tz->idr, &tz->lock, pos->id);
- kfree(pos);
- return 0;
-}
-
-EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
-
-static void thermal_release(struct device *dev)
-{
- struct thermal_zone_device *tz;
- struct thermal_cooling_device *cdev;
-
- if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {
- tz = to_thermal_zone(dev);
- kfree(tz);
- } else {
- cdev = to_cooling_device(dev);
- kfree(cdev);
- }
-}
-
-static struct class thermal_class = {
- .name = "thermal",
- .dev_release = thermal_release,
-};
-
-/**
- * thermal_cooling_device_register - register a new thermal cooling device
- * @type: the thermal cooling device type.
- * @devdata: device private data.
- * @ops: standard thermal cooling devices callbacks.
- */
-struct thermal_cooling_device *thermal_cooling_device_register(char *type,
- void *devdata,
- struct
- thermal_cooling_device_ops
- *ops)
-{
- struct thermal_cooling_device *cdev;
- struct thermal_zone_device *pos;
- int result;
-
- if (strlen(type) >= THERMAL_NAME_LENGTH)
- return ERR_PTR(-EINVAL);
-
- if (!ops || !ops->get_max_state || !ops->get_cur_state ||
- !ops->set_cur_state)
- return ERR_PTR(-EINVAL);
-
- cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
- if (!cdev)
- return ERR_PTR(-ENOMEM);
-
- result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
- if (result) {
- kfree(cdev);
- return ERR_PTR(result);
- }
-
- strcpy(cdev->type, type);
- cdev->ops = ops;
- cdev->device.class = &thermal_class;
- cdev->devdata = devdata;
- sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);
- result = device_register(&cdev->device);
- if (result) {
- release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
- kfree(cdev);
- return ERR_PTR(result);
- }
-
- /* sys I/F */
- if (type) {
- result = device_create_file(&cdev->device, &dev_attr_cdev_type);
- if (result)
- goto unregister;
- }
-
- result = device_create_file(&cdev->device, &dev_attr_max_state);
- if (result)
- goto unregister;
-
- result = device_create_file(&cdev->device, &dev_attr_cur_state);
- if (result)
- goto unregister;
-
- mutex_lock(&thermal_list_lock);
- list_add(&cdev->node, &thermal_cdev_list);
- list_for_each_entry(pos, &thermal_tz_list, node) {
- if (!pos->ops->bind)
- continue;
- result = pos->ops->bind(pos, cdev);
- if (result)
- break;
-
- }
- mutex_unlock(&thermal_list_lock);
-
- if (!result)
- return cdev;
-
- unregister:
- release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
- device_unregister(&cdev->device);
- return ERR_PTR(result);
-}
-
-EXPORT_SYMBOL(thermal_cooling_device_register);
-
-/**
- * thermal_cooling_device_unregister - removes the registered thermal cooling device
- * @cdev: the thermal cooling device to remove.
- *
- * thermal_cooling_device_unregister() must be called when the device is no
- * longer needed.
- */
-void thermal_cooling_device_unregister(struct
- thermal_cooling_device
- *cdev)
-{
- struct thermal_zone_device *tz;
- struct thermal_cooling_device *pos = NULL;
-
- if (!cdev)
- return;
-
- mutex_lock(&thermal_list_lock);
- list_for_each_entry(pos, &thermal_cdev_list, node)
- if (pos == cdev)
- break;
- if (pos != cdev) {
- /* thermal cooling device not found */
- mutex_unlock(&thermal_list_lock);
- return;
- }
- list_del(&cdev->node);
- list_for_each_entry(tz, &thermal_tz_list, node) {
- if (!tz->ops->unbind)
- continue;
- tz->ops->unbind(tz, cdev);
- }
- mutex_unlock(&thermal_list_lock);
- if (cdev->type[0])
- device_remove_file(&cdev->device, &dev_attr_cdev_type);
- device_remove_file(&cdev->device, &dev_attr_max_state);
- device_remove_file(&cdev->device, &dev_attr_cur_state);
-
- release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
- device_unregister(&cdev->device);
- return;
-}
-
-EXPORT_SYMBOL(thermal_cooling_device_unregister);
-
-/**
- * thermal_zone_device_register - register a new thermal zone device
- * @type: the thermal zone device type
- * @trips: the number of trip points the thermal zone support
- * @devdata: private device data
- * @ops: standard thermal zone device callbacks
- *
- * thermal_zone_device_unregister() must be called when the device is no
- * longer needed.
- */
-struct thermal_zone_device *thermal_zone_device_register(char *type,
- int trips,
- void *devdata, struct
- thermal_zone_device_ops
- *ops)
-{
- struct thermal_zone_device *tz;
- struct thermal_cooling_device *pos;
- int result;
- int count;
-
- if (strlen(type) >= THERMAL_NAME_LENGTH)
- return ERR_PTR(-EINVAL);
-
- if (trips > THERMAL_MAX_TRIPS || trips < 0)
- return ERR_PTR(-EINVAL);
-
- if (!ops || !ops->get_temp)
- return ERR_PTR(-EINVAL);
-
- tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
- if (!tz)
- return ERR_PTR(-ENOMEM);
-
- INIT_LIST_HEAD(&tz->cooling_devices);
- idr_init(&tz->idr);
- mutex_init(&tz->lock);
- result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
- if (result) {
- kfree(tz);
- return ERR_PTR(result);
- }
-
- strcpy(tz->type, type);
- tz->ops = ops;
- tz->device.class = &thermal_class;
- tz->devdata = devdata;
- tz->trips = trips;
- sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
- result = device_register(&tz->device);
- if (result) {
- release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
- kfree(tz);
- return ERR_PTR(result);
- }
-
- /* sys I/F */
- if (type) {
- result = device_create_file(&tz->device, &dev_attr_type);
- if (result)
- goto unregister;
- }
-
- result = device_create_file(&tz->device, &dev_attr_temp);
- if (result)
- goto unregister;
-
- if (ops->get_mode) {
- result = device_create_file(&tz->device, &dev_attr_mode);
- if (result)
- goto unregister;
- }
-
- for (count = 0; count < trips; count++) {
- TRIP_POINT_ATTR_ADD(&tz->device, count, result);
- if (result)
- goto unregister;
- }
-
- result = thermal_add_hwmon_sysfs(tz);
- if (result)
- goto unregister;
-
- mutex_lock(&thermal_list_lock);
- list_add_tail(&tz->node, &thermal_tz_list);
- if (ops->bind)
- list_for_each_entry(pos, &thermal_cdev_list, node) {
- result = ops->bind(tz, pos);
- if (result)
- break;
- }
- mutex_unlock(&thermal_list_lock);
-
- if (!result)
- return tz;
-
- unregister:
- release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
- device_unregister(&tz->device);
- return ERR_PTR(result);
-}
-
-EXPORT_SYMBOL(thermal_zone_device_register);
-
-/**
- * thermal_device_unregister - removes the registered thermal zone device
- * @tz: the thermal zone device to remove
- */
-void thermal_zone_device_unregister(struct thermal_zone_device *tz)
-{
- struct thermal_cooling_device *cdev;
- struct thermal_zone_device *pos = NULL;
- int count;
-
- if (!tz)
- return;
-
- mutex_lock(&thermal_list_lock);
- list_for_each_entry(pos, &thermal_tz_list, node)
- if (pos == tz)
- break;
- if (pos != tz) {
- /* thermal zone device not found */
- mutex_unlock(&thermal_list_lock);
- return;
- }
- list_del(&tz->node);
- if (tz->ops->unbind)
- list_for_each_entry(cdev, &thermal_cdev_list, node)
- tz->ops->unbind(tz, cdev);
- mutex_unlock(&thermal_list_lock);
-
- if (tz->type[0])
- device_remove_file(&tz->device, &dev_attr_type);
- device_remove_file(&tz->device, &dev_attr_temp);
- if (tz->ops->get_mode)
- device_remove_file(&tz->device, &dev_attr_mode);
-
- for (count = 0; count < tz->trips; count++)
- TRIP_POINT_ATTR_REMOVE(&tz->device, count);
-
- thermal_remove_hwmon_sysfs(tz);
- release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
- idr_destroy(&tz->idr);
- mutex_destroy(&tz->lock);
- device_unregister(&tz->device);
- return;
-}
-
-EXPORT_SYMBOL(thermal_zone_device_unregister);
-
-static int __init thermal_init(void)
-{
- int result = 0;
-
- result = class_register(&thermal_class);
- if (result) {
- idr_destroy(&thermal_tz_idr);
- idr_destroy(&thermal_cdev_idr);
- mutex_destroy(&thermal_idr_lock);
- mutex_destroy(&thermal_list_lock);
- }
- return result;
-}
-
-static void __exit thermal_exit(void)
-{
- class_unregister(&thermal_class);
- idr_destroy(&thermal_tz_idr);
- idr_destroy(&thermal_cdev_idr);
- mutex_destroy(&thermal_idr_lock);
- mutex_destroy(&thermal_list_lock);
-}
-
-subsys_initcall(thermal_init);
-module_exit(thermal_exit);
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
new file mode 100644
index 0000000..6098787
--- /dev/null
+++ b/drivers/thermal/thermal_sys.c
@@ -0,0 +1,899 @@
+/*
+ * thermal.c - Generic Thermal Management Sysfs support.
+ *
+ * Copyright (C) 2008 Intel Corp
+ * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
+ * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/thermal.h>
+#include <linux/spinlock.h>
+
+MODULE_AUTHOR("Zhang Rui");
+MODULE_DESCRIPTION("Generic thermal management sysfs support");
+MODULE_LICENSE("GPL");
+
+#define PREFIX "Thermal: "
+
+struct thermal_cooling_device_instance {
+ int id;
+ char name[THERMAL_NAME_LENGTH];
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *cdev;
+ int trip;
+ char attr_name[THERMAL_NAME_LENGTH];
+ struct device_attribute attr;
+ struct list_head node;
+};
+
+static DEFINE_IDR(thermal_tz_idr);
+static DEFINE_IDR(thermal_cdev_idr);
+static DEFINE_MUTEX(thermal_idr_lock);
+
+static LIST_HEAD(thermal_tz_list);
+static LIST_HEAD(thermal_cdev_list);
+static DEFINE_MUTEX(thermal_list_lock);
+
+static int get_idr(struct idr *idr, struct mutex *lock, int *id)
+{
+ int err;
+
+ again:
+ if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
+ return -ENOMEM;
+
+ if (lock)
+ mutex_lock(lock);
+ err = idr_get_new(idr, NULL, id);
+ if (lock)
+ mutex_unlock(lock);
+ if (unlikely(err == -EAGAIN))
+ goto again;
+ else if (unlikely(err))
+ return err;
+
+ *id = *id & MAX_ID_MASK;
+ return 0;
+}
+
+static void release_idr(struct idr *idr, struct mutex *lock, int id)
+{
+ if (lock)
+ mutex_lock(lock);
+ idr_remove(idr, id);
+ if (lock)
+ mutex_unlock(lock);
+}
+
+/* sys I/F for thermal zone */
+
+#define to_thermal_zone(_dev) \
+ container_of(_dev, struct thermal_zone_device, device)
+
+static ssize_t
+type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+
+ return sprintf(buf, "%s\n", tz->type);
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+
+ if (!tz->ops->get_temp)
+ return -EPERM;
+
+ return tz->ops->get_temp(tz, buf);
+}
+
+static ssize_t
+mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+
+ if (!tz->ops->get_mode)
+ return -EPERM;
+
+ return tz->ops->get_mode(tz, buf);
+}
+
+static ssize_t
+mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int result;
+
+ if (!tz->ops->set_mode)
+ return -EPERM;
+
+ result = tz->ops->set_mode(tz, buf);
+ if (result)
+ return result;
+
+ return count;
+}
+
+static ssize_t
+trip_point_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int trip;
+
+ if (!tz->ops->get_trip_type)
+ return -EPERM;
+
+ if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
+ return -EINVAL;
+
+ return tz->ops->get_trip_type(tz, trip, buf);
+}
+
+static ssize_t
+trip_point_temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int trip;
+
+ if (!tz->ops->get_trip_temp)
+ return -EPERM;
+
+ if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
+ return -EINVAL;
+
+ return tz->ops->get_trip_temp(tz, trip, buf);
+}
+
+static DEVICE_ATTR(type, 0444, type_show, NULL);
+static DEVICE_ATTR(temp, 0444, temp_show, NULL);
+static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
+
+static struct device_attribute trip_point_attrs[] = {
+ __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_10_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_10_temp, 0444, trip_point_temp_show, NULL),
+ __ATTR(trip_point_11_type, 0444, trip_point_type_show, NULL),
+ __ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
+};
+
+#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
+do { \
+ result = device_create_file(_dev, \
+ &trip_point_attrs[_index * 2]); \
+ if (result) \
+ break; \
+ result = device_create_file(_dev, \
+ &trip_point_attrs[_index * 2 + 1]); \
+} while (0)
+
+#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
+do { \
+ device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
+ device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
+} while (0)
+
+/* sys I/F for cooling device */
+#define to_cooling_device(_dev) \
+ container_of(_dev, struct thermal_cooling_device, device)
+
+static ssize_t
+thermal_cooling_device_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+
+ return sprintf(buf, "%s\n", cdev->type);
+}
+
+static ssize_t
+thermal_cooling_device_max_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+
+ return cdev->ops->get_max_state(cdev, buf);
+}
+
+static ssize_t
+thermal_cooling_device_cur_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+
+ return cdev->ops->get_cur_state(cdev, buf);
+}
+
+static ssize_t
+thermal_cooling_device_cur_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ int state;
+ int result;
+
+ if (!sscanf(buf, "%d\n", &state))
+ return -EINVAL;
+
+ if (state < 0)
+ return -EINVAL;
+
+ result = cdev->ops->set_cur_state(cdev, state);
+ if (result)
+ return result;
+ return count;
+}
+
+static struct device_attribute dev_attr_cdev_type =
+__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
+static DEVICE_ATTR(max_state, 0444,
+ thermal_cooling_device_max_state_show, NULL);
+static DEVICE_ATTR(cur_state, 0644,
+ thermal_cooling_device_cur_state_show,
+ thermal_cooling_device_cur_state_store);
+
+static ssize_t
+thermal_cooling_device_trip_point_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device_instance *instance;
+
+ instance =
+ container_of(attr, struct thermal_cooling_device_instance, attr);
+
+ if (instance->trip == THERMAL_TRIPS_NONE)
+ return sprintf(buf, "-1\n");
+ else
+ return sprintf(buf, "%d\n", instance->trip);
+}
+
+/* Device management */
+
+#if defined(CONFIG_HWMON) || \
+ (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE))
+/* hwmon sys I/F */
+#include <linux/hwmon.h>
+static LIST_HEAD(thermal_hwmon_list);
+
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_hwmon_device *hwmon = dev->driver_data;
+ return sprintf(buf, "%s\n", hwmon->type);
+}
+static DEVICE_ATTR(name, 0444, name_show, NULL);
+
+static ssize_t
+temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_zone_device *tz
+ = container_of(hwmon_attr, struct thermal_zone_device,
+ temp_input);
+
+ return tz->ops->get_temp(tz, buf);
+}
+
+static ssize_t
+temp_crit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct thermal_hwmon_attr *hwmon_attr
+ = container_of(attr, struct thermal_hwmon_attr, attr);
+ struct thermal_zone_device *tz
+ = container_of(hwmon_attr, struct thermal_zone_device,
+ temp_crit);
+
+ return tz->ops->get_trip_temp(tz, 0, buf);
+}
+
+
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon;
+ int new_hwmon_device = 1;
+ int result;
+
+ mutex_lock(&thermal_list_lock);
+ list_for_each_entry(hwmon, &thermal_hwmon_list, node)
+ if (!strcmp(hwmon->type, tz->type)) {
+ new_hwmon_device = 0;
+ mutex_unlock(&thermal_list_lock);
+ goto register_sys_interface;
+ }
+ mutex_unlock(&thermal_list_lock);
+
+ hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&hwmon->tz_list);
+ strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
+ hwmon->device = hwmon_device_register(NULL);
+ if (IS_ERR(hwmon->device)) {
+ result = PTR_ERR(hwmon->device);
+ goto free_mem;
+ }
+ hwmon->device->driver_data = hwmon;
+ result = device_create_file(hwmon->device, &dev_attr_name);
+ if (result)
+ goto unregister_hwmon_device;
+
+ register_sys_interface:
+ tz->hwmon = hwmon;
+ hwmon->count++;
+
+ snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
+ "temp%d_input", hwmon->count);
+ tz->temp_input.attr.attr.name = tz->temp_input.name;
+ tz->temp_input.attr.attr.mode = 0444;
+ tz->temp_input.attr.show = temp_input_show;
+ result = device_create_file(hwmon->device, &tz->temp_input.attr);
+ if (result)
+ goto unregister_hwmon_device;
+
+ if (tz->ops->get_crit_temp) {
+ unsigned long temperature;
+ if (!tz->ops->get_crit_temp(tz, &temperature)) {
+ snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
+ "temp%d_crit", hwmon->count);
+ tz->temp_crit.attr.attr.name = tz->temp_crit.name;
+ tz->temp_crit.attr.attr.mode = 0444;
+ tz->temp_crit.attr.show = temp_crit_show;
+ result = device_create_file(hwmon->device,
+ &tz->temp_crit.attr);
+ if (result)
+ goto unregister_hwmon_device;
+ }
+ }
+
+ mutex_lock(&thermal_list_lock);
+ if (new_hwmon_device)
+ list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
+ mutex_unlock(&thermal_list_lock);
+
+ return 0;
+
+ unregister_hwmon_device:
+ device_remove_file(hwmon->device, &tz->temp_crit.attr);
+ device_remove_file(hwmon->device, &tz->temp_input.attr);
+ if (new_hwmon_device) {
+ device_remove_file(hwmon->device, &dev_attr_name);
+ hwmon_device_unregister(hwmon->device);
+ }
+ free_mem:
+ if (new_hwmon_device)
+ kfree(hwmon);
+
+ return result;
+}
+
+static void
+thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon = tz->hwmon;
+
+ tz->hwmon = NULL;
+ device_remove_file(hwmon->device, &tz->temp_input.attr);
+ device_remove_file(hwmon->device, &tz->temp_crit.attr);
+
+ mutex_lock(&thermal_list_lock);
+ list_del(&tz->hwmon_node);
+ if (!list_empty(&hwmon->tz_list)) {
+ mutex_unlock(&thermal_list_lock);
+ return;
+ }
+ list_del(&hwmon->node);
+ mutex_unlock(&thermal_list_lock);
+
+ device_remove_file(hwmon->device, &dev_attr_name);
+ hwmon_device_unregister(hwmon->device);
+ kfree(hwmon);
+}
+#else
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+ return 0;
+}
+
+static void
+thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+}
+#endif
+
+
+/**
+ * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
+ * @tz: thermal zone device
+ * @trip: indicates which trip point the cooling devices is
+ * associated with in this thermal zone.
+ * @cdev: thermal cooling device
+ *
+ * This function is usually called in the thermal zone device .bind callback.
+ */
+int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
+ int trip,
+ struct thermal_cooling_device *cdev)
+{
+ struct thermal_cooling_device_instance *dev;
+ struct thermal_cooling_device_instance *pos;
+ struct thermal_zone_device *pos1;
+ struct thermal_cooling_device *pos2;
+ int result;
+
+ if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
+ return -EINVAL;
+
+ list_for_each_entry(pos1, &thermal_tz_list, node) {
+ if (pos1 == tz)
+ break;
+ }
+ list_for_each_entry(pos2, &thermal_cdev_list, node) {
+ if (pos2 == cdev)
+ break;
+ }
+
+ if (tz != pos1 || cdev != pos2)
+ return -EINVAL;
+
+ dev =
+ kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->tz = tz;
+ dev->cdev = cdev;
+ dev->trip = trip;
+ result = get_idr(&tz->idr, &tz->lock, &dev->id);
+ if (result)
+ goto free_mem;
+
+ sprintf(dev->name, "cdev%d", dev->id);
+ result =
+ sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
+ if (result)
+ goto release_idr;
+
+ sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
+ dev->attr.attr.name = dev->attr_name;
+ dev->attr.attr.mode = 0444;
+ dev->attr.show = thermal_cooling_device_trip_point_show;
+ result = device_create_file(&tz->device, &dev->attr);
+ if (result)
+ goto remove_symbol_link;
+
+ mutex_lock(&tz->lock);
+ list_for_each_entry(pos, &tz->cooling_devices, node)
+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
+ result = -EEXIST;
+ break;
+ }
+ if (!result)
+ list_add_tail(&dev->node, &tz->cooling_devices);
+ mutex_unlock(&tz->lock);
+
+ if (!result)
+ return 0;
+
+ device_remove_file(&tz->device, &dev->attr);
+ remove_symbol_link:
+ sysfs_remove_link(&tz->device.kobj, dev->name);
+ release_idr:
+ release_idr(&tz->idr, &tz->lock, dev->id);
+ free_mem:
+ kfree(dev);
+ return result;
+}
+
+EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
+
+/**
+ * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
+ * @tz: thermal zone device
+ * @trip: indicates which trip point the cooling devices is
+ * associated with in this thermal zone.
+ * @cdev: thermal cooling device
+ *
+ * This function is usually called in the thermal zone device .unbind callback.
+ */
+int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
+ int trip,
+ struct thermal_cooling_device *cdev)
+{
+ struct thermal_cooling_device_instance *pos, *next;
+
+ mutex_lock(&tz->lock);
+ list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
+ list_del(&pos->node);
+ mutex_unlock(&tz->lock);
+ goto unbind;
+ }
+ }
+ mutex_unlock(&tz->lock);
+
+ return -ENODEV;
+
+ unbind:
+ device_remove_file(&tz->device, &pos->attr);
+ sysfs_remove_link(&tz->device.kobj, pos->name);
+ release_idr(&tz->idr, &tz->lock, pos->id);
+ kfree(pos);
+ return 0;
+}
+
+EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
+
+static void thermal_release(struct device *dev)
+{
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *cdev;
+
+ if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {
+ tz = to_thermal_zone(dev);
+ kfree(tz);
+ } else {
+ cdev = to_cooling_device(dev);
+ kfree(cdev);
+ }
+}
+
+static struct class thermal_class = {
+ .name = "thermal",
+ .dev_release = thermal_release,
+};
+
+/**
+ * thermal_cooling_device_register - register a new thermal cooling device
+ * @type: the thermal cooling device type.
+ * @devdata: device private data.
+ * @ops: standard thermal cooling devices callbacks.
+ */
+struct thermal_cooling_device *thermal_cooling_device_register(char *type,
+ void *devdata,
+ struct
+ thermal_cooling_device_ops
+ *ops)
+{
+ struct thermal_cooling_device *cdev;
+ struct thermal_zone_device *pos;
+ int result;
+
+ if (strlen(type) >= THERMAL_NAME_LENGTH)
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->get_max_state || !ops->get_cur_state ||
+ !ops->set_cur_state)
+ return ERR_PTR(-EINVAL);
+
+ cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
+ if (!cdev)
+ return ERR_PTR(-ENOMEM);
+
+ result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
+ if (result) {
+ kfree(cdev);
+ return ERR_PTR(result);
+ }
+
+ strcpy(cdev->type, type);
+ cdev->ops = ops;
+ cdev->device.class = &thermal_class;
+ cdev->devdata = devdata;
+ sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);
+ result = device_register(&cdev->device);
+ if (result) {
+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
+ kfree(cdev);
+ return ERR_PTR(result);
+ }
+
+ /* sys I/F */
+ if (type) {
+ result = device_create_file(&cdev->device, &dev_attr_cdev_type);
+ if (result)
+ goto unregister;
+ }
+
+ result = device_create_file(&cdev->device, &dev_attr_max_state);
+ if (result)
+ goto unregister;
+
+ result = device_create_file(&cdev->device, &dev_attr_cur_state);
+ if (result)
+ goto unregister;
+
+ mutex_lock(&thermal_list_lock);
+ list_add(&cdev->node, &thermal_cdev_list);
+ list_for_each_entry(pos, &thermal_tz_list, node) {
+ if (!pos->ops->bind)
+ continue;
+ result = pos->ops->bind(pos, cdev);
+ if (result)
+ break;
+
+ }
+ mutex_unlock(&thermal_list_lock);
+
+ if (!result)
+ return cdev;
+
+ unregister:
+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
+ device_unregister(&cdev->device);
+ return ERR_PTR(result);
+}
+
+EXPORT_SYMBOL(thermal_cooling_device_register);
+
+/**
+ * thermal_cooling_device_unregister - removes the registered thermal cooling device
+ * @cdev: the thermal cooling device to remove.
+ *
+ * thermal_cooling_device_unregister() must be called when the device is no
+ * longer needed.
+ */
+void thermal_cooling_device_unregister(struct
+ thermal_cooling_device
+ *cdev)
+{
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *pos = NULL;
+
+ if (!cdev)
+ return;
+
+ mutex_lock(&thermal_list_lock);
+ list_for_each_entry(pos, &thermal_cdev_list, node)
+ if (pos == cdev)
+ break;
+ if (pos != cdev) {
+ /* thermal cooling device not found */
+ mutex_unlock(&thermal_list_lock);
+ return;
+ }
+ list_del(&cdev->node);
+ list_for_each_entry(tz, &thermal_tz_list, node) {
+ if (!tz->ops->unbind)
+ continue;
+ tz->ops->unbind(tz, cdev);
+ }
+ mutex_unlock(&thermal_list_lock);
+ if (cdev->type[0])
+ device_remove_file(&cdev->device, &dev_attr_cdev_type);
+ device_remove_file(&cdev->device, &dev_attr_max_state);
+ device_remove_file(&cdev->device, &dev_attr_cur_state);
+
+ release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
+ device_unregister(&cdev->device);
+ return;
+}
+
+EXPORT_SYMBOL(thermal_cooling_device_unregister);
+
+/**
+ * thermal_zone_device_register - register a new thermal zone device
+ * @type: the thermal zone device type
+ * @trips: the number of trip points the thermal zone support
+ * @devdata: private device data
+ * @ops: standard thermal zone device callbacks
+ *
+ * thermal_zone_device_unregister() must be called when the device is no
+ * longer needed.
+ */
+struct thermal_zone_device *thermal_zone_device_register(char *type,
+ int trips,
+ void *devdata, struct
+ thermal_zone_device_ops
+ *ops)
+{
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *pos;
+ int result;
+ int count;
+
+ if (strlen(type) >= THERMAL_NAME_LENGTH)
+ return ERR_PTR(-EINVAL);
+
+ if (trips > THERMAL_MAX_TRIPS || trips < 0)
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->get_temp)
+ return ERR_PTR(-EINVAL);
+
+ tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
+ if (!tz)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&tz->cooling_devices);
+ idr_init(&tz->idr);
+ mutex_init(&tz->lock);
+ result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
+ if (result) {
+ kfree(tz);
+ return ERR_PTR(result);
+ }
+
+ strcpy(tz->type, type);
+ tz->ops = ops;
+ tz->device.class = &thermal_class;
+ tz->devdata = devdata;
+ tz->trips = trips;
+ sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
+ result = device_register(&tz->device);
+ if (result) {
+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
+ kfree(tz);
+ return ERR_PTR(result);
+ }
+
+ /* sys I/F */
+ if (type) {
+ result = device_create_file(&tz->device, &dev_attr_type);
+ if (result)
+ goto unregister;
+ }
+
+ result = device_create_file(&tz->device, &dev_attr_temp);
+ if (result)
+ goto unregister;
+
+ if (ops->get_mode) {
+ result = device_create_file(&tz->device, &dev_attr_mode);
+ if (result)
+ goto unregister;
+ }
+
+ for (count = 0; count < trips; count++) {
+ TRIP_POINT_ATTR_ADD(&tz->device, count, result);
+ if (result)
+ goto unregister;
+ }
+
+ result = thermal_add_hwmon_sysfs(tz);
+ if (result)
+ goto unregister;
+
+ mutex_lock(&thermal_list_lock);
+ list_add_tail(&tz->node, &thermal_tz_list);
+ if (ops->bind)
+ list_for_each_entry(pos, &thermal_cdev_list, node) {
+ result = ops->bind(tz, pos);
+ if (result)
+ break;
+ }
+ mutex_unlock(&thermal_list_lock);
+
+ if (!result)
+ return tz;
+
+ unregister:
+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
+ device_unregister(&tz->device);
+ return ERR_PTR(result);
+}
+
+EXPORT_SYMBOL(thermal_zone_device_register);
+
+/**
+ * thermal_device_unregister - removes the registered thermal zone device
+ * @tz: the thermal zone device to remove
+ */
+void thermal_zone_device_unregister(struct thermal_zone_device *tz)
+{
+ struct thermal_cooling_device *cdev;
+ struct thermal_zone_device *pos = NULL;
+ int count;
+
+ if (!tz)
+ return;
+
+ mutex_lock(&thermal_list_lock);
+ list_for_each_entry(pos, &thermal_tz_list, node)
+ if (pos == tz)
+ break;
+ if (pos != tz) {
+ /* thermal zone device not found */
+ mutex_unlock(&thermal_list_lock);
+ return;
+ }
+ list_del(&tz->node);
+ if (tz->ops->unbind)
+ list_for_each_entry(cdev, &thermal_cdev_list, node)
+ tz->ops->unbind(tz, cdev);
+ mutex_unlock(&thermal_list_lock);
+
+ if (tz->type[0])
+ device_remove_file(&tz->device, &dev_attr_type);
+ device_remove_file(&tz->device, &dev_attr_temp);
+ if (tz->ops->get_mode)
+ device_remove_file(&tz->device, &dev_attr_mode);
+
+ for (count = 0; count < tz->trips; count++)
+ TRIP_POINT_ATTR_REMOVE(&tz->device, count);
+
+ thermal_remove_hwmon_sysfs(tz);
+ release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
+ idr_destroy(&tz->idr);
+ mutex_destroy(&tz->lock);
+ device_unregister(&tz->device);
+ return;
+}
+
+EXPORT_SYMBOL(thermal_zone_device_unregister);
+
+static int __init thermal_init(void)
+{
+ int result = 0;
+
+ result = class_register(&thermal_class);
+ if (result) {
+ idr_destroy(&thermal_tz_idr);
+ idr_destroy(&thermal_cdev_idr);
+ mutex_destroy(&thermal_idr_lock);
+ mutex_destroy(&thermal_list_lock);
+ }
+ return result;
+}
+
+static void __exit thermal_exit(void)
+{
+ class_unregister(&thermal_class);
+ idr_destroy(&thermal_tz_idr);
+ idr_destroy(&thermal_cdev_idr);
+ mutex_destroy(&thermal_idr_lock);
+ mutex_destroy(&thermal_list_lock);
+}
+
+subsys_initcall(thermal_init);
+module_exit(thermal_exit);
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 156/171] ACPI: thinkpad-acpi: BIOS backlight mode helper (v2)
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (27 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 100/171] thermal: re-name thermal.c to thermal_sys.c Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 157/171] ACPI: thinkpad-acpi: warn once about weird hotkey masks Len Brown
` (13 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi
Cc: Henrique de Moraes Holschuh, Matthew Garrett, Thomas Renninger,
Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Lenovo ThinkPads with generic ACPI backlight level control can be easily
set to react to keyboard brightness key presses in a more predictable way
than what they do when in "DOS / bootloader" mode after Linux brings
up the ACPI interface.
The switch to the ACPI backlight mode in the firmware is designed to be
safe to use only as an one way trapdoor. One is not to force the firmware
to switch back to "DOS/bootloader" mode except by rebooting. The mode
switch itself is performed by calling any of the ACPI _BCL methods at least
once.
When in ACPI mode, the backlight firmware just issues (standard) events for
the brightness up/down hot key presses along with the non-standard HKEY
events which thinkpad-acpi traps, and doesn't touch the hardware.
thinkpad-acpi will:
1. Place the ThinkPad firmware in ACPI backlight control mode
if one is available
2. Suppress HKEY backlight change notifications by default
to avoid double-reporting when ACPI video is loaded when
the ThinkPad is in ACPI backlight control mode
3. Urge the user to load the ACPI video driver
The user is free to use either the ACPI video driver to get the brightness
key events, or to override the thinkpad-acpi default hotkey mask to get
them from thinkpad-acpi as well (this will result in duplicate events if
ACPI video is loaded, so let's hope distros won't screw this up).
Provided userspace is sane, all should work (and *keep* working), which is
more that can be said about the non-ACPI mode of the new Lenovo ThinkPad
BIOSes when coupled to current userspace and X.org drivers.
Full guidelines for backlight hot key reporting and use of the
thinkpad-acpi backlight interface have been added to the documentation.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Thomas Renninger <trenn@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/laptops/thinkpad-acpi.txt | 49 +++++++
drivers/misc/thinkpad_acpi.c | 238 +++++++++++++++++--------------
2 files changed, 182 insertions(+), 105 deletions(-)
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 76cb428..bbc8275 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -571,6 +571,46 @@ netlink interface and the input layer interface, and don't bother at all
with hotkey_report_mode.
+Brightness hotkey notes:
+
+These are the current sane choices for brightness key mapping in
+thinkpad-acpi:
+
+For IBM and Lenovo models *without* ACPI backlight control (the ones on
+which thinkpad-acpi will autoload its backlight interface by default,
+and on which ACPI video does not export a backlight interface):
+
+1. Don't enable or map the brightness hotkeys in thinkpad-acpi, as
+ these older firmware versions unfortunately won't respect the hotkey
+ mask for brightness keys anyway, and always reacts to them. This
+ usually work fine, unless X.org is doing something to block the BIOS.
+ In that case, use (3) below. This is the default mode of operation.
+
+2. Enable the hotkeys, but map them to something else that is NOT
+ KEY_BRIGHTNESS_UP/DOWN or any other keycode that would cause
+ userspace to try to change the backlight level, and use that as an
+ on-screen-display hint.
+
+3. IF AND ONLY IF X.org finds a way to block the firmware from
+ automatically changing the brightness, enable the hotkeys and map
+ them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN, and feed that to
+ something that calls xbacklight. thinkpad-acpi will not be able to
+ change brightness in that case either, so you should disable its
+ backlight interface.
+
+For Lenovo models *with* ACPI backlight control:
+
+1. Load up ACPI video and use that. ACPI video will report ACPI
+ events for brightness change keys. Do not mess with thinkpad-acpi
+ defaults in this case. thinkpad-acpi should not have anything to do
+ with backlight events in a scenario where ACPI video is loaded:
+ brightness hotkeys must be disabled, and the backlight interface is
+ to be kept disabled as well. This is the default mode of operation.
+
+2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi,
+ and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process
+ these keys on userspace somehow (e.g. by calling xbacklight).
+
Bluetooth
---------
@@ -1090,6 +1130,15 @@ it there will be the following attributes:
dim the display.
+WARNING:
+
+ Whatever you do, do NOT ever call thinkpad-acpi backlight-level change
+ interface and the ACPI-based backlight level change interface
+ (available on newer BIOSes, and driven by the Linux ACPI video driver)
+ at the same time. The two will interact in bad ways, do funny things,
+ and maybe reduce the life of the backlight lamps by needlessly kicking
+ its level up and down at every change.
+
Volume control -- /proc/acpi/ibm/volume
---------------------------------------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 6cb7812..2c85a2e 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -225,6 +225,7 @@ static struct {
u32 light:1;
u32 light_status:1;
u32 bright_16levels:1;
+ u32 bright_acpimode:1;
u32 wan:1;
u32 fan_ctrl_status_undef:1;
u32 input_device_registered:1;
@@ -807,6 +808,80 @@ static int parse_strtoul(const char *buf,
return 0;
}
+static int __init tpacpi_query_bcl_levels(acpi_handle handle)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ int rc;
+
+ if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
+ obj = (union acpi_object *)buffer.pointer;
+ if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
+ printk(TPACPI_ERR "Unknown _BCL data, "
+ "please report this to %s\n", TPACPI_MAIL);
+ rc = 0;
+ } else {
+ rc = obj->package.count;
+ }
+ } else {
+ return 0;
+ }
+
+ kfree(buffer.pointer);
+ return rc;
+}
+
+static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
+ u32 lvl, void *context, void **rv)
+{
+ char name[ACPI_PATH_SEGMENT_LENGTH];
+ struct acpi_buffer buffer = { sizeof(name), &name };
+
+ if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
+ !strncmp("_BCL", name, sizeof(name) - 1)) {
+ BUG_ON(!rv || !*rv);
+ **(int **)rv = tpacpi_query_bcl_levels(handle);
+ return AE_CTRL_TERMINATE;
+ } else {
+ return AE_OK;
+ }
+}
+
+/*
+ * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
+ */
+static int __init tpacpi_check_std_acpi_brightness_support(void)
+{
+ int status;
+ int bcl_levels = 0;
+ void *bcl_ptr = &bcl_levels;
+
+ if (!vid_handle) {
+ TPACPI_ACPIHANDLE_INIT(vid);
+ }
+ if (!vid_handle)
+ return 0;
+
+ /*
+ * Search for a _BCL method, and execute it. This is safe on all
+ * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
+ * BIOS in ACPI backlight control mode. We do NOT have to care
+ * about calling the _BCL method in an enabled video device, any
+ * will do for our purposes.
+ */
+
+ status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
+ tpacpi_acpi_walk_find_bcl, NULL,
+ &bcl_ptr);
+
+ if (ACPI_SUCCESS(status) && bcl_levels > 2) {
+ tp_features.bright_acpimode = 1;
+ return (bcl_levels - 2);
+ }
+
+ return 0;
+}
+
/*************************************************************************
* thinkpad-acpi driver attributes
*/
@@ -1887,6 +1962,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_UNKNOWN, /* 0x0D: FN+INSERT */
KEY_UNKNOWN, /* 0x0E: FN+DELETE */
+ /* These either have to go through ACPI video, or
+ * act like in the IBM ThinkPads, so don't ever
+ * enable them by default */
KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
KEY_RESERVED, /* 0x10: FN+END (brightness down) */
@@ -2091,6 +2169,32 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
}
+ /* Do not issue duplicate brightness change events to
+ * userspace */
+ if (!tp_features.bright_acpimode)
+ /* update bright_acpimode... */
+ tpacpi_check_std_acpi_brightness_support();
+
+ if (tp_features.bright_acpimode) {
+ printk(TPACPI_INFO
+ "This ThinkPad has standard ACPI backlight "
+ "brightness control, supported by the ACPI "
+ "video driver\n");
+ printk(TPACPI_NOTICE
+ "Disabling thinkpad-acpi brightness events "
+ "by default...\n");
+
+ /* The hotkey_reserved_mask change below is not
+ * necessary while the keys are at KEY_RESERVED in the
+ * default map, but better safe than sorry, leave it
+ * here as a marker of what we have to do, especially
+ * when we finally become able to set this at runtime
+ * on response to X.org requests */
+ hotkey_reserved_mask |=
+ (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
+ | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
+ }
+
dbg_printk(TPACPI_DBG_INIT,
"enabling hot key handling\n");
res = hotkey_status_set(1);
@@ -4273,100 +4377,6 @@ static struct backlight_ops ibm_backlight_data = {
/* --------------------------------------------------------------------- */
-static int __init tpacpi_query_bcll_levels(acpi_handle handle)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *obj;
- int rc;
-
- if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
- obj = (union acpi_object *)buffer.pointer;
- if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
- printk(TPACPI_ERR "Unknown BCLL data, "
- "please report this to %s\n", TPACPI_MAIL);
- rc = 0;
- } else {
- rc = obj->package.count;
- }
- } else {
- return 0;
- }
-
- kfree(buffer.pointer);
- return rc;
-}
-
-static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl,
- void *context, void **rv)
-{
- char name[ACPI_PATH_SEGMENT_LENGTH];
- struct acpi_buffer buffer = { sizeof(name), &name };
-
- if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
- !strncmp("BCLL", name, sizeof(name) - 1)) {
- if (tpacpi_query_bcll_levels(handle) == 16) {
- *rv = handle;
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
- } else {
- return AE_OK;
- }
-}
-
-static int __init brightness_check_levels(void)
-{
- int status;
- void *found_node = NULL;
-
- if (!vid_handle) {
- TPACPI_ACPIHANDLE_INIT(vid);
- }
- if (!vid_handle)
- return 0;
-
- /* Search for a BCLL package with 16 levels */
- status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3,
- brightness_find_bcll, NULL,
- &found_node);
-
- return (ACPI_SUCCESS(status) && found_node != NULL);
-}
-
-static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl,
- void *context, void **rv)
-{
- char name[ACPI_PATH_SEGMENT_LENGTH];
- struct acpi_buffer buffer = { sizeof(name), &name };
-
- if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
- !strncmp("_BCL", name, sizeof(name) - 1)) {
- *rv = handle;
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
-}
-
-static int __init brightness_check_std_acpi_support(void)
-{
- int status;
- void *found_node = NULL;
-
- if (!vid_handle) {
- TPACPI_ACPIHANDLE_INIT(vid);
- }
- if (!vid_handle)
- return 0;
-
- /* Search for a _BCL method, but don't execute it */
- status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
- brightness_find_bcl, NULL, &found_node);
-
- return (ACPI_SUCCESS(status) && found_node != NULL);
-}
-
static int __init brightness_init(struct ibm_init_struct *iibm)
{
int b;
@@ -4375,13 +4385,19 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
mutex_init(&brightness_mutex);
- if (!brightness_enable) {
- dbg_printk(TPACPI_DBG_INIT,
- "brightness support disabled by "
- "module parameter\n");
- return 1;
- } else if (brightness_enable > 1) {
- if (brightness_check_std_acpi_support()) {
+ /*
+ * We always attempt to detect acpi support, so as to switch
+ * Lenovo Vista BIOS to ACPI brightness mode even if we are not
+ * going to publish a backlight interface
+ */
+ b = tpacpi_check_std_acpi_brightness_support();
+ if (b > 0) {
+ if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
+ printk(TPACPI_NOTICE
+ "Lenovo BIOS switched to ACPI backlight "
+ "control mode\n");
+ }
+ if (brightness_enable > 1) {
printk(TPACPI_NOTICE
"standard ACPI backlight interface "
"available, not loading native one...\n");
@@ -4389,6 +4405,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
}
}
+ if (!brightness_enable) {
+ dbg_printk(TPACPI_DBG_INIT,
+ "brightness support disabled by "
+ "module parameter\n");
+ return 1;
+ }
+
+ if (b > 16) {
+ printk(TPACPI_ERR
+ "Unsupported brightness interface, "
+ "please contact %s\n", TPACPI_MAIL);
+ return 1;
+ }
+ if (b == 16)
+ tp_features.bright_16levels = 1;
+
if (!brightness_mode) {
if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
brightness_mode = 2;
@@ -4402,10 +4434,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if (brightness_mode > 3)
return -EINVAL;
- tp_features.bright_16levels =
- thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO &&
- brightness_check_levels();
-
b = brightness_get(NULL);
if (b < 0)
return 1;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 157/171] ACPI: thinkpad-acpi: warn once about weird hotkey masks
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (28 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 156/171] ACPI: thinkpad-acpi: BIOS backlight mode helper (v2) Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 158/171] ACPI: thinkpad-acpi: enhance box identification output Len Brown
` (12 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
thinkpad-acpi knows for a while now how to best program the hotkeys by
default, and always enable them by default. Unfortunately, this
information has not filtered down everywhere it needs to, yet. Notably,
old ibm-acpi documentation and most "thinkpad setup guides" will have wrong
information on this area.
Warn the local admin once whenever any of the following patterns are met:
1. Attempts to set hotkey mask to 0xffff (artifact from docs and config
for the old ibm-acpi driver and behaviour). This mask makes no
real-world sense;
2. Attempts to set hotkey mask to 0xffffffff, which means the user is
trying to just have "everything work" without even reading the
documentation, or that we need to get a bug report, because there
is a new thinkpad out there with new exciting hot keys :-)
3. Attempts to set hotkey mask to 0xffffff, which is almost never the
correct way to set up volume and brightness event reporting (and with
the current state-of-the-art, it is known to never be right way to do
it).
The driver will perform any and all requested operations, though,
regardless of any warnings. I hope these warnings can be removed one or
two years from now.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 2c85a2e..cd263c5 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -237,6 +237,10 @@ static struct {
u32 hotkey_poll_active:1;
} tp_features;
+static struct {
+ u16 hotkey_mask_ff:1;
+} tp_warned;
+
struct thinkpad_id_data {
unsigned int vendor; /* ThinkPad vendor:
* PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
@@ -1182,6 +1186,19 @@ static int hotkey_mask_set(u32 mask)
int rc = 0;
if (tp_features.hotkey_mask) {
+ if (!tp_warned.hotkey_mask_ff &&
+ (mask == 0xffff || mask == 0xffffff ||
+ mask == 0xffffffff)) {
+ tp_warned.hotkey_mask_ff = 1;
+ printk(TPACPI_NOTICE
+ "setting the hotkey mask to 0x%08x is likely "
+ "not the best way to go about it\n", mask);
+ printk(TPACPI_NOTICE
+ "please consider using the driver defaults, "
+ "and refer to up-to-date thinkpad-acpi "
+ "documentation\n");
+ }
+
HOTKEY_CONFIG_CRITICAL_START
for (i = 0; i < 32; i++) {
u32 m = 1 << i;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 158/171] ACPI: thinkpad-acpi: enhance box identification output
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (29 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 157/171] ACPI: thinkpad-acpi: warn once about weird hotkey masks Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 159/171] ACPI: thinkpad-acpi: rate-limit CMOS/EC unsynced error messages Len Brown
` (11 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
During initialization, thinkpad-acpi outputs some messages to make sure
releavant box identification information is easily available in-line with
the rest of the driver messages.
Enhance those messages to output the alfanumeric model number as well.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index cd263c5..c8c1d6c 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -251,7 +251,8 @@ struct thinkpad_id_data {
u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
u16 ec_model;
- char *model_str;
+ char *model_str; /* ThinkPad T43 */
+ char *nummodel_str; /* 9384A9C for a 9384-A9C model */
};
static struct thinkpad_id_data thinkpad_id;
@@ -988,12 +989,13 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
thinkpad_id.ec_version_str : "unknown");
if (thinkpad_id.vendor && thinkpad_id.model_str)
- printk(TPACPI_INFO "%s %s\n",
+ printk(TPACPI_INFO "%s %s, model %s\n",
(thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
"IBM" : ((thinkpad_id.vendor ==
PCI_VENDOR_ID_LENOVO) ?
"Lenovo" : "Unknown vendor"),
- thinkpad_id.model_str);
+ thinkpad_id.model_str,
+ thinkpad_id.nummodel_str);
return 0;
}
@@ -5875,6 +5877,9 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
kfree(tp->model_str);
tp->model_str = NULL;
}
+
+ tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME),
+ GFP_KERNEL);
}
static int __init probe_for_thinkpad(void)
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 159/171] ACPI: thinkpad-acpi: rate-limit CMOS/EC unsynced error messages
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (30 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 158/171] ACPI: thinkpad-acpi: enhance box identification output Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 160/171] ACPI: thinkpad-acpi: fix brightness dimming control bug Len Brown
` (10 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Joerg Platte, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
If userspace applications mess with the CMOS NVRAM, or something causes
both the ACPI firmware and thinkpad-acpi to try to change the brightness at
the same time, it is possible to have the CMOS and EC registers for the
current brightness go out of sync.
Should that happen, thinkpad-acpi could be really obnoxious when using a
brightness_mode of 3 (both EC and CMOS). Instead of complaining a massive
number of times, make sure to complain only once until EC and CMOS are back
in sync.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Joerg Platte <lists@naasa.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 22 +++++++++++++++-------
1 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index c8c1d6c..3513d99 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -239,6 +239,7 @@ static struct {
static struct {
u16 hotkey_mask_ff:1;
+ u16 bright_cmos_ec_unsync:1;
} tp_warned;
struct thinkpad_id_data {
@@ -4322,13 +4323,20 @@ static int brightness_get(struct backlight_device *bd)
level = lcmos;
}
- if (brightness_mode == 3 && lec != lcmos) {
- printk(TPACPI_ERR
- "CMOS NVRAM (%u) and EC (%u) do not agree "
- "on display brightness level\n",
- (unsigned int) lcmos,
- (unsigned int) lec);
- return -EIO;
+ if (brightness_mode == 3) {
+ if (lec == lcmos)
+ tp_warned.bright_cmos_ec_unsync = 0;
+ else {
+ if (!tp_warned.bright_cmos_ec_unsync) {
+ printk(TPACPI_ERR
+ "CMOS NVRAM (%u) and EC (%u) do not "
+ "agree on display brightness level\n",
+ (unsigned int) lcmos,
+ (unsigned int) lec);
+ tp_warned.bright_cmos_ec_unsync = 1;
+ }
+ return -EIO;
+ }
}
return level;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 160/171] ACPI: thinkpad-acpi: fix brightness dimming control bug
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (31 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 159/171] ACPI: thinkpad-acpi: rate-limit CMOS/EC unsynced error messages Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 161/171] ACPI: thinkpad-acpi: claim tpacpi as an official short handle Len Brown
` (9 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
ibm-acpi and thinkpad-acpi did not know about bit 5 of the EC backlight
level control register (EC 0x31), so it was always forced to zero on
any writes.
This would disable the BIOS option to *not* use a dimmer backlight level
scale while on battery, and who knows what else (there are two other
control bits of unknown function).
Bit 5 controls the "reduce backlight levels when on battery" optional
functionality (active low). Bits 6 and 7 are better left alone as well,
instead of being forced to zero.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 64 ++++++++++++++++++++++++++++++++----------
1 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 3513d99..1a26423 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -4294,8 +4294,16 @@ static struct ibm_struct ecdump_driver_data = {
#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
+enum {
+ TP_EC_BACKLIGHT = 0x31,
+
+ /* TP_EC_BACKLIGHT bitmasks */
+ TP_EC_BACKLIGHT_LVLMSK = 0x1F,
+ TP_EC_BACKLIGHT_CMDMSK = 0xE0,
+ TP_EC_BACKLIGHT_MAPSW = 0x20,
+};
+
static struct backlight_device *ibm_backlight_device;
-static int brightness_offset = 0x31;
static int brightness_mode;
static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
@@ -4304,16 +4312,24 @@ static struct mutex brightness_mutex;
/*
* ThinkPads can read brightness from two places: EC 0x31, or
* CMOS NVRAM byte 0x5E, bits 0-3.
+ *
+ * EC 0x31 has the following layout
+ * Bit 7: unknown function
+ * Bit 6: unknown function
+ * Bit 5: Z: honour scale changes, NZ: ignore scale changes
+ * Bit 4: must be set to zero to avoid problems
+ * Bit 3-0: backlight brightness level
+ *
+ * brightness_get_raw returns status data in the EC 0x31 layout
*/
-static int brightness_get(struct backlight_device *bd)
+static int brightness_get_raw(int *status)
{
u8 lec = 0, lcmos = 0, level = 0;
if (brightness_mode & 1) {
- if (!acpi_ec_read(brightness_offset, &lec))
+ if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
return -EIO;
- lec &= (tp_features.bright_16levels)? 0x0f : 0x07;
- level = lec;
+ level = lec & TP_EC_BACKLIGHT_LVLMSK;
};
if (brightness_mode & 2) {
lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
@@ -4324,6 +4340,8 @@ static int brightness_get(struct backlight_device *bd)
}
if (brightness_mode == 3) {
+ *status = lec; /* Prefer EC, CMOS is just a backing store */
+ lec &= TP_EC_BACKLIGHT_LVLMSK;
if (lec == lcmos)
tp_warned.bright_cmos_ec_unsync = 0;
else {
@@ -4337,9 +4355,11 @@ static int brightness_get(struct backlight_device *bd)
}
return -EIO;
}
+ } else {
+ *status = level;
}
- return level;
+ return 0;
}
/* May return EINTR which can always be mapped to ERESTARTSYS */
@@ -4347,19 +4367,22 @@ static int brightness_set(int value)
{
int cmos_cmd, inc, i, res;
int current_value;
+ int command_bits;
- if (value > ((tp_features.bright_16levels)? 15 : 7))
+ if (value > ((tp_features.bright_16levels)? 15 : 7) ||
+ value < 0)
return -EINVAL;
res = mutex_lock_interruptible(&brightness_mutex);
if (res < 0)
return res;
- current_value = brightness_get(NULL);
- if (current_value < 0) {
- res = current_value;
+ res = brightness_get_raw(¤t_value);
+ if (res < 0)
goto errout;
- }
+
+ command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
+ current_value &= TP_EC_BACKLIGHT_LVLMSK;
cmos_cmd = value > current_value ?
TP_CMOS_BRIGHTNESS_UP :
@@ -4374,7 +4397,8 @@ static int brightness_set(int value)
goto errout;
}
if ((brightness_mode & 1) &&
- !acpi_ec_write(brightness_offset, i + inc)) {
+ !acpi_ec_write(TP_EC_BACKLIGHT,
+ (i + inc) | command_bits)) {
res = -EIO;
goto errout;;
}
@@ -4397,6 +4421,17 @@ static int brightness_update_status(struct backlight_device *bd)
bd->props.brightness : 0);
}
+static int brightness_get(struct backlight_device *bd)
+{
+ int status, res;
+
+ res = brightness_get_raw(&status);
+ if (res < 0)
+ return 0; /* FIXME: teach backlight about error handling */
+
+ return status & TP_EC_BACKLIGHT_LVLMSK;
+}
+
static struct backlight_ops ibm_backlight_data = {
.get_brightness = brightness_get,
.update_status = brightness_update_status,
@@ -4461,8 +4496,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if (brightness_mode > 3)
return -EINVAL;
- b = brightness_get(NULL);
- if (b < 0)
+ if (brightness_get_raw(&b) < 0)
return 1;
if (tp_features.bright_16levels)
@@ -4480,7 +4514,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
ibm_backlight_device->props.max_brightness =
(tp_features.bright_16levels)? 15 : 7;
- ibm_backlight_device->props.brightness = b;
+ ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
backlight_update_status(ibm_backlight_device);
return 0;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 161/171] ACPI: thinkpad-acpi: claim tpacpi as an official short handle
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (32 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 160/171] ACPI: thinkpad-acpi: fix brightness dimming control bug Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 162/171] ACPI: thinkpad-acpi: prepare light and LED for sysfs support Len Brown
` (8 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Unfortunately, a lot of stuff in the kernel has size limitations, so
"thinkpad-acpi" ends up eating up too much real estate. We were using
"tpacpi" in symbols already, but this shorthand was not visible to
userland.
Document that the driver will use tpacpi as a short hand where necessary,
and use it to name the kernel thread for NVRAM polling (now named
"ktpacpi_nvramd").
Also, register a module alias with the shorthand. One can refer to the
module using the shorthand name.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/laptops/thinkpad-acpi.txt | 5 +++++
drivers/misc/thinkpad_acpi.c | 8 ++++++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index bbc8275..0f403ff 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -18,6 +18,11 @@ This driver used to be named ibm-acpi until kernel 2.6.21 and release
moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel
2.6.22, and release 0.14.
+The driver is named "thinkpad-acpi". In some places, like module
+names, "thinkpad_acpi" is used because of userspace issues.
+
+"tpacpi" is used as a shorthand where "thinkpad-acpi" would be too
+long due to lenght limitations on some Linux kernel versions.
Status
------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 1a26423..f23acc8 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -133,8 +133,11 @@ enum {
#define TPACPI_PROC_DIR "ibm"
#define TPACPI_ACPI_EVENT_PREFIX "ibm"
#define TPACPI_DRVR_NAME TPACPI_FILE
+#define TPACPI_DRVR_SHORTNAME "tpacpi"
#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
+#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
+
#define TPACPI_MAX_ACPI_ARGS 3
/* Debugging */
@@ -1522,8 +1525,7 @@ static void hotkey_poll_setup(int may_warn)
(tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) {
if (!tpacpi_hotkey_task) {
tpacpi_hotkey_task = kthread_run(hotkey_kthread,
- NULL,
- TPACPI_FILE "d");
+ NULL, TPACPI_NVRAM_KTHREAD_NAME);
if (IS_ERR(tpacpi_hotkey_task)) {
tpacpi_hotkey_task = NULL;
printk(TPACPI_ERR
@@ -6315,6 +6317,8 @@ static int __init thinkpad_acpi_module_init(void)
/* Please remove this in year 2009 */
MODULE_ALIAS("ibm_acpi");
+MODULE_ALIAS(TPACPI_DRVR_SHORTNAME);
+
/*
* DMI matching for module autoloading
*
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 162/171] ACPI: thinkpad-acpi: prepare light and LED for sysfs support
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (33 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 161/171] ACPI: thinkpad-acpi: claim tpacpi as an official short handle Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 163/171] ACPI: thinkpad-acpi: add sysfs led class support for thinklight (v3.1) Len Brown
` (7 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
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>
---
drivers/misc/Kconfig | 2 +
drivers/misc/thinkpad_acpi.c | 191 ++++++++++++++++++++++++++++++------------
2 files changed, 138 insertions(+), 55 deletions(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 297a48f..3a5d769 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -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
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index f23acc8..c39f1ad 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -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>
@@ -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 {
@@ -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;
+};
+
/****************************************************************************
****************************************************************************
*
@@ -3236,6 +3246,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");
@@ -3262,7 +3305,7 @@ 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");
@@ -3270,8 +3313,9 @@ static int light_read(char *p)
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");
}
@@ -3281,31 +3325,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 = {
@@ -3709,6 +3744,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 */
@@ -3717,6 +3758,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");
@@ -3742,7 +3846,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)
{
@@ -3758,11 +3864,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));
}
}
@@ -3772,16 +3878,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;
@@ -3791,38 +3892,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;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 163/171] ACPI: thinkpad-acpi: add sysfs led class support for thinklight (v3.1)
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (34 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 162/171] ACPI: thinkpad-acpi: prepare light and LED for sysfs support Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 164/171] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.1) Len Brown
` (6 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Richard Purdie, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Add a sysfs led class interface to the thinklight (light subdriver).
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/laptops/thinkpad-acpi.txt | 25 +++++++++++---
drivers/misc/thinkpad_acpi.c | 57 ++++++++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 0f403ff..22c1ac4 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -692,16 +692,31 @@ while others are still having problems. For more information:
https://bugs.freedesktop.org/show_bug.cgi?id=2000
-ThinkLight control -- /proc/acpi/ibm/light
-------------------------------------------
+ThinkLight control
+------------------
+
+procfs: /proc/acpi/ibm/light
+sysfs attributes: as per led class, for the "tpacpi::thinklight" led
+
+procfs notes:
-The current status of the ThinkLight can be found in this file. A few
-models which do not make the status available will show it as
-"unknown". The available commands are:
+The ThinkLight status can be read and set through the procfs interface. A
+few models which do not make the status available will show the ThinkLight
+status as "unknown". The available commands are:
echo on > /proc/acpi/ibm/light
echo off > /proc/acpi/ibm/light
+sysfs notes:
+
+The ThinkLight sysfs interface is documented by the led class
+documentation, in Documentation/leds-class.txt. The ThinkLight led name
+is "tpacpi::thinklight".
+
+Due to limitations in the sysfs led class, if the status of the thinklight
+cannot be read or if it is unknown, thinkpad-acpi will report it as "off".
+It is impossible to know if the status returned through sysfs is valid.
+
Docking / undocking -- /proc/acpi/ibm/dock
------------------------------------------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index c39f1ad..f647dc7 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -3279,13 +3279,49 @@ static int light_set_status(int status)
return -ENXIO;
}
+static void light_set_status_worker(struct work_struct *work)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(work, struct tpacpi_led_classdev, work);
+
+ if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+ light_set_status((data->new_brightness != LED_OFF));
+}
+
+static void light_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(led_cdev,
+ struct tpacpi_led_classdev,
+ led_classdev);
+ data->new_brightness = brightness;
+ schedule_work(&data->work);
+}
+
+static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
+{
+ return (light_get_status() == 1)? LED_FULL : LED_OFF;
+}
+
+static struct tpacpi_led_classdev tpacpi_led_thinklight = {
+ .led_classdev = {
+ .name = "tpacpi::thinklight",
+ .brightness_set = &light_sysfs_set,
+ .brightness_get = &light_sysfs_get,
+ }
+};
+
static int __init light_init(struct ibm_init_struct *iibm)
{
+ int rc = 0;
+
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
TPACPI_ACPIHANDLE_INIT(ledb);
TPACPI_ACPIHANDLE_INIT(lght);
TPACPI_ACPIHANDLE_INIT(cmos);
+ INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
@@ -3299,7 +3335,25 @@ static int __init light_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
str_supported(tp_features.light));
- return (tp_features.light)? 0 : 1;
+ if (tp_features.light) {
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_led_thinklight.led_classdev);
+ }
+
+ if (rc < 0) {
+ tp_features.light = 0;
+ tp_features.light_status = 0;
+ } else {
+ rc = (tp_features.light)? 0 : 1;
+ }
+ return rc;
+}
+
+static void light_exit(void)
+{
+ led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
+ if (work_pending(&tpacpi_led_thinklight.work))
+ flush_scheduled_work();
}
static int light_read(char *p)
@@ -3347,6 +3401,7 @@ static struct ibm_struct light_driver_data = {
.name = "light",
.read = light_read,
.write = light_write,
+ .exit = light_exit,
};
/*************************************************************************
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 164/171] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.1)
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (35 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 163/171] ACPI: thinkpad-acpi: add sysfs led class support for thinklight (v3.1) Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 165/171] ACPI: thinkpad-acpi: fluff really minor fix Len Brown
` (5 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Richard Purdie, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Add a sysfs led class interface to the led subdriver.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/laptops/thinkpad-acpi.txt | 47 +++++++++--
drivers/misc/thinkpad_acpi.c | 136 ++++++++++++++++++++++++++++++-
2 files changed, 176 insertions(+), 7 deletions(-)
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 22c1ac4..27ca0f9 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -875,28 +875,63 @@ The cmos command interface is prone to firmware split-brain problems, as
in newer ThinkPads it is just a compatibility layer. Do not use it, it is
exported just as a debug tool.
-LED control -- /proc/acpi/ibm/led
----------------------------------
+LED control
+-----------
-Some of the LED indicators can be controlled through this feature. The
-available commands are:
+procfs: /proc/acpi/ibm/led
+sysfs attributes: as per led class, see below for names
+
+Some of the LED indicators can be controlled through this feature. On
+some older ThinkPad models, it is possible to query the status of the
+LED indicators as well. Newer ThinkPads cannot query the real status
+of the LED indicators.
+
+procfs notes:
+
+The available commands are:
echo '<led number> on' >/proc/acpi/ibm/led
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
-controlled varies from model to model. Here is the mapping on the X40:
+controlled varies from model to model. Here is the common ThinkPad
+mapping:
0 - power
1 - battery (orange)
2 - battery (green)
- 3 - UltraBase
+ 3 - UltraBase/dock
4 - UltraBay
+ 5 - UltraBase battery slot
+ 6 - (unknown)
7 - standby
All of the above can be turned on and off and can be made to blink.
+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 to7):
+"tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
+"tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
+"tpacpi::unknown_led", "tpacpi::standby".
+
+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
+a brightness of zero (same as LED off).
+
+If the thinkpad firmware doesn't support reading the current status,
+trying to read the current LED brightness will just return whatever
+brightness was last written to that attribute.
+
+These LEDs can blink using hardware acceleration. To request that a
+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).
+
ACPI sounds -- /proc/acpi/ibm/beep
----------------------------------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index f647dc7..547324e 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -277,6 +277,7 @@ struct tpacpi_led_classdev {
struct led_classdev led_classdev;
struct work_struct work;
enum led_brightness new_brightness;
+ unsigned int led;
};
/****************************************************************************
@@ -3813,20 +3814,38 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
"LED", /* all others */
); /* R30, R31 */
+#define TPACPI_LED_NUMLEDS 8
+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] = {
+ /* there's a limit of 19 chars + NULL before 2.6.26 */
+ "tpacpi::power",
+ "tpacpi:orange:batt",
+ "tpacpi:green:batt",
+ "tpacpi::dock_active",
+ "tpacpi::bay_active",
+ "tpacpi::dock_batt",
+ "tpacpi::unknown_led",
+ "tpacpi::standby",
+};
+
static int led_get_status(unsigned int led)
{
int status;
+ enum led_status_t led_s;
switch (led_supported) {
case TPACPI_LED_570:
if (!acpi_evalf(ec_handle,
&status, "GLED", "dd", 1 << led))
return -EIO;
- return (status == 0)?
+ led_s = (status == 0)?
TPACPI_LED_OFF :
((status == 1)?
TPACPI_LED_ON :
TPACPI_LED_BLINK);
+ tpacpi_led_state_cache[led] = led_s;
+ return led_s;
default:
return -ENXIO;
}
@@ -3873,11 +3892,96 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus)
rc = -ENXIO;
}
+ if (!rc)
+ tpacpi_led_state_cache[led] = ledstatus;
+
return rc;
}
+static void led_sysfs_set_status(unsigned int led,
+ enum led_brightness brightness)
+{
+ led_set_status(led,
+ (brightness == LED_OFF) ?
+ TPACPI_LED_OFF :
+ (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
+ TPACPI_LED_BLINK : TPACPI_LED_ON);
+}
+
+static void led_set_status_worker(struct work_struct *work)
+{
+ struct tpacpi_led_classdev *data =
+ container_of(work, struct tpacpi_led_classdev, work);
+
+ if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+ led_sysfs_set_status(data->led, data->new_brightness);
+}
+
+static void led_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ data->new_brightness = brightness;
+ schedule_work(&data->work);
+}
+
+static int led_sysfs_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ /* Can we choose the flash rate? */
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* yes. set them to the hardware blink rate (1 Hz) */
+ *delay_on = 500; /* ms */
+ *delay_off = 500; /* ms */
+ } else if ((*delay_on != 500) || (*delay_off != 500))
+ return -EINVAL;
+
+ data->new_brightness = TPACPI_LED_BLINK;
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
+{
+ int rc;
+
+ struct tpacpi_led_classdev *data = container_of(led_cdev,
+ struct tpacpi_led_classdev, led_classdev);
+
+ rc = led_get_status(data->led);
+
+ if (rc == TPACPI_LED_OFF || rc < 0)
+ rc = LED_OFF; /* no error handling in led class :( */
+ else
+ rc = LED_FULL;
+
+ return rc;
+}
+
+static void led_exit(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ if (tpacpi_leds[i].led_classdev.name)
+ led_classdev_unregister(&tpacpi_leds[i].led_classdev);
+ }
+
+ kfree(tpacpi_leds);
+ tpacpi_leds = NULL;
+}
+
static int __init led_init(struct ibm_init_struct *iibm)
{
+ unsigned int i;
+ int rc;
+
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
TPACPI_ACPIHANDLE_INIT(led);
@@ -3898,6 +4002,35 @@ 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);
+ 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;
+ }
+
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ tpacpi_leds[i].led = i;
+
+ tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
+ tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
+ if (led_supported == TPACPI_LED_570)
+ tpacpi_leds[i].led_classdev.brightness_get =
+ &led_sysfs_get;
+
+ tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
+
+ INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
+
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_leds[i].led_classdev);
+ if (rc < 0) {
+ tpacpi_leds[i].led_classdev.name = NULL;
+ led_exit();
+ return rc;
+ }
+ }
+
return (led_supported != TPACPI_LED_NONE)? 0 : 1;
}
@@ -3968,6 +4101,7 @@ static struct ibm_struct led_driver_data = {
.name = "led",
.read = led_read,
.write = led_write,
+ .exit = led_exit,
};
/*************************************************************************
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 165/171] ACPI: thinkpad-acpi: fluff really minor fix
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (36 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 164/171] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.1) Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 166/171] ACPI: thinkpad-acpi: use a private workqueue Len Brown
` (4 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Fix a minor (nano?) thing that bothered me at exactly at the wrong time.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 547324e..0d05719 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -3880,7 +3880,7 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus)
led * led_exp_hlbl[ledstatus]);
if (rc >= 0)
rc = ec_write(TPACPI_LED_EC_HLCL,
- led * led_exp_hlcl[ledstatus]);
+ led * led_exp_hlcl[ledstatus]);
break;
case TPACPI_LED_NEW:
/* all others */
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 166/171] ACPI: thinkpad-acpi: use a private workqueue
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (37 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 165/171] ACPI: thinkpad-acpi: fluff really minor fix Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 167/171] ACPI: thinkpad-acpi: fix selects in Kconfig Len Brown
` (3 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Switch all task workers to a private thinkpad-acpi workqueue.
This way, we don't risk causing trouble for other tasks scheduled to the
default work queue, as our workers end up needing to access the ACPI EC,
run ACPI AML code, trigger SMI traps... and none of those are exactly known
to be fast, simple operations.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 26 +++++++++++++++++++-------
1 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 0d05719..22ba93c 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -140,6 +140,7 @@ enum {
#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
+#define TPACPI_WORKQUEUE_NAME "ktpacpid"
#define TPACPI_MAX_ACPI_ARGS 3
@@ -272,6 +273,8 @@ static enum {
static int experimental;
static u32 dbg_level;
+static struct workqueue_struct *tpacpi_wq;
+
/* Special LED class that can defer work */
struct tpacpi_led_classdev {
struct led_classdev led_classdev;
@@ -3297,7 +3300,7 @@ static void light_sysfs_set(struct led_classdev *led_cdev,
struct tpacpi_led_classdev,
led_classdev);
data->new_brightness = brightness;
- schedule_work(&data->work);
+ queue_work(tpacpi_wq, &data->work);
}
static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
@@ -3354,7 +3357,7 @@ static void light_exit(void)
{
led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
if (work_pending(&tpacpi_led_thinklight.work))
- flush_scheduled_work();
+ flush_workqueue(tpacpi_wq);
}
static int light_read(char *p)
@@ -3924,7 +3927,7 @@ static void led_sysfs_set(struct led_classdev *led_cdev,
struct tpacpi_led_classdev, led_classdev);
data->new_brightness = brightness;
- schedule_work(&data->work);
+ queue_work(tpacpi_wq, &data->work);
}
static int led_sysfs_blink_set(struct led_classdev *led_cdev,
@@ -3942,7 +3945,7 @@ static int led_sysfs_blink_set(struct led_classdev *led_cdev,
return -EINVAL;
data->new_brightness = TPACPI_LED_BLINK;
- schedule_work(&data->work);
+ queue_work(tpacpi_wq, &data->work);
return 0;
}
@@ -5407,11 +5410,11 @@ static void fan_watchdog_reset(void)
if (fan_watchdog_maxinterval > 0 &&
tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
fan_watchdog_active = 1;
- if (!schedule_delayed_work(&fan_watchdog_task,
+ if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
msecs_to_jiffies(fan_watchdog_maxinterval
* 1000))) {
printk(TPACPI_ERR
- "failed to schedule the fan watchdog, "
+ "failed to queue the fan watchdog, "
"watchdog will not trigger\n");
}
} else
@@ -5781,7 +5784,7 @@ static void fan_exit(void)
&driver_attr_fan_watchdog);
cancel_delayed_work(&fan_watchdog_task);
- flush_scheduled_work();
+ flush_workqueue(tpacpi_wq);
}
static int fan_read(char *p)
@@ -6435,6 +6438,9 @@ static void thinkpad_acpi_module_exit(void)
if (proc_dir)
remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
+ if (tpacpi_wq)
+ destroy_workqueue(tpacpi_wq);
+
kfree(thinkpad_id.bios_version_str);
kfree(thinkpad_id.ec_version_str);
kfree(thinkpad_id.model_str);
@@ -6465,6 +6471,12 @@ static int __init thinkpad_acpi_module_init(void)
TPACPI_ACPIHANDLE_INIT(ecrd);
TPACPI_ACPIHANDLE_INIT(ecwr);
+ tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
+ if (!tpacpi_wq) {
+ thinkpad_acpi_module_exit();
+ return -ENOMEM;
+ }
+
proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
if (!proc_dir) {
printk(TPACPI_ERR
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 167/171] ACPI: thinkpad-acpi: fix selects in Kconfig
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (38 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 166/171] ACPI: thinkpad-acpi: use a private workqueue Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 168/171] ACPI: thinkpad-acpi: bump up version to 0.20 Len Brown
` (2 subsequent siblings)
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Add missing select for BACKLIGHT_LCD_SUPPORT, as select doesn't select the
dependencies of a symbol for us.
Also, "select INPUT" in Kconfig. We are not an Input device, nor are we
anywhere close to the input subsystem in the Kconfig tree, so using
"depends on INPUT" is not user-friendly at all.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/Kconfig | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3a5d769..2e21794 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -241,10 +241,11 @@ config SONYPI_COMPAT
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on X86 && ACPI
+ select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
- depends on INPUT
+ select INPUT
select NEW_LEDS
select LEDS_CLASS
---help---
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 168/171] ACPI: thinkpad-acpi: bump up version to 0.20
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (39 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 167/171] ACPI: thinkpad-acpi: fix selects in Kconfig Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 170/171] ACPI: check a return value correctly in acpi_power_get_context() Len Brown
2008-04-29 8:44 ` [PATCH 171/171] thinkpad_acpi: fix possible NULL pointer dereference if kstrdup failed Len Brown
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Henrique de Moraes Holschuh, Len Brown
From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Full LED sysfs support, and the rest of the assorted minor fixes and
enhancements are a good reason to checkpoint a new version...
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
---
Documentation/laptops/thinkpad-acpi.txt | 4 ++--
drivers/misc/thinkpad_acpi.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 27ca0f9..a2ea504 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -1,7 +1,7 @@
ThinkPad ACPI Extras Driver
- Version 0.19
- January 06th, 2008
+ Version 0.20
+ April 09th, 2008
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 22ba93c..76e3ae3 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -21,7 +21,7 @@
* 02110-1301, USA.
*/
-#define TPACPI_VERSION "0.19"
+#define TPACPI_VERSION "0.20"
#define TPACPI_SYSFS_VERSION 0x020200
/*
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 170/171] ACPI: check a return value correctly in acpi_power_get_context()
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (40 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 168/171] ACPI: thinkpad-acpi: bump up version to 0.20 Len Brown
@ 2008-04-29 8:44 ` Len Brown
2008-04-29 8:44 ` [PATCH 171/171] thinkpad_acpi: fix possible NULL pointer dereference if kstrdup failed Len Brown
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Li Zefan, Andrew Morton, Len Brown
From: Li Zefan <lizf@cn.fujitsu.com>
We should check *resource != NULL rather than resource != NULL, which will be
always true.
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/power.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 76bf6d9..f2a76ac 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -121,7 +121,7 @@ acpi_power_get_context(acpi_handle handle,
}
*resource = acpi_driver_data(device);
- if (!resource)
+ if (!*resource)
return -ENODEV;
return 0;
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH 171/171] thinkpad_acpi: fix possible NULL pointer dereference if kstrdup failed
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
` (41 preceding siblings ...)
2008-04-29 8:44 ` [PATCH 170/171] ACPI: check a return value correctly in acpi_power_get_context() Len Brown
@ 2008-04-29 8:44 ` Len Brown
42 siblings, 0 replies; 54+ messages in thread
From: Len Brown @ 2008-04-29 8:44 UTC (permalink / raw)
To: linux-acpi; +Cc: Cyrill Gorcunov, Andrew Morton, Len Brown
From: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/misc/thinkpad_acpi.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 6cb7812..31115c9 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -5826,7 +5826,7 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION),
GFP_KERNEL);
- if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
+ if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
kfree(tp->model_str);
tp->model_str = NULL;
}
--
1.5.5.1.99.gf0ec4
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 10:25 ` Zhao Yakui
@ 2008-04-30 6:58 ` Carlos Corbacho
2008-04-30 7:17 ` Carlos Corbacho
2008-04-30 14:53 ` Henrique de Moraes Holschuh
0 siblings, 2 replies; 54+ messages in thread
From: Carlos Corbacho @ 2008-04-30 6:58 UTC (permalink / raw)
To: Zhao Yakui; +Cc: Alexey Starikovskiy, linux-acpi, Len Brown, Len Brown
On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
> On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> > From: Alexey Starikovskiy <astarikovskiy@suse.de>
> >
> > GPE could try to access EC region, so should not be enabled before
> > EC is installed
>
> It is caused by broken bios if GPE method tries to access EC region
> before EC is initialized correctly. It is more appropriate to fix this
> issue by upgrading bios rather than by the patch.
> And this patch will cause some potential problems.
1) Does the BIOS in question work under Windows?
2) Does the system in question require a BIOS upgrade under Windows for this
issue?
If the answer to both of those is 'no', then the answer is that it's a Linux
bug, and a patch of some description is appropriate.
Just because there are crappy BIOS's out there, and we'd rather not put in
nasty fixes, doesn't mean that we shouldn't.
-Carlos
--
E-Mail: carlos@strangeworlds.co.uk
Web: strangeworlds.co.uk
GPG Key ID: 0x23EE722D
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 6:58 ` Carlos Corbacho
@ 2008-04-30 7:17 ` Carlos Corbacho
2008-04-30 16:43 ` Zhao Yakui
2008-04-30 14:53 ` Henrique de Moraes Holschuh
1 sibling, 1 reply; 54+ messages in thread
From: Carlos Corbacho @ 2008-04-30 7:17 UTC (permalink / raw)
To: Zhao Yakui; +Cc: Alexey Starikovskiy, linux-acpi, Len Brown, Len Brown
On Wednesday 30 April 2008 07:58:53 Carlos Corbacho wrote:
> On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
> > On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> > > From: Alexey Starikovskiy <astarikovskiy@suse.de>
> > >
> > > GPE could try to access EC region, so should not be enabled before
> > > EC is installed
> >
> > It is caused by broken bios if GPE method tries to access EC region
> > before EC is initialized correctly. It is more appropriate to fix this
> > issue by upgrading bios rather than by the patch.
> > And this patch will cause some potential problems.
>
> 1) Does the BIOS in question work under Windows?
^^^
that should be 'not work'
> 2) Does the system in question require a BIOS upgrade under Windows for
> this issue?
>
> If the answer to both of those is 'no', then the answer is that it's a
> Linux bug, and a patch of some description is appropriate.
>
> Just because there are crappy BIOS's out there, and we'd rather not put in
> nasty fixes, doesn't mean that we shouldn't.
-Carlos
--
E-Mail: carlos@strangeworlds.co.uk
Web: strangeworlds.co.uk
GPG Key ID: 0x23EE722D
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 16:43 ` Zhao Yakui
@ 2008-04-30 10:04 ` Alexey Starikovskiy
2008-05-04 10:28 ` Zhao Yakui
0 siblings, 1 reply; 54+ messages in thread
From: Alexey Starikovskiy @ 2008-04-30 10:04 UTC (permalink / raw)
To: Zhao Yakui; +Cc: Carlos Corbacho, linux-acpi, Len Brown, Len Brown
Zhao Yakui wrote:
> On Wed, 2008-04-30 at 08:17 +0100, Carlos Corbacho wrote:
>> On Wednesday 30 April 2008 07:58:53 Carlos Corbacho wrote:
>>> On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
>>>> On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
>>>>> From: Alexey Starikovskiy <astarikovskiy@suse.de>
>>>>>
>>>>> GPE could try to access EC region, so should not be enabled before
>>>>> EC is installed
>>>> It is caused by broken bios if GPE method tries to access EC region
>>>> before EC is initialized correctly. It is more appropriate to fix this
>>>> issue by upgrading bios rather than by the patch.
>>>> And this patch will cause some potential problems.
>>> 1) Does the BIOS in question work under Windows?
>> ^^^
>> that should be 'not work'
> For the laptop in bug 9916:
> Windows can work on the BIOS in question. In fact only warning message
> is printed for Linux and it won't break anything. (Maybe the warning
> message also exists on windows. But we can't see them.)
This is generally a stupid idea to enable interrupt before you have handler for it ready,
even if you are able to _survive_ unhandled interrupt.
Your other issue about WAKE GPEs, which could be wrongly enabled, could be solved by
moving WAKE GPE init closer to RUNTIME GPE one:
Regards,
Alex.
diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c
index 361ebe6..37c68ae 100644
--- a/drivers/acpi/events/evgpeblk.c
+++ b/drivers/acpi/events/evgpeblk.c
@@ -940,6 +940,27 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
acpi_ev_save_method_info, gpe_block,
NULL);
+ /*
+ * Runtime option: Should wake GPEs be enabled at runtime? The default
+ * is no, they should only be enabled just as the machine goes to sleep.
+ */
+ if (acpi_gbl_leave_wake_gpes_disabled) {
+ /*
+ * Differentiate runtime vs wake GPEs, via the _PRW control methods.
+ * Each GPE that has one or more _PRWs that reference it is by
+ * definition a wake GPE and will not be enabled while the machine
+ * is running.
+ */
+ gpe_info.gpe_block = gpe_block;
+ gpe_info.gpe_device = gpe_device;
+
+ status =
+ acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
+ acpi_ev_match_prw_and_gpe, &gpe_info,
+ NULL);
+ }
+
/* Return the new block */
if (return_gpe_block) {
@@ -995,27 +1016,6 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
}
/*
- * Runtime option: Should wake GPEs be enabled at runtime? The default
- * is no, they should only be enabled just as the machine goes to sleep.
- */
- if (acpi_gbl_leave_wake_gpes_disabled) {
- /*
- * Differentiate runtime vs wake GPEs, via the _PRW control methods.
- * Each GPE that has one or more _PRWs that reference it is by
- * definition a wake GPE and will not be enabled while the machine
- * is running.
- */
- gpe_info.gpe_block = gpe_block;
- gpe_info.gpe_device = gpe_device;
-
- status =
- acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
- acpi_ev_match_prw_and_gpe, &gpe_info,
- NULL);
- }
-
- /*
* Enable all GPEs in this block that have these attributes:
* 1) are "runtime" or "run/wake" GPEs, and
* 2) have a corresponding _Lxx or _Exx method
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-29 8:44 ` [PATCH 091/171] ACPI: GPE enabling should happen after EC installation Len Brown
@ 2008-04-30 10:25 ` Zhao Yakui
2008-04-30 6:58 ` Carlos Corbacho
0 siblings, 1 reply; 54+ messages in thread
From: Zhao Yakui @ 2008-04-30 10:25 UTC (permalink / raw)
Cc: linux-acpi, Alexey Starikovskiy, Len Brown, Len Brown
On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> From: Alexey Starikovskiy <astarikovskiy@suse.de>
>
> GPE could try to access EC region, so should not be enabled before
> EC is installed
It is caused by broken bios if GPE method tries to access EC region
before EC is initialized correctly. It is more appropriate to fix this
issue by upgrading bios rather than by the patch.
And this patch will cause some potential problems.
In the course of GPE event initialization the GPE will be set as
RUN_TIME and deferred to be enabled.(Only update the GPE enable mask and
not write the GPE register). And when the acpi_ev_install_fadt_gpes is
called, some GPEs will be changed from the RUN_TIME to WAKEUP and only
RUNTIME GPEs are enabled.(GPE register will be accessed).
If acpi_ev_install_fadt_gpe is called after EC is stalled, the
acpi_enable_gpe will be called , in which some GPEs will be also
enabled. In fact these are the WAKUP GPEs and shouldn't be enabled.
Maybe this will bring some potential problems.
Best regards.
Yakui
> http://bugzilla.kernel.org/show_bug.cgi?id=9916
>
> Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
> Signed-off-by: Len Brown <len.brown@intel.com>
> ---
> drivers/acpi/utilities/utxface.c | 35 +++++++++++++++++------------------
> 1 files changed, 17 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c
> index 2d49691..df41312 100644
> --- a/drivers/acpi/utilities/utxface.c
> +++ b/drivers/acpi/utilities/utxface.c
> @@ -192,24 +192,6 @@ acpi_status acpi_enable_subsystem(u32 flags)
> }
> }
>
> - /*
> - * Complete the GPE initialization for the GPE blocks defined in the FADT
> - * (GPE block 0 and 1).
> - *
> - * Note1: This is where the _PRW methods are executed for the GPEs. These
> - * methods can only be executed after the SCI and Global Lock handlers are
> - * installed and initialized.
> - *
> - * Note2: Currently, there seems to be no need to run the _REG methods
> - * before execution of the _PRW methods and enabling of the GPEs.
> - */
> - if (!(flags & ACPI_NO_EVENT_INIT)) {
> - status = acpi_ev_install_fadt_gpes();
> - if (ACPI_FAILURE(status)) {
> - return (status);
> - }
> - }
> -
> return_ACPI_STATUS(status);
> }
>
> @@ -280,6 +262,23 @@ acpi_status acpi_initialize_objects(u32 flags)
> }
>
> /*
> + * Complete the GPE initialization for the GPE blocks defined in the FADT
> + * (GPE block 0 and 1).
> + *
> + * Note1: This is where the _PRW methods are executed for the GPEs. These
> + * methods can only be executed after the SCI and Global Lock handlers are
> + * installed and initialized.
> + *
> + * Note2: Currently, there seems to be no need to run the _REG methods
> + * before execution of the _PRW methods and enabling of the GPEs.
> + */
> + if (!(flags & ACPI_NO_EVENT_INIT)) {
> + status = acpi_ev_install_fadt_gpes();
> + if (ACPI_FAILURE(status))
> + return (status);
> + }
> +
> + /*
> * Empty the caches (delete the cached objects) on the assumption that
> * the table load filled them up more than they will be at runtime --
> * thus wasting non-paged memory.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 6:58 ` Carlos Corbacho
2008-04-30 7:17 ` Carlos Corbacho
@ 2008-04-30 14:53 ` Henrique de Moraes Holschuh
1 sibling, 0 replies; 54+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-04-30 14:53 UTC (permalink / raw)
To: Carlos Corbacho
Cc: Zhao Yakui, Alexey Starikovskiy, linux-acpi, Len Brown, Len Brown
On Wed, 30 Apr 2008, Carlos Corbacho wrote:
> On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
> > On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> > > From: Alexey Starikovskiy <astarikovskiy@suse.de>
> > >
> > > GPE could try to access EC region, so should not be enabled before
> > > EC is installed
> >
> > It is caused by broken bios if GPE method tries to access EC region
> > before EC is initialized correctly. It is more appropriate to fix this
> > issue by upgrading bios rather than by the patch.
> > And this patch will cause some potential problems.
>
> 1) Does the BIOS in question work under Windows?
> 2) Does the system in question require a BIOS upgrade under Windows for this
> issue?
>
> If the answer to both of those is 'no', then the answer is that it's a Linux
> bug, and a patch of some description is appropriate.
3) Did the vendor issue an O.S. "driver workaround"? If so, it means we
add a *quirk*, and not that we hose ACPI for everyone else.
> Just because there are crappy BIOS's out there, and we'd rather not put in
> nasty fixes, doesn't mean that we shouldn't.
But it means we need to limit the damage these nasty fixes can do to
others.
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 7:17 ` Carlos Corbacho
@ 2008-04-30 16:43 ` Zhao Yakui
2008-04-30 10:04 ` Alexey Starikovskiy
0 siblings, 1 reply; 54+ messages in thread
From: Zhao Yakui @ 2008-04-30 16:43 UTC (permalink / raw)
To: Carlos Corbacho; +Cc: Alexey Starikovskiy, linux-acpi, Len Brown, Len Brown
On Wed, 2008-04-30 at 08:17 +0100, Carlos Corbacho wrote:
> On Wednesday 30 April 2008 07:58:53 Carlos Corbacho wrote:
> > On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
> > > On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> > > > From: Alexey Starikovskiy <astarikovskiy@suse.de>
> > > >
> > > > GPE could try to access EC region, so should not be enabled before
> > > > EC is installed
> > >
> > > It is caused by broken bios if GPE method tries to access EC region
> > > before EC is initialized correctly. It is more appropriate to fix this
> > > issue by upgrading bios rather than by the patch.
> > > And this patch will cause some potential problems.
> >
> > 1) Does the BIOS in question work under Windows?
>
> ^^^
> that should be 'not work'
For the laptop in bug 9916:
Windows can work on the BIOS in question. In fact only warning message
is printed for Linux and it won't break anything. (Maybe the warning
message also exists on windows. But we can't see them.)
>
> > 2) Does the system in question require a BIOS upgrade under Windows for
> > this issue?
> >
After the BIOS is upgraded, the warning message disappears on Linux. It
is very lucky that we have such a laptop. After the bios is upgraded,
the warning message disappears.
> > If the answer to both of those is 'no', then the answer is that it's a
> > Linux bug, and a patch of some description is appropriate.
> >
> > Just because there are crappy BIOS's out there, and we'd rather not put in
> > nasty fixes, doesn't mean that we shouldn't.
> -Carlos
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-05-04 10:28 ` Zhao Yakui
@ 2008-05-04 6:40 ` Alexey Starikovskiy
2008-05-04 16:22 ` Zhao Yakui
0 siblings, 1 reply; 54+ messages in thread
From: Alexey Starikovskiy @ 2008-05-04 6:40 UTC (permalink / raw)
To: Zhao Yakui; +Cc: Carlos Corbacho, linux-acpi, Len Brown, Len Brown
Zhao Yakui wrote:
> But after the patch is applied, it seems that we make a lot of changes
> about the GPE initialization flowchart in ACPICA.
And this flowchart is cast in stone?
> Will these be easily accepted by ACPICA author?
You should probably ask him, not me. We are talking Bob Moore here, yes?
Why don't you add CC to him if you are worried?
> At the same time some RUNTIME GPEs will be enabled twice. When
> acpi_enable_gpe is called in the course of EC installation, some RUNTIME
> GPEs will also be enabled. In fact when the acpi_ev_install_fadt_gpes is
> called, all the RUNTIME GPEs will be enabled. In such case it brings
> that some RUNTIME GPEs are enabled twice. Of course the patch should
> work fine if it is safe to enable RUNTTME GPEs twice.
Please advise me, how we are not enabling them twice if EC init happens after?
The same acpi_enable_gpe will be called from it.
Regards,
Alex.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-04-30 10:04 ` Alexey Starikovskiy
@ 2008-05-04 10:28 ` Zhao Yakui
2008-05-04 6:40 ` Alexey Starikovskiy
0 siblings, 1 reply; 54+ messages in thread
From: Zhao Yakui @ 2008-05-04 10:28 UTC (permalink / raw)
To: Alexey Starikovskiy; +Cc: Carlos Corbacho, linux-acpi, Len Brown, Len Brown
On Wed, 2008-04-30 at 14:04 +0400, Alexey Starikovskiy wrote:
> Zhao Yakui wrote:
> > On Wed, 2008-04-30 at 08:17 +0100, Carlos Corbacho wrote:
> >> On Wednesday 30 April 2008 07:58:53 Carlos Corbacho wrote:
> >>> On Wednesday 30 April 2008 11:25:34 Zhao Yakui wrote:
> >>>> On Tue, 2008-04-29 at 04:44 -0400, Len Brown wrote:
> >>>>> From: Alexey Starikovskiy <astarikovskiy@suse.de>
> >>>>>
> >>>>> GPE could try to access EC region, so should not be enabled before
> >>>>> EC is installed
> >>>> It is caused by broken bios if GPE method tries to access EC region
> >>>> before EC is initialized correctly. It is more appropriate to fix this
> >>>> issue by upgrading bios rather than by the patch.
> >>>> And this patch will cause some potential problems.
> >>> 1) Does the BIOS in question work under Windows?
> >> ^^^
> >> that should be 'not work'
> > For the laptop in bug 9916:
> > Windows can work on the BIOS in question. In fact only warning message
> > is printed for Linux and it won't break anything. (Maybe the warning
> > message also exists on windows. But we can't see them.)
> This is generally a stupid idea to enable interrupt before you have handler for it ready,
> even if you are able to _survive_ unhandled interrupt.
>
> Your other issue about WAKE GPEs, which could be wrongly enabled, could be solved by
> moving WAKE GPE init closer to RUNTIME GPE one:
Thanks for the so quick response.
The following patch can solve the issue about the WAKE GPEs.
But after the patch is applied, it seems that we make a lot of changes
about the GPE initialization flowchart in ACPICA. Will these be easily
accepted by ACPICA author?
At the same time some RUNTIME GPEs will be enabled twice. When
acpi_enable_gpe is called in the course of EC installation, some RUNTIME
GPEs will also be enabled. In fact when the acpi_ev_install_fadt_gpes is
called, all the RUNTIME GPEs will be enabled. In such case it brings
that some RUNTIME GPEs are enabled twice. Of course the patch should
work fine if it is safe to enable RUNTTME GPEs twice.
Best regards.
Yakui
> Regards,
> Alex.
>
>
>
> diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c
> index 361ebe6..37c68ae 100644
> --- a/drivers/acpi/events/evgpeblk.c
> +++ b/drivers/acpi/events/evgpeblk.c
> @@ -940,6 +940,27 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
> acpi_ev_save_method_info, gpe_block,
> NULL);
>
> + /*
> + * Runtime option: Should wake GPEs be enabled at runtime? The default
> + * is no, they should only be enabled just as the machine goes to sleep.
> + */
> + if (acpi_gbl_leave_wake_gpes_disabled) {
> + /*
> + * Differentiate runtime vs wake GPEs, via the _PRW control methods.
> + * Each GPE that has one or more _PRWs that reference it is by
> + * definition a wake GPE and will not be enabled while the machine
> + * is running.
> + */
> + gpe_info.gpe_block = gpe_block;
> + gpe_info.gpe_device = gpe_device;
> +
> + status =
> + acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> + ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
> + acpi_ev_match_prw_and_gpe, &gpe_info,
> + NULL);
> + }
> +
> /* Return the new block */
>
> if (return_gpe_block) {
> @@ -995,27 +1016,6 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
> }
>
> /*
> - * Runtime option: Should wake GPEs be enabled at runtime? The default
> - * is no, they should only be enabled just as the machine goes to sleep.
> - */
> - if (acpi_gbl_leave_wake_gpes_disabled) {
> - /*
> - * Differentiate runtime vs wake GPEs, via the _PRW control methods.
> - * Each GPE that has one or more _PRWs that reference it is by
> - * definition a wake GPE and will not be enabled while the machine
> - * is running.
> - */
> - gpe_info.gpe_block = gpe_block;
> - gpe_info.gpe_device = gpe_device;
> -
> - status =
> - acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> - ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
> - acpi_ev_match_prw_and_gpe, &gpe_info,
> - NULL);
> - }
> -
> - /*
> * Enable all GPEs in this block that have these attributes:
> * 1) are "runtime" or "run/wake" GPEs, and
> * 2) have a corresponding _Lxx or _Exx method
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH 091/171] ACPI: GPE enabling should happen after EC installation
2008-05-04 6:40 ` Alexey Starikovskiy
@ 2008-05-04 16:22 ` Zhao Yakui
0 siblings, 0 replies; 54+ messages in thread
From: Zhao Yakui @ 2008-05-04 16:22 UTC (permalink / raw)
To: Alexey Starikovskiy
Cc: Carlos Corbacho, linux-acpi, Len Brown, Len Brown, robert.moore,
Lin, Ming M
On Sun, 2008-05-04 at 10:40 +0400, Alexey Starikovskiy wrote:
> Zhao Yakui wrote:
> > But after the patch is applied, it seems that we make a lot of changes
> > about the GPE initialization flowchart in ACPICA.
> And this flowchart is cast in stone?
After the two patches are applied, the issue in bug9916 can be fixed.
But in fact the issue in bug9916 is related with bios.
In the L02 method the _PSR object of AC will be accessed.
> Method (_PSR, 0, NotSerialized)
> {
> Return (HPAC)
> }
HPAC is defined in EC OperationRegion. IMO Whether the HPAC can be
accessed should be determined by the flag of H8DR, which indicates
whether the EC operation region can be accessed. (1 means that EC
opregion can be accessed. 0 means that EC opregion can't be accessed.)
It seems that this is an obvious bios bug. It is more appropriate to
fix this issue by upgrading bios.
IMO it is unnecessary to make a lot of changes about GPE
initialization flowchart for the obvious bios bug.
At the same time it is very luck that there exists ECDT table on the
laptop of bug9916. And after the two patches are applied, the GPE can be
enabled after EC installation. But if the ECDT table doesn't exist(EC
device has no _INI object in DSDT table), it seems that GPE enabling
still happens before EC installation.
> > Will these be easily accepted by ACPICA author?
> You should probably ask him, not me. We are talking Bob Moore here, yes?
> Why don't you add CC to him if you are worried?
CC Bob and Lin Ming to see this issue.
> > At the same time some RUNTIME GPEs will be enabled twice. When
> > acpi_enable_gpe is called in the course of EC installation, some RUNTIME
> > GPEs will also be enabled. In fact when the acpi_ev_install_fadt_gpes is
> > called, all the RUNTIME GPEs will be enabled. In such case it brings
> > that some RUNTIME GPEs are enabled twice. Of course the patch should
> > work fine if it is safe to enable RUNTTME GPEs twice.
> Please advise me, how we are not enabling them twice if EC init happens after?
> The same acpi_enable_gpe will be called from it.
>
> Regards,
> Alex.
^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2008-05-04 8:31 UTC | newest]
Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-29 8:43 ACPI patches for 2.6.26 Len Brown
2008-04-29 8:43 ` [PATCH 001/171] ACPI: crosslink ACPI and "real" device nodes Len Brown
2008-04-29 8:43 ` [PATCH 002/171] ACPI: EC: Restore udelay in poll mode Len Brown
2008-04-29 8:43 ` [PATCH 003/171] ACPI: EC: Add poll timer Len Brown
2008-04-29 8:43 ` [PATCH 004/171] ACPI: EC: Improve debug output Len Brown
2008-04-29 8:43 ` [PATCH 005/171] ACPI: EC: Replace broken controller workarounds with poll mode Len Brown
2008-04-29 8:43 ` [PATCH 006/171] ACPI: EC: Switch off GPE mode during suspend/resume Len Brown
2008-04-29 8:43 ` [PATCH 007/171] ACPI: EC: Detect irq storm Len Brown
2008-04-29 8:43 ` [PATCH 008/171] ACPI: EC: Use default setup handler Len Brown
2008-04-29 8:44 ` [PATCH 009/171] ACPI: EC: Don't delete boot EC Len Brown
2008-04-29 8:44 ` [PATCH 010/171] ACPI : Disable the device's ability to wake the sleeping system in the boot phase Len Brown
2008-04-29 8:44 ` [PATCH 011/171] #if 0 acpi/bay.c:eject_removable_drive() Len Brown
2008-04-29 8:44 ` [PATCH 012/171] PM: Remove legacy PM Len Brown
2008-04-29 8:44 ` [PATCH 013/171] PM: arch/x86/kernel/apm_32.c: fix build warning Len Brown
2008-04-29 8:44 ` [PATCH 014/171] MIPS Alchemy: Crapectomy after removal of pm_send_all calls Len Brown
2008-04-29 8:44 ` [PATCH 085/171] eeepc-laptop: add base driver Len Brown
2008-04-29 8:44 ` [PATCH 086/171] eeepc-laptop: add backlight Len Brown
2008-04-29 8:44 ` [PATCH 087/171] eeepc-laptop: add hwmon fan control Len Brown
2008-04-29 8:44 ` [PATCH 088/171] eeepc-laptop: MAINTAINERS entry Len Brown
2008-04-29 8:44 ` [PATCH 089/171] ACPI: re-name acpi_pm_ops to acpi_suspend_ops Len Brown
2008-04-29 8:44 ` [PATCH 091/171] ACPI: GPE enabling should happen after EC installation Len Brown
2008-04-30 10:25 ` Zhao Yakui
2008-04-30 6:58 ` Carlos Corbacho
2008-04-30 7:17 ` Carlos Corbacho
2008-04-30 16:43 ` Zhao Yakui
2008-04-30 10:04 ` Alexey Starikovskiy
2008-05-04 10:28 ` Zhao Yakui
2008-05-04 6:40 ` Alexey Starikovskiy
2008-05-04 16:22 ` Zhao Yakui
2008-04-30 14:53 ` Henrique de Moraes Holschuh
2008-04-29 8:44 ` [PATCH 092/171] 2.6.25 regression: powertop says 120K wakeups/sec Len Brown
2008-04-29 8:44 ` [PATCH 093/171] flush kacpi_notify_wq before removing notify handler Len Brown
2008-04-29 8:44 ` [PATCH 094/171] thermal: add the support for building the generic thermal as a module Len Brown
2008-04-29 8:44 ` [PATCH 095/171] thermal: add new get_crit_temp callback Len Brown
2008-04-29 8:44 ` [PATCH 096/171] thermal: add hwmon sysfs I/F Len Brown
2008-04-29 8:44 ` [PATCH 097/171] thermal: update the documentation Len Brown
2008-04-29 8:44 ` [PATCH 098/171] ACPI: elide a non-zero test on a result that is never 0 Len Brown
2008-04-29 8:44 ` [PATCH 099/171] ACPI: update thermal temperature Len Brown
2008-04-29 8:44 ` [PATCH 100/171] thermal: re-name thermal.c to thermal_sys.c Len Brown
2008-04-29 8:44 ` [PATCH 156/171] ACPI: thinkpad-acpi: BIOS backlight mode helper (v2) Len Brown
2008-04-29 8:44 ` [PATCH 157/171] ACPI: thinkpad-acpi: warn once about weird hotkey masks Len Brown
2008-04-29 8:44 ` [PATCH 158/171] ACPI: thinkpad-acpi: enhance box identification output Len Brown
2008-04-29 8:44 ` [PATCH 159/171] ACPI: thinkpad-acpi: rate-limit CMOS/EC unsynced error messages Len Brown
2008-04-29 8:44 ` [PATCH 160/171] ACPI: thinkpad-acpi: fix brightness dimming control bug Len Brown
2008-04-29 8:44 ` [PATCH 161/171] ACPI: thinkpad-acpi: claim tpacpi as an official short handle Len Brown
2008-04-29 8:44 ` [PATCH 162/171] ACPI: thinkpad-acpi: prepare light and LED for sysfs support Len Brown
2008-04-29 8:44 ` [PATCH 163/171] ACPI: thinkpad-acpi: add sysfs led class support for thinklight (v3.1) Len Brown
2008-04-29 8:44 ` [PATCH 164/171] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.1) Len Brown
2008-04-29 8:44 ` [PATCH 165/171] ACPI: thinkpad-acpi: fluff really minor fix Len Brown
2008-04-29 8:44 ` [PATCH 166/171] ACPI: thinkpad-acpi: use a private workqueue Len Brown
2008-04-29 8:44 ` [PATCH 167/171] ACPI: thinkpad-acpi: fix selects in Kconfig Len Brown
2008-04-29 8:44 ` [PATCH 168/171] ACPI: thinkpad-acpi: bump up version to 0.20 Len Brown
2008-04-29 8:44 ` [PATCH 170/171] ACPI: check a return value correctly in acpi_power_get_context() Len Brown
2008-04-29 8:44 ` [PATCH 171/171] thinkpad_acpi: fix possible NULL pointer dereference if kstrdup failed Len Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).