power supply and reset changes for the v5.3 series
Core: * Add HWMON compat layer * New properties - input power limit - input voltage limit Drivers: * qcom-pon: add gen2 support * New driver for storing reboot move in NVMEM * New driver for Wilco EC charger configuration * simplify getting the adapter of a client -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAl0s0xgACgkQ2O7X88g7 +pqxFQ/9GmfgHpzZ+qQmpBR5zyw1+yrhls3BXYEgHtGM+3YZ6n1sF8Yl1eUYpviC ldvN3vHXaxRlG5eDBwMl3ScWZnaxMpZssigO3lL4o+kYng0c0xqaPZZYxA9oJNgn 0ertZrYcQZWmT82aRnjt2/p+8n+Hld6bv89PodWdLvsDvId1qQPXu5ILV0JL/QNK FMQepuaiRu9VXlyPCuWYwoOmKruZjLF7SOyis+I4e55U7lHeyCOySH/tZTTFgd+n hUpWm4ekc7YCAJVVJUQcdBtfNvQm1KtGkLSnSockH/636kP2fh5ESj76z8i5I6/6 yl7OrkCyhespqS9hGCKCPU95s8MQe8HurlGR8aIWHLJJMiv1hIVOq7n9Uj+mmdRS OkKQHo/RUxXn5ioCUF3F3NcB94/95f0AWrx3RXjeXd2kYlUmVKCHyaGjPT9WfSOe MUcLZwM+GsG+3SWBhPGqjuIhIGfBBuQk+mcYLPLP/j3emNeLByYEtEDhvoQbEooU TCyJGR+FGIAyjXcW/uZzxx8MiZPybSXo7a4j837Cx6sRNwZJ4V9Ve/7XdUy7DKD0 kOBH/ndJhoKJQkup+HEGmv/8os4K8gyW/kaiu718mS0oLDfQGDy0C0Y8BNoJnw4k /jo/1q0KY+8Hd6bxqbommA2ORAw7XsDZB7eWWC4gDqMXVcF1S6k= =fmGg -----END PGP SIGNATURE----- Merge tag 'for-v5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: "Core: - add HWMON compat layer - new properties: - input power limit - input voltage limit Drivers: - qcom-pon: add gen2 support - new driver for storing reboot move in NVMEM - new driver for Wilco EC charger configuration - simplify getting the adapter of a client" * tag 'for-v5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: power: reset: nvmem-reboot-mode: add CONFIG_OF dependency power_supply: wilco_ec: Add charging config driver power: supply: cros: allow to set input voltage and current limit power: supply: add input power and voltage limit properties power: supply: fix semicolon.cocci warnings power: reset: nvmem-reboot-mode: use NVMEM as reboot mode write interface dt-bindings: power: reset: add document for NVMEM based reboot-mode reset: qcom-pon: Add support for gen2 pon dt-bindings: power: reset: qcom: Add qcom,pm8998-pon compatibility line power: supply: Add HWMON compatibility layer power: supply: sbs-manager: simplify getting the adapter of a client power: supply: rt9455_charger: simplify getting the adapter of a client power: supply: rt5033_battery: simplify getting the adapter of a client power: supply: max17042_battery: simplify getting the adapter of a client power: supply: max17040_battery: simplify getting the adapter of a client power: supply: max14656_charger_detector: simplify getting the adapter of a client power: supply: bq25890_charger: simplify getting the adapter of a client power: supply: bq24257_charger: simplify getting the adapter of a client power: supply: bq24190_charger: simplify getting the adapter of a client
This commit is contained in:
commit
5fe7b600a1
27 changed files with 908 additions and 13 deletions
|
@ -376,10 +376,42 @@ Description:
|
|||
supply. Normally this is configured based on the type of
|
||||
connection made (e.g. A configured SDP should output a maximum
|
||||
of 500mA so the input current limit is set to the same value).
|
||||
Use preferably input_power_limit, and for problems that can be
|
||||
solved using power limit use input_current_limit.
|
||||
|
||||
Access: Read, Write
|
||||
Valid values: Represented in microamps
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/input_voltage_limit
|
||||
Date: May 2019
|
||||
Contact: linux-pm@vger.kernel.org
|
||||
Description:
|
||||
This entry configures the incoming VBUS voltage limit currently
|
||||
set in the supply. Normally this is configured based on
|
||||
system-level knowledge or user input (e.g. This is part of the
|
||||
Pixel C's thermal management strategy to effectively limit the
|
||||
input power to 5V when the screen is on to meet Google's skin
|
||||
temperature targets). Note that this feature should not be
|
||||
used for safety critical things.
|
||||
Use preferably input_power_limit, and for problems that can be
|
||||
solved using power limit use input_voltage_limit.
|
||||
|
||||
Access: Read, Write
|
||||
Valid values: Represented in microvolts
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/input_power_limit
|
||||
Date: May 2019
|
||||
Contact: linux-pm@vger.kernel.org
|
||||
Description:
|
||||
This entry configures the incoming power limit currently set
|
||||
in the supply. Normally this is configured based on
|
||||
system-level knowledge or user input. Use preferably this
|
||||
feature to limit the incoming power and use current/voltage
|
||||
limit only for problems that can be solved using power limit.
|
||||
|
||||
Access: Read, Write
|
||||
Valid values: Represented in microwatts
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/online,
|
||||
Date: May 2007
|
||||
Contact: linux-pm@vger.kernel.org
|
||||
|
|
30
Documentation/ABI/testing/sysfs-class-power-wilco
Normal file
30
Documentation/ABI/testing/sysfs-class-power-wilco
Normal file
|
@ -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].
|
|
@ -0,0 +1,26 @@
|
|||
NVMEM reboot mode driver
|
||||
|
||||
This driver gets reboot mode magic value from reboot-mode driver
|
||||
and stores it in a NVMEM cell named "reboot-mode". Then the bootloader
|
||||
can read it and take different action according to the magic
|
||||
value stored.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nvmem-reboot-mode".
|
||||
- nvmem-cells: A phandle to the reboot mode provided by a nvmem device.
|
||||
- nvmem-cell-names: Should be "reboot-mode".
|
||||
|
||||
The rest of the properties should follow the generic reboot-mode description
|
||||
found in reboot-mode.txt
|
||||
|
||||
Example:
|
||||
reboot-mode {
|
||||
compatible = "nvmem-reboot-mode";
|
||||
nvmem-cells = <&reboot_mode>;
|
||||
nvmem-cell-names = "reboot-mode";
|
||||
|
||||
mode-normal = <0xAAAA5501>;
|
||||
mode-bootloader = <0xBBBB5500>;
|
||||
mode-recovery = <0xCCCC5502>;
|
||||
mode-test = <0xDDDD5503>;
|
||||
};
|
|
@ -9,6 +9,7 @@ Required Properties:
|
|||
-compatible: Must be one of:
|
||||
"qcom,pm8916-pon"
|
||||
"qcom,pms405-pon"
|
||||
"qcom,pm8998-pon"
|
||||
|
||||
-reg: Specifies the physical address of the pon register
|
||||
|
||||
|
|
|
@ -165,6 +165,12 @@ CONSTANT_CHARGE_VOLTAGE_MAX
|
|||
INPUT_CURRENT_LIMIT
|
||||
input current limit programmed by charger. Indicates
|
||||
the current drawn from a charging source.
|
||||
INPUT_VOLTAGE_LIMIT
|
||||
input voltage limit programmed by charger. Indicates
|
||||
the voltage limit from a charging source.
|
||||
INPUT_POWER_LIMIT
|
||||
input power limit programmed by charger. Indicates
|
||||
the power limit from a charging source.
|
||||
|
||||
CHARGE_CONTROL_LIMIT
|
||||
current charge control limit setting
|
||||
|
|
|
@ -246,5 +246,15 @@ config POWER_RESET_SC27XX
|
|||
PMICs includes the SC2720, SC2721, SC2723, SC2730
|
||||
and SC2731 chips.
|
||||
|
||||
config NVMEM_REBOOT_MODE
|
||||
tristate "Generic NVMEM reboot mode driver"
|
||||
depends on OF
|
||||
select REBOOT_MODE
|
||||
help
|
||||
Say y here will enable reboot mode driver. This will
|
||||
get reboot mode arguments and store it in a NVMEM cell,
|
||||
then the bootloader can read it and take different
|
||||
action according to the mode.
|
||||
|
||||
endif
|
||||
|
||||
|
|
|
@ -29,3 +29,4 @@ obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
|
|||
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
|
||||
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
|
||||
obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
|
||||
obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o
|
||||
|
|
76
drivers/power/reset/nvmem-reboot-mode.c
Normal file
76
drivers/power/reset/nvmem-reboot-mode.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) Vaisala Oyj. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot-mode.h>
|
||||
|
||||
struct nvmem_reboot_mode {
|
||||
struct reboot_mode_driver reboot;
|
||||
struct nvmem_cell *cell;
|
||||
};
|
||||
|
||||
static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot,
|
||||
unsigned int magic)
|
||||
{
|
||||
int ret;
|
||||
struct nvmem_reboot_mode *nvmem_rbm;
|
||||
|
||||
nvmem_rbm = container_of(reboot, struct nvmem_reboot_mode, reboot);
|
||||
|
||||
ret = nvmem_cell_write(nvmem_rbm->cell, &magic, sizeof(magic));
|
||||
if (ret < 0)
|
||||
dev_err(reboot->dev, "update reboot mode bits failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nvmem_reboot_mode_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct nvmem_reboot_mode *nvmem_rbm;
|
||||
|
||||
nvmem_rbm = devm_kzalloc(&pdev->dev, sizeof(*nvmem_rbm), GFP_KERNEL);
|
||||
if (!nvmem_rbm)
|
||||
return -ENOMEM;
|
||||
|
||||
nvmem_rbm->reboot.dev = &pdev->dev;
|
||||
nvmem_rbm->reboot.write = nvmem_reboot_mode_write;
|
||||
|
||||
nvmem_rbm->cell = devm_nvmem_cell_get(&pdev->dev, "reboot-mode");
|
||||
if (IS_ERR(nvmem_rbm->cell)) {
|
||||
dev_err(&pdev->dev, "failed to get the nvmem cell reboot-mode\n");
|
||||
return PTR_ERR(nvmem_rbm->cell);
|
||||
}
|
||||
|
||||
ret = devm_reboot_mode_register(&pdev->dev, &nvmem_rbm->reboot);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "can't register reboot mode\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id nvmem_reboot_mode_of_match[] = {
|
||||
{ .compatible = "nvmem-reboot-mode" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nvmem_reboot_mode_of_match);
|
||||
|
||||
static struct platform_driver nvmem_reboot_mode_driver = {
|
||||
.probe = nvmem_reboot_mode_probe,
|
||||
.driver = {
|
||||
.name = "nvmem-reboot-mode",
|
||||
.of_match_table = nvmem_reboot_mode_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(nvmem_reboot_mode_driver);
|
||||
|
||||
MODULE_AUTHOR("Nandor Han <nandor.han@vaisala.com>");
|
||||
MODULE_DESCRIPTION("NVMEM reboot mode driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -14,11 +14,15 @@
|
|||
|
||||
#define PON_SOFT_RB_SPARE 0x8f
|
||||
|
||||
#define GEN1_REASON_SHIFT 2
|
||||
#define GEN2_REASON_SHIFT 1
|
||||
|
||||
struct pm8916_pon {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
u32 baseaddr;
|
||||
struct reboot_mode_driver reboot_mode;
|
||||
long reason_shift;
|
||||
};
|
||||
|
||||
static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot,
|
||||
|
@ -30,7 +34,7 @@ static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot,
|
|||
|
||||
ret = regmap_update_bits(pon->regmap,
|
||||
pon->baseaddr + PON_SOFT_RB_SPARE,
|
||||
0xfc, magic << 2);
|
||||
0xfc, magic << pon->reason_shift);
|
||||
if (ret < 0)
|
||||
dev_err(pon->dev, "update reboot mode bits failed\n");
|
||||
|
||||
|
@ -60,6 +64,7 @@ static int pm8916_pon_probe(struct platform_device *pdev)
|
|||
return error;
|
||||
|
||||
pon->reboot_mode.dev = &pdev->dev;
|
||||
pon->reason_shift = (long)of_device_get_match_data(&pdev->dev);
|
||||
pon->reboot_mode.write = pm8916_reboot_mode_write;
|
||||
error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode);
|
||||
if (error) {
|
||||
|
@ -73,8 +78,9 @@ static int pm8916_pon_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id pm8916_pon_id_table[] = {
|
||||
{ .compatible = "qcom,pm8916-pon" },
|
||||
{ .compatible = "qcom,pms405-pon" },
|
||||
{ .compatible = "qcom,pm8916-pon", .data = (void *)GEN1_REASON_SHIFT },
|
||||
{ .compatible = "qcom,pms405-pon", .data = (void *)GEN1_REASON_SHIFT },
|
||||
{ .compatible = "qcom,pm8998-pon", .data = (void *)GEN2_REASON_SHIFT },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm8916_pon_id_table);
|
||||
|
|
|
@ -15,6 +15,20 @@ config POWER_SUPPLY_DEBUG
|
|||
Say Y here to enable debugging messages for power supply class
|
||||
and drivers.
|
||||
|
||||
config POWER_SUPPLY_HWMON
|
||||
bool
|
||||
prompt "Expose power supply sensors as hwmon device"
|
||||
depends on HWMON=y || HWMON=POWER_SUPPLY
|
||||
default y
|
||||
help
|
||||
This options enables API that allows sensors found on a
|
||||
power supply device (current, voltage, temperature) to be
|
||||
exposed as a hwmon device.
|
||||
|
||||
Say 'Y' here if you want power supplies to
|
||||
have hwmon sysfs interface too.
|
||||
|
||||
|
||||
config PDA_POWER
|
||||
tristate "Generic PDA/phone power driver"
|
||||
depends on !S390
|
||||
|
@ -698,4 +712,13 @@ config CHARGER_BD70528
|
|||
information and altering charger configurations from charger
|
||||
block of the ROHM BD70528 Power Management IC.
|
||||
|
||||
config CHARGER_WILCO
|
||||
tristate "Wilco EC based charger for ChromeOS"
|
||||
depends on WILCO_EC
|
||||
help
|
||||
Say Y here to enable control of the charging routines performed
|
||||
by the Embedded Controller on the Chromebook named Wilco. Further
|
||||
information can be found in
|
||||
Documentation/ABI/testing/sysfs-class-power-wilco
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
|
|
@ -6,6 +6,7 @@ power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
|
|||
power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
|
||||
|
||||
obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
|
||||
obj-$(CONFIG_POWER_SUPPLY_HWMON) += power_supply_hwmon.o
|
||||
obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
|
||||
|
||||
obj-$(CONFIG_PDA_POWER) += pda_power.o
|
||||
|
@ -91,3 +92,4 @@ obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
|
|||
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
|
||||
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
|
||||
obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o
|
||||
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o
|
||||
|
|
|
@ -1697,7 +1697,7 @@ static int bq24190_get_config(struct bq24190_dev_info *bdi)
|
|||
static int bq24190_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct power_supply_config charger_cfg = {}, battery_cfg = {};
|
||||
struct bq24190_dev_info *bdi;
|
||||
|
|
|
@ -950,7 +950,7 @@ static int bq24257_fw_probe(struct bq24257_device *bq)
|
|||
static int bq24257_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
struct bq24257_device *bq;
|
||||
|
|
|
@ -817,7 +817,7 @@ static int bq25890_fw_probe(struct bq25890_device *bq)
|
|||
static int bq25890_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct bq25890_device *bq;
|
||||
int ret;
|
||||
|
|
|
@ -53,6 +53,8 @@ struct charger_data {
|
|||
};
|
||||
|
||||
static enum power_supply_property cros_usbpd_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
|
@ -80,6 +82,10 @@ static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = {
|
|||
POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID
|
||||
};
|
||||
|
||||
/* Input voltage/current limit in mV/mA. Default to none. */
|
||||
static u16 input_voltage_limit = EC_POWER_LIMIT_NONE;
|
||||
static u16 input_current_limit = EC_POWER_LIMIT_NONE;
|
||||
|
||||
static bool cros_usbpd_charger_port_is_dedicated(struct port_data *port)
|
||||
{
|
||||
return port->port_number >= port->charger->num_usbpd_ports;
|
||||
|
@ -324,6 +330,26 @@ static int cros_usbpd_charger_get_port_status(struct port_data *port,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int cros_usbpd_charger_set_ext_power_limit(struct charger_data *charger,
|
||||
u16 current_lim,
|
||||
u16 voltage_lim)
|
||||
{
|
||||
struct ec_params_external_power_limit_v1 req;
|
||||
int ret;
|
||||
|
||||
req.current_lim = current_lim;
|
||||
req.voltage_lim = voltage_lim;
|
||||
|
||||
ret = cros_usbpd_charger_ec_command(charger, 0,
|
||||
EC_CMD_EXTERNAL_POWER_LIMIT,
|
||||
&req, sizeof(req), NULL, 0);
|
||||
if (ret < 0)
|
||||
dev_err(charger->dev,
|
||||
"Unable to set the 'External Power Limit': %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cros_usbpd_charger_power_changed(struct power_supply *psy)
|
||||
{
|
||||
struct port_data *port = power_supply_get_drvdata(psy);
|
||||
|
@ -396,6 +422,18 @@ static int cros_usbpd_charger_get_prop(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
val->intval = port->psy_usb_type;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
if (input_current_limit == EC_POWER_LIMIT_NONE)
|
||||
val->intval = -1;
|
||||
else
|
||||
val->intval = input_current_limit * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
if (input_voltage_limit == EC_POWER_LIMIT_NONE)
|
||||
val->intval = -1;
|
||||
else
|
||||
val->intval = input_voltage_limit * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = port->model_name;
|
||||
break;
|
||||
|
@ -409,6 +447,81 @@ static int cros_usbpd_charger_get_prop(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cros_usbpd_charger_set_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct port_data *port = power_supply_get_drvdata(psy);
|
||||
struct charger_data *charger = port->charger;
|
||||
struct device *dev = charger->dev;
|
||||
u16 intval;
|
||||
int ret;
|
||||
|
||||
/* U16_MAX in mV/mA is the maximum supported value */
|
||||
if (val->intval >= U16_MAX * 1000)
|
||||
return -EINVAL;
|
||||
/* A negative number is used to clear the limit */
|
||||
if (val->intval < 0)
|
||||
intval = EC_POWER_LIMIT_NONE;
|
||||
else /* Convert from uA/uV to mA/mV */
|
||||
intval = val->intval / 1000;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = cros_usbpd_charger_set_ext_power_limit(charger, intval,
|
||||
input_voltage_limit);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
input_current_limit = intval;
|
||||
if (input_current_limit == EC_POWER_LIMIT_NONE)
|
||||
dev_info(dev,
|
||||
"External Current Limit cleared for all ports\n");
|
||||
else
|
||||
dev_info(dev,
|
||||
"External Current Limit set to %dmA for all ports\n",
|
||||
input_current_limit);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
ret = cros_usbpd_charger_set_ext_power_limit(charger,
|
||||
input_current_limit,
|
||||
intval);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
input_voltage_limit = intval;
|
||||
if (input_voltage_limit == EC_POWER_LIMIT_NONE)
|
||||
dev_info(dev,
|
||||
"External Voltage Limit cleared for all ports\n");
|
||||
else
|
||||
dev_info(dev,
|
||||
"External Voltage Limit set to %dmV for all ports\n",
|
||||
input_voltage_limit);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_usbpd_charger_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_usbpd_charger_ec_event(struct notifier_block *nb,
|
||||
unsigned long queued_during_suspend,
|
||||
void *_notify)
|
||||
|
@ -525,6 +638,9 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
|
|||
|
||||
psy_desc = &port->psy_desc;
|
||||
psy_desc->get_property = cros_usbpd_charger_get_prop;
|
||||
psy_desc->set_property = cros_usbpd_charger_set_prop;
|
||||
psy_desc->property_is_writeable =
|
||||
cros_usbpd_charger_property_is_writeable;
|
||||
psy_desc->external_power_changed =
|
||||
cros_usbpd_charger_power_changed;
|
||||
psy_cfg.drv_data = port;
|
||||
|
|
|
@ -247,7 +247,7 @@ static void stop_irq_work(void *data)
|
|||
static int max14656_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max14656_chip *chip;
|
||||
|
|
|
@ -193,7 +193,7 @@ static const struct power_supply_desc max17040_battery_desc = {
|
|||
static int max17040_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max17040_chip *chip;
|
||||
|
||||
|
|
|
@ -1005,7 +1005,7 @@ static void max17042_stop_work(void *data)
|
|||
static int max17042_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
const struct acpi_device_id *acpi_id = NULL;
|
||||
|
|
|
@ -1071,6 +1071,10 @@ __power_supply_register(struct device *parent,
|
|||
if (rc)
|
||||
goto create_triggers_failed;
|
||||
|
||||
rc = power_supply_add_hwmon_sysfs(psy);
|
||||
if (rc)
|
||||
goto add_hwmon_sysfs_failed;
|
||||
|
||||
/*
|
||||
* Update use_cnt after any uevents (most notably from device_add()).
|
||||
* We are here still during driver's probe but
|
||||
|
@ -1089,6 +1093,8 @@ __power_supply_register(struct device *parent,
|
|||
|
||||
return psy;
|
||||
|
||||
add_hwmon_sysfs_failed:
|
||||
power_supply_remove_triggers(psy);
|
||||
create_triggers_failed:
|
||||
psy_unregister_cooler(psy);
|
||||
register_cooler_failed:
|
||||
|
@ -1241,6 +1247,7 @@ void power_supply_unregister(struct power_supply *psy)
|
|||
cancel_work_sync(&psy->changed_work);
|
||||
cancel_delayed_work_sync(&psy->deferred_register_work);
|
||||
sysfs_remove_link(&psy->dev.kobj, "powers");
|
||||
power_supply_remove_hwmon_sysfs(psy);
|
||||
power_supply_remove_triggers(psy);
|
||||
psy_unregister_cooler(psy);
|
||||
psy_unregister_thermal(psy);
|
||||
|
|
355
drivers/power/supply/power_supply_hwmon.c
Normal file
355
drivers/power/supply/power_supply_hwmon.c
Normal file
|
@ -0,0 +1,355 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* power_supply_hwmon.c - power supply hwmon support.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct power_supply_hwmon {
|
||||
struct power_supply *psy;
|
||||
unsigned long *props;
|
||||
};
|
||||
|
||||
static int power_supply_hwmon_in_to_property(u32 attr)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_in_average:
|
||||
return POWER_SUPPLY_PROP_VOLTAGE_AVG;
|
||||
case hwmon_in_min:
|
||||
return POWER_SUPPLY_PROP_VOLTAGE_MIN;
|
||||
case hwmon_in_max:
|
||||
return POWER_SUPPLY_PROP_VOLTAGE_MAX;
|
||||
case hwmon_in_input:
|
||||
return POWER_SUPPLY_PROP_VOLTAGE_NOW;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int power_supply_hwmon_curr_to_property(u32 attr)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_curr_average:
|
||||
return POWER_SUPPLY_PROP_CURRENT_AVG;
|
||||
case hwmon_curr_max:
|
||||
return POWER_SUPPLY_PROP_CURRENT_MAX;
|
||||
case hwmon_curr_input:
|
||||
return POWER_SUPPLY_PROP_CURRENT_NOW;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
|
||||
{
|
||||
if (channel) {
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return POWER_SUPPLY_PROP_TEMP_AMBIENT;
|
||||
case hwmon_temp_min_alarm:
|
||||
return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
|
||||
case hwmon_temp_max_alarm:
|
||||
return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return POWER_SUPPLY_PROP_TEMP;
|
||||
case hwmon_temp_max:
|
||||
return POWER_SUPPLY_PROP_TEMP_MAX;
|
||||
case hwmon_temp_min:
|
||||
return POWER_SUPPLY_PROP_TEMP_MIN;
|
||||
case hwmon_temp_min_alarm:
|
||||
return POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
|
||||
case hwmon_temp_max_alarm:
|
||||
return POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
power_supply_hwmon_to_property(enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_in:
|
||||
return power_supply_hwmon_in_to_property(attr);
|
||||
case hwmon_curr:
|
||||
return power_supply_hwmon_curr_to_property(attr);
|
||||
case hwmon_temp:
|
||||
return power_supply_hwmon_temp_to_property(attr, channel);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
|
||||
u32 attr)
|
||||
{
|
||||
return type == hwmon_temp && attr == hwmon_temp_label;
|
||||
}
|
||||
|
||||
static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
|
||||
u32 attr)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_in:
|
||||
return attr == hwmon_in_min ||
|
||||
attr == hwmon_in_max;
|
||||
case hwmon_curr:
|
||||
return attr == hwmon_curr_max;
|
||||
case hwmon_temp:
|
||||
return attr == hwmon_temp_max ||
|
||||
attr == hwmon_temp_min ||
|
||||
attr == hwmon_temp_min_alarm ||
|
||||
attr == hwmon_temp_max_alarm;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static umode_t power_supply_hwmon_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct power_supply_hwmon *psyhw = data;
|
||||
int prop;
|
||||
|
||||
|
||||
if (power_supply_hwmon_is_a_label(type, attr))
|
||||
return 0444;
|
||||
|
||||
prop = power_supply_hwmon_to_property(type, attr, channel);
|
||||
if (prop < 0 || !test_bit(prop, psyhw->props))
|
||||
return 0;
|
||||
|
||||
if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
|
||||
power_supply_hwmon_is_writable(type, attr))
|
||||
return 0644;
|
||||
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static int power_supply_hwmon_read_string(struct device *dev,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel,
|
||||
const char **str)
|
||||
{
|
||||
*str = channel ? "temp" : "temp ambient";
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
|
||||
struct power_supply *psy = psyhw->psy;
|
||||
union power_supply_propval pspval;
|
||||
int ret, prop;
|
||||
|
||||
prop = power_supply_hwmon_to_property(type, attr, channel);
|
||||
if (prop < 0)
|
||||
return prop;
|
||||
|
||||
ret = power_supply_get_property(psy, prop, &pspval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (type) {
|
||||
/*
|
||||
* Both voltage and current is reported in units of
|
||||
* microvolts/microamps, so we need to adjust it to
|
||||
* milliamps(volts)
|
||||
*/
|
||||
case hwmon_curr:
|
||||
case hwmon_in:
|
||||
pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
|
||||
break;
|
||||
/*
|
||||
* Temp needs to be converted from 1/10 C to milli-C
|
||||
*/
|
||||
case hwmon_temp:
|
||||
if (check_mul_overflow(pspval.intval, 100,
|
||||
&pspval.intval))
|
||||
return -EOVERFLOW;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = pspval.intval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
|
||||
struct power_supply *psy = psyhw->psy;
|
||||
union power_supply_propval pspval;
|
||||
int prop;
|
||||
|
||||
prop = power_supply_hwmon_to_property(type, attr, channel);
|
||||
if (prop < 0)
|
||||
return prop;
|
||||
|
||||
pspval.intval = val;
|
||||
|
||||
switch (type) {
|
||||
/*
|
||||
* Both voltage and current is reported in units of
|
||||
* microvolts/microamps, so we need to adjust it to
|
||||
* milliamps(volts)
|
||||
*/
|
||||
case hwmon_curr:
|
||||
case hwmon_in:
|
||||
if (check_mul_overflow(pspval.intval, 1000,
|
||||
&pspval.intval))
|
||||
return -EOVERFLOW;
|
||||
break;
|
||||
/*
|
||||
* Temp needs to be converted from 1/10 C to milli-C
|
||||
*/
|
||||
case hwmon_temp:
|
||||
pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return power_supply_set_property(psy, prop, &pspval);
|
||||
}
|
||||
|
||||
static const struct hwmon_ops power_supply_hwmon_ops = {
|
||||
.is_visible = power_supply_hwmon_is_visible,
|
||||
.read = power_supply_hwmon_read,
|
||||
.write = power_supply_hwmon_write,
|
||||
.read_string = power_supply_hwmon_read_string,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *power_supply_hwmon_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_LABEL |
|
||||
HWMON_T_INPUT |
|
||||
HWMON_T_MAX |
|
||||
HWMON_T_MIN |
|
||||
HWMON_T_MIN_ALARM |
|
||||
HWMON_T_MIN_ALARM,
|
||||
|
||||
HWMON_T_LABEL |
|
||||
HWMON_T_INPUT |
|
||||
HWMON_T_MIN_ALARM |
|
||||
HWMON_T_LABEL |
|
||||
HWMON_T_MAX_ALARM),
|
||||
|
||||
HWMON_CHANNEL_INFO(curr,
|
||||
HWMON_C_AVERAGE |
|
||||
HWMON_C_MAX |
|
||||
HWMON_C_INPUT),
|
||||
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_AVERAGE |
|
||||
HWMON_I_MIN |
|
||||
HWMON_I_MAX |
|
||||
HWMON_I_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
|
||||
.ops = &power_supply_hwmon_ops,
|
||||
.info = power_supply_hwmon_info,
|
||||
};
|
||||
|
||||
static void power_supply_hwmon_bitmap_free(void *data)
|
||||
{
|
||||
bitmap_free(data);
|
||||
}
|
||||
|
||||
int power_supply_add_hwmon_sysfs(struct power_supply *psy)
|
||||
{
|
||||
const struct power_supply_desc *desc = psy->desc;
|
||||
struct power_supply_hwmon *psyhw;
|
||||
struct device *dev = &psy->dev;
|
||||
struct device *hwmon;
|
||||
int ret, i;
|
||||
|
||||
if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
|
||||
GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
|
||||
if (!psyhw) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
psyhw->psy = psy;
|
||||
psyhw->props = bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
|
||||
GFP_KERNEL);
|
||||
if (!psyhw->props) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = devm_add_action(dev, power_supply_hwmon_bitmap_free,
|
||||
psyhw->props);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < desc->num_properties; i++) {
|
||||
const enum power_supply_property prop = desc->properties[i];
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
case POWER_SUPPLY_PROP_TEMP_MAX:
|
||||
case POWER_SUPPLY_PROP_TEMP_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
|
||||
case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
|
||||
case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
set_bit(prop, psyhw->props);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, psy->desc->name,
|
||||
psyhw,
|
||||
&power_supply_hwmon_chip_info,
|
||||
NULL);
|
||||
ret = PTR_ERR_OR_ZERO(hwmon);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
devres_close_group(dev, power_supply_add_hwmon_sysfs);
|
||||
return 0;
|
||||
error:
|
||||
devres_release_group(dev, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
|
||||
{
|
||||
devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);
|
||||
}
|
|
@ -276,6 +276,8 @@ static struct device_attribute power_supply_attrs[] = {
|
|||
POWER_SUPPLY_ATTR(charge_control_start_threshold),
|
||||
POWER_SUPPLY_ATTR(charge_control_end_threshold),
|
||||
POWER_SUPPLY_ATTR(input_current_limit),
|
||||
POWER_SUPPLY_ATTR(input_voltage_limit),
|
||||
POWER_SUPPLY_ATTR(input_power_limit),
|
||||
POWER_SUPPLY_ATTR(energy_full_design),
|
||||
POWER_SUPPLY_ATTR(energy_empty_design),
|
||||
POWER_SUPPLY_ATTR(energy_full),
|
||||
|
|
|
@ -115,7 +115,7 @@ static const struct power_supply_desc rt5033_battery_desc = {
|
|||
static int rt5033_battery_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct rt5033_battery *battery;
|
||||
u32 ret;
|
||||
|
|
|
@ -1584,7 +1584,7 @@ static const struct regmap_config rt9455_regmap_config = {
|
|||
static int rt9455_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct rt9455_info *info;
|
||||
struct power_supply_config rt9455_charger_config = {};
|
||||
|
|
|
@ -314,7 +314,7 @@ static const struct power_supply_desc sbsm_default_psy_desc = {
|
|||
static int sbsm_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct sbsm_data *data;
|
||||
struct device *dev = &client->dev;
|
||||
struct power_supply_desc *psy_desc;
|
||||
|
|
|
@ -336,7 +336,7 @@ static int ucs1002_get_usb_type(struct ucs1002_info *info,
|
|||
case F_ACTIVE_MODE_BC12_CDP:
|
||||
type = POWER_SUPPLY_USB_TYPE_CDP;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
val->intval = type;
|
||||
|
||||
|
|
187
drivers/power/supply/wilco-charger.c
Normal file
187
drivers/power/supply/wilco-charger.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
// 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 */
|
||||
};
|
||||
|
||||
#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;
|
||||
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 < 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 <ncrews@chromium.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Wilco EC charge control driver");
|
|
@ -128,6 +128,8 @@ enum power_supply_property {
|
|||
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_INPUT_VOLTAGE_LIMIT,
|
||||
POWER_SUPPLY_PROP_INPUT_POWER_LIMIT,
|
||||
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
|
||||
POWER_SUPPLY_PROP_ENERGY_FULL,
|
||||
|
@ -480,4 +482,17 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_POWER_SUPPLY_HWMON
|
||||
int power_supply_add_hwmon_sysfs(struct power_supply *psy);
|
||||
void power_supply_remove_hwmon_sysfs(struct power_supply *psy);
|
||||
#else
|
||||
static inline int power_supply_add_hwmon_sysfs(struct power_supply *psy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {}
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_POWER_SUPPLY_H__ */
|
||||
|
|
Loading…
Reference in a new issue