ACPI / battery: use callback for setting up quirks
[firefly-linux-kernel-4.4.55.git] / drivers / acpi / battery.c
index 6e7b2a12860d31ac533c730e97d080daaa6f1385..9530f4478ae2407386ea80470da03a22b18ff908 100644 (file)
 /* Battery power unit: 0 means mW, 1 means mA */
 #define ACPI_BATTERY_POWER_UNIT_MA     1
 
+#define ACPI_BATTERY_STATE_DISCHARGING 0x1
+#define ACPI_BATTERY_STATE_CHARGING    0x2
+#define ACPI_BATTERY_STATE_CRITICAL    0x4
+
 #define _COMPONENT             ACPI_BATTERY_COMPONENT
 
 ACPI_MODULE_NAME("battery");
@@ -169,7 +173,7 @@ static int acpi_battery_get_state(struct acpi_battery *battery);
 
 static int acpi_battery_is_charged(struct acpi_battery *battery)
 {
-       /* either charging or discharging */
+       /* charging, discharging or critical low */
        if (battery->state != 0)
                return 0;
 
@@ -204,9 +208,9 @@ static int acpi_battery_get_property(struct power_supply *psy,
                return -ENODEV;
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (battery->state & 0x01)
+               if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (battery->state & 0x02)
+               else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
                else if (acpi_battery_is_charged(battery))
                        val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -269,6 +273,17 @@ static int acpi_battery_get_property(struct power_supply *psy,
                else
                        val->intval = 0;
                break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               if (battery->state & ACPI_BATTERY_STATE_CRITICAL)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else if (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+                       (battery->capacity_now <= battery->alarm))
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (acpi_battery_is_charged(battery))
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+               break;
        case POWER_SUPPLY_PROP_MODEL_NAME:
                val->strval = battery->model_number;
                break;
@@ -296,6 +311,7 @@ static enum power_supply_property charge_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -313,6 +329,7 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_ENERGY_FULL,
        POWER_SUPPLY_PROP_ENERGY_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -605,7 +622,8 @@ static int sysfs_add_battery(struct acpi_battery *battery)
        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 = power_supply_register_no_ws(&battery->device->dev, &battery->bat);
+
        if (result)
                return result;
        return device_create_file(battery->bat.dev, &alarm_attr);
@@ -696,7 +714,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
        }
 }
 
-static int acpi_battery_update(struct acpi_battery *battery)
+static int acpi_battery_update(struct acpi_battery *battery, bool resume)
 {
        int result, old_present = acpi_battery_present(battery);
        result = acpi_battery_get_status(battery);
@@ -707,6 +725,10 @@ static int acpi_battery_update(struct acpi_battery *battery)
                battery->update_time = 0;
                return 0;
        }
+
+       if (resume)
+               return 0;
+
        if (!battery->update_time ||
            old_present != acpi_battery_present(battery)) {
                result = acpi_battery_get_info(battery);
@@ -720,7 +742,19 @@ static int acpi_battery_update(struct acpi_battery *battery)
                        return result;
        }
        result = acpi_battery_get_state(battery);
+       if (result)
+               return result;
        acpi_battery_quirks(battery);
+
+       /*
+        * Wakeup the system if battery is critical low
+        * or lower than the alarm level
+        */
+       if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
+           (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+            (battery->capacity_now <= battery->alarm)))
+               pm_wakeup_event(&battery->device->dev, 0);
+
        return result;
 }
 
@@ -915,7 +949,7 @@ static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = {
 static int acpi_battery_read(int fid, struct seq_file *seq)
 {
        struct acpi_battery *battery = seq->private;
-       int result = acpi_battery_update(battery);
+       int result = acpi_battery_update(battery, false);
        return acpi_print_funcs[fid](seq, result);
 }
 
@@ -1030,7 +1064,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
        old = battery->bat.dev;
        if (event == ACPI_BATTERY_NOTIFY_INFO)
                acpi_battery_refresh(battery);
-       acpi_battery_update(battery);
+       acpi_battery_update(battery, false);
        acpi_bus_generate_netlink_event(device->pnp.device_class,
                                        dev_name(&device->dev), event,
                                        acpi_battery_present(battery));
@@ -1045,21 +1079,42 @@ static int battery_notify(struct notifier_block *nb,
 {
        struct acpi_battery *battery = container_of(nb, struct acpi_battery,
                                                    pm_nb);
+       int result;
+
        switch (mode) {
        case PM_POST_HIBERNATION:
        case PM_POST_SUSPEND:
-               if (battery->bat.dev) {
-                       sysfs_remove_battery(battery);
-                       sysfs_add_battery(battery);
-               }
+               if (!acpi_battery_present(battery))
+                       return 0;
+
+               if (!battery->bat.dev) {
+                       result = acpi_battery_get_info(battery);
+                       if (result)
+                               return result;
+
+                       result = sysfs_add_battery(battery);
+                       if (result)
+                               return result;
+               } else
+                       acpi_battery_refresh(battery);
+
+               acpi_battery_init_alarm(battery);
+               acpi_battery_get_state(battery);
                break;
        }
 
        return 0;
 }
 
+static int battery_bix_broken_package_quirk(const struct dmi_system_id *d)
+{
+       battery_bix_broken_package = 1;
+       return 0;
+}
+
 static struct dmi_system_id bat_dmi_table[] = {
        {
+               .callback = battery_bix_broken_package_quirk,
                .ident = "NEC LZ750/LS",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
@@ -1087,7 +1142,7 @@ static int acpi_battery_add(struct acpi_device *device)
        mutex_init(&battery->sysfs_lock);
        if (acpi_has_method(battery->device->handle, "_BIX"))
                set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
-       result = acpi_battery_update(battery);
+       result = acpi_battery_update(battery, false);
        if (result)
                goto fail;
 #ifdef CONFIG_ACPI_PROCFS_POWER
@@ -1107,6 +1162,8 @@ static int acpi_battery_add(struct acpi_device *device)
        battery->pm_nb.notifier_call = battery_notify;
        register_pm_notifier(&battery->pm_nb);
 
+       device_init_wakeup(&device->dev, 1);
+
        return result;
 
 fail:
@@ -1123,6 +1180,7 @@ static int acpi_battery_remove(struct acpi_device *device)
 
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
+       device_init_wakeup(&device->dev, 0);
        battery = acpi_driver_data(device);
        unregister_pm_notifier(&battery->pm_nb);
 #ifdef CONFIG_ACPI_PROCFS_POWER
@@ -1149,7 +1207,7 @@ static int acpi_battery_resume(struct device *dev)
                return -EINVAL;
 
        battery->update_time = 0;
-       acpi_battery_update(battery);
+       acpi_battery_update(battery, true);
        return 0;
 }
 #else
@@ -1176,8 +1234,7 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
        if (acpi_disabled)
                return;
 
-       if (dmi_check_system(bat_dmi_table))
-               battery_bix_broken_package = 1;
+       dmi_check_system(bat_dmi_table);
        
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_dir = acpi_lock_battery_dir();