Add several new standard properties to the power supply subsystem
and sysfs support for them. These policies are supported on several
kinds of devices, including Lenovo and Dell. I am adding this in
particular for a new Chrome OS device. All dates and times are expected
to be in local time. Thus, the individual driver that implements these
features is responsible for maintaining the schedule. This might entail
scheduling events in the kernel that send commands to the power
controller in the device at the proper times, or, if the power
controller has its own RTC and keeps track of the time, then the user
is responsible for ensuring that the power controller's RTC stays in
sync with the local time, even through changes such as time zone shifts
or daylight savings beginning or ending.
Peak Shift is power saving policy that minimizes AC usage during the
peak usage times during the day. For each weekday a start and end time
to run in Peak Shift mode can be set. During these times the system will
run from the battery even if the AC is attached as long as the battery
stays above the threshold specified. After the end time specified the
system will run from AC if attached but will not charge the battery. The
system will again function normally using AC and recharging the battery
after the specified Charge Start time.
Advanced Charging Mode allows the user to maximize the battery health.
In Advanced Charging Mode the system will use standard charging
algorithm and other techniques during non-work hours to maximize battery
health. During work hours, an express charge is used. This express
charge allows the battery to be charged faster; therefore, the battery
is at full charge sooner. For each day the time in which the system will
be most heavily used is specified by the start time and the duration.
Signed-off-by: Nick Crews <[email protected]>
---
drivers/power/supply/power_supply_sysfs.c | 64 +++++++++++++++++++++++
include/linux/power_supply.h | 41 +++++++++++++++
2 files changed, 105 insertions(+)
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index dce24f596160..5b528526d8fd 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -172,6 +172,24 @@ static ssize_t power_supply_show_property(struct device *dev,
ret = sprintf(buf, "%s\n",
power_supply_scope_text[value.intval]);
break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
+ ret = sprintf(buf, "%02d %02d %02d %02d %02d %02d\n",
+ value.psval.start_hours,
+ value.psval.start_minutes,
+ value.psval.end_hours,
+ value.psval.end_minutes,
+ value.psval.charge_start_hours,
+ value.psval.charge_start_minutes);
+ break;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
+ ret = sprintf(buf, "%02d %02d %02d %02d\n",
+ value.abcval.start_hours,
+ value.abcval.start_minutes,
+ value.abcval.duration_hours,
+ value.abcval.duration_minutes);
+ break;
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = sprintf(buf, "%s\n", value.strval);
break;
@@ -209,6 +227,28 @@ static ssize_t power_supply_store_property(struct device *dev,
case POWER_SUPPLY_PROP_SCOPE:
ret = sysfs_match_string(power_supply_scope_text, buf);
break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
+ ret = sscanf(buf, "%d %d %d %d %d %d",
+ &value.psval.start_hours,
+ &value.psval.start_minutes,
+ &value.psval.end_hours,
+ &value.psval.end_minutes,
+ &value.psval.charge_start_hours,
+ &value.psval.charge_start_minutes);
+ if (ret != 6)
+ return -EINVAL;
+ goto store_property;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
+ ret = sscanf(buf, "%d %d %d %d",
+ &value.abcval.start_hours,
+ &value.abcval.start_minutes,
+ &value.abcval.duration_hours,
+ &value.abcval.duration_minutes);
+ if (ret != 4)
+ return -EINVAL;
+ goto store_property;
default:
ret = -EINVAL;
}
@@ -229,6 +269,7 @@ static ssize_t power_supply_store_property(struct device *dev,
value.intval = ret;
+store_property:
ret = power_supply_set_property(psy, psp, &value);
if (ret < 0)
return ret;
@@ -303,10 +344,33 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(precharge_current),
POWER_SUPPLY_ATTR(charge_term_current),
POWER_SUPPLY_ATTR(calibrate),
+ POWER_SUPPLY_ATTR(peak_shift_enable),
+ POWER_SUPPLY_ATTR(peak_shift_batt_threshold),
+ POWER_SUPPLY_ATTR(adv_batt_charging_enable),
+ /* Local extensions */
+ POWER_SUPPLY_ATTR(usb_hc),
+ POWER_SUPPLY_ATTR(usb_otg),
+ POWER_SUPPLY_ATTR(charge_enabled),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
POWER_SUPPLY_ATTR(serial_number),
+ /* Peak Shift Schedule properties */
+ POWER_SUPPLY_ATTR(peak_shift_sched_sunday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_monday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_tuesday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_wednesday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_thursday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_friday),
+ POWER_SUPPLY_ATTR(peak_shift_sched_saturday),
+ /* Advanced Battery Charging Schedule properties */
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_sunday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_monday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_tuesday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_wednesday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_thursday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_friday),
+ POWER_SUPPLY_ATTR(adv_batt_charging_sched_saturday),
};
static struct attribute *
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 57b2ab82b951..1d89590537bf 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -150,10 +150,33 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CALIBRATE,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE,
+ /* Local extensions */
+ POWER_SUPPLY_PROP_USB_HC,
+ POWER_SUPPLY_PROP_USB_OTG,
+ POWER_SUPPLY_PROP_CHARGE_ENABLED,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
+ /* Peak Shift Schedule properties */
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_MONDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_TUESDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_WEDNESDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_THURSDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_FRIDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY,
+ /* Advanced Battery Charging Schedule properties */
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_MONDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_TUESDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_WEDNESDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_THURSDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_FRIDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY,
};
enum power_supply_type {
@@ -188,9 +211,27 @@ enum power_supply_notifier_events {
PSY_EVENT_PROP_CHANGED,
};
+struct peak_shift_schedule {
+ int start_hours;
+ int start_minutes;
+ int end_hours;
+ int end_minutes;
+ int charge_start_hours;
+ int charge_start_minutes;
+};
+
+struct adv_batt_charging_schedule {
+ int start_hours;
+ int start_minutes;
+ int duration_hours;
+ int duration_minutes;
+};
+
union power_supply_propval {
int intval;
const char *strval;
+ struct peak_shift_schedule psval;
+ struct adv_batt_charging_schedule abcval;
};
struct device_node;
--
2.20.1
This is an example driver that uses the proposed Peak Shift and
Adv Batt Charging support in the main power supply subsystem.
At this point, there is no actual communication with the EC,
the point of this is to show the interface that usespace would see,
and to show the use case for the added properties to the power
supply susbsystem. To see the location of where the properties
would appear in userspace, see the Documentation file.
Signed-off-by: Nick Crews <[email protected]>
---
.../ABI/testing/sysfs-class-power-wilco | 81 +++++++
drivers/platform/chrome/wilco_ec/Kconfig | 14 ++
drivers/platform/chrome/wilco_ec/Makefile | 2 +
.../platform/chrome/wilco_ec/advanced_power.c | 199 ++++++++++++++++++
drivers/platform/chrome/wilco_ec/core.c | 14 ++
include/linux/platform_data/wilco-ec.h | 2 +
6 files changed, 312 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-power-wilco
create mode 100644 drivers/platform/chrome/wilco_ec/advanced_power.c
diff --git a/Documentation/ABI/testing/sysfs-class-power-wilco b/Documentation/ABI/testing/sysfs-class-power-wilco
new file mode 100644
index 000000000000..5c96b28aa597
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power-wilco
@@ -0,0 +1,81 @@
+What: /sys/class/power_supply/wilco_adv_power/peak_shift_sched_<day of week>
+Date: January 2019
+KernelVersion: 4.19
+Description:
+ For each weekday a start and end time to run in Peak Shift mode
+ can be set. During these times the system will run from the
+ battery even if the AC is attached as long as the battery stays
+ above the threshold specified. After the end time specified the
+ system will run from AC if attached but will not charge the
+ battery. The system will again function normally using AC and
+ recharging the battery after the specified Charge Start time.
+
+ The input buffer must have the format "start_hr start_min end_hr
+ end_min charge_start_hr charge_start_min" The hour fields must
+ be in the range [0-23], and the minutes must be one of (0, 15,
+ 30, 45). The string must be parseable by sscanf() using the
+ format string "%d %d %d %d %d %d". An example valid input is
+ "6 15 009 45 23 0", which corresponds to 6:15, 9:45, and 23:00
+
+ The output buffer will be filled with the format "start_hr
+ start_min end_hr end_min charge_start_hr charge_start_min" The
+ hour fields will be in the range [0-23], and the minutes will be
+ one of (0, 15, 30, 45). Each number will be zero padded to two
+ characters. An example output is "06 15 09 45 23 00", which
+ corresponds to 6:15, 9:45, and 23:00
+
+What: /sys/class/power_supply/wilco_adv_power/peak_shift_enable
+Date: January 2019
+KernelVersion: 4.19
+Description:
+ Enable/disable the peakshift power management policy.
+
+ Input should be parseable by kstrtou8().
+ Output will be either "0\n" or "1\n".
+
+What: /sys/class/power_supply/wilco_adv_power/peak_shift_batt_threshold
+Date: January 2019
+KernelVersion: 4.19
+Description:
+ Read/write the battery percentage threshold for which the
+ peakshift policy is used. The valid range is [15, 50].
+
+ Input should be parseable to range [15,50] by kstrtou8().
+ Output will be two-digit ascii number in range [15, 50].
+
+What: /sys/class/power_supply/wilco_adv_power/adv_batt_charging_sched_<day of week>
+Date: January 2019
+KernelVersion: 4.19
+Description:
+ Advanced Charging Mode allows the user to maximize the battery
+ health. In Advanced Charging Mode the system will use standard
+ charging algorithm and other techniques during non-work hours to
+ maximize battery health. During work hours, an express charge is
+ used. This express charge allows the battery to be charged
+ faster; therefore, the battery is at full charge sooner. For
+ each day the time in which the system will be most heavily used
+ is specified by the start time and the duration.
+
+ The input buffer must have the format "start_hr start_min
+ duration_hr duration_min" The hour fields must be in the range
+ [0-23], and the minutes must be one of (0, 15, 30, 45). The
+ string must be parseable by sscanf() using the format string
+ "%d %d %d %d %d %d". An example valid input is "0006 15 23 45",
+ which corresponds to a start time of 6:15 and a duration of
+ 23:45.
+
+ The output buffer will be filled with the format "start_hr
+ start_min duration_hr duration_min" The hour fields will be in
+ the range [0-23], and the minutes will be one of (0, 15, 30,
+ 45). Each number will be zero padded to two characters. An
+ example output is "06 15 23 45", which corresponds to a start
+ time of 6:15 and a duration of 23:45
+
+What: /sys/class/power_supply/wilco_adv_power/adv_batt_charging_enable
++Date: January 2019
++KernelVersion: 4.19
++Description:
+ Enable/disable the Advanced Battery Charging policy.
+
+ Input should be parseable by kstrtou8().
+ Output will be either "0\n" or "1\n".
diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
index 4a119ced4d0c..0e172076b14e 100644
--- a/drivers/platform/chrome/wilco_ec/Kconfig
+++ b/drivers/platform/chrome/wilco_ec/Kconfig
@@ -18,3 +18,17 @@ config WILCO_EC_DEBUGFS
manipulation and allow for testing arbitrary commands. This
interface is intended for debug only and will not be present
on production devices.
+
+config WILCO_EC_ADV_POWER
+ tristate "Enable Peak Shift and Adv Batt Charging power policies"
+ depends on WILCO_EC
+ help
+ If you say Y here, you get support to control two power policies
+ managed by the EC:
+
+ Peak Shift is power saving policy that minimizes AC usage during
+ the peak usage times during the day.
+
+ In Advanced Charging Mode the system will use standard
+ charging algorithms and other techniques during non-work
+ hours to maximize battery health.
diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
index 063e7fb4ea17..b7e8df5d3806 100644
--- a/drivers/platform/chrome/wilco_ec/Makefile
+++ b/drivers/platform/chrome/wilco_ec/Makefile
@@ -4,3 +4,5 @@ wilco_ec-objs := core.o mailbox.o
obj-$(CONFIG_WILCO_EC) += wilco_ec.o
wilco_ec_debugfs-objs := debugfs.o
obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o
+wilco_ec_adv_power-objs := advanced_power.o
+obj-$(CONFIG_WILCO_EC_ADV_POWER) += wilco_ec_adv_power.o
diff --git a/drivers/platform/chrome/wilco_ec/advanced_power.c b/drivers/platform/chrome/wilco_ec/advanced_power.c
new file mode 100644
index 000000000000..d53c8d60f523
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/advanced_power.c
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Peak Shift and Advanced Battery Charging Power Policies
+ *
+ * Copyright 2018 Google LLC
+ *
+ * Peak Shift is power saving policy that minimizes AC usage during the
+ * peak usage times during the day. For each weekday a start and end time
+ * to run in Peak Shift mode can be set. During these times the system will
+ * run from the battery even if the AC is attached as long as the battery
+ * stays above the threshold specified. After the end time specified the
+ * system will run from AC if attached but will not charge the battery. The
+ * system will again function normally using AC and recharging the battery
+ * after the specified Charge Start time.
+ *
+ * Advanced Charging Mode allows the user to maximize the battery health.
+ * In Advanced Charging Mode the system will use standard charging
+ * algorithm and other techniques during non-work hours to maximize battery
+ * health. During work hours, an express charge is used. This express
+ * charge allows the battery to be charged faster; therefore, the battery
+ * is at full charge sooner. For each day the time in which the system will
+ * be most heavily used is specified by the start time and the duration.
+
+ */
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/module.h>
+#include <linux/platform_data/wilco-ec.h>
+
+#define DRV_NAME "wilco_adv_power"
+
+static enum power_supply_property wilco_adv_power_props[] = {
+ POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_MONDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_TUESDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_WEDNESDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_THURSDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_FRIDAY,
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY,
+
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_MONDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_TUESDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_WEDNESDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_THURSDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_FRIDAY,
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY,
+};
+
+struct wilco_adv_power_data {
+ struct wilco_ec_device *ec;
+ struct power_supply *psy;
+};
+
+static int wilco_adv_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ // struct wilco_adv_power_data *data = power_supply_get_drvdata(psy);
+ // This is a stub, really will use mailbox() on data->ec...
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD:
+ val->intval = 42;
+ break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
+ val->psval.start_hours = 23;
+ val->psval.start_minutes = 0;
+ val->psval.end_hours = 75;
+ val->psval.end_minutes = 1;
+ val->psval.charge_start_hours = 0;
+ val->psval.charge_start_minutes = 59;
+ break;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
+ val->abcval.start_hours = 67;
+ val->abcval.start_minutes = 13;
+ val->abcval.duration_hours = 0;
+ val->abcval.duration_minutes = 88;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int wilco_adv_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ // struct wilco_adv_power_data *data = power_supply_get_drvdata(psy);
+ // This is a stub, really will use mailbox() on data->ec...
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE:
+ dev_err(&psy->dev, "peak_shift_enable=%d", val->intval);
+ break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD:
+ dev_err(&psy->dev, "peak_shift_batt_thresh=%d", val->intval);
+ break;
+ case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
+ dev_err(&psy->dev, "peak_shift_sched_%d=%d %d %d %d %d %d", psp,
+ val->psval.start_hours,
+ val->psval.start_minutes,
+ val->psval.end_hours,
+ val->psval.end_minutes,
+ val->psval.charge_start_hours,
+ val->psval.charge_start_minutes);
+ break;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE:
+ dev_err(&psy->dev, "adv_batt_charging_enable=%d", val->intval);
+ break;
+ case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
+ POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
+ dev_err(&psy->dev, "adv_batt_charging_sched_%d=%d %d %d %d", psp,
+ val->abcval.start_hours,
+ val->abcval.start_minutes,
+ val->abcval.duration_hours,
+ val->abcval.duration_minutes);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int wilco_adv_power_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return 1;
+}
+
+static const struct power_supply_desc wps_desc = {
+ .properties = wilco_adv_power_props,
+ .num_properties = ARRAY_SIZE(wilco_adv_power_props),
+ .get_property = wilco_adv_power_get_property,
+ .set_property = wilco_adv_power_set_property,
+ .property_is_writeable = wilco_adv_power_property_is_writeable,
+ .name = DRV_NAME,
+ .type = POWER_SUPPLY_TYPE_MAINS,
+};
+
+static int wilco_adv_power_probe(struct platform_device *pdev)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+ struct power_supply_config psy_cfg = {};
+ struct wilco_adv_power_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->ec = ec;
+ psy_cfg.drv_data = data;
+ platform_set_drvdata(pdev, data);
+
+ data->psy = power_supply_register(&pdev->dev, &wps_desc, &psy_cfg);
+ if (IS_ERR(data->psy))
+ return PTR_ERR(data->psy);
+
+ return 0;
+}
+
+static int wilco_adv_power_remove(struct platform_device *pdev)
+{
+ struct wilco_adv_power_data *data = platform_get_drvdata(pdev);
+
+ power_supply_unregister(data->psy);
+ return 0;
+}
+
+static struct platform_driver wilco_adv_power_driver = {
+ .probe = wilco_adv_power_probe,
+ .remove = wilco_adv_power_remove,
+ .driver = {
+ .name = DRV_NAME
+ }
+};
+module_platform_driver(wilco_adv_power_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Nick Crews <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Wilco Peak Shift and Advanced Battery Charging driver");
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
index 05e1e2be1c91..8f53214cbb1e 100644
--- a/drivers/platform/chrome/wilco_ec/core.c
+++ b/drivers/platform/chrome/wilco_ec/core.c
@@ -89,8 +89,21 @@ static int wilco_ec_probe(struct platform_device *pdev)
goto unregister_debugfs;
}
+ /* Register child device that will be found by the peak shift driver. */
+ ec->adv_power_pdev = platform_device_register_data(dev,
+ "wilco_adv_power",
+ PLATFORM_DEVID_AUTO,
+ NULL, 0);
+ if (IS_ERR(ec->adv_power_pdev)) {
+ dev_err(dev, "Failed to create adv power platform device\n");
+ ret = PTR_ERR(ec->adv_power_pdev);
+ goto unregister_rtc;
+ }
+
return 0;
+unregister_rtc:
+ platform_device_unregister(ec->rtc_pdev);
unregister_debugfs:
if (ec->debugfs_pdev)
platform_device_unregister(ec->debugfs_pdev);
@@ -102,6 +115,7 @@ static int wilco_ec_remove(struct platform_device *pdev)
{
struct wilco_ec_device *ec = platform_get_drvdata(pdev);
+ platform_device_unregister(ec->adv_power_pdev);
platform_device_unregister(ec->rtc_pdev);
if (ec->debugfs_pdev)
platform_device_unregister(ec->debugfs_pdev);
diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h
index 446473a46b88..84e9aacd3759 100644
--- a/include/linux/platform_data/wilco-ec.h
+++ b/include/linux/platform_data/wilco-ec.h
@@ -36,6 +36,7 @@
* @data_size: Size of the data buffer used for EC communication.
* @debugfs_pdev: The child platform_device used by the debugfs sub-driver.
* @rtc_pdev: The child platform_device used by the RTC sub-driver.
+ * @adv_power_pdev: Child platform_device used by the advanced power sub-driver.
*/
struct wilco_ec_device {
struct device *dev;
@@ -47,6 +48,7 @@ struct wilco_ec_device {
size_t data_size;
struct platform_device *debugfs_pdev;
struct platform_device *rtc_pdev;
+ struct platform_device *adv_power_pdev;
};
/**
--
2.20.1
Friendly bump to make sure this doesn't slip down peoples' inboxes.
Any comments would be appreciated :)
-Nick
On Fri, Feb 22, 2019 at 4:10 PM Nick Crews <[email protected]> wrote:
>
> This is an example driver that uses the proposed Peak Shift and
> Adv Batt Charging support in the main power supply subsystem.
>
> At this point, there is no actual communication with the EC,
> the point of this is to show the interface that usespace would see,
> and to show the use case for the added properties to the power
> supply susbsystem. To see the location of where the properties
> would appear in userspace, see the Documentation file.
>
> Signed-off-by: Nick Crews <[email protected]>
> ---
> .../ABI/testing/sysfs-class-power-wilco | 81 +++++++
> drivers/platform/chrome/wilco_ec/Kconfig | 14 ++
> drivers/platform/chrome/wilco_ec/Makefile | 2 +
> .../platform/chrome/wilco_ec/advanced_power.c | 199 ++++++++++++++++++
> drivers/platform/chrome/wilco_ec/core.c | 14 ++
> include/linux/platform_data/wilco-ec.h | 2 +
> 6 files changed, 312 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-class-power-wilco
> create mode 100644 drivers/platform/chrome/wilco_ec/advanced_power.c
>
> diff --git a/Documentation/ABI/testing/sysfs-class-power-wilco b/Documentation/ABI/testing/sysfs-class-power-wilco
> new file mode 100644
> index 000000000000..5c96b28aa597
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-power-wilco
> @@ -0,0 +1,81 @@
> +What: /sys/class/power_supply/wilco_adv_power/peak_shift_sched_<day of week>
> +Date: January 2019
> +KernelVersion: 4.19
> +Description:
> + For each weekday a start and end time to run in Peak Shift mode
> + can be set. During these times the system will run from the
> + battery even if the AC is attached as long as the battery stays
> + above the threshold specified. After the end time specified the
> + system will run from AC if attached but will not charge the
> + battery. The system will again function normally using AC and
> + recharging the battery after the specified Charge Start time.
> +
> + The input buffer must have the format "start_hr start_min end_hr
> + end_min charge_start_hr charge_start_min" The hour fields must
> + be in the range [0-23], and the minutes must be one of (0, 15,
> + 30, 45). The string must be parseable by sscanf() using the
> + format string "%d %d %d %d %d %d". An example valid input is
> + "6 15 009 45 23 0", which corresponds to 6:15, 9:45, and 23:00
> +
> + The output buffer will be filled with the format "start_hr
> + start_min end_hr end_min charge_start_hr charge_start_min" The
> + hour fields will be in the range [0-23], and the minutes will be
> + one of (0, 15, 30, 45). Each number will be zero padded to two
> + characters. An example output is "06 15 09 45 23 00", which
> + corresponds to 6:15, 9:45, and 23:00
> +
> +What: /sys/class/power_supply/wilco_adv_power/peak_shift_enable
> +Date: January 2019
> +KernelVersion: 4.19
> +Description:
> + Enable/disable the peakshift power management policy.
> +
> + Input should be parseable by kstrtou8().
> + Output will be either "0\n" or "1\n".
> +
> +What: /sys/class/power_supply/wilco_adv_power/peak_shift_batt_threshold
> +Date: January 2019
> +KernelVersion: 4.19
> +Description:
> + Read/write the battery percentage threshold for which the
> + peakshift policy is used. The valid range is [15, 50].
> +
> + Input should be parseable to range [15,50] by kstrtou8().
> + Output will be two-digit ascii number in range [15, 50].
> +
> +What: /sys/class/power_supply/wilco_adv_power/adv_batt_charging_sched_<day of week>
> +Date: January 2019
> +KernelVersion: 4.19
> +Description:
> + Advanced Charging Mode allows the user to maximize the battery
> + health. In Advanced Charging Mode the system will use standard
> + charging algorithm and other techniques during non-work hours to
> + maximize battery health. During work hours, an express charge is
> + used. This express charge allows the battery to be charged
> + faster; therefore, the battery is at full charge sooner. For
> + each day the time in which the system will be most heavily used
> + is specified by the start time and the duration.
> +
> + The input buffer must have the format "start_hr start_min
> + duration_hr duration_min" The hour fields must be in the range
> + [0-23], and the minutes must be one of (0, 15, 30, 45). The
> + string must be parseable by sscanf() using the format string
> + "%d %d %d %d %d %d". An example valid input is "0006 15 23 45",
> + which corresponds to a start time of 6:15 and a duration of
> + 23:45.
> +
> + The output buffer will be filled with the format "start_hr
> + start_min duration_hr duration_min" The hour fields will be in
> + the range [0-23], and the minutes will be one of (0, 15, 30,
> + 45). Each number will be zero padded to two characters. An
> + example output is "06 15 23 45", which corresponds to a start
> + time of 6:15 and a duration of 23:45
> +
> +What: /sys/class/power_supply/wilco_adv_power/adv_batt_charging_enable
> ++Date: January 2019
> ++KernelVersion: 4.19
> ++Description:
> + Enable/disable the Advanced Battery Charging policy.
> +
> + Input should be parseable by kstrtou8().
> + Output will be either "0\n" or "1\n".
> diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
> index 4a119ced4d0c..0e172076b14e 100644
> --- a/drivers/platform/chrome/wilco_ec/Kconfig
> +++ b/drivers/platform/chrome/wilco_ec/Kconfig
> @@ -18,3 +18,17 @@ config WILCO_EC_DEBUGFS
> manipulation and allow for testing arbitrary commands. This
> interface is intended for debug only and will not be present
> on production devices.
> +
> +config WILCO_EC_ADV_POWER
> + tristate "Enable Peak Shift and Adv Batt Charging power policies"
> + depends on WILCO_EC
> + help
> + If you say Y here, you get support to control two power policies
> + managed by the EC:
> +
> + Peak Shift is power saving policy that minimizes AC usage during
> + the peak usage times during the day.
> +
> + In Advanced Charging Mode the system will use standard
> + charging algorithms and other techniques during non-work
> + hours to maximize battery health.
> diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
> index 063e7fb4ea17..b7e8df5d3806 100644
> --- a/drivers/platform/chrome/wilco_ec/Makefile
> +++ b/drivers/platform/chrome/wilco_ec/Makefile
> @@ -4,3 +4,5 @@ wilco_ec-objs := core.o mailbox.o
> obj-$(CONFIG_WILCO_EC) += wilco_ec.o
> wilco_ec_debugfs-objs := debugfs.o
> obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o
> +wilco_ec_adv_power-objs := advanced_power.o
> +obj-$(CONFIG_WILCO_EC_ADV_POWER) += wilco_ec_adv_power.o
> diff --git a/drivers/platform/chrome/wilco_ec/advanced_power.c b/drivers/platform/chrome/wilco_ec/advanced_power.c
> new file mode 100644
> index 000000000000..d53c8d60f523
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec/advanced_power.c
> @@ -0,0 +1,199 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Peak Shift and Advanced Battery Charging Power Policies
> + *
> + * Copyright 2018 Google LLC
> + *
> + * Peak Shift is power saving policy that minimizes AC usage during the
> + * peak usage times during the day. For each weekday a start and end time
> + * to run in Peak Shift mode can be set. During these times the system will
> + * run from the battery even if the AC is attached as long as the battery
> + * stays above the threshold specified. After the end time specified the
> + * system will run from AC if attached but will not charge the battery. The
> + * system will again function normally using AC and recharging the battery
> + * after the specified Charge Start time.
> + *
> + * Advanced Charging Mode allows the user to maximize the battery health.
> + * In Advanced Charging Mode the system will use standard charging
> + * algorithm and other techniques during non-work hours to maximize battery
> + * health. During work hours, an express charge is used. This express
> + * charge allows the battery to be charged faster; therefore, the battery
> + * is at full charge sooner. For each day the time in which the system will
> + * be most heavily used is specified by the start time and the duration.
> +
> + */
> +#include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/wilco-ec.h>
> +
> +#define DRV_NAME "wilco_adv_power"
> +
> +static enum power_supply_property wilco_adv_power_props[] = {
> + POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_MONDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_TUESDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_WEDNESDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_THURSDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_FRIDAY,
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY,
> +
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_MONDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_TUESDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_WEDNESDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_THURSDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_FRIDAY,
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY,
> +};
> +
> +struct wilco_adv_power_data {
> + struct wilco_ec_device *ec;
> + struct power_supply *psy;
> +};
> +
> +static int wilco_adv_power_get_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + // struct wilco_adv_power_data *data = power_supply_get_drvdata(psy);
> + // This is a stub, really will use mailbox() on data->ec...
> + int ret = 0;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE:
> + val->intval = 1;
> + break;
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD:
> + val->intval = 42;
> + break;
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
> + val->psval.start_hours = 23;
> + val->psval.start_minutes = 0;
> + val->psval.end_hours = 75;
> + val->psval.end_minutes = 1;
> + val->psval.charge_start_hours = 0;
> + val->psval.charge_start_minutes = 59;
> + break;
> + case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE:
> + val->intval = 0;
> + break;
> + case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
> + val->abcval.start_hours = 67;
> + val->abcval.start_minutes = 13;
> + val->abcval.duration_hours = 0;
> + val->abcval.duration_minutes = 88;
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int wilco_adv_power_set_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + // struct wilco_adv_power_data *data = power_supply_get_drvdata(psy);
> + // This is a stub, really will use mailbox() on data->ec...
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_ENABLE:
> + dev_err(&psy->dev, "peak_shift_enable=%d", val->intval);
> + break;
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_BATT_THRESHOLD:
> + dev_err(&psy->dev, "peak_shift_batt_thresh=%d", val->intval);
> + break;
> + case POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SUNDAY ...
> + POWER_SUPPLY_PROP_PEAK_SHIFT_SCHED_SATURDAY:
> + dev_err(&psy->dev, "peak_shift_sched_%d=%d %d %d %d %d %d", psp,
> + val->psval.start_hours,
> + val->psval.start_minutes,
> + val->psval.end_hours,
> + val->psval.end_minutes,
> + val->psval.charge_start_hours,
> + val->psval.charge_start_minutes);
> + break;
> + case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_ENABLE:
> + dev_err(&psy->dev, "adv_batt_charging_enable=%d", val->intval);
> + break;
> + case POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SUNDAY ...
> + POWER_SUPPLY_PROP_ADV_BATT_CHARGING_SCHED_SATURDAY:
> + dev_err(&psy->dev, "adv_batt_charging_sched_%d=%d %d %d %d", psp,
> + val->abcval.start_hours,
> + val->abcval.start_minutes,
> + val->abcval.duration_hours,
> + val->abcval.duration_minutes);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static inline int wilco_adv_power_property_is_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return 1;
> +}
> +
> +static const struct power_supply_desc wps_desc = {
> + .properties = wilco_adv_power_props,
> + .num_properties = ARRAY_SIZE(wilco_adv_power_props),
> + .get_property = wilco_adv_power_get_property,
> + .set_property = wilco_adv_power_set_property,
> + .property_is_writeable = wilco_adv_power_property_is_writeable,
> + .name = DRV_NAME,
> + .type = POWER_SUPPLY_TYPE_MAINS,
> +};
> +
> +static int wilco_adv_power_probe(struct platform_device *pdev)
> +{
> + struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
> + struct power_supply_config psy_cfg = {};
> + struct wilco_adv_power_data *data;
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (data == NULL)
> + return -ENOMEM;
> +
> + data->ec = ec;
> + psy_cfg.drv_data = data;
> + platform_set_drvdata(pdev, data);
> +
> + data->psy = power_supply_register(&pdev->dev, &wps_desc, &psy_cfg);
> + if (IS_ERR(data->psy))
> + return PTR_ERR(data->psy);
> +
> + return 0;
> +}
> +
> +static int wilco_adv_power_remove(struct platform_device *pdev)
> +{
> + struct wilco_adv_power_data *data = platform_get_drvdata(pdev);
> +
> + power_supply_unregister(data->psy);
> + return 0;
> +}
> +
> +static struct platform_driver wilco_adv_power_driver = {
> + .probe = wilco_adv_power_probe,
> + .remove = wilco_adv_power_remove,
> + .driver = {
> + .name = DRV_NAME
> + }
> +};
> +module_platform_driver(wilco_adv_power_driver);
> +
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_AUTHOR("Nick Crews <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Wilco Peak Shift and Advanced Battery Charging driver");
> diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
> index 05e1e2be1c91..8f53214cbb1e 100644
> --- a/drivers/platform/chrome/wilco_ec/core.c
> +++ b/drivers/platform/chrome/wilco_ec/core.c
> @@ -89,8 +89,21 @@ static int wilco_ec_probe(struct platform_device *pdev)
> goto unregister_debugfs;
> }
>
> + /* Register child device that will be found by the peak shift driver. */
> + ec->adv_power_pdev = platform_device_register_data(dev,
> + "wilco_adv_power",
> + PLATFORM_DEVID_AUTO,
> + NULL, 0);
> + if (IS_ERR(ec->adv_power_pdev)) {
> + dev_err(dev, "Failed to create adv power platform device\n");
> + ret = PTR_ERR(ec->adv_power_pdev);
> + goto unregister_rtc;
> + }
> +
> return 0;
>
> +unregister_rtc:
> + platform_device_unregister(ec->rtc_pdev);
> unregister_debugfs:
> if (ec->debugfs_pdev)
> platform_device_unregister(ec->debugfs_pdev);
> @@ -102,6 +115,7 @@ static int wilco_ec_remove(struct platform_device *pdev)
> {
> struct wilco_ec_device *ec = platform_get_drvdata(pdev);
>
> + platform_device_unregister(ec->adv_power_pdev);
> platform_device_unregister(ec->rtc_pdev);
> if (ec->debugfs_pdev)
> platform_device_unregister(ec->debugfs_pdev);
> diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h
> index 446473a46b88..84e9aacd3759 100644
> --- a/include/linux/platform_data/wilco-ec.h
> +++ b/include/linux/platform_data/wilco-ec.h
> @@ -36,6 +36,7 @@
> * @data_size: Size of the data buffer used for EC communication.
> * @debugfs_pdev: The child platform_device used by the debugfs sub-driver.
> * @rtc_pdev: The child platform_device used by the RTC sub-driver.
> + * @adv_power_pdev: Child platform_device used by the advanced power sub-driver.
> */
> struct wilco_ec_device {
> struct device *dev;
> @@ -47,6 +48,7 @@ struct wilco_ec_device {
> size_t data_size;
> struct platform_device *debugfs_pdev;
> struct platform_device *rtc_pdev;
> + struct platform_device *adv_power_pdev;
> };
>
> /**
> --
> 2.20.1
>