ACPI / Battery: Add a _BIX quirk for NEC LZ750/LS
[linux-3.10.git] / drivers / acpi / battery.c
index 0b2707e..99427d7 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/async.h>
 #include <linux/dmi.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
+#include <asm/unaligned.h>
 
 #ifdef CONFIG_ACPI_PROCFS_POWER
 #include <linux/proc_fs.h>
@@ -54,6 +56,9 @@
 #define ACPI_BATTERY_NOTIFY_INFO       0x81
 #define ACPI_BATTERY_NOTIFY_THRESHOLD   0x82
 
+/* Battery power unit: 0 means mW, 1 means mA */
+#define ACPI_BATTERY_POWER_UNIT_MA     1
+
 #define _COMPONENT             ACPI_BATTERY_COMPONENT
 
 ACPI_MODULE_NAME("battery");
@@ -63,6 +68,7 @@ MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
 MODULE_DESCRIPTION("ACPI Battery Driver");
 MODULE_LICENSE("GPL");
 
+static int battery_bix_broken_package;
 static unsigned int cache_time = 1000;
 module_param(cache_time, uint, 0644);
 MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
@@ -90,19 +96,29 @@ MODULE_DEVICE_TABLE(acpi, battery_device_ids);
 enum {
        ACPI_BATTERY_ALARM_PRESENT,
        ACPI_BATTERY_XINFO_PRESENT,
-       /* For buggy DSDTs that report negative 16-bit values for either
-        * charging or discharging current and/or report 0 as 65536
-        * due to bad math.
-        */
-       ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
        ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
+       /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
+          switches between mWh and mAh depending on whether the system
+          is running on battery or not.  When mAh is the unit, most
+          reported values are incorrect and need to be adjusted by
+          10000/design_voltage.  Verified on x201, t410, t410s, and x220.
+          Pre-2010 and 2012 models appear to always report in mWh and
+          are thus unaffected (tested with t42, t61, t500, x200, x300,
+          and x230).  Also, in mid-2012 Lenovo issued a BIOS update for
+          the 2011 models that fixes the issue (tested on x220 with a
+          post-1.29 BIOS), but as of Nov. 2012, no such update is
+          available for the 2010 models.  */
+       ACPI_BATTERY_QUIRK_THINKPAD_MAH,
 };
 
 struct acpi_battery {
        struct mutex lock;
+       struct mutex sysfs_lock;
        struct power_supply bat;
        struct acpi_device *device;
+       struct notifier_block pm_nb;
        unsigned long update_time;
+       int revision;
        int rate_now;
        int capacity_now;
        int voltage_now;
@@ -130,9 +146,9 @@ struct acpi_battery {
        unsigned long flags;
 };
 
-#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat);
+#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
 
-inline int acpi_battery_present(struct acpi_battery *battery)
+static inline int acpi_battery_present(struct acpi_battery *battery)
 {
        return battery->device->status.battery_present;
 }
@@ -181,6 +197,7 @@ static int acpi_battery_get_property(struct power_supply *psy,
                                     enum power_supply_property psp,
                                     union power_supply_propval *val)
 {
+       int ret = 0;
        struct acpi_battery *battery = to_acpi_battery(psy);
 
        if (acpi_battery_present(battery)) {
@@ -209,26 +226,51 @@ static int acpi_battery_get_property(struct power_supply *psy,
                val->intval = battery->cycle_count;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = battery->design_voltage * 1000;
+               if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       val->intval = battery->design_voltage * 1000;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = battery->voltage_now * 1000;
+               if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       val->intval = battery->voltage_now * 1000;
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
        case POWER_SUPPLY_PROP_POWER_NOW:
-               val->intval = battery->rate_now * 1000;
+               if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       val->intval = battery->rate_now * 1000;
                break;
        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
        case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
-               val->intval = battery->design_capacity * 1000;
+               if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       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;
+               if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       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;
+               if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
+                       ret = -ENODEV;
+               else
+                       val->intval = battery->capacity_now * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (battery->capacity_now && battery->full_charge_capacity)
+                       val->intval = battery->capacity_now * 100/
+                                       battery->full_charge_capacity;
+               else
+                       val->intval = 0;
                break;
        case POWER_SUPPLY_PROP_MODEL_NAME:
                val->strval = battery->model_number;
@@ -240,9 +282,9 @@ static int acpi_battery_get_property(struct power_supply *psy,
                val->strval = battery->serial_number;
                break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
        }
-       return 0;
+       return ret;
 }
 
 static enum power_supply_property charge_battery_props[] = {
@@ -256,6 +298,7 @@ static enum power_supply_property charge_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -272,6 +315,7 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
        POWER_SUPPLY_PROP_ENERGY_FULL,
        POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -280,7 +324,8 @@ static enum power_supply_property energy_battery_props[] = {
 #ifdef CONFIG_ACPI_PROCFS_POWER
 inline char *acpi_battery_units(struct acpi_battery *battery)
 {
-       return (battery->power_unit)?"mA":"mW";
+       return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+               "mA" : "mW";
 }
 #endif
 
@@ -316,6 +361,7 @@ static struct acpi_offsets info_offsets[] = {
 };
 
 static struct acpi_offsets extended_info_offsets[] = {
+       {offsetof(struct acpi_battery, revision), 0},
        {offsetof(struct acpi_battery, power_unit), 0},
        {offsetof(struct acpi_battery, design_capacity), 0},
        {offsetof(struct acpi_battery, full_charge_capacity), 0},
@@ -398,7 +444,12 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
                ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
                return -ENODEV;
        }
-       if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
+
+       if (battery_bix_broken_package)
+               result = extract_package(battery, buffer.pointer,
+                               extended_info_offsets + 1,
+                               ARRAY_SIZE(extended_info_offsets) - 1);
+       else if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
                result = extract_package(battery, buffer.pointer,
                                extended_info_offsets,
                                ARRAY_SIZE(extended_info_offsets));
@@ -408,6 +459,21 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
        kfree(buffer.pointer);
        if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
                battery->full_charge_capacity = battery->design_capacity;
+       if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
+           battery->power_unit && battery->design_voltage) {
+               battery->design_capacity = battery->design_capacity *
+                   10000 / battery->design_voltage;
+               battery->full_charge_capacity = battery->full_charge_capacity *
+                   10000 / battery->design_voltage;
+               battery->design_capacity_warning =
+                   battery->design_capacity_warning *
+                   10000 / battery->design_voltage;
+               /* Curiously, design_capacity_low, unlike the rest of them,
+                  is correct.  */
+               /* capacity_granularity_* equal 1 on the systems tested, so
+                  it's impossible to tell if they would need an adjustment
+                  or not if their values were higher.  */
+       }
        return result;
 }
 
@@ -440,14 +506,27 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
        battery->update_time = jiffies;
        kfree(buffer.pointer);
 
-       if (test_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags) &&
-           battery->rate_now != -1)
+       /* For buggy DSDTs that report negative 16-bit values for either
+        * charging or discharging current and/or report 0 as 65536
+        * due to bad math.
+        */
+       if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
+               battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+               (s16)(battery->rate_now) < 0) {
                battery->rate_now = abs((s16)battery->rate_now);
+               printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
+                       " invalid.\n");
+       }
 
        if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
            && battery->capacity_now >= 0 && battery->capacity_now <= 100)
                battery->capacity_now = (battery->capacity_now *
                                battery->full_charge_capacity) / 100;
