Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / drivers / acpi / battery.c
index ac74a7d..95649d3 100644 (file)
 
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
-
-#ifdef CONFIG_ACPI_SYSFS_POWER
 #include <linux/power_supply.h>
-#endif
 
 #define PREFIX "ACPI: "
 
@@ -98,13 +95,12 @@ enum {
         * due to bad math.
         */
        ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
+       ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
 };
 
 struct acpi_battery {
        struct mutex lock;
-#ifdef CONFIG_ACPI_SYSFS_POWER
        struct power_supply bat;
-#endif
        struct acpi_device *device;
        unsigned long update_time;
        int rate_now;
@@ -141,7 +137,6 @@ inline int acpi_battery_present(struct acpi_battery *battery)
        return battery->device->status.battery_present;
 }
 
-#ifdef CONFIG_ACPI_SYSFS_POWER
 static int acpi_battery_technology(struct acpi_battery *battery)
 {
        if (!strcasecmp("NiCd", battery->type))
@@ -300,7 +295,6 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
 };
-#endif
 
 #ifdef CONFIG_ACPI_PROCFS_POWER
 inline char *acpi_battery_units(struct acpi_battery *battery)
@@ -431,6 +425,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
                result = extract_package(battery, buffer.pointer,
                                info_offsets, ARRAY_SIZE(info_offsets));
        kfree(buffer.pointer);
+       if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+               battery->full_charge_capacity = battery->design_capacity;
        return result;
 }
 
@@ -467,6 +463,10 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
            battery->rate_now != -1)
                battery->rate_now = abs((s16)battery->rate_now);
 
+       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;
        return result;
 }
 
@@ -511,7 +511,6 @@ static int acpi_battery_init_alarm(struct acpi_battery *battery)
        return acpi_battery_set_alarm(battery);
 }
 
-#ifdef CONFIG_ACPI_SYSFS_POWER
 static ssize_t acpi_battery_alarm_show(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -571,7 +570,6 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
        power_supply_unregister(&battery->bat);
        battery->bat.dev = NULL;
 }
-#endif
 
 static void acpi_battery_quirks(struct acpi_battery *battery)
 {
@@ -580,6 +578,33 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
        }
 }
 
+/*
+ * According to the ACPI spec, some kinds of primary batteries can
+ * report percentage battery remaining capacity directly to OS.
+ * In this case, it reports the Last Full Charged Capacity == 100
+ * and BatteryPresentRate == 0xFFFFFFFF.
+ *
+ * Now we found some battery reports percentage remaining capacity
+ * even if it's rechargeable.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=15979
+ *
+ * Handle this correctly so that they won't break userspace.
+ */
+static void acpi_battery_quirks2(struct acpi_battery *battery)
+{
+       if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
+               return ;
+
+        if (battery->full_charge_capacity == 100 &&
+            battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
+            battery->capacity_now >=0 && battery->capacity_now <= 100) {
+               set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
+               battery->full_charge_capacity = battery->design_capacity;
+               battery->capacity_now = (battery->capacity_now *
+                               battery->full_charge_capacity) / 100;
+       }
+}
+
 static int acpi_battery_update(struct acpi_battery *battery)
 {
        int result, old_present = acpi_battery_present(battery);
@@ -587,9 +612,7 @@ static int acpi_battery_update(struct acpi_battery *battery)
        if (result)
                return result;
        if (!acpi_battery_present(battery)) {
-#ifdef CONFIG_ACPI_SYSFS_POWER
                sysfs_remove_battery(battery);
-#endif
                battery->update_time = 0;
                return 0;
        }
@@ -601,11 +624,11 @@ static int acpi_battery_update(struct acpi_battery *battery)
                acpi_battery_quirks(battery);
                acpi_battery_init_alarm(battery);
        }
-#ifdef CONFIG_ACPI_SYSFS_POWER
        if (!battery->bat.dev)
                sysfs_add_battery(battery);
-#endif
-       return acpi_battery_get_state(battery);
+       result = acpi_battery_get_state(battery);
+       acpi_battery_quirks2(battery);
+       return result;
 }
 
 /* --------------------------------------------------------------------------
@@ -886,26 +909,20 @@ static void acpi_battery_remove_fs(struct acpi_device *device)
 static void acpi_battery_notify(struct acpi_device *device, u32 event)
 {
        struct acpi_battery *battery = acpi_driver_data(device);
-#ifdef CONFIG_ACPI_SYSFS_POWER
        struct device *old;
-#endif
 
        if (!battery)
                return;
-#ifdef CONFIG_ACPI_SYSFS_POWER
        old = battery->bat.dev;
-#endif
        acpi_battery_update(battery);
        acpi_bus_generate_proc_event(device, event,
                                     acpi_battery_present(battery));
        acpi_bus_generate_netlink_event(device->pnp.device_class,
                                        dev_name(&device->dev), event,
                                        acpi_battery_present(battery));
-#ifdef CONFIG_ACPI_SYSFS_POWER
        /* acpi_battery_update could remove power_supply object */
        if (old && battery->bat.dev)
                power_supply_changed(&battery->bat);
-#endif
 }
 
 static int acpi_battery_add(struct acpi_device *device)
@@ -953,9 +970,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_remove_fs(device);
 #endif
-#ifdef CONFIG_ACPI_SYSFS_POWER
        sysfs_remove_battery(battery);
-#endif
        mutex_destroy(&battery->lock);
        kfree(battery);
        return 0;