Add "Standard", "Adaptive", and "Custom" modes to the charge_type
property, to expand the existing "Trickle" and "Fast" modes.
In addition, add POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD
and POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD properties, to expand
the existing CHARGE_CONTROL_* properties. I am adding them in order
to support a new Chrome OS device, but these properties should be
general enough that they can be used on other devices.
The meaning of "Standard" is obvious, but "Adaptive" and "Custom" are
more tricky: "Adaptive" means that the charge controller uses some
custom algorithm to change the charge type automatically, with no
configuration needed. "Custom" means that the charge controller uses the
POWER_SUPPLY_PROP_CHARGE_CONTROL_* properties as configuration for some
other algorithm. For example, in the use case that I am supporting,
this means the battery begins charging when the percentage
level drops below POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD and
charging ceases when the percentage level goes above
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD.
Signed-off-by: Nick Crews <[email protected]>
---
drivers/power/supply/power_supply_sysfs.c | 4 +++-
include/linux/power_supply.h | 10 ++++++++--
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index dce24f596160..6104a3f03d46 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -56,7 +56,7 @@ static const char * const power_supply_status_text[] = {
};
static const char * const power_supply_charge_type_text[] = {
- "Unknown", "N/A", "Trickle", "Fast"
+ "Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom"
};
static const char * const power_supply_health_text[] = {
@@ -274,6 +274,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(constant_charge_voltage_max),
POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max),
+ POWER_SUPPLY_ATTR(charge_control_start_threshold),
+ POWER_SUPPLY_ATTR(charge_control_end_threshold),
POWER_SUPPLY_ATTR(input_current_limit),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 2f9c201a54d1..d59205170232 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -40,11 +40,15 @@ enum {
POWER_SUPPLY_STATUS_FULL,
};
+/* What algorithm is the charger using? */
enum {
POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0,
POWER_SUPPLY_CHARGE_TYPE_NONE,
- POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
- POWER_SUPPLY_CHARGE_TYPE_FAST,
+ POWER_SUPPLY_CHARGE_TYPE_TRICKLE, /* slow speed */
+ POWER_SUPPLY_CHARGE_TYPE_FAST, /* fast speed */
+ POWER_SUPPLY_CHARGE_TYPE_STANDARD, /* normal speed */
+ POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE, /* dynamically adjusted speed */
+ POWER_SUPPLY_CHARGE_TYPE_CUSTOM, /* use CHARGE_CONTROL_* props */
};
enum {
@@ -121,6 +125,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, /* in percents! */
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
--
2.20.1
Add control of the charging algorithm used on Wilco devices.
See Documentation/ABI/testing/sysfs-class-power-wilco for the
userspace interface and other info.
v3 changes:
-Add this changelog
-Fix commit message tags
v2 changes:
-Update Documentation to say KernelVersion 5.2
-Update Documentation to explain Trickle mode better.
-rename things from using *PCC* to *CHARGE*
-Split up conversions between POWER_SUPPLY_PROP_CHARGE_TYPE values
and Wilco EC codes
-Use devm_ flavor of power_supply_register(), which simplifies things
-Add extra error checking on property messages received from the EC
-Fix bug in memcpy() calls in properties.c
-Refactor fill_property_id()
-Add valid input checks to charge_type
-Properly convert charge_type when get()ting
Signed-off-by: Nick Crews <[email protected]>
---
.../ABI/testing/sysfs-class-power-wilco | 30 +++
drivers/platform/chrome/wilco_ec/Kconfig | 9 +
drivers/platform/chrome/wilco_ec/Makefile | 2 +
.../platform/chrome/wilco_ec/charge_config.c | 190 ++++++++++++++++++
drivers/platform/chrome/wilco_ec/core.c | 14 ++
drivers/platform/chrome/wilco_ec/properties.c | 134 ++++++++++++
drivers/platform/chrome/wilco_ec/properties.h | 68 +++++++
include/linux/platform_data/wilco-ec.h | 2 +
8 files changed, 449 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-power-wilco
create mode 100644 drivers/platform/chrome/wilco_ec/charge_config.c
create mode 100644 drivers/platform/chrome/wilco_ec/properties.c
create mode 100644 drivers/platform/chrome/wilco_ec/properties.h
diff --git a/Documentation/ABI/testing/sysfs-class-power-wilco b/Documentation/ABI/testing/sysfs-class-power-wilco
new file mode 100644
index 000000000000..7f3b01310476
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power-wilco
@@ -0,0 +1,30 @@
+What: /sys/class/power_supply/wilco_charger/charge_type
+Date: April 2019
+KernelVersion: 5.2
+Description:
+ What charging algorithm to use:
+
+ Standard: Fully charges battery at a standard rate.
+ Adaptive: Battery settings adaptively optimized based on
+ typical battery usage pattern.
+ Fast: Battery charges over a shorter period.
+ Trickle: Extends battery lifespan, intended for users who
+ primarily use their Chromebook while connected to AC.
+ Custom: A low and high threshold percentage is specified.
+ Charging begins when level drops below
+ charge_control_start_threshold, and ceases when
+ level is above charge_control_end_threshold.
+
+What: /sys/class/power_supply/wilco_charger/charge_control_start_threshold
+Date: April 2019
+KernelVersion: 5.2
+Description:
+ Used when charge_type="Custom", as described above. Measured in
+ percentages. The valid range is [50, 95].
+
+What: /sys/class/power_supply/wilco_charger/charge_control_end_threshold
+Date: April 2019
+KernelVersion: 5.2
+Description:
+ Used when charge_type="Custom", as described above. Measured in
+ percentages. The valid range is [55, 100].
diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
index e09e4cebe9b4..1c427830bd57 100644
--- a/drivers/platform/chrome/wilco_ec/Kconfig
+++ b/drivers/platform/chrome/wilco_ec/Kconfig
@@ -18,3 +18,12 @@ 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_CHARGE_CNTL
+ tristate "Enable charging control"
+ depends on WILCO_EC
+ help
+ If you say Y here, you get support to control the charging
+ routines performed by the Wilco Embedded Controller.
+ Further information can be found in
+ Documentation/ABI/testing/sysfs-class-power-wilco)
diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
index 063e7fb4ea17..7e980f56f793 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_charging-objs := charge_config.o properties.o
+obj-$(CONFIG_WILCO_EC_CHARGE_CNTL) += wilco_ec_charging.o
diff --git a/drivers/platform/chrome/wilco_ec/charge_config.c b/drivers/platform/chrome/wilco_ec/charge_config.c
new file mode 100644
index 000000000000..7c41b847396d
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/charge_config.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Charging control driver for the Wilco EC
+ *
+ * Copyright 2019 Google LLC
+ *
+ * See Documentation/ABI/testing/sysfs-class-power-wilco for
+ * userspace interface and other info.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/wilco-ec.h>
+#include <linux/power_supply.h>
+
+#include "properties.h"
+
+#define DRV_NAME "wilco-ec-charging"
+
+/* Property IDs and related EC constants */
+#define PID_CHARGE_MODE 0x0710
+#define PID_CHARGE_LOWER_LIMIT 0x0711
+#define PID_CHARGE_UPPER_LIMIT 0x0712
+
+enum charge_mode {
+ CHARGE_MODE_STD = 1, /* Used for Standard */
+ CHARGE_MODE_EXP = 2, /* Express Charge, used for Fast */
+ CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */
+ CHARGE_MODE_AUTO = 4, /* Used for Adaptive */
+ CHARGE_MODE_CUSTOM = 5, /* Used for Custom */
+};
+
+#define CHARGE_LOWER_LIMIT_MIN 50
+#define CHARGE_LOWER_LIMIT_MAX 95
+#define CHARGE_UPPER_LIMIT_MIN 55
+#define CHARGE_UPPER_LIMIT_MAX 100
+
+/* Convert from POWER_SUPPLY_PROP_CHARGE_TYPE value to the EC's charge mode */
+static enum charge_mode psp_val_to_charge_mode(int psp_val)
+{
+ switch (psp_val) {
+ case (POWER_SUPPLY_CHARGE_TYPE_TRICKLE):
+ return CHARGE_MODE_AC;
+ case (POWER_SUPPLY_CHARGE_TYPE_FAST):
+ return CHARGE_MODE_EXP;
+ case (POWER_SUPPLY_CHARGE_TYPE_STANDARD):
+ return CHARGE_MODE_STD;
+ case (POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE):
+ return CHARGE_MODE_AUTO;
+ case (POWER_SUPPLY_CHARGE_TYPE_CUSTOM):
+ return CHARGE_MODE_CUSTOM;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Convert from EC's charge mode to POWER_SUPPLY_PROP_CHARGE_TYPE value */
+static int charge_mode_to_psp_val(enum charge_mode mode)
+{
+ switch (mode) {
+ case (CHARGE_MODE_AC):
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ case (CHARGE_MODE_EXP):
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ case (CHARGE_MODE_STD):
+ return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ case (CHARGE_MODE_AUTO):
+ return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
+ case (CHARGE_MODE_CUSTOM):
+ return POWER_SUPPLY_CHARGE_TYPE_CUSTOM;
+ default:
+ return -EINVAL;
+ }
+}
+
+static enum power_supply_property wilco_charge_props[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+};
+
+static int wilco_charge_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
+ u32 property_id;
+ int ret;
+ u8 raw;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ property_id = PID_CHARGE_MODE;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
+ property_id = PID_CHARGE_LOWER_LIMIT;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+ property_id = PID_CHARGE_UPPER_LIMIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = wilco_ec_get_byte_property(ec, property_id, &raw);
+ if (ret < 0)
+ return ret;
+ if (property_id == PID_CHARGE_MODE) {
+ ret = charge_mode_to_psp_val(raw);
+ if (ret == -EINVAL)
+ return -EBADMSG;
+ raw = ret;
+ }
+ val->intval = raw;
+
+ return 0;
+}
+
+static int wilco_charge_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
+ enum charge_mode mode;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ mode = psp_val_to_charge_mode(val->intval);
+ if (mode == -EINVAL)
+ return -EINVAL;
+ return wilco_ec_set_byte_property(ec, PID_CHARGE_MODE, mode);
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
+ if (val->intval < CHARGE_LOWER_LIMIT_MIN ||
+ val->intval > CHARGE_LOWER_LIMIT_MAX)
+ return -EINVAL;
+ return wilco_ec_set_byte_property(ec, PID_CHARGE_LOWER_LIMIT,
+ val->intval);
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+ if (val->intval < CHARGE_UPPER_LIMIT_MIN ||
+ val->intval > CHARGE_UPPER_LIMIT_MAX)
+ return -EINVAL;
+ return wilco_ec_set_byte_property(ec, PID_CHARGE_UPPER_LIMIT,
+ val->intval);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wilco_charge_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return 1;
+}
+
+static const struct power_supply_desc wilco_ps_desc = {
+ .properties = wilco_charge_props,
+ .num_properties = ARRAY_SIZE(wilco_charge_props),
+ .get_property = wilco_charge_get_property,
+ .set_property = wilco_charge_set_property,
+ .property_is_writeable = wilco_charge_property_is_writeable,
+ .name = "wilco-charger",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+};
+
+static int wilco_charge_probe(struct platform_device *pdev)
+{
+ struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+ struct power_supply_config psy_cfg = {};
+ struct power_supply *psy;
+
+ psy_cfg.drv_data = ec;
+ psy = devm_power_supply_register(&pdev->dev, &wilco_ps_desc, &psy_cfg);
+ if (IS_ERR(psy))
+ return PTR_ERR(psy);
+
+ return 0;
+}
+
+static struct platform_driver wilco_charge_driver = {
+ .probe = wilco_charge_probe,
+ .driver = {
+ .name = DRV_NAME,
+ }
+};
+module_platform_driver(wilco_charge_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Nick Crews <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Wilco EC charge control driver");
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
index 05e1e2be1c91..b6f3b061f37b 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 to be found by charging config driver. */
+ ec->charging_pdev = platform_device_register_data(dev,
+ "wilco-ec-charging",
+ PLATFORM_DEVID_AUTO,
+ NULL, 0);
+ if (IS_ERR(ec->charging_pdev)) {
+ dev_err(dev, "Failed to create charging platform device\n");
+ ret = PTR_ERR(ec->charging_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->charging_pdev);
platform_device_unregister(ec->rtc_pdev);
if (ec->debugfs_pdev)
platform_device_unregister(ec->debugfs_pdev);
diff --git a/drivers/platform/chrome/wilco_ec/properties.c b/drivers/platform/chrome/wilco_ec/properties.c
new file mode 100644
index 000000000000..d74eb1208afd
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/properties.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#include <linux/string.h>
+#include "properties.h"
+
+struct ec_property_request {
+ u8 op;
+ u8 property_id[4];
+ u8 length;
+ u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
+} __packed;
+
+struct ec_property_response {
+ u8 reserved[2];
+ u8 op;
+ u8 property_id[4];
+ u8 length;
+ u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
+} __packed;
+
+static inline void fill_request_property_id(struct ec_property_request *rq,
+ u32 property_id)
+{
+ rq->property_id[0] = property_id & 0xff;
+ rq->property_id[1] = (property_id >> 8) & 0xff;
+ rq->property_id[2] = (property_id >> 16) & 0xff;
+ rq->property_id[3] = (property_id >> 24) & 0xff;
+}
+
+static int send_property_msg(struct wilco_ec_device *ec,
+ struct ec_property_request *rq,
+ struct ec_property_response *rs)
+{
+ struct wilco_ec_message ec_msg;
+ int ret;
+
+ memset(&ec_msg, 0, sizeof(ec_msg));
+ ec_msg.type = WILCO_EC_MSG_PROPERTY;
+ ec_msg.request_data = rq;
+ ec_msg.request_size = sizeof(*rq);
+ ec_msg.response_data = rs;
+ ec_msg.response_size = sizeof(*rs);
+ ret = wilco_ec_mailbox(ec, &ec_msg);
+ if (ret < 0)
+ return ret;
+
+ if (rs->op != rq->op)
+ return -EBADMSG;
+ if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id)))
+ return -EBADMSG;
+ if (rs->length > sizeof(rs->data))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+int wilco_ec_get_property(struct wilco_ec_device *ec,
+ struct ec_property_get_msg *prop_msg)
+{
+ struct ec_property_request rq;
+ struct ec_property_response rs;
+ int ret;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.op = OP_GET;
+ fill_request_property_id(&rq, prop_msg->property_id);
+
+ ret = send_property_msg(ec, &rq, &rs);
+ if (ret < 0)
+ return ret;
+
+ prop_msg->length = rs.length;
+ memcpy(prop_msg->data, rs.data, rs.length);
+
+ return 0;
+}
+
+int wilco_ec_set_property(struct wilco_ec_device *ec,
+ struct ec_property_set_msg *prop_msg)
+{
+ struct ec_property_request rq;
+ struct ec_property_response rs;
+ int ret;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.op = prop_msg->op;
+ fill_request_property_id(&rq, prop_msg->property_id);
+ rq.length = prop_msg->length;
+ memcpy(rq.data, prop_msg->data, prop_msg->length);
+
+ ret = send_property_msg(ec, &rq, &rs);
+ if (ret < 0)
+ return ret;
+
+ if (rs.length != prop_msg->length)
+ return -EBADMSG;
+
+ return 0;
+}
+
+int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id,
+ u8 *val)
+{
+ struct ec_property_get_msg msg;
+ int ret;
+
+ msg.property_id = property_id;
+ ret = wilco_ec_get_property(ec, &msg);
+ if (ret)
+ return ret;
+
+ if (msg.length != 1)
+ return -EBADMSG;
+
+ *val = msg.data[0];
+
+ return 0;
+}
+
+int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id,
+ u8 val)
+{
+ struct ec_property_set_msg msg;
+
+ msg.property_id = property_id;
+ msg.op = OP_SET;
+ msg.data[0] = val;
+ msg.length = 1;
+
+ return wilco_ec_set_property(ec, &msg);
+}
diff --git a/drivers/platform/chrome/wilco_ec/properties.h b/drivers/platform/chrome/wilco_ec/properties.h
new file mode 100644
index 000000000000..da0bb3b869af
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/properties.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Helper library for property access on the Wilco EC.
+ *
+ * Copyright 2019 Google LLC
+ *
+ * A Property is typically a data item that is stored to NVRAM
+ * by the EC. Each of these data items has an index associated
+ * with it known as the Property ID (PID). Properties may have
+ * variable lengths, up to a max of WILCO_EC_PROPERTY_MAX_SIZE
+ * bytes. Properties can be simple integers, or they may be more
+ * complex binary data.
+ */
+
+#include <linux/platform_data/wilco-ec.h>
+
+#define WILCO_EC_PROPERTY_MAX_SIZE 4
+
+/*
+ * Properties are accessed with an subcommand, or "op". OP_GET
+ * requests the property from the EC. OP_SET and OP_SYNC do the
+ * exact same thing from our perspective: save a property. Only
+ * one of them works for a given property, so each property uses
+ * either OP_GET and OP_SET, or OP_GET and OP_SYNC.
+ */
+enum get_set_sync_op {
+ OP_GET = 0,
+ OP_SET = 1,
+ OP_SYNC = 4
+};
+
+/**
+ * struct ec_property_get_msg - Message to retrieve a property.
+ * @property_id: PID of property to retrieve.
+ * @length: number of bytes received, set by wilco_ec_get_property().
+ * @data: actual property data, set by wilco_ec_get_property().
+ */
+struct ec_property_get_msg {
+ u32 property_id;
+ int length;
+ u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
+};
+
+/**
+ * struct ec_property_set_msg - Message to save a property.
+ * @op: Which subcommand to use, either OP_SET or OP_SYNC
+ * @property_id: PID of property to save.
+ * @length: number of bytes to save, must not exceed WILCO_EC_PROPERTY_MAX_SIZE.
+ * @data: actual property data.
+ */
+struct ec_property_set_msg {
+ enum get_set_sync_op op;
+ u32 property_id;
+ int length;
+ u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
+};
+
+/* Both of these will return 0 on success, negative error code on failure */
+int wilco_ec_get_property(struct wilco_ec_device *ec,
+ struct ec_property_get_msg *prop_msg);
+int wilco_ec_set_property(struct wilco_ec_device *ec,
+ struct ec_property_set_msg *prop_msg);
+
+/* Both of these will return 0 on success, negative error code on failure */
+int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id,
+ u8 *val);
+int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id,
+ u8 val);
diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h
index 1ff224793c99..4e7dce897500 100644
--- a/include/linux/platform_data/wilco-ec.h
+++ b/include/linux/platform_data/wilco-ec.h
@@ -32,6 +32,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.
+ * @charging_pdev: Child platform_device used by the charging config sub-driver.
*/
struct wilco_ec_device {
struct device *dev;
@@ -43,6 +44,7 @@ struct wilco_ec_device {
size_t data_size;
struct platform_device *debugfs_pdev;
struct platform_device *rtc_pdev;
+ struct platform_device *charging_pdev;
};
/**
--
2.20.1
From: kbuild test robot <[email protected]>
drivers/platform/chrome/wilco_ec/charge_config.c:173:1-3: WARNING: PTR_ERR_OR_ZERO can be used
Use PTR_ERR_OR_ZERO rather than if(IS_ERR(...)) + PTR_ERR
Generated by: scripts/coccinelle/api/ptr_ret.cocci
Fixes: 08c1b2e2e50e ("power_supply: platform/chrome: wilco_ec: Add charging config driver")
CC: Nick Crews <[email protected]>
Signed-off-by: kbuild test robot <[email protected]>
---
url: https://github.com/0day-ci/linux/commits/Nick-Crews/power_supply-Add-more-charge-types-and-CHARGE_CONTROL_-properties/20190412-211253
charge_config.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
--- a/drivers/platform/chrome/wilco_ec/charge_config.c
+++ b/drivers/platform/chrome/wilco_ec/charge_config.c
@@ -170,10 +170,7 @@ static int wilco_charge_probe(struct pla
psy_cfg.drv_data = ec;
psy = devm_power_supply_register(&pdev->dev, &wilco_ps_desc, &psy_cfg);
- if (IS_ERR(psy))
- return PTR_ERR(psy);
-
- return 0;
+ return PTR_ERR_OR_ZERO(psy);
}
static struct platform_driver wilco_charge_driver = {
Hi Nick,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[also build test WARNING on v5.1-rc4 next-20190412]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Nick-Crews/power_supply-Add-more-charge-types-and-CHARGE_CONTROL_-properties/20190412-211253
coccinelle warnings: (new ones prefixed by >>)
>> drivers/platform/chrome/wilco_ec/charge_config.c:173:1-3: WARNING: PTR_ERR_OR_ZERO can be used
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Nick,
On 12/4/19 2:20, Nick Crews wrote:
> Add "Standard", "Adaptive", and "Custom" modes to the charge_type
> property, to expand the existing "Trickle" and "Fast" modes.
> In addition, add POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD
> and POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD properties, to expand
> the existing CHARGE_CONTROL_* properties. I am adding them in order
> to support a new Chrome OS device, but these properties should be
> general enough that they can be used on other devices.
>
> The meaning of "Standard" is obvious, but "Adaptive" and "Custom" are
> more tricky: "Adaptive" means that the charge controller uses some
> custom algorithm to change the charge type automatically, with no
> configuration needed. "Custom" means that the charge controller uses the
> POWER_SUPPLY_PROP_CHARGE_CONTROL_* properties as configuration for some
> other algorithm. For example, in the use case that I am supporting,
> this means the battery begins charging when the percentage
> level drops below POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD and
> charging ceases when the percentage level goes above
> POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD.
>
> Signed-off-by: Nick Crews <[email protected]>
> ---
> drivers/power/supply/power_supply_sysfs.c | 4 +++-
> include/linux/power_supply.h | 10 ++++++++--
> 2 files changed, 11 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
> index dce24f596160..6104a3f03d46 100644
> --- a/drivers/power/supply/power_supply_sysfs.c
> +++ b/drivers/power/supply/power_supply_sysfs.c
> @@ -56,7 +56,7 @@ static const char * const power_supply_status_text[] = {
> };
>
> static const char * const power_supply_charge_type_text[] = {
> - "Unknown", "N/A", "Trickle", "Fast"
> + "Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom"
> };
>
> static const char * const power_supply_health_text[] = {
> @@ -274,6 +274,8 @@ static struct device_attribute power_supply_attrs[] = {
> POWER_SUPPLY_ATTR(constant_charge_voltage_max),
> POWER_SUPPLY_ATTR(charge_control_limit),
> POWER_SUPPLY_ATTR(charge_control_limit_max),
> + POWER_SUPPLY_ATTR(charge_control_start_threshold),
> + POWER_SUPPLY_ATTR(charge_control_end_threshold),
> POWER_SUPPLY_ATTR(input_current_limit),
> POWER_SUPPLY_ATTR(energy_full_design),
> POWER_SUPPLY_ATTR(energy_empty_design),
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index 2f9c201a54d1..d59205170232 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -40,11 +40,15 @@ enum {
> POWER_SUPPLY_STATUS_FULL,
> };
>
> +/* What algorithm is the charger using? */
> enum {
> POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0,
> POWER_SUPPLY_CHARGE_TYPE_NONE,
> - POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
> - POWER_SUPPLY_CHARGE_TYPE_FAST,
> + POWER_SUPPLY_CHARGE_TYPE_TRICKLE, /* slow speed */
> + POWER_SUPPLY_CHARGE_TYPE_FAST, /* fast speed */
> + POWER_SUPPLY_CHARGE_TYPE_STANDARD, /* normal speed */
> + POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE, /* dynamically adjusted speed */
> + POWER_SUPPLY_CHARGE_TYPE_CUSTOM, /* use CHARGE_CONTROL_* props */
You should probably add the info in Documentation/ABI/testing/sysfs-class-power
and add the new values in /sys/class/power_supply/<supply_name>/charge_type
propriety
> };
>
> enum {
> @@ -121,6 +125,8 @@ enum power_supply_property {
> POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
> POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
> POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, /* in percents! */
This also needs to be documented in Documentation/ABI/testing/sysfs-class-power
> POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
> POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
>
Thanks,
Enric
Hi Nick,
On 12/4/19 2:20, Nick Crews wrote:
> Add control of the charging algorithm used on Wilco devices.
> See Documentation/ABI/testing/sysfs-class-power-wilco for the
> userspace interface and other info.
>
> v3 changes:
> -Add this changelog
> -Fix commit message tags
> v2 changes:
> -Update Documentation to say KernelVersion 5.2
> -Update Documentation to explain Trickle mode better.
> -rename things from using *PCC* to *CHARGE*
> -Split up conversions between POWER_SUPPLY_PROP_CHARGE_TYPE values
> and Wilco EC codes
> -Use devm_ flavor of power_supply_register(), which simplifies things
> -Add extra error checking on property messages received from the EC
> -Fix bug in memcpy() calls in properties.c
> -Refactor fill_property_id()
> -Add valid input checks to charge_type
> -Properly convert charge_type when get()ting
>
> Signed-off-by: Nick Crews <[email protected]>
> ---
> .../ABI/testing/sysfs-class-power-wilco | 30 +++
Oh, I see the doc now :-) I think that part of this documentation should be generic.
> drivers/platform/chrome/wilco_ec/Kconfig | 9 +
> drivers/platform/chrome/wilco_ec/Makefile | 2 +
> .../platform/chrome/wilco_ec/charge_config.c | 190 ++++++++++++++++++
> drivers/platform/chrome/wilco_ec/core.c | 14 ++
> drivers/platform/chrome/wilco_ec/properties.c | 134 ++++++++++++
> drivers/platform/chrome/wilco_ec/properties.h | 68 +++++++
> include/linux/platform_data/wilco-ec.h | 2 +
> 8 files changed, 449 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-class-power-wilco
> create mode 100644 drivers/platform/chrome/wilco_ec/charge_config.c
> create mode 100644 drivers/platform/chrome/wilco_ec/properties.c
> create mode 100644 drivers/platform/chrome/wilco_ec/properties.h
>
> diff --git a/Documentation/ABI/testing/sysfs-class-power-wilco b/Documentation/ABI/testing/sysfs-class-power-wilco
> new file mode 100644
> index 000000000000..7f3b01310476
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-power-wilco
> @@ -0,0 +1,30 @@
> +What: /sys/class/power_supply/wilco_charger/charge_type
> +Date: April 2019
> +KernelVersion: 5.2
> +Description:
> + What charging algorithm to use:
> +
> + Standard: Fully charges battery at a standard rate.
> + Adaptive: Battery settings adaptively optimized based on
> + typical battery usage pattern.
> + Fast: Battery charges over a shorter period.
> + Trickle: Extends battery lifespan, intended for users who
> + primarily use their Chromebook while connected to AC.
> + Custom: A low and high threshold percentage is specified.
> + Charging begins when level drops below
> + charge_control_start_threshold, and ceases when
> + level is above charge_control_end_threshold.
> +
> +What: /sys/class/power_supply/wilco_charger/charge_control_start_threshold
> +Date: April 2019
> +KernelVersion: 5.2
> +Description:
> + Used when charge_type="Custom", as described above. Measured in
> + percentages. The valid range is [50, 95].
> +
> +What: /sys/class/power_supply/wilco_charger/charge_control_end_threshold
> +Date: April 2019
> +KernelVersion: 5.2
> +Description:
> + Used when charge_type="Custom", as described above. Measured in
> + percentages. The valid range is [55, 100].
> diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
> index e09e4cebe9b4..1c427830bd57 100644
> --- a/drivers/platform/chrome/wilco_ec/Kconfig
> +++ b/drivers/platform/chrome/wilco_ec/Kconfig
> @@ -18,3 +18,12 @@ 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_CHARGE_CNTL
> + tristate "Enable charging control"
> + depends on WILCO_EC
> + help
> + If you say Y here, you get support to control the charging
> + routines performed by the Wilco Embedded Controller.
> + Further information can be found in
> + Documentation/ABI/testing/sysfs-class-power-wilco)
> diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
> index 063e7fb4ea17..7e980f56f793 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_charging-objs := charge_config.o properties.o
> +obj-$(CONFIG_WILCO_EC_CHARGE_CNTL) += wilco_ec_charging.o
Sebastian can correct me but I think this driver should go in
drivers/power/supply/ instead of platform (wilco-ec-charger?)
> diff --git a/drivers/platform/chrome/wilco_ec/charge_config.c b/drivers/platform/chrome/wilco_ec/charge_config.c
> new file mode 100644
> index 000000000000..7c41b847396d
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec/charge_config.c
> @@ -0,0 +1,190 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Charging control driver for the Wilco EC
> + *
> + * Copyright 2019 Google LLC
> + *
> + * See Documentation/ABI/testing/sysfs-class-power-wilco for
> + * userspace interface and other info.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/wilco-ec.h>
> +#include <linux/power_supply.h>
> +
> +#include "properties.h"
> +
> +#define DRV_NAME "wilco-ec-charging"
> +
> +/* Property IDs and related EC constants */
> +#define PID_CHARGE_MODE 0x0710
> +#define PID_CHARGE_LOWER_LIMIT 0x0711
> +#define PID_CHARGE_UPPER_LIMIT 0x0712
> +
> +enum charge_mode {
> + CHARGE_MODE_STD = 1, /* Used for Standard */
> + CHARGE_MODE_EXP = 2, /* Express Charge, used for Fast */
> + CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */
> + CHARGE_MODE_AUTO = 4, /* Used for Adaptive */
> + CHARGE_MODE_CUSTOM = 5, /* Used for Custom */
> +};
> +
> +#define CHARGE_LOWER_LIMIT_MIN 50
> +#define CHARGE_LOWER_LIMIT_MAX 95
> +#define CHARGE_UPPER_LIMIT_MIN 55
> +#define CHARGE_UPPER_LIMIT_MAX 100
> +
> +/* Convert from POWER_SUPPLY_PROP_CHARGE_TYPE value to the EC's charge mode */
> +static enum charge_mode psp_val_to_charge_mode(int psp_val)
> +{
> + switch (psp_val) {
> + case (POWER_SUPPLY_CHARGE_TYPE_TRICKLE):
> + return CHARGE_MODE_AC;
> + case (POWER_SUPPLY_CHARGE_TYPE_FAST):
> + return CHARGE_MODE_EXP;
> + case (POWER_SUPPLY_CHARGE_TYPE_STANDARD):
> + return CHARGE_MODE_STD;
> + case (POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE):
> + return CHARGE_MODE_AUTO;
> + case (POWER_SUPPLY_CHARGE_TYPE_CUSTOM):
> + return CHARGE_MODE_CUSTOM;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +/* Convert from EC's charge mode to POWER_SUPPLY_PROP_CHARGE_TYPE value */
> +static int charge_mode_to_psp_val(enum charge_mode mode)
> +{
> + switch (mode) {
> + case (CHARGE_MODE_AC):
> + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
> + case (CHARGE_MODE_EXP):
> + return POWER_SUPPLY_CHARGE_TYPE_FAST;
> + case (CHARGE_MODE_STD):
> + return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
> + case (CHARGE_MODE_AUTO):
> + return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
> + case (CHARGE_MODE_CUSTOM):
> + return POWER_SUPPLY_CHARGE_TYPE_CUSTOM;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static enum power_supply_property wilco_charge_props[] = {
> + POWER_SUPPLY_PROP_CHARGE_TYPE,
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
> + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
> +};
> +
> +static int wilco_charge_get_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
> + u32 property_id;
> + int ret;
> + u8 raw;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_CHARGE_TYPE:
> + property_id = PID_CHARGE_MODE;
> + break;
> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
> + property_id = PID_CHARGE_LOWER_LIMIT;
> + break;
> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
> + property_id = PID_CHARGE_UPPER_LIMIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + ret = wilco_ec_get_byte_property(ec, property_id, &raw);
> + if (ret < 0)
> + return ret;
> + if (property_id == PID_CHARGE_MODE) {
> + ret = charge_mode_to_psp_val(raw);
> + if (ret == -EINVAL)
> + return -EBADMSG;
> + raw = ret;
> + }
> + val->intval = raw;
> +
> + return 0;
> +}
> +
> +static int wilco_charge_set_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
> + enum charge_mode mode;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_CHARGE_TYPE:
> + mode = psp_val_to_charge_mode(val->intval);
> + if (mode == -EINVAL)
> + return -EINVAL;
> + return wilco_ec_set_byte_property(ec, PID_CHARGE_MODE, mode);
> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
> + if (val->intval < CHARGE_LOWER_LIMIT_MIN ||
> + val->intval > CHARGE_LOWER_LIMIT_MAX)
> + return -EINVAL;
> + return wilco_ec_set_byte_property(ec, PID_CHARGE_LOWER_LIMIT,
> + val->intval);
> + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
> + if (val->intval < CHARGE_UPPER_LIMIT_MIN ||
> + val->intval > CHARGE_UPPER_LIMIT_MAX)
> + return -EINVAL;
> + return wilco_ec_set_byte_property(ec, PID_CHARGE_UPPER_LIMIT,
> + val->intval);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int wilco_charge_property_is_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return 1;
> +}
> +
> +static const struct power_supply_desc wilco_ps_desc = {
> + .properties = wilco_charge_props,
> + .num_properties = ARRAY_SIZE(wilco_charge_props),
> + .get_property = wilco_charge_get_property,
> + .set_property = wilco_charge_set_property,
> + .property_is_writeable = wilco_charge_property_is_writeable,
> + .name = "wilco-charger",
> + .type = POWER_SUPPLY_TYPE_MAINS,
> +};
> +
> +static int wilco_charge_probe(struct platform_device *pdev)
> +{
> + struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
> + struct power_supply_config psy_cfg = {};
> + struct power_supply *psy;
> +
> + psy_cfg.drv_data = ec;
> + psy = devm_power_supply_register(&pdev->dev, &wilco_ps_desc, &psy_cfg);
> + if (IS_ERR(psy))
> + return PTR_ERR(psy);
> +
> + return 0;
> +}
> +
> +static struct platform_driver wilco_charge_driver = {
> + .probe = wilco_charge_probe,
> + .driver = {
> + .name = DRV_NAME,
> + }
> +};
> +module_platform_driver(wilco_charge_driver);
> +
> +MODULE_ALIAS("platform:" DRV_NAME);
> +MODULE_AUTHOR("Nick Crews <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Wilco EC charge control driver");
> diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
> index 05e1e2be1c91..b6f3b061f37b 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 to be found by charging config driver. */
> + ec->charging_pdev = platform_device_register_data(dev,
> + "wilco-ec-charging",
> + PLATFORM_DEVID_AUTO,
> + NULL, 0);
> + if (IS_ERR(ec->charging_pdev)) {
> + dev_err(dev, "Failed to create charging platform device\n");
> + ret = PTR_ERR(ec->charging_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->charging_pdev);
> platform_device_unregister(ec->rtc_pdev);
> if (ec->debugfs_pdev)
> platform_device_unregister(ec->debugfs_pdev);
> diff --git a/drivers/platform/chrome/wilco_ec/properties.c b/drivers/platform/chrome/wilco_ec/properties.c
> new file mode 100644
> index 000000000000..d74eb1208afd
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec/properties.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 Google LLC
> + */
> +
> +#include <linux/string.h>
> +#include "properties.h"
> +
> +struct ec_property_request {
> + u8 op;
> + u8 property_id[4];
> + u8 length;
> + u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
> +} __packed;
> +
> +struct ec_property_response {
> + u8 reserved[2];
> + u8 op;
> + u8 property_id[4];
> + u8 length;
> + u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
> +} __packed;
> +
> +static inline void fill_request_property_id(struct ec_property_request *rq,
> + u32 property_id)
> +{
> + rq->property_id[0] = property_id & 0xff;
> + rq->property_id[1] = (property_id >> 8) & 0xff;
> + rq->property_id[2] = (property_id >> 16) & 0xff;
> + rq->property_id[3] = (property_id >> 24) & 0xff;
> +}
> +
> +static int send_property_msg(struct wilco_ec_device *ec,
> + struct ec_property_request *rq,
> + struct ec_property_response *rs)
> +{
> + struct wilco_ec_message ec_msg;
> + int ret;
> +
> + memset(&ec_msg, 0, sizeof(ec_msg));
> + ec_msg.type = WILCO_EC_MSG_PROPERTY;
> + ec_msg.request_data = rq;
> + ec_msg.request_size = sizeof(*rq);
> + ec_msg.response_data = rs;
> + ec_msg.response_size = sizeof(*rs);
> + ret = wilco_ec_mailbox(ec, &ec_msg);
> + if (ret < 0)
> + return ret;
> +
> + if (rs->op != rq->op)
> + return -EBADMSG;
> + if (memcmp(rq->property_id, rs->property_id, sizeof(rs->property_id)))
> + return -EBADMSG;
> + if (rs->length > sizeof(rs->data))
> + return -EMSGSIZE;
> +
> + return 0;
> +}
> +
> +int wilco_ec_get_property(struct wilco_ec_device *ec,
> + struct ec_property_get_msg *prop_msg)
> +{
> + struct ec_property_request rq;
> + struct ec_property_response rs;
> + int ret;
> +
> + memset(&rq, 0, sizeof(rq));
> + rq.op = OP_GET;
> + fill_request_property_id(&rq, prop_msg->property_id);
> +
> + ret = send_property_msg(ec, &rq, &rs);
> + if (ret < 0)
> + return ret;
> +
> + prop_msg->length = rs.length;
> + memcpy(prop_msg->data, rs.data, rs.length);
> +
> + return 0;
> +}
> +
> +int wilco_ec_set_property(struct wilco_ec_device *ec,
> + struct ec_property_set_msg *prop_msg)
> +{
> + struct ec_property_request rq;
> + struct ec_property_response rs;
> + int ret;
> +
> + memset(&rq, 0, sizeof(rq));
> + rq.op = prop_msg->op;
> + fill_request_property_id(&rq, prop_msg->property_id);
> + rq.length = prop_msg->length;
> + memcpy(rq.data, prop_msg->data, prop_msg->length);
> +
> + ret = send_property_msg(ec, &rq, &rs);
> + if (ret < 0)
> + return ret;
> +
> + if (rs.length != prop_msg->length)
> + return -EBADMSG;
> +
> + return 0;
> +}
> +
> +int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id,
> + u8 *val)
> +{
> + struct ec_property_get_msg msg;
> + int ret;
> +
> + msg.property_id = property_id;
> + ret = wilco_ec_get_property(ec, &msg);
> + if (ret)
> + return ret;
> +
> + if (msg.length != 1)
> + return -EBADMSG;
> +
> + *val = msg.data[0];
> +
> + return 0;
> +}
> +
> +int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id,
> + u8 val)
> +{
> + struct ec_property_set_msg msg;
> +
> + msg.property_id = property_id;
> + msg.op = OP_SET;
> + msg.data[0] = val;
> + msg.length = 1;
> +
> + return wilco_ec_set_property(ec, &msg);
> +}
> diff --git a/drivers/platform/chrome/wilco_ec/properties.h b/drivers/platform/chrome/wilco_ec/properties.h
> new file mode 100644
> index 000000000000..da0bb3b869af
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec/properties.h
> @@ -0,0 +1,68 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Helper library for property access on the Wilco EC.
> + *
> + * Copyright 2019 Google LLC
> + *
> + * A Property is typically a data item that is stored to NVRAM
> + * by the EC. Each of these data items has an index associated
> + * with it known as the Property ID (PID). Properties may have
> + * variable lengths, up to a max of WILCO_EC_PROPERTY_MAX_SIZE
> + * bytes. Properties can be simple integers, or they may be more
> + * complex binary data.
> + */
> +
> +#include <linux/platform_data/wilco-ec.h>
> +
> +#define WILCO_EC_PROPERTY_MAX_SIZE 4
> +
> +/*
> + * Properties are accessed with an subcommand, or "op". OP_GET
> + * requests the property from the EC. OP_SET and OP_SYNC do the
> + * exact same thing from our perspective: save a property. Only
> + * one of them works for a given property, so each property uses
> + * either OP_GET and OP_SET, or OP_GET and OP_SYNC.
> + */
> +enum get_set_sync_op {
> + OP_GET = 0,
> + OP_SET = 1,
> + OP_SYNC = 4
> +};
> +
> +/**
> + * struct ec_property_get_msg - Message to retrieve a property.
> + * @property_id: PID of property to retrieve.
> + * @length: number of bytes received, set by wilco_ec_get_property().
> + * @data: actual property data, set by wilco_ec_get_property().
> + */
> +struct ec_property_get_msg {
> + u32 property_id;
> + int length;
> + u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
> +};
> +
> +/**
> + * struct ec_property_set_msg - Message to save a property.
> + * @op: Which subcommand to use, either OP_SET or OP_SYNC
> + * @property_id: PID of property to save.
> + * @length: number of bytes to save, must not exceed WILCO_EC_PROPERTY_MAX_SIZE.
> + * @data: actual property data.
> + */
> +struct ec_property_set_msg {
> + enum get_set_sync_op op;
> + u32 property_id;
> + int length;
> + u8 data[WILCO_EC_PROPERTY_MAX_SIZE];
> +};
> +
> +/* Both of these will return 0 on success, negative error code on failure */
> +int wilco_ec_get_property(struct wilco_ec_device *ec,
> + struct ec_property_get_msg *prop_msg);
> +int wilco_ec_set_property(struct wilco_ec_device *ec,
> + struct ec_property_set_msg *prop_msg);
> +
> +/* Both of these will return 0 on success, negative error code on failure */
> +int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id,
> + u8 *val);
> +int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id,
> + u8 val);
> diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h
> index 1ff224793c99..4e7dce897500 100644
> --- a/include/linux/platform_data/wilco-ec.h
> +++ b/include/linux/platform_data/wilco-ec.h
> @@ -32,6 +32,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.
> + * @charging_pdev: Child platform_device used by the charging config sub-driver.
> */
> struct wilco_ec_device {
> struct device *dev;
> @@ -43,6 +44,7 @@ struct wilco_ec_device {
> size_t data_size;
> struct platform_device *debugfs_pdev;
> struct platform_device *rtc_pdev;
> + struct platform_device *charging_pdev;
> };
>
> /**
>