power supply and reset changes for the 6.18 series
* power-supply core
- introduce adc-battery-helper for capacity estimation based on
simple ADC readings of battery voltage and current
- add new property for battery internal resistance
- add new property for battery state of health
* power-supply drivers
- ug3105_battery: convert to adc-battery-helper
- intel_dc_ti_battery: New driver for Intel Dollar Cove TI batteries
- rt9467-charger: add voltage and current ADC support
- sbs-charger: support multiple instances
- qcom_battmgr: add charge control support
- qcom_battmgr: add support for state of health and internal resistance
- max77705_charger: big driver cleanup
- max77705_charger: add support for setting charge current
- misc. minor fixes and cleanups
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmjcV2cACgkQ2O7X88g7
+pol1RAAkGjNlitEnD+j5HB3i2ROjVg1oOd/iBiIwZ9wEpOemy/bNEpt9M69U4jJ
IRKPJcnjOf37koWegyIoQ/te1qAdiXZm7oc/SIN63hn0U+nb2HB8TDVP7J9nzG3b
qTTViSOciJfpmwn7P/jFehY3oL29eMELamMn5AnVx5jCH0Baiw4qQSRCG81s40hK
6+WdLXhcQBeTzwj69FvTgdQ1McIiAgciGEN+3t5vB/3D4gstMV22DyrKuJOX9NNk
4wdSb5t3O+JuQ819T02O4ovyp1JHC0CPm/Si2n+TRBWmXEWJu/so18SQvT1u9SYb
/9p83bZJLRF7icWRGXHvM/eqV/K+G5MMHLKY+5tEG4gFLnKZl3WaTG1gAuoTNqx0
unR95ipWbQjy7z90QGlxF1+Ui3ScdwHic2fZ3DW0Bl2aIXARCJ0jOU0ShZAPS7Ff
NvVDoj/2/IQSzl0UJDb2uyW5S3Vl94f8z1w3xDmV3atA9ccioyTibTjiULPw42C4
GVtRHJFjQW2FVQ+rIvnjc3BHn2i6Tqj0qVHQQUH8xL17TXg2cK6KkTIB3LuyFKai
ol74I6VUsEKLZbKM0s+7Dyukwf+YlgpQpG7M+B1v7MQbPMHdiG3WUAF8JHud2/Dz
6Tiq/IGzwbse0DzVSiC3KT3CX/DohkFKLYdRkTBmd38aVV/i6b4=
=UcWZ
-----END PGP SIGNATURE-----
Merge tag 'for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
"Power-supply core:
- introduce adc-battery-helper for capacity estimation based on
simple ADC readings of battery voltage and current
- add new properties for battery internal resistance and state of
health
Power-supply drivers:
- ug3105_battery: convert to adc-battery-helper
- intel_dc_ti_battery: New driver for Intel Dollar Cove TI batteries
- rt9467-charger: add voltage and current ADC support
- sbs-charger: support multiple instances
- qcom_battmgr:
- add charge control support
- add support for state of health and internal resistance
- max77705_charger:
- big driver cleanup
- add support for setting charge current
- misc minor fixes and cleanups"
* tag 'for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (38 commits)
power: supply: qcom_battmgr: handle charging state change notifications
power: supply: max77705_charger: use REGMAP_IRQ_REG_LINE macro
power: supply: max77705_charger: rework interrupts
power: supply: max77705_charger: add writable properties
power: supply: max77705_charger: return error when config fails
power: supply: max77705_charger: use regfields for config registers
power: supply: max77705_charger: refactoring: rename charger to chg
mfd: max77705: max77705_charger: move active discharge setting to mfd parent
power: supply: max77976_charger: fix constant current reporting
power: supply: qcom_battmgr: Add charge control support
dt-bindings: soc: qcom,pmic-glink: Add charge limit nvmem properties
power: supply: qcom_battmgr: update compats for SM8550 and X1E80100
power: supply: qcom_battmgr: Add state_of_health property
power: supply: qcom_battmgr: Add resistance power supply property
power: supply: core: Add state_of_health power supply property
power: supply: core: Add resistance power supply property
power: supply: rx51: remove redundant condition checks
dt-bindings: power: supply: bq24190: document charge enable pin
dt-bindings: power: supply: bq27xxx: document optional interrupt
power: supply: intel_dc_ti_battery: Drop no longer relevant comment
...
pull/1354/merge
commit
3ee22ad492
|
|
@ -553,6 +553,43 @@ Description:
|
|||
Integer > 0: representing full cycles
|
||||
Integer = 0: cycle_count info is not available
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/internal_resistance
|
||||
Date: August 2025
|
||||
Contact: linux-arm-msm@vger.kernel.org
|
||||
Description:
|
||||
Represent the battery's internal resistance, often referred
|
||||
to as Equivalent Series Resistance (ESR). It is a dynamic
|
||||
parameter that reflects the opposition to current flow within
|
||||
the cell. It is not a fixed value but varies significantly
|
||||
based on several operational conditions, including battery
|
||||
state of charge (SoC), temperature, and whether the battery
|
||||
is in a charging or discharging state.
|
||||
|
||||
Access: Read
|
||||
|
||||
Valid values: Represented in microohms
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/state_of_health
|
||||
Date: August 2025
|
||||
Contact: linux-arm-msm@vger.kernel.org
|
||||
Description:
|
||||
The state_of_health parameter quantifies the overall condition
|
||||
of a battery as a percentage, reflecting its ability to deliver
|
||||
rated performance relative to its original specifications. It is
|
||||
dynamically computed using a combination of learned capacity
|
||||
and impedance-based degradation indicators, both of which evolve
|
||||
over the battery's lifecycle.
|
||||
Note that the exact algorithms are kept secret by most battery
|
||||
vendors and the value from different battery vendors cannot be
|
||||
compared with each other as there is no vendor-agnostic definition
|
||||
of "performance". Also this usually cannot be used for any
|
||||
calculations (i.e. this is not the factor between charge_full and
|
||||
charge_full_design).
|
||||
|
||||
Access: Read
|
||||
|
||||
Valid values: 0 - 100 (percent)
|
||||
|
||||
**USB Properties**
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/input_current_limit
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ properties:
|
|||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
ce-gpios:
|
||||
description:
|
||||
Active low Charge Enable pin. Battery charging is enabled when
|
||||
REG01[5:4] = 01 and CE pin is Low. CE pin must be pulled high or low.
|
||||
maxItems: 1
|
||||
|
||||
usb-otg-vbus:
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
description: |
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ description: |
|
|||
Support various Texas Instruments fuel gauge devices that share similar
|
||||
register maps and power supply properties
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
|
|
@ -58,6 +55,10 @@ properties:
|
|||
maxItems: 1
|
||||
description: integer, I2C address of the fuel gauge.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: the SOC_INT or GPOUT pin
|
||||
|
||||
monitored-battery:
|
||||
description: |
|
||||
The fuel gauge uses the following battery properties:
|
||||
|
|
@ -68,6 +69,36 @@ properties:
|
|||
|
||||
power-supplies: true
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,bq27200
|
||||
- ti,bq27210
|
||||
- ti,bq27500 # deprecated, use revision specific property below
|
||||
- ti,bq27510 # deprecated, use revision specific property below
|
||||
- ti,bq27520 # deprecated, use revision specific property below
|
||||
- ti,bq27500-1
|
||||
- ti,bq27510g1
|
||||
- ti,bq27510g2
|
||||
- ti,bq27521
|
||||
- ti,bq27541
|
||||
- ti,bq27542
|
||||
- ti,bq27546
|
||||
- ti,bq27742
|
||||
- ti,bq27545
|
||||
- ti,bq27411
|
||||
- ti,bq27z561
|
||||
- ti,bq28z610
|
||||
- ti,bq34z100
|
||||
- ti,bq78z100
|
||||
then:
|
||||
properties:
|
||||
interrupts: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -56,6 +56,20 @@ properties:
|
|||
The array should contain a gpio entry for each PMIC Glink connector, in reg order.
|
||||
It is defined that GPIO active level means "CC2" or Reversed/Flipped orientation.
|
||||
|
||||
nvmem-cells:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
description:
|
||||
The nvmem cells contain the charge control settings, including the charge control
|
||||
enable status, the battery state of charge (SoC) threshold for stopping charging,
|
||||
and the battery SoC delta required to restart charging.
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: charge_limit_en
|
||||
- const: charge_limit_end
|
||||
- const: charge_limit_delta
|
||||
|
||||
patternProperties:
|
||||
'^connector@\d$':
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ static int max77705_i2c_probe(struct i2c_client *i2c)
|
|||
if (pmic_rev != MAX77705_PASS3)
|
||||
return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev);
|
||||
|
||||
/* Active Discharge Enable */
|
||||
regmap_update_bits(max77705->regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
|
||||
|
||||
ret = devm_regmap_add_irq_chip(dev, max77705->regmap,
|
||||
i2c->irq,
|
||||
IRQF_ONESHOT, 0,
|
||||
|
|
|
|||
|
|
@ -284,8 +284,8 @@ static int set_charging_fsm(struct pm860x_charger_info *info)
|
|||
{
|
||||
struct power_supply *psy;
|
||||
union power_supply_propval data;
|
||||
unsigned char fsm_state[][16] = { "init", "discharge", "precharge",
|
||||
"fastcharge",
|
||||
static const unsigned char fsm_state[][16] = {
|
||||
"init", "discharge", "precharge", "fastcharge",
|
||||
};
|
||||
int ret;
|
||||
int vbatt;
|
||||
|
|
@ -313,7 +313,7 @@ static int set_charging_fsm(struct pm860x_charger_info *info)
|
|||
|
||||
dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, "
|
||||
"Allowed:%d\n",
|
||||
&fsm_state[info->state][0],
|
||||
fsm_state[info->state],
|
||||
(info->online) ? "online" : "N/A",
|
||||
(info->present) ? "present" : "N/A", info->allowed);
|
||||
dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt);
|
||||
|
|
@ -385,7 +385,7 @@ static int set_charging_fsm(struct pm860x_charger_info *info)
|
|||
}
|
||||
dev_dbg(info->dev,
|
||||
"Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
|
||||
&fsm_state[info->state][0],
|
||||
fsm_state[info->state],
|
||||
(info->online) ? "online" : "N/A",
|
||||
(info->present) ? "present" : "N/A", info->allowed);
|
||||
mutex_unlock(&info->lock);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ config APM_POWER
|
|||
Say Y here to enable support APM status emulation using
|
||||
battery class devices.
|
||||
|
||||
config ADC_BATTERY_HELPER
|
||||
tristate
|
||||
|
||||
config GENERIC_ADC_BATTERY
|
||||
tristate "Generic battery support using IIO"
|
||||
depends on IIO
|
||||
|
|
@ -244,6 +247,18 @@ config BATTERY_INGENIC
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ingenic-battery.
|
||||
|
||||
config BATTERY_INTEL_DC_TI
|
||||
tristate "Intel Bay / Cherry Trail Dollar Cove TI battery driver"
|
||||
depends on INTEL_SOC_PMIC_CHTDC_TI && INTEL_DC_TI_ADC && IIO && ACPI
|
||||
select ADC_BATTERY_HELPER
|
||||
help
|
||||
Choose this option if you want to monitor battery status on Intel
|
||||
Bay Trail / Cherry Trail tablets using the Dollar Cove TI PMIC's
|
||||
coulomb-counter as fuel-gauge.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called intel_dc_ti_battery.
|
||||
|
||||
config BATTERY_IPAQ_MICRO
|
||||
tristate "iPAQ Atmel Micro ASIC battery driver"
|
||||
depends on MFD_IPAQ_MICRO
|
||||
|
|
@ -1050,6 +1065,7 @@ config CHARGER_SURFACE
|
|||
config BATTERY_UG3105
|
||||
tristate "uPI uG3105 battery monitor driver"
|
||||
depends on I2C
|
||||
select ADC_BATTERY_HELPER
|
||||
help
|
||||
Battery monitor driver for the uPI uG3105 battery monitor.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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_ADC_BATTERY_HELPER) += adc-battery-helper.o
|
||||
obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
|
||||
|
||||
obj-$(CONFIG_APM_POWER) += apm_power.o
|
||||
|
|
@ -41,6 +42,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
|||
obj-$(CONFIG_BATTERY_SAMSUNG_SDI) += samsung-sdi-battery.o
|
||||
obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
|
||||
obj-$(CONFIG_BATTERY_INGENIC) += ingenic-battery.o
|
||||
obj-$(CONFIG_BATTERY_INTEL_DC_TI) += intel_dc_ti_battery.o
|
||||
obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
|
||||
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
||||
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
|
||||
|
|
|
|||
|
|
@ -667,7 +667,8 @@ static int ab8500_btemp_bind(struct device *dev, struct device *master,
|
|||
|
||||
/* Create a work queue for the btemp */
|
||||
di->btemp_wq =
|
||||
alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);
|
||||
alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM | WQ_PERCPU,
|
||||
0);
|
||||
if (di->btemp_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,327 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Helper for batteries with accurate current and voltage measurement, but
|
||||
* without temperature measurement or without a "resistance-temp-table".
|
||||
*
|
||||
* Some fuel-gauges are not full-featured autonomous fuel-gauges.
|
||||
* These fuel-gauges offer accurate current and voltage measurements but
|
||||
* their coulomb-counters are intended to work together with an always on
|
||||
* micro-controller monitoring the fuel-gauge.
|
||||
*
|
||||
* This adc-battery-helper code offers open-circuit-voltage (ocv) and through
|
||||
* that capacity estimation for devices where such limited functionality
|
||||
* fuel-gauges are exposed directly to Linux.
|
||||
*
|
||||
* This helper requires the hw to provide accurate battery current_now and
|
||||
* voltage_now measurement and this helper the provides the following properties
|
||||
* based on top of those readings:
|
||||
*
|
||||
* POWER_SUPPLY_PROP_STATUS
|
||||
* POWER_SUPPLY_PROP_VOLTAGE_OCV
|
||||
* POWER_SUPPLY_PROP_VOLTAGE_NOW
|
||||
* POWER_SUPPLY_PROP_CURRENT_NOW
|
||||
* POWER_SUPPLY_PROP_CAPACITY
|
||||
*
|
||||
* As well as optional the following properties assuming an always present
|
||||
* system-scope battery, allowing direct use of adc_battery_helper_get_prop()
|
||||
* in this common case:
|
||||
* POWER_SUPPLY_PROP_PRESENT
|
||||
* POWER_SUPPLY_PROP_SCOPE
|
||||
*
|
||||
* Using this helper is as simple as:
|
||||
*
|
||||
* 1. Embed a struct adc_battery_helper this MUST be the first member of
|
||||
* the battery driver's data struct.
|
||||
* 2. Use adc_battery_helper_props[] or add the above properties to
|
||||
* the list of properties in power_supply_desc
|
||||
* 3. Call adc_battery_helper_init() after registering the power_supply and
|
||||
* before returning from the probe() function
|
||||
* 4. Use adc_battery_helper_get_prop() as the power-supply's get_property()
|
||||
* method, or call it for the above properties.
|
||||
* 5. Use adc_battery_helper_external_power_changed() as the power-supply's
|
||||
* external_power_changed() method or call it from that method.
|
||||
* 6. Use adc_battery_helper_[suspend|resume]() as suspend-resume methods or
|
||||
* call them from the driver's suspend-resume methods.
|
||||
*
|
||||
* The provided get_voltage_and_current_now() method will be called by this
|
||||
* helper at adc_battery_helper_init() time and later.
|
||||
*
|
||||
* Copyright (c) 2021-2025 Hans de Goede <hansg@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "adc-battery-helper.h"
|
||||
|
||||
#define MOV_AVG_WINDOW_SIZE ADC_BAT_HELPER_MOV_AVG_WINDOW_SIZE
|
||||
#define INIT_POLL_TIME (5 * HZ)
|
||||
#define POLL_TIME (30 * HZ)
|
||||
#define SETTLE_TIME (1 * HZ)
|
||||
|
||||
#define INIT_POLL_COUNT 30
|
||||
|
||||
#define CURR_HYST_UA 65000
|
||||
|
||||
#define LOW_BAT_UV 3700000
|
||||
#define FULL_BAT_HYST_UV 38000
|
||||
|
||||
#define AMBIENT_TEMP_CELSIUS 25
|
||||
|
||||
static int adc_battery_helper_get_status(struct adc_battery_helper *help)
|
||||
{
|
||||
int full_uv =
|
||||
help->psy->battery_info->constant_charge_voltage_max_uv - FULL_BAT_HYST_UV;
|
||||
|
||||
if (help->curr_ua > CURR_HYST_UA)
|
||||
return POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
if (help->curr_ua < -CURR_HYST_UA)
|
||||
return POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
|
||||
if (help->supplied) {
|
||||
bool full;
|
||||
|
||||
if (help->charge_finished)
|
||||
full = gpiod_get_value_cansleep(help->charge_finished);
|
||||
else
|
||||
full = help->ocv_avg_uv > full_uv;
|
||||
|
||||
if (full)
|
||||
return POWER_SUPPLY_STATUS_FULL;
|
||||
}
|
||||
|
||||
return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
}
|
||||
|
||||
static void adc_battery_helper_work(struct work_struct *work)
|
||||
{
|
||||
struct adc_battery_helper *help = container_of(work, struct adc_battery_helper,
|
||||
work.work);
|
||||
int i, curr_diff_ua, volt_diff_uv, res_mohm, ret, win_size;
|
||||
struct device *dev = help->psy->dev.parent;
|
||||
int volt_uv, prev_volt_uv = help->volt_uv;
|
||||
int curr_ua, prev_curr_ua = help->curr_ua;
|
||||
bool prev_supplied = help->supplied;
|
||||
int prev_status = help->status;
|
||||
|
||||
guard(mutex)(&help->lock);
|
||||
|
||||
ret = help->get_voltage_and_current_now(help->psy, &volt_uv, &curr_ua);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
help->volt_uv = volt_uv;
|
||||
help->curr_ua = curr_ua;
|
||||
|
||||
help->ocv_uv[help->ocv_avg_index] =
|
||||
help->volt_uv - help->curr_ua * help->intern_res_avg_mohm / 1000;
|
||||
dev_dbg(dev, "volt-now: %d, curr-now: %d, volt-ocv: %d\n",
|
||||
help->volt_uv, help->curr_ua, help->ocv_uv[help->ocv_avg_index]);
|
||||
help->ocv_avg_index = (help->ocv_avg_index + 1) % MOV_AVG_WINDOW_SIZE;
|
||||
help->poll_count++;
|
||||
|
||||
help->ocv_avg_uv = 0;
|
||||
win_size = min(help->poll_count, MOV_AVG_WINDOW_SIZE);
|
||||
for (i = 0; i < win_size; i++)
|
||||
help->ocv_avg_uv += help->ocv_uv[i];
|
||||
help->ocv_avg_uv /= win_size;
|
||||
|
||||
help->supplied = power_supply_am_i_supplied(help->psy);
|
||||
help->status = adc_battery_helper_get_status(help);
|
||||
if (help->status == POWER_SUPPLY_STATUS_FULL)
|
||||
help->capacity = 100;
|
||||
else
|
||||
help->capacity = power_supply_batinfo_ocv2cap(help->psy->battery_info,
|
||||
help->ocv_avg_uv,
|
||||
AMBIENT_TEMP_CELSIUS);
|
||||
|
||||
/*
|
||||
* Skip internal resistance calc on charger [un]plug and
|
||||
* when the battery is almost empty (voltage low).
|
||||
*/
|
||||
if (help->supplied != prev_supplied ||
|
||||
help->volt_uv < LOW_BAT_UV ||
|
||||
help->poll_count < 2)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Assuming that the OCV voltage does not change significantly
|
||||
* between 2 polls, then we can calculate the internal resistance
|
||||
* on a significant current change by attributing all voltage
|
||||
* change between the 2 readings to the internal resistance.
|
||||
*/
|
||||
curr_diff_ua = abs(help->curr_ua - prev_curr_ua);
|
||||
if (curr_diff_ua < CURR_HYST_UA)
|
||||
goto out;
|
||||
|
||||
volt_diff_uv = abs(help->volt_uv - prev_volt_uv);
|
||||
res_mohm = volt_diff_uv * 1000 / curr_diff_ua;
|
||||
|
||||
if ((res_mohm < (help->intern_res_avg_mohm * 2 / 3)) ||
|
||||
(res_mohm > (help->intern_res_avg_mohm * 4 / 3))) {
|
||||
dev_dbg(dev, "Ignoring outlier internal resistance %d mOhm\n", res_mohm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Internal resistance %d mOhm\n", res_mohm);
|
||||
|
||||
help->intern_res_mohm[help->intern_res_avg_index] = res_mohm;
|
||||
help->intern_res_avg_index = (help->intern_res_avg_index + 1) % MOV_AVG_WINDOW_SIZE;
|
||||
help->intern_res_poll_count++;
|
||||
|
||||
help->intern_res_avg_mohm = 0;
|
||||
win_size = min(help->intern_res_poll_count, MOV_AVG_WINDOW_SIZE);
|
||||
for (i = 0; i < win_size; i++)
|
||||
help->intern_res_avg_mohm += help->intern_res_mohm[i];
|
||||
help->intern_res_avg_mohm /= win_size;
|
||||
|
||||
out:
|
||||
queue_delayed_work(system_percpu_wq, &help->work,
|
||||
(help->poll_count <= INIT_POLL_COUNT) ?
|
||||
INIT_POLL_TIME : POLL_TIME);
|
||||
|
||||
if (help->status != prev_status)
|
||||
power_supply_changed(help->psy);
|
||||
}
|
||||
|
||||
const enum power_supply_property adc_battery_helper_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_properties);
|
||||
|
||||
static_assert(ARRAY_SIZE(adc_battery_helper_properties) ==
|
||||
ADC_HELPER_NUM_PROPERTIES);
|
||||
|
||||
int adc_battery_helper_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct adc_battery_helper *help = power_supply_get_drvdata(psy);
|
||||
int dummy, ret = 0;
|
||||
|
||||
/*
|
||||
* Avoid racing with adc_battery_helper_work() while it is updating
|
||||
* variables and avoid calling get_voltage_and_current_now() reentrantly.
|
||||
*/
|
||||
guard(mutex)(&help->lock);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = help->status;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = help->get_voltage_and_current_now(psy, &val->intval, &dummy);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
val->intval = help->ocv_avg_uv;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = help->get_voltage_and_current_now(psy, &dummy, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = help->capacity;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_get_property);
|
||||
|
||||
void adc_battery_helper_external_power_changed(struct power_supply *psy)
|
||||
{
|
||||
struct adc_battery_helper *help = power_supply_get_drvdata(psy);
|
||||
|
||||
dev_dbg(help->psy->dev.parent, "external power changed\n");
|
||||
mod_delayed_work(system_percpu_wq, &help->work, SETTLE_TIME);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_external_power_changed);
|
||||
|
||||
static void adc_battery_helper_start_work(struct adc_battery_helper *help)
|
||||
{
|
||||
help->poll_count = 0;
|
||||
help->ocv_avg_index = 0;
|
||||
|
||||
queue_delayed_work(system_percpu_wq, &help->work, 0);
|
||||
flush_delayed_work(&help->work);
|
||||
}
|
||||
|
||||
int adc_battery_helper_init(struct adc_battery_helper *help, struct power_supply *psy,
|
||||
adc_battery_helper_get_func get_voltage_and_current_now,
|
||||
struct gpio_desc *charge_finished_gpio)
|
||||
{
|
||||
struct device *dev = psy->dev.parent;
|
||||
int ret;
|
||||
|
||||
help->psy = psy;
|
||||
help->get_voltage_and_current_now = get_voltage_and_current_now;
|
||||
help->charge_finished = charge_finished_gpio;
|
||||
|
||||
ret = devm_mutex_init(dev, &help->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_delayed_work_autocancel(dev, &help->work, adc_battery_helper_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!help->psy->battery_info ||
|
||||
help->psy->battery_info->factory_internal_resistance_uohm == -EINVAL ||
|
||||
help->psy->battery_info->constant_charge_voltage_max_uv == -EINVAL ||
|
||||
!psy->battery_info->ocv_table[0]) {
|
||||
dev_err(dev, "error required properties are missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Use provided internal resistance as start point (in milli-ohm) */
|
||||
help->intern_res_avg_mohm =
|
||||
help->psy->battery_info->factory_internal_resistance_uohm / 1000;
|
||||
/* Also add it to the internal resistance moving average window */
|
||||
help->intern_res_mohm[0] = help->intern_res_avg_mohm;
|
||||
help->intern_res_avg_index = 1;
|
||||
help->intern_res_poll_count = 1;
|
||||
|
||||
adc_battery_helper_start_work(help);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_init);
|
||||
|
||||
int adc_battery_helper_suspend(struct device *dev)
|
||||
{
|
||||
struct adc_battery_helper *help = dev_get_drvdata(dev);
|
||||
|
||||
cancel_delayed_work_sync(&help->work);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_suspend);
|
||||
|
||||
int adc_battery_helper_resume(struct device *dev)
|
||||
{
|
||||
struct adc_battery_helper *help = dev_get_drvdata(dev);
|
||||
|
||||
adc_battery_helper_start_work(help);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adc_battery_helper_resume);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
|
||||
MODULE_DESCRIPTION("ADC battery capacity estimation helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Helper for batteries with accurate current and voltage measurement, but
|
||||
* without temperature measurement or without a "resistance-temp-table".
|
||||
* Copyright (c) 2021-2025 Hans de Goede <hansg@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define ADC_BAT_HELPER_MOV_AVG_WINDOW_SIZE 8
|
||||
|
||||
struct power_supply;
|
||||
struct gpio_desc;
|
||||
|
||||
/*
|
||||
* The adc battery helper code needs voltage- and current-now to be sampled as
|
||||
* close to each other (in sample-time) as possible. A single getter function is
|
||||
* used to allow the battery driver to handle this in the best way possible.
|
||||
*/
|
||||
typedef int (*adc_battery_helper_get_func)(struct power_supply *psy, int *volt, int *curr);
|
||||
|
||||
struct adc_battery_helper {
|
||||
struct power_supply *psy;
|
||||
struct gpio_desc *charge_finished;
|
||||
struct delayed_work work;
|
||||
struct mutex lock;
|
||||
adc_battery_helper_get_func get_voltage_and_current_now;
|
||||
int ocv_uv[ADC_BAT_HELPER_MOV_AVG_WINDOW_SIZE]; /* micro-volt */
|
||||
int intern_res_mohm[ADC_BAT_HELPER_MOV_AVG_WINDOW_SIZE]; /* milli-ohm */
|
||||
int poll_count;
|
||||
int ocv_avg_index;
|
||||
int ocv_avg_uv; /* micro-volt */
|
||||
int intern_res_poll_count;
|
||||
int intern_res_avg_index;
|
||||
int intern_res_avg_mohm; /* milli-ohm */
|
||||
int volt_uv; /* micro-volt */
|
||||
int curr_ua; /* micro-ampere */
|
||||
int capacity; /* percent */
|
||||
int status;
|
||||
bool supplied;
|
||||
};
|
||||
|
||||
extern const enum power_supply_property adc_battery_helper_properties[];
|
||||
/* Must be const cannot be an external. Asserted in adc-battery-helper.c */
|
||||
#define ADC_HELPER_NUM_PROPERTIES 7
|
||||
|
||||
int adc_battery_helper_init(struct adc_battery_helper *help, struct power_supply *psy,
|
||||
adc_battery_helper_get_func get_voltage_and_current_now,
|
||||
struct gpio_desc *charge_finished_gpio);
|
||||
/*
|
||||
* The below functions can be directly used as power-supply / suspend-resume
|
||||
* callbacks. They cast the power_supply_get_drvdata() / dev_get_drvdata() data
|
||||
* directly to struct adc_battery_helper. Therefor struct adc_battery_helper
|
||||
* MUST be the first member of the battery driver's data struct.
|
||||
*/
|
||||
int adc_battery_helper_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val);
|
||||
void adc_battery_helper_external_power_changed(struct power_supply *psy);
|
||||
int adc_battery_helper_suspend(struct device *dev);
|
||||
int adc_battery_helper_resume(struct device *dev);
|
||||
|
|
@ -842,7 +842,7 @@ static int bq2415x_notifier_call(struct notifier_block *nb,
|
|||
if (bq->automode < 1)
|
||||
return NOTIFY_OK;
|
||||
|
||||
mod_delayed_work(system_wq, &bq->work, 0);
|
||||
mod_delayed_work(system_percpu_wq, &bq->work, 0);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
|
@ -1516,7 +1516,7 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
|
|||
|
||||
ret = bq2415x_detect_revision(bq);
|
||||
if (ret < 0)
|
||||
strcpy(revstr, "unknown");
|
||||
strscpy(revstr, "unknown", sizeof(revstr));
|
||||
else
|
||||
sprintf(revstr, "1.%d", ret);
|
||||
|
||||
|
|
|
|||
|
|
@ -1467,7 +1467,7 @@ static void bq24190_charger_external_power_changed(struct power_supply *psy)
|
|||
* too low default 500mA iinlim. Delay setting the input-current-limit
|
||||
* for 300ms to avoid this.
|
||||
*/
|
||||
queue_delayed_work(system_wq, &bdi->input_current_limit_work,
|
||||
queue_delayed_work(system_percpu_wq, &bdi->input_current_limit_work,
|
||||
msecs_to_jiffies(300));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1127,7 +1127,7 @@ static int poll_interval_param_set(const char *val, const struct kernel_param *k
|
|||
|
||||
mutex_lock(&bq27xxx_list_lock);
|
||||
list_for_each_entry(di, &bq27xxx_battery_devices, list)
|
||||
mod_delayed_work(system_wq, &di->work, 0);
|
||||
mod_delayed_work(system_percpu_wq, &di->work, 0);
|
||||
mutex_unlock(&bq27xxx_list_lock);
|
||||
|
||||
return ret;
|
||||
|
|
@ -1945,7 +1945,7 @@ static void bq27xxx_battery_update_unlocked(struct bq27xxx_device_info *di)
|
|||
di->last_update = jiffies;
|
||||
|
||||
if (!di->removed && poll_interval > 0)
|
||||
mod_delayed_work(system_wq, &di->work, poll_interval * HZ);
|
||||
mod_delayed_work(system_percpu_wq, &di->work, poll_interval * HZ);
|
||||
}
|
||||
|
||||
void bq27xxx_battery_update(struct bq27xxx_device_info *di)
|
||||
|
|
@ -2221,14 +2221,7 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
|
|||
struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
|
||||
|
||||
/* After charger plug in/out wait 0.5s for things to stabilize */
|
||||
mod_delayed_work(system_wq, &di->work, HZ / 2);
|
||||
}
|
||||
|
||||
static void bq27xxx_battery_mutex_destroy(void *data)
|
||||
{
|
||||
struct mutex *lock = data;
|
||||
|
||||
mutex_destroy(lock);
|
||||
mod_delayed_work(system_percpu_wq, &di->work, HZ / 2);
|
||||
}
|
||||
|
||||
int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
||||
|
|
@ -2242,9 +2235,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|||
int ret;
|
||||
|
||||
INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
|
||||
mutex_init(&di->lock);
|
||||
ret = devm_add_action_or_reset(di->dev, bq27xxx_battery_mutex_destroy,
|
||||
&di->lock);
|
||||
ret = devm_mutex_init(di->dev, &di->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -506,10 +506,7 @@ static int cw_battery_get_property(struct power_supply *psy,
|
|||
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
if (cw_bat->battery->charge_full_design_uah > 0)
|
||||
val->intval = cw_bat->battery->charge_full_design_uah;
|
||||
else
|
||||
val->intval = 0;
|
||||
val->intval = max(cw_bat->battery->charge_full_design_uah, 0);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
|
|
@ -702,8 +699,7 @@ static int cw_bat_probe(struct i2c_client *client)
|
|||
if (!cw_bat->battery_workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
devm_delayed_work_autocancel(&client->dev,
|
||||
&cw_bat->battery_delay_work, cw_bat_work);
|
||||
devm_delayed_work_autocancel(&client->dev, &cw_bat->battery_delay_work, cw_bat_work);
|
||||
queue_delayed_work(cw_bat->battery_workqueue,
|
||||
&cw_bat->battery_delay_work, msecs_to_jiffies(10));
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ static int set_charge_current_limit(struct gpio_charger *gpio_charger, int val)
|
|||
|
||||
for (i = 0; i < ndescs; i++) {
|
||||
bool val = (mapping.gpiodata >> i) & 1;
|
||||
gpiod_set_value_cansleep(gpios[ndescs-i-1], val);
|
||||
|
||||
gpiod_set_value_cansleep(gpios[ndescs - i - 1], val);
|
||||
}
|
||||
|
||||
gpio_charger->charge_current_limit = mapping.limit_ua;
|
||||
|
|
@ -226,14 +227,14 @@ static int init_charge_current_limit(struct device *dev,
|
|||
gpio_charger->current_limit_map_size = len / 2;
|
||||
|
||||
len = device_property_read_u32_array(dev, "charge-current-limit-mapping",
|
||||
(u32*) gpio_charger->current_limit_map, len);
|
||||
(u32 *) gpio_charger->current_limit_map, len);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
set_def_limit = !device_property_read_u32(dev,
|
||||
"charge-current-limit-default-microamp",
|
||||
&def_limit);
|
||||
for (i=0; i < gpio_charger->current_limit_map_size; i++) {
|
||||
for (i = 0; i < gpio_charger->current_limit_map_size; i++) {
|
||||
if (gpio_charger->current_limit_map[i].limit_ua > cur_limit) {
|
||||
dev_err(dev, "charge-current-limit-mapping not sorted by current in descending order\n");
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,389 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Battery driver for the coulomb-counter of the Intel Dollar Cove TI PMIC
|
||||
*
|
||||
* Note the Intel Dollar Cove TI PMIC coulomb-counter is not a full-featured
|
||||
* autonomous fuel-gauge. It is intended to work together with an always on
|
||||
* micro-controller monitoring it.
|
||||
*
|
||||
* Since Linux does not monitor coulomb-counter changes while the device
|
||||
* is off or suspended, voltage based capacity estimation from
|
||||
* the adc-battery-helper code is used.
|
||||
*
|
||||
* Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
|
||||
*
|
||||
* Register definitions and calibration code was taken from
|
||||
* kernel/drivers/platform/x86/dc_ti_cc.c from the Acer A1-840 Android kernel
|
||||
* which has the following copyright header:
|
||||
*
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
* Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
|
||||
*
|
||||
* dc_ti_cc.c is part of the Acer A1-840 Android kernel source-code archive
|
||||
* named: "App. Guide_Acer_20151221_A_A.zip"
|
||||
* which is distributed by Acer from the Acer A1-840 support page:
|
||||
* https://www.acer.com/us-en/support/product-support/A1-840/downloads
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "adc-battery-helper.h"
|
||||
|
||||
#define DC_TI_PMIC_VERSION_REG 0x00
|
||||
#define PMIC_VERSION_A0 0xC0
|
||||
#define PMIC_VERSION_A1 0xC1
|
||||
|
||||
#define DC_TI_CC_CNTL_REG 0x60
|
||||
#define CC_CNTL_CC_CTR_EN BIT(0)
|
||||
#define CC_CNTL_CC_CLR_EN BIT(1)
|
||||
#define CC_CNTL_CC_CAL_EN BIT(2)
|
||||
#define CC_CNTL_CC_OFFSET_EN BIT(3)
|
||||
#define CC_CNTL_SMPL_INTVL GENMASK(5, 4)
|
||||
#define CC_CNTL_SMPL_INTVL_15MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 0)
|
||||
#define CC_CNTL_SMPL_INTVL_62MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 1)
|
||||
#define CC_CNTL_SMPL_INTVL_125MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 2)
|
||||
#define CC_CNTL_SMPL_INTVL_250MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 3)
|
||||
|
||||
#define DC_TI_SMPL_CTR0_REG 0x69
|
||||
#define DC_TI_SMPL_CTR1_REG 0x68
|
||||
#define DC_TI_SMPL_CTR2_REG 0x67
|
||||
|
||||
#define DC_TI_CC_OFFSET_HI_REG 0x61
|
||||
#define CC_OFFSET_HI_MASK 0x3F
|
||||
#define DC_TI_CC_OFFSET_LO_REG 0x62
|
||||
|
||||
#define DC_TI_SW_OFFSET_REG 0x6C
|
||||
|
||||
#define DC_TI_CC_ACC3_REG 0x63
|
||||
#define DC_TI_CC_ACC2_REG 0x64
|
||||
#define DC_TI_CC_ACC1_REG 0x65
|
||||
#define DC_TI_CC_ACC0_REG 0x66
|
||||
|
||||
#define DC_TI_CC_INTG1_REG 0x6A
|
||||
#define DC_TI_CC_INTG1_MASK 0x3F
|
||||
#define DC_TI_CC_INTG0_REG 0x6B
|
||||
|
||||
#define DC_TI_EEPROM_ACCESS_CONTROL 0x88
|
||||
#define EEPROM_UNLOCK 0xDA
|
||||
#define EEPROM_LOCK 0x00
|
||||
|
||||
#define DC_TI_EEPROM_CC_GAIN_REG 0xF4
|
||||
#define CC_TRIM_REVISION GENMASK(3, 0)
|
||||
#define CC_GAIN_CORRECTION GENMASK(7, 4)
|
||||
|
||||
#define PMIC_VERSION_A0_TRIM_REV 3
|
||||
#define PMIC_VERSION_A1_MIN_TRIM_REV 1
|
||||
|
||||
#define DC_TI_EEPROM_CC_OFFSET_REG 0xFD
|
||||
|
||||
#define DC_TI_EEPROM_CTRL 0xFE
|
||||
#define EEPROM_BANK0_SEL 0x01
|
||||
#define EEPROM_BANK1_SEL 0x02
|
||||
|
||||
#define SMPL_INTVL_US 15000
|
||||
#define SMPL_INTVL_MS (SMPL_INTVL_US / USEC_PER_MSEC)
|
||||
#define CALIBRATION_TIME_US (10 * SMPL_INTVL_US)
|
||||
#define SLEEP_SLACK_US 2500
|
||||
|
||||
/* CC gain correction is in 0.0025 increments */
|
||||
#define CC_GAIN_STEP 25
|
||||
#define CC_GAIN_DIV 10000
|
||||
|
||||
/* CC offset is in 0.5 units per 250ms (default sample interval) */
|
||||
#define CC_OFFSET_DIV 2
|
||||
#define CC_OFFSET_SMPL_INTVL_MS 250
|
||||
|
||||
/* CC accumulator scale is 366.2 ųCoulumb / unit */
|
||||
#define CC_ACC_TO_UA(acc, smpl_ctr) \
|
||||
((acc) * (3662 * MSEC_PER_SEC / 10) / ((smpl_ctr) * SMPL_INTVL_MS))
|
||||
|
||||
#define DEV_NAME "chtdc_ti_battery"
|
||||
|
||||
struct dc_ti_battery_chip {
|
||||
/* Must be the first member see adc-battery-helper documentation */
|
||||
struct adc_battery_helper helper;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct iio_channel *vbat_channel;
|
||||
struct power_supply *psy;
|
||||
int cc_gain;
|
||||
int cc_offset;
|
||||
};
|
||||
|
||||
static int dc_ti_battery_get_voltage_and_current_now(struct power_supply *psy, int *volt, int *curr)
|
||||
{
|
||||
struct dc_ti_battery_chip *chip = power_supply_get_drvdata(psy);
|
||||
s64 cnt_start_usec, now_usec, sleep_usec;
|
||||
unsigned int reg_val;
|
||||
s32 acc, smpl_ctr;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Enable coulomb-counter before reading Vbat from ADC, so that the CC
|
||||
* samples are from the same time period as the Vbat reading.
|
||||
*/
|
||||
ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG,
|
||||
CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN | CC_CNTL_CC_CTR_EN);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
cnt_start_usec = ktime_get_ns() / NSEC_PER_USEC;
|
||||
|
||||
/* Read Vbat, convert IIO mV to power-supply ųV */
|
||||
ret = iio_read_channel_processed_scale(chip->vbat_channel, volt, 1000);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
/* Sleep at least 3 sample-times + slack to get 3+ CC samples */
|
||||
now_usec = ktime_get_ns() / NSEC_PER_USEC;
|
||||
sleep_usec = 3 * SMPL_INTVL_US + SLEEP_SLACK_US - (now_usec - cnt_start_usec);
|
||||
if (sleep_usec > 0 && sleep_usec < 1000000)
|
||||
usleep_range(sleep_usec, sleep_usec + SLEEP_SLACK_US);
|
||||
|
||||
/*
|
||||
* The PMIC latches the coulomb- and sample-counters upon reading the
|
||||
* CC_ACC0 register. Reading multiple registers at once is not supported.
|
||||
*
|
||||
* Step 1: Read CC_ACC0 - CC_ACC3
|
||||
*/
|
||||
ret = regmap_read(chip->regmap, DC_TI_CC_ACC0_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
acc = reg_val;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_CC_ACC1_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
acc |= reg_val << 8;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_CC_ACC2_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
acc |= reg_val << 16;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_CC_ACC3_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
acc |= reg_val << 24;
|
||||
|
||||
/* Step 2: Read SMPL_CTR0 - SMPL_CTR2 */
|
||||
ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR0_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
smpl_ctr = reg_val;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR1_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
smpl_ctr |= reg_val << 8;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR2_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
smpl_ctr |= reg_val << 16;
|
||||
|
||||
/* Disable the coulumb-counter again */
|
||||
ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG,
|
||||
CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/* Apply calibration */
|
||||
acc -= chip->cc_offset * smpl_ctr * SMPL_INTVL_MS /
|
||||
(CC_OFFSET_SMPL_INTVL_MS * CC_OFFSET_DIV);
|
||||
acc = acc * (CC_GAIN_DIV - chip->cc_gain * CC_GAIN_STEP) / CC_GAIN_DIV;
|
||||
*curr = CC_ACC_TO_UA(acc, smpl_ctr);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
dev_err(chip->dev, "IO-error %d communicating with PMIC\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc dc_ti_battery_psy_desc = {
|
||||
.name = "intel_dc_ti_battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.get_property = adc_battery_helper_get_property,
|
||||
.external_power_changed = adc_battery_helper_external_power_changed,
|
||||
.properties = adc_battery_helper_properties,
|
||||
.num_properties = ADC_HELPER_NUM_PROPERTIES,
|
||||
};
|
||||
|
||||
static int dc_ti_battery_hw_init(struct dc_ti_battery_chip *chip)
|
||||
{
|
||||
u8 pmic_version, cc_trim_rev;
|
||||
unsigned int reg_val;
|
||||
int ret;
|
||||
|
||||
/* Set sample rate to 15 ms and calibrate the coulomb-counter */
|
||||
ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG,
|
||||
CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN |
|
||||
CC_CNTL_CC_CAL_EN | CC_CNTL_CC_CTR_EN);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
fsleep(CALIBRATION_TIME_US);
|
||||
|
||||
/* Disable coulomb-counter it is only used while getting the current */
|
||||
ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG,
|
||||
CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_PMIC_VERSION_REG, ®_val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pmic_version = reg_val;
|
||||
|
||||
/*
|
||||
* As per the PMIC vendor (TI), the calibration offset and gain err
|
||||
* values are stored in EEPROM Bank 0 and Bank 1 of the PMIC.
|
||||
* We need to read the stored offset and gain margins and need
|
||||
* to apply the corrections to the raw coulomb counter value.
|
||||
*/
|
||||
|
||||
/* Unlock the EEPROM Access */
|
||||
ret = regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_UNLOCK);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Select Bank 1 to read CC GAIN Err correction */
|
||||
ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK1_SEL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_GAIN_REG, ®_val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
cc_trim_rev = FIELD_GET(CC_TRIM_REVISION, reg_val);
|
||||
|
||||
dev_dbg(chip->dev, "pmic-ver 0x%02x trim-rev %d\n", pmic_version, cc_trim_rev);
|
||||
|
||||
if (!(pmic_version == PMIC_VERSION_A0 && cc_trim_rev == PMIC_VERSION_A0_TRIM_REV) &&
|
||||
!(pmic_version == PMIC_VERSION_A1 && cc_trim_rev >= PMIC_VERSION_A1_MIN_TRIM_REV)) {
|
||||
dev_dbg(chip->dev, "unsupported trim-revision, using uncalibrated CC values\n");
|
||||
goto out_relock;
|
||||
}
|
||||
|
||||
chip->cc_gain = 1 - (int)FIELD_GET(CC_GAIN_CORRECTION, reg_val);
|
||||
|
||||
/* Select Bank 0 to read CC OFFSET Correction */
|
||||
ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK0_SEL);
|
||||
if (ret)
|
||||
goto out_relock;
|
||||
|
||||
ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_OFFSET_REG, ®_val);
|
||||
if (ret)
|
||||
goto out_relock;
|
||||
|
||||
chip->cc_offset = (s8)reg_val;
|
||||
|
||||
dev_dbg(chip->dev, "cc-offset %d cc-gain %d\n", chip->cc_offset, chip->cc_gain);
|
||||
|
||||
out_relock:
|
||||
/* Re-lock the EEPROM Access */
|
||||
regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_LOCK);
|
||||
out:
|
||||
if (ret)
|
||||
dev_err(chip->dev, "IO-error %d initializing PMIC\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dc_ti_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct fwnode_reference_args args;
|
||||
struct gpio_desc *charge_finished;
|
||||
struct dc_ti_battery_chip *chip;
|
||||
int ret;
|
||||
|
||||
/* On most devices with a Dollar Cove TI the battery is handled by ACPI */
|
||||
if (!acpi_quirk_skip_acpi_ac_and_battery())
|
||||
return -ENODEV;
|
||||
|
||||
/* ACPI glue code adds a "monitored-battery" fwnode, wait for this */
|
||||
ret = fwnode_property_get_reference_args(dev_fwnode(dev), "monitored-battery",
|
||||
NULL, 0, 0, &args);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "fwnode_property_get_ref() ret %d\n", ret);
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for monitored-battery fwnode\n");
|
||||
}
|
||||
|
||||
fwnode_handle_put(args.fwnode);
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = dev;
|
||||
chip->regmap = pmic->regmap;
|
||||
|
||||
chip->vbat_channel = devm_iio_channel_get(dev, "VBAT");
|
||||
if (IS_ERR(chip->vbat_channel)) {
|
||||
dev_dbg(dev, "devm_iio_channel_get() ret %ld\n", PTR_ERR(chip->vbat_channel));
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for VBAT IIO channel\n");
|
||||
}
|
||||
|
||||
charge_finished = devm_gpiod_get_optional(dev, "charged", GPIOD_IN);
|
||||
if (IS_ERR(charge_finished))
|
||||
return dev_err_probe(dev, PTR_ERR(charge_finished), "Getting charged GPIO\n");
|
||||
|
||||
ret = dc_ti_battery_hw_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
|
||||
psy_cfg.drv_data = chip;
|
||||
chip->psy = devm_power_supply_register(dev, &dc_ti_battery_psy_desc, &psy_cfg);
|
||||
if (IS_ERR(chip->psy))
|
||||
return PTR_ERR(chip->psy);
|
||||
|
||||
return adc_battery_helper_init(&chip->helper, chip->psy,
|
||||
dc_ti_battery_get_voltage_and_current_now,
|
||||
charge_finished);
|
||||
}
|
||||
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(dc_ti_battery_pm_ops, adc_battery_helper_suspend,
|
||||
adc_battery_helper_resume, NULL);
|
||||
|
||||
static struct platform_driver dc_ti_battery_driver = {
|
||||
.driver = {
|
||||
.name = DEV_NAME,
|
||||
.pm = pm_sleep_ptr(&dc_ti_battery_pm_ops),
|
||||
},
|
||||
.probe = dc_ti_battery_probe,
|
||||
};
|
||||
module_platform_driver(dc_ti_battery_driver);
|
||||
|
||||
MODULE_ALIAS("platform:" DEV_NAME);
|
||||
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
|
||||
MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -232,7 +232,8 @@ static int micro_batt_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
mb->micro = dev_get_drvdata(pdev->dev.parent);
|
||||
mb->wq = alloc_workqueue("ipaq-battery-wq", WQ_MEM_RECLAIM, 0);
|
||||
mb->wq = alloc_workqueue("ipaq-battery-wq",
|
||||
WQ_MEM_RECLAIM | WQ_PERCPU, 0);
|
||||
if (!mb->wq)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,31 +40,30 @@ static enum power_supply_property max77705_charger_props[] = {
|
|||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
};
|
||||
|
||||
static int max77705_chgin_irq(void *irq_drv_data)
|
||||
static irqreturn_t max77705_chgin_irq(int irq, void *irq_drv_data)
|
||||
{
|
||||
struct max77705_charger_data *charger = irq_drv_data;
|
||||
struct max77705_charger_data *chg = irq_drv_data;
|
||||
|
||||
queue_work(charger->wqueue, &charger->chgin_work);
|
||||
queue_work(chg->wqueue, &chg->chgin_work);
|
||||
|
||||
return 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct regmap_irq max77705_charger_irqs[] = {
|
||||
{ .mask = MAX77705_BYP_IM, },
|
||||
{ .mask = MAX77705_INP_LIMIT_IM, },
|
||||
{ .mask = MAX77705_BATP_IM, },
|
||||
{ .mask = MAX77705_BAT_IM, },
|
||||
{ .mask = MAX77705_CHG_IM, },
|
||||
{ .mask = MAX77705_WCIN_IM, },
|
||||
{ .mask = MAX77705_CHGIN_IM, },
|
||||
{ .mask = MAX77705_AICL_IM, },
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_BYP_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_INP_LIMIT_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_BATP_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_BAT_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_CHG_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_WCIN_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_CHGIN_I, BITS_PER_BYTE),
|
||||
REGMAP_IRQ_REG_LINE(MAX77705_AICL_I, BITS_PER_BYTE),
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip max77705_charger_irq_chip = {
|
||||
.name = "max77705-charger",
|
||||
.status_base = MAX77705_CHG_REG_INT,
|
||||
.mask_base = MAX77705_CHG_REG_INT_MASK,
|
||||
.handle_post_irq = max77705_chgin_irq,
|
||||
.num_regs = 1,
|
||||
.irqs = max77705_charger_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77705_charger_irqs),
|
||||
|
|
@ -74,8 +73,7 @@ static int max77705_charger_enable(struct max77705_charger_data *chg)
|
|||
{
|
||||
int rv;
|
||||
|
||||
rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09,
|
||||
MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK);
|
||||
rv = regmap_field_write(chg->rfield[MAX77705_CHG_EN], 1);
|
||||
if (rv)
|
||||
dev_err(chg->dev, "unable to enable the charger: %d\n", rv);
|
||||
|
||||
|
|
@ -87,10 +85,7 @@ static void max77705_charger_disable(void *data)
|
|||
struct max77705_charger_data *chg = data;
|
||||
int rv;
|
||||
|
||||
rv = regmap_update_bits(chg->regmap,
|
||||
MAX77705_CHG_REG_CNFG_09,
|
||||
MAX77705_CHG_EN_MASK,
|
||||
MAX77705_CHG_DISABLE);
|
||||
rv = regmap_field_write(chg->rfield[MAX77705_CHG_EN], MAX77705_CHG_DISABLE);
|
||||
if (rv)
|
||||
dev_err(chg->dev, "unable to disable the charger: %d\n", rv);
|
||||
}
|
||||
|
|
@ -109,19 +104,30 @@ static int max77705_get_online(struct regmap *regmap, int *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_check_battery(struct max77705_charger_data *charger, int *val)
|
||||
static int max77705_set_integer(struct max77705_charger_data *chg, enum max77705_field_idx fidx,
|
||||
unsigned int clamp_min, unsigned int clamp_max,
|
||||
unsigned int div, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
regval = clamp_val(val, clamp_min, clamp_max) / div;
|
||||
|
||||
return regmap_field_write(chg->rfield[fidx], regval);
|
||||
}
|
||||
|
||||
static int max77705_check_battery(struct max77705_charger_data *chg, int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
unsigned int reg_data2;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_INT_OK, ®_data);
|
||||
|
||||
dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data);
|
||||
dev_dbg(chg->dev, "CHG_INT_OK(0x%x)\n", reg_data);
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, ®_data2);
|
||||
|
||||
dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2);
|
||||
dev_dbg(chg->dev, "CHG_DETAILS00(0x%x)\n", reg_data2);
|
||||
|
||||
if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS))
|
||||
*val = true;
|
||||
|
|
@ -131,13 +137,13 @@ static int max77705_check_battery(struct max77705_charger_data *charger, int *va
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val)
|
||||
static int max77705_get_charge_type(struct max77705_charger_data *chg, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
unsigned int reg_data;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
unsigned int reg_data, chg_en;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
|
||||
regmap_field_read(chg->rfield[MAX77705_CHG_EN], &chg_en);
|
||||
if (!chg_en) {
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -159,13 +165,13 @@ static int max77705_get_charge_type(struct max77705_charger_data *charger, int *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_status(struct max77705_charger_data *charger, int *val)
|
||||
static int max77705_get_status(struct max77705_charger_data *chg, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
unsigned int reg_data;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
unsigned int reg_data, chg_en;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
|
||||
regmap_field_read(chg->rfield[MAX77705_CHG_EN], &chg_en);
|
||||
if (!chg_en) {
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -234,10 +240,10 @@ static int max77705_get_vbus_state(struct regmap *regmap, int *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_battery_health(struct max77705_charger_data *charger,
|
||||
static int max77705_get_battery_health(struct max77705_charger_data *chg,
|
||||
int *value)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
unsigned int bat_dtls;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls);
|
||||
|
|
@ -245,16 +251,16 @@ static int max77705_get_battery_health(struct max77705_charger_data *charger,
|
|||
|
||||
switch (bat_dtls) {
|
||||
case MAX77705_BATTERY_NOBAT:
|
||||
dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n",
|
||||
dev_dbg(chg->dev, "%s: No battery and the chg is suspended\n",
|
||||
__func__);
|
||||
*value = POWER_SUPPLY_HEALTH_NO_BATTERY;
|
||||
break;
|
||||
case MAX77705_BATTERY_PREQUALIFICATION:
|
||||
dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n",
|
||||
dev_dbg(chg->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n",
|
||||
__func__);
|
||||
break;
|
||||
case MAX77705_BATTERY_DEAD:
|
||||
dev_dbg(charger->dev, "%s: battery dead\n", __func__);
|
||||
dev_dbg(chg->dev, "%s: battery dead\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_DEAD;
|
||||
break;
|
||||
case MAX77705_BATTERY_GOOD:
|
||||
|
|
@ -262,11 +268,11 @@ static int max77705_get_battery_health(struct max77705_charger_data *charger,
|
|||
*value = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case MAX77705_BATTERY_OVERVOLTAGE:
|
||||
dev_dbg(charger->dev, "%s: battery ovp\n", __func__);
|
||||
dev_dbg(chg->dev, "%s: battery ovp\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(charger->dev, "%s: battery unknown\n", __func__);
|
||||
dev_dbg(chg->dev, "%s: battery unknown\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
|
@ -274,9 +280,9 @@ static int max77705_get_battery_health(struct max77705_charger_data *charger,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_health(struct max77705_charger_data *charger, int *val)
|
||||
static int max77705_get_health(struct max77705_charger_data *chg, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
int ret, is_online = 0;
|
||||
|
||||
ret = max77705_get_online(regmap, &is_online);
|
||||
|
|
@ -287,24 +293,19 @@ static int max77705_get_health(struct max77705_charger_data *charger, int *val)
|
|||
if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD))
|
||||
return ret;
|
||||
}
|
||||
return max77705_get_battery_health(charger, val);
|
||||
return max77705_get_battery_health(chg, val);
|
||||
}
|
||||
|
||||
static int max77705_get_input_current(struct max77705_charger_data *charger,
|
||||
static int max77705_get_input_current(struct max77705_charger_data *chg,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
int get_current = 0;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
|
||||
reg_data &= MAX77705_CHG_CHGIN_LIM_MASK;
|
||||
regmap_field_read(chg->rfield[MAX77705_CHG_CHGIN_LIM], ®_data);
|
||||
|
||||
if (reg_data <= 3)
|
||||
get_current = MAX77705_CURRENT_CHGIN_MIN;
|
||||
else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK)
|
||||
get_current = MAX77705_CURRENT_CHGIN_MAX;
|
||||
else
|
||||
get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP;
|
||||
|
||||
|
|
@ -313,26 +314,23 @@ static int max77705_get_input_current(struct max77705_charger_data *charger,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_charge_current(struct max77705_charger_data *charger,
|
||||
static int max77705_get_charge_current(struct max77705_charger_data *chg,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, ®_data);
|
||||
reg_data &= MAX77705_CHG_CC;
|
||||
regmap_field_read(chg->rfield[MAX77705_CHG_CC_LIM], ®_data);
|
||||
|
||||
*val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_set_float_voltage(struct max77705_charger_data *charger,
|
||||
static int max77705_set_float_voltage(struct max77705_charger_data *chg,
|
||||
int float_voltage)
|
||||
{
|
||||
int float_voltage_mv;
|
||||
unsigned int reg_data = 0;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
float_voltage_mv = float_voltage / 1000;
|
||||
reg_data = float_voltage_mv <= 4000 ? 0x0 :
|
||||
|
|
@ -340,20 +338,16 @@ static int max77705_set_float_voltage(struct max77705_charger_data *charger,
|
|||
(float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 :
|
||||
(((float_voltage_mv - 4200) / 10) + 0x04);
|
||||
|
||||
return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04,
|
||||
MAX77705_CHG_CV_PRM_MASK,
|
||||
(reg_data << MAX77705_CHG_CV_PRM_SHIFT));
|
||||
return regmap_field_write(chg->rfield[MAX77705_CHG_CV_PRM], reg_data);
|
||||
}
|
||||
|
||||
static int max77705_get_float_voltage(struct max77705_charger_data *charger,
|
||||
static int max77705_get_float_voltage(struct max77705_charger_data *chg,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data = 0;
|
||||
int voltage_mv;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, ®_data);
|
||||
reg_data &= MAX77705_CHG_PRM_MASK;
|
||||
regmap_field_read(chg->rfield[MAX77705_CHG_CV_PRM], ®_data);
|
||||
voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 :
|
||||
(reg_data - 4) * 10 + 4200;
|
||||
*val = voltage_mv * 1000;
|
||||
|
|
@ -365,28 +359,28 @@ static int max77705_chg_get_property(struct power_supply *psy,
|
|||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max77705_charger_data *charger = power_supply_get_drvdata(psy);
|
||||
struct regmap *regmap = charger->regmap;
|
||||
struct max77705_charger_data *chg = power_supply_get_drvdata(psy);
|
||||
struct regmap *regmap = chg->regmap;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return max77705_get_online(regmap, &val->intval);
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
return max77705_check_battery(charger, &val->intval);
|
||||
return max77705_check_battery(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
return max77705_get_status(charger, &val->intval);
|
||||
return max77705_get_status(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
return max77705_get_charge_type(charger, &val->intval);
|
||||
return max77705_get_charge_type(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
return max77705_get_health(charger, &val->intval);
|
||||
return max77705_get_health(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return max77705_get_input_current(charger, &val->intval);
|
||||
return max77705_get_input_current(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return max77705_get_charge_current(charger, &val->intval);
|
||||
return max77705_get_charge_current(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
return max77705_get_float_voltage(charger, &val->intval);
|
||||
return max77705_get_float_voltage(chg, &val->intval);
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = charger->bat_info->voltage_max_design_uv;
|
||||
val->intval = chg->bat_info->voltage_max_design_uv;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = max77705_charger_model;
|
||||
|
|
@ -400,74 +394,131 @@ static int max77705_chg_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct max77705_charger_data *chg = power_supply_get_drvdata(psy);
|
||||
int err = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
err = max77705_set_integer(chg, MAX77705_CHG_CC_LIM,
|
||||
MAX77705_CURRENT_CHGIN_MIN,
|
||||
MAX77705_CURRENT_CHGIN_MAX,
|
||||
MAX77705_CURRENT_CHG_STEP,
|
||||
val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
err = max77705_set_integer(chg, MAX77705_CHG_CHGIN_LIM,
|
||||
MAX77705_CURRENT_CHGIN_MIN,
|
||||
MAX77705_CURRENT_CHGIN_MAX,
|
||||
MAX77705_CURRENT_CHGIN_STEP,
|
||||
val->intval);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
static int max77705_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct power_supply_desc max77705_charger_psy_desc = {
|
||||
.name = "max77705-charger",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = max77705_charger_props,
|
||||
.property_is_writeable = max77705_property_is_writeable,
|
||||
.num_properties = ARRAY_SIZE(max77705_charger_props),
|
||||
.get_property = max77705_chg_get_property,
|
||||
.set_property = max77705_set_property,
|
||||
};
|
||||
|
||||
static void max77705_chgin_isr_work(struct work_struct *work)
|
||||
{
|
||||
struct max77705_charger_data *charger =
|
||||
struct max77705_charger_data *chg =
|
||||
container_of(work, struct max77705_charger_data, chgin_work);
|
||||
|
||||
power_supply_changed(charger->psy_chg);
|
||||
power_supply_changed(chg->psy_chg);
|
||||
}
|
||||
|
||||
static void max77705_charger_initialize(struct max77705_charger_data *chg)
|
||||
static int max77705_charger_initialize(struct max77705_charger_data *chg)
|
||||
{
|
||||
u8 reg_data;
|
||||
struct power_supply_battery_info *info;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
int err;
|
||||
|
||||
if (power_supply_get_battery_info(chg->psy_chg, &info) < 0)
|
||||
return;
|
||||
err = power_supply_get_battery_info(chg->psy_chg, &info);
|
||||
if (err)
|
||||
return dev_err_probe(chg->dev, err, "error on getting battery info");
|
||||
|
||||
chg->bat_info = info;
|
||||
|
||||
/* unlock charger setting protect */
|
||||
/* slowest LX slope */
|
||||
reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE;
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data,
|
||||
reg_data);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_CHGPROT], MAX77705_CHGPROT_UNLOCKED);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_LX_SLOPE], MAX77705_SLOWEST_LX_SLOPE);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* fast charge timer disable */
|
||||
/* restart threshold disable */
|
||||
/* pre-qual charge disable */
|
||||
reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) |
|
||||
(MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) |
|
||||
(MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01,
|
||||
(MAX77705_FCHGTIME_MASK |
|
||||
MAX77705_CHG_RSTRT_MASK |
|
||||
MAX77705_PQEN_MASK),
|
||||
reg_data);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_FCHGTIME], MAX77705_FCHGTIME_DISABLE);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* OTG off(UNO on), boost off */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
|
||||
MAX77705_OTG_CTRL, 0);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_CHG_RSTRT], MAX77705_CHG_RSTRT_DISABLE);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_CHG_PQEN], MAX77705_CHG_PQEN_DISABLE);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_MODE],
|
||||
MAX77705_CHG_MASK | MAX77705_BUCK_MASK);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* charge current 450mA(default) */
|
||||
/* otg current limit 900mA */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02,
|
||||
MAX77705_OTG_ILIM_MASK,
|
||||
MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_OTG_ILIM], MAX77705_OTG_ILIM_900);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* BAT to SYS OCP 4.80A */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05,
|
||||
MAX77705_REG_B2SOVRC_MASK,
|
||||
MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_REG_B2SOVRC], MAX77705_B2SOVRC_4_8A);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* top off current 150mA */
|
||||
/* top off timer 30min */
|
||||
reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) |
|
||||
(MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) |
|
||||
(MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03,
|
||||
(MAX77705_TO_ITH_MASK |
|
||||
MAX77705_TO_TIME_MASK |
|
||||
MAX77705_SYS_TRACK_DIS_MASK), reg_data);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_TO], MAX77705_TO_ITH_150MA);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_TO_TIME], MAX77705_TO_TIME_30M);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_SYS_TRACK], MAX77705_SYS_TRACK_DISABLE);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* cv voltage 4.2V or 4.35V */
|
||||
/* MINVSYS 3.6V(default) */
|
||||
|
|
@ -478,28 +529,38 @@ static void max77705_charger_initialize(struct max77705_charger_data *chg)
|
|||
max77705_set_float_voltage(chg, info->voltage_max_design_uv);
|
||||
}
|
||||
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
|
||||
MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
|
||||
MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_VCHGIN], MAX77705_VCHGIN_4_5);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = regmap_field_write(chg->rfield[MAX77705_WCIN], MAX77705_WCIN_4_5);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* Watchdog timer */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
|
||||
MAX77705_WDTEN_MASK, 0);
|
||||
|
||||
/* Active Discharge Enable */
|
||||
regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
|
||||
|
||||
/* VBYPSET=5.0V */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0);
|
||||
err = regmap_field_write(chg->rfield[MAX77705_VBYPSET], 0);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* Switching Frequency : 1.5MHz */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK,
|
||||
(MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT));
|
||||
err = regmap_field_write(chg->rfield[MAX77705_REG_FSW], MAX77705_CHG_FSW_1_5MHz);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* Auto skip mode */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK,
|
||||
(MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT));
|
||||
err = regmap_field_write(chg->rfield[MAX77705_REG_DISKIP], MAX77705_AUTO_SKIP);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return dev_err_probe(chg->dev, err, "error while configuring");
|
||||
|
||||
}
|
||||
|
||||
static int max77705_charger_probe(struct i2c_client *i2c)
|
||||
|
|
@ -523,11 +584,13 @@ static int max77705_charger_probe(struct i2c_client *i2c)
|
|||
if (IS_ERR(chg->regmap))
|
||||
return PTR_ERR(chg->regmap);
|
||||
|
||||
ret = regmap_update_bits(chg->regmap,
|
||||
MAX77705_CHG_REG_INT_MASK,
|
||||
MAX77705_CHGIN_IM, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (int i = 0; i < MAX77705_N_REGMAP_FIELDS; i++) {
|
||||
chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
|
||||
max77705_reg_field[i]);
|
||||
if (IS_ERR(chg->rfield[i]))
|
||||
return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
|
||||
"cannot allocate regmap field\n");
|
||||
}
|
||||
|
||||
pscfg.fwnode = dev_fwnode(dev);
|
||||
pscfg.drv_data = chg;
|
||||
|
|
@ -538,7 +601,7 @@ static int max77705_charger_probe(struct i2c_client *i2c)
|
|||
|
||||
max77705_charger_irq_chip.irq_drv_data = chg;
|
||||
ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED, 0,
|
||||
IRQF_ONESHOT, 0,
|
||||
&max77705_charger_irq_chip,
|
||||
&irq_data);
|
||||
if (ret)
|
||||
|
|
@ -546,7 +609,7 @@ static int max77705_charger_probe(struct i2c_client *i2c)
|
|||
|
||||
chg->wqueue = create_singlethread_workqueue(dev_name(dev));
|
||||
if (!chg->wqueue)
|
||||
return dev_err_probe(dev, -ENOMEM, "failed to create workqueue\n");
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work);
|
||||
if (ret) {
|
||||
|
|
@ -554,7 +617,20 @@ static int max77705_charger_probe(struct i2c_client *i2c)
|
|||
goto destroy_wq;
|
||||
}
|
||||
|
||||
max77705_charger_initialize(chg);
|
||||
ret = max77705_charger_initialize(chg);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to initialize charger IC\n");
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, regmap_irq_get_virq(irq_data, MAX77705_CHGIN_I),
|
||||
NULL, max77705_chgin_irq,
|
||||
IRQF_TRIGGER_NONE,
|
||||
"chgin-irq", chg);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to Request chgin IRQ\n");
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
ret = max77705_charger_enable(chg);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -292,10 +292,10 @@ static int max77976_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
err = max77976_get_online(chg, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
val->intval = MAX77976_CHG_CC_MAX;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
err = max77976_get_integer(chg, CHG_CC,
|
||||
MAX77976_CHG_CC_MIN,
|
||||
MAX77976_CHG_CC_MAX,
|
||||
|
|
@ -330,7 +330,7 @@ static int max77976_set_property(struct power_supply *psy,
|
|||
int err = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
err = max77976_set_integer(chg, CHG_CC,
|
||||
MAX77976_CHG_CC_MIN,
|
||||
MAX77976_CHG_CC_MAX,
|
||||
|
|
@ -355,7 +355,7 @@ static int max77976_property_is_writeable(struct power_supply *psy,
|
|||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
|
|
@ -368,8 +368,8 @@ static enum power_supply_property max77976_psy_props[] = {
|
|||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
|
|
|
|||
|
|
@ -761,13 +761,6 @@ static int mt6370_chg_init_psy(struct mt6370_priv *priv)
|
|||
return PTR_ERR_OR_ZERO(priv->psy);
|
||||
}
|
||||
|
||||
static void mt6370_chg_destroy_attach_lock(void *data)
|
||||
{
|
||||
struct mutex *attach_lock = data;
|
||||
|
||||
mutex_destroy(attach_lock);
|
||||
}
|
||||
|
||||
static void mt6370_chg_destroy_wq(void *data)
|
||||
{
|
||||
struct workqueue_struct *wq = data;
|
||||
|
|
@ -894,22 +887,19 @@ static int mt6370_chg_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init psy\n");
|
||||
|
||||
mutex_init(&priv->attach_lock);
|
||||
ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_attach_lock,
|
||||
&priv->attach_lock);
|
||||
ret = devm_mutex_init(dev, &priv->attach_lock);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init attach lock\n");
|
||||
return ret;
|
||||
|
||||
priv->attach = MT6370_ATTACH_STAT_DETACH;
|
||||
|
||||
priv->wq = create_singlethread_workqueue(dev_name(priv->dev));
|
||||
if (!priv->wq)
|
||||
return dev_err_probe(dev, -ENOMEM,
|
||||
"Failed to create workqueue\n");
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_wq, priv->wq);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init wq\n");
|
||||
return ret;
|
||||
|
||||
ret = devm_work_autocancel(dev, &priv->bc12_work, mt6370_chg_bc12_work_func);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -223,6 +223,8 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = {
|
|||
POWER_SUPPLY_ATTR(MANUFACTURE_YEAR),
|
||||
POWER_SUPPLY_ATTR(MANUFACTURE_MONTH),
|
||||
POWER_SUPPLY_ATTR(MANUFACTURE_DAY),
|
||||
POWER_SUPPLY_ATTR(INTERNAL_RESISTANCE),
|
||||
POWER_SUPPLY_ATTR(STATE_OF_HEALTH),
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_ATTR(MODEL_NAME),
|
||||
POWER_SUPPLY_ATTR(MANUFACTURER),
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022, Linaro Ltd
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/property.h>
|
||||
|
|
@ -18,8 +20,10 @@
|
|||
#define BATTMGR_STRING_LEN 128
|
||||
|
||||
enum qcom_battmgr_variant {
|
||||
QCOM_BATTMGR_SM8350,
|
||||
QCOM_BATTMGR_SC8280XP,
|
||||
QCOM_BATTMGR_SM8350,
|
||||
QCOM_BATTMGR_SM8550,
|
||||
QCOM_BATTMGR_X1E80100,
|
||||
};
|
||||
|
||||
#define BATTMGR_BAT_STATUS 0x1
|
||||
|
|
@ -30,8 +34,9 @@ enum qcom_battmgr_variant {
|
|||
#define NOTIF_BAT_PROPERTY 0x30
|
||||
#define NOTIF_USB_PROPERTY 0x32
|
||||
#define NOTIF_WLS_PROPERTY 0x34
|
||||
#define NOTIF_BAT_INFO 0x81
|
||||
#define NOTIF_BAT_STATUS 0x80
|
||||
#define NOTIF_BAT_INFO 0x81
|
||||
#define NOTIF_BAT_CHARGING_STATE 0x83
|
||||
|
||||
#define BATTMGR_BAT_INFO 0x9
|
||||
|
||||
|
|
@ -65,6 +70,9 @@ enum qcom_battmgr_variant {
|
|||
#define BATT_RESISTANCE 21
|
||||
#define BATT_POWER_NOW 22
|
||||
#define BATT_POWER_AVG 23
|
||||
#define BATT_CHG_CTRL_EN 24
|
||||
#define BATT_CHG_CTRL_START_THR 25
|
||||
#define BATT_CHG_CTRL_END_THR 26
|
||||
|
||||
#define BATTMGR_USB_PROPERTY_GET 0x32
|
||||
#define BATTMGR_USB_PROPERTY_SET 0x33
|
||||
|
|
@ -89,6 +97,13 @@ enum qcom_battmgr_variant {
|
|||
#define WLS_TYPE 5
|
||||
#define WLS_BOOST_EN 6
|
||||
|
||||
#define BATTMGR_CHG_CTRL_LIMIT_EN 0x48
|
||||
#define CHARGE_CTRL_START_THR_MIN 50
|
||||
#define CHARGE_CTRL_START_THR_MAX 95
|
||||
#define CHARGE_CTRL_END_THR_MIN 55
|
||||
#define CHARGE_CTRL_END_THR_MAX 100
|
||||
#define CHARGE_CTRL_DELTA_SOC 5
|
||||
|
||||
struct qcom_battmgr_enable_request {
|
||||
struct pmic_glink_hdr hdr;
|
||||
__le32 battery_id;
|
||||
|
|
@ -123,6 +138,13 @@ struct qcom_battmgr_discharge_time_request {
|
|||
__le32 reserved;
|
||||
};
|
||||
|
||||
struct qcom_battmgr_charge_ctrl_request {
|
||||
struct pmic_glink_hdr hdr;
|
||||
__le32 enable;
|
||||
__le32 target_soc;
|
||||
__le32 delta_soc;
|
||||
};
|
||||
|
||||
struct qcom_battmgr_message {
|
||||
struct pmic_glink_hdr hdr;
|
||||
union {
|
||||
|
|
@ -235,6 +257,8 @@ struct qcom_battmgr_info {
|
|||
unsigned int capacity_warning;
|
||||
unsigned int cycle_count;
|
||||
unsigned int charge_count;
|
||||
unsigned int charge_ctrl_start;
|
||||
unsigned int charge_ctrl_end;
|
||||
char model_number[BATTMGR_STRING_LEN];
|
||||
char serial_number[BATTMGR_STRING_LEN];
|
||||
char oem_info[BATTMGR_STRING_LEN];
|
||||
|
|
@ -254,6 +278,8 @@ struct qcom_battmgr_status {
|
|||
unsigned int voltage_now;
|
||||
unsigned int voltage_ocv;
|
||||
unsigned int temperature;
|
||||
unsigned int resistance;
|
||||
unsigned int soh_percent;
|
||||
|
||||
unsigned int discharge_time;
|
||||
unsigned int charge_time;
|
||||
|
|
@ -418,7 +444,11 @@ static const u8 sm8350_bat_prop_map[] = {
|
|||
[POWER_SUPPLY_PROP_MODEL_NAME] = BATT_MODEL_NAME,
|
||||
[POWER_SUPPLY_PROP_TIME_TO_FULL_AVG] = BATT_TTF_AVG,
|
||||
[POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG] = BATT_TTE_AVG,
|
||||
[POWER_SUPPLY_PROP_INTERNAL_RESISTANCE] = BATT_RESISTANCE,
|
||||
[POWER_SUPPLY_PROP_STATE_OF_HEALTH] = BATT_SOH,
|
||||
[POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW,
|
||||
[POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD] = BATT_CHG_CTRL_START_THR,
|
||||
[POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD] = BATT_CHG_CTRL_END_THR,
|
||||
};
|
||||
|
||||
static int qcom_battmgr_bat_sm8350_update(struct qcom_battmgr *battmgr,
|
||||
|
|
@ -489,7 +519,8 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy,
|
|||
if (!battmgr->service_up)
|
||||
return -EAGAIN;
|
||||
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP ||
|
||||
battmgr->variant == QCOM_BATTMGR_X1E80100)
|
||||
ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
|
||||
else
|
||||
ret = qcom_battmgr_bat_sm8350_update(battmgr, psp);
|
||||
|
|
@ -584,12 +615,24 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_TEMP:
|
||||
val->intval = battmgr->status.temperature;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INTERNAL_RESISTANCE:
|
||||
val->intval = battmgr->status.resistance;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATE_OF_HEALTH:
|
||||
val->intval = battmgr->status.soh_percent;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||
val->intval = battmgr->status.discharge_time;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
|
||||
val->intval = battmgr->status.charge_time;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
|
||||
val->intval = battmgr->info.charge_ctrl_start;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
val->intval = battmgr->info.charge_ctrl_end;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
|
||||
val->intval = battmgr->info.year;
|
||||
break;
|
||||
|
|
@ -615,6 +658,149 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_battmgr_set_charge_control(struct qcom_battmgr *battmgr,
|
||||
u32 target_soc, u32 delta_soc)
|
||||
{
|
||||
struct qcom_battmgr_charge_ctrl_request request = {
|
||||
.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
|
||||
.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
|
||||
.hdr.opcode = cpu_to_le32(BATTMGR_CHG_CTRL_LIMIT_EN),
|
||||
.enable = cpu_to_le32(1),
|
||||
.target_soc = cpu_to_le32(target_soc),
|
||||
.delta_soc = cpu_to_le32(delta_soc),
|
||||
};
|
||||
|
||||
return qcom_battmgr_request(battmgr, &request, sizeof(request));
|
||||
}
|
||||
|
||||
static int qcom_battmgr_set_charge_start_threshold(struct qcom_battmgr *battmgr, int start_soc)
|
||||
{
|
||||
u32 target_soc, delta_soc;
|
||||
int ret;
|
||||
|
||||
if (start_soc < CHARGE_CTRL_START_THR_MIN ||
|
||||
start_soc > CHARGE_CTRL_START_THR_MAX) {
|
||||
dev_err(battmgr->dev, "charge control start threshold exceed range: [%u - %u]\n",
|
||||
CHARGE_CTRL_START_THR_MIN, CHARGE_CTRL_START_THR_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the new start threshold is larger than the old end threshold,
|
||||
* move the end threshold one step (DELTA_SOC) after the new start
|
||||
* threshold.
|
||||
*/
|
||||
if (start_soc > battmgr->info.charge_ctrl_end) {
|
||||
target_soc = start_soc + CHARGE_CTRL_DELTA_SOC;
|
||||
target_soc = min_t(u32, target_soc, CHARGE_CTRL_END_THR_MAX);
|
||||
delta_soc = target_soc - start_soc;
|
||||
delta_soc = min_t(u32, delta_soc, CHARGE_CTRL_DELTA_SOC);
|
||||
} else {
|
||||
target_soc = battmgr->info.charge_ctrl_end;
|
||||
delta_soc = battmgr->info.charge_ctrl_end - start_soc;
|
||||
}
|
||||
|
||||
mutex_lock(&battmgr->lock);
|
||||
ret = qcom_battmgr_set_charge_control(battmgr, target_soc, delta_soc);
|
||||
mutex_unlock(&battmgr->lock);
|
||||
if (!ret) {
|
||||
battmgr->info.charge_ctrl_start = start_soc;
|
||||
battmgr->info.charge_ctrl_end = target_soc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_battmgr_set_charge_end_threshold(struct qcom_battmgr *battmgr, int end_soc)
|
||||
{
|
||||
u32 delta_soc = CHARGE_CTRL_DELTA_SOC;
|
||||
int ret;
|
||||
|
||||
if (end_soc < CHARGE_CTRL_END_THR_MIN ||
|
||||
end_soc > CHARGE_CTRL_END_THR_MAX) {
|
||||
dev_err(battmgr->dev, "charge control end threshold exceed range: [%u - %u]\n",
|
||||
CHARGE_CTRL_END_THR_MIN, CHARGE_CTRL_END_THR_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (battmgr->info.charge_ctrl_start && end_soc > battmgr->info.charge_ctrl_start)
|
||||
delta_soc = end_soc - battmgr->info.charge_ctrl_start;
|
||||
|
||||
mutex_lock(&battmgr->lock);
|
||||
ret = qcom_battmgr_set_charge_control(battmgr, end_soc, delta_soc);
|
||||
mutex_unlock(&battmgr->lock);
|
||||
if (!ret) {
|
||||
battmgr->info.charge_ctrl_start = end_soc - delta_soc;
|
||||
battmgr->info.charge_ctrl_end = end_soc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_battmgr_charge_control_thresholds_init(struct qcom_battmgr *battmgr)
|
||||
{
|
||||
int ret;
|
||||
u8 en, end_soc, start_soc, delta_soc;
|
||||
|
||||
ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_en", &en);
|
||||
if (!ret && en != 0) {
|
||||
ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_end", &end_soc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_u8(battmgr->dev->parent, "charge_limit_delta", &delta_soc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (delta_soc >= end_soc)
|
||||
return -EINVAL;
|
||||
|
||||
start_soc = end_soc - delta_soc;
|
||||
end_soc = clamp(end_soc, CHARGE_CTRL_END_THR_MIN, CHARGE_CTRL_END_THR_MAX);
|
||||
start_soc = clamp(start_soc, CHARGE_CTRL_START_THR_MIN, CHARGE_CTRL_START_THR_MAX);
|
||||
|
||||
battmgr->info.charge_ctrl_start = start_soc;
|
||||
battmgr->info.charge_ctrl_end = end_soc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_battmgr_bat_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_battmgr_bat_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *pval)
|
||||
{
|
||||
struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
|
||||
|
||||
if (!battmgr->service_up)
|
||||
return -EAGAIN;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
|
||||
return qcom_battmgr_set_charge_start_threshold(battmgr, pval->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
|
||||
return qcom_battmgr_set_charge_end_threshold(battmgr, pval->intval);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const enum power_supply_property sc8280xp_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
|
|
@ -649,6 +835,43 @@ static const struct power_supply_desc sc8280xp_bat_psy_desc = {
|
|||
.get_property = qcom_battmgr_bat_get_property,
|
||||
};
|
||||
|
||||
static const enum power_supply_property x1e80100_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_EMPTY,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_ENERGY_FULL,
|
||||
POWER_SUPPLY_PROP_ENERGY_EMPTY,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
|
||||
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc x1e80100_bat_psy_desc = {
|
||||
.name = "qcom-battmgr-bat",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = x1e80100_bat_props,
|
||||
.num_properties = ARRAY_SIZE(x1e80100_bat_props),
|
||||
.get_property = qcom_battmgr_bat_get_property,
|
||||
.set_property = qcom_battmgr_bat_set_property,
|
||||
.property_is_writeable = qcom_battmgr_bat_is_writeable,
|
||||
};
|
||||
|
||||
static const enum power_supply_property sm8350_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
|
|
@ -668,6 +891,8 @@ static const enum power_supply_property sm8350_bat_props[] = {
|
|||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||
POWER_SUPPLY_PROP_INTERNAL_RESISTANCE,
|
||||
POWER_SUPPLY_PROP_STATE_OF_HEALTH,
|
||||
POWER_SUPPLY_PROP_POWER_NOW,
|
||||
};
|
||||
|
||||
|
|
@ -679,6 +904,42 @@ static const struct power_supply_desc sm8350_bat_psy_desc = {
|
|||
.get_property = qcom_battmgr_bat_get_property,
|
||||
};
|
||||
|
||||
static const enum power_supply_property sm8550_bat_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||
POWER_SUPPLY_PROP_INTERNAL_RESISTANCE,
|
||||
POWER_SUPPLY_PROP_STATE_OF_HEALTH,
|
||||
POWER_SUPPLY_PROP_POWER_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc sm8550_bat_psy_desc = {
|
||||
.name = "qcom-battmgr-bat",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = sm8550_bat_props,
|
||||
.num_properties = ARRAY_SIZE(sm8550_bat_props),
|
||||
.get_property = qcom_battmgr_bat_get_property,
|
||||
.set_property = qcom_battmgr_bat_set_property,
|
||||
.property_is_writeable = qcom_battmgr_bat_is_writeable,
|
||||
};
|
||||
|
||||
static int qcom_battmgr_ac_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
|
@ -754,7 +1015,8 @@ static int qcom_battmgr_usb_get_property(struct power_supply *psy,
|
|||
if (!battmgr->service_up)
|
||||
return -EAGAIN;
|
||||
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP ||
|
||||
battmgr->variant == QCOM_BATTMGR_X1E80100)
|
||||
ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
|
||||
else
|
||||
ret = qcom_battmgr_usb_sm8350_update(battmgr, psp);
|
||||
|
|
@ -876,7 +1138,8 @@ static int qcom_battmgr_wls_get_property(struct power_supply *psy,
|
|||
if (!battmgr->service_up)
|
||||
return -EAGAIN;
|
||||
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP ||
|
||||
battmgr->variant == QCOM_BATTMGR_X1E80100)
|
||||
ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
|
||||
else
|
||||
ret = qcom_battmgr_wls_sm8350_update(battmgr, psp);
|
||||
|
|
@ -947,12 +1210,14 @@ static void qcom_battmgr_notification(struct qcom_battmgr *battmgr,
|
|||
}
|
||||
|
||||
notification = le32_to_cpu(msg->notification);
|
||||
notification &= 0xff;
|
||||
switch (notification) {
|
||||
case NOTIF_BAT_INFO:
|
||||
battmgr->info.valid = false;
|
||||
fallthrough;
|
||||
case NOTIF_BAT_STATUS:
|
||||
case NOTIF_BAT_PROPERTY:
|
||||
case NOTIF_BAT_CHARGING_STATE:
|
||||
power_supply_changed(battmgr->bat_psy);
|
||||
break;
|
||||
case NOTIF_USB_PROPERTY:
|
||||
|
|
@ -982,7 +1247,8 @@ static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src)
|
|||
|
||||
static unsigned int qcom_battmgr_sc8280xp_parse_technology(const char *chemistry)
|
||||
{
|
||||
if (!strncmp(chemistry, "LIO", BATTMGR_CHEMISTRY_LEN))
|
||||
if ((!strncmp(chemistry, "LIO", BATTMGR_CHEMISTRY_LEN)) ||
|
||||
(!strncmp(chemistry, "OOI", BATTMGR_CHEMISTRY_LEN)))
|
||||
return POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
if (!strncmp(chemistry, "LIP", BATTMGR_CHEMISTRY_LEN))
|
||||
return POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
|
|
@ -1095,6 +1361,9 @@ static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
|
|||
case BATTMGR_BAT_CHARGE_TIME:
|
||||
battmgr->status.charge_time = le32_to_cpu(resp->time);
|
||||
break;
|
||||
case BATTMGR_CHG_CTRL_LIMIT_EN:
|
||||
battmgr->error = 0;
|
||||
break;
|
||||
default:
|
||||
dev_warn(battmgr->dev, "unknown message %#x\n", opcode);
|
||||
break;
|
||||
|
|
@ -1159,6 +1428,9 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
|
|||
case BATT_CAPACITY:
|
||||
battmgr->status.percent = le32_to_cpu(resp->intval.value) / 100;
|
||||
break;
|
||||
case BATT_SOH:
|
||||
battmgr->status.soh_percent = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case BATT_VOLT_OCV:
|
||||
battmgr->status.voltage_ocv = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
|
|
@ -1199,9 +1471,18 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
|
|||
case BATT_TTE_AVG:
|
||||
battmgr->status.discharge_time = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case BATT_RESISTANCE:
|
||||
battmgr->status.resistance = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case BATT_POWER_NOW:
|
||||
battmgr->status.power_now = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case BATT_CHG_CTRL_START_THR:
|
||||
battmgr->info.charge_ctrl_start = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case BATT_CHG_CTRL_END_THR:
|
||||
battmgr->info.charge_ctrl_end = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
default:
|
||||
dev_warn(battmgr->dev, "unknown property %#x\n", property);
|
||||
break;
|
||||
|
|
@ -1284,6 +1565,7 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
|
|||
}
|
||||
break;
|
||||
case BATTMGR_REQUEST_NOTIFICATION:
|
||||
case BATTMGR_CHG_CTRL_LIMIT_EN:
|
||||
battmgr->error = 0;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1303,7 +1585,8 @@ static void qcom_battmgr_callback(const void *data, size_t len, void *priv)
|
|||
|
||||
if (opcode == BATTMGR_NOTIFICATION)
|
||||
qcom_battmgr_notification(battmgr, data, len);
|
||||
else if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
|
||||
else if (battmgr->variant == QCOM_BATTMGR_SC8280XP ||
|
||||
battmgr->variant == QCOM_BATTMGR_X1E80100)
|
||||
qcom_battmgr_sc8280xp_callback(battmgr, data, len);
|
||||
else
|
||||
qcom_battmgr_sm8350_callback(battmgr, data, len);
|
||||
|
|
@ -1339,7 +1622,8 @@ static void qcom_battmgr_pdr_notify(void *priv, int state)
|
|||
static const struct of_device_id qcom_battmgr_of_variants[] = {
|
||||
{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
|
||||
{ .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
|
||||
{ .compatible = "qcom,x1e80100-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
|
||||
{ .compatible = "qcom,sm8550-pmic-glink", .data = (void *)QCOM_BATTMGR_SM8550 },
|
||||
{ .compatible = "qcom,x1e80100-pmic-glink", .data = (void *)QCOM_BATTMGR_X1E80100 },
|
||||
/* Unmatched devices falls back to QCOM_BATTMGR_SM8350 */
|
||||
{}
|
||||
};
|
||||
|
|
@ -1349,11 +1633,13 @@ static char *qcom_battmgr_battery[] = { "battery" };
|
|||
static int qcom_battmgr_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
const struct power_supply_desc *psy_desc;
|
||||
struct power_supply_config psy_cfg_supply = {};
|
||||
struct power_supply_config psy_cfg = {};
|
||||
const struct of_device_id *match;
|
||||
struct qcom_battmgr *battmgr;
|
||||
struct device *dev = &adev->dev;
|
||||
int ret;
|
||||
|
||||
battmgr = devm_kzalloc(dev, sizeof(*battmgr), GFP_KERNEL);
|
||||
if (!battmgr)
|
||||
|
|
@ -1379,8 +1665,19 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
|
|||
else
|
||||
battmgr->variant = QCOM_BATTMGR_SM8350;
|
||||
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP) {
|
||||
battmgr->bat_psy = devm_power_supply_register(dev, &sc8280xp_bat_psy_desc, &psy_cfg);
|
||||
ret = qcom_battmgr_charge_control_thresholds_init(battmgr);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to init battery charge control thresholds\n");
|
||||
|
||||
if (battmgr->variant == QCOM_BATTMGR_SC8280XP ||
|
||||
battmgr->variant == QCOM_BATTMGR_X1E80100) {
|
||||
if (battmgr->variant == QCOM_BATTMGR_X1E80100)
|
||||
psy_desc = &x1e80100_bat_psy_desc;
|
||||
else
|
||||
psy_desc = &sc8280xp_bat_psy_desc;
|
||||
|
||||
battmgr->bat_psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(battmgr->bat_psy))
|
||||
return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
|
||||
"failed to register battery power supply\n");
|
||||
|
|
@ -1400,7 +1697,12 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
|
|||
return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
|
||||
"failed to register wireless charing power supply\n");
|
||||
} else {
|
||||
battmgr->bat_psy = devm_power_supply_register(dev, &sm8350_bat_psy_desc, &psy_cfg);
|
||||
if (battmgr->variant == QCOM_BATTMGR_SM8550)
|
||||
psy_desc = &sm8550_bat_psy_desc;
|
||||
else
|
||||
psy_desc = &sm8350_bat_psy_desc;
|
||||
|
||||
battmgr->bat_psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(battmgr->bat_psy))
|
||||
return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
|
||||
"failed to register battery power supply\n");
|
||||
|
|
|
|||
|
|
@ -1046,7 +1046,7 @@ static void rk817_charging_monitor(struct work_struct *work)
|
|||
rk817_read_props(charger);
|
||||
|
||||
/* Run every 8 seconds like the BSP driver did. */
|
||||
queue_delayed_work(system_wq, &charger->work, msecs_to_jiffies(8000));
|
||||
queue_delayed_work(system_percpu_wq, &charger->work, msecs_to_jiffies(8000));
|
||||
}
|
||||
|
||||
static void rk817_cleanup_node(void *data)
|
||||
|
|
@ -1206,7 +1206,7 @@ static int rk817_charger_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
/* Force the first update immediately. */
|
||||
mod_delayed_work(system_wq, &charger->work, 0);
|
||||
mod_delayed_work(system_percpu_wq, &charger->work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1226,7 +1226,7 @@ static int __maybe_unused rk817_resume(struct device *dev)
|
|||
struct rk817_charger *charger = dev_get_drvdata(dev);
|
||||
|
||||
/* force an immediate update */
|
||||
mod_delayed_work(system_wq, &charger->work, 0);
|
||||
mod_delayed_work(system_percpu_wq, &charger->work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -633,7 +633,9 @@ out:
|
|||
static const enum power_supply_property rt9467_chg_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
|
||||
|
|
@ -656,6 +658,8 @@ static int rt9467_psy_get_property(struct power_supply *psy,
|
|||
return rt9467_psy_get_status(data, &val->intval);
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return regmap_field_read(data->rm_field[F_PWR_RDY], &val->intval);
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
return rt9467_get_adc(data, RT9467_ADC_VBUS_DIV5, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
mutex_lock(&data->attach_lock);
|
||||
if (data->psy_usb_type == POWER_SUPPLY_USB_TYPE_UNKNOWN ||
|
||||
|
|
@ -665,6 +669,8 @@ static int rt9467_psy_get_property(struct power_supply *psy,
|
|||
val->intval = 1500000;
|
||||
mutex_unlock(&data->attach_lock);
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
return rt9467_get_adc(data, RT9467_ADC_IBUS, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
mutex_lock(&data->ichg_ieoc_lock);
|
||||
val->intval = data->ichg_ua;
|
||||
|
|
@ -1141,27 +1147,6 @@ static int rt9467_reset_chip(struct rt9467_chg_data *data)
|
|||
return regmap_field_write(data->rm_field[F_RST], 1);
|
||||
}
|
||||
|
||||
static void rt9467_chg_destroy_adc_lock(void *data)
|
||||
{
|
||||
struct mutex *adc_lock = data;
|
||||
|
||||
mutex_destroy(adc_lock);
|
||||
}
|
||||
|
||||
static void rt9467_chg_destroy_attach_lock(void *data)
|
||||
{
|
||||
struct mutex *attach_lock = data;
|
||||
|
||||
mutex_destroy(attach_lock);
|
||||
}
|
||||
|
||||
static void rt9467_chg_destroy_ichg_ieoc_lock(void *data)
|
||||
{
|
||||
struct mutex *ichg_ieoc_lock = data;
|
||||
|
||||
mutex_destroy(ichg_ieoc_lock);
|
||||
}
|
||||
|
||||
static void rt9467_chg_complete_aicl_done(void *data)
|
||||
{
|
||||
struct completion *aicl_done = data;
|
||||
|
|
@ -1214,29 +1199,23 @@ static int rt9467_charger_probe(struct i2c_client *i2c)
|
|||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add irq chip\n");
|
||||
|
||||
mutex_init(&data->adc_lock);
|
||||
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_adc_lock,
|
||||
&data->adc_lock);
|
||||
ret = devm_mutex_init(dev, &data->adc_lock);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init ADC lock\n");
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->attach_lock);
|
||||
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_attach_lock,
|
||||
&data->attach_lock);
|
||||
ret = devm_mutex_init(dev, &data->attach_lock);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init attach lock\n");
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->ichg_ieoc_lock);
|
||||
ret = devm_add_action_or_reset(dev, rt9467_chg_destroy_ichg_ieoc_lock,
|
||||
&data->ichg_ieoc_lock);
|
||||
ret = devm_mutex_init(dev, &data->ichg_ieoc_lock);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init ICHG/IEOC lock\n");
|
||||
return ret;
|
||||
|
||||
init_completion(&data->aicl_done);
|
||||
ret = devm_add_action_or_reset(dev, rt9467_chg_complete_aicl_done,
|
||||
&data->aicl_done);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to init AICL done completion\n");
|
||||
return ret;
|
||||
|
||||
ret = rt9467_do_charger_init(data);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
|
|||
int mid = (max + min) / 2;
|
||||
if (rx51_temp_table2[mid] <= raw)
|
||||
min = mid;
|
||||
else if (rx51_temp_table2[mid] > raw)
|
||||
else
|
||||
max = mid;
|
||||
if (rx51_temp_table2[mid] == raw)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -154,8 +154,7 @@ static const struct regmap_config sbs_regmap = {
|
|||
.val_format_endian = REGMAP_ENDIAN_LITTLE, /* since based on SMBus */
|
||||
};
|
||||
|
||||
static const struct power_supply_desc sbs_desc = {
|
||||
.name = "sbs-charger",
|
||||
static const struct power_supply_desc sbs_default_desc = {
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.properties = sbs_properties,
|
||||
.num_properties = ARRAY_SIZE(sbs_properties),
|
||||
|
|
@ -165,9 +164,20 @@ static const struct power_supply_desc sbs_desc = {
|
|||
static int sbs_probe(struct i2c_client *client)
|
||||
{
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct power_supply_desc *sbs_desc;
|
||||
struct sbs_info *chip;
|
||||
int ret, val;
|
||||
|
||||
sbs_desc = devm_kmemdup(&client->dev, &sbs_default_desc,
|
||||
sizeof(*sbs_desc), GFP_KERNEL);
|
||||
if (!sbs_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
sbs_desc->name = devm_kasprintf(&client->dev, GFP_KERNEL, "sbs-%s",
|
||||
dev_name(&client->dev));
|
||||
if (!sbs_desc->name)
|
||||
return -ENOMEM;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(struct sbs_info), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
|
@ -191,7 +201,7 @@ static int sbs_probe(struct i2c_client *client)
|
|||
return dev_err_probe(&client->dev, ret, "Failed to get device status\n");
|
||||
chip->last_state = val;
|
||||
|
||||
chip->power_supply = devm_power_supply_register(&client->dev, &sbs_desc, &psy_cfg);
|
||||
chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc, &psy_cfg);
|
||||
if (IS_ERR(chip->power_supply))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply),
|
||||
"Failed to register power supply\n");
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ static int sbsm_probe(struct i2c_client *client)
|
|||
data->muxc = i2c_mux_alloc(adapter, dev, SBSM_MAX_BATS, 0,
|
||||
I2C_MUX_LOCKED, &sbsm_select, NULL);
|
||||
if (!data->muxc)
|
||||
return dev_err_probe(dev, -ENOMEM, "failed to alloc i2c mux\n");
|
||||
return -ENOMEM;
|
||||
data->muxc->priv = data;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, sbsm_del_mux_adapter, data);
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data)
|
|||
{
|
||||
struct ucs1002_info *info = data;
|
||||
|
||||
mod_delayed_work(system_wq, &info->health_poll, 0);
|
||||
mod_delayed_work(system_percpu_wq, &info->health_poll, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,22 @@
|
|||
* is off or suspended, the coulomb counter is not used atm.
|
||||
*
|
||||
* Possible improvements:
|
||||
* 1. Activate commented out total_coulomb_count code
|
||||
* 1. Add coulumb counter reading, e.g. something like this:
|
||||
* Read + reset coulomb counter every 10 polls (every 300 seconds)
|
||||
*
|
||||
* if ((chip->poll_count % 10) == 0) {
|
||||
* val = ug3105_read_word(chip->client, UG3105_REG_COULOMB_CNT);
|
||||
* if (val < 0)
|
||||
* goto out;
|
||||
*
|
||||
* i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
|
||||
* UG3105_CTRL1_RESET_COULOMB_CNT);
|
||||
*
|
||||
* chip->total_coulomb_count += (s16)val;
|
||||
* dev_dbg(&chip->client->dev, "coulomb count %d total %d\n",
|
||||
* (s16)val, chip->total_coulomb_count);
|
||||
* }
|
||||
*
|
||||
* 2. Reset total_coulomb_count val to 0 when the battery is as good as empty
|
||||
* and remember that we did this (and clear the flag for this on susp/resume)
|
||||
* 3. When the battery is full check if the flag that we set total_coulomb_count
|
||||
|
|
@ -31,24 +46,16 @@
|
|||
* has shown that an estimated 7404mWh increase of the battery's energy results
|
||||
* in a total_coulomb_count increase of 3277 units with a 5 milli-ohm sense R.
|
||||
*
|
||||
* Copyright (C) 2021 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2021 - 2025 Hans de Goede <hansg@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define UG3105_MOV_AVG_WINDOW 8
|
||||
#define UG3105_INIT_POLL_TIME (5 * HZ)
|
||||
#define UG3105_POLL_TIME (30 * HZ)
|
||||
#define UG3105_SETTLE_TIME (1 * HZ)
|
||||
|
||||
#define UG3105_INIT_POLL_COUNT 30
|
||||
#include "adc-battery-helper.h"
|
||||
|
||||
#define UG3105_REG_MODE 0x00
|
||||
#define UG3105_REG_CTRL1 0x01
|
||||
|
|
@ -61,34 +68,13 @@
|
|||
|
||||
#define UG3105_CTRL1_RESET_COULOMB_CNT 0x03
|
||||
|
||||
#define UG3105_CURR_HYST_UA 65000
|
||||
|
||||
#define UG3105_LOW_BAT_UV 3700000
|
||||
#define UG3105_FULL_BAT_HYST_UV 38000
|
||||
|
||||
#define AMBIENT_TEMP_CELCIUS 25
|
||||
|
||||
struct ug3105_chip {
|
||||
/* Must be the first member see adc-battery-helper documentation */
|
||||
struct adc_battery_helper helper;
|
||||
struct i2c_client *client;
|
||||
struct power_supply *psy;
|
||||
struct delayed_work work;
|
||||
struct mutex lock;
|
||||
int ocv[UG3105_MOV_AVG_WINDOW]; /* micro-volt */
|
||||
int intern_res[UG3105_MOV_AVG_WINDOW]; /* milli-ohm */
|
||||
int poll_count;
|
||||
int ocv_avg_index;
|
||||
int ocv_avg; /* micro-volt */
|
||||
int intern_res_poll_count;
|
||||
int intern_res_avg_index;
|
||||
int intern_res_avg; /* milli-ohm */
|
||||
int volt; /* micro-volt */
|
||||
int curr; /* micro-ampere */
|
||||
int total_coulomb_count;
|
||||
int uv_per_unit;
|
||||
int ua_per_unit;
|
||||
int status;
|
||||
int capacity;
|
||||
bool supplied;
|
||||
};
|
||||
|
||||
static int ug3105_read_word(struct i2c_client *client, u8 reg)
|
||||
|
|
@ -102,230 +88,43 @@ static int ug3105_read_word(struct i2c_client *client, u8 reg)
|
|||
return val;
|
||||
}
|
||||
|
||||
static int ug3105_get_status(struct ug3105_chip *chip)
|
||||
{
|
||||
int full = chip->psy->battery_info->constant_charge_voltage_max_uv -
|
||||
UG3105_FULL_BAT_HYST_UV;
|
||||
|
||||
if (chip->curr > UG3105_CURR_HYST_UA)
|
||||
return POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
if (chip->curr < -UG3105_CURR_HYST_UA)
|
||||
return POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
|
||||
if (chip->supplied && chip->ocv_avg > full)
|
||||
return POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
}
|
||||
|
||||
static void ug3105_work(struct work_struct *work)
|
||||
{
|
||||
struct ug3105_chip *chip = container_of(work, struct ug3105_chip,
|
||||
work.work);
|
||||
int i, val, curr_diff, volt_diff, res, win_size;
|
||||
bool prev_supplied = chip->supplied;
|
||||
int prev_status = chip->status;
|
||||
int prev_volt = chip->volt;
|
||||
int prev_curr = chip->curr;
|
||||
struct power_supply *psy;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
psy = chip->psy;
|
||||
if (!psy)
|
||||
goto out;
|
||||
|
||||
val = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
|
||||
if (val < 0)
|
||||
goto out;
|
||||
chip->volt = val * chip->uv_per_unit;
|
||||
|
||||
val = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
|
||||
if (val < 0)
|
||||
goto out;
|
||||
chip->curr = (s16)val * chip->ua_per_unit;
|
||||
|
||||
chip->ocv[chip->ocv_avg_index] =
|
||||
chip->volt - chip->curr * chip->intern_res_avg / 1000;
|
||||
chip->ocv_avg_index = (chip->ocv_avg_index + 1) % UG3105_MOV_AVG_WINDOW;
|
||||
chip->poll_count++;
|
||||
|
||||
/*
|
||||
* See possible improvements comment above.
|
||||
*
|
||||
* Read + reset coulomb counter every 10 polls (every 300 seconds)
|
||||
* if ((chip->poll_count % 10) == 0) {
|
||||
* val = ug3105_read_word(chip->client, UG3105_REG_COULOMB_CNT);
|
||||
* if (val < 0)
|
||||
* goto out;
|
||||
*
|
||||
* i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
|
||||
* UG3105_CTRL1_RESET_COULOMB_CNT);
|
||||
*
|
||||
* chip->total_coulomb_count += (s16)val;
|
||||
* dev_dbg(&chip->client->dev, "coulomb count %d total %d\n",
|
||||
* (s16)val, chip->total_coulomb_count);
|
||||
* }
|
||||
*/
|
||||
|
||||
chip->ocv_avg = 0;
|
||||
win_size = min(chip->poll_count, UG3105_MOV_AVG_WINDOW);
|
||||
for (i = 0; i < win_size; i++)
|
||||
chip->ocv_avg += chip->ocv[i];
|
||||
chip->ocv_avg /= win_size;
|
||||
|
||||
chip->supplied = power_supply_am_i_supplied(psy);
|
||||
chip->status = ug3105_get_status(chip);
|
||||
if (chip->status == POWER_SUPPLY_STATUS_FULL)
|
||||
chip->capacity = 100;
|
||||
else
|
||||
chip->capacity = power_supply_batinfo_ocv2cap(chip->psy->battery_info,
|
||||
chip->ocv_avg,
|
||||
AMBIENT_TEMP_CELCIUS);
|
||||
|
||||
/*
|
||||
* Skip internal resistance calc on charger [un]plug and
|
||||
* when the battery is almost empty (voltage low).
|
||||
*/
|
||||
if (chip->supplied != prev_supplied ||
|
||||
chip->volt < UG3105_LOW_BAT_UV ||
|
||||
chip->poll_count < 2)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Assuming that the OCV voltage does not change significantly
|
||||
* between 2 polls, then we can calculate the internal resistance
|
||||
* on a significant current change by attributing all voltage
|
||||
* change between the 2 readings to the internal resistance.
|
||||
*/
|
||||
curr_diff = abs(chip->curr - prev_curr);
|
||||
if (curr_diff < UG3105_CURR_HYST_UA)
|
||||
goto out;
|
||||
|
||||
volt_diff = abs(chip->volt - prev_volt);
|
||||
res = volt_diff * 1000 / curr_diff;
|
||||
|
||||
if ((res < (chip->intern_res_avg * 2 / 3)) ||
|
||||
(res > (chip->intern_res_avg * 4 / 3))) {
|
||||
dev_dbg(&chip->client->dev, "Ignoring outlier internal resistance %d mOhm\n", res);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(&chip->client->dev, "Internal resistance %d mOhm\n", res);
|
||||
|
||||
chip->intern_res[chip->intern_res_avg_index] = res;
|
||||
chip->intern_res_avg_index = (chip->intern_res_avg_index + 1) % UG3105_MOV_AVG_WINDOW;
|
||||
chip->intern_res_poll_count++;
|
||||
|
||||
chip->intern_res_avg = 0;
|
||||
win_size = min(chip->intern_res_poll_count, UG3105_MOV_AVG_WINDOW);
|
||||
for (i = 0; i < win_size; i++)
|
||||
chip->intern_res_avg += chip->intern_res[i];
|
||||
chip->intern_res_avg /= win_size;
|
||||
|
||||
out:
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
queue_delayed_work(system_wq, &chip->work,
|
||||
(chip->poll_count <= UG3105_INIT_POLL_COUNT) ?
|
||||
UG3105_INIT_POLL_TIME : UG3105_POLL_TIME);
|
||||
|
||||
if (chip->status != prev_status && psy)
|
||||
power_supply_changed(psy);
|
||||
}
|
||||
|
||||
static enum power_supply_property ug3105_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
};
|
||||
|
||||
static int ug3105_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
static int ug3105_get_voltage_and_current_now(struct power_supply *psy, int *volt, int *curr)
|
||||
{
|
||||
struct ug3105_chip *chip = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
ret = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!chip->psy) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
*volt = ret * chip->uv_per_unit;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = chip->status;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
|
||||
if (ret < 0)
|
||||
break;
|
||||
val->intval = ret * chip->uv_per_unit;
|
||||
ret = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
val->intval = chip->ocv_avg;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
|
||||
if (ret < 0)
|
||||
break;
|
||||
val->intval = (s16)ret * chip->ua_per_unit;
|
||||
ret = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = chip->capacity;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
ret = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
out:
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ug3105_external_power_changed(struct power_supply *psy)
|
||||
{
|
||||
struct ug3105_chip *chip = power_supply_get_drvdata(psy);
|
||||
|
||||
dev_dbg(&chip->client->dev, "external power changed\n");
|
||||
mod_delayed_work(system_wq, &chip->work, UG3105_SETTLE_TIME);
|
||||
*curr = (s16)ret * chip->ua_per_unit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc ug3105_psy_desc = {
|
||||
.name = "ug3105_battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.get_property = ug3105_get_property,
|
||||
.external_power_changed = ug3105_external_power_changed,
|
||||
.properties = ug3105_battery_props,
|
||||
.num_properties = ARRAY_SIZE(ug3105_battery_props),
|
||||
.get_property = adc_battery_helper_get_property,
|
||||
.external_power_changed = adc_battery_helper_external_power_changed,
|
||||
.properties = adc_battery_helper_properties,
|
||||
.num_properties = ADC_HELPER_NUM_PROPERTIES,
|
||||
};
|
||||
|
||||
static void ug3105_init(struct ug3105_chip *chip)
|
||||
static void ug3105_start(struct i2c_client *client)
|
||||
{
|
||||
chip->poll_count = 0;
|
||||
chip->ocv_avg_index = 0;
|
||||
chip->total_coulomb_count = 0;
|
||||
i2c_smbus_write_byte_data(chip->client, UG3105_REG_MODE,
|
||||
UG3105_MODE_RUN);
|
||||
i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
|
||||
UG3105_CTRL1_RESET_COULOMB_CNT);
|
||||
queue_delayed_work(system_wq, &chip->work, 0);
|
||||
flush_delayed_work(&chip->work);
|
||||
i2c_smbus_write_byte_data(client, UG3105_REG_MODE, UG3105_MODE_RUN);
|
||||
i2c_smbus_write_byte_data(client, UG3105_REG_CTRL1, UG3105_CTRL1_RESET_COULOMB_CNT);
|
||||
}
|
||||
|
||||
static void ug3105_stop(struct i2c_client *client)
|
||||
{
|
||||
i2c_smbus_write_byte_data(client, UG3105_REG_MODE, UG3105_MODE_STANDBY);
|
||||
}
|
||||
|
||||
static int ug3105_probe(struct i2c_client *client)
|
||||
|
|
@ -333,7 +132,6 @@ static int ug3105_probe(struct i2c_client *client)
|
|||
struct power_supply_config psy_cfg = {};
|
||||
struct device *dev = &client->dev;
|
||||
u32 curr_sense_res_uohm = 10000;
|
||||
struct power_supply *psy;
|
||||
struct ug3105_chip *chip;
|
||||
int ret;
|
||||
|
||||
|
|
@ -342,23 +140,8 @@ static int ug3105_probe(struct i2c_client *client)
|
|||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
mutex_init(&chip->lock);
|
||||
ret = devm_delayed_work_autocancel(dev, &chip->work, ug3105_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
psy_cfg.drv_data = chip;
|
||||
psy = devm_power_supply_register(dev, &ug3105_psy_desc, &psy_cfg);
|
||||
if (IS_ERR(psy))
|
||||
return PTR_ERR(psy);
|
||||
|
||||
if (!psy->battery_info ||
|
||||
psy->battery_info->factory_internal_resistance_uohm == -EINVAL ||
|
||||
psy->battery_info->constant_charge_voltage_max_uv == -EINVAL ||
|
||||
!psy->battery_info->ocv_table[0]) {
|
||||
dev_err(dev, "error required properties are missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ug3105_start(client);
|
||||
|
||||
device_property_read_u32(dev, "upisemi,rsns-microohm", &curr_sense_res_uohm);
|
||||
|
||||
|
|
@ -366,35 +149,36 @@ static int ug3105_probe(struct i2c_client *client)
|
|||
* DAC maximum is 4.5V divided by 65536 steps + an unknown factor of 10
|
||||
* coming from somewhere for some reason (verified with a volt-meter).
|
||||
*/
|
||||
chip->uv_per_unit = 45000000/65536;
|
||||
chip->uv_per_unit = 45000000 / 65536;
|
||||
/* Datasheet says 8.1 uV per unit for the current ADC */
|
||||
chip->ua_per_unit = 8100000 / curr_sense_res_uohm;
|
||||
|
||||
/* Use provided internal resistance as start point (in milli-ohm) */
|
||||
chip->intern_res_avg = psy->battery_info->factory_internal_resistance_uohm / 1000;
|
||||
/* Also add it to the internal resistance moving average window */
|
||||
chip->intern_res[0] = chip->intern_res_avg;
|
||||
chip->intern_res_avg_index = 1;
|
||||
chip->intern_res_poll_count = 1;
|
||||
psy_cfg.drv_data = chip;
|
||||
chip->psy = devm_power_supply_register(dev, &ug3105_psy_desc, &psy_cfg);
|
||||
if (IS_ERR(chip->psy)) {
|
||||
ret = PTR_ERR(chip->psy);
|
||||
goto stop;
|
||||
}
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
chip->psy = psy;
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
ug3105_init(chip);
|
||||
ret = adc_battery_helper_init(&chip->helper, chip->psy,
|
||||
ug3105_get_voltage_and_current_now, NULL);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
return 0;
|
||||
|
||||
stop:
|
||||
ug3105_stop(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused ug3105_suspend(struct device *dev)
|
||||
{
|
||||
struct ug3105_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
i2c_smbus_write_byte_data(chip->client, UG3105_REG_MODE,
|
||||
UG3105_MODE_STANDBY);
|
||||
|
||||
adc_battery_helper_suspend(dev);
|
||||
ug3105_stop(chip->client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -402,8 +186,8 @@ static int __maybe_unused ug3105_resume(struct device *dev)
|
|||
{
|
||||
struct ug3105_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
ug3105_init(chip);
|
||||
|
||||
ug3105_start(chip->client);
|
||||
adc_battery_helper_resume(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -422,10 +206,12 @@ static struct i2c_driver ug3105_i2c_driver = {
|
|||
.pm = &ug3105_pm_ops,
|
||||
},
|
||||
.probe = ug3105_probe,
|
||||
.remove = ug3105_stop,
|
||||
.shutdown = ug3105_stop,
|
||||
.id_table = ug3105_id,
|
||||
};
|
||||
module_i2c_driver(ug3105_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
|
||||
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org");
|
||||
MODULE_DESCRIPTION("uPI uG3105 battery monitor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -9,35 +9,27 @@
|
|||
#ifndef __MAX77705_CHARGER_H
|
||||
#define __MAX77705_CHARGER_H __FILE__
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT */
|
||||
#define MAX77705_BYP_I BIT(0)
|
||||
#define MAX77705_INP_LIMIT_I BIT(1)
|
||||
#define MAX77705_BATP_I BIT(2)
|
||||
#define MAX77705_BAT_I BIT(3)
|
||||
#define MAX77705_CHG_I BIT(4)
|
||||
#define MAX77705_WCIN_I BIT(5)
|
||||
#define MAX77705_CHGIN_I BIT(6)
|
||||
#define MAX77705_AICL_I BIT(7)
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT_MASK */
|
||||
#define MAX77705_BYP_IM BIT(0)
|
||||
#define MAX77705_INP_LIMIT_IM BIT(1)
|
||||
#define MAX77705_BATP_IM BIT(2)
|
||||
#define MAX77705_BAT_IM BIT(3)
|
||||
#define MAX77705_CHG_IM BIT(4)
|
||||
#define MAX77705_WCIN_IM BIT(5)
|
||||
#define MAX77705_CHGIN_IM BIT(6)
|
||||
#define MAX77705_AICL_IM BIT(7)
|
||||
/* MAX77705_CHG_REG_CHG_INT */
|
||||
#define MAX77705_BYP_I (0)
|
||||
#define MAX77705_INP_LIMIT_I (1)
|
||||
#define MAX77705_BATP_I (2)
|
||||
#define MAX77705_BAT_I (3)
|
||||
#define MAX77705_CHG_I (4)
|
||||
#define MAX77705_WCIN_I (5)
|
||||
#define MAX77705_CHGIN_I (6)
|
||||
#define MAX77705_AICL_I (7)
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT_OK */
|
||||
#define MAX77705_BYP_OK BIT(0)
|
||||
#define MAX77705_DISQBAT_OK BIT(1)
|
||||
#define MAX77705_BATP_OK BIT(2)
|
||||
#define MAX77705_BAT_OK BIT(3)
|
||||
#define MAX77705_CHG_OK BIT(4)
|
||||
#define MAX77705_WCIN_OK BIT(5)
|
||||
#define MAX77705_CHGIN_OK BIT(6)
|
||||
#define MAX77705_AICL_OK BIT(7)
|
||||
#define MAX77705_BYP_OK BIT(MAX77705_BYP_I)
|
||||
#define MAX77705_DISQBAT_OK BIT(MAX77705_INP_LIMIT_I)
|
||||
#define MAX77705_BATP_OK BIT(MAX77705_BATP_I)
|
||||
#define MAX77705_BAT_OK BIT(MAX77705_BAT_I)
|
||||
#define MAX77705_CHG_OK BIT(MAX77705_CHG_I)
|
||||
#define MAX77705_WCIN_OK BIT(MAX77705_WCIN_I)
|
||||
#define MAX77705_CHGIN_OK BIT(MAX77705_CHGIN_I)
|
||||
#define MAX77705_AICL_OK BIT(MAX77705_AICL_I)
|
||||
|
||||
/* MAX77705_CHG_REG_DETAILS_00 */
|
||||
#define MAX77705_BATP_DTLS BIT(0)
|
||||
|
|
@ -63,7 +55,6 @@
|
|||
#define MAX77705_BUCK_SHIFT 2
|
||||
#define MAX77705_BOOST_SHIFT 3
|
||||
#define MAX77705_WDTEN_SHIFT 4
|
||||
#define MAX77705_MODE_MASK GENMASK(3, 0)
|
||||
#define MAX77705_CHG_MASK BIT(MAX77705_CHG_SHIFT)
|
||||
#define MAX77705_UNO_MASK BIT(MAX77705_UNO_SHIFT)
|
||||
#define MAX77705_OTG_MASK BIT(MAX77705_OTG_SHIFT)
|
||||
|
|
@ -74,34 +65,19 @@
|
|||
#define MAX77705_OTG_CTRL (MAX77705_OTG_MASK | MAX77705_BOOST_MASK)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_01 */
|
||||
#define MAX77705_FCHGTIME_SHIFT 0
|
||||
#define MAX77705_FCHGTIME_MASK GENMASK(2, 0)
|
||||
#define MAX77705_CHG_RSTRT_SHIFT 4
|
||||
#define MAX77705_CHG_RSTRT_MASK GENMASK(5, 4)
|
||||
#define MAX77705_FCHGTIME_DISABLE 0
|
||||
#define MAX77705_CHG_RSTRT_DISABLE 0x3
|
||||
|
||||
#define MAX77705_PQEN_SHIFT 7
|
||||
#define MAX77705_PQEN_MASK BIT(7)
|
||||
#define MAX77705_CHG_PQEN_DISABLE 0
|
||||
#define MAX77705_CHG_PQEN_ENABLE 1
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_02 */
|
||||
#define MAX77705_OTG_ILIM_SHIFT 6
|
||||
#define MAX77705_OTG_ILIM_MASK GENMASK(7, 6)
|
||||
#define MAX77705_OTG_ILIM_500 0
|
||||
#define MAX77705_OTG_ILIM_900 1
|
||||
#define MAX77705_OTG_ILIM_1200 2
|
||||
#define MAX77705_OTG_ILIM_1500 3
|
||||
#define MAX77705_CHG_CC GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_03 */
|
||||
#define MAX77705_TO_ITH_SHIFT 0
|
||||
#define MAX77705_TO_ITH_MASK GENMASK(2, 0)
|
||||
#define MAX77705_TO_TIME_SHIFT 3
|
||||
#define MAX77705_TO_TIME_MASK GENMASK(5, 3)
|
||||
#define MAX77705_SYS_TRACK_DIS_SHIFT 7
|
||||
#define MAX77705_SYS_TRACK_DIS_MASK BIT(7)
|
||||
#define MAX77705_TO_ITH_150MA 0
|
||||
#define MAX77705_TO_TIME_30M 3
|
||||
#define MAX77705_SYS_TRACK_ENABLE 0
|
||||
|
|
@ -110,15 +86,8 @@
|
|||
/* MAX77705_CHG_REG_CNFG_04 */
|
||||
#define MAX77705_CHG_MINVSYS_SHIFT 6
|
||||
#define MAX77705_CHG_MINVSYS_MASK GENMASK(7, 6)
|
||||
#define MAX77705_CHG_PRM_SHIFT 0
|
||||
#define MAX77705_CHG_PRM_MASK GENMASK(5, 0)
|
||||
|
||||
#define MAX77705_CHG_CV_PRM_SHIFT 0
|
||||
#define MAX77705_CHG_CV_PRM_MASK GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_05 */
|
||||
#define MAX77705_REG_B2SOVRC_SHIFT 0
|
||||
#define MAX77705_REG_B2SOVRC_MASK GENMASK(3, 0)
|
||||
#define MAX77705_B2SOVRC_DISABLE 0
|
||||
#define MAX77705_B2SOVRC_4_5A 6
|
||||
#define MAX77705_B2SOVRC_4_8A 8
|
||||
|
|
@ -128,9 +97,8 @@
|
|||
#define MAX77705_WDTCLR_SHIFT 0
|
||||
#define MAX77705_WDTCLR_MASK GENMASK(1, 0)
|
||||
#define MAX77705_WDTCLR 1
|
||||
#define MAX77705_CHGPROT_MASK GENMASK(3, 2)
|
||||
#define MAX77705_CHGPROT_UNLOCKED GENMASK(3, 2)
|
||||
#define MAX77705_SLOWEST_LX_SLOPE GENMASK(6, 5)
|
||||
#define MAX77705_CHGPROT_UNLOCKED 3
|
||||
#define MAX77705_SLOWEST_LX_SLOPE 3
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_07 */
|
||||
#define MAX77705_CHG_FMBST 4
|
||||
|
|
@ -140,36 +108,14 @@
|
|||
#define MAX77705_REG_FGSRC_MASK BIT(MAX77705_REG_FGSRC_SHIFT)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_08 */
|
||||
#define MAX77705_REG_FSW_SHIFT 0
|
||||
#define MAX77705_REG_FSW_MASK GENMASK(1, 0)
|
||||
#define MAX77705_CHG_FSW_3MHz 0
|
||||
#define MAX77705_CHG_FSW_2MHz 1
|
||||
#define MAX77705_CHG_FSW_1_5MHz 2
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_09 */
|
||||
#define MAX77705_CHG_CHGIN_LIM_MASK GENMASK(6, 0)
|
||||
#define MAX77705_CHG_EN_MASK BIT(7)
|
||||
#define MAX77705_CHG_DISABLE 0
|
||||
#define MAX77705_CHARGER_CHG_CHARGING(_reg) \
|
||||
(((_reg) & MAX77705_CHG_EN_MASK) > 1)
|
||||
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_10 */
|
||||
#define MAX77705_CHG_WCIN_LIM GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_11 */
|
||||
#define MAX77705_VBYPSET_SHIFT 0
|
||||
#define MAX77705_VBYPSET_MASK GENMASK(6, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_12 */
|
||||
#define MAX77705_CHGINSEL_SHIFT 5
|
||||
#define MAX77705_CHGINSEL_MASK BIT(MAX77705_CHGINSEL_SHIFT)
|
||||
#define MAX77705_WCINSEL_SHIFT 6
|
||||
#define MAX77705_WCINSEL_MASK BIT(MAX77705_WCINSEL_SHIFT)
|
||||
#define MAX77705_VCHGIN_REG_MASK GENMASK(4, 3)
|
||||
#define MAX77705_WCIN_REG_MASK GENMASK(2, 1)
|
||||
#define MAX77705_REG_DISKIP_SHIFT 0
|
||||
#define MAX77705_REG_DISKIP_MASK BIT(MAX77705_REG_DISKIP_SHIFT)
|
||||
/* REG=4.5V, UVLO=4.7V */
|
||||
#define MAX77705_VCHGIN_4_5 0
|
||||
/* REG=4.5V, UVLO=4.7V */
|
||||
|
|
@ -183,9 +129,59 @@
|
|||
#define MAX77705_CURRENT_CHGIN_MIN 100000
|
||||
#define MAX77705_CURRENT_CHGIN_MAX 3200000
|
||||
|
||||
enum max77705_field_idx {
|
||||
MAX77705_CHGPROT,
|
||||
MAX77705_CHG_EN,
|
||||
MAX77705_CHG_CC_LIM,
|
||||
MAX77705_CHG_CHGIN_LIM,
|
||||
MAX77705_CHG_CV_PRM,
|
||||
MAX77705_CHG_PQEN,
|
||||
MAX77705_CHG_RSTRT,
|
||||
MAX77705_CHG_WCIN,
|
||||
MAX77705_FCHGTIME,
|
||||
MAX77705_LX_SLOPE,
|
||||
MAX77705_MODE,
|
||||
MAX77705_OTG_ILIM,
|
||||
MAX77705_REG_B2SOVRC,
|
||||
MAX77705_REG_DISKIP,
|
||||
MAX77705_REG_FSW,
|
||||
MAX77705_SYS_TRACK,
|
||||
MAX77705_TO,
|
||||
MAX77705_TO_TIME,
|
||||
MAX77705_VBYPSET,
|
||||
MAX77705_VCHGIN,
|
||||
MAX77705_WCIN,
|
||||
MAX77705_N_REGMAP_FIELDS,
|
||||
};
|
||||
|
||||
static const struct reg_field max77705_reg_field[MAX77705_N_REGMAP_FIELDS] = {
|
||||
[MAX77705_MODE] = REG_FIELD(MAX77705_CHG_REG_CNFG_00, 0, 3),
|
||||
[MAX77705_FCHGTIME] = REG_FIELD(MAX77705_CHG_REG_CNFG_01, 0, 2),
|
||||
[MAX77705_CHG_RSTRT] = REG_FIELD(MAX77705_CHG_REG_CNFG_01, 4, 5),
|
||||
[MAX77705_CHG_PQEN] = REG_FIELD(MAX77705_CHG_REG_CNFG_01, 7, 7),
|
||||
[MAX77705_CHG_CC_LIM] = REG_FIELD(MAX77705_CHG_REG_CNFG_02, 0, 5),
|
||||
[MAX77705_OTG_ILIM] = REG_FIELD(MAX77705_CHG_REG_CNFG_02, 6, 7),
|
||||
[MAX77705_TO] = REG_FIELD(MAX77705_CHG_REG_CNFG_03, 0, 2),
|
||||
[MAX77705_TO_TIME] = REG_FIELD(MAX77705_CHG_REG_CNFG_03, 3, 5),
|
||||
[MAX77705_SYS_TRACK] = REG_FIELD(MAX77705_CHG_REG_CNFG_03, 7, 7),
|
||||
[MAX77705_CHG_CV_PRM] = REG_FIELD(MAX77705_CHG_REG_CNFG_04, 0, 5),
|
||||
[MAX77705_REG_B2SOVRC] = REG_FIELD(MAX77705_CHG_REG_CNFG_05, 0, 3),
|
||||
[MAX77705_CHGPROT] = REG_FIELD(MAX77705_CHG_REG_CNFG_06, 2, 3),
|
||||
[MAX77705_LX_SLOPE] = REG_FIELD(MAX77705_CHG_REG_CNFG_06, 5, 6),
|
||||
[MAX77705_REG_FSW] = REG_FIELD(MAX77705_CHG_REG_CNFG_08, 0, 1),
|
||||
[MAX77705_CHG_CHGIN_LIM] = REG_FIELD(MAX77705_CHG_REG_CNFG_09, 0, 6),
|
||||
[MAX77705_CHG_EN] = REG_FIELD(MAX77705_CHG_REG_CNFG_09, 7, 7),
|
||||
[MAX77705_CHG_WCIN] = REG_FIELD(MAX77705_CHG_REG_CNFG_10, 0, 5),
|
||||
[MAX77705_VBYPSET] = REG_FIELD(MAX77705_CHG_REG_CNFG_11, 0, 6),
|
||||
[MAX77705_REG_DISKIP] = REG_FIELD(MAX77705_CHG_REG_CNFG_12, 0, 0),
|
||||
[MAX77705_WCIN] = REG_FIELD(MAX77705_CHG_REG_CNFG_12, 1, 2),
|
||||
[MAX77705_VCHGIN] = REG_FIELD(MAX77705_CHG_REG_CNFG_12, 3, 4),
|
||||
};
|
||||
|
||||
struct max77705_charger_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *rfield[MAX77705_N_REGMAP_FIELDS];
|
||||
struct power_supply_battery_info *bat_info;
|
||||
struct workqueue_struct *wqueue;
|
||||
struct work_struct chgin_work;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ enum power_supply_property {
|
|||
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
|
||||
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
|
||||
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
|
||||
POWER_SUPPLY_PROP_INTERNAL_RESISTANCE,
|
||||
POWER_SUPPLY_PROP_STATE_OF_HEALTH,
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
|
|
|
|||
Loading…
Reference in New Issue