linux/drivers/power/supply/wilco-charger.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Charging control driver for the Wilco EC
   4 *
   5 * Copyright 2019 Google LLC
   6 *
   7 * See Documentation/ABI/testing/sysfs-class-power and
   8 * Documentation/ABI/testing/sysfs-class-power-wilco for userspace interface
   9 * and other info.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/platform_data/wilco-ec.h>
  15#include <linux/power_supply.h>
  16
  17#define DRV_NAME "wilco-charger"
  18
  19/* Property IDs and related EC constants */
  20#define PID_CHARGE_MODE         0x0710
  21#define PID_CHARGE_LOWER_LIMIT  0x0711
  22#define PID_CHARGE_UPPER_LIMIT  0x0712
  23
  24enum charge_mode {
  25        CHARGE_MODE_STD = 1,    /* Used for Standard */
  26        CHARGE_MODE_EXP = 2,    /* Express Charge, used for Fast */
  27        CHARGE_MODE_AC = 3,     /* Mostly AC use, used for Trickle */
  28        CHARGE_MODE_AUTO = 4,   /* Used for Adaptive */
  29        CHARGE_MODE_CUSTOM = 5, /* Used for Custom */
  30        CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */
  31};
  32
  33#define CHARGE_LOWER_LIMIT_MIN  50
  34#define CHARGE_LOWER_LIMIT_MAX  95
  35#define CHARGE_UPPER_LIMIT_MIN  55
  36#define CHARGE_UPPER_LIMIT_MAX  100
  37
  38/* Convert from POWER_SUPPLY_PROP_CHARGE_TYPE value to the EC's charge mode */
  39static int psp_val_to_charge_mode(int psp_val)
  40{
  41        switch (psp_val) {
  42        case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
  43                return CHARGE_MODE_AC;
  44        case POWER_SUPPLY_CHARGE_TYPE_FAST:
  45                return CHARGE_MODE_EXP;
  46        case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
  47                return CHARGE_MODE_STD;
  48        case POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE:
  49                return CHARGE_MODE_AUTO;
  50        case POWER_SUPPLY_CHARGE_TYPE_CUSTOM:
  51                return CHARGE_MODE_CUSTOM;
  52        case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
  53                return CHARGE_MODE_LONGLIFE;
  54        default:
  55                return -EINVAL;
  56        }
  57}
  58
  59/* Convert from EC's charge mode to POWER_SUPPLY_PROP_CHARGE_TYPE value */
  60static int charge_mode_to_psp_val(enum charge_mode mode)
  61{
  62        switch (mode) {
  63        case CHARGE_MODE_AC:
  64                return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
  65        case CHARGE_MODE_EXP:
  66                return POWER_SUPPLY_CHARGE_TYPE_FAST;
  67        case CHARGE_MODE_STD:
  68                return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
  69        case CHARGE_MODE_AUTO:
  70                return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
  71        case CHARGE_MODE_CUSTOM:
  72                return POWER_SUPPLY_CHARGE_TYPE_CUSTOM;
  73        case CHARGE_MODE_LONGLIFE:
  74                return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
  75        default:
  76                return -EINVAL;
  77        }
  78}
  79
  80static enum power_supply_property wilco_charge_props[] = {
  81        POWER_SUPPLY_PROP_CHARGE_TYPE,
  82        POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
  83        POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
  84};
  85
  86static int wilco_charge_get_property(struct power_supply *psy,
  87                                     enum power_supply_property psp,
  88                                     union power_supply_propval *val)
  89{
  90        struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
  91        u32 property_id;
  92        int ret;
  93        u8 raw;
  94
  95        switch (psp) {
  96        case POWER_SUPPLY_PROP_CHARGE_TYPE:
  97                property_id = PID_CHARGE_MODE;
  98                break;
  99        case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
 100                property_id = PID_CHARGE_LOWER_LIMIT;
 101                break;
 102        case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
 103                property_id = PID_CHARGE_UPPER_LIMIT;
 104                break;
 105        default:
 106                return -EINVAL;
 107        }
 108
 109        ret = wilco_ec_get_byte_property(ec, property_id, &raw);
 110        if (ret < 0)
 111                return ret;
 112        if (property_id == PID_CHARGE_MODE) {
 113                ret = charge_mode_to_psp_val(raw);
 114                if (ret < 0)
 115                        return -EBADMSG;
 116                raw = ret;
 117        }
 118        val->intval = raw;
 119
 120        return 0;
 121}
 122
 123static int wilco_charge_set_property(struct power_supply *psy,
 124                                     enum power_supply_property psp,
 125                                     const union power_supply_propval *val)
 126{
 127        struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
 128        int mode;
 129
 130        switch (psp) {
 131        case POWER_SUPPLY_PROP_CHARGE_TYPE:
 132                mode = psp_val_to_charge_mode(val->intval);
 133                if (mode < 0)
 134                        return -EINVAL;
 135                return wilco_ec_set_byte_property(ec, PID_CHARGE_MODE, mode);
 136        case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
 137                if (val->intval < CHARGE_LOWER_LIMIT_MIN ||
 138                    val->intval > CHARGE_LOWER_LIMIT_MAX)
 139                        return -EINVAL;
 140                return wilco_ec_set_byte_property(ec, PID_CHARGE_LOWER_LIMIT,
 141                                                  val->intval);
 142        case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
 143                if (val->intval < CHARGE_UPPER_LIMIT_MIN ||
 144                    val->intval > CHARGE_UPPER_LIMIT_MAX)
 145                        return -EINVAL;
 146                return wilco_ec_set_byte_property(ec, PID_CHARGE_UPPER_LIMIT,
 147                                                  val->intval);
 148        default:
 149                return -EINVAL;
 150        }
 151}
 152
 153static int wilco_charge_property_is_writeable(struct power_supply *psy,
 154                                              enum power_supply_property psp)
 155{
 156        return 1;
 157}
 158
 159static const struct power_supply_desc wilco_ps_desc = {
 160        .properties             = wilco_charge_props,
 161        .num_properties         = ARRAY_SIZE(wilco_charge_props),
 162        .get_property           = wilco_charge_get_property,
 163        .set_property           = wilco_charge_set_property,
 164        .property_is_writeable  = wilco_charge_property_is_writeable,
 165        .name                   = DRV_NAME,
 166        .type                   = POWER_SUPPLY_TYPE_MAINS,
 167};
 168
 169static int wilco_charge_probe(struct platform_device *pdev)
 170{
 171        struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
 172        struct power_supply_config psy_cfg = {};
 173        struct power_supply *psy;
 174
 175        psy_cfg.drv_data = ec;
 176        psy = devm_power_supply_register(&pdev->dev, &wilco_ps_desc, &psy_cfg);
 177
 178        return PTR_ERR_OR_ZERO(psy);
 179}
 180
 181static struct platform_driver wilco_charge_driver = {
 182        .probe  = wilco_charge_probe,
 183        .driver = {
 184                .name = DRV_NAME,
 185        }
 186};
 187module_platform_driver(wilco_charge_driver);
 188
 189MODULE_ALIAS("platform:" DRV_NAME);
 190MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
 191MODULE_LICENSE("GPL v2");
 192MODULE_DESCRIPTION("Wilco EC charge control driver");
 193