+       if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
+           battery->power_unit && battery->design_voltage) {
+               battery->capacity_now = battery->capacity_now *
+                   10000 / battery->design_voltage;
+       }
        return result;
 }
 
@@ -523,7 +602,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
 {
        int result;
 
-       if (battery->power_unit) {
+       if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
                battery->bat.properties = charge_battery_props;
                battery->bat.num_properties =
                        ARRAY_SIZE(charge_battery_props);
@@ -545,17 +624,33 @@ static int sysfs_add_battery(struct acpi_battery *battery)
 
 static void sysfs_remove_battery(struct acpi_battery *battery)
 {
-       if (!battery->bat.dev)
+       mutex_lock(&battery->sysfs_lock);
+       if (!battery->bat.dev) {
+               mutex_unlock(&battery->sysfs_lock);
                return;
+       }
+
        device_remove_file(battery->bat.dev, &alarm_attr);
        power_supply_unregister(&battery->bat);
        battery->bat.dev = NULL;
+       mutex_unlock(&battery->sysfs_lock);
 }
 
-static void acpi_battery_quirks(struct acpi_battery *battery)
+static void find_battery(const struct dmi_header *dm, void *private)
 {
-       if (dmi_name_in_vendors("Acer") && battery->power_unit) {
-               set_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags);
+       struct acpi_battery *battery = (struct acpi_battery *)private;
+       /* Note: the hardcoded offsets below have been extracted from
+          the source code of dmidecode.  */
+       if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
+               const u8 *dmi_data = (const u8 *)(dm + 1);
+               int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
+               if (dm->length >= 18)
+                       dmi_capacity *= dmi_data[17];
+               if (battery->design_capacity * battery->design_voltage / 1000
+                   != dmi_capacity &&
+                   battery->design_capacity * 10 == dmi_capacity)
+                       set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+                               &battery->flags);
        }
 }
 
