OSDN Git Service

platform/x86: panasonic-laptop: Add support for battery charging threshold (eco mode)
authorKenneth Chan <kenneth.t.chan@gmail.com>
Fri, 21 Aug 2020 18:14:32 +0000 (02:14 +0800)
committerHans de Goede <hdegoede@redhat.com>
Tue, 10 Nov 2020 13:49:50 +0000 (14:49 +0100)
Add battery charging threshold (aka ECO mode) support.

NOTE: The state of ECO mode is persistent until the next POST cycle which
reset it to previous state.

Signed-off-by: Kenneth Chan <kenneth.t.chan@gmail.com>
Link: https://lore.kernel.org/r/20200821181433.17653-9-kenneth.t.chan@gmail.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
drivers/platform/x86/panasonic-laptop.c

index 6779099..6355d60 100644 (file)
@@ -13,6 +13,7 @@
  *
  * ChangeLog:
  *     Aug.18, 2020    Kenneth Chan <kenneth.t.chan@gmail.com>
+ *                     add support for battery charging threshold (eco mode)
  *                     resolve hotkey double trigger
  *                     add write support to mute
  *                     fix sticky_key init bug
@@ -147,7 +148,10 @@ MODULE_LICENSE("GPL");
 #define METHOD_HKEY_SQTY       "SQTY"
 #define METHOD_HKEY_SINF       "SINF"
 #define METHOD_HKEY_SSET       "SSET"
-#define HKEY_NOTIFY             0x80
+#define METHOD_ECWR            "\\_SB.ECWR"
+#define HKEY_NOTIFY            0x80
+#define ECO_MODE_OFF           0x00
+#define ECO_MODE_ON            0x80
 
 #define ACPI_PCC_DRIVER_NAME   "Panasonic Laptop Support"
 #define ACPI_PCC_DEVICE_NAME   "Hotkey"
@@ -156,7 +160,7 @@ MODULE_LICENSE("GPL");
 #define ACPI_PCC_INPUT_PHYS    "panasonic/hkey0"
 
 /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
-   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+   ECO_MODEs: 0x03 = off, 0x83 = on
 */
 enum SINF_BITS { SINF_NUM_BATTERIES = 0,
                 SINF_LCD_TYPE,
@@ -168,7 +172,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0,
                 SINF_DC_CUR_BRIGHT,
                 SINF_MUTE,
                 SINF_RESERVED,
-                SINF_ENV_STATE,
+                SINF_ECO_MODE = 0x0A,
                 SINF_STICKY_KEY = 0x80,
        };
 /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
@@ -222,6 +226,7 @@ struct pcc_acpi {
        acpi_handle             handle;
        unsigned long           num_sifr;
        int                     sticky_key;
+       int                     eco_mode;
        int                     mute;
        u32                     *sinf;
        struct acpi_device      *device;
@@ -534,6 +539,77 @@ static ssize_t sticky_key_store(struct device *dev, struct device_attribute *att
        return count;
 }
 
+static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr,
+                               char *buf)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+       int result;
+
+       if (!acpi_pcc_retrieve_biosdata(pcc))
+               return -EIO;
+
+       switch (pcc->sinf[SINF_ECO_MODE]) {
+       case (ECO_MODE_OFF + 3):
+               result = 0;
+               break;
+       case (ECO_MODE_ON + 3):
+               result = 1;
+               break;
+       default:
+               result = -EIO;
+               break;
+       }
+       return snprintf(buf, PAGE_SIZE, "%u\n", result);
+}
+
+static ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+       int err, state;
+
+       union acpi_object param[2];
+       struct acpi_object_list input;
+       acpi_status status;
+
+       param[0].type = ACPI_TYPE_INTEGER;
+       param[0].integer.value = 0x15;
+       param[1].type = ACPI_TYPE_INTEGER;
+       input.count = 2;
+       input.pointer = param;
+
+       err = kstrtoint(buf, 0, &state);
+       if (err)
+               return err;
+
+       switch (state) {
+       case 0:
+               param[1].integer.value = ECO_MODE_OFF;
+               pcc->sinf[SINF_ECO_MODE] = 0;
+               pcc->eco_mode = 0;
+               break;
+       case 1:
+               param[1].integer.value = ECO_MODE_ON;
+               pcc->sinf[SINF_ECO_MODE] = 1;
+               pcc->eco_mode = 1;
+               break;
+       default:
+               /* nothing to do */
+               return count;
+       }
+
+       status = acpi_evaluate_object(NULL, METHOD_ECWR,
+                                      &input, NULL);
+       if (ACPI_FAILURE(status)) {
+               pr_err("%s evaluation failed\n", METHOD_ECWR);
+               return -EINVAL;
+       }
+
+       return count;
+}
+
 static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
@@ -556,6 +632,7 @@ static DEVICE_ATTR_RO(numbatt);
 static DEVICE_ATTR_RO(lcdtype);
 static DEVICE_ATTR_RW(mute);
 static DEVICE_ATTR_RW(sticky_key);
+static DEVICE_ATTR_RW(eco_mode);
 static DEVICE_ATTR_RW(cdpower);
 
 static struct attribute *pcc_sysfs_entries[] = {
@@ -563,6 +640,7 @@ static struct attribute *pcc_sysfs_entries[] = {
        &dev_attr_lcdtype.attr,
        &dev_attr_mute.attr,
        &dev_attr_sticky_key.attr,
+       &dev_attr_eco_mode.attr,
        &dev_attr_cdpower.attr,
        NULL,
 };
@@ -714,6 +792,7 @@ static int acpi_pcc_hotkey_resume(struct device *dev)
                return -EINVAL;
 
        acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute);
+       acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode);
        acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key);
 
        return 0;
@@ -784,6 +863,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
        acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0);
        pcc->sticky_key = 0;
 
+       pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];
        pcc->mute = pcc->sinf[SINF_MUTE];
 
        /* add sysfs attributes */