GIT 419a29d0995dae97ffea2e9f2f2cac506cbfe915 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git#test commit 53df8c9b60006d158ff7f373d225b0939bf7dead Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:36 2007 +0400 ACPI: Battery: add sysfs alarm Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 524914fa372351ef9e8a5248907dadaf76415b6e Author: Alexey Starikovskiy Date: Thu Sep 13 18:57:15 2007 +0400 ACPI: Battery: Add sysfs support Refer to Documentation/power_supply_class.txt for interface description. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit c3e3f92238d90e205bff8c42e3c838238ea29c95 Author: Alexey Starikovskiy Date: Sat Sep 1 00:10:05 2007 +0400 ACPI: Battery: Misc clean-ups, no functional changes Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 387dcee1d2f672983f6428c31e90ac4cb92609b2 Author: Alexey Starikovskiy Date: Sat Sep 1 00:09:59 2007 +0400 ACPI: Battery: simplify update scheme Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 301529654653b35d5fdf2c6665b606e1a4f7cb8c Author: Alexey Starikovskiy Date: Sat Sep 1 00:09:53 2007 +0400 ACPI: Battery: don't use acpi_extract_package() acpi_extract_package() creates more problems with memory management than solves as helper for package handling. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 2af8b3c13cb94fa9aac779a922afe9cd4a676ca4 Author: Alexey Starikovskiy Date: Sat Sep 1 00:09:19 2007 +0400 ACPI: AC: Add sysfs interface Refer to Documentation/power_supply_class.txt for interface description. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 2369cc9492a462285f9eec9d2bbfa730cc2ab5ac Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:07 2007 -0300 ACPI: thinkpad-acpi: duplicate driver attributes to new hwmon pdrv Thinkpad-acpi has some driver attributes (debug level, sysfs interface version, etc) that also belong to the new hwmon driver. Duplicate them there. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 7fd400297978a2cf7a74344fb22020e9479b4f69 Author: Henrique de Moraes Holschuh Date: Tue Sep 25 06:38:03 2007 -0300 ACPI: thinkpad-acpi: use a separate platform device for hwmon and name it (v2) Use a separate platform device and driver ("thinkpad_hwmon") to attach hwmon attributes and class, and add a name attribute of "thinkpad" to it, which defines the hwmon device name for libsensors4. This makes thinkpad-acpi compatible with libsensors4 from lm-sensors, and the platform driver and device split will make it much easier to separate hwmon functionality into its own module later on. Signed-off-by: Henrique de Moraes Holschuh Cc: Jean Delvare Signed-off-by: Len Brown commit 3e5ce914bd17335ca74a7c7b06a776c6be6ca434 Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:05 2007 -0300 ACPI: thinkpad-acpi: fix regression on HKEY LID event handling We were letting ThinkPad-specific LID events through to userspace again, instead of dropping them. Fix it. We don't want to give userspace the option of not using generic LID handling. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 3eea123df1637a88d0899626a67b83dca959efff Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:04 2007 -0300 ACPI: thinkpad-acpi: dequeue all pending hot key events at once (v2.2) Receive all pending HKEY events at once from a single notification, and don't complain if the queue is empty. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 1b6521dc84f372dd92a96381fbeeebb01173d050 Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:03 2007 -0300 ACPI: thinkpad-acpi: check version of hot key firmware Check the HKEY firmware version (HKEY.MHKV handler), and refuse to load if it is unknown. Use this instead of the presence of HKEY.DHKV to detect hot key mask capability. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 8fef502e5a14df05f1e755edc9175e01c9814080 Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:02 2007 -0300 ACPI: thinkpad-acpi: keep track of module state Keep track of module state (init, running, exit). This makes it trivially easy to avoid running any interrupt handlers, threads, or any other async activity before we are ready, or when we want to go away. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 8523ed6fb2ca04973fe759fda8ab4af72492fc7e Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:01 2007 -0300 ACPI: thinkpad-acpi: add mutex-based locking to input device event send path Protect the input device event sending path with a mutex, since hot key input events are not atomic and require an cohesive event block to be sent together. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit f4e1e43c607b5ead89b2135c348392810420de69 Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:39:00 2007 -0300 ACPI: thinkpad-acpi: issue EV_SYNC after EV_SWITCH We were missing a input_sync on the radio switch event report path. Add it. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 4b2fe7e2a79727104e549a89a32b4aae26521861 Author: Henrique de Moraes Holschuh Date: Sun Sep 23 11:38:59 2007 -0300 ACPI: thinkpad-acpi: make room for more features in tp_features bitfield Increase tp_features to 32 bits. It is too close to running out of room. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown commit 03e2bf261e832abf48d40113ce08a70338dd18c9 Author: Andreas Herrmann Date: Mon Sep 17 23:41:05 2007 +0200 acpi_video: kernel build error if !INPUT Commit e9dab1960ac9746fa34eff726b81635147615a79 (ACPI video hotkey: export missing ACPI video hotkey events via input layer) exports ACPI video hotkey events via input layer. But this breaks kernel build if ACPI_VIDEO && !INPUT: LD .tmp_vmlinux1 drivers/built-in.o: In function `acpi_video_bus_remove': drivers/acpi/video.c:2007: undefined reference to `input_unregister_device' ... Signed-off-by: Andreas Herrmann Signed-off-by: Len Brown commit 8a66074c379cacc79168681bf7dea37ad278f5d1 Author: Andreas Herrmann Date: Mon Sep 17 22:54:34 2007 +0200 sony-laptop/thinkpad-acpi: fix INPUT=n build Build errors if CONFIG_SONY_LAPTOP && !INPUT or if CONFIG_THINKPAD_ACPI && !INPUT: LD vmlinux ... drivers/built-in.o: In function `sony_laptop_remove_input': sony-laptop.c:(.text+0x768fb): undefined reference to `input_unregister_device' ... drivers/built-in.o: In function `thinkpad_acpi_module_exit': thinkpad_acpi.c:(.text+0x78c1b): undefined reference to `input_free_device' ... Signed-off-by: Andreas Herrmann Acked-by: Henrique de Moraes Holschuh Acked-by: Mattia Dongili Signed-off-by: Len Brown commit 4c611060660f0de3e9b8f02df207312bc6f5c331 Author: Alexey Starikovskiy Date: Wed Sep 5 19:56:38 2007 -0400 ACPI: EC: Drop ECDT-based boot_ec as soon as we find DSDT-based one. ASUS notebooks have numerous problems with EC initialization This patch tries to work around three known issues reported in bugzilla 8598, 8709 and 8909/8919. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 63f0edfc0b7f8058f9d3f9b572615ec97ae011ba Author: Alexey Starikovskiy Date: Mon Sep 3 16:30:08 2007 +0400 ACPI: VIDEO: Adjust current level to closest available one. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 4500ca8e221e72cc38e7c239b9b1a041031ee450 Author: Alexey Starikovskiy Date: Mon Sep 3 16:29:58 2007 +0400 ACPI: video: Don't call absent methods Signed-off-by: Ryan May Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 6e2157858ac94530fddbf19dc59ab6b392baf1f3 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:59 2007 +0400 ACPI: Thermal: Drop concurrent thermal checks Fix for #3686, where get_temperature() may cause thermal notify, which causes one more get_temperature(). Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit f127e3766686c2d525d930fb2a707cc90df9e2f1 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:43 2007 +0400 ACPI: SBS: Add sysfs alarm Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 041a0232f1e89dfa7cffed74a6cbb121032c36f8 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:30 2007 +0400 ACPI: SBS: Add ACPI_PROCFS around procfs handling code. Make procfs support optional under ACPI_PROCFS Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit fa5e2338bbbe17700781e6fdabf6cc158667c987 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:24 2007 +0400 ACPI: SBS: Add support for power_supply class (and sysfs) Add support for power_supply class and sysfs interface of it. Refer to Documentation/power_supply_class.txt for interface description. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 011c6997c0b54067d89fde31b4cb5b01792afcd3 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:17 2007 +0400 ACPI: SBS: Make SBS reads table-driven. Re-factor SBS functions to use tables and cycles for repeated operations. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 86b80c709d723d55844097e589f2e58d39857d21 Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:10 2007 +0400 ACPI: SBS: Simplify data structures in SBS Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit f0ebb0c2228ebb95b1dc400bcc05d6fe873e2b3f Author: Alexey Starikovskiy Date: Sat Sep 1 00:11:04 2007 +0400 ACPI: SBS: Split host controller (ACPI0001) from SBS driver (ACPI0002) Replace poll-based host controller driver with the notify-based one. Split it out of sbs.c. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit e59812330152303aff434d2d3a8a2d9340fbb66e Author: Alexey Starikovskiy Date: Sat Sep 1 00:10:58 2007 +0400 ACPI: EC: Add new query handler to list head. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit cb7ce92d4707dcac07c934f794195e39bd08221f Author: Alexey Starikovskiy Date: Sat Sep 1 00:10:51 2007 +0400 ACPI: Add acpi_bus_generate_event4() function acpi_bus_generate_event() takes two strings out of passed device object. SBS needs to supply these strings directly. Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown commit 21bc42ab852549f4a547d18d77e0e4d1b24ffd96 Author: Len Brown Date: Tue Sep 4 12:49:22 2007 -0400 ACPI: thermal: use round_jiffies when thermal zone polling is enabled Properly functioning systems do not use thermal zone polling, they use event-based notification. However, some users enable periodic thermal zone polling to work around bugs on their platforms, and at least one platform exists with a real _TZP that requests polling. While thermal zone polling (_TZP) is specified in units to 0.1 seconds, it actually has a maximum granularity of 1 second. Thus, we can safely round up the _TZP timeout to occur on the next 1-second boundary. This will batch it with other 1-second-granularity timers in the system and thus potentially extend processor idle duration. Note that the same timer is used both for _TZP and for passive processor thermal throttling. We can not round up the timeout when it is used for passive thermal throttling. Also, we can not make this a deferrable timer, as temperature is just as relevant during idle as it is during non-idle. Signed-off-by: Len Brown commit d0482533c73a8685f7ce0951a10280cfd58b8825 Author: Jonathan Woithe Date: Wed Aug 29 15:58:19 2007 +0930 fujitsu-laptop: create Fujitsu laptop platform specific driver Signed-off-by: Jonathan Woithe Signed-off-by: Len Brown commit fd1caaed466de2ee100e250b6c755376eda7ba3b Author: Mattia Dongili Date: Sun Aug 12 16:20:28 2007 +0900 sony-laptop: old Vaio models contain 2 IO port entries Make the driver aware of this case and manage the existence of a second separate IO port. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown Documentation/thinkpad-acpi.txt | 25 - drivers/acpi/Kconfig | 1 drivers/acpi/Makefile | 1 drivers/acpi/ac.c | 49 + drivers/acpi/battery.c | 1056 +++++++++------------- drivers/acpi/bus.c | 23 drivers/acpi/ec.c | 93 +- drivers/acpi/sbs.c | 1894 ++++++++++++--------------------------- drivers/acpi/sbshc.c | 309 ++++++ drivers/acpi/sbshc.h | 27 + drivers/acpi/thermal.c | 28 - drivers/acpi/video.c | 31 - drivers/misc/Kconfig | 17 drivers/misc/Makefile | 1 drivers/misc/fujitsu-laptop.c | 358 +++++++ drivers/misc/sony-laptop.c | 204 +++- drivers/misc/thinkpad_acpi.c | 205 +++- drivers/misc/thinkpad_acpi.h | 37 - include/acpi/acpi_bus.h | 1 19 files changed, 2231 insertions(+), 2129 deletions(-) diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 60953d6..3b95bba 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt @@ -105,10 +105,15 @@ The version of thinkpad-acpi's sysfs int as a driver attribute (see below). Sysfs driver attributes are on the driver's sysfs attribute space, -for 2.6.20 this is /sys/bus/platform/drivers/thinkpad_acpi/. +for 2.6.23 this is /sys/bus/platform/drivers/thinkpad_acpi/ and +/sys/bus/platform/drivers/thinkpad_hwmon/ -Sysfs device attributes are on the driver's sysfs attribute space, -for 2.6.20 this is /sys/devices/platform/thinkpad_acpi/. +Sysfs device attributes are on the thinkpad_acpi device sysfs attribute +space, for 2.6.23 this is /sys/devices/platform/thinkpad_acpi/. + +Sysfs device attributes for the sensors and fan are on the +thinkpad_hwmon device's sysfs attribute space, but you should locate it +looking for a hwmon device with the name attribute of "thinkpad". Driver version -------------- @@ -766,7 +771,7 @@ Temperature sensors ------------------- procfs: /proc/acpi/ibm/thermal -sysfs device attributes: (hwmon) temp*_input +sysfs device attributes: (hwmon "thinkpad") temp*_input Most ThinkPads include six or more separate temperature sensors but only expose the CPU temperature through the standard ACPI methods. This @@ -989,7 +994,9 @@ Fan control and monitoring: fan speed, f --------------------------------------------------------- procfs: /proc/acpi/ibm/fan -sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable +sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1, + pwm1_enable +sysfs hwmon driver attributes: fan_watchdog NOTE NOTE NOTE: fan control operations are disabled by default for safety reasons. To enable them, the module parameter "fan_control=1" @@ -1131,7 +1138,7 @@ hwmon device attribute fan1_input: which can take up to two minutes. May return rubbish on older ThinkPads. -driver attribute fan_watchdog: +hwmon driver attribute fan_watchdog: Fan safety watchdog timer interval, in seconds. Minimum is 1 second, maximum is 120 seconds. 0 disables the watchdog. @@ -1233,3 +1240,9 @@ Sysfs interface changelog: layer, the radio switch generates input event EV_RADIO, and the driver enables hot key handling by default in the firmware. + +0x020000: ABI fix: added a separate hwmon platform device and + driver, which must be located by name (thinkpad) + and the hwmon class for libsensors4 (lm-sensors 3) + compatibility. Moved all hwmon attributes to this + new platform device. diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 4875f01..9685b75 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -117,6 +117,7 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL + depends on INPUT help This driver implement the ACPI Extensions For Display Adapters for integrated graphics devices on motherboard, as specified in diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d4336f1..54e3ab0 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_ac obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += sbs.o +obj-$(CONFIG_ACPI_SBS) += sbshc.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 26d7070..a08ecbb 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -29,6 +29,12 @@ #include #include #include #include + +#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE) +#define ENABLE_POWER_SUPPLY +#include +#endif + #include #include @@ -72,16 +78,42 @@ static struct acpi_driver acpi_ac_driver }; struct acpi_ac { +#ifdef ENABLE_POWER_SUPPLY + struct power_supply charger; +#endif struct acpi_device * device; unsigned long state; }; +#define to_acpi_ac(x) container_of(x, struct acpi_ac, charger); + static const struct file_operations acpi_ac_fops = { .open = acpi_ac_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; +#ifdef ENABLE_POWER_SUPPLY +static int get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct acpi_ac *ac = to_acpi_ac(psy); + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ac->state; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +#endif /* -------------------------------------------------------------------------- AC Adapter Management @@ -208,6 +240,9 @@ static void acpi_ac_notify(acpi_handle h acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, (u32) ac->state); +#ifdef ENABLE_POWER_SUPPLY + kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); +#endif break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -244,7 +279,14 @@ static int acpi_ac_add(struct acpi_devic result = acpi_ac_add_fs(device); if (result) goto end; - +#ifdef ENABLE_POWER_SUPPLY + ac->charger.name = acpi_device_bid(device); + ac->charger.type = POWER_SUPPLY_TYPE_MAINS; + ac->charger.properties = ac_props; + ac->charger.num_properties = ARRAY_SIZE(ac_props); + ac->charger.get_property = get_ac_property; + power_supply_register(&ac->device->dev, &ac->charger); +#endif status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify, ac); @@ -279,7 +321,10 @@ static int acpi_ac_remove(struct acpi_de status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify); - +#ifdef ENABLE_POWER_SUPPLY + if (ac->charger.dev) + power_supply_unregister(&ac->charger); +#endif acpi_ac_remove_fs(device); kfree(ac); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9b2c0f7..4864694 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1,6 +1,8 @@ /* - * acpi_battery.c - ACPI Battery Driver ($Revision: 37 $) + * battery.c - ACPI Battery Driver (Revision: 2.0) * + * Copyright (C) 2007 Alexey Starikovskiy + * Copyright (C) 2004-2007 Vladimir Lebedev * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh * @@ -27,244 +29,295 @@ #include #include #include #include +#include + +#ifdef CONFIG_ACPI_PROCFS #include #include #include +#endif #include #include -#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF +#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE) +#define ENABLE_POWER_SUPPLY +#include +#endif -#define ACPI_BATTERY_FORMAT_BIF "NNNNNNNNNSSSS" -#define ACPI_BATTERY_FORMAT_BST "NNNN" +#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF #define ACPI_BATTERY_COMPONENT 0x00040000 #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 #define ACPI_BATTERY_NOTIFY_INFO 0x81 -#define ACPI_BATTERY_UNITS_WATTS "mW" -#define ACPI_BATTERY_UNITS_AMPS "mA" #define _COMPONENT ACPI_BATTERY_COMPONENT -#define ACPI_BATTERY_UPDATE_TIME 0 - -#define ACPI_BATTERY_NONE_UPDATE 0 -#define ACPI_BATTERY_EASY_UPDATE 1 -#define ACPI_BATTERY_INIT_UPDATE 2 - ACPI_MODULE_NAME("battery"); MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_AUTHOR("Alexey Starikovskiy "); MODULE_DESCRIPTION("ACPI Battery Driver"); MODULE_LICENSE("GPL"); -static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME; - -/* 0 - every time, > 0 - by update_time */ -module_param(update_time, uint, 0644); +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); +#ifdef CONFIG_ACPI_PROCFS extern struct proc_dir_entry *acpi_lock_battery_dir(void); extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); -static int acpi_battery_add(struct acpi_device *device); -static int acpi_battery_remove(struct acpi_device *device, int type); -static int acpi_battery_resume(struct acpi_device *device); +enum acpi_battery_files { + info_tag = 0, + state_tag, + alarm_tag, + ACPI_BATTERY_NUMFILES, +}; + +#endif static const struct acpi_device_id battery_device_ids[] = { {"PNP0C0A", 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, battery_device_ids); - -static struct acpi_driver acpi_battery_driver = { - .name = "battery", - .class = ACPI_BATTERY_CLASS, - .ids = battery_device_ids, - .ops = { - .add = acpi_battery_add, - .resume = acpi_battery_resume, - .remove = acpi_battery_remove, - }, -}; -struct acpi_battery_state { - acpi_integer state; - acpi_integer present_rate; - acpi_integer remaining_capacity; - acpi_integer present_voltage; -}; - -struct acpi_battery_info { - acpi_integer power_unit; - acpi_integer design_capacity; - acpi_integer last_full_capacity; - acpi_integer battery_technology; - acpi_integer design_voltage; - acpi_integer design_capacity_warning; - acpi_integer design_capacity_low; - acpi_integer battery_capacity_granularity_1; - acpi_integer battery_capacity_granularity_2; - acpi_string model_number; - acpi_string serial_number; - acpi_string battery_type; - acpi_string oem_info; -}; - -enum acpi_battery_files{ - ACPI_BATTERY_INFO = 0, - ACPI_BATTERY_STATE, - ACPI_BATTERY_ALARM, - ACPI_BATTERY_NUMFILES, -}; +MODULE_DEVICE_TABLE(acpi, battery_device_ids); -struct acpi_battery_flags { - u8 battery_present_prev; - u8 alarm_present; - u8 init_update; - u8 update[ACPI_BATTERY_NUMFILES]; - u8 power_unit; -}; struct acpi_battery { - struct mutex mutex; + struct mutex lock; +#ifdef ENABLE_POWER_SUPPLY + struct power_supply bat; +#endif struct acpi_device *device; - struct acpi_battery_flags flags; - struct acpi_buffer bif_data; - struct acpi_buffer bst_data; - unsigned long alarm; - unsigned long update_time[ACPI_BATTERY_NUMFILES]; + unsigned long update_time; + int current_now; + int capacity_now; + int voltage_now; + int design_capacity; + int full_charge_capacity; + int technology; + int design_voltage; + int design_capacity_warning; + int design_capacity_low; + int capacity_granularity_1; + int capacity_granularity_2; + int alarm; + char model_number[32]; + char serial_number[32]; + char type[32]; + char oem_info[32]; + int state; + int power_unit; + u8 alarm_present; }; +#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); + inline int acpi_battery_present(struct acpi_battery *battery) { return battery->device->status.battery_present; } -inline char *acpi_battery_power_units(struct acpi_battery *battery) -{ - if (battery->flags.power_unit) - return ACPI_BATTERY_UNITS_AMPS; - else - return ACPI_BATTERY_UNITS_WATTS; -} -inline acpi_handle acpi_battery_handle(struct acpi_battery *battery) +#ifdef ENABLE_POWER_SUPPLY +static int acpi_battery_technology(struct acpi_battery *battery) { - return battery->device->handle; + if (!strcasecmp("NiCd", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_NiCd; + if (!strcasecmp("NiMH", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_NiMH; + if (!strcasecmp("LION", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_LION; + if (!strcasecmp("LiP", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_LIPO; + return POWER_SUPPLY_TECHNOLOGY_UNKNOWN; } -/* -------------------------------------------------------------------------- - Battery Management - -------------------------------------------------------------------------- */ - -static void acpi_battery_check_result(struct acpi_battery *battery, int result) +static int acpi_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { - if (!battery) - return; + struct acpi_battery *battery = to_acpi_battery(psy); - if (result) { - battery->flags.init_update = 1; + if ((!acpi_battery_present(battery)) && + psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (battery->state & 0x01) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (battery->state & 0x02) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (battery->state == 0) + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = acpi_battery_present(battery); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = acpi_battery_technology(battery); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = battery->design_voltage * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery->voltage_now * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = battery->current_now * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = battery->design_capacity * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = battery->full_charge_capacity * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = battery->capacity_now * 1000; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = battery->model_number; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = battery->oem_info; + break; + default: + return -EINVAL; } + return 0; } -static int acpi_battery_extract_package(struct acpi_battery *battery, - union acpi_object *package, - struct acpi_buffer *format, - struct acpi_buffer *data, - char *package_name) +static enum power_supply_property charge_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property energy_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; +#endif + +#ifdef CONFIG_ACPI_PROCFS +inline char *acpi_battery_units(struct acpi_battery *battery) { - acpi_status status = AE_OK; - struct acpi_buffer data_null = { 0, NULL }; + return (battery->power_unit)?"mA":"mW"; +} +#endif - status = acpi_extract_package(package, format, &data_null); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s", - package_name)); - return -ENODEV; - } +/* -------------------------------------------------------------------------- + Battery Management + -------------------------------------------------------------------------- */ +struct acpi_offsets { + size_t offset; /* offset inside struct acpi_sbs_battery */ + u8 mode; /* int or string? */ +}; - if (data_null.length != data->length) { - kfree(data->pointer); - data->pointer = kzalloc(data_null.length, GFP_KERNEL); - if (!data->pointer) { - ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()")); - return -ENOMEM; - } - data->length = data_null.length; - } +static struct acpi_offsets state_offsets[] = { + {offsetof(struct acpi_battery, state), 0}, + {offsetof(struct acpi_battery, current_now), 0}, + {offsetof(struct acpi_battery, capacity_now), 0}, + {offsetof(struct acpi_battery, voltage_now), 0}, +}; - status = acpi_extract_package(package, format, data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting %s", - package_name)); - return -ENODEV; - } +static struct acpi_offsets info_offsets[] = { + {offsetof(struct acpi_battery, power_unit), 0}, + {offsetof(struct acpi_battery, design_capacity), 0}, + {offsetof(struct acpi_battery, full_charge_capacity), 0}, + {offsetof(struct acpi_battery, technology), 0}, + {offsetof(struct acpi_battery, design_voltage), 0}, + {offsetof(struct acpi_battery, design_capacity_warning), 0}, + {offsetof(struct acpi_battery, design_capacity_low), 0}, + {offsetof(struct acpi_battery, capacity_granularity_1), 0}, + {offsetof(struct acpi_battery, capacity_granularity_2), 0}, + {offsetof(struct acpi_battery, model_number), 1}, + {offsetof(struct acpi_battery, serial_number), 1}, + {offsetof(struct acpi_battery, type), 1}, + {offsetof(struct acpi_battery, oem_info), 1}, +}; +static int extract_package(struct acpi_battery *battery, + union acpi_object *package, + struct acpi_offsets *offsets, int num) +{ + int i, *x; + union acpi_object *element; + if (package->type != ACPI_TYPE_PACKAGE) + return -EFAULT; + for (i = 0; i < num; ++i) { + if (package->package.count <= i) + return -EFAULT; + element = &package->package.elements[i]; + if (offsets[i].mode) { + if (element->type != ACPI_TYPE_STRING && + element->type != ACPI_TYPE_BUFFER) + return -EFAULT; + strncpy((u8 *)battery + offsets[i].offset, + element->string.pointer, 32); + } else { + if (element->type != ACPI_TYPE_INTEGER) + return -EFAULT; + x = (int *)((u8 *)battery + offsets[i].offset); + *x = element->integer.value; + } + } return 0; } static int acpi_battery_get_status(struct acpi_battery *battery) { - int result = 0; - - result = acpi_bus_get_status(battery->device); - if (result) { + if (acpi_bus_get_status(battery->device)) { ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA")); return -ENODEV; } - return result; + return 0; } static int acpi_battery_get_info(struct acpi_battery *battery) { - int result = 0; + int result = -EFAULT; acpi_status status = 0; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF), - ACPI_BATTERY_FORMAT_BIF - }; - union acpi_object *package = NULL; - struct acpi_buffer *data = NULL; - struct acpi_battery_info *bif = NULL; - - battery->update_time[ACPI_BATTERY_INFO] = get_seconds(); if (!acpi_battery_present(battery)) return 0; + mutex_lock(&battery->lock); + status = acpi_evaluate_object(battery->device->handle, "_BIF", + NULL, &buffer); + mutex_unlock(&battery->lock); - /* Evaluate _BIF */ - - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL, - &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF")); return -ENODEV; } - package = buffer.pointer; - - data = &battery->bif_data; - - /* Extract Package Data */ - - result = - acpi_battery_extract_package(battery, package, &format, data, - "_BIF"); - if (result) - goto end; - - end: - + result = extract_package(battery, buffer.pointer, + info_offsets, ARRAY_SIZE(info_offsets)); kfree(buffer.pointer); - - if (!result) { - bif = data->pointer; - battery->flags.power_unit = bif->power_unit; - } - return result; } @@ -273,342 +326,205 @@ static int acpi_battery_get_state(struct int result = 0; acpi_status status = 0; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST), - ACPI_BATTERY_FORMAT_BST - }; - union acpi_object *package = NULL; - struct acpi_buffer *data = NULL; - - battery->update_time[ACPI_BATTERY_STATE] = get_seconds(); if (!acpi_battery_present(battery)) return 0; - /* Evaluate _BST */ + if (battery->update_time && + time_before(jiffies, battery->update_time + + msecs_to_jiffies(cache_time))) + return 0; + + mutex_lock(&battery->lock); + status = acpi_evaluate_object(battery->device->handle, "_BST", + NULL, &buffer); + mutex_unlock(&battery->lock); - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL, - &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); return -ENODEV; } - package = buffer.pointer; - - data = &battery->bst_data; - - /* Extract Package Data */ - - result = - acpi_battery_extract_package(battery, package, &format, data, - "_BST"); - if (result) - goto end; - - end: + result = extract_package(battery, buffer.pointer, + state_offsets, ARRAY_SIZE(state_offsets)); + battery->update_time = jiffies; kfree(buffer.pointer); - return result; } -static int acpi_battery_get_alarm(struct acpi_battery *battery) -{ - battery->update_time[ACPI_BATTERY_ALARM] = get_seconds(); - - return 0; -} - -static int acpi_battery_set_alarm(struct acpi_battery *battery, - unsigned long alarm) +static int acpi_battery_set_alarm(struct acpi_battery *battery) { acpi_status status = 0; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg0 }; - battery->update_time[ACPI_BATTERY_ALARM] = get_seconds(); - - if (!acpi_battery_present(battery)) + if (!acpi_battery_present(battery)|| !battery->alarm_present) return -ENODEV; - if (!battery->flags.alarm_present) - return -ENODEV; - - arg0.integer.value = alarm; + arg0.integer.value = battery->alarm; - status = - acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", + mutex_lock(&battery->lock); + status = acpi_evaluate_object(battery->device->handle, "_BTP", &arg_list, NULL); + mutex_unlock(&battery->lock); + if (ACPI_FAILURE(status)) return -ENODEV; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm)); - - battery->alarm = alarm; - + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm)); return 0; } static int acpi_battery_init_alarm(struct acpi_battery *battery) { - int result = 0; acpi_status status = AE_OK; acpi_handle handle = NULL; - struct acpi_battery_info *bif = battery->bif_data.pointer; - unsigned long alarm = battery->alarm; /* See if alarms are supported, and if so, set default */ - - status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle); - if (ACPI_SUCCESS(status)) { - battery->flags.alarm_present = 1; - if (!alarm && bif) { - alarm = bif->design_capacity_warning; - } - result = acpi_battery_set_alarm(battery, alarm); - if (result) - goto end; - } else { - battery->flags.alarm_present = 0; + status = acpi_get_handle(battery->device->handle, "_BTP", &handle); + if (ACPI_FAILURE(status)) { + battery->alarm_present = 0; + return 0; } - - end: - - return result; + battery->alarm_present = 1; + if (!battery->alarm) + battery->alarm = battery->design_capacity_warning; + return acpi_battery_set_alarm(battery); } -static int acpi_battery_init_update(struct acpi_battery *battery) +static int acpi_battery_update(struct acpi_battery *battery) { - int result = 0; - - result = acpi_battery_get_status(battery); - if (result) + int saved_present = acpi_battery_present(battery); + int result = acpi_battery_get_status(battery); + if (result || !acpi_battery_present(battery)) return result; - - battery->flags.battery_present_prev = acpi_battery_present(battery); - - if (acpi_battery_present(battery)) { + if (saved_present != acpi_battery_present(battery) || + !battery->update_time) { + battery->update_time = 0; result = acpi_battery_get_info(battery); if (result) return result; - result = acpi_battery_get_state(battery); - if (result) - return result; - - acpi_battery_init_alarm(battery); - } - - return result; -} - -static int acpi_battery_update(struct acpi_battery *battery, - int update, int *update_result_ptr) -{ - int result = 0; - int update_result = ACPI_BATTERY_NONE_UPDATE; - - if (!acpi_battery_present(battery)) { - update = 1; - } - - if (battery->flags.init_update) { - result = acpi_battery_init_update(battery); - if (result) - goto end; - update_result = ACPI_BATTERY_INIT_UPDATE; - } else if (update) { - result = acpi_battery_get_status(battery); - if (result) - goto end; - if ((!battery->flags.battery_present_prev & acpi_battery_present(battery)) - || (battery->flags.battery_present_prev & !acpi_battery_present(battery))) { - result = acpi_battery_init_update(battery); - if (result) - goto end; - update_result = ACPI_BATTERY_INIT_UPDATE; +#ifdef ENABLE_POWER_SUPPLY + if (battery->power_unit) { + battery->bat.properties = charge_battery_props; + battery->bat.num_properties = + ARRAY_SIZE(charge_battery_props); } else { - update_result = ACPI_BATTERY_EASY_UPDATE; + battery->bat.properties = energy_battery_props; + battery->bat.num_properties = + ARRAY_SIZE(energy_battery_props); } +#endif + acpi_battery_init_alarm(battery); } - - end: - - battery->flags.init_update = (result != 0); - - *update_result_ptr = update_result; - - return result; -} - -static void acpi_battery_notify_update(struct acpi_battery *battery) -{ - acpi_battery_get_status(battery); - - if (battery->flags.init_update) { - return; - } - - if ((!battery->flags.battery_present_prev & - acpi_battery_present(battery)) || - (battery->flags.battery_present_prev & - !acpi_battery_present(battery))) { - battery->flags.init_update = 1; - } else { - battery->flags.update[ACPI_BATTERY_INFO] = 1; - battery->flags.update[ACPI_BATTERY_STATE] = 1; - battery->flags.update[ACPI_BATTERY_ALARM] = 1; - } + return acpi_battery_get_state(battery); } /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS static struct proc_dir_entry *acpi_battery_dir; static int acpi_battery_print_info(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; - struct acpi_battery_info *bif = NULL; - char *units = "?"; if (result) goto end; - if (acpi_battery_present(battery)) - seq_printf(seq, "present: yes\n"); - else { - seq_printf(seq, "present: no\n"); - goto end; - } - - bif = battery->bif_data.pointer; - if (!bif) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL")); - result = -ENODEV; + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery)?"yes":"no"); + if (!acpi_battery_present(battery)) goto end; - } - - /* Battery Units */ - - units = acpi_battery_power_units(battery); - - if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "design capacity: unknown\n"); else seq_printf(seq, "design capacity: %d %sh\n", - (u32) bif->design_capacity, units); + battery->design_capacity, + acpi_battery_units(battery)); - if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "last full capacity: unknown\n"); else seq_printf(seq, "last full capacity: %d %sh\n", - (u32) bif->last_full_capacity, units); + battery->full_charge_capacity, + acpi_battery_units(battery)); - switch ((u32) bif->battery_technology) { - case 0: - seq_printf(seq, "battery technology: non-rechargeable\n"); - break; - case 1: - seq_printf(seq, "battery technology: rechargeable\n"); - break; - default: - seq_printf(seq, "battery technology: unknown\n"); - break; - } + seq_printf(seq, "battery technology: %srechargeable\n", + (!battery->technology)?"non-":""); - if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "design voltage: unknown\n"); else seq_printf(seq, "design voltage: %d mV\n", - (u32) bif->design_voltage); + battery->design_voltage); seq_printf(seq, "design capacity warning: %d %sh\n", - (u32) bif->design_capacity_warning, units); + battery->design_capacity_warning, + acpi_battery_units(battery)); seq_printf(seq, "design capacity low: %d %sh\n", - (u32) bif->design_capacity_low, units); + battery->design_capacity_low, + acpi_battery_units(battery)); seq_printf(seq, "capacity granularity 1: %d %sh\n", - (u32) bif->battery_capacity_granularity_1, units); + battery->capacity_granularity_1, + acpi_battery_units(battery)); seq_printf(seq, "capacity granularity 2: %d %sh\n", - (u32) bif->battery_capacity_granularity_2, units); - seq_printf(seq, "model number: %s\n", bif->model_number); - seq_printf(seq, "serial number: %s\n", bif->serial_number); - seq_printf(seq, "battery type: %s\n", bif->battery_type); - seq_printf(seq, "OEM info: %s\n", bif->oem_info); - + battery->capacity_granularity_2, + acpi_battery_units(battery)); + seq_printf(seq, "model number: %s\n", battery->model_number); + seq_printf(seq, "serial number: %s\n", battery->serial_number); + seq_printf(seq, "battery type: %s\n", battery->type); + seq_printf(seq, "OEM info: %s\n", battery->oem_info); end: - if (result) seq_printf(seq, "ERROR: Unable to read battery info\n"); - return result; } static int acpi_battery_print_state(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; - struct acpi_battery_state *bst = NULL; - char *units = "?"; if (result) goto end; - if (acpi_battery_present(battery)) - seq_printf(seq, "present: yes\n"); - else { - seq_printf(seq, "present: no\n"); - goto end; - } - - bst = battery->bst_data.pointer; - if (!bst) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL")); - result = -ENODEV; + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery)?"yes":"no"); + if (!acpi_battery_present(battery)) goto end; - } - - /* Battery Units */ - - units = acpi_battery_power_units(battery); - - if (!(bst->state & 0x04)) - seq_printf(seq, "capacity state: ok\n"); - else - seq_printf(seq, "capacity state: critical\n"); - if ((bst->state & 0x01) && (bst->state & 0x02)) { + seq_printf(seq, "capacity state: %s\n", + (battery->state & 0x04)?"critical":"ok"); + if ((battery->state & 0x01) && (battery->state & 0x02)) seq_printf(seq, "charging state: charging/discharging\n"); - } else if (bst->state & 0x01) + else if (battery->state & 0x01) seq_printf(seq, "charging state: discharging\n"); - else if (bst->state & 0x02) + else if (battery->state & 0x02) seq_printf(seq, "charging state: charging\n"); - else { + else seq_printf(seq, "charging state: charged\n"); - } - if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN) + if (battery->current_now == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "present rate: unknown\n"); else seq_printf(seq, "present rate: %d %s\n", - (u32) bst->present_rate, units); + battery->current_now, acpi_battery_units(battery)); - if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "remaining capacity: unknown\n"); else seq_printf(seq, "remaining capacity: %d %sh\n", - (u32) bst->remaining_capacity, units); - - if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + battery->capacity_now, acpi_battery_units(battery)); + if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) seq_printf(seq, "present voltage: unknown\n"); else seq_printf(seq, "present voltage: %d mV\n", - (u32) bst->present_voltage); - + battery->voltage_now); end: - - if (result) { + if (result) seq_printf(seq, "ERROR: Unable to read battery state\n"); - } return result; } @@ -616,7 +532,6 @@ static int acpi_battery_print_state(stru static int acpi_battery_print_alarm(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; - char *units = "?"; if (result) goto end; @@ -625,189 +540,121 @@ static int acpi_battery_print_alarm(stru seq_printf(seq, "present: no\n"); goto end; } - - /* Battery Units */ - - units = acpi_battery_power_units(battery); - seq_printf(seq, "alarm: "); if (!battery->alarm) seq_printf(seq, "unsupported\n"); else - seq_printf(seq, "%lu %sh\n", battery->alarm, units); - + seq_printf(seq, "%u %sh\n", battery->alarm, + acpi_battery_units(battery)); end: - if (result) seq_printf(seq, "ERROR: Unable to read battery alarm\n"); - return result; } -static ssize_t -acpi_battery_write_alarm(struct file *file, - const char __user * buffer, - size_t count, loff_t * ppos) +static ssize_t acpi_battery_write_alarm(struct file *file, + const char __user * buffer, + size_t count, loff_t * ppos) { int result = 0; char alarm_string[12] = { '\0' }; struct seq_file *m = file->private_data; struct acpi_battery *battery = m->private; - int update_result = ACPI_BATTERY_NONE_UPDATE; if (!battery || (count > sizeof(alarm_string) - 1)) return -EINVAL; - - mutex_lock(&battery->mutex); - - result = acpi_battery_update(battery, 1, &update_result); if (result) { result = -ENODEV; goto end; } - if (!acpi_battery_present(battery)) { result = -ENODEV; goto end; } - if (copy_from_user(alarm_string, buffer, count)) { result = -EFAULT; goto end; } - alarm_string[count] = '\0'; - - result = acpi_battery_set_alarm(battery, - simple_strtoul(alarm_string, NULL, 0)); - if (result) - goto end; - + battery->alarm = simple_strtol(alarm_string, NULL, 0); + result = acpi_battery_set_alarm(battery); end: - - acpi_battery_check_result(battery, result); - if (!result) - result = count; - - mutex_unlock(&battery->mutex); - + return count; return result; } typedef int(*print_func)(struct seq_file *seq, int result); -typedef int(*get_func)(struct acpi_battery *battery); - -static struct acpi_read_mux { - print_func print; - get_func get; -} acpi_read_funcs[ACPI_BATTERY_NUMFILES] = { - {.get = acpi_battery_get_info, .print = acpi_battery_print_info}, - {.get = acpi_battery_get_state, .print = acpi_battery_print_state}, - {.get = acpi_battery_get_alarm, .print = acpi_battery_print_alarm}, + +static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = { + acpi_battery_print_info, + acpi_battery_print_state, + acpi_battery_print_alarm, }; static int acpi_battery_read(int fid, struct seq_file *seq) { struct acpi_battery *battery = seq->private; - int result = 0; - int update_result = ACPI_BATTERY_NONE_UPDATE; - int update = 0; - - mutex_lock(&battery->mutex); - - update = (get_seconds() - battery->update_time[fid] >= update_time); - update = (update | battery->flags.update[fid]); - - result = acpi_battery_update(battery, update, &update_result); - if (result) - goto end; - - if (update_result == ACPI_BATTERY_EASY_UPDATE) { - result = acpi_read_funcs[fid].get(battery); - if (result) - goto end; + int result = acpi_battery_update(battery); + return acpi_print_funcs[fid](seq, result); +} + +#define DECLARE_FILE_FUNCTIONS(_name) \ +static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \ +{ \ + return acpi_battery_read(_name##_tag, seq); \ +} \ +static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, acpi_battery_read_##_name, PDE(inode)->data); \ +} + +DECLARE_FILE_FUNCTIONS(info); +DECLARE_FILE_FUNCTIONS(state); +DECLARE_FILE_FUNCTIONS(alarm); + +#undef DECLARE_FILE_FUNCTIONS + +#define FILE_DESCRIPTION_RO(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IRUGO, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define FILE_DESCRIPTION_RW(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IFREG | S_IRUGO | S_IWUSR, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .write = acpi_battery_write_##_name, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ } - end: - result = acpi_read_funcs[fid].print(seq, result); - acpi_battery_check_result(battery, result); - battery->flags.update[fid] = result; - mutex_unlock(&battery->mutex); - return result; -} - -static int acpi_battery_read_info(struct seq_file *seq, void *offset) -{ - return acpi_battery_read(ACPI_BATTERY_INFO, seq); -} - -static int acpi_battery_read_state(struct seq_file *seq, void *offset) -{ - return acpi_battery_read(ACPI_BATTERY_STATE, seq); -} - -static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) -{ - return acpi_battery_read(ACPI_BATTERY_ALARM, seq); -} - -static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_battery_read_info, PDE(inode)->data); -} - -static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_battery_read_state, PDE(inode)->data); -} - -static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); -} - static struct battery_file { struct file_operations ops; mode_t mode; char *name; } acpi_battery_file[] = { - { - .name = "info", - .mode = S_IRUGO, - .ops = { - .open = acpi_battery_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, - }, - }, - { - .name = "state", - .mode = S_IRUGO, - .ops = { - .open = acpi_battery_state_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, - }, - }, - { - .name = "alarm", - .mode = S_IFREG | S_IRUGO | S_IWUSR, - .ops = { - .open = acpi_battery_alarm_open_fs, - .read = seq_read, - .write = acpi_battery_write_alarm, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, - }, - }, + FILE_DESCRIPTION_RO(info), + FILE_DESCRIPTION_RO(state), + FILE_DESCRIPTION_RW(alarm), }; +#undef FILE_DESCRIPTION_RO +#undef FILE_DESCRIPTION_RW + static int acpi_battery_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; @@ -832,25 +679,54 @@ static int acpi_battery_add_fs(struct ac entry->owner = THIS_MODULE; } } - return 0; } -static int acpi_battery_remove_fs(struct acpi_device *device) +static void acpi_battery_remove_fs(struct acpi_device *device) { int i; - if (acpi_device_dir(device)) { - for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { - remove_proc_entry(acpi_battery_file[i].name, + if (!acpi_device_dir(device)) + return; + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) + remove_proc_entry(acpi_battery_file[i].name, acpi_device_dir(device)); - } - remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); - acpi_device_dir(device) = NULL; - } - return 0; + remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); + acpi_device_dir(device) = NULL; +} + +#endif + +#ifdef ENABLE_POWER_SUPPLY + +static ssize_t acpi_battery_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + return sprintf(buf, "%d\n", battery->alarm * 1000); +} + +static ssize_t acpi_battery_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long x; + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + if (sscanf(buf, "%ld\n", &x) == 1) + battery->alarm = x/1000; + if (acpi_battery_present(battery)) + acpi_battery_set_alarm(battery); + return count; } +static struct device_attribute alarm_attr = { + .attr = {.name = "alarm", .mode = 0644, .owner = THIS_MODULE}, + .show = acpi_battery_alarm_show, + .store = acpi_battery_alarm_store, +}; + +#endif /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -858,33 +734,19 @@ static int acpi_battery_remove_fs(struct static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) { struct acpi_battery *battery = data; - struct acpi_device *device = NULL; - + struct acpi_device *device; if (!battery) return; - device = battery->device; - - switch (event) { - case ACPI_BATTERY_NOTIFY_STATUS: - case ACPI_BATTERY_NOTIFY_INFO: - case ACPI_NOTIFY_BUS_CHECK: - case ACPI_NOTIFY_DEVICE_CHECK: - device = battery->device; - acpi_battery_notify_update(battery); - acpi_bus_generate_proc_event(device, event, + acpi_battery_update(battery); + acpi_bus_generate_proc_event(device, event, + acpi_battery_present(battery)); + acpi_bus_generate_netlink_event(device->pnp.device_class, + device->dev.bus_id, event, acpi_battery_present(battery)); - acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, - acpi_battery_present(battery)); - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } - - return; +#ifdef ENABLE_POWER_SUPPLY + kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE); +#endif } static int acpi_battery_add(struct acpi_device *device) @@ -892,33 +754,29 @@ static int acpi_battery_add(struct acpi_ int result = 0; acpi_status status = 0; struct acpi_battery *battery = NULL; - if (!device) return -EINVAL; - battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); if (!battery) return -ENOMEM; - - mutex_init(&battery->mutex); - - mutex_lock(&battery->mutex); - battery->device = device; strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); acpi_driver_data(device) = battery; - - result = acpi_battery_get_status(battery); - if (result) - goto end; - - battery->flags.init_update = 1; - + mutex_init(&battery->lock); + acpi_battery_update(battery); +#ifdef CONFIG_ACPI_PROCFS result = acpi_battery_add_fs(device); if (result) goto end; - +#endif +#ifdef ENABLE_POWER_SUPPLY + battery->bat.name = acpi_device_bid(device); + battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; + battery->bat.get_property = acpi_battery_get_property; + result = power_supply_register(&battery->device->dev, &battery->bat); + result = device_create_file(battery->bat.dev, &alarm_attr); +#endif status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify, battery); @@ -927,20 +785,16 @@ static int acpi_battery_add(struct acpi_ result = -ENODEV; goto end; } - printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), device->status.battery_present ? "present" : "absent"); - end: - if (result) { +#ifdef CONFIG_ACPI_PROCFS acpi_battery_remove_fs(device); +#endif kfree(battery); } - - mutex_unlock(&battery->mutex); - return result; } @@ -951,27 +805,21 @@ static int acpi_battery_remove(struct ac if (!device || !acpi_driver_data(device)) return -EINVAL; - battery = acpi_driver_data(device); - - mutex_lock(&battery->mutex); - status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify); - +#ifdef CONFIG_ACPI_PROCFS acpi_battery_remove_fs(device); - - kfree(battery->bif_data.pointer); - - kfree(battery->bst_data.pointer); - - mutex_unlock(&battery->mutex); - - mutex_destroy(&battery->mutex); - +#endif +#ifdef ENABLE_POWER_SUPPLY + if (battery->bat.dev) { + device_remove_file(battery->bat.dev, &alarm_attr); + power_supply_unregister(&battery->bat); + } +#endif + mutex_destroy(&battery->lock); kfree(battery); - return 0; } @@ -979,44 +827,48 @@ static int acpi_battery_remove(struct ac static int acpi_battery_resume(struct acpi_device *device) { struct acpi_battery *battery; - if (!device) return -EINVAL; - - battery = device->driver_data; - - battery->flags.init_update = 1; - + battery = acpi_driver_data(device); + battery->update_time = 0; return 0; } +static struct acpi_driver acpi_battery_driver = { + .name = "battery", + .class = ACPI_BATTERY_CLASS, + .ids = battery_device_ids, + .ops = { + .add = acpi_battery_add, + .resume = acpi_battery_resume, + .remove = acpi_battery_remove, + }, +}; + static int __init acpi_battery_init(void) { - int result; - if (acpi_disabled) return -ENODEV; - +#ifdef CONFIG_ACPI_PROCFS acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) return -ENODEV; - - result = acpi_bus_register_driver(&acpi_battery_driver); - if (result < 0) { +#endif + if (acpi_bus_register_driver(&acpi_battery_driver) < 0) { +#ifdef CONFIG_ACPI_PROCFS acpi_unlock_battery_dir(acpi_battery_dir); +#endif return -ENODEV; } - return 0; } static void __exit acpi_battery_exit(void) { acpi_bus_unregister_driver(&acpi_battery_driver); - +#ifdef CONFIG_ACPI_PROCFS acpi_unlock_battery_dir(acpi_battery_dir); - - return; +#endif } module_init(acpi_battery_init); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9ba778a..a54234d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -284,15 +284,11 @@ DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_q extern int event_is_open; -int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) +int acpi_bus_generate_proc_event4(const char *device_class, const char *bus_id, u8 type, int data) { - struct acpi_bus_event *event = NULL; + struct acpi_bus_event *event; unsigned long flags = 0; - - if (!device) - return -EINVAL; - /* drop event on the floor if no one's listening */ if (!event_is_open) return 0; @@ -301,8 +297,8 @@ int acpi_bus_generate_proc_event(struct if (!event) return -ENOMEM; - strcpy(event->device_class, device->pnp.device_class); - strcpy(event->bus_id, device->pnp.bus_id); + strcpy(event->device_class, device_class); + strcpy(event->bus_id, bus_id); event->type = type; event->data = data; @@ -313,6 +309,17 @@ int acpi_bus_generate_proc_event(struct wake_up_interruptible(&acpi_bus_event_queue); return 0; + +} + +EXPORT_SYMBOL_GPL(acpi_bus_generate_proc_event4); + +int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) +{ + if (!device) + return -EINVAL; + return acpi_bus_generate_proc_event4(device->pnp.device_class, + device->pnp.bus_id, type, data); } EXPORT_SYMBOL(acpi_bus_generate_proc_event); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 3f7935a..7b41783 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -121,6 +121,7 @@ static struct acpi_ec { atomic_t event_count; wait_queue_head_t wait; struct list_head list; + u8 handlers_installed; } *boot_ec, *first_ec; /* -------------------------------------------------------------------------- @@ -425,7 +426,7 @@ int acpi_ec_add_query_handler(struct acp handler->func = func; handler->data = data; mutex_lock(&ec->lock); - list_add_tail(&handler->node, &ec->list); + list_add(&handler->node, &ec->list); mutex_unlock(&ec->lock); return 0; } @@ -440,7 +441,6 @@ void acpi_ec_remove_query_handler(struct if (query_bit == handler->query_bit) { list_del(&handler->node); kfree(handler); - break; } } mutex_unlock(&ec->lock); @@ -680,32 +680,50 @@ ec_parse_device(acpi_handle handle, u32 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; - - printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", - ec->gpe, ec->command_addr, ec->data_addr); - return AE_CTRL_TERMINATE; } +static void ec_remove_handlers(struct acpi_ec *ec) +{ + if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) + printk(KERN_ERR PREFIX "failed to remove space handler\n"); + if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler))) + printk(KERN_ERR PREFIX "failed to remove gpe handler\n"); + ec->handlers_installed = 0; +} + static int acpi_ec_add(struct acpi_device *device) { struct acpi_ec *ec = NULL; if (!device) return -EINVAL; - strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); 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; + } + } + ec = make_acpi_ec(); if (!ec) return -ENOMEM; @@ -715,25 +733,14 @@ static int acpi_ec_add(struct acpi_devic kfree(ec); return -EINVAL; } - - /* Check if we found the boot EC */ - if (boot_ec) { - if (boot_ec->gpe == ec->gpe) { - /* We might have incorrect info for GL at boot time */ - mutex_lock(&boot_ec->lock); - boot_ec->global_lock = ec->global_lock; - /* Copy handlers from new ec into boot ec */ - list_splice(&ec->list, &boot_ec->list); - mutex_unlock(&boot_ec->lock); - kfree(ec); - ec = boot_ec; - } - } else - first_ec = ec; ec->handle = device->handle; + end: + if (!first_ec) + first_ec = ec; acpi_driver_data(device) = ec; - acpi_ec_add_fs(device); + printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", + ec->gpe, ec->command_addr, ec->data_addr); return 0; } @@ -756,10 +763,7 @@ static int acpi_ec_remove(struct acpi_de acpi_driver_data(device) = NULL; if (ec == first_ec) first_ec = NULL; - - /* Don't touch boot EC */ - if (boot_ec != ec) - kfree(ec); + kfree(ec); return 0; } @@ -789,6 +793,8 @@ ec_parse_io_ports(struct acpi_resource * static int ec_install_handlers(struct acpi_ec *ec) { acpi_status status; + if (ec->handlers_installed) + return 0; status = acpi_install_gpe_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); @@ -807,6 +813,7 @@ static int ec_install_handlers(struct ac return -ENODEV; } + ec->handlers_installed = 1; return 0; } @@ -823,41 +830,22 @@ static int acpi_ec_start(struct acpi_dev if (!ec) return -EINVAL; - /* Boot EC is already working */ - if (ec != boot_ec) - ret = ec_install_handlers(ec); + ret = ec_install_handlers(ec); /* EC is fully operational, allow queries */ atomic_set(&ec->query_pending, 0); - return ret; } static int acpi_ec_stop(struct acpi_device *device, int type) { - acpi_status status; struct acpi_ec *ec; - if (!device) return -EINVAL; - ec = acpi_driver_data(device); if (!ec) return -EINVAL; - - /* Don't touch boot EC */ - if (ec == boot_ec) - return 0; - - status = acpi_remove_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler); - if (ACPI_FAILURE(status)) - return -ENODEV; - - status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); - if (ACPI_FAILURE(status)) - return -ENODEV; + ec_remove_handlers(ec); return 0; } @@ -877,7 +865,7 @@ int __init acpi_ec_ecdt_probe(void) status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { - printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n\n"); + printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n"); boot_ec->command_addr = ecdt_ptr->control.address; boot_ec->data_addr = ecdt_ptr->data.address; boot_ec->gpe = ecdt_ptr->gpe; @@ -899,7 +887,6 @@ int __init acpi_ec_ecdt_probe(void) error: kfree(boot_ec); boot_ec = NULL; - return -ENODEV; } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index a578986..3f249df 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -1,6 +1,8 @@ /* - * acpi_sbs.c - ACPI Smart Battery System Driver ($Revision: 1.16 $) + * sbs.c - ACPI Smart Battery System Driver ($Revision: 2.0 $) * + * Copyright (c) 2007 Alexey Starikovskiy + * Copyright (c) 2005-2007 Vladimir Lebedev * Copyright (c) 2005 Rich Townsend * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -26,15 +28,25 @@ #include #include #include #include + +#ifdef CONFIG_ACPI_PROCFS #include #include #include +#endif + #include #include #include #include -#define ACPI_SBS_COMPONENT 0x00080000 +#if defined(CONFIG_POWER_SUPPLY) || defined (CONFIG_POWER_SUPPLY_MODULE) +#define ENABLE_POWER_SUPPLY +#include +#endif + +#include "sbshc.h" + #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" @@ -44,836 +56,450 @@ #define ACPI_SBS_FILE_STATE "state" #define ACPI_SBS_FILE_ALARM "alarm" #define ACPI_BATTERY_DIR_NAME "BAT%i" #define ACPI_AC_DIR_NAME "AC0" -#define ACPI_SBC_SMBUS_ADDR 0x9 -#define ACPI_SBSM_SMBUS_ADDR 0xa -#define ACPI_SB_SMBUS_ADDR 0xb -#define ACPI_SBS_AC_NOTIFY_STATUS 0x80 -#define ACPI_SBS_BATTERY_NOTIFY_STATUS 0x80 -#define ACPI_SBS_BATTERY_NOTIFY_INFO 0x81 -#define _COMPONENT ACPI_SBS_COMPONENT +enum acpi_sbs_device_addr { + ACPI_SBS_CHARGER = 0x9, + ACPI_SBS_MANAGER = 0xa, + ACPI_SBS_BATTERY = 0xb, +}; -ACPI_MODULE_NAME("sbs"); +#define ACPI_SBS_NOTIFY_STATUS 0x80 +#define ACPI_SBS_NOTIFY_INFO 0x81 -MODULE_AUTHOR("Rich Townsend"); +MODULE_AUTHOR("Alexey Starikovskiy "); MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); MODULE_LICENSE("GPL"); -#define xmsleep(t) msleep(t) - -#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ - -#define ACPI_EC_SMB_STS 0x01 /* status */ -#define ACPI_EC_SMB_ADDR 0x02 /* address */ -#define ACPI_EC_SMB_CMD 0x03 /* command */ -#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ -#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ - -#define ACPI_EC_SMB_STS_DONE 0x80 -#define ACPI_EC_SMB_STS_STATUS 0x1f - -#define ACPI_EC_SMB_PRTCL_WRITE 0x00 -#define ACPI_EC_SMB_PRTCL_READ 0x01 -#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 -#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a - -#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 -#define ACPI_EC_SMB_ACCESS_SLEEP1 1 -#define ACPI_EC_SMB_ACCESS_SLEEP2 10 - -#define DEF_CAPACITY_UNIT 3 -#define MAH_CAPACITY_UNIT 1 -#define MWH_CAPACITY_UNIT 2 -#define CAPACITY_UNIT DEF_CAPACITY_UNIT - -#define REQUEST_UPDATE_MODE 1 -#define QUEUE_UPDATE_MODE 2 - -#define DATA_TYPE_COMMON 0 -#define DATA_TYPE_INFO 1 -#define DATA_TYPE_STATE 2 -#define DATA_TYPE_ALARM 3 -#define DATA_TYPE_AC_STATE 4 +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); extern struct proc_dir_entry *acpi_lock_ac_dir(void); extern struct proc_dir_entry *acpi_lock_battery_dir(void); extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); -#define MAX_SBS_BAT 4 +#define MAX_SBS_BAT 4 #define ACPI_SBS_BLOCK_MAX 32 -#define ACPI_SBS_SMBUS_READ 1 -#define ACPI_SBS_SMBUS_WRITE 2 - -#define ACPI_SBS_WORD_DATA 1 -#define ACPI_SBS_BLOCK_DATA 2 - -#define UPDATE_DELAY 10 - -/* 0 - every time, > 0 - by update_time */ -static unsigned int update_time = 120; - -static unsigned int capacity_mode = CAPACITY_UNIT; - -module_param(update_time, uint, 0644); -module_param(capacity_mode, uint, 0444); - -static int acpi_sbs_add(struct acpi_device *device); -static int acpi_sbs_remove(struct acpi_device *device, int type); -static int acpi_sbs_resume(struct acpi_device *device); - static const struct acpi_device_id sbs_device_ids[] = { - {"ACPI0001", 0}, - {"ACPI0005", 0}, + {"ACPI0002", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, sbs_device_ids); -static struct acpi_driver acpi_sbs_driver = { - .name = "sbs", - .class = ACPI_SBS_CLASS, - .ids = sbs_device_ids, - .ops = { - .add = acpi_sbs_add, - .remove = acpi_sbs_remove, - .resume = acpi_sbs_resume, - }, -}; - -struct acpi_ac { - int ac_present; -}; - -struct acpi_battery_info { - int capacity_mode; - s16 full_charge_capacity; - s16 design_capacity; - s16 design_voltage; - int vscale; - int ipscale; - s16 serial_number; - char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3]; - char device_name[ACPI_SBS_BLOCK_MAX + 3]; - char device_chemistry[ACPI_SBS_BLOCK_MAX + 3]; -}; - -struct acpi_battery_state { - s16 voltage; - s16 amperage; - s16 remaining_capacity; - s16 battery_state; -}; - -struct acpi_battery_alarm { - s16 remaining_capacity; -}; - struct acpi_battery { - int alive; - int id; - int init_state; - int battery_present; +#ifdef ENABLE_POWER_SUPPLY + struct power_supply bat; +#endif struct acpi_sbs *sbs; - struct acpi_battery_info info; - struct acpi_battery_state state; - struct acpi_battery_alarm alarm; - struct proc_dir_entry *battery_entry; +#ifdef CONFIG_ACPI_PROCFS + struct proc_dir_entry *proc_entry; +#endif + unsigned long update_time; + char name[8]; + char manufacturer_name[ACPI_SBS_BLOCK_MAX]; + char device_name[ACPI_SBS_BLOCK_MAX]; + char device_chemistry[ACPI_SBS_BLOCK_MAX]; + u16 alarm_capacity; + u16 full_charge_capacity; + u16 design_capacity; + u16 design_voltage; + u16 serial_number; + u16 cycle_count; + u16 temp_now; + u16 voltage_now; + s16 current_now; + s16 current_avg; + u16 capacity_now; + u16 state_of_charge; + u16 state; + u16 mode; + u16 spec; + u8 id; + u8 present:1; }; +#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); + struct acpi_sbs { - int base; +#ifdef ENABLE_POWER_SUPPLY + struct power_supply charger; +#endif struct acpi_device *device; - struct mutex mutex; - int sbsm_present; - int sbsm_batteries_supported; - struct proc_dir_entry *ac_entry; - struct acpi_ac ac; + struct acpi_smb_hc *hc; + struct mutex lock; +#ifdef CONFIG_ACPI_PROCFS + struct proc_dir_entry *charger_entry; +#endif struct acpi_battery battery[MAX_SBS_BAT]; - int zombie; - struct timer_list update_timer; - int run_cnt; - int update_proc_flg; -}; - -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type); -static void acpi_sbs_update_time(void *data); - -union sbs_rw_data { - u16 word; - u8 block[ACPI_SBS_BLOCK_MAX + 2]; + u8 batteries_supported:4; + u8 manager_present:1; + u8 charger_present:1; }; -static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, - char read_write, u8 command, int size, - union sbs_rw_data *data); - -/* -------------------------------------------------------------------------- - SMBus Communication - -------------------------------------------------------------------------- */ +#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger) -static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data) +static inline int battery_scale(int log) { - u8 val; - int err; - - err = ec_read(sbs->base + address, &val); - if (!err) { - *data = val; - } - xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); - return (err); + int scale = 1; + while (log--) + scale *= 10; + return scale; } -static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data) +static inline int acpi_battery_vscale(struct acpi_battery *battery) { - int err; - - err = ec_write(sbs->base + address, data); - return (err); + return battery_scale((battery->spec & 0x0f00) >> 8); } -static int -acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, - char read_write, u8 command, int size, - union sbs_rw_data *data) +static inline int acpi_battery_ipscale(struct acpi_battery *battery) { - unsigned char protocol, len = 0, temp[2] = { 0, 0 }; - int i; - - if (read_write == ACPI_SBS_SMBUS_READ) { - protocol = ACPI_EC_SMB_PRTCL_READ; - } else { - protocol = ACPI_EC_SMB_PRTCL_WRITE; - } - - switch (size) { - - case ACPI_SBS_WORD_DATA: - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); - if (read_write == ACPI_SBS_SMBUS_WRITE) { - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word); - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1, - data->word >> 8); - } - protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA; - break; - case ACPI_SBS_BLOCK_DATA: - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); - if (read_write == ACPI_SBS_SMBUS_WRITE) { - len = min_t(u8, data->block[0], 32); - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len); - for (i = 0; i < len; i++) - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - } - protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA; - break; - default: - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "unsupported transaction %d", size)); - return (-1); - } - - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1); - acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol); - - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); - - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1); - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); - } - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); - } - if ((~temp[0] & ACPI_EC_SMB_STS_DONE) - || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "transaction %d error", size)); - return (-1); - } - - if (read_write == ACPI_SBS_SMBUS_WRITE) { - return (0); - } - - switch (size) { - - case ACPI_SBS_WORD_DATA: - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp); - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1); - data->word = (temp[1] << 8) | temp[0]; - break; - - case ACPI_SBS_BLOCK_DATA: - len = 0; - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len); - len = min_t(u8, len, 32); - for (i = 0; i < len; i++) - acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i, - data->block + i + 1); - data->block[0] = len; - break; - default: - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "unsupported transaction %d", size)); - return (-1); - } - - return (0); + return battery_scale((battery->spec & 0xf000) >> 12); } -static int -acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word) +static inline int acpi_battery_mode(struct acpi_battery *battery) { - union sbs_rw_data data; - int result = 0; - - result = acpi_ec_sbs_access(sbs, addr, - ACPI_SBS_SMBUS_READ, func, - ACPI_SBS_WORD_DATA, &data); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_ec_sbs_access() failed")); - } else { - *word = data.word; - } - - return result; + return (battery->mode & 0x8000); } -static int -acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str) +static inline int acpi_battery_scale(struct acpi_battery *battery) { - union sbs_rw_data data; - int result = 0; - - result = acpi_ec_sbs_access(sbs, addr, - ACPI_SBS_SMBUS_READ, func, - ACPI_SBS_BLOCK_DATA, &data); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_ec_sbs_access() failed")); - } else { - strncpy(str, (const char *)data.block + 1, data.block[0]); - str[data.block[0]] = 0; - } - - return result; + return (acpi_battery_mode(battery) ? 10 : 1) * + acpi_battery_ipscale(battery); } -static int -acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word) +#ifdef ENABLE_POWER_SUPPLY +static int sbs_get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { - union sbs_rw_data data; - int result = 0; - - data.word = word; - - result = acpi_ec_sbs_access(sbs, addr, - ACPI_SBS_SMBUS_WRITE, func, - ACPI_SBS_WORD_DATA, &data); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_ec_sbs_access() failed")); - } - - return result; -} - -static int sbs_zombie(struct acpi_sbs *sbs) -{ - return (sbs->zombie); -} - -static int sbs_mutex_lock(struct acpi_sbs *sbs) -{ - if (sbs_zombie(sbs)) { - return -ENODEV; + struct acpi_sbs *sbs = to_acpi_sbs(psy); + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = sbs->charger_present; + break; + default: + return -EINVAL; } - mutex_lock(&sbs->mutex); return 0; } -static void sbs_mutex_unlock(struct acpi_sbs *sbs) +static int acpi_battery_technology(struct acpi_battery *battery) { - mutex_unlock(&sbs->mutex); + if (!strcasecmp("NiCd", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_NiCd; + if (!strcasecmp("NiMH", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_NiMH; + if (!strcasecmp("LION", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_LION; + if (!strcasecmp("LiP", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_LIPO; + return POWER_SUPPLY_TECHNOLOGY_UNKNOWN; } -/* -------------------------------------------------------------------------- - Smart Battery System Management - -------------------------------------------------------------------------- */ - -static int acpi_check_update_proc(struct acpi_sbs *sbs) +static int acpi_sbs_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { - acpi_status status = AE_OK; + struct acpi_battery *battery = to_acpi_battery(psy); - if (update_time == 0) { - sbs->update_proc_flg = 0; - return 0; - } - if (sbs->update_proc_flg == 0) { - status = acpi_os_execute(OSL_GPE_HANDLER, - acpi_sbs_update_time, sbs); - if (status != AE_OK) { - ACPI_EXCEPTION((AE_INFO, status, - "acpi_os_execute() failed")); - return 1; - } - sbs->update_proc_flg = 1; + if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (battery->current_now < 0) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (battery->current_now > 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery->present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = acpi_battery_technology(battery); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = battery->design_voltage * + acpi_battery_vscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery->voltage_now * + acpi_battery_vscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = abs(battery->current_now) * + acpi_battery_ipscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = abs(battery->current_avg) * + acpi_battery_ipscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery->state_of_charge; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = battery->design_capacity * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = battery->full_charge_capacity * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = battery->capacity_now * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->temp_now - 2730; // dK -> dC + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = battery->device_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = battery->manufacturer_name; + break; + default: + return -EINVAL; } return 0; } -static int acpi_sbs_generate_event(struct acpi_device *device, - int event, int state, char *bid, char *class) -{ - char bid_saved[5]; - char class_saved[20]; - int result = 0; - - strcpy(bid_saved, acpi_device_bid(device)); - strcpy(class_saved, acpi_device_class(device)); - - strcpy(acpi_device_bid(device), bid); - strcpy(acpi_device_class(device), class); - - result = acpi_bus_generate_proc_event(device, event, state); - - strcpy(acpi_device_bid(device), bid_saved); - strcpy(acpi_device_class(device), class_saved); - - acpi_bus_generate_netlink_event(class, bid, event, state); - return result; -} - -static int acpi_battery_get_present(struct acpi_battery *battery) -{ - s16 state; - int result = 0; - int is_present = 0; - - result = acpi_sbs_read_word(battery->sbs, - ACPI_SBSM_SMBUS_ADDR, 0x01, &state); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - } - if (!result) { - is_present = (state & 0x000f) & (1 << battery->id); - } - battery->battery_present = is_present; - - return result; -} +static enum power_supply_property sbs_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; -static int acpi_battery_select(struct acpi_battery *battery) -{ - struct acpi_sbs *sbs = battery->sbs; - int result = 0; - s16 state; - int foo; +static enum power_supply_property sbs_charge_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; - if (sbs->sbsm_present) { +static enum power_supply_property sbs_energy_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; +#endif - /* Take special care not to knobble other nibbles of - * state (aka selector_state), since - * it causes charging to halt on SBSELs */ +/* -------------------------------------------------------------------------- + Smart Battery System Management + -------------------------------------------------------------------------- */ - result = - acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } +struct acpi_battery_reader { + u8 command; /* command for battery */ + u8 mode; /* word or block? */ + size_t offset; /* offset inside struct acpi_sbs_battery */ +}; - foo = (state & 0x0fff) | (1 << (battery->id + 12)); - result = - acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_write_word() failed")); - goto end; - } - } +static struct acpi_battery_reader info_readers[] = { + {0x01, SMBUS_READ_WORD, offsetof(struct acpi_battery, alarm_capacity)}, + {0x03, SMBUS_READ_WORD, offsetof(struct acpi_battery, mode)}, + {0x10, SMBUS_READ_WORD, offsetof(struct acpi_battery, full_charge_capacity)}, + {0x17, SMBUS_READ_WORD, offsetof(struct acpi_battery, cycle_count)}, + {0x18, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_capacity)}, + {0x19, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_voltage)}, + {0x1a, SMBUS_READ_WORD, offsetof(struct acpi_battery, spec)}, + {0x1c, SMBUS_READ_WORD, offsetof(struct acpi_battery, serial_number)}, + {0x20, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, manufacturer_name)}, + {0x21, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_name)}, + {0x22, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_chemistry)}, +}; - end: - return result; -} +static struct acpi_battery_reader state_readers[] = { + {0x08, SMBUS_READ_WORD, offsetof(struct acpi_battery, temp_now)}, + {0x09, SMBUS_READ_WORD, offsetof(struct acpi_battery, voltage_now)}, + {0x0a, SMBUS_READ_WORD, offsetof(struct acpi_battery, current_now)}, + {0x0b, SMBUS_READ_WORD, offsetof(struct acpi_battery, current_avg)}, + {0x0f, SMBUS_READ_WORD, offsetof(struct acpi_battery, capacity_now)}, + {0x0e, SMBUS_READ_WORD, offsetof(struct acpi_battery, state_of_charge)}, + {0x16, SMBUS_READ_WORD, offsetof(struct acpi_battery, state)}, +}; -static int acpi_sbsm_get_info(struct acpi_sbs *sbs) +static int acpi_manager_get_info(struct acpi_sbs *sbs) { int result = 0; - s16 battery_system_info; - - result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04, - &battery_system_info); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - sbs->sbsm_present = 1; - sbs->sbsm_batteries_supported = battery_system_info & 0x000f; - - end: + u16 battery_system_info; + result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER, + 0x04, (u8 *)&battery_system_info); + if (!result) + sbs->batteries_supported = battery_system_info & 0x000f; return result; } static int acpi_battery_get_info(struct acpi_battery *battery) { - struct acpi_sbs *sbs = battery->sbs; - int result = 0; - s16 battery_mode; - s16 specification_info; - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; + int i, result = 0; + + for (i = 0; i < ARRAY_SIZE(info_readers); ++i) { + result = acpi_smbus_read(battery->sbs->hc, + info_readers[i].mode, + ACPI_SBS_BATTERY, + info_readers[i].command, + (u8 *) battery + + info_readers[i].offset); + if (result) + break; } - battery->info.capacity_mode = (battery_mode & 0x8000) >> 15; - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10, - &battery->info.full_charge_capacity); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18, - &battery->info.design_capacity); - - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19, - &battery->info.design_voltage); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a, - &specification_info); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - switch ((specification_info & 0x0f00) >> 8) { - case 1: - battery->info.vscale = 10; - break; - case 2: - battery->info.vscale = 100; - break; - case 3: - battery->info.vscale = 1000; - break; - default: - battery->info.vscale = 1; - } - - switch ((specification_info & 0xf000) >> 12) { - case 1: - battery->info.ipscale = 10; - break; - case 2: - battery->info.ipscale = 100; - break; - case 3: - battery->info.ipscale = 1000; - break; - default: - battery->info.ipscale = 1; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c, - &battery->info.serial_number); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20, - battery->info.manufacturer_name); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_str() failed")); - goto end; - } - - result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21, - battery->info.device_name); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_str() failed")); - goto end; - } - - result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22, - battery->info.device_chemistry); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_str() failed")); - goto end; - } - - end: return result; } static int acpi_battery_get_state(struct acpi_battery *battery) { - struct acpi_sbs *sbs = battery->sbs; - int result = 0; + int i, result = 0; - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09, - &battery->state.voltage); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a, - &battery->state.amperage); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f, - &battery->state.remaining_capacity); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16, - &battery->state.battery_state); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; + if (battery->update_time && + time_before(jiffies, battery->update_time + + msecs_to_jiffies(cache_time))) + return 0; + for (i = 0; i < ARRAY_SIZE(state_readers); ++i) { + result = acpi_smbus_read(battery->sbs->hc, + state_readers[i].mode, + ACPI_SBS_BATTERY, + state_readers[i].command, + (u8 *)battery + + state_readers[i].offset); + if (result) + goto end; } - end: + battery->update_time = jiffies; return result; } +#ifdef CONFIG_ACPI_PROCFS + static int acpi_battery_get_alarm(struct acpi_battery *battery) { - struct acpi_sbs *sbs = battery->sbs; - int result = 0; - - result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, - &battery->alarm.remaining_capacity); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - end: - - return result; + return acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, + ACPI_SBS_BATTERY, 0x01, + (u8 *)&battery->alarm_capacity); } -static int acpi_battery_set_alarm(struct acpi_battery *battery, - unsigned long alarm) +static int acpi_battery_set_alarm(struct acpi_battery *battery) { struct acpi_sbs *sbs = battery->sbs; - int result = 0; - s16 battery_mode; - int foo; + u16 value, sel = 1 << (battery->id + 12); - result = acpi_battery_select(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_select() failed")); - goto end; - } + int ret; - /* If necessary, enable the alarm */ - if (alarm > 0) { - result = - acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); + if (sbs->manager_present) { + ret = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER, + 0x01, (u8 *)&value); + if (ret) goto end; - } - - result = - acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, - battery_mode & 0xbfff); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_write_word() failed")); + if ((value & 0xf000) != sel) { + value &= 0x0fff; + value |= sel; + ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, + ACPI_SBS_MANAGER, + 0x01, (u8 *)&value, 2); + if (ret) goto end; } } - - foo = alarm / (battery->info.capacity_mode ? 10 : 1); - result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_write_word() failed")); - goto end; - } - + ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, + 0x01, (u8 *)&battery->alarm_capacity, 2); end: - - return result; + return ret; } -static int acpi_battery_set_mode(struct acpi_battery *battery) -{ - struct acpi_sbs *sbs = battery->sbs; - int result = 0; - s16 battery_mode; - - if (capacity_mode == DEF_CAPACITY_UNIT) { - goto end; - } - - result = acpi_sbs_read_word(sbs, - ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } - - if (capacity_mode == MAH_CAPACITY_UNIT) { - battery_mode &= 0x7fff; - } else { - battery_mode |= 0x8000; - } - result = acpi_sbs_write_word(sbs, - ACPI_SB_SMBUS_ADDR, 0x03, battery_mode); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_write_word() failed")); - goto end; - } +#endif - result = acpi_sbs_read_word(sbs, - ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } +static int acpi_ac_get_present(struct acpi_sbs *sbs) +{ + int result; + u16 status; - end: + result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER, + 0x13, (u8 *) & status); + if (!result) + sbs->charger_present = (status >> 15) & 0x1; return result; } -static int acpi_battery_init(struct acpi_battery *battery) -{ - int result = 0; +#ifdef ENABLE_POWER_SUPPLY - result = acpi_battery_select(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_select() failed")); - goto end; - } - - result = acpi_battery_set_mode(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_set_mode() failed")); - goto end; - } - - result = acpi_battery_get_info(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_info() failed")); - goto end; - } - - result = acpi_battery_get_state(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_state() failed")); - goto end; - } - - result = acpi_battery_get_alarm(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_alarm() failed")); - goto end; - } - - end: - return result; +static ssize_t acpi_battery_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + acpi_battery_get_alarm(battery); + return sprintf(buf, "%d\n", battery->alarm_capacity * + acpi_battery_scale(battery) * 1000); } -static int acpi_ac_get_present(struct acpi_sbs *sbs) +static ssize_t acpi_battery_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - int result = 0; - s16 charger_status; - - result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13, - &charger_status); - - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_read_word() failed")); - goto end; - } + unsigned long x; + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + if (sscanf(buf, "%ld\n", &x) == 1) + battery->alarm_capacity = x / + (1000 * acpi_battery_scale(battery)); + if (battery->present) + acpi_battery_set_alarm(battery); + return count; +} - sbs->ac.ac_present = (charger_status & 0x8000) >> 15; +static struct device_attribute alarm_attr = { + .attr = {.name = "alarm", .mode = 0644, .owner = THIS_MODULE}, + .show = acpi_battery_alarm_show, + .store = acpi_battery_alarm_store, +}; - end: - - return result; -} +#endif /* -------------------------------------------------------------------------- FS Interface (/proc/acpi) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS /* Generic Routines */ - static int -acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, - struct proc_dir_entry *parent_dir, - char *dir_name, - struct file_operations *info_fops, - struct file_operations *state_fops, - struct file_operations *alarm_fops, void *data) +acpi_sbs_add_fs(struct proc_dir_entry **dir, + struct proc_dir_entry *parent_dir, + char *dir_name, + struct file_operations *info_fops, + struct file_operations *state_fops, + struct file_operations *alarm_fops, void *data) { struct proc_dir_entry *entry = NULL; if (!*dir) { *dir = proc_mkdir(dir_name, parent_dir); if (!*dir) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "proc_mkdir() failed")); return -ENODEV; } (*dir)->owner = THIS_MODULE; @@ -882,10 +508,7 @@ acpi_sbs_generic_add_fs(struct proc_dir_ /* 'info' [R] */ if (info_fops) { entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); - if (!entry) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "create_proc_entry() failed")); - } else { + if (entry) { entry->proc_fops = info_fops; entry->data = data; entry->owner = THIS_MODULE; @@ -895,10 +518,7 @@ acpi_sbs_generic_add_fs(struct proc_dir_ /* 'state' [R] */ if (state_fops) { entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); - if (!entry) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "create_proc_entry() failed")); - } else { + if (entry) { entry->proc_fops = state_fops; entry->data = data; entry->owner = THIS_MODULE; @@ -908,24 +528,19 @@ acpi_sbs_generic_add_fs(struct proc_dir_ /* 'alarm' [R/W] */ if (alarm_fops) { entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir); - if (!entry) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "create_proc_entry() failed")); - } else { + if (entry) { entry->proc_fops = alarm_fops; entry->data = data; entry->owner = THIS_MODULE; } } - return 0; } static void -acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir, +acpi_sbs_remove_fs(struct proc_dir_entry **dir, struct proc_dir_entry *parent_dir) { - if (*dir) { remove_proc_entry(ACPI_SBS_FILE_INFO, *dir); remove_proc_entry(ACPI_SBS_FILE_STATE, *dir); @@ -933,82 +548,52 @@ acpi_sbs_generic_remove_fs(struct proc_d remove_proc_entry((*dir)->name, parent_dir); *dir = NULL; } - } /* Smart Battery Interface */ - static struct proc_dir_entry *acpi_battery_dir = NULL; +static inline char *acpi_battery_units(struct acpi_battery *battery) +{ + return acpi_battery_mode(battery) ? " mWh" : " mAh"; +} + + static int acpi_battery_read_info(struct seq_file *seq, void *offset) { struct acpi_battery *battery = seq->private; struct acpi_sbs *sbs = battery->sbs; - int cscale; int result = 0; - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } + mutex_lock(&sbs->lock); - result = acpi_check_update_proc(sbs); - if (result) + seq_printf(seq, "present: %s\n", + (battery->present) ? "yes" : "no"); + if (!battery->present) goto end; - if (update_time == 0) { - result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_update_run() failed")); - } - } - - if (battery->battery_present) { - seq_printf(seq, "present: yes\n"); - } else { - seq_printf(seq, "present: no\n"); - goto end; - } - - if (battery->info.capacity_mode) { - cscale = battery->info.vscale * battery->info.ipscale; - } else { - cscale = battery->info.ipscale; - } seq_printf(seq, "design capacity: %i%s\n", - battery->info.design_capacity * cscale, - battery->info.capacity_mode ? "0 mWh" : " mAh"); - + battery->design_capacity * acpi_battery_scale(battery), + acpi_battery_units(battery)); seq_printf(seq, "last full capacity: %i%s\n", - battery->info.full_charge_capacity * cscale, - battery->info.capacity_mode ? "0 mWh" : " mAh"); - + battery->full_charge_capacity * acpi_battery_scale(battery), + acpi_battery_units(battery)); seq_printf(seq, "battery technology: rechargeable\n"); - seq_printf(seq, "design voltage: %i mV\n", - battery->info.design_voltage * battery->info.vscale); - + battery->design_voltage * acpi_battery_vscale(battery)); seq_printf(seq, "design capacity warning: unknown\n"); seq_printf(seq, "design capacity low: unknown\n"); seq_printf(seq, "capacity granularity 1: unknown\n"); seq_printf(seq, "capacity granularity 2: unknown\n"); - - seq_printf(seq, "model number: %s\n", - battery->info.device_name); - + seq_printf(seq, "model number: %s\n", battery->device_name); seq_printf(seq, "serial number: %i\n", - battery->info.serial_number); - + battery->serial_number); seq_printf(seq, "battery type: %s\n", - battery->info.device_chemistry); - + battery->device_chemistry); seq_printf(seq, "OEM info: %s\n", - battery->info.manufacturer_name); - + battery->manufacturer_name); end: - - sbs_mutex_unlock(sbs); - + mutex_unlock(&sbs->lock); return result; } @@ -1022,73 +607,29 @@ static int acpi_battery_read_state(struc struct acpi_battery *battery = seq->private; struct acpi_sbs *sbs = battery->sbs; int result = 0; - int cscale; - int foo; - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } - - result = acpi_check_update_proc(sbs); - if (result) + mutex_lock(&sbs->lock); + seq_printf(seq, "present: %s\n", + (battery->present) ? "yes" : "no"); + if (!battery->present) goto end; - if (update_time == 0) { - result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_update_run() failed")); - } - } - - if (battery->battery_present) { - seq_printf(seq, "present: yes\n"); - } else { - seq_printf(seq, "present: no\n"); - goto end; - } - - if (battery->info.capacity_mode) { - cscale = battery->info.vscale * battery->info.ipscale; - } else { - cscale = battery->info.ipscale; - } - - if (battery->state.battery_state & 0x0010) { - seq_printf(seq, "capacity state: critical\n"); - } else { - seq_printf(seq, "capacity state: ok\n"); - } - - foo = (s16) battery->state.amperage * battery->info.ipscale; - if (battery->info.capacity_mode) { - foo = foo * battery->info.design_voltage / 1000; - } - if (battery->state.amperage < 0) { - seq_printf(seq, "charging state: discharging\n"); - seq_printf(seq, "present rate: %d %s\n", - -foo, battery->info.capacity_mode ? "mW" : "mA"); - } else if (battery->state.amperage > 0) { - seq_printf(seq, "charging state: charging\n"); - seq_printf(seq, "present rate: %d %s\n", - foo, battery->info.capacity_mode ? "mW" : "mA"); - } else { - seq_printf(seq, "charging state: charged\n"); - seq_printf(seq, "present rate: 0 %s\n", - battery->info.capacity_mode ? "mW" : "mA"); - } - + acpi_battery_get_state(battery); + seq_printf(seq, "capacity state: %s\n", + (battery->state & 0x0010) ? "critical" : "ok"); + seq_printf(seq, "charging state: %s\n", + (battery->current_now < 0) ? "discharging" : + ((battery->current_now > 0) ? "charging" : "charged")); + seq_printf(seq, "present rate: %d mA\n", + abs(battery->current_now) * acpi_battery_ipscale(battery)); seq_printf(seq, "remaining capacity: %i%s\n", - battery->state.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh" : " mAh"); - + battery->capacity_now * acpi_battery_scale(battery), + acpi_battery_units(battery)); seq_printf(seq, "present voltage: %i mV\n", - battery->state.voltage * battery->info.vscale); + battery->voltage_now * acpi_battery_vscale(battery)); end: - - sbs_mutex_unlock(sbs); - + mutex_unlock(&sbs->lock); return result; } @@ -1102,48 +643,25 @@ static int acpi_battery_read_alarm(struc struct acpi_battery *battery = seq->private; struct acpi_sbs *sbs = battery->sbs; int result = 0; - int cscale; - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } + mutex_lock(&sbs->lock); - result = acpi_check_update_proc(sbs); - if (result) - goto end; - - if (update_time == 0) { - result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_update_run() failed")); - } - } - - if (!battery->battery_present) { + if (!battery->present) { seq_printf(seq, "present: no\n"); goto end; } - if (battery->info.capacity_mode) { - cscale = battery->info.vscale * battery->info.ipscale; - } else { - cscale = battery->info.ipscale; - } - + acpi_battery_get_alarm(battery); seq_printf(seq, "alarm: "); - if (battery->alarm.remaining_capacity) { + if (battery->alarm_capacity) seq_printf(seq, "%i%s\n", - battery->alarm.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh" : " mAh"); - } else { + battery->alarm_capacity * + acpi_battery_scale(battery), + acpi_battery_units(battery)); + else seq_printf(seq, "disabled\n"); - } - end: - - sbs_mutex_unlock(sbs); - + mutex_unlock(&sbs->lock); return result; } @@ -1155,59 +673,29 @@ acpi_battery_write_alarm(struct file *fi struct acpi_battery *battery = seq->private; struct acpi_sbs *sbs = battery->sbs; char alarm_string[12] = { '\0' }; - int result, old_alarm, new_alarm; - - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } - - result = acpi_check_update_proc(sbs); - if (result) - goto end; - - if (!battery->battery_present) { + int result = 0; + mutex_lock(&sbs->lock); + if (!battery->present) { result = -ENODEV; goto end; } - if (count > sizeof(alarm_string) - 1) { result = -EINVAL; goto end; } - if (copy_from_user(alarm_string, buffer, count)) { result = -EFAULT; goto end; } - alarm_string[count] = 0; - - old_alarm = battery->alarm.remaining_capacity; - new_alarm = simple_strtoul(alarm_string, NULL, 0); - - result = acpi_battery_set_alarm(battery, new_alarm); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_set_alarm() failed")); - acpi_battery_set_alarm(battery, old_alarm); - goto end; - } - result = acpi_battery_get_alarm(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_alarm() failed")); - acpi_battery_set_alarm(battery, old_alarm); - goto end; - } - + battery->alarm_capacity = simple_strtoul(alarm_string, NULL, 0) / + acpi_battery_scale(battery); + acpi_battery_set_alarm(battery); end: - sbs_mutex_unlock(sbs); - - if (result) { + mutex_unlock(&sbs->lock); + if (result) return result; - } else { - return count; - } + return count; } static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) @@ -1246,26 +734,15 @@ static struct proc_dir_entry *acpi_ac_di static int acpi_ac_read_state(struct seq_file *seq, void *offset) { - struct acpi_sbs *sbs = seq->private; - int result; - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } + struct acpi_sbs *sbs = seq->private; - if (update_time == 0) { - result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_update_run() failed")); - } - } + mutex_lock(&sbs->lock); seq_printf(seq, "state: %s\n", - sbs->ac.ac_present ? "on-line" : "off-line"); - - sbs_mutex_unlock(sbs); + sbs->charger_present ? "on-line" : "off-line"); + mutex_unlock(&sbs->lock); return 0; } @@ -1282,429 +759,211 @@ static struct file_operations acpi_ac_st .owner = THIS_MODULE, }; +#endif + /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ +static int acpi_battery_read(struct acpi_battery *battery) +{ + int result = 0, saved_present = battery->present; + u16 state; + + if (battery->sbs->manager_present) { + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, + ACPI_SBS_MANAGER, 0x01, (u8 *)&state); + if (!result) + battery->present = state & (1 << battery->id); + state &= 0x0fff; + state |= 1 << (battery->id + 12); + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, + ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2); + } else if (battery->id == 0) + battery->present = 1; + if (result || !battery->present) + return result; -/* Smart Battery */ + if (saved_present != battery->present) { + battery->update_time = 0; + result = acpi_battery_get_info(battery); + if (result) + return result; + } + result = acpi_battery_get_state(battery); + return result; +} +/* Smart Battery */ static int acpi_battery_add(struct acpi_sbs *sbs, int id) { - int is_present; + struct acpi_battery *battery = &sbs->battery[id]; int result; - char dir_name[32]; - struct acpi_battery *battery; - - battery = &sbs->battery[id]; - battery->alive = 0; - - battery->init_state = 0; battery->id = id; battery->sbs = sbs; + result = acpi_battery_read(battery); + if (result) + return result; - result = acpi_battery_select(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_select() failed")); - goto end; - } - - result = acpi_battery_get_present(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_present() failed")); - goto end; - } - - is_present = battery->battery_present; - - if (is_present) { - result = acpi_battery_init(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_init() failed")); - goto end; - } - battery->init_state = 1; - } - - sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); - - result = acpi_sbs_generic_add_fs(&battery->battery_entry, - acpi_battery_dir, - dir_name, - &acpi_battery_info_fops, - &acpi_battery_state_fops, - &acpi_battery_alarm_fops, battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_generic_add_fs() failed")); - goto end; - } - battery->alive = 1; - + sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id); +#ifdef CONFIG_ACPI_PROCFS + acpi_sbs_add_fs(&battery->proc_entry, acpi_battery_dir, + battery->name, &acpi_battery_info_fops, + &acpi_battery_state_fops, &acpi_battery_alarm_fops, + battery); +#endif +#ifdef ENABLE_POWER_SUPPLY + battery->bat.name = battery->name; + battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; + if (!acpi_battery_mode(battery)) { + battery->bat.properties = sbs_charge_battery_props; + battery->bat.num_properties = + ARRAY_SIZE(sbs_charge_battery_props); + } else { + battery->bat.properties = sbs_energy_battery_props; + battery->bat.num_properties = + ARRAY_SIZE(sbs_energy_battery_props); + } + battery->bat.get_property = acpi_sbs_battery_get_property; + result = power_supply_register(&sbs->device->dev, &battery->bat); + device_create_file(battery->bat.dev, &alarm_attr); +#endif printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", - ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name, - sbs->battery->battery_present ? "present" : "absent"); - - end: + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), + battery->name, sbs->battery->present ? "present" : "absent"); return result; } static void acpi_battery_remove(struct acpi_sbs *sbs, int id) { - - if (sbs->battery[id].battery_entry) { - acpi_sbs_generic_remove_fs(&(sbs->battery[id].battery_entry), - acpi_battery_dir); - } +#ifdef ENABLE_POWER_SUPPLY + if (sbs->battery[id].bat.dev) + device_remove_file(sbs->battery[id].bat.dev, &alarm_attr); + power_supply_unregister(&sbs->battery[id].bat); +#endif +#ifdef CONFIG_ACPI_PROCFS + if (sbs->battery[id].proc_entry) { + acpi_sbs_remove_fs(&(sbs->battery[id].proc_entry), + acpi_battery_dir); + } +#endif } -static int acpi_ac_add(struct acpi_sbs *sbs) +static int acpi_charger_add(struct acpi_sbs *sbs) { int result; result = acpi_ac_get_present(sbs); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_ac_get_present() failed")); + if (result) goto end; - } - - result = acpi_sbs_generic_add_fs(&sbs->ac_entry, - acpi_ac_dir, - ACPI_AC_DIR_NAME, - NULL, &acpi_ac_state_fops, NULL, sbs); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_generic_add_fs() failed")); +#ifdef CONFIG_ACPI_PROCFS + result = acpi_sbs_add_fs(&sbs->charger_entry, acpi_ac_dir, + ACPI_AC_DIR_NAME, NULL, + &acpi_ac_state_fops, NULL, sbs); + if (result) goto end; - } - +#endif +#ifdef ENABLE_POWER_SUPPLY + sbs->charger.name = "sbs-charger"; + sbs->charger.type = POWER_SUPPLY_TYPE_MAINS; + sbs->charger.properties = sbs_ac_props; + sbs->charger.num_properties = ARRAY_SIZE(sbs_ac_props); + sbs->charger.get_property = sbs_get_ac_property; + power_supply_register(&sbs->device->dev, &sbs->charger); +#endif printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), - ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line"); - + ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line"); end: - return result; } -static void acpi_ac_remove(struct acpi_sbs *sbs) +static void acpi_charger_remove(struct acpi_sbs *sbs) { - - if (sbs->ac_entry) { - acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir); - } +#ifdef ENABLE_POWER_SUPPLY + if (sbs->charger.dev) + power_supply_unregister(&sbs->charger); +#endif +#ifdef CONFIG_ACPI_PROCFS + if (sbs->charger_entry) + acpi_sbs_remove_fs(&sbs->charger_entry, acpi_ac_dir); +#endif } -static void acpi_sbs_update_time_run(unsigned long data) +void acpi_sbs_callback(void *context) { - acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data); -} - -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type) -{ - struct acpi_battery *battery; - int result = 0, cnt; - int old_ac_present = -1; - int old_battery_present = -1; - int new_ac_present = -1; - int new_battery_present = -1; - int id_min = 0, id_max = MAX_SBS_BAT - 1; - char dir_name[32]; - int do_battery_init = 0, do_ac_init = 0; - int old_remaining_capacity = 0; - int update_battery = 1; - int up_tm = update_time; - - if (sbs_zombie(sbs)) { - goto end; - } - - if (id >= 0) { - id_min = id_max = id; - } - - if (data_type == DATA_TYPE_COMMON && up_tm > 0) { - cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); - if (sbs->run_cnt % cnt != 0) { - update_battery = 0; - } - } - - sbs->run_cnt++; - - old_ac_present = sbs->ac.ac_present; - - result = acpi_ac_get_present(sbs); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_ac_get_present() failed")); - } - - new_ac_present = sbs->ac.ac_present; - - do_ac_init = (old_ac_present != new_ac_present); - if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) { - do_ac_init = 1; - } - - if (do_ac_init) { - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_AC_NOTIFY_STATUS, - new_ac_present, - ACPI_AC_DIR_NAME, - ACPI_AC_CLASS); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_generate_event() failed")); - } - } - - if (data_type == DATA_TYPE_COMMON) { - if (!do_ac_init && !update_battery) { - goto end; - } - } - - if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) { - goto end; - } - - for (id = id_min; id <= id_max; id++) { - battery = &sbs->battery[id]; - if (battery->alive == 0) { - continue; - } - - old_remaining_capacity = battery->state.remaining_capacity; - - old_battery_present = battery->battery_present; - - result = acpi_battery_select(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_select() failed")); - } - - result = acpi_battery_get_present(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_present() failed")); - } - - new_battery_present = battery->battery_present; - - do_battery_init = ((old_battery_present != new_battery_present) - && new_battery_present); - if (!new_battery_present) - goto event; - if (do_ac_init || do_battery_init) { - result = acpi_battery_init(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_init() " - "failed")); - } - } - if (sbs_zombie(sbs)) { - goto end; - } - - if ((data_type == DATA_TYPE_COMMON - || data_type == DATA_TYPE_INFO) - && new_battery_present) { - result = acpi_battery_get_info(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_info() failed")); - } - } - if (data_type == DATA_TYPE_INFO) { - continue; - } - if (sbs_zombie(sbs)) { - goto end; - } - - if ((data_type == DATA_TYPE_COMMON - || data_type == DATA_TYPE_STATE) - && new_battery_present) { - result = acpi_battery_get_state(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_state() failed")); - } - } - if (data_type == DATA_TYPE_STATE) { - goto event; - } - if (sbs_zombie(sbs)) { - goto end; - } - - if ((data_type == DATA_TYPE_COMMON - || data_type == DATA_TYPE_ALARM) - && new_battery_present) { - result = acpi_battery_get_alarm(battery); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_get_alarm() " - "failed")); - } - } - if (data_type == DATA_TYPE_ALARM) { - continue; - } - if (sbs_zombie(sbs)) { - goto end; - } - - event: - - if (old_battery_present != new_battery_present || do_ac_init || - old_remaining_capacity != - battery->state.remaining_capacity) { - sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_BATTERY_NOTIFY_STATUS, - new_battery_present, - dir_name, - ACPI_BATTERY_CLASS); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_generate_event() " - "failed")); - } + int id; + struct acpi_sbs *sbs = context; + struct acpi_battery *bat; + u8 saved_charger_state = sbs->charger_present; + u8 saved_battery_state; + acpi_ac_get_present(sbs); + if (sbs->charger_present != saved_charger_state) { + acpi_bus_generate_proc_event4(ACPI_AC_CLASS, ACPI_AC_DIR_NAME, + ACPI_SBS_NOTIFY_STATUS, + sbs->charger_present); +#ifdef ENABLE_POWER_SUPPLY + kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); +#endif + } + if (sbs->manager_present) { + for (id = 0; id < MAX_SBS_BAT; ++id) { + if (!(sbs->batteries_supported & (1 << id))) + continue; + bat = &sbs->battery[id]; + saved_battery_state = bat->present; + acpi_battery_read(bat); + if (saved_battery_state == bat->present) + continue; + acpi_bus_generate_proc_event4(ACPI_BATTERY_CLASS, + bat->name, + ACPI_SBS_NOTIFY_STATUS, + bat->present); +#ifdef ENABLE_POWER_SUPPLY + kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); +#endif } } - - end: - - return result; } -static void acpi_sbs_update_time(void *data) -{ - struct acpi_sbs *sbs = data; - unsigned long delay = -1; - int result; - unsigned int up_tm = update_time; - - if (sbs_mutex_lock(sbs)) - return; - - result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_update_run() failed")); - } - - if (sbs_zombie(sbs)) { - goto end; - } - - if (!up_tm) { - if (timer_pending(&sbs->update_timer)) - del_timer(&sbs->update_timer); - } else { - delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); - delay = jiffies + HZ * delay; - if (timer_pending(&sbs->update_timer)) { - mod_timer(&sbs->update_timer, delay); - } else { - sbs->update_timer.data = (unsigned long)data; - sbs->update_timer.function = acpi_sbs_update_time_run; - sbs->update_timer.expires = delay; - add_timer(&sbs->update_timer); - } - } - - end: - - sbs_mutex_unlock(sbs); -} +static int acpi_sbs_remove(struct acpi_device *device, int type); static int acpi_sbs_add(struct acpi_device *device) { - struct acpi_sbs *sbs = NULL; - int result = 0, remove_result = 0; + struct acpi_sbs *sbs; + int result = 0; int id; - acpi_status status = AE_OK; - unsigned long val; - - status = - acpi_evaluate_integer(device->handle, "_EC", NULL, &val); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC")); - return -EIO; - } sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL); if (!sbs) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed")); result = -ENOMEM; goto end; } - mutex_init(&sbs->mutex); - - sbs_mutex_lock(sbs); + mutex_init(&sbs->lock); - sbs->base = 0xff & (val >> 8); + sbs->hc = acpi_driver_data(device->parent); sbs->device = device; - strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_SBS_CLASS); acpi_driver_data(device) = sbs; - result = acpi_ac_add(sbs); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed")); - goto end; - } - - acpi_sbsm_get_info(sbs); - - if (!sbs->sbsm_present) { - result = acpi_battery_add(sbs, 0); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_add() failed")); - goto end; - } - } else { - for (id = 0; id < MAX_SBS_BAT; id++) { - if ((sbs->sbsm_batteries_supported & (1 << id))) { - result = acpi_battery_add(sbs, id); - if (result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_battery_add() failed")); - goto end; - } - } - } - } - - init_timer(&sbs->update_timer); - result = acpi_check_update_proc(sbs); + result = acpi_charger_add(sbs); if (result) goto end; + result = acpi_manager_get_info(sbs); + if (!result) { + sbs->manager_present = 1; + for (id = 0; id < MAX_SBS_BAT; ++id) + if ((sbs->batteries_supported & (1 << id))) + acpi_battery_add(sbs, id); + } else + acpi_battery_add(sbs, 0); + acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); end: - - sbs_mutex_unlock(sbs); - - if (result) { - remove_result = acpi_sbs_remove(device, 0); - if (remove_result) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_sbs_remove() failed")); - } - } - + if (result) + acpi_sbs_remove(device, 0); return result; } @@ -1713,39 +972,25 @@ static int acpi_sbs_remove(struct acpi_d struct acpi_sbs *sbs; int id; - if (!device) { + if (!device) return -EINVAL; - } - sbs = acpi_driver_data(device); - if (!sbs) { + if (!sbs) return -EINVAL; - } - - sbs_mutex_lock(sbs); - - sbs->zombie = 1; - del_timer_sync(&sbs->update_timer); - acpi_os_wait_events_complete(NULL); - del_timer_sync(&sbs->update_timer); - - for (id = 0; id < MAX_SBS_BAT; id++) { + mutex_lock(&sbs->lock); + acpi_smbus_unregister_callback(sbs->hc); + for (id = 0; id < MAX_SBS_BAT; ++id) acpi_battery_remove(sbs, id); - } - - acpi_ac_remove(sbs); - - sbs_mutex_unlock(sbs); - - mutex_destroy(&sbs->mutex); - + acpi_charger_remove(sbs); + mutex_unlock(&sbs->lock); + mutex_destroy(&sbs->lock); kfree(sbs); - return 0; } static void acpi_sbs_rmdirs(void) { +#ifdef CONFIG_ACPI_PROCFS if (acpi_ac_dir) { acpi_unlock_ac_dir(acpi_ac_dir); acpi_ac_dir = NULL; @@ -1754,69 +999,58 @@ static void acpi_sbs_rmdirs(void) acpi_unlock_battery_dir(acpi_battery_dir); acpi_battery_dir = NULL; } +#endif } static int acpi_sbs_resume(struct acpi_device *device) { struct acpi_sbs *sbs; - if (!device) return -EINVAL; - sbs = device->driver_data; - - sbs->run_cnt = 0; - + acpi_sbs_callback(sbs); return 0; } +static struct acpi_driver acpi_sbs_driver = { + .name = "sbs", + .class = ACPI_SBS_CLASS, + .ids = sbs_device_ids, + .ops = { + .add = acpi_sbs_add, + .remove = acpi_sbs_remove, + .resume = acpi_sbs_resume, + }, +}; + static int __init acpi_sbs_init(void) { int result = 0; if (acpi_disabled) return -ENODEV; - - if (capacity_mode != DEF_CAPACITY_UNIT - && capacity_mode != MAH_CAPACITY_UNIT - && capacity_mode != MWH_CAPACITY_UNIT) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "invalid capacity_mode = %d", capacity_mode)); - return -EINVAL; - } - +#ifdef CONFIG_ACPI_PROCFS acpi_ac_dir = acpi_lock_ac_dir(); - if (!acpi_ac_dir) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_lock_ac_dir() failed")); + if (!acpi_ac_dir) return -ENODEV; - } - acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_lock_battery_dir() failed")); acpi_sbs_rmdirs(); return -ENODEV; } - +#endif result = acpi_bus_register_driver(&acpi_sbs_driver); if (result < 0) { - ACPI_EXCEPTION((AE_INFO, AE_ERROR, - "acpi_bus_register_driver() failed")); acpi_sbs_rmdirs(); return -ENODEV; } - return 0; } static void __exit acpi_sbs_exit(void) { acpi_bus_unregister_driver(&acpi_sbs_driver); - acpi_sbs_rmdirs(); - return; } diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c new file mode 100644 index 0000000..046d7c3 --- /dev/null +++ b/drivers/acpi/sbshc.c @@ -0,0 +1,309 @@ +/* + * SMBus driver for ACPI Embedded Controller (v0.1) + * + * Copyright (c) 2007 Alexey Starikovskiy + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "sbshc.h" + +#define ACPI_SMB_HC_CLASS "smbus_host_controller" +#define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC" + +struct acpi_smb_hc { + struct acpi_ec *ec; + struct mutex lock; + wait_queue_head_t wait; + u8 offset; + u8 query_bit; + smbus_alarm_callback callback; + void *context; +}; + +static int acpi_smbus_hc_add(struct acpi_device *device); +static int acpi_smbus_hc_remove(struct acpi_device *device, int type); + +static const struct acpi_device_id sbs_device_ids[] = { + {"ACPI0001", 0}, + {"ACPI0005", 0}, + {"", 0}, +}; + +MODULE_DEVICE_TABLE(acpi, sbs_device_ids); + +static struct acpi_driver acpi_smb_hc_driver = { + .name = "smbus_hc", + .class = ACPI_SMB_HC_CLASS, + .ids = sbs_device_ids, + .ops = { + .add = acpi_smbus_hc_add, + .remove = acpi_smbus_hc_remove, + }, +}; + +union acpi_smb_status { + u8 raw; + struct { + u8 status:5; + u8 reserved:1; + u8 alarm:1; + u8 done:1; + } fields; +}; + +enum acpi_smb_status_codes { + SMBUS_OK = 0, + SMBUS_UNKNOWN_FAILURE = 0x07, + SMBUS_DEVICE_ADDRESS_NACK = 0x10, + SMBUS_DEVICE_ERROR = 0x11, + SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12, + SMBUS_UNKNOWN_ERROR = 0x13, + SMBUS_DEVICE_ACCESS_DENIED = 0x17, + SMBUS_TIMEOUT = 0x18, + SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19, + SMBUS_BUSY = 0x1a, + SMBUS_PEC_ERROR = 0x1f, +}; + +enum acpi_smb_offset { + ACPI_SMB_PROTOCOL = 0, /* protocol, PEC */ + ACPI_SMB_STATUS = 1, /* status */ + ACPI_SMB_ADDRESS = 2, /* address */ + ACPI_SMB_COMMAND = 3, /* command */ + ACPI_SMB_DATA = 4, /* 32 data registers */ + ACPI_SMB_BLOCK_COUNT = 0x24, /* number of data bytes */ + ACPI_SMB_ALARM_ADDRESS = 0x25, /* alarm address */ + ACPI_SMB_ALARM_DATA = 0x26, /* 2 bytes alarm data */ +}; + +static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data) +{ + return ec_read(hc->offset + address, data); +} + +static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data) +{ + return ec_write(hc->offset + address, data); +} + +static inline int smb_check_done(struct acpi_smb_hc *hc) +{ + union acpi_smb_status status = {.raw = 0}; + smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw); + return status.fields.done && (status.fields.status == SMBUS_OK); +} + +static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout) +{ + if (wait_event_timeout(hc->wait, smb_check_done(hc), + msecs_to_jiffies(timeout))) + return 0; + else + return -ETIME; +} + +int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 *data, u8 length) +{ + int ret = -EFAULT, i; + u8 temp, sz = 0; + + mutex_lock(&hc->lock); + if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp)) + goto end; + if (temp) { + ret = -EBUSY; + goto end; + } + smb_hc_write(hc, ACPI_SMB_COMMAND, command); + smb_hc_write(hc, ACPI_SMB_COMMAND, command); + if (!(protocol & 0x01)) { + smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length); + for (i = 0; i < length; ++i) + smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]); + } + smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1); + smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol); + /* + * Wait for completion. Save the status code, data size, + * and data into the return package (if required by the protocol). + */ + ret = wait_transaction_complete(hc, 1000); + if (ret || !(protocol & 0x01)) + goto end; + switch (protocol) { + case SMBUS_RECEIVE_BYTE: + case SMBUS_READ_BYTE: + sz = 1; + break; + case SMBUS_READ_WORD: + sz = 2; + break; + case SMBUS_READ_BLOCK: + if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) { + ret = -EFAULT; + goto end; + } + sz &= 0x1f; + break; + } + for (i = 0; i < sz; ++i) + smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]); + end: + mutex_unlock(&hc->lock); + return ret; +} + +int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 *data) +{ + return acpi_smbus_transaction(hc, protocol, address, command, data, 0); +} + +EXPORT_SYMBOL_GPL(acpi_smbus_read); + +int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 *data, u8 length) +{ + return acpi_smbus_transaction(hc, protocol, address, command, data, length); +} + +EXPORT_SYMBOL_GPL(acpi_smbus_write); + +int acpi_smbus_register_callback(struct acpi_smb_hc *hc, + smbus_alarm_callback callback, void *context) +{ + mutex_lock(&hc->lock); + hc->callback = callback; + hc->context = context; + mutex_unlock(&hc->lock); + return 0; +} + +EXPORT_SYMBOL_GPL(acpi_smbus_register_callback); + +int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc) +{ + mutex_lock(&hc->lock); + hc->callback = NULL; + hc->context = NULL; + mutex_unlock(&hc->lock); + return 0; +} + +EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback); + +static void acpi_smbus_callback(void *context) +{ + struct acpi_smb_hc *hc = context; + + if (hc->callback) + hc->callback(hc->context); +} + +static int smbus_alarm(void *context) +{ + struct acpi_smb_hc *hc = context; + union acpi_smb_status status; + if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw)) + return 0; + /* Check if it is only a completion notify */ + if (status.fields.done) + wake_up(&hc->wait); + if (!status.fields.alarm) + return 0; + mutex_lock(&hc->lock); + smb_hc_write(hc, ACPI_SMB_STATUS, status.raw); + if (hc->callback) + acpi_os_execute(OSL_GPE_HANDLER, acpi_smbus_callback, hc); + mutex_unlock(&hc->lock); + return 0; +} + +typedef int (*acpi_ec_query_func) (void *data); + +extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, + acpi_handle handle, acpi_ec_query_func func, + void *data); + +static int acpi_smbus_hc_add(struct acpi_device *device) +{ + int status; + unsigned long val; + struct acpi_smb_hc *hc; + + if (!device) + return -EINVAL; + + status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "error obtaining _EC.\n"); + return -EIO; + } + + strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS); + + hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL); + if (!hc) + return -ENOMEM; + mutex_init(&hc->lock); + init_waitqueue_head(&hc->wait); + + hc->ec = acpi_driver_data(device->parent); + hc->offset = (val >> 8) & 0xff; + hc->query_bit = val & 0xff; + acpi_driver_data(device) = hc; + + acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); + printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n", + hc->ec, hc->offset, hc->query_bit); + + return 0; +} + +extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); + +static int acpi_smbus_hc_remove(struct acpi_device *device, int type) +{ + struct acpi_smb_hc *hc; + + if (!device) + return -EINVAL; + + hc = acpi_driver_data(device); + acpi_ec_remove_query_handler(hc->ec, hc->query_bit); + kfree(hc); + return 0; +} + +static int __init acpi_smb_hc_init(void) +{ + int result; + + result = acpi_bus_register_driver(&acpi_smb_hc_driver); + if (result < 0) + return -ENODEV; + return 0; +} + +static void __exit acpi_smb_hc_exit(void) +{ + acpi_bus_unregister_driver(&acpi_smb_hc_driver); +} + +module_init(acpi_smb_hc_init); +module_exit(acpi_smb_hc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexey Starikovskiy"); +MODULE_DESCRIPTION("ACPI SMBus HC driver"); diff --git a/drivers/acpi/sbshc.h b/drivers/acpi/sbshc.h new file mode 100644 index 0000000..3bda349 --- /dev/null +++ b/drivers/acpi/sbshc.h @@ -0,0 +1,27 @@ +struct acpi_smb_hc; +enum acpi_smb_protocol { + SMBUS_WRITE_QUICK = 2, + SMBUS_READ_QUICK = 3, + SMBUS_SEND_BYTE = 4, + SMBUS_RECEIVE_BYTE = 5, + SMBUS_WRITE_BYTE = 6, + SMBUS_READ_BYTE = 7, + SMBUS_WRITE_WORD = 8, + SMBUS_READ_WORD = 9, + SMBUS_WRITE_BLOCK = 0xa, + SMBUS_READ_BLOCK = 0xb, + SMBUS_PROCESS_CALL = 0xc, + SMBUS_BLOCK_PROCESS_CALL = 0xd, +}; + +static const u8 SMBUS_PEC = 0x80; + +typedef void (*smbus_alarm_callback)(void *context); + +extern int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 * data); +extern int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 slave_address, + u8 command, u8 * data, u8 length); +extern int acpi_smbus_register_callback(struct acpi_smb_hc *hc, + smbus_alarm_callback callback, void *context); +extern int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index bc6d586..69ec73b 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -195,6 +195,7 @@ struct acpi_thermal { struct acpi_thermal_trips trips; struct acpi_handle_list devices; struct timer_list timer; + struct mutex lock; }; static const struct file_operations acpi_thermal_state_fops = { @@ -711,6 +712,7 @@ static void acpi_thermal_check(void *dat int result = 0; struct acpi_thermal *tz = data; unsigned long sleep_time = 0; + unsigned long timeout_jiffies = 0; int i = 0; struct acpi_thermal_state state; @@ -720,11 +722,15 @@ static void acpi_thermal_check(void *dat return; } + /* Check if someone else is already running */ + if (!mutex_trylock(&tz->lock)) + return; + state = tz->state; result = acpi_thermal_get_temperature(tz); if (result) - return; + goto unlock; memset(&tz->state, 0, sizeof(tz->state)); @@ -787,10 +793,13 @@ static void acpi_thermal_check(void *dat * a thermal event occurs). Note that _TSP and _TZD values are * given in 1/10th seconds (we must covert to milliseconds). */ - if (tz->state.passive) + if (tz->state.passive) { sleep_time = tz->trips.passive.tsp * 100; - else if (tz->polling_frequency > 0) + timeout_jiffies = jiffies + (HZ * sleep_time) / 1000; + } else if (tz->polling_frequency > 0) { sleep_time = tz->polling_frequency * 100; + timeout_jiffies = round_jiffies(jiffies + (HZ * sleep_time) / 1000); + } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n", tz->name, tz->temperature, sleep_time)); @@ -804,17 +813,16 @@ static void acpi_thermal_check(void *dat del_timer(&(tz->timer)); } else { if (timer_pending(&(tz->timer))) - mod_timer(&(tz->timer), - jiffies + (HZ * sleep_time) / 1000); + mod_timer(&(tz->timer), timeout_jiffies); else { tz->timer.data = (unsigned long)tz; tz->timer.function = acpi_thermal_run; - tz->timer.expires = jiffies + (HZ * sleep_time) / 1000; + tz->timer.expires = timeout_jiffies; add_timer(&(tz->timer)); } } - - return; + unlock: + mutex_unlock(&tz->lock); } /* -------------------------------------------------------------------------- @@ -1251,7 +1259,7 @@ static int acpi_thermal_add(struct acpi_ strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); acpi_driver_data(device) = tz; - + mutex_init(&tz->lock); result = acpi_thermal_get_info(tz); if (result) goto end; @@ -1321,7 +1329,7 @@ static int acpi_thermal_remove(struct ac } acpi_thermal_remove_fs(device); - + mutex_destroy(&tz->lock); kfree(tz); return 0; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index d05891f..a36e992 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -409,14 +409,16 @@ acpi_video_device_lcd_query_levels(struc static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) { - int status; + int status = AE_OK; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; arg0.integer.value = level; - status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL); - + if (device->cap._BCM) + status = acpi_evaluate_object(device->dev->handle, "_BCM", + &args, NULL); + device->brightness->curr = level; return status; } @@ -424,11 +426,11 @@ static int acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, unsigned long *level) { - int status; - - status = acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, level); - - return status; + if (device->cap._BQC) + return acpi_evaluate_integer(device->dev->handle, "_BQC", NULL, + level); + *level = device->brightness->curr; + return AE_OK; } static int @@ -1633,9 +1635,20 @@ static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event) { - int min, max, min_above, max_below, i, l; + int min, max, min_above, max_below, i, l, delta = 255; max = max_below = 0; min = min_above = 255; + /* Find closest level to level_current */ + for (i = 0; i < device->brightness->count; i++) { + l = device->brightness->levels[i]; + if (abs(l - level_current) < abs(delta)) { + delta = l - level_current; + if (!delta) + break; + } + } + /* Ajust level_current to closest available level */ + level_current += delta; for (i = 0; i < device->brightness->count; i++) { l = device->brightness->levels[i]; if (l < min) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 73e248f..e0a1ff9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -111,6 +111,21 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. +config FUJITSU_LAPTOP + tristate "Fujitsu Laptop Extras" + depends on X86 + depends on ACPI + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by Fujitsu: + + * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks + * Possibly other Fujitsu laptop models + + It adds support for LCD brightness control. + + If you have a Fujitsu laptop, say Y or M here. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on X86 @@ -134,6 +149,7 @@ config SONY_LAPTOP tristate "Sony Laptop Extras" depends on X86 && ACPI select BACKLIGHT_CLASS_DEVICE + depends on INPUT ---help--- This mini-driver drives the SNC and SPIC devices present in the ACPI BIOS of the Sony Vaio laptops. @@ -156,6 +172,7 @@ config THINKPAD_ACPI select BACKLIGHT_CLASS_DEVICE select HWMON select NVRAM + depends on INPUT ---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/Makefile b/drivers/misc/Makefile index b5ce0e3..be90d48 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c new file mode 100644 index 0000000..d366a6c --- /dev/null +++ b/drivers/misc/fujitsu-laptop.c @@ -0,0 +1,358 @@ +/*-*-linux-c-*-*/ + +/* + Copyright (C) 2007 Jonathan Woithe + Based on earlier work: + Copyright (C) 2003 Shane Spencer + Adrian Yee + + Templated from msi-laptop.c which is copyright by its respective authors. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +/* + * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional + * features made available on a range of Fujitsu laptops including the + * P2xxx/P5xxx/S6xxx/S7xxx series. + * + * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/; + * others may be added at a later date. + * + * lcd_level - Screen brightness: contains a single integer in the + * range 0..7. (rw) + * + * In addition to these platform device attributes the driver + * registers itself in the Linux backlight control subsystem and is + * available to userspace under /sys/class/backlight/fujitsu-laptop/. + * + * This driver has been tested on a Fujitsu Lifebook S7020. It should + * work on most P-series and S-series Lifebooks, but YMMV. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUJITSU_DRIVER_VERSION "0.3" + +#define FUJITSU_LCD_N_LEVELS 8 + +#define ACPI_FUJITSU_CLASS "fujitsu" +#define ACPI_FUJITSU_HID "FUJ02B1" +#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver" +#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" + +struct fujitsu_t { + acpi_handle acpi_handle; + struct backlight_device *bl_device; + struct platform_device *pf_device; + + unsigned long fuj02b1_state; + unsigned int brightness_changed; + unsigned int brightness_level; +}; + +static struct fujitsu_t *fujitsu; + +/* Hardware access */ + +static int set_lcd_level(int level) +{ + acpi_status status = AE_OK; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { 1, &arg0 }; + acpi_handle handle = NULL; + + if (level < 0 || level >= FUJITSU_LCD_N_LEVELS) + return -EINVAL; + + if (!fujitsu) + return -EINVAL; + + status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n")); + return -ENODEV; + } + + arg0.integer.value = level; + + status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; +} + +static int get_lcd_level(void) +{ + unsigned long state = 0; + acpi_status status = AE_OK; + + // Get the Brightness + status = + acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); + if (status < 0) + return status; + + fujitsu->fuj02b1_state = state; + fujitsu->brightness_level = state & 0x0fffffff; + + if (state & 0x80000000) + fujitsu->brightness_changed = 1; + else + fujitsu->brightness_changed = 0; + + if (status < 0) + return status; + + return fujitsu->brightness_level; +} + +/* Backlight device stuff */ + +static int bl_get_brightness(struct backlight_device *b) +{ + return get_lcd_level(); +} + +static int bl_update_status(struct backlight_device *b) +{ + return set_lcd_level(b->props.brightness); +} + +static struct backlight_ops fujitsubl_ops = { + .get_brightness = bl_get_brightness, + .update_status = bl_update_status, +}; + +/* Platform device */ + +static ssize_t show_lcd_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret; + + ret = get_lcd_level(); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t store_lcd_level(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + + int level, ret; + + if (sscanf(buf, "%i", &level) != 1 + || (level < 0 || level >= FUJITSU_LCD_N_LEVELS)) + return -EINVAL; + + ret = set_lcd_level(level); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); + +static struct attribute *fujitsupf_attributes[] = { + &dev_attr_lcd_level.attr, + NULL +}; + +static struct attribute_group fujitsupf_attribute_group = { + .attrs = fujitsupf_attributes +}; + +static struct platform_driver fujitsupf_driver = { + .driver = { + .name = "fujitsu-laptop", + .owner = THIS_MODULE, + } +}; + +/* ACPI device */ + +int acpi_fujitsu_add(struct acpi_device *device) +{ + int result = 0; + int state = 0; + + ACPI_FUNCTION_TRACE("acpi_fujitsu_add"); + + if (!device) + return -EINVAL; + + fujitsu->acpi_handle = device->handle; + sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); + sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); + acpi_driver_data(device) = fujitsu; + + result = acpi_bus_get_power(fujitsu->acpi_handle, &state); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error reading power state\n")); + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + acpi_device_name(device), acpi_device_bid(device), + !device->power.state ? "on" : "off"); + + end: + + return result; +} + +int acpi_fujitsu_remove(struct acpi_device *device, int type) +{ + ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + fujitsu->acpi_handle = 0; + + return 0; +} + +static const struct acpi_device_id fujitsu_device_ids[] = { + {ACPI_FUJITSU_HID, 0}, + {"", 0}, +}; + +static struct acpi_driver acpi_fujitsu_driver = { + .name = ACPI_FUJITSU_DRIVER_NAME, + .class = ACPI_FUJITSU_CLASS, + .ids = fujitsu_device_ids, + .ops = { + .add = acpi_fujitsu_add, + .remove = acpi_fujitsu_remove, + }, +}; + +/* Initialization */ + +static int __init fujitsu_init(void) +{ + int ret, result; + + if (acpi_disabled) + return -ENODEV; + + fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); + if (!fujitsu) + return -ENOMEM; + memset(fujitsu, 0, sizeof(struct fujitsu_t)); + + result = acpi_bus_register_driver(&acpi_fujitsu_driver); + if (result < 0) { + ret = -ENODEV; + goto fail_acpi; + } + + /* Register backlight stuff */ + + fujitsu->bl_device = + backlight_device_register("fujitsu-laptop", NULL, NULL, + &fujitsubl_ops); + if (IS_ERR(fujitsu->bl_device)) + return PTR_ERR(fujitsu->bl_device); + + fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1; + ret = platform_driver_register(&fujitsupf_driver); + if (ret) + goto fail_backlight; + + /* Register platform stuff */ + + fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); + if (!fujitsu->pf_device) { + ret = -ENOMEM; + goto fail_platform_driver; + } + + ret = platform_device_add(fujitsu->pf_device); + if (ret) + goto fail_platform_device1; + + ret = + sysfs_create_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); + if (ret) + goto fail_platform_device2; + + printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION + " successfully loaded.\n"); + + return 0; + + fail_platform_device2: + + platform_device_del(fujitsu->pf_device); + + fail_platform_device1: + + platform_device_put(fujitsu->pf_device); + + fail_platform_driver: + + platform_driver_unregister(&fujitsupf_driver); + + fail_backlight: + + backlight_device_unregister(fujitsu->bl_device); + + fail_acpi: + + kfree(fujitsu); + + return ret; +} + +static void __exit fujitsu_cleanup(void) +{ + sysfs_remove_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); + platform_device_unregister(fujitsu->pf_device); + platform_driver_unregister(&fujitsupf_driver); + backlight_device_unregister(fujitsu->bl_device); + + acpi_bus_unregister_driver(&acpi_fujitsu_driver); + + kfree(fujitsu); + + printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); +} + +module_init(fujitsu_init); +module_exit(fujitsu_cleanup); + +MODULE_AUTHOR("Jonathan Woithe"); +MODULE_DESCRIPTION("Fujitsu laptop extras support"); +MODULE_VERSION(FUJITSU_DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index d38ddce..f248080 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -1173,7 +1173,8 @@ #define SONYPI_TYPE2_OFFSET 0x12 #define SONYPI_TYPE3_OFFSET 0x12 struct sony_pic_ioport { - struct acpi_resource_io io; + struct acpi_resource_io io1; + struct acpi_resource_io io2; struct list_head list; }; @@ -1443,11 +1444,11 @@ static u8 sony_pic_call1(u8 dev) { u8 v1, v2; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4); - v2 = inb_p(spic_dev.cur_ioport->io.minimum); + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4); + v2 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1); return v2; } @@ -1456,13 +1457,13 @@ static u8 sony_pic_call2(u8 dev, u8 fn) { u8 v1; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(fn, spic_dev.cur_ioport->io.minimum); - v1 = inb_p(spic_dev.cur_ioport->io.minimum); + outb(fn, spic_dev.cur_ioport->io1.minimum); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call2: 0x%.4x\n", v1); return v1; } @@ -1471,13 +1472,13 @@ static u8 sony_pic_call3(u8 dev, u8 fn, { u8 v1; - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io.minimum + 4); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(fn, spic_dev.cur_ioport->io.minimum); - wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); - outb(v, spic_dev.cur_ioport->io.minimum); - v1 = inb_p(spic_dev.cur_ioport->io.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io1.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io1.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); + outb(v, spic_dev.cur_ioport->io1.minimum); + v1 = inb_p(spic_dev.cur_ioport->io1.minimum); dprintk("sony_pic_call3: 0x%.4x\n", v1); return v1; } @@ -2074,7 +2075,18 @@ sony_pic_read_possible_resource(struct a switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: + { + /* start IO enumeration */ + struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); + if (!ioport) + return AE_ERROR; + + list_add(&ioport->list, &dev->ioports); + return AE_OK; + } + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + /* end IO enumeration */ return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: @@ -2101,7 +2113,7 @@ sony_pic_read_possible_resource(struct a if (!interrupt) return AE_ERROR; - list_add_tail(&interrupt->list, &dev->interrupts); + list_add(&interrupt->list, &dev->interrupts); interrupt->irq.triggering = p->triggering; interrupt->irq.polarity = p->polarity; interrupt->irq.sharable = p->sharable; @@ -2113,18 +2125,27 @@ sony_pic_read_possible_resource(struct a case ACPI_RESOURCE_TYPE_IO: { struct acpi_resource_io *io = &resource->data.io; - struct sony_pic_ioport *ioport = NULL; + struct sony_pic_ioport *ioport = + list_first_entry(&dev->ioports, struct sony_pic_ioport, list); if (!io) { dprintk("Blank IO resource\n"); return AE_OK; } - ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); - if (!ioport) + if (!ioport->io1.minimum) { + memcpy(&ioport->io1, io, sizeof(*io)); + dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum, + ioport->io1.address_length); + } + else if (!ioport->io2.minimum) { + memcpy(&ioport->io2, io, sizeof(*io)); + dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum, + ioport->io2.address_length); + } + else { + printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); return AE_ERROR; - - list_add_tail(&ioport->list, &dev->ioports); - memcpy(&ioport->io, io, sizeof(*io)); + } return AE_OK; } default: @@ -2199,10 +2220,22 @@ static int sony_pic_enable(struct acpi_d { acpi_status status; int result = 0; + /* Type 1 resource layout is: + * IO + * IO + * IRQNoFlags + * End + * + * Type 2 and 3 resource layout is: + * IO + * IRQNoFlags + * End + */ struct { - struct acpi_resource io_res; - struct acpi_resource irq_res; - struct acpi_resource end; + struct acpi_resource res1; + struct acpi_resource res2; + struct acpi_resource res3; + struct acpi_resource res4; } *resource; struct acpi_buffer buffer = { 0, NULL }; @@ -2217,21 +2250,49 @@ static int sony_pic_enable(struct acpi_d buffer.length = sizeof(*resource) + 1; buffer.pointer = resource; - /* setup io resource */ - resource->io_res.type = ACPI_RESOURCE_TYPE_IO; - resource->io_res.length = sizeof(struct acpi_resource); - memcpy(&resource->io_res.data.io, &ioport->io, - sizeof(struct acpi_resource_io)); + /* setup Type 1 resources */ + if (spic_dev.model == SONYPI_DEVICE_TYPE1) { - /* setup irq resource */ - resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ; - resource->irq_res.length = sizeof(struct acpi_resource); - memcpy(&resource->irq_res.data.irq, &irq->irq, - sizeof(struct acpi_resource_irq)); - /* we requested a shared irq */ - resource->irq_res.data.irq.sharable = ACPI_SHARED; + /* setup io resources */ + resource->res1.type = ACPI_RESOURCE_TYPE_IO; + resource->res1.length = sizeof(struct acpi_resource); + memcpy(&resource->res1.data.io, &ioport->io1, + sizeof(struct acpi_resource_io)); - resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; + resource->res2.type = ACPI_RESOURCE_TYPE_IO; + resource->res2.length = sizeof(struct acpi_resource); + memcpy(&resource->res2.data.io, &ioport->io2, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->res3.type = ACPI_RESOURCE_TYPE_IRQ; + resource->res3.length = sizeof(struct acpi_resource); + memcpy(&resource->res3.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->res3.data.irq.sharable = ACPI_SHARED; + + resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG; + + } + /* setup Type 2/3 resources */ + else { + /* setup io resource */ + resource->res1.type = ACPI_RESOURCE_TYPE_IO; + resource->res1.length = sizeof(struct acpi_resource); + memcpy(&resource->res1.data.io, &ioport->io1, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->res2.type = ACPI_RESOURCE_TYPE_IRQ; + resource->res2.length = sizeof(struct acpi_resource); + memcpy(&resource->res2.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->res2.data.irq.sharable = ACPI_SHARED; + + resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG; + } /* Attempt to set the resource */ dprintk("Evaluating _SRS\n"); @@ -2239,7 +2300,7 @@ static int sony_pic_enable(struct acpi_d /* check for total failure */ if (ACPI_FAILURE(status)) { - printk(KERN_ERR DRV_PFX "Error evaluating _SRS"); + printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); result = -ENODEV; goto end; } @@ -2268,11 +2329,14 @@ static irqreturn_t sony_pic_irq(int irq, struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; - ev = inb_p(dev->cur_ioport->io.minimum); - data_mask = inb_p(dev->cur_ioport->io.minimum + dev->evport_offset); + ev = inb_p(dev->cur_ioport->io1.minimum); + if (dev->cur_ioport->io2.minimum) + data_mask = inb_p(dev->cur_ioport->io2.minimum); + else + data_mask = inb_p(dev->cur_ioport->io1.minimum + dev->evport_offset); dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", - ev, data_mask, dev->cur_ioport->io.minimum, dev->evport_offset); + ev, data_mask, dev->cur_ioport->io1.minimum, dev->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; @@ -2323,8 +2387,11 @@ static int sony_pic_remove(struct acpi_d } free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); - release_region(spic_dev.cur_ioport->io.minimum, - spic_dev.cur_ioport->io.address_length); + release_region(spic_dev.cur_ioport->io1.minimum, + spic_dev.cur_ioport->io1.address_length); + if (spic_dev.cur_ioport->io2.minimum) + release_region(spic_dev.cur_ioport->io2.minimum, + spic_dev.cur_ioport->io2.address_length); sonypi_compat_exit(); @@ -2397,14 +2464,36 @@ static int sony_pic_add(struct acpi_devi goto err_remove_input; /* request io port */ - list_for_each_entry(io, &spic_dev.ioports, list) { - if (request_region(io->io.minimum, io->io.address_length, + list_for_each_entry_reverse(io, &spic_dev.ioports, list) { + if (request_region(io->io1.minimum, io->io1.address_length, "Sony Programable I/O Device")) { - dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n", - io->io.minimum, io->io.maximum, - io->io.address_length); - spic_dev.cur_ioport = io; - break; + dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io1.minimum, io->io1.maximum, + io->io1.address_length); + /* Type 1 have 2 ioports */ + if (io->io2.minimum) { + if (request_region(io->io2.minimum, + io->io2.address_length, + "Sony Programable I/O Device")) { + dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io2.minimum, io->io2.maximum, + io->io2.address_length); + spic_dev.cur_ioport = io; + break; + } + else { + dprintk("Unable to get I/O port2: " + "0x%.4x (0x%.4x) + 0x%.2x\n", + io->io2.minimum, io->io2.maximum, + io->io2.address_length); + release_region(io->io1.minimum, + io->io1.address_length); + } + } + else { + spic_dev.cur_ioport = io; + break; + } } } if (!spic_dev.cur_ioport) { @@ -2414,7 +2503,7 @@ static int sony_pic_add(struct acpi_devi } /* request IRQ */ - list_for_each_entry(irq, &spic_dev.interrupts, list) { + list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, IRQF_SHARED, "sony-laptop", &spic_dev)) { dprintk("IRQ: %d - triggering: %d - " @@ -2462,8 +2551,11 @@ err_free_irq: free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); err_release_region: - release_region(spic_dev.cur_ioport->io.minimum, - spic_dev.cur_ioport->io.address_length); + release_region(spic_dev.cur_ioport->io1.minimum, + spic_dev.cur_ioport->io1.address_length); + if (spic_dev.cur_ioport->io2.minimum) + release_region(spic_dev.cur_ioport->io2.minimum, + spic_dev.cur_ioport->io2.address_length); err_remove_compat: sonypi_compat_exit(); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 0222bba..81693b4 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define IBM_VERSION "0.16" -#define TPACPI_SYSFS_VERSION 0x010000 +#define TPACPI_SYSFS_VERSION 0x020000 /* * Changelog: @@ -117,6 +117,12 @@ IBM_BIOS_MODULE_ALIAS("K[U,X-Z]"); #define __unused __attribute__ ((unused)) +static enum { + TPACPI_LIFE_INIT = 0, + TPACPI_LIFE_RUNNING, + TPACPI_LIFE_EXITING, +} tpacpi_lifecycle; + /**************************************************************************** **************************************************************************** * @@ -342,6 +348,9 @@ static void dispatch_acpi_notify(acpi_ha { struct ibm_struct *ibm = data; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + if (!ibm || !ibm->acpi || !ibm->acpi->notify) return; @@ -517,8 +526,10 @@ static char *next_cmd(char **cmds) ****************************************************************************/ static struct platform_device *tpacpi_pdev; +static struct platform_device *tpacpi_sensors_pdev; static struct class_device *tpacpi_hwmon; static struct input_dev *tpacpi_inputdev; +static struct mutex tpacpi_inputdev_send_mutex; static int tpacpi_resume_handler(struct platform_device *pdev) @@ -543,6 +554,12 @@ static struct platform_driver tpacpi_pdr .resume = tpacpi_resume_handler, }; +static struct platform_driver tpacpi_hwmon_pdriver = { + .driver = { + .name = IBM_HWMON_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; /************************************************************************* * thinkpad-acpi driver attributes @@ -989,6 +1006,7 @@ #define TPACPI_HOTKEY_MAP_TYPESIZE sizeo int res, i; int status; + int hkeyv; vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); @@ -1014,18 +1032,35 @@ #define TPACPI_HOTKEY_MAP_TYPESIZE sizeo return res; /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - A30, R30, R31, T20-22, X20-21, X22-24 */ - tp_features.hotkey_mask = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking + for HKEY interface version 0x100 */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) != 1) { + printk(IBM_ERR "unknown version of the " + "HKEY interface: 0x%x\n", hkeyv); + printk(IBM_ERR "please report this to %s\n", + IBM_MAIL); + } else { + /* + * MHKV 0x100 in A31, R40, R40e, + * T4x, X31, and later + * */ + tp_features.hotkey_mask = 1; + } + } vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", str_supported(tp_features.hotkey_mask)); if (tp_features.hotkey_mask) { - /* MHKA available in A31, R40, R40e, T4x, X31, and later */ if (!acpi_evalf(hkey_handle, &hotkey_all_mask, - "MHKA", "qd")) + "MHKA", "qd")) { + printk(IBM_ERR + "missing MHKA handler, " + "please report this to %s\n", + IBM_MAIL); hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ + } } res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); @@ -1131,6 +1166,8 @@ static void tpacpi_input_send_key(unsign unsigned int keycode) { if (keycode != KEY_RESERVED) { + mutex_lock(&tpacpi_inputdev_send_mutex); + input_report_key(tpacpi_inputdev, keycode, 1); if (keycode == KEY_UNKNOWN) input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, @@ -1142,6 +1179,8 @@ static void tpacpi_input_send_key(unsign input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); } } @@ -1149,18 +1188,47 @@ static void tpacpi_input_send_radiosw(vo { int wlsw; - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) + mutex_lock(&tpacpi_inputdev_send_mutex); + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { input_report_switch(tpacpi_inputdev, SW_RADIO, !!wlsw); + input_sync(tpacpi_inputdev); + } + + mutex_unlock(&tpacpi_inputdev_send_mutex); } static void hotkey_notify(struct ibm_struct *ibm, u32 event) { u32 hkey; unsigned int keycode, scancode; - int send_acpi_ev = 0; + int send_acpi_ev; + int ignore_acpi_ev; + + if (event != 0x80) { + printk(IBM_ERR "unknown HKEY notification event %d\n", event); + /* forward it to userspace, maybe it knows how to handle it */ + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, 0); + return; + } + + while (1) { + if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { + printk(IBM_ERR "failed to retrieve HKEY event\n"); + return; + } + + if (hkey == 0) { + /* queue empty */ + return; + } + + send_acpi_ev = 0; + ignore_acpi_ev = 0; - if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { switch (hkey >> 12) { case 1: /* 0x1000-0x1FFF: key presses */ @@ -1182,9 +1250,11 @@ static void hotkey_notify(struct ibm_str * eat up known LID events */ if (hkey != 0x5001 && hkey != 0x5002) { printk(IBM_ERR - "unknown LID-related hotkey event: 0x%04x\n", - hkey); + "unknown LID-related HKEY event: 0x%04x\n", + hkey); send_acpi_ev = 1; + } else { + ignore_acpi_ev = 1; } break; case 7: @@ -1202,21 +1272,18 @@ static void hotkey_notify(struct ibm_str printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey); send_acpi_ev = 1; } - } else { - printk(IBM_ERR "unknown hotkey notification event %d\n", event); - hkey = 0; - send_acpi_ev = 1; - } - /* Legacy events */ - if (send_acpi_ev || hotkey_report_mode < 2) - acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey); + /* Legacy events */ + if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) { + acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey); + } - /* netlink events */ - if (send_acpi_ev) { - acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, - ibm->acpi->device->dev.bus_id, - event, hkey); + /* netlink events */ + if (!ignore_acpi_ev && send_acpi_ev) { + acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, + ibm->acpi->device->dev.bus_id, + event, hkey); + } } } @@ -2812,7 +2879,7 @@ static int __init thermal_init(struct ib switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); if (res) return res; @@ -2820,7 +2887,7 @@ static int __init thermal_init(struct ib case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input8_group); if (res) return res; @@ -2837,13 +2904,13 @@ static void thermal_exit(void) { switch(thermal_read_mode) { case TPACPI_THERMAL_TPEC_16: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_TPEC_8: case TPACPI_THERMAL_ACPI_TMP07: case TPACPI_THERMAL_ACPI_UPDT: - sysfs_remove_group(&tpacpi_pdev->dev.kobj, + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &thermal_temp_input16_group); break; case TPACPI_THERMAL_NONE: @@ -3626,7 +3693,7 @@ static struct device_attribute dev_attr_ __ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); -/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_fan_watchdog_show(struct device_driver *drv, char *buf) { @@ -3768,10 +3835,10 @@ static int __init fan_init(struct ibm_in if (fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE) { - rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); if (!(rc < 0)) - rc = driver_create_file(&tpacpi_pdriver.driver, + rc = driver_create_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); if (rc < 0) return rc; @@ -3854,8 +3921,8 @@ static void fan_exit(void) vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); /* FIXME: can we really do this unconditionally? */ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); - driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog); cancel_delayed_work(&fan_watchdog_task); flush_scheduled_work(); @@ -3888,6 +3955,9 @@ static void fan_watchdog_fire(struct wor { int rc; + if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) + return; + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); rc = fan_set_enable(); if (rc < 0) { @@ -3908,7 +3978,8 @@ static void fan_watchdog_reset(void) if (fan_watchdog_active) cancel_delayed_work(&fan_watchdog_task); - if (fan_watchdog_maxinterval > 0) { + if (fan_watchdog_maxinterval > 0 && + tpacpi_lifecycle != TPACPI_LIFE_EXITING) { fan_watchdog_active = 1; if (!schedule_delayed_work(&fan_watchdog_task, msecs_to_jiffies(fan_watchdog_maxinterval @@ -4302,6 +4373,19 @@ static struct ibm_struct fan_driver_data **************************************************************************** ****************************************************************************/ +/* sysfs name ---------------------------------------------------------- */ +static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", IBM_NAME); +} + +static struct device_attribute dev_attr_thinkpad_acpi_pdev_name = + __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL); + +/* --------------------------------------------------------------------- */ + /* /proc support */ static struct proc_dir_entry *proc_dir; @@ -4674,6 +4758,8 @@ static int __init thinkpad_acpi_module_i { int ret, i; + tpacpi_lifecycle = TPACPI_LIFE_INIT; + /* Parameter checking */ if (hotkey_report_mode > 2) return -EINVAL; @@ -4702,19 +4788,31 @@ static int __init thinkpad_acpi_module_i ret = platform_driver_register(&tpacpi_pdriver); if (ret) { - printk(IBM_ERR "unable to register platform driver\n"); + printk(IBM_ERR "unable to register main platform driver\n"); thinkpad_acpi_module_exit(); return ret; } tp_features.platform_drv_registered = 1; + ret = platform_driver_register(&tpacpi_hwmon_pdriver); + if (ret) { + printk(IBM_ERR "unable to register hwmon platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdrv_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (!ret) { + tp_features.platform_drv_attrs_registered = 1; + ret = tpacpi_create_driver_attributes(&tpacpi_hwmon_pdriver.driver); + } if (ret) { printk(IBM_ERR "unable to create sysfs driver attributes\n"); thinkpad_acpi_module_exit(); return ret; } - tp_features.platform_drv_attrs_registered = 1; + tp_features.sensors_pdrv_attrs_registered = 1; /* Device initialization */ @@ -4727,7 +4825,26 @@ static int __init thinkpad_acpi_module_i thinkpad_acpi_module_exit(); return ret; } - tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + tpacpi_sensors_pdev = platform_device_register_simple( + IBM_HWMON_DRVR_NAME, + -1, NULL, 0); + if (IS_ERR(tpacpi_sensors_pdev)) { + ret = PTR_ERR(tpacpi_sensors_pdev); + tpacpi_sensors_pdev = NULL; + printk(IBM_ERR "unable to register hwmon platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = device_create_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (ret) { + printk(IBM_ERR + "unable to create sysfs hwmon device attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tp_features.sensors_pdev_attrs_registered = 1; + tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev); if (IS_ERR(tpacpi_hwmon)) { ret = PTR_ERR(tpacpi_hwmon); tpacpi_hwmon = NULL; @@ -4735,6 +4852,7 @@ static int __init thinkpad_acpi_module_i thinkpad_acpi_module_exit(); return ret; } + mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { printk(IBM_ERR "unable to allocate input device\n"); @@ -4769,6 +4887,7 @@ static int __init thinkpad_acpi_module_i tp_features.input_device_registered = 1; } + tpacpi_lifecycle = TPACPI_LIFE_RUNNING; return 0; } @@ -4776,6 +4895,8 @@ static void thinkpad_acpi_module_exit(vo { struct ibm_struct *ibm, *itmp; + tpacpi_lifecycle = TPACPI_LIFE_EXITING; + list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers) { @@ -4794,12 +4915,22 @@ static void thinkpad_acpi_module_exit(vo if (tpacpi_hwmon) hwmon_device_unregister(tpacpi_hwmon); + if (tp_features.sensors_pdev_attrs_registered) + device_remove_file(&tpacpi_sensors_pdev->dev, + &dev_attr_thinkpad_acpi_pdev_name); + if (tpacpi_sensors_pdev) + platform_device_unregister(tpacpi_sensors_pdev); if (tpacpi_pdev) platform_device_unregister(tpacpi_pdev); + if (tp_features.sensors_pdrv_attrs_registered) + tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver); if (tp_features.platform_drv_attrs_registered) tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + if (tp_features.sensors_pdrv_registered) + platform_driver_unregister(&tpacpi_hwmon_pdriver); + if (tp_features.platform_drv_registered) platform_driver_unregister(&tpacpi_pdriver); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index 082a1cb..c5fdd68 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h @@ -58,13 +58,14 @@ #include #define IBM_NAME "thinkpad" #define IBM_DESC "ThinkPad ACPI Extras" -#define IBM_FILE "thinkpad_acpi" +#define IBM_FILE IBM_NAME "_acpi" #define IBM_URL "http://ibm-acpi.sf.net/" #define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net" #define IBM_PROC_DIR "ibm" #define IBM_ACPI_EVENT_PREFIX "ibm" #define IBM_DRVR_NAME IBM_FILE +#define IBM_HWMON_DRVR_NAME IBM_NAME "_hwmon" #define IBM_LOG IBM_FILE ": " #define IBM_ERR KERN_ERR IBM_LOG @@ -171,6 +172,7 @@ static int parse_strtoul(const char *buf /* Device model */ static struct platform_device *tpacpi_pdev; +static struct platform_device *tpacpi_sensors_pdev; static struct class_device *tpacpi_hwmon; static struct platform_driver tpacpi_pdriver; static struct input_dev *tpacpi_inputdev; @@ -233,22 +235,25 @@ struct ibm_init_struct { static struct { #ifdef CONFIG_THINKPAD_ACPI_BAY - u16 bay_status:1; - u16 bay_eject:1; - u16 bay_status2:1; - u16 bay_eject2:1; + u32 bay_status:1; + u32 bay_eject:1; + u32 bay_status2:1; + u32 bay_eject2:1; #endif - u16 bluetooth:1; - u16 hotkey:1; - u16 hotkey_mask:1; - u16 hotkey_wlsw:1; - u16 light:1; - u16 light_status:1; - u16 wan:1; - u16 fan_ctrl_status_undef:1; - u16 input_device_registered:1; - u16 platform_drv_registered:1; - u16 platform_drv_attrs_registered:1; + u32 bluetooth:1; + u32 hotkey:1; + u32 hotkey_mask:1; + u32 hotkey_wlsw:1; + u32 light:1; + u32 light_status:1; + u32 wan:1; + u32 fan_ctrl_status_undef:1; + u32 input_device_registered:1; + u32 platform_drv_registered:1; + u32 platform_drv_attrs_registered:1; + u32 sensors_pdrv_registered:1; + u32 sensors_pdrv_attrs_registered:1; + u32 sensors_pdev_attrs_registered:1; } tp_features; struct thinkpad_id_data { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 86aea44..8a388e7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -333,6 +333,7 @@ int acpi_bus_get_power(acpi_handle handl int acpi_bus_set_power(acpi_handle handle, int state); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); +int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); int acpi_bus_receive_event(struct acpi_bus_event *event); #else static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data)