@@ -571,7 +666,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
  *
  * Handle this correctly so that they won't break userspace.
  */
-static void acpi_battery_quirks2(struct acpi_battery *battery)
+static void acpi_battery_quirks(struct acpi_battery *battery)
 {
        if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
                return ;
@@ -584,6 +679,32 @@ static void acpi_battery_quirks2(struct acpi_battery *battery)
                battery->capacity_now = (battery->capacity_now *
                                battery->full_charge_capacity) / 100;
        }
+
+       if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
+               return ;
+
+       if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
+               const char *s;
+               s = dmi_get_system_info(DMI_PRODUCT_VERSION);
+               if (s && !strnicmp(s, "ThinkPad", 8)) {
+                       dmi_walk(find_battery, battery);
+                       if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
+                                    &battery->flags) &&
+                           battery->design_voltage) {
+                               battery->design_capacity =
+                                   battery->design_capacity *
+                                   10000 / battery->design_voltage;
+                               battery->full_charge_capacity =
+                                   battery->full_charge_capacity *
+                                   10000 / battery->design_voltage;
+                               battery->design_capacity_warning =
+                                   battery->design_capacity_warning *
+                                   10000 / battery->design_voltage;
+                               battery->capacity_now = battery->capacity_now *
+                                   10000 / battery->design_voltage;
+                       }
+               }
+       }
 }
 
 static int acpi_battery_update(struct acpi_battery *battery)
@@ -602,16 +723,37 @@ static int acpi_battery_update(struct acpi_battery *battery)
                result = acpi_battery_get_info(battery);
                if (result)
                        return result;
-               acpi_battery_quirks(battery);
                acpi_battery_init_alarm(battery);
        }
-       if (!battery->bat.dev)
-               sysfs_add_battery(battery);
+       if (!battery->bat.dev) {
+               result = sysfs_add_battery(battery);
+               if (result)
+                       return result;
+       }
        result = acpi_battery_get_state(battery);
-       acpi_battery_quirks2(battery);
+       acpi_battery_quirks(battery);
        return result;
 }
 
+static void acpi_battery_refresh(struct acpi_battery *battery)
+{
+       int power_unit;
+
+       if (!battery->bat.dev)
+               return;
+
+       power_unit = battery->power_unit;
+
+       acpi_battery_get_info(battery);
+
+       if (power_unit == battery->power_unit)
+               return;
+
+       /* The battery has changed its reporting units. */
+       sysfs_remove_battery(battery);
+       sysfs_add_battery(battery);
+}
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
@@ -795,7 +937,7 @@ static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \
 } \
 static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \
 { \
-       return single_open(file, acpi_battery_read_##_name, PDE(inode)->data); \
+       return single_open(file, acpi_battery_read_##_name, PDE_DATA(inode)); \
 }
 
 DECLARE_FILE_FUNCTIONS(info);
@@ -831,9 +973,9 @@ DECLARE_FILE_FUNCTIONS(alarm);
                }, \
        }
 
