[RFC power_supply 1/2] power_supply: Add Peak Shift and Adv Batt Charging support

From: Nick Crews
Date: Fri Feb 22 2019 - 18:11:51 EST


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 <ncrews@xxxxxxxxxxxx>
---
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