123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Charging control driver for the Wilco EC
- *
- * Copyright 2019 Google LLC
- *
- * See Documentation/ABI/testing/sysfs-class-power and
- * 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>
- #define DRV_NAME "wilco-charger"
- /* 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 */
- CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */
- };
- #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 int 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;
- case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
- return CHARGE_MODE_LONGLIFE;
- 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;
- case CHARGE_MODE_LONGLIFE:
- return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
- 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 < 0)
- 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);
- int mode;
- switch (psp) {
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- mode = psp_val_to_charge_mode(val->intval);
- if (mode < 0)
- 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 = DRV_NAME,
- .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);
- return PTR_ERR_OR_ZERO(psy);
- }
- 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");
|