-static struct battery_file {
+static const struct battery_file {
        struct file_operations ops;
-       mode_t mode;
+       umode_t mode;
        const char *name;
 } acpi_battery_file[] = {
        FILE_DESCRIPTION_RO(info),
@@ -849,6 +991,8 @@ static int acpi_battery_add_fs(struct acpi_device *device)
        struct proc_dir_entry *entry = NULL;
        int i;
 
+       printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded,"
+                       " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
        if (!acpi_device_dir(device)) {
                acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
                                                     acpi_battery_dir);
@@ -895,6 +1039,8 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
        if (!battery)
                return;
        old = battery->bat.dev;
+       if (event == ACPI_BATTERY_NOTIFY_INFO)
+               acpi_battery_refresh(battery);
        acpi_battery_update(battery);
        acpi_bus_generate_proc_event(device, event,
                                     acpi_battery_present(battery));
@@ -906,6 +1052,35 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
                power_supply_changed(&battery->bat);
 }
 
+static int battery_notify(struct notifier_block *nb,
+                              unsigned long mode, void *_unused)
+{
+       struct acpi_battery *battery = container_of(nb, struct acpi_battery,
+                                                   pm_nb);
+       switch (mode) {
+       case PM_POST_HIBERNATION:
+       case PM_POST_SUSPEND:
+               if (battery->bat.dev) {
+                       sysfs_remove_battery(battery);
+                       sysfs_add_battery(battery);
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static struct dmi_system_id bat_dmi_table[] = {
+       {
+               .ident = "NEC LZ750/LS",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"),
+               },
+       },
+       {},
+};
+
 static int acpi_battery_add(struct acpi_device *device)
 {
        int result = 0;
@@ -921,53 +1096,78 @@ static int acpi_battery_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
        device->driver_data = battery;
        mutex_init(&battery->lock);
+       mutex_init(&battery->sysfs_lock);
        if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
                        "_BIX", &handle)))
                set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
-       acpi_battery_update(battery);
+       result = acpi_battery_update(battery);
+       if (result)
+               goto fail;
 #ifdef CONFIG_ACPI_PROCFS_POWER
        result = acpi_battery_add_fs(device);
 #endif
-       if (!result) {
-               printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
-                       ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
-                       device->status.battery_present ? "present" : "absent");
-       } else {
+       if (result) {
 #ifdef CONFIG_ACPI_PROCFS_POWER
                acpi_battery_remove_fs(device);
 #endif
-               kfree(battery);
+               goto fail;
        }
+
+       printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+               ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+               device->status.battery_present ? "present" : "absent");
+
+       battery->pm_nb.notifier_call = battery_notify;
+       register_pm_notifier(&battery->pm_nb);
+
+       return result;
+
+fail:
+       sysfs_remove_battery(battery);
+       mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
+       kfree(battery);
        return result;
 }
 
-static int acpi_battery_remove(struct acpi_device *device, int type)
+static int acpi_battery_remove(struct acpi_device *device)
 {
        struct acpi_battery *battery = NULL;
 
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
        battery = acpi_driver_data(device);
+       unregister_pm_notifier(&battery->pm_nb);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_remove_fs(device);
 #endif
        sysfs_remove_battery(battery);
        mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
        kfree(battery);
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
 /* this is needed to learn about changes made in suspended state */
-static int acpi_battery_resume(struct acpi_device *device)
+static int acpi_battery_resume(struct device *dev)
 {
        struct acpi_battery *battery;
-       if (!device)
+
+       if (!dev)
                return -EINVAL;
-       battery = acpi_driver_data(device);
+
+       battery = acpi_driver_data(to_acpi_device(dev));
+       if (!battery)
+               return -EINVAL;
+
        battery->update_time = 0;
        acpi_battery_update(battery);
        return 0;
 }
+#endif
+
+static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
 
 static struct acpi_driver acpi_battery_driver = {
        .name = "battery",
@@ -976,10 +1176,10 @@ static struct acpi_driver acpi_battery_driver = {
        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
                .add = acpi_battery_add,
-               .resume = acpi_battery_resume,
                .remove = acpi_battery_remove,
                .notify = acpi_battery_notify,
                },
+       .drv.pm = &acpi_battery_pm,
 };
 
 static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
@@ -991,6 +1191,8 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
        if (!acpi_battery_dir)
                return;
 #endif
+       if (dmi_check_system(bat_dmi_table))
+               battery_bix_broken_package = 1;
        if (acpi_bus_register_driver(&acpi_battery_driver) < 0) {
 #ifdef CONFIG_ACPI_PROCFS_POWER
                acpi_unlock_battery_dir(acpi_battery_dir);