diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 14 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/ac.c | 33 | ||||
-rw-r--r-- | drivers/acpi/battery.c | 1038 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 23 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 93 | ||||
-rw-r--r-- | drivers/acpi/events/evevent.c | 6 | ||||
-rw-r--r-- | drivers/acpi/hardware/hwregs.c | 89 | ||||
-rw-r--r-- | drivers/acpi/hardware/hwsleep.c | 40 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 8 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 22 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 488 | ||||
-rw-r--r-- | drivers/acpi/sbs.c | 1869 | ||||
-rw-r--r-- | drivers/acpi/sbshc.c | 309 | ||||
-rw-r--r-- | drivers/acpi/sbshc.h | 27 | ||||
-rw-r--r-- | drivers/acpi/sleep/main.c | 6 | ||||
-rw-r--r-- | drivers/acpi/sleep/sleep.h | 1 | ||||
-rw-r--r-- | drivers/acpi/sleep/wakeup.c | 117 | ||||
-rw-r--r-- | drivers/acpi/tables/tbutils.c | 2 | ||||
-rw-r--r-- | drivers/acpi/thermal.c | 28 | ||||
-rw-r--r-- | drivers/acpi/video.c | 30 |
21 files changed, 2008 insertions, 2236 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 4875f01..b833891 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -88,7 +88,7 @@ config ACPI_PROC_EVENT config ACPI_AC tristate "AC Adapter" - depends on X86 + depends on X86 && POWER_SUPPLY default y help This driver adds support for the AC Adapter object, which indicates @@ -97,7 +97,7 @@ config ACPI_AC config ACPI_BATTERY tristate "Battery" - depends on X86 + depends on X86 && POWER_SUPPLY default y help This driver adds support for battery information through @@ -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 @@ -349,12 +350,11 @@ config ACPI_HOTPLUG_MEMORY $>modprobe acpi_memhotplug config ACPI_SBS - tristate "Smart Battery System (EXPERIMENTAL)" + tristate "Smart Battery System" depends on X86 - depends on EXPERIMENTAL + depends on POWER_SUPPLY help - This driver adds support for the Smart Battery System. - A "Smart Battery" is quite old and quite rare compared - to today's ACPI "Control Method" battery. + This driver adds support for the Smart Battery System, another + type of access to battery information, found on some laptops. endif # ACPI 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_acpi.o 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..e03de37 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -29,6 +29,7 @@ #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/power_supply.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -72,16 +73,37 @@ static struct acpi_driver acpi_ac_driver = { }; struct acpi_ac { + struct power_supply charger; 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, }; +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, +}; /* -------------------------------------------------------------------------- AC Adapter Management @@ -208,6 +230,7 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_netlink_event(device->pnp.device_class, device->dev.bus_id, event, (u32) ac->state); + kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -244,7 +267,12 @@ static int acpi_ac_add(struct acpi_device *device) result = acpi_ac_add_fs(device); if (result) goto end; - + 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); status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify, ac); @@ -279,7 +307,8 @@ static int acpi_ac_remove(struct acpi_device *device, int type) status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_ac_notify); - + if (ac->charger.dev) + power_supply_unregister(&ac->charger); acpi_ac_remove_fs(device); kfree(ac); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9b2c0f7..681e26b 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 <astarikovskiy@suse.de> + * Copyright (C) 2004-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com> * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * @@ -27,244 +29,288 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/jiffies.h> + +#ifdef CONFIG_ACPI_PROCFS #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> +#endif #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF +#include <linux/power_supply.h> -#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 <astarikovskiy@suse.de>"); 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; + struct power_supply bat; 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) +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, +}; + +#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 +319,203 @@ static int acpi_battery_get_state(struct acpi_battery *battery) 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; + 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); } + 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 +523,6 @@ static int acpi_battery_print_state(struct seq_file *seq, int result) 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 +531,121 @@ static int acpi_battery_print_alarm(struct seq_file *seq, int result) 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 +670,51 @@ static int acpi_battery_add_fs(struct acpi_device *device) 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 + +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, +}; + /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -858,33 +722,17 @@ static int acpi_battery_remove_fs(struct acpi_device *device) 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; + kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE); } static int acpi_battery_add(struct acpi_device *device) @@ -892,33 +740,27 @@ static int acpi_battery_add(struct acpi_device *device) 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 + 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); status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, acpi_battery_notify, battery); @@ -927,20 +769,16 @@ static int acpi_battery_add(struct acpi_device *device) 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 +789,19 @@ static int acpi_battery_remove(struct acpi_device *device, int type) 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 + if (battery->bat.dev) { + device_remove_file(battery->bat.dev, &alarm_attr); + power_supply_unregister(&battery->bat); + } + mutex_destroy(&battery->lock); kfree(battery); - return 0; } @@ -979,44 +809,48 @@ static int acpi_battery_remove(struct acpi_device *device, int type) 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 cbfc815..fb2cff9 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -286,15 +286,11 @@ DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue); 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; @@ -303,8 +299,8 @@ int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) 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; @@ -315,6 +311,17 @@ int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) 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 acpi_ec *ec, u8 query_bit, 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 acpi_ec *ec, u8 query_bit) 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 Level, void *context, void **retval) status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); if (ACPI_FAILURE(status)) return status; - /* Find and register all query methods */ acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, acpi_ec_register_query_methods, ec, NULL); - /* Use the global lock for all EC transactions? */ acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); - ec->handle = handle; - - 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_device *device) 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_device *device, int type) 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 *resource, void *context) 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 acpi_ec *ec) return -ENODEV; } + ec->handlers_installed = 1; return 0; } @@ -823,41 +830,22 @@ static int acpi_ec_start(struct acpi_device *device) 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/events/evevent.c b/drivers/acpi/events/evevent.c index a1f87b5..e412878 100644 --- a/drivers/acpi/events/evevent.c +++ b/drivers/acpi/events/evevent.c @@ -239,10 +239,8 @@ u32 acpi_ev_fixed_event_detect(void) * Read the fixed feature status and enable registers, as all the cases * depend on their values. Ignore errors here. */ - (void)acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_STATUS, &fixed_status); - (void)acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_ENABLE, &fixed_enable); + (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status); + (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable); ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, "Fixed Event Block: Enable %08X Status %08X\n", diff --git a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/hardware/hwregs.c index 1d371fa..73f9c5f 100644 --- a/drivers/acpi/hardware/hwregs.c +++ b/drivers/acpi/hardware/hwregs.c @@ -75,8 +75,7 @@ acpi_status acpi_hw_clear_acpi_status(void) lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_STATUS, + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, ACPI_BITMASK_ALL_FIXED_STATUS); if (ACPI_FAILURE(status)) { goto unlock_and_exit; @@ -259,7 +258,7 @@ struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) * ******************************************************************************/ -acpi_status acpi_get_register(u32 register_id, u32 * return_value) +acpi_status acpi_get_register_unlocked(u32 register_id, u32 * return_value) { u32 register_value = 0; struct acpi_bit_register_info *bit_reg_info; @@ -276,8 +275,7 @@ acpi_status acpi_get_register(u32 register_id, u32 * return_value) /* Read from the register */ - status = acpi_hw_register_read(ACPI_MTX_LOCK, - bit_reg_info->parent_register, + status = acpi_hw_register_read(bit_reg_info->parent_register, ®ister_value); if (ACPI_SUCCESS(status)) { @@ -298,6 +296,16 @@ acpi_status acpi_get_register(u32 register_id, u32 * return_value) return_ACPI_STATUS(status); } +acpi_status acpi_get_register(u32 register_id, u32 * return_value) +{ + acpi_status status; + acpi_cpu_flags flags; + flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + status = acpi_get_register_unlocked(register_id, return_value); + acpi_os_release_lock(acpi_gbl_hardware_lock, flags); + return status; +} + ACPI_EXPORT_SYMBOL(acpi_get_register) /******************************************************************************* @@ -335,8 +343,7 @@ acpi_status acpi_set_register(u32 register_id, u32 value) /* Always do a register read first so we can insert the new bits */ - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - bit_reg_info->parent_register, + status = acpi_hw_register_read(bit_reg_info->parent_register, ®ister_value); if (ACPI_FAILURE(status)) { goto unlock_and_exit; @@ -363,8 +370,7 @@ acpi_status acpi_set_register(u32 register_id, u32 value) bit_reg_info-> access_bit_mask); if (value) { - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_STATUS, + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, (u16) value); register_value = 0; } @@ -377,8 +383,7 @@ acpi_status acpi_set_register(u32 register_id, u32 value) bit_reg_info->access_bit_mask, value); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_ENABLE, + status = acpi_hw_register_write(ACPI_REGISTER_PM1_ENABLE, (u16) register_value); break; @@ -397,15 +402,13 @@ acpi_status acpi_set_register(u32 register_id, u32 value) bit_reg_info->access_bit_mask, value); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL, (u16) register_value); break; case ACPI_REGISTER_PM2_CONTROL: - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM2_CONTROL, + status = acpi_hw_register_read(ACPI_REGISTER_PM2_CONTROL, ®ister_value); if (ACPI_FAILURE(status)) { goto unlock_and_exit; @@ -430,8 +433,7 @@ acpi_status acpi_set_register(u32 register_id, u32 value) xpm2_control_block. address))); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM2_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM2_CONTROL, (u8) (register_value)); break; @@ -461,8 +463,7 @@ ACPI_EXPORT_SYMBOL(acpi_set_register) * * FUNCTION: acpi_hw_register_read * - * PARAMETERS: use_lock - Lock hardware? True/False - * register_id - ACPI Register ID + * PARAMETERS: register_id - ACPI Register ID * return_value - Where the register value is returned * * RETURN: Status and the value read. @@ -471,19 +472,14 @@ ACPI_EXPORT_SYMBOL(acpi_set_register) * ******************************************************************************/ acpi_status -acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) +acpi_hw_register_read(u32 register_id, u32 * return_value) { u32 value1 = 0; u32 value2 = 0; acpi_status status; - acpi_cpu_flags lock_flags = 0; ACPI_FUNCTION_TRACE(hw_register_read); - if (ACPI_MTX_LOCK == use_lock) { - lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); - } - switch (register_id) { case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ @@ -491,7 +487,7 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) acpi_hw_low_level_read(16, &value1, &acpi_gbl_FADT.xpm1a_event_block); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* PM1B is optional */ @@ -507,7 +503,7 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) status = acpi_hw_low_level_read(16, &value1, &acpi_gbl_xpm1a_enable); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* PM1B is optional */ @@ -523,7 +519,7 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) acpi_hw_low_level_read(16, &value1, &acpi_gbl_FADT.xpm1a_control_block); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } status = @@ -558,10 +554,7 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) break; } - unlock_and_exit: - if (ACPI_MTX_LOCK == use_lock) { - acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); - } + exit: if (ACPI_SUCCESS(status)) { *return_value = value1; @@ -574,8 +567,7 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) * * FUNCTION: acpi_hw_register_write * - * PARAMETERS: use_lock - Lock hardware? True/False - * register_id - ACPI Register ID + * PARAMETERS: register_id - ACPI Register ID * Value - The value to write * * RETURN: Status @@ -597,28 +589,22 @@ acpi_hw_register_read(u8 use_lock, u32 register_id, u32 * return_value) * ******************************************************************************/ -acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) +acpi_status acpi_hw_register_write(u32 register_id, u32 value) { acpi_status status; - acpi_cpu_flags lock_flags = 0; u32 read_value; ACPI_FUNCTION_TRACE(hw_register_write); - if (ACPI_MTX_LOCK == use_lock) { - lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); - } - switch (register_id) { case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ /* Perform a read first to preserve certain bits (per ACPI spec) */ - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_STATUS, + status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &read_value); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* Insert the bits to be preserved */ @@ -632,7 +618,7 @@ acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) acpi_hw_low_level_write(16, value, &acpi_gbl_FADT.xpm1a_event_block); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* PM1B is optional */ @@ -647,7 +633,7 @@ acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) status = acpi_hw_low_level_write(16, value, &acpi_gbl_xpm1a_enable); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* PM1B is optional */ @@ -661,11 +647,10 @@ acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) /* * Perform a read first to preserve certain bits (per ACPI spec) */ - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_CONTROL, + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, &read_value); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } /* Insert the bits to be preserved */ @@ -679,7 +664,7 @@ acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) acpi_hw_low_level_write(16, value, &acpi_gbl_FADT.xpm1a_control_block); if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + goto exit; } status = @@ -728,11 +713,7 @@ acpi_status acpi_hw_register_write(u8 use_lock, u32 register_id, u32 value) break; } - unlock_and_exit: - if (ACPI_MTX_LOCK == use_lock) { - acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); - } - + exit: return_ACPI_STATUS(status); } diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index cf69c00..81b2484 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -234,15 +234,11 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) "While executing method _SST")); } - /* - * 1) Disable/Clear all GPEs - */ + /* Disable/Clear all GPEs */ + status = acpi_hw_disable_all_gpes(); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - return_ACPI_STATUS(AE_OK); + return_ACPI_STATUS(status); } ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) @@ -313,8 +309,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) /* Get current value of PM1A control */ - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -341,15 +336,13 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) /* Write #1: fill in SLP_TYP data */ - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1A_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1B_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); @@ -364,15 +357,13 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) ACPI_FLUSH_CPU_CACHE(); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1A_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1B_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); @@ -392,8 +383,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) */ acpi_os_stall(10000000); - status = acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_CONTROL, + status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL, sleep_enable_reg_info-> access_bit_mask); if (ACPI_FAILURE(status)) { @@ -404,7 +394,8 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) /* Wait until we enter sleep state */ do { - status = acpi_get_register(ACPI_BITREG_WAKE_STATUS, &in_value); + status = acpi_get_register_unlocked(ACPI_BITREG_WAKE_STATUS, + &in_value); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -520,8 +511,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) /* Get current value of PM1A control */ - status = acpi_hw_register_read(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1_CONTROL, + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); if (ACPI_SUCCESS(status)) { @@ -543,11 +533,9 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) /* Just ignore any errors */ - (void)acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1A_CONTROL, + (void)acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); - (void)acpi_hw_register_write(ACPI_MTX_DO_NOT_LOCK, - ACPI_REGISTER_PM1B_CONTROL, + (void)acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); } } diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 352cf81..aabc6ca 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1043,14 +1043,6 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); /* - * max_cstate is defined in the base kernel so modules can - * change it w/o depending on the state of the processor module. - */ -unsigned int max_cstate = ACPI_PROCESSOR_MAX_POWER; - -EXPORT_SYMBOL(max_cstate); - -/* * Acquire a spinlock. * * handle is a pointer to the spinlock_t. diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 9f11dc2..a735108 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -44,6 +44,7 @@ #include <linux/seq_file.h> #include <linux/dmi.h> #include <linux/moduleparam.h> +#include <linux/cpuidle.h> #include <asm/io.h> #include <asm/system.h> @@ -1049,11 +1050,13 @@ static int __init acpi_processor_init(void) return -ENOMEM; acpi_processor_dir->owner = THIS_MODULE; + result = cpuidle_register_driver(&acpi_idle_driver); + if (result < 0) + goto out_proc; + result = acpi_bus_register_driver(&acpi_processor_driver); - if (result < 0) { - remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); - return result; - } + if (result < 0) + goto out_cpuidle; acpi_processor_install_hotplug_notify(); @@ -1062,11 +1065,18 @@ static int __init acpi_processor_init(void) acpi_processor_ppc_init(); return 0; + +out_cpuidle: + cpuidle_unregister_driver(&acpi_idle_driver); + +out_proc: + remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); + + return result; } static void __exit acpi_processor_exit(void) { - acpi_processor_ppc_exit(); acpi_thermal_cpufreq_exit(); @@ -1075,6 +1085,8 @@ static void __exit acpi_processor_exit(void) acpi_bus_unregister_driver(&acpi_processor_driver); + cpuidle_unregister_driver(&acpi_idle_driver); + remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); return; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 1f6fb38d..f996d0e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -40,6 +40,7 @@ #include <linux/sched.h> /* need_resched() */ #include <linux/latency.h> #include <linux/clockchips.h> +#include <linux/cpuidle.h> /* * Include the apic definitions for x86 to have the APIC timer related defines @@ -64,14 +65,22 @@ ACPI_MODULE_NAME("processor_idle"); #define ACPI_PROCESSOR_FILE_POWER "power" #define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000) #define PM_TIMER_TICK_NS (1000000000ULL/PM_TIMER_FREQUENCY) +#ifndef CONFIG_CPU_IDLE #define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */ #define C3_OVERHEAD 4 /* 1us (3.579 ticks per us) */ static void (*pm_idle_save) (void) __read_mostly; -module_param(max_cstate, uint, 0644); +#else +#define C2_OVERHEAD 1 /* 1us */ +#define C3_OVERHEAD 1 /* 1us */ +#endif +#define PM_TIMER_TICKS_TO_US(p) (((p) * 1000)/(PM_TIMER_FREQUENCY/1000)) +static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; +module_param(max_cstate, uint, 0000); static unsigned int nocst __read_mostly; module_param(nocst, uint, 0000); +#ifndef CONFIG_CPU_IDLE /* * bm_history -- bit-mask with a bit per jiffy of bus-master activity * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms @@ -82,9 +91,10 @@ module_param(nocst, uint, 0000); static unsigned int bm_history __read_mostly = (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1)); module_param(bm_history, uint, 0644); -/* -------------------------------------------------------------------------- - Power Management - -------------------------------------------------------------------------- */ + +static int acpi_processor_set_power_policy(struct acpi_processor *pr); + +#endif /* * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. @@ -177,6 +187,18 @@ static inline u32 ticks_elapsed(u32 t1, u32 t2) return ((0xFFFFFFFF - t1) + t2); } +static inline u32 ticks_elapsed_in_us(u32 t1, u32 t2) +{ + if (t2 >= t1) + return PM_TIMER_TICKS_TO_US(t2 - t1); + else if (!(acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER)) + return PM_TIMER_TICKS_TO_US(((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); + else + return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2); +} + +#ifndef CONFIG_CPU_IDLE + static void acpi_processor_power_activate(struct acpi_processor *pr, struct acpi_processor_cx *new) @@ -248,6 +270,7 @@ static void acpi_cstate_enter(struct acpi_processor_cx *cstate) unused = inl(acpi_gbl_FADT.xpm_timer_block.address); } } +#endif /* !CONFIG_CPU_IDLE */ #ifdef ARCH_APICTIMER_STOPS_ON_C3 @@ -330,6 +353,7 @@ int acpi_processor_resume(struct acpi_device * device) return 0; } +#ifndef CONFIG_CPU_IDLE static void acpi_processor_idle(void) { struct acpi_processor *pr = NULL; @@ -427,7 +451,7 @@ static void acpi_processor_idle(void) * an SMP system. We do it here instead of doing it at _CST/P_LVL * detection phase, to work cleanly with logical CPU hotplug. */ - if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && + if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && !pr->flags.has_cst && !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) cx = &pr->power.states[ACPI_STATE_C1]; #endif @@ -727,6 +751,7 @@ static int acpi_processor_set_power_policy(struct acpi_processor *pr) return 0; } +#endif /* !CONFIG_CPU_IDLE */ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) { @@ -744,7 +769,7 @@ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) #ifndef CONFIG_HOTPLUG_CPU /* * Check for P_LVL2_UP flag before entering C2 and above on - * an SMP system. + * an SMP system. */ if ((num_online_cpus() > 1) && !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) @@ -945,7 +970,12 @@ static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx) * Normalize the C2 latency to expidite policy */ cx->valid = 1; + +#ifndef CONFIG_CPU_IDLE cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); +#else + cx->latency_ticks = cx->latency; +#endif return; } @@ -1025,7 +1055,12 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, * use this in our C3 policy */ cx->valid = 1; + +#ifndef CONFIG_CPU_IDLE cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); +#else + cx->latency_ticks = cx->latency; +#endif return; } @@ -1090,6 +1125,7 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) pr->power.count = acpi_processor_power_verify(pr); +#ifndef CONFIG_CPU_IDLE /* * Set Default Policy * ------------------ @@ -1101,6 +1137,7 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) result = acpi_processor_set_power_policy(pr); if (result) return result; +#endif /* * if one state of type C2 or C3 is available, mark this @@ -1117,35 +1154,6 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) return 0; } -int acpi_processor_cst_has_changed(struct acpi_processor *pr) -{ - int result = 0; - - - if (!pr) - return -EINVAL; - - if (nocst) { - return -ENODEV; - } - - if (!pr->flags.power_setup_done) - return -ENODEV; - - /* Fall back to the default idle loop */ - pm_idle = pm_idle_save; - synchronize_sched(); /* Relies on interrupts forcing exit from idle. */ - - pr->flags.power = 0; - result = acpi_processor_get_power_info(pr); - if ((pr->flags.power == 1) && (pr->flags.power_setup_done)) - pm_idle = acpi_processor_idle; - - return result; -} - -/* proc interface */ - static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) { struct acpi_processor *pr = seq->private; @@ -1227,6 +1235,35 @@ static const struct file_operations acpi_processor_power_fops = { .release = single_release, }; +#ifndef CONFIG_CPU_IDLE + +int acpi_processor_cst_has_changed(struct acpi_processor *pr) +{ + int result = 0; + + + if (!pr) + return -EINVAL; + + if (nocst) { + return -ENODEV; + } + + if (!pr->flags.power_setup_done) + return -ENODEV; + + /* Fall back to the default idle loop */ + pm_idle = pm_idle_save; + synchronize_sched(); /* Relies on interrupts forcing exit from idle. */ + + pr->flags.power = 0; + result = acpi_processor_get_power_info(pr); + if ((pr->flags.power == 1) && (pr->flags.power_setup_done)) + pm_idle = acpi_processor_idle; + + return result; +} + #ifdef CONFIG_SMP static void smp_callback(void *v) { @@ -1249,7 +1286,366 @@ static int acpi_processor_latency_notify(struct notifier_block *b, static struct notifier_block acpi_processor_latency_notifier = { .notifier_call = acpi_processor_latency_notify, }; + +#endif + +#else /* CONFIG_CPU_IDLE */ + +/** + * acpi_idle_bm_check - checks if bus master activity was detected + */ +static int acpi_idle_bm_check(void) +{ + u32 bm_status = 0; + + acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); + if (bm_status) + acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); + /* + * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect + * the true state of bus mastering activity; forcing us to + * manually check the BMIDEA bit of each IDE channel. + */ + else if (errata.piix4.bmisx) { + if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) + || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) + bm_status = 1; + } + return bm_status; +} + +/** + * acpi_idle_update_bm_rld - updates the BM_RLD bit depending on target state + * @pr: the processor + * @target: the new target state + */ +static inline void acpi_idle_update_bm_rld(struct acpi_processor *pr, + struct acpi_processor_cx *target) +{ + if (pr->flags.bm_rld_set && target->type != ACPI_STATE_C3) { + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); + pr->flags.bm_rld_set = 0; + } + + if (!pr->flags.bm_rld_set && target->type == ACPI_STATE_C3) { + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1); + pr->flags.bm_rld_set = 1; + } +} + +/** + * acpi_idle_do_entry - a helper function that does C2 and C3 type entry + * @cx: cstate data + */ +static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) +{ + if (cx->space_id == ACPI_CSTATE_FFH) { + /* Call into architectural FFH based C-state */ + acpi_processor_ffh_cstate_enter(cx); + } else { + int unused; + /* IO port based C-state */ + inb(cx->address); + /* Dummy wait op - must do something useless after P_LVL2 read + because chipsets cannot guarantee that STPCLK# signal + gets asserted in time to freeze execution properly. */ + unused = inl(acpi_gbl_FADT.xpm_timer_block.address); + } +} + +/** + * acpi_idle_enter_c1 - enters an ACPI C1 state-type + * @dev: the target CPU + * @state: the state data + * + * This is equivalent to the HALT instruction. + */ +static int acpi_idle_enter_c1(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (pr->flags.bm_check) + acpi_idle_update_bm_rld(pr, cx); + + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + if (!need_resched()) + safe_halt(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + return 0; +} + +/** + * acpi_idle_enter_simple - enters an ACPI state without BM handling + * @dev: the target CPU + * @state: the state data + */ +static int acpi_idle_enter_simple(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + u32 t1, t2; + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (acpi_idle_suspend) + return(acpi_idle_enter_c1(dev, state)); + + if (pr->flags.bm_check) + acpi_idle_update_bm_rld(pr, cx); + + local_irq_disable(); + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + if (unlikely(need_resched())) { + current_thread_info()->status |= TS_POLLING; + local_irq_enable(); + return 0; + } + + if (cx->type == ACPI_STATE_C3) + ACPI_FLUSH_CPU_CACHE(); + + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_state_timer_broadcast(pr, cx, 1); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + +#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) + /* TSC could halt in idle, so notify users */ + mark_tsc_unstable("TSC halts in idle");; +#endif + + local_irq_enable(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + acpi_state_timer_broadcast(pr, cx, 0); + cx->time += ticks_elapsed(t1, t2); + return ticks_elapsed_in_us(t1, t2); +} + +static int c3_cpu_count; +static DEFINE_SPINLOCK(c3_lock); + +/** + * acpi_idle_enter_bm - enters C3 with proper BM handling + * @dev: the target CPU + * @state: the state data + * + * If BM is detected, the deepest non-C3 idle state is entered instead. + */ +static int acpi_idle_enter_bm(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct acpi_processor *pr; + struct acpi_processor_cx *cx = cpuidle_get_statedata(state); + u32 t1, t2; + pr = processors[smp_processor_id()]; + + if (unlikely(!pr)) + return 0; + + if (acpi_idle_suspend) + return(acpi_idle_enter_c1(dev, state)); + + local_irq_disable(); + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + if (unlikely(need_resched())) { + current_thread_info()->status |= TS_POLLING; + local_irq_enable(); + return 0; + } + + /* + * Must be done before busmaster disable as we might need to + * access HPET ! + */ + acpi_state_timer_broadcast(pr, cx, 1); + + if (acpi_idle_bm_check()) { + cx = pr->power.bm_state; + + acpi_idle_update_bm_rld(pr, cx); + + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + } else { + acpi_idle_update_bm_rld(pr, cx); + + spin_lock(&c3_lock); + c3_cpu_count++; + /* Disable bus master arbitration when all CPUs are in C3 */ + if (c3_cpu_count == num_online_cpus()) + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); + spin_unlock(&c3_lock); + + t1 = inl(acpi_gbl_FADT.xpm_timer_block.address); + acpi_idle_do_entry(cx); + t2 = inl(acpi_gbl_FADT.xpm_timer_block.address); + + spin_lock(&c3_lock); + /* Re-enable bus master arbitration */ + if (c3_cpu_count == num_online_cpus()) + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); + c3_cpu_count--; + spin_unlock(&c3_lock); + } + +#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC) + /* TSC could halt in idle, so notify users */ + mark_tsc_unstable("TSC halts in idle"); +#endif + + local_irq_enable(); + current_thread_info()->status |= TS_POLLING; + + cx->usage++; + + acpi_state_timer_broadcast(pr, cx, 0); + cx->time += ticks_elapsed(t1, t2); + return ticks_elapsed_in_us(t1, t2); +} + +struct cpuidle_driver acpi_idle_driver = { + .name = "acpi_idle", + .owner = THIS_MODULE, +}; + +/** + * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE + * @pr: the ACPI processor + */ +static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) +{ + int i, count = 0; + struct acpi_processor_cx *cx; + struct cpuidle_state *state; + struct cpuidle_device *dev = &pr->power.dev; + + if (!pr->flags.power_setup_done) + return -EINVAL; + + if (pr->flags.power == 0) { + return -EINVAL; + } + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + cx = &pr->power.states[i]; + state = &dev->states[count]; + + if (!cx->valid) + continue; + +#ifdef CONFIG_HOTPLUG_CPU + if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && + !pr->flags.has_cst && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) + continue; #endif + cpuidle_set_statedata(state, cx); + + snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); + state->exit_latency = cx->latency; + state->target_residency = cx->latency * 6; + state->power_usage = cx->power; + + state->flags = 0; + switch (cx->type) { + case ACPI_STATE_C1: + state->flags |= CPUIDLE_FLAG_SHALLOW; + state->enter = acpi_idle_enter_c1; + break; + + case ACPI_STATE_C2: + state->flags |= CPUIDLE_FLAG_BALANCED; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->enter = acpi_idle_enter_simple; + break; + + case ACPI_STATE_C3: + state->flags |= CPUIDLE_FLAG_DEEP; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->flags |= CPUIDLE_FLAG_CHECK_BM; + state->enter = pr->flags.bm_check ? + acpi_idle_enter_bm : + acpi_idle_enter_simple; + break; + } + + count++; + } + + dev->state_count = count; + + if (!count) + return -EINVAL; + + /* find the deepest state that can handle active BM */ + if (pr->flags.bm_check) { + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) + if (pr->power.states[i].type == ACPI_STATE_C3) + break; + pr->power.bm_state = &pr->power.states[i-1]; + } + + return 0; +} + +int acpi_processor_cst_has_changed(struct acpi_processor *pr) +{ + int ret; + + if (!pr) + return -EINVAL; + + if (nocst) { + return -ENODEV; + } + + if (!pr->flags.power_setup_done) + return -ENODEV; + + cpuidle_pause_and_lock(); + cpuidle_disable_device(&pr->power.dev); + acpi_processor_get_power_info(pr); + acpi_processor_setup_cpuidle(pr); + ret = cpuidle_enable_device(&pr->power.dev); + cpuidle_resume_and_unlock(); + + return ret; +} + +#endif /* CONFIG_CPU_IDLE */ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device) @@ -1267,7 +1663,7 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, "ACPI: processor limited to max C-state %d\n", max_cstate); first_run++; -#ifdef CONFIG_SMP +#if !defined (CONFIG_CPU_IDLE) && defined (CONFIG_SMP) register_latency_notifier(&acpi_processor_latency_notifier); #endif } @@ -1285,6 +1681,7 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, } acpi_processor_get_power_info(pr); + pr->flags.power_setup_done = 1; /* * Install the idle handler if processor power management is supported. @@ -1292,6 +1689,13 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, * platforms that only support C1. */ if ((pr->flags.power) && (!boot_option_idle_override)) { +#ifdef CONFIG_CPU_IDLE + acpi_processor_setup_cpuidle(pr); + pr->power.dev.cpu = pr->id; + if (cpuidle_register_device(&pr->power.dev)) + return -EIO; +#endif + printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); for (i = 1; i <= pr->power.count; i++) if (pr->power.states[i].valid) @@ -1299,10 +1703,12 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, pr->power.states[i].type); printk(")\n"); +#ifndef CONFIG_CPU_IDLE if (pr->id == 0) { pm_idle_save = pm_idle; pm_idle = acpi_processor_idle; } +#endif } /* 'power' [R] */ @@ -1316,21 +1722,24 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, entry->owner = THIS_MODULE; } - pr->flags.power_setup_done = 1; - return 0; } int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device) { - +#ifdef CONFIG_CPU_IDLE + if ((pr->flags.power) && (!boot_option_idle_override)) + cpuidle_unregister_device(&pr->power.dev); +#endif pr->flags.power_setup_done = 0; if (acpi_device_dir(device)) remove_proc_entry(ACPI_PROCESSOR_FILE_POWER, acpi_device_dir(device)); +#ifndef CONFIG_CPU_IDLE + /* Unregister the idle handler when processor #0 is removed. */ if (pr->id == 0) { pm_idle = pm_idle_save; @@ -1345,6 +1754,7 @@ int acpi_processor_power_exit(struct acpi_processor *pr, unregister_latency_notifier(&acpi_processor_latency_notifier); #endif } +#endif return 0; } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index a578986..90fd09c 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 <astarikovskiy@suse.de> + * Copyright (c) 2005-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com> * Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -26,15 +28,22 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> + +#ifdef CONFIG_ACPI_PROCFS #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> +#endif + #include <linux/acpi.h> #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/delay.h> -#define ACPI_SBS_COMPONENT 0x00080000 +#include <linux/power_supply.h> + +#include "sbshc.h" + #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" @@ -44,836 +53,436 @@ #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 <astarikovskiy@suse.de>"); 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; + struct power_supply bat; 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; + struct power_supply charger; 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; + u8 batteries_supported:4; + u8 manager_present:1; + u8 charger_present:1; }; -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type); -static void acpi_sbs_update_time(void *data); +#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger) -union sbs_rw_data { - u16 word; - u8 block[ACPI_SBS_BLOCK_MAX + 2]; -}; - -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 - -------------------------------------------------------------------------- */ - -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); -} - -static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data) -{ - int err; - - err = ec_write(sbs->base + address, data); - return (err); -} - -static int -acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, - char read_write, u8 command, int size, - union sbs_rw_data *data) -{ - 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); + int scale = 1; + while (log--) + scale *= 10; + return scale; } -static int -acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word) +static inline int acpi_battery_vscale(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_scale((battery->spec & 0x0f00) >> 8); } -static int -acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str) +static inline int acpi_battery_ipscale(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 battery_scale((battery->spec & 0xf000) >> 12); } -static int -acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word) +static inline int acpi_battery_mode(struct acpi_battery *battery) { - 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; + return (battery->mode & 0x8000); } -static int sbs_zombie(struct acpi_sbs *sbs) +static inline int acpi_battery_scale(struct acpi_battery *battery) { - return (sbs->zombie); + return (acpi_battery_mode(battery) ? 10 : 1) * + acpi_battery_ipscale(battery); } -static int sbs_mutex_lock(struct acpi_sbs *sbs) +static int sbs_get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { - 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, +}; - /* 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; - } - 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; + 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; } - - 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; } 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) +static int acpi_ac_get_present(struct acpi_sbs *sbs) { - 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; - } - - 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 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) +static ssize_t acpi_battery_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - int result = 0; - - 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; + 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; - } - - sbs->ac.ac_present = (charger_status & 0x8000) >> 15; - - end: - - return result; + 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; } +static struct device_attribute alarm_attr = { + .attr = {.name = "alarm", .mode = 0644, .owner = THIS_MODULE}, + .show = acpi_battery_alarm_show, + .store = acpi_battery_alarm_store, +}; + /* -------------------------------------------------------------------------- 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 +491,7 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **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 +501,7 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **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 +511,19 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **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 +531,52 @@ acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir, 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; - } - - result = acpi_check_update_proc(sbs); - if (result) - 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")); - } - } + mutex_lock(&sbs->lock); - if (battery->battery_present) { - seq_printf(seq, "present: yes\n"); - } else { - seq_printf(seq, "present: no\n"); + seq_printf(seq, "present: %s\n", + (battery->present) ? "yes" : "no"); + if (!battery->present) 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 +590,29 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset) 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 +626,25 @@ static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) struct acpi_battery *battery = seq->private; struct acpi_sbs *sbs = battery->sbs; int result = 0; - int cscale; - - if (sbs_mutex_lock(sbs)) { - return -ENODEV; - } - - 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")); - } - } + mutex_lock(&sbs->lock); - 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 +656,29 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer, 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 +717,15 @@ static struct proc_dir_entry *acpi_ac_dir = NULL; 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 +742,203 @@ static struct file_operations acpi_ac_state_fops = { .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; + 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 + 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->alive = 1; - + 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); 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); - } + if (sbs->battery[id].bat.dev) + device_remove_file(sbs->battery[id].bat.dev, &alarm_attr); + power_supply_unregister(&sbs->battery[id].bat); +#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 + 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); 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); - } + if (sbs->charger.dev) + power_supply_unregister(&sbs->charger); +#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) { +#ifdef CONFIG_ACPI_PROC_EVENT + acpi_bus_generate_proc_event4(ACPI_AC_CLASS, ACPI_AC_DIR_NAME, + ACPI_SBS_NOTIFY_STATUS, + sbs->charger_present); +#endif + kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); + } + 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; +#ifdef CONFIG_ACPI_PROC_EVENT + acpi_bus_generate_proc_event4(ACPI_BATTERY_CLASS, + bat->name, + ACPI_SBS_NOTIFY_STATUS, + bat->present); +#endif + kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); } } - - 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 +947,25 @@ static int acpi_sbs_remove(struct acpi_device *device, int type) 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 +974,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 <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include <acpi/actypes.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#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/sleep/main.c b/drivers/acpi/sleep/main.c index 048295e..f3d3867 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -44,7 +44,6 @@ int acpi_sleep_prepare(u32 acpi_state) ACPI_FLUSH_CPU_CACHE(); acpi_enable_wakeup_device_prep(acpi_state); #endif - acpi_gpe_sleep_prepare(acpi_state); acpi_enter_sleep_state_prep(acpi_state); return 0; } @@ -268,6 +267,11 @@ static void acpi_hibernation_leave(void) static void acpi_hibernation_finish(void) { + /* + * If ACPI is not enabled by the BIOS and the boot kernel, we need to + * enable it here. + */ + acpi_enable(); acpi_leave_sleep_state(ACPI_STATE_S4); acpi_disable_wakeup_device(ACPI_STATE_S4); diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h index ff1f850..a2ea125 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep/sleep.h @@ -5,6 +5,5 @@ extern int acpi_suspend (u32 state); extern void acpi_enable_wakeup_device_prep(u8 sleep_state); extern void acpi_enable_wakeup_device(u8 sleep_state); extern void acpi_disable_wakeup_device(u8 sleep_state); -extern void acpi_gpe_sleep_prepare(u32 sleep_state); extern int acpi_sleep_prepare(u32 acpi_state); diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index 97c27dd..ed8e41b 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -64,36 +64,29 @@ void acpi_enable_wakeup_device(u8 sleep_state) ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device"); spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { - struct acpi_device *dev = container_of(node, - struct acpi_device, - wakeup_list); - + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) + continue; /* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ - if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { - spin_unlock(&acpi_device_lock); - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_RUNTIME); - /* Re-enable it, since set_gpe_type will disable it */ - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_ISR); - spin_lock(&acpi_device_lock); + if (!dev->wakeup.state.enabled || + sleep_state > (u32) dev->wakeup.sleep_state) { + if (dev->wakeup.flags.run_wake) { + spin_unlock(&acpi_device_lock); + /* set_gpe_type will disable GPE, leave it like that */ + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, + ACPI_GPE_TYPE_RUNTIME); + spin_lock(&acpi_device_lock); + } continue; } - - if (!dev->wakeup.flags.valid || - !dev->wakeup.state.enabled || - (sleep_state > (u32) dev->wakeup.sleep_state)) - continue; - spin_unlock(&acpi_device_lock); - /* run-wake GPE has been enabled */ if (!dev->wakeup.flags.run_wake) acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_ISR); - dev->wakeup.state.active = 1; spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); @@ -112,26 +105,25 @@ void acpi_disable_wakeup_device(u8 sleep_state) spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { - struct acpi_device *dev = container_of(node, - struct acpi_device, - wakeup_list); + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); - if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { - spin_unlock(&acpi_device_lock); - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_WAKE_RUN); - /* Re-enable it, since set_gpe_type will disable it */ - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); - spin_lock(&acpi_device_lock); + if (!dev->wakeup.flags.valid) continue; - } - - if (!dev->wakeup.flags.valid || - !dev->wakeup.state.active || - (sleep_state > (u32) dev->wakeup.sleep_state)) + if (!dev->wakeup.state.enabled || + sleep_state > (u32) dev->wakeup.sleep_state) { + if (dev->wakeup.flags.run_wake) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE_RUN); + /* Re-enable it, since set_gpe_type will disable it */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + spin_lock(&acpi_device_lock); + } continue; + } spin_unlock(&acpi_device_lock); acpi_disable_wakeup_device_power(dev); @@ -142,7 +134,6 @@ void acpi_disable_wakeup_device(u8 sleep_state) acpi_clear_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_NOT_ISR); } - dev->wakeup.state.active = 0; spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); @@ -160,48 +151,20 @@ static int __init acpi_wakeup_device_init(void) struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); - /* In case user doesn't load button driver */ - if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { - spin_unlock(&acpi_device_lock); - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_WAKE_RUN); - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); - dev->wakeup.state.enabled = 1; - spin_lock(&acpi_device_lock); - } + if (!dev->wakeup.flags.run_wake || dev->wakeup.state.enabled) + continue; + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE_RUN); + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.state.enabled = 1; + spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); - return 0; } late_initcall(acpi_wakeup_device_init); - -/* - * Disable all wakeup GPEs before entering requested sleep state. - * @sleep_state: ACPI state - * Since acpi_enter_sleep_state() will disable all - * RUNTIME GPEs, we simply mark all GPES that - * are not enabled for wakeup from requested state as RUNTIME. - */ -void acpi_gpe_sleep_prepare(u32 sleep_state) -{ - struct list_head *node, *next; - - list_for_each_safe(node, next, &acpi_wakeup_device_list) { - struct acpi_device *dev = container_of(node, - struct acpi_device, - wakeup_list); - - /* The GPE can wakeup system from this state, don't touch it */ - if ((u32) dev->wakeup.sleep_state >= sleep_state) - continue; - /* acpi_set_gpe_type will automatically disable GPE */ - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_RUNTIME); - } -} diff --git a/drivers/acpi/tables/tbutils.c b/drivers/acpi/tables/tbutils.c index 8cc9492..5f1d85f 100644 --- a/drivers/acpi/tables/tbutils.c +++ b/drivers/acpi/tables/tbutils.c @@ -400,7 +400,7 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags) u32 table_count; struct acpi_table_header *table; acpi_physical_address address; - acpi_physical_address rsdt_address; + acpi_physical_address uninitialized_var(rsdt_address); u32 length; u8 *table_entry; acpi_status status; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index ad898e1..5f79b44 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 *data) 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 *data) 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 *data) * 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 *data) 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_device *device) 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 acpi_device *device, int type) } 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 b8a2095..bac956b 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -409,14 +409,17 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device, 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 +427,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 +1636,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) |