forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.3 KiB
192 lines
5.3 KiB
// 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");
|
|
|