power supply and reset changes for the v5.17 series

power-supply core:
  - introduce "No Battery" health status
  - use library interpolation
  - add power_supply_battery_info documentation
  - migrate power_supply_battery_info to be fully heap allocated
    making it more obvious that it needs to be free'd manually
 
 Drivers:
  - max77976-charger: new driver
  - qcom-smbb: add pm8226 charger support
  - bq25890-charger: support battery temperature readings
  - ab8500: continue migrating towards using standard core APIs
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmHb+9YACgkQ2O7X88g7
 +pqhGw/+NyqnkSIsU4udxYJY47ooaZ7f2rApFueJSre0Xzwi37SQFFvstBb196YP
 B1mr2H16Slni0kRItu+B1H04mI1y1o7zT4E84s1DoMQjILLtxh+LiT8tefhhWtBs
 5a6IiUKtxo2HZZgreTqyAAjqPYkdRGDkChK87zBZdxIGcaGHXkRlyuTSR2P406r/
 qvA5uCkVg7CMqXf0RemcIlEOvSxaPXZz+PVJjn6qbjSioQHtOekoxoMBziy31Nhm
 Qp0sEYYlW6ZRpaP/IFU21sZb8zaH+isOrg2U/LttgfABdwtEVUz/nygZwSD0ncBi
 zSUrc4Mu/goh7m5oY91HUsis3fzgABMd/xdaDDzJoh91LvguE9lA2vP5r7hEDLVD
 S7v9RNNEfALce5sHGAEXQX0IztD0xZhSRe6jpeAYLVB0OG1Tae3q/6dlEHQsz2rb
 oANYsxrAU0hf2MnsXa6FnR1cnVJNm7z/bQpTFbU7guSX8Vi5n8jzmghoi3piCjyk
 9YXMROuivXuaBz1wXNk2IXzdYwtOTeateo2yYdSQol3UnYgUtIrn+qBVeuElbC3C
 qkb21yTpEIk3aoZOjbKFze7ks6L8c5Nip+66s4WkcAucK3Uxii1QpTR9f1+TdxN0
 vjybsIRHqPnCh6yl1JR/0NNvFPGQWkJt6sveQXas/LAZ77MOMro=
 =9cda
 -----END PGP SIGNATURE-----

Merge tag 'for-v5.17' 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 "No Battery" health status

   - use library interpolation

   - add power_supply_battery_info documentation

   - migrate power_supply_battery_info to be fully heap allocated making
     it more obvious that it needs to be free'd manually

  Drivers:

   - max77976-charger: new driver

   - qcom-smbb: add pm8226 charger support

   - bq25890-charger: support battery temperature readings

   - ab8500: continue migrating towards using standard core APIs"

* tag 'for-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (28 commits)
  power: supply_core: Pass pointer to battery info
  power: supply: ab8500: Fix the error handling path of ab8500_charger_probe()
  power: reset: mt6397: Check for null res pointer
  power: bq25890: add POWER_SUPPLY_PROP_TEMP
  power: supply: qcom_smbb: support pm8226
  dt-bindings: power: supply: pm8941-charger: add pm8226
  power: supply: ab8500: Standardize capacity lookup
  power: supply: ab8500: Standardize temp res lookup
  power: supply: ab8500: Standardize CV voltage
  power: supply: ab8500: Standardize CC current
  power: supply: ab8500: Make recharge capacity a constant
  power: supply: ab8500: Standardize termination current
  power: supply: ab8500: Standardize internal resistance
  power: supply: ab8500_fg: Init battery data in bind()
  power: supply: ab8500: Standardize voltages
  power: supply: ab8500: Standardize technology
  power: supply: ab8500: Standardize design capacity
  power: supply: ab8500: Use only one battery type
  power: supply: ab8500: Drop unused battery types
  power: supply: ab8500: Standardize operating temperature
  ...
This commit is contained in:
Linus Torvalds 2022-01-11 11:20:27 -08:00
commit 039053c119
34 changed files with 1842 additions and 1383 deletions

View File

@ -413,7 +413,7 @@ Description:
"Over voltage", "Unspecified failure", "Cold",
"Watchdog timer expire", "Safety timer expire",
"Over current", "Calibration required", "Warm",
"Cool", "Hot"
"Cool", "Hot", "No battery"
What: /sys/class/power_supply/<supply_name>/precharge_current
Date: June 2017

View File

@ -1,41 +0,0 @@
Driver a GPIO line that can be used to turn the power off.
The driver supports both level triggered and edge triggered power off.
At driver load time, the driver will request the given gpio line and
install a handler to power off the system. If the optional properties
'input' is not found, the GPIO line will be driven in the inactive
state. Otherwise its configured as an input.
When the power-off handler is called, the gpio is configured as an
output, and drive active, so triggering a level triggered power off
condition. This will also cause an inactive->active edge condition, so
triggering positive edge triggered power off. After a delay of 100ms,
the GPIO is set to inactive, thus causing an active->inactive edge,
triggering negative edge triggered power off. After another 100ms
delay the GPIO is driver active again. If the power is still on and
the CPU still running after a 3000ms delay, a WARN_ON(1) is emitted.
Required properties:
- compatible : should be "gpio-poweroff".
- gpios : The GPIO to set high/low, see "gpios property" in
Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be
low to power down the board set it to "Active Low", otherwise set
gpio to "Active High".
Optional properties:
- input : Initially configure the GPIO line as an input. Only reconfigure
it to an output when the power-off handler is called. If this optional
property is not specified, the GPIO is initialized as an output in its
inactive state.
- active-delay-ms: Delay (default 100) to wait after driving gpio active
- inactive-delay-ms: Delay (default 100) to wait after driving gpio inactive
- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
specified, 3000 ms is used.
Examples:
gpio-poweroff {
compatible = "gpio-poweroff";
gpios = <&gpio 4 0>;
timeout-ms = <3000>;
};

View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/reset/gpio-poweroff.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: GPIO controlled power off
maintainers:
- Sebastian Reichel <sre@kernel.org>
description: >
System power off support via a GPIO line. When a shutdown is
executed the operating system is expected to switch the GPIO
from inactive to active. After a delay (active-delay-ms) it
is expected to be switched back to inactive. After another
delay (inactive-delay-ms) it is configured as active again.
Finally the operating system assumes the power off failed if
the system is still running after waiting some time (timeout-ms).
properties:
compatible:
const: gpio-poweroff
gpios:
maxItems: 1
input:
type: boolean
description: >
Initially configure the GPIO line as an input. Only reconfigure
it to an output when the power-off sequence is initiated. If this optional
property is not specified, the GPIO is initialized as an output in its inactive state.
active-delay-ms:
default: 100
description: Delay to wait after driving gpio active
inactive-delay-ms:
default: 100
description: Delay to wait after driving gpio inactive
timeout-ms:
default: 3000
description: Time to wait before assuming the power off sequence failed.
required:
- compatible
- gpios
additionalProperties: false
examples:
- |
gpio-poweroff {
compatible = "gpio-poweroff";
gpios = <&gpio 4 0>;
timeout-ms = <3000>;
};

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/maxim,max77976.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim Integrated MAX77976 Battery charger
maintainers:
- Luca Ceresoli <luca@lucaceresoli.net>
description: |
The Maxim MAX77976 is a 19Vin / 5.5A, 1-Cell Li+ battery charger
configured via I2C.
allOf:
- $ref: power-supply.yaml#
properties:
compatible:
const: maxim,max77976
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
charger@6b {
compatible = "maxim,max77976";
reg = <0x6b>;
};
};
...

View File

@ -11,7 +11,9 @@ maintainers:
properties:
compatible:
const: qcom,pm8941-charger
enum:
- qcom,pm8226-charger
- qcom,pm8941-charger
reg:
maxItems: 1

View File

@ -11668,6 +11668,12 @@ F: Documentation/devicetree/bindings/*/*max77802.txt
F: drivers/regulator/max77802-regulator.c
F: include/dt-bindings/*/*max77802.h
MAXIM MAX77976 BATTERY CHARGER
M: Luca Ceresoli <luca@lucaceresoli.net>
S: Supported
F: Documentation/devicetree/bindings/power/supply/maxim,max77976.yaml
F: drivers/power/supply/max77976_charger.c
MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
M: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>

View File

@ -57,6 +57,9 @@ static int mt6323_pwrc_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
pwrc->base = res->start;
pwrc->regmap = mt6397_chip->regmap;
pwrc->dev = &pdev->dev;

View File

@ -557,6 +557,18 @@ config CHARGER_MAX77693
help
Say Y to enable support for the Maxim MAX77693 battery charger.
config CHARGER_MAX77976
tristate "Maxim MAX77976 battery charger driver"
depends on I2C
select REGMAP_I2C
help
The Maxim MAX77976 is a 19 Vin, 5.5A 1-Cell Li+ Battery Charger
USB OTG support. It has an I2C interface for configuration.
Say Y to enable support for the Maxim MAX77976 battery charger.
This driver can also be built as a module. If so, the module will be
called max77976_charger.
config CHARGER_MAX8997
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
depends on MFD_MAX8997 && REGULATOR_MAX8997

View File

@ -75,6 +75,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o

View File

@ -160,13 +160,6 @@
#define BTEMP_HIGH_TH_57_1 0x02
#define BTEMP_HIGH_TH_62 0x03
/* current is mA */
#define USB_0P1A 100
#define USB_0P2A 200
#define USB_0P3A 300
#define USB_0P4A 400
#define USB_0P5A 500
#define LOW_BAT_3P1V 0x20
#define LOW_BAT_2P3V 0x00
#define LOW_BAT_RESET 0x01
@ -203,8 +196,8 @@ enum bup_vch_sel {
#define BATT_OVV_TH_3P7 0x00
#define BATT_OVV_TH_4P75 0x01
/* A value to indicate over voltage */
#define BATT_OVV_VALUE 4750
/* A value to indicate over voltage (microvolts) */
#define BATT_OVV_VALUE 4750000
/* VBUS OVV constants */
#define VBUS_OVV_SELECT_MASK 0x78
@ -291,16 +284,6 @@ struct ab8500_res_to_temp {
int resist;
};
/**
* struct ab8500_v_to_cap - Table for translating voltage to capacity
* @voltage: Voltage in mV
* @capacity: Capacity in percent
*/
struct ab8500_v_to_cap {
int voltage;
int capacity;
};
/* Forward declaration */
struct ab8500_fg;
@ -314,10 +297,9 @@ struct ab8500_fg;
* @init_total_time: Total init time during startup
* @high_curr_time: Time current has to be high to go to recovery
* @accu_charging: FG accumulation time while charging
* @accu_high_curr: FG accumulation time in high current mode
* @high_curr_threshold: High current threshold, in mA
* @lowbat_threshold: Low battery threshold, in mV
* @overbat_threshold: Over battery threshold, in mV
* @accu_high_curr_ua: FG accumulation time in high current mode
* @high_curr_threshold_ua: High current threshold, in uA
* @lowbat_threshold_uv: Low battery threshold, in uV
* @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
* Resolution in 50 mV step.
* @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
@ -342,9 +324,8 @@ struct ab8500_fg_parameters {
int high_curr_time;
int accu_charging;
int accu_high_curr;
int high_curr_threshold;
int lowbat_threshold;
int overbat_threshold;
int high_curr_threshold_ua;
int lowbat_threshold_uv;
int battok_falling_th_sel0;
int battok_raising_th_sel1;
int user_cap_limit;
@ -359,31 +340,21 @@ struct ab8500_fg_parameters {
/**
* struct ab8500_charger_maximization - struct used by the board config.
* @use_maxi: Enable maximization for this battery type
* @maxi_chg_curr: Maximum charger current allowed
* @maxi_chg_curr_ua: Maximum charger current allowed in microampere
* @maxi_wait_cycles: cycles to wait before setting charger current
* @charger_curr_step delta between two charger current settings (mA)
* @charger_curr_step_ua: delta between two charger current settings (uA)
*/
struct ab8500_maxim_parameters {
bool ena_maxi;
int chg_curr;
int chg_curr_ua;
int wait_cycles;
int charger_curr_step;
int charger_curr_step_ua;
};
/**
* struct ab8500_battery_type - different batteries supported
* @name: battery technology
* @resis_high: battery upper resistance limit
* @resis_low: battery lower resistance limit
* @charge_full_design: Maximum battery capacity in mAh
* @nominal_voltage: Nominal voltage of the battery in mV
* @termination_vol: max voltage upto which battery can be charged
* @termination_curr battery charging termination current in mA
* @recharge_cap battery capacity limit that will trigger a new
* full charging cycle in the case where maintenan-
* -ce charging has been disabled
* @normal_cur_lvl: charger current in normal state in mA
* @normal_vol_lvl: charger voltage in normal state in mV
* @maint_a_cur_lvl: charger current in maintenance A state in mA
* @maint_a_vol_lvl: charger voltage in maintenance A state in mV
* @maint_a_chg_timer_h: charge time in maintenance A state
@ -392,25 +363,12 @@ struct ab8500_maxim_parameters {
* @maint_b_chg_timer_h: charge time in maintenance B state
* @low_high_cur_lvl: charger current in temp low/high state in mA
* @low_high_vol_lvl: charger voltage in temp low/high state in mV'
* @battery_resistance: battery inner resistance in mOhm.
* @n_r_t_tbl_elements: number of elements in r_to_t_tbl
* @r_to_t_tbl: table containing resistance to temp points
* @n_v_cap_tbl_elements: number of elements in v_to_cap_tbl
* @v_to_cap_tbl: Voltage to capacity (in %) table
* @n_batres_tbl_elements number of elements in the batres_tbl
* @batres_tbl battery internal resistance vs temperature table
*/
struct ab8500_battery_type {
int name;
int resis_high;
int resis_low;
int charge_full_design;
int nominal_voltage;
int termination_vol;
int termination_curr;
int recharge_cap;
int normal_cur_lvl;
int normal_vol_lvl;
int maint_a_cur_lvl;
int maint_a_vol_lvl;
int maint_a_chg_timer_h;
@ -419,13 +377,8 @@ struct ab8500_battery_type {
int maint_b_chg_timer_h;
int low_high_cur_lvl;
int low_high_vol_lvl;
int battery_resistance;
int n_temp_tbl_elements;
const struct ab8500_res_to_temp *r_to_t_tbl;
int n_v_cap_tbl_elements;
const struct ab8500_v_to_cap *v_to_cap_tbl;
int n_batres_tbl_elements;
const struct batres_vs_temp *batres_tbl;
};
/**
@ -446,24 +399,21 @@ struct ab8500_bm_capacity_levels {
/**
* struct ab8500_bm_charger_parameters - Charger specific parameters
* @usb_volt_max: maximum allowed USB charger voltage in mV
* @usb_curr_max: maximum allowed USB charger current in mA
* @ac_volt_max: maximum allowed AC charger voltage in mV
* @ac_curr_max: maximum allowed AC charger current in mA
* @usb_volt_max_uv: maximum allowed USB charger voltage in uV
* @usb_curr_max_ua: maximum allowed USB charger current in uA
* @ac_volt_max_uv: maximum allowed AC charger voltage in uV
* @ac_curr_max_ua: maximum allowed AC charger current in uA
*/
struct ab8500_bm_charger_parameters {
int usb_volt_max;
int usb_curr_max;
int ac_volt_max;
int ac_curr_max;
int usb_volt_max_uv;
int usb_curr_max_ua;
int ac_volt_max_uv;
int ac_curr_max_ua;
};
/**
* struct ab8500_bm_data - ab8500 battery management data
* @temp_under under this temp, charging is stopped
* @temp_low between this temp and temp_under charging is reduced
* @temp_high between this temp and temp_over charging is reduced
* @temp_over over this temp, charging is stopped
* @bi battery info from device tree
* @temp_now present battery temperature
* @temp_interval_chg temperature measurement interval in s when charging
* @temp_interval_nochg temperature measurement interval in s when not charging
@ -478,16 +428,10 @@ struct ab8500_bm_charger_parameters {
* @enable_overshoot flag to enable VBAT overshoot control
* @auto_trig flag to enable auto adc trigger
* @fg_res resistance of FG resistor in 0.1mOhm
* @n_btypes number of elements in array bat_type
* @batt_id index of the identified battery in array bat_type
* @interval_charging charge alg cycle period time when charging (sec)
* @interval_not_charging charge alg cycle period time when not charging (sec)
* @temp_hysteresis temperature hysteresis
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
* @n_chg_out_curr number of elements in array chg_output_curr
* @n_chg_in_curr number of elements in array chg_input_curr
* @chg_output_curr charger output current level map
* @chg_input_curr charger input current level map
* @maxi maximization parameters
* @cap_levels capacity in percent for the different capacity levels
* @bat_type table of supported battery types
@ -495,10 +439,7 @@ struct ab8500_bm_charger_parameters {
* @fg_params fuel gauge parameters
*/
struct ab8500_bm_data {
int temp_under;
int temp_low;
int temp_high;
int temp_over;
struct power_supply_battery_info *bi;
int temp_now;
int temp_interval_chg;
int temp_interval_nochg;
@ -513,16 +454,10 @@ struct ab8500_bm_data {
bool auto_trig;
enum ab8500_adc_therm adc_therm;
int fg_res;
int n_btypes;
int batt_id;
int interval_charging;
int interval_not_charging;
int temp_hysteresis;
int gnd_lift_resistance;
int n_chg_out_curr;
int n_chg_in_curr;
int *chg_output_curr;
int *chg_input_curr;
const struct ab8500_maxim_parameters *maxi;
const struct ab8500_bm_capacity_levels *cap_levels;
struct ab8500_battery_type *bat_type;
@ -547,17 +482,6 @@ struct res_to_temp {
int resist;
};
/**
* struct batres_vs_temp - defines one point in a temp vs battery internal
* resistance curve.
* @temp: battery pack temperature in Celsius
* @resist: battery internal reistance in mOhm
*/
struct batres_vs_temp {
int temp;
int resist;
};
/* Forward declaration */
struct ab8500_fg;
@ -570,9 +494,10 @@ int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
int ab8500_bm_of_probe(struct power_supply *psy,
struct ab8500_bm_data *bm);
void ab8500_bm_of_remove(struct power_supply *psy,
struct ab8500_bm_data *bm);
extern struct platform_driver ab8500_fg_driver;
extern struct platform_driver ab8500_btemp_driver;

View File

@ -31,16 +31,16 @@ struct ux500_charger_ops {
* struct ux500_charger - power supply ux500 charger sub class
* @psy power supply base class
* @ops ux500 charger operations
* @max_out_volt maximum output charger voltage in mV
* @max_out_curr maximum output charger current in mA
* @max_out_volt_uv maximum output charger voltage in uV
* @max_out_curr_ua maximum output charger current in uA
* @enabled indicates if this charger is used or not
* @external external charger unit (pm2xxx)
*/
struct ux500_charger {
struct power_supply *psy;
struct ux500_charger_ops ops;
int max_out_volt;
int max_out_curr;
int max_out_volt_uv;
int max_out_curr_ua;
int wdt_refresh;
bool enabled;
bool external;

View File

@ -5,127 +5,42 @@
#include "ab8500-bm.h"
/*
* These are the defined batteries that uses a NTC and ID resistor placed
* inside of the battery pack.
* Note that the res_to_temp table must be strictly sorted by falling resistance
* values to work.
*/
const struct ab8500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
{-5, 53407},
{ 0, 48594},
{ 5, 43804},
{10, 39188},
{15, 34870},
{20, 30933},
{25, 27422},
{30, 24347},
{35, 21694},
{40, 19431},
{45, 17517},
{50, 15908},
{55, 14561},
{60, 13437},
{65, 12500},
};
EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
/* Default: under this temperature, charging is stopped */
#define AB8500_TEMP_UNDER 3
/* Default: between this temp and AB8500_TEMP_UNDER charging is reduced */
#define AB8500_TEMP_LOW 8
/* Default: between this temp and AB8500_TEMP_OVER charging is reduced */
#define AB8500_TEMP_HIGH 43
/* Default: over this temp, charging is stopped */
#define AB8500_TEMP_OVER 48
/* Default: temperature hysteresis */
#define AB8500_TEMP_HYSTERESIS 3
const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
const struct ab8500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
{-5, 200000},
{ 0, 159024},
{ 5, 151921},
{10, 144300},
{15, 136424},
{20, 128565},
{25, 120978},
{30, 113875},
{35, 107397},
{40, 101629},
{45, 96592},
{50, 92253},
{55, 88569},
{60, 85461},
{65, 82869},
};
EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
static const struct ab8500_v_to_cap cap_tbl_a_thermistor[] = {
{4171, 100},
{4114, 95},
{4009, 83},
{3947, 74},
{3907, 67},
{3863, 59},
{3830, 56},
{3813, 53},
{3791, 46},
{3771, 33},
{3754, 25},
{3735, 20},
{3717, 17},
{3681, 13},
{3664, 8},
{3651, 6},
{3635, 5},
{3560, 3},
{3408, 1},
{3247, 0},
};
static const struct ab8500_v_to_cap cap_tbl_b_thermistor[] = {
{4161, 100},
{4124, 98},
{4044, 90},
{4003, 85},
{3966, 80},
{3933, 75},
{3888, 67},
{3849, 60},
{3813, 55},
{3787, 47},
{3772, 30},
{3751, 25},
{3718, 20},
{3681, 16},
{3660, 14},
{3589, 10},
{3546, 7},
{3495, 4},
{3404, 2},
{3250, 0},
};
static const struct ab8500_v_to_cap cap_tbl[] = {
{4186, 100},
{4163, 99},
{4114, 95},
{4068, 90},
{3990, 80},
{3926, 70},
{3898, 65},
{3866, 60},
{3833, 55},
{3812, 50},
{3787, 40},
{3768, 30},
{3747, 25},
{3730, 20},
{3705, 15},
{3699, 14},
{3684, 12},
{3672, 9},
{3657, 7},
{3638, 6},
{3556, 4},
{3424, 2},
{3317, 1},
{3094, 0},
static struct power_supply_battery_ocv_table ocv_cap_tbl[] = {
{ .ocv = 4186000, .capacity = 100},
{ .ocv = 4163000, .capacity = 99},
{ .ocv = 4114000, .capacity = 95},
{ .ocv = 4068000, .capacity = 90},
{ .ocv = 3990000, .capacity = 80},
{ .ocv = 3926000, .capacity = 70},
{ .ocv = 3898000, .capacity = 65},
{ .ocv = 3866000, .capacity = 60},
{ .ocv = 3833000, .capacity = 55},
{ .ocv = 3812000, .capacity = 50},
{ .ocv = 3787000, .capacity = 40},
{ .ocv = 3768000, .capacity = 30},
{ .ocv = 3747000, .capacity = 25},
{ .ocv = 3730000, .capacity = 20},
{ .ocv = 3705000, .capacity = 15},
{ .ocv = 3699000, .capacity = 14},
{ .ocv = 3684000, .capacity = 12},
{ .ocv = 3672000, .capacity = 9},
{ .ocv = 3657000, .capacity = 7},
{ .ocv = 3638000, .capacity = 6},
{ .ocv = 3556000, .capacity = 4},
{ .ocv = 3424000, .capacity = 2},
{ .ocv = 3317000, .capacity = 1},
{ .ocv = 3094000, .capacity = 0},
};
/*
@ -152,244 +67,33 @@ static const struct ab8500_res_to_temp temp_tbl[] = {
/*
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
* temperature values to work. Factory resistance is 300 mOhm and the
* resistance values to the right are percentages of 300 mOhm.
*/
static const struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
{ 40, 120},
{ 30, 135},
{ 20, 165},
{ 10, 230},
{ 00, 325},
{-10, 445},
{-20, 595},
static struct power_supply_resistance_temp_table temp_to_batres_tbl_thermistor[] = {
{ .temp = 40, .resistance = 40 /* 120 mOhm */ },
{ .temp = 30, .resistance = 45 /* 135 mOhm */ },
{ .temp = 20, .resistance = 55 /* 165 mOhm */ },
{ .temp = 10, .resistance = 77 /* 230 mOhm */ },
{ .temp = 00, .resistance = 108 /* 325 mOhm */ },
{ .temp = -10, .resistance = 158 /* 445 mOhm */ },
{ .temp = -20, .resistance = 198 /* 595 mOhm */ },
};
/*
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
*/
static const struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
{ 60, 300},
{ 30, 300},
{ 20, 300},
{ 10, 300},
{ 00, 300},
{-10, 300},
{-20, 300},
};
/* battery resistance table for LI ION 9100 battery */
static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
{ 60, 180},
{ 30, 180},
{ 20, 180},
{ 10, 180},
{ 00, 180},
{-10, 180},
{-20, 180},
};
static struct ab8500_battery_type bat_type_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0,
.resis_low = 0,
.battery_resistance = 300,
.charge_full_design = 612,
.nominal_voltage = 3700,
.termination_vol = 4050,
.termination_curr = 200,
.recharge_cap = 95,
.normal_cur_lvl = 400,
.normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 53407,
.resis_low = 12500,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3600,
.termination_vol = 4150,
.termination_curr = 80,
.recharge_cap = 95,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor),
.r_to_t_tbl = ab8500_temp_tbl_a_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_a_thermistor),
.v_to_cap_tbl = cap_tbl_a_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 200000,
.resis_low = 82869,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3600,
.termination_vol = 4150,
.termination_curr = 80,
.recharge_cap = 95,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor),
.r_to_t_tbl = ab8500_temp_tbl_b_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_b_thermistor),
.v_to_cap_tbl = cap_tbl_b_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
};
static struct ab8500_battery_type bat_type_ext_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0,
.resis_low = 0,
.battery_resistance = 300,
.charge_full_design = 612,
.nominal_voltage = 3700,
.termination_vol = 4050,
.termination_curr = 200,
.recharge_cap = 95,
.normal_cur_lvl = 400,
.normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
/*
* These are the batteries that doesn't have an internal NTC resistor to measure
* its temperature. The temperature in this case is measure with a NTC placed
* near the battery but on the PCB.
*/
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 76000,
.resis_low = 53000,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_cap = 95,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 30000,
.resis_low = 10000,
.battery_resistance = 300,
.charge_full_design = 950,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_cap = 95,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 95000,
.resis_low = 76001,
.battery_resistance = 300,
.charge_full_design = 950,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_cap = 95,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
/* Default battery type for reference designs is the unknown type */
static struct ab8500_battery_type bat_type_thermistor_unknown = {
.resis_high = 0,
.resis_low = 0,
.maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
};
static const struct ab8500_bm_capacity_levels cap_levels = {
@ -409,8 +113,8 @@ static const struct ab8500_fg_parameters fg = {
.high_curr_time = 60,
.accu_charging = 30,
.accu_high_curr = 30,
.high_curr_threshold = 50,
.lowbat_threshold = 3100,
.high_curr_threshold_ua = 50000,
.lowbat_threshold_uv = 3100000,
.battok_falling_th_sel0 = 2860,
.battok_raising_th_sel1 = 2860,
.maint_thres = 95,
@ -424,41 +128,20 @@ static const struct ab8500_fg_parameters fg = {
static const struct ab8500_maxim_parameters ab8500_maxi_params = {
.ena_maxi = true,
.chg_curr = 910,
.chg_curr_ua = 910000,
.wait_cycles = 10,
.charger_curr_step = 100,
.charger_curr_step_ua = 100000,
};
static const struct ab8500_bm_charger_parameters chg = {
.usb_volt_max = 5500,
.usb_curr_max = 1500,
.ac_volt_max = 7500,
.ac_curr_max = 1500,
};
/*
* This array maps the raw hex value to charger output current used by the
* AB8500 values
*/
static int ab8500_charge_output_curr_map[] = {
100, 200, 300, 400, 500, 600, 700, 800,
900, 1000, 1100, 1200, 1300, 1400, 1500, 1500,
};
/*
* This array maps the raw hex value to charger input current used by the
* AB8500 values
*/
static int ab8500_charge_input_curr_map[] = {
50, 98, 193, 290, 380, 450, 500, 600,
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
.usb_volt_max_uv = 5500000,
.usb_curr_max_ua = 1500000,
.ac_volt_max_uv = 7500000,
.ac_curr_max_ua = 1500000,
};
/* This is referenced directly in the charger code */
struct ab8500_bm_data ab8500_bm_data = {
.temp_under = 3,
.temp_low = 8,
.temp_high = 43,
.temp_over = 48,
.main_safety_tmr_h = 4,
.temp_interval_chg = 20,
.temp_interval_nochg = 120,
@ -472,71 +155,91 @@ struct ab8500_bm_data ab8500_bm_data = {
.enable_overshoot = false,
.fg_res = 100,
.cap_levels = &cap_levels,
.bat_type = bat_type_thermistor,
.n_btypes = ARRAY_SIZE(bat_type_thermistor),
.batt_id = 0,
.bat_type = &bat_type_thermistor_unknown,
.interval_charging = 5,
.interval_not_charging = 120,
.temp_hysteresis = 3,
.gnd_lift_resistance = 34,
.chg_output_curr = ab8500_charge_output_curr_map,
.n_chg_out_curr = ARRAY_SIZE(ab8500_charge_output_curr_map),
.maxi = &ab8500_maxi_params,
.chg_params = &chg,
.fg_params = &fg,
.chg_input_curr = ab8500_charge_input_curr_map,
.n_chg_in_curr = ARRAY_SIZE(ab8500_charge_input_curr_map),
};
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
int ab8500_bm_of_probe(struct power_supply *psy,
struct ab8500_bm_data *bm)
{
const struct batres_vs_temp *tmp_batres_tbl;
struct device_node *battery_node;
const char *btech;
int i;
struct power_supply_battery_info *bi;
struct device *dev = &psy->dev;
int ret;
battery_node = of_parse_phandle(np, "monitored-battery", 0);
if (!battery_node) {
dev_err(dev, "battery node or reference missing\n");
return -EINVAL;
ret = power_supply_get_battery_info(psy, &bm->bi);
if (ret) {
dev_err(dev, "cannot retrieve battery info\n");
return ret;
}
bi = bm->bi;
/* Fill in defaults for any data missing from the device tree */
if (bi->charge_full_design_uah < 0)
/* The default capacity is 612 mAh for unknown batteries */
bi->charge_full_design_uah = 612000;
/*
* All of these voltages need to be specified or we will simply
* fall back to safe defaults.
*/
if ((bi->voltage_min_design_uv < 0) ||
(bi->voltage_max_design_uv < 0) ||
(bi->overvoltage_limit_uv < 0)) {
/* Nominal voltage is 3.7V for unknown batteries */
bi->voltage_min_design_uv = 3700000;
bi->voltage_max_design_uv = 3700000;
/* Termination voltage (overcharge limit) 4.05V */
bi->overvoltage_limit_uv = 4050000;
}
btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
if (!btech) {
dev_warn(dev, "missing property battery-name/type\n");
of_node_put(battery_node);
return -EINVAL;
if (bi->constant_charge_current_max_ua < 0)
bi->constant_charge_current_max_ua = 400000;
if (bi->constant_charge_voltage_max_uv < 0)
bi->constant_charge_voltage_max_uv = 4100000;
if (bi->charge_term_current_ua)
/* Charging stops when we drop below this current */
bi->charge_term_current_ua = 200000;
/*
* Internal resistance and factory resistance are tightly coupled
* so both MUST be defined or we fall back to defaults.
*/
if ((bi->factory_internal_resistance_uohm < 0) ||
!bi->resist_table) {
bi->factory_internal_resistance_uohm = 300000;
bi->resist_table = temp_to_batres_tbl_thermistor;
bi->resist_table_size = ARRAY_SIZE(temp_to_batres_tbl_thermistor);
}
if (strncmp(btech, "LION", 4) == 0) {
bm->no_maintenance = true;
bm->chg_unknown_bat = true;
bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
bm->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
bm->bat_type[BATTERY_UNKNOWN].recharge_cap = 95;
bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
if (!bi->ocv_table[0]) {
/* Default capacity table at say 25 degrees Celsius */
bi->ocv_temp[0] = 25;
bi->ocv_table[0] = ocv_cap_tbl;
bi->ocv_table_size[0] = ARRAY_SIZE(ocv_cap_tbl);
}
if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
if (strncmp(btech, "LION", 4) == 0)
tmp_batres_tbl = temp_to_batres_tbl_9100;
else
tmp_batres_tbl = temp_to_batres_tbl_thermistor;
} else {
bm->n_btypes = 4;
bm->bat_type = bat_type_ext_thermistor;
bm->adc_therm = AB8500_ADC_THERM_BATTEMP;
tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
}
/* select the battery resolution table */
for (i = 0; i < bm->n_btypes; ++i)
bm->bat_type[i].batres_tbl = tmp_batres_tbl;
of_node_put(battery_node);
if (bi->temp_min == INT_MIN)
bi->temp_min = AB8500_TEMP_UNDER;
if (bi->temp_max == INT_MAX)
bi->temp_max = AB8500_TEMP_OVER;
if (bi->temp_alert_min == INT_MIN)
bi->temp_alert_min = AB8500_TEMP_LOW;
if (bi->temp_alert_max == INT_MAX)
bi->temp_alert_max = AB8500_TEMP_HIGH;
bm->temp_hysteresis = AB8500_TEMP_HYSTERESIS;
return 0;
}
void ab8500_bm_of_remove(struct power_supply *psy,
struct ab8500_bm_data *bm)
{
power_supply_put_battery_info(psy, bm->bi);
}

View File

@ -451,15 +451,13 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
*/
static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
{
struct power_supply_battery_info *bi = di->bm->bi;
int temp, ret;
static int prev;
int rbat, rntc, vntc;
u8 id;
id = di->bm->batt_id;
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
id != BATTERY_UNKNOWN) {
if ((di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) &&
(bi && (bi->technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN))) {
rbat = ab8500_btemp_get_batctrl_res(di);
if (rbat < 0) {
@ -473,8 +471,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
}
temp = ab8500_btemp_res_to_temp(di,
di->bm->bat_type[id].r_to_t_tbl,
di->bm->bat_type[id].n_temp_tbl_elements, rbat);
di->bm->bat_type->r_to_t_tbl,
di->bm->bat_type->n_temp_tbl_elements, rbat);
} else {
ret = iio_read_channel_processed(di->btemp_ball, &vntc);
if (ret < 0) {
@ -490,8 +488,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
rntc = 230000 * vntc / (VTVOUT_V - vntc);
temp = ab8500_btemp_res_to_temp(di,
di->bm->bat_type[id].r_to_t_tbl,
di->bm->bat_type[id].n_temp_tbl_elements, rntc);
di->bm->bat_type->r_to_t_tbl,
di->bm->bat_type->n_temp_tbl_elements, rntc);
prev = temp;
}
dev_dbg(di->dev, "Battery temperature is %d\n", temp);
@ -512,7 +510,6 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
u8 i;
di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
di->bm->batt_id = BATTERY_UNKNOWN;
res = ab8500_btemp_get_batctrl_res(di);
if (res < 0) {
@ -520,40 +517,37 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
return -ENXIO;
}
/* BATTERY_UNKNOWN is defined on position 0, skip it! */
for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
if ((res <= di->bm->bat_type[i].resis_high) &&
(res >= di->bm->bat_type[i].resis_low)) {
dev_dbg(di->dev, "Battery detected on %s"
" low %d < res %d < high: %d"
" index: %d\n",
di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ?
"BATCTRL" : "BATTEMP",
di->bm->bat_type[i].resis_low, res,
di->bm->bat_type[i].resis_high, i);
di->bm->batt_id = i;
break;
}
}
if (di->bm->batt_id == BATTERY_UNKNOWN) {
if ((res <= di->bm->bat_type->resis_high) &&
(res >= di->bm->bat_type->resis_low)) {
dev_info(di->dev, "Battery detected on %s"
" low %d < res %d < high: %d"
" index: %d\n",
di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ?
"BATCTRL" : "BATTEMP",
di->bm->bat_type->resis_low, res,
di->bm->bat_type->resis_high, i);
} else {
dev_warn(di->dev, "Battery identified as unknown"
", resistance %d Ohm\n", res);
", resistance %d Ohm\n", res);
return -ENXIO;
}
/*
* We only have to change current source if the
* detected type is Type 1.
* detected type is Type 1 (LIPO) resis_high = 53407, resis_low = 12500
* if someone hacks this in.
*
* FIXME: make sure this is done automatically for the batteries
* that need it.
*/
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
di->bm->batt_id == 1) {
if ((di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) &&
(di->bm->bi && (di->bm->bi->technology == POWER_SUPPLY_TECHNOLOGY_LIPO)) &&
(res <= 53407) && (res >= 12500)) {
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
}
return di->bm->batt_id;
return 0;
}
/**
@ -814,7 +808,10 @@ static int ab8500_btemp_get_property(struct power_supply *psy,
val->intval = 1;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = di->bm->bat_type[di->bm->batt_id].name;
if (di->bm->bi)
val->intval = di->bm->bi->technology;
else
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = ab8500_btemp_get_temp(di);

View File

@ -46,8 +46,15 @@
/* Five minutes expressed in seconds */
#define FIVE_MINUTES_IN_SECONDS 300
#define CHARGALG_CURR_STEP_LOW 0
#define CHARGALG_CURR_STEP_HIGH 100
#define CHARGALG_CURR_STEP_LOW_UA 0
#define CHARGALG_CURR_STEP_HIGH_UA 100000
/*
* This is the battery capacity limit that will trigger a new
* full charging cycle in the case where maintenance charging
* has been disabled
*/
#define AB8500_RECHARGE_CAP 95
enum ab8500_chargers {
NO_CHG,
@ -63,14 +70,14 @@ struct ab8500_chargalg_charger_info {
enum ab8500_chargers charger_type;
bool usb_chg_ok;
bool ac_chg_ok;
int usb_volt;
int usb_curr;
int ac_volt;
int ac_curr;
int usb_vset;
int usb_iset;
int ac_vset;
int ac_iset;
int usb_volt_uv;
int usb_curr_ua;
int ac_volt_uv;
int ac_curr_ua;
int usb_vset_uv;
int usb_iset_ua;
int ac_vset_uv;
int ac_iset_ua;
};
struct ab8500_chargalg_suspension_status {
@ -81,14 +88,14 @@ struct ab8500_chargalg_suspension_status {
struct ab8500_chargalg_current_step_status {
bool curr_step_change;
int curr_step;
int curr_step_ua;
};
struct ab8500_chargalg_battery_data {
int temp;
int volt;
int avg_curr;
int inst_curr;
int volt_uv;
int avg_curr_ua;
int inst_curr_ua;
int percent;
};
@ -177,13 +184,13 @@ struct ab8500_chargalg_events {
/**
* struct ab8500_charge_curr_maximization - Charger maximization parameters
* @original_iset: the non optimized/maximised charger current
* @current_iset: the charging current used at this moment
* @test_delta_i: the delta between the current we want to charge and the
* @original_iset_ua: the non optimized/maximised charger current
* @current_iset_ua: the charging current used at this moment
* @test_delta_i_ua: the delta between the current we want to charge and the
current that is really going into the battery
* @condition_cnt: number of iterations needed before a new charger current
is set
* @max_current: maximum charger current
* @max_current_ua: maximum charger current
* @wait_cnt: to avoid too fast current step down in case of charger
* voltage collapse, we insert this delay between step
* down
@ -191,11 +198,11 @@ struct ab8500_chargalg_events {
increased
*/
struct ab8500_charge_curr_maximization {
int original_iset;
int current_iset;
int test_delta_i;
int original_iset_ua;
int current_iset_ua;
int test_delta_i_ua;
int condition_cnt;
int max_current;
int max_current_ua;
int wait_cnt;
u8 level;
};
@ -345,6 +352,8 @@ static void ab8500_chargalg_state_to(struct ab8500_chargalg *di,
static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di)
{
struct power_supply_battery_info *bi = di->bm->bi;
switch (di->charge_state) {
case STATE_NORMAL:
case STATE_MAINTENANCE_A:
@ -356,13 +365,13 @@ static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di)
if (di->chg_info.charger_type & USB_CHG) {
return di->usb_chg->ops.check_enable(di->usb_chg,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
bi->constant_charge_voltage_max_uv,
bi->constant_charge_current_max_ua);
} else if ((di->chg_info.charger_type & AC_CHG) &&
!(di->ac_chg->external)) {
return di->ac_chg->ops.check_enable(di->ac_chg,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
bi->constant_charge_voltage_max_uv,
bi->constant_charge_current_max_ua);
}
return 0;
}
@ -537,14 +546,14 @@ static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di)
* ab8500_chargalg_ac_en() - Turn on/off the AC charger
* @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
* @vset_uv: requested charger output voltage in microvolt
* @iset_ua: requested charger output current in microampere
*
* The AC charger will be turned on/off with the requested charge voltage and
* current
*/
static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
int vset_uv, int iset_ua)
{
static int ab8500_chargalg_ex_ac_enable_toggle;
@ -552,13 +561,13 @@ static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
return -ENXIO;
/* Select maximum of what both the charger and the battery supports */
if (di->ac_chg->max_out_volt)
vset = min(vset, di->ac_chg->max_out_volt);
if (di->ac_chg->max_out_curr)
iset = min(iset, di->ac_chg->max_out_curr);
if (di->ac_chg->max_out_volt_uv)
vset_uv = min(vset_uv, di->ac_chg->max_out_volt_uv);
if (di->ac_chg->max_out_curr_ua)
iset_ua = min(iset_ua, di->ac_chg->max_out_curr_ua);
di->chg_info.ac_iset = iset;
di->chg_info.ac_vset = vset;
di->chg_info.ac_iset_ua = iset_ua;
di->chg_info.ac_vset_uv = vset_uv;
/* Enable external charger */
if (enable && di->ac_chg->external &&
@ -568,47 +577,47 @@ static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
ab8500_chargalg_ex_ac_enable_toggle++;
}
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
return di->ac_chg->ops.enable(di->ac_chg, enable, vset_uv, iset_ua);
}
/**
* ab8500_chargalg_usb_en() - Turn on/off the USB charger
* @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
* @vset_uv: requested charger output voltage in microvolt
* @iset_ua: requested charger output current in microampere
*
* The USB charger will be turned on/off with the requested charge voltage and
* current
*/
static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
int vset_uv, int iset_ua)
{
if (!di->usb_chg || !di->usb_chg->ops.enable)
return -ENXIO;
/* Select maximum of what both the charger and the battery supports */
if (di->usb_chg->max_out_volt)
vset = min(vset, di->usb_chg->max_out_volt);
if (di->usb_chg->max_out_curr)
iset = min(iset, di->usb_chg->max_out_curr);
if (di->usb_chg->max_out_volt_uv)
vset_uv = min(vset_uv, di->usb_chg->max_out_volt_uv);
if (di->usb_chg->max_out_curr_ua)
iset_ua = min(iset_ua, di->usb_chg->max_out_curr_ua);
di->chg_info.usb_iset = iset;
di->chg_info.usb_vset = vset;
di->chg_info.usb_iset_ua = iset_ua;
di->chg_info.usb_vset_uv = vset_uv;
return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
return di->usb_chg->ops.enable(di->usb_chg, enable, vset_uv, iset_ua);
}
/**
* ab8500_chargalg_update_chg_curr() - Update charger current
* @di: pointer to the ab8500_chargalg structure
* @iset: requested charger output current
* @iset_ua: requested charger output current in microampere
*
* The charger output current will be updated for the charger
* that is currently in use
*/
static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
int iset)
int iset_ua)
{
/* Check if charger exists and update current if charging */
if (di->ac_chg && di->ac_chg->ops.update_curr &&
@ -617,24 +626,24 @@ static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
* Select maximum of what both the charger
* and the battery supports
*/
if (di->ac_chg->max_out_curr)
iset = min(iset, di->ac_chg->max_out_curr);
if (di->ac_chg->max_out_curr_ua)
iset_ua = min(iset_ua, di->ac_chg->max_out_curr_ua);
di->chg_info.ac_iset = iset;
di->chg_info.ac_iset_ua = iset_ua;
return di->ac_chg->ops.update_curr(di->ac_chg, iset);
return di->ac_chg->ops.update_curr(di->ac_chg, iset_ua);
} else if (di->usb_chg && di->usb_chg->ops.update_curr &&
di->chg_info.charger_type & USB_CHG) {
/*
* Select maximum of what both the charger
* and the battery supports
*/
if (di->usb_chg->max_out_curr)
iset = min(iset, di->usb_chg->max_out_curr);
if (di->usb_chg->max_out_curr_ua)
iset_ua = min(iset_ua, di->usb_chg->max_out_curr_ua);
di->chg_info.usb_iset = iset;
di->chg_info.usb_iset_ua = iset_ua;
return di->usb_chg->ops.update_curr(di->usb_chg, iset);
return di->usb_chg->ops.update_curr(di->usb_chg, iset_ua);
}
return -ENXIO;
@ -683,28 +692,28 @@ static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di)
/**
* ab8500_chargalg_start_charging() - Start the charger
* @di: pointer to the ab8500_chargalg structure
* @vset: requested charger output voltage
* @iset: requested charger output current
* @vset_uv: requested charger output voltage in microvolt
* @iset_ua: requested charger output current in microampere
*
* A charger will be enabled depending on the requested charger type that was
* detected previously.
*/
static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di,
int vset, int iset)
int vset_uv, int iset_ua)
{
switch (di->chg_info.charger_type) {
case AC_CHG:
dev_dbg(di->dev,
"AC parameters: Vset %d, Ich %d\n", vset, iset);
"AC parameters: Vset %d, Ich %d\n", vset_uv, iset_ua);
ab8500_chargalg_usb_en(di, false, 0, 0);
ab8500_chargalg_ac_en(di, true, vset, iset);
ab8500_chargalg_ac_en(di, true, vset_uv, iset_ua);
break;
case USB_CHG:
dev_dbg(di->dev,
"USB parameters: Vset %d, Ich %d\n", vset, iset);
"USB parameters: Vset %d, Ich %d\n", vset_uv, iset_ua);
ab8500_chargalg_ac_en(di, false, 0, 0);
ab8500_chargalg_usb_en(di, true, vset, iset);
ab8500_chargalg_usb_en(di, true, vset_uv, iset_ua);
break;
default:
@ -722,27 +731,29 @@ static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di,
*/
static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di)
{
if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
struct power_supply_battery_info *bi = di->bm->bi;
if (di->batt_data.temp > (bi->temp_alert_min + di->t_hyst_norm) &&
di->batt_data.temp < (bi->temp_alert_max - di->t_hyst_norm)) {
/* Temp OK! */
di->events.btemp_underover = false;
di->events.btemp_lowhigh = false;
di->t_hyst_norm = 0;
di->t_hyst_lowhigh = 0;
} else {
if (((di->batt_data.temp >= di->bm->temp_high) &&
if (((di->batt_data.temp >= bi->temp_alert_max) &&
(di->batt_data.temp <
(di->bm->temp_over - di->t_hyst_lowhigh))) ||
(bi->temp_max - di->t_hyst_lowhigh))) ||
((di->batt_data.temp >
(di->bm->temp_under + di->t_hyst_lowhigh)) &&
(di->batt_data.temp <= di->bm->temp_low))) {
(bi->temp_min + di->t_hyst_lowhigh)) &&
(di->batt_data.temp <= bi->temp_alert_min))) {
/* TEMP minor!!!!! */
di->events.btemp_underover = false;
di->events.btemp_lowhigh = true;
di->t_hyst_norm = di->bm->temp_hysteresis;
di->t_hyst_lowhigh = 0;
} else if (di->batt_data.temp <= di->bm->temp_under ||
di->batt_data.temp >= di->bm->temp_over) {
} else if (di->batt_data.temp <= bi->temp_min ||
di->batt_data.temp >= bi->temp_max) {
/* TEMP major!!!!! */
di->events.btemp_underover = true;
di->events.btemp_lowhigh = false;
@ -766,12 +777,12 @@ static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di)
*/
static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di)
{
if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
if (di->chg_info.usb_volt_uv > di->bm->chg_params->usb_volt_max_uv)
di->chg_info.usb_chg_ok = false;
else
di->chg_info.usb_chg_ok = true;
if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max)
if (di->chg_info.ac_volt_uv > di->bm->chg_params->ac_volt_max_uv)
di->chg_info.ac_chg_ok = false;
else
di->chg_info.ac_chg_ok = true;
@ -790,12 +801,12 @@ static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di)
{
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
di->charge_state == STATE_NORMAL &&
!di->maintenance_chg && (di->batt_data.volt >=
di->bm->bat_type[di->bm->batt_id].termination_vol ||
!di->maintenance_chg && (di->batt_data.volt_uv >=
di->bm->bi->overvoltage_limit_uv ||
di->events.usb_cv_active || di->events.ac_cv_active) &&
di->batt_data.avg_curr <
di->bm->bat_type[di->bm->batt_id].termination_curr &&
di->batt_data.avg_curr > 0) {
di->batt_data.avg_curr_ua <
di->bm->bi->charge_term_current_ua &&
di->batt_data.avg_curr_ua > 0) {
if (++di->eoc_cnt >= EOC_COND_CNT) {
di->eoc_cnt = 0;
di->charge_status = POWER_SUPPLY_STATUS_FULL;
@ -816,12 +827,12 @@ static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di)
static void init_maxim_chg_curr(struct ab8500_chargalg *di)
{
di->ccm.original_iset =
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
di->ccm.current_iset =
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
di->ccm.max_current = di->bm->maxi->chg_curr;
struct power_supply_battery_info *bi = di->bm->bi;
di->ccm.original_iset_ua = bi->constant_charge_current_max_ua;
di->ccm.current_iset_ua = bi->constant_charge_current_max_ua;
di->ccm.test_delta_i_ua = di->bm->maxi->charger_curr_step_ua;
di->ccm.max_current_ua = di->bm->maxi->chg_curr_ua;
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
di->ccm.level = 0;
}
@ -837,12 +848,12 @@ static void init_maxim_chg_curr(struct ab8500_chargalg *di)
*/
static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
{
int delta_i;
int delta_i_ua;
if (!di->bm->maxi->ena_maxi)
return MAXIM_RET_NOACTION;
delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
delta_i_ua = di->ccm.original_iset_ua - di->batt_data.inst_curr_ua;
if (di->events.vbus_collapsed) {
dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
@ -851,9 +862,9 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
dev_dbg(di->dev, "lowering current\n");
di->ccm.wait_cnt++;
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
di->ccm.max_current =
di->ccm.current_iset - di->ccm.test_delta_i;
di->ccm.current_iset = di->ccm.max_current;
di->ccm.max_current_ua =
di->ccm.current_iset_ua - di->ccm.test_delta_i_ua;
di->ccm.current_iset_ua = di->ccm.max_current_ua;
di->ccm.level--;
return MAXIM_RET_CHANGE;
} else {
@ -866,36 +877,36 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
di->ccm.wait_cnt = 0;
if (di->batt_data.inst_curr > di->ccm.original_iset) {
dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
" (limit %dmA) (current iset: %dmA)!\n",
di->batt_data.inst_curr, di->ccm.original_iset,
di->ccm.current_iset);
if (di->batt_data.inst_curr_ua > di->ccm.original_iset_ua) {
dev_dbg(di->dev, " Maximization Ibat (%duA) too high"
" (limit %duA) (current iset: %duA)!\n",
di->batt_data.inst_curr_ua, di->ccm.original_iset_ua,
di->ccm.current_iset_ua);
if (di->ccm.current_iset == di->ccm.original_iset)
if (di->ccm.current_iset_ua == di->ccm.original_iset_ua)
return MAXIM_RET_NOACTION;
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
di->ccm.current_iset = di->ccm.original_iset;
di->ccm.current_iset_ua = di->ccm.original_iset_ua;
di->ccm.level = 0;
return MAXIM_RET_IBAT_TOO_HIGH;
}
if (delta_i > di->ccm.test_delta_i &&
(di->ccm.current_iset + di->ccm.test_delta_i) <
di->ccm.max_current) {
if (delta_i_ua > di->ccm.test_delta_i_ua &&
(di->ccm.current_iset_ua + di->ccm.test_delta_i_ua) <
di->ccm.max_current_ua) {
if (di->ccm.condition_cnt-- == 0) {
/* Increse the iset with cco.test_delta_i */
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
di->ccm.current_iset += di->ccm.test_delta_i;
di->ccm.current_iset_ua += di->ccm.test_delta_i_ua;
di->ccm.level++;
dev_dbg(di->dev, " Maximization needed, increase"
" with %d mA to %dmA (Optimal ibat: %d)"
" with %d uA to %duA (Optimal ibat: %d uA)"
" Level %d\n",
di->ccm.test_delta_i,
di->ccm.current_iset,
di->ccm.original_iset,
di->ccm.test_delta_i_ua,
di->ccm.current_iset_ua,
di->ccm.original_iset_ua,
di->ccm.level);
return MAXIM_RET_CHANGE;
} else {
@ -909,6 +920,7 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
{
struct power_supply_battery_info *bi = di->bm->bi;
enum maxim_ret ret;
int result;
@ -916,13 +928,13 @@ static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
switch (ret) {
case MAXIM_RET_CHANGE:
result = ab8500_chargalg_update_chg_curr(di,
di->ccm.current_iset);
di->ccm.current_iset_ua);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
break;
case MAXIM_RET_IBAT_TOO_HIGH:
result = ab8500_chargalg_update_chg_curr(di,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
bi->constant_charge_current_max_ua);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
break;
@ -1158,13 +1170,13 @@ static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
switch (ext->desc->type) {
case POWER_SUPPLY_TYPE_BATTERY:
di->batt_data.volt = ret.intval / 1000;
di->batt_data.volt_uv = ret.intval;
break;
case POWER_SUPPLY_TYPE_MAINS:
di->chg_info.ac_volt = ret.intval / 1000;
di->chg_info.ac_volt_uv = ret.intval;
break;
case POWER_SUPPLY_TYPE_USB:
di->chg_info.usb_volt = ret.intval / 1000;
di->chg_info.usb_volt_uv = ret.intval;
break;
default:
break;
@ -1217,15 +1229,13 @@ static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_PROP_CURRENT_NOW:
switch (ext->desc->type) {
case POWER_SUPPLY_TYPE_MAINS:
di->chg_info.ac_curr =
ret.intval / 1000;
break;
di->chg_info.ac_curr_ua = ret.intval;
break;
case POWER_SUPPLY_TYPE_USB:
di->chg_info.usb_curr =
ret.intval / 1000;
di->chg_info.usb_curr_ua = ret.intval;
break;
case POWER_SUPPLY_TYPE_BATTERY:
di->batt_data.inst_curr = ret.intval / 1000;
di->batt_data.inst_curr_ua = ret.intval;
break;
default:
break;
@ -1235,7 +1245,7 @@ static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_PROP_CURRENT_AVG:
switch (ext->desc->type) {
case POWER_SUPPLY_TYPE_BATTERY:
di->batt_data.avg_curr = ret.intval / 1000;
di->batt_data.avg_curr_ua = ret.intval;
break;
case POWER_SUPPLY_TYPE_USB:
if (ret.intval)
@ -1289,9 +1299,10 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
*/
static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
{
struct power_supply_battery_info *bi = di->bm->bi;
int charger_status;
int ret;
int curr_step_lvl;
int curr_step_lvl_ua;
/* Collect data from all power_supply class devices */
class_for_each_device(power_supply_class, NULL,
@ -1395,9 +1406,9 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
"State %s Active_chg %d Chg_status %d AC %d USB %d "
"AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
"USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
di->batt_data.volt,
di->batt_data.avg_curr,
di->batt_data.inst_curr,
di->batt_data.volt_uv,
di->batt_data.avg_curr_ua,
di->batt_data.inst_curr_ua,
di->batt_data.temp,
di->batt_data.percent,
di->maintenance_chg,
@ -1410,12 +1421,12 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
di->chg_info.online_chg & USB_CHG,
di->events.ac_cv_active,
di->events.usb_cv_active,
di->chg_info.ac_curr,
di->chg_info.usb_curr,
di->chg_info.ac_vset,
di->chg_info.ac_iset,
di->chg_info.usb_vset,
di->chg_info.usb_iset);
di->chg_info.ac_curr_ua,
di->chg_info.usb_curr_ua,
di->chg_info.ac_vset_uv,
di->chg_info.ac_iset_ua,
di->chg_info.usb_vset_uv,
di->chg_info.usb_iset_ua);
switch (di->charge_state) {
case STATE_HANDHELD_INIT:
@ -1500,16 +1511,15 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
break;
case STATE_NORMAL_INIT:
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
if (di->curr_status.curr_step_ua == CHARGALG_CURR_STEP_LOW_UA)
ab8500_chargalg_stop_charging(di);
else {
curr_step_lvl = di->bm->bat_type[
di->bm->batt_id].normal_cur_lvl
* di->curr_status.curr_step
/ CHARGALG_CURR_STEP_HIGH;
curr_step_lvl_ua = bi->constant_charge_current_max_ua
* di->curr_status.curr_step_ua
/ CHARGALG_CURR_STEP_HIGH_UA;
ab8500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id]
.normal_vol_lvl, curr_step_lvl);
bi->constant_charge_voltage_max_uv,
curr_step_lvl_ua);
}
ab8500_chargalg_state_to(di, STATE_NORMAL);
@ -1543,21 +1553,17 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
fallthrough;
case STATE_WAIT_FOR_RECHARGE:
if (di->batt_data.percent <=
di->bm->bat_type[di->bm->batt_id].recharge_cap)
if (di->batt_data.percent <= AB8500_RECHARGE_CAP)
ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_MAINTENANCE_A_INIT:
ab8500_chargalg_stop_safety_timer(di);
ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_chg_timer_h);
di->bm->bat_type->maint_a_chg_timer_h);
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_a_cur_lvl);
di->bm->bat_type->maint_a_vol_lvl,
di->bm->bat_type->maint_a_cur_lvl);
ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
power_supply_changed(di->chargalg_psy);
fallthrough;
@ -1571,13 +1577,10 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
case STATE_MAINTENANCE_B_INIT:
ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_chg_timer_h);
di->bm->bat_type->maint_b_chg_timer_h);
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_b_cur_lvl);
di->bm->bat_type->maint_b_vol_lvl,
di->bm->bat_type->maint_b_cur_lvl);
ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
power_supply_changed(di->chargalg_psy);
fallthrough;
@ -1591,10 +1594,8 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
case STATE_TEMP_LOWHIGH_INIT:
ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].low_high_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].low_high_cur_lvl);
di->bm->bat_type->low_high_vol_lvl,
di->bm->bat_type->low_high_cur_lvl);
ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
@ -1722,7 +1723,7 @@ static int ab8500_chargalg_get_property(struct power_supply *psy,
if (di->events.batt_ovv) {
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
} else if (di->events.btemp_underover) {
if (di->batt_data.temp <= di->bm->temp_under)
if (di->batt_data.temp <= di->bm->bi->temp_min)
val->intval = POWER_SUPPLY_HEALTH_COLD;
else
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
@ -1744,7 +1745,7 @@ static int ab8500_chargalg_get_property(struct power_supply *psy,
static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n", di->curr_status.curr_step);
return sprintf(buf, "%d\n", di->curr_status.curr_step_ua);
}
static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di,
@ -1757,9 +1758,9 @@ static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di,
if (ret < 0)
return ret;
di->curr_status.curr_step = param;
if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
di->curr_status.curr_step_ua = param;
if (di->curr_status.curr_step_ua >= CHARGALG_CURR_STEP_LOW_UA &&
di->curr_status.curr_step_ua <= CHARGALG_CURR_STEP_HIGH_UA) {
di->curr_status.curr_step_change = true;
queue_work(di->chargalg_wq, &di->chargalg_work);
} else
@ -2056,7 +2057,7 @@ static int ab8500_chargalg_probe(struct platform_device *pdev)
dev_err(di->dev, "failed to create sysfs entry\n");
return ret;
}
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
di->curr_status.curr_step_ua = CHARGALG_CURR_STEP_HIGH_UA;
dev_info(di->dev, "probe success\n");
return component_add(dev, &ab8500_chargalg_component_ops);

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,6 @@
#include "ab8500-bm.h"
#define MILLI_TO_MICRO 1000
#define FG_LSB_IN_MA 1627
#define QLSB_NANO_AMP_HOURS_X10 1071
#define INS_CURR_TIMEOUT (3 * HZ)
@ -157,10 +156,10 @@ struct inst_curr_result_list {
* @dev: Pointer to the structure device
* @node: a list of AB8500 FGs, hence prepared for reentrance
* @irq holds the CCEOC interrupt number
* @vbat: Battery voltage in mV
* @vbat_nom: Nominal battery voltage in mV
* @inst_curr: Instantenous battery current in mA
* @avg_curr: Average battery current in mA
* @vbat_uv: Battery voltage in uV
* @vbat_nom_uv: Nominal battery voltage in uV
* @inst_curr_ua: Instantenous battery current in uA
* @avg_curr_ua: Average battery current in uA
* @bat_temp battery temperature
* @fg_samples: Number of samples used in the FG accumulation
* @accu_charge: Accumulated charge from the last conversion
@ -199,10 +198,10 @@ struct ab8500_fg {
struct device *dev;
struct list_head node;
int irq;
int vbat;
int vbat_nom;
int inst_curr;
int avg_curr;
int vbat_uv;
int vbat_nom_uv;
int inst_curr_ua;
int avg_curr_ua;
int bat_temp;
int fg_samples;
int accu_charge;
@ -266,84 +265,84 @@ static enum power_supply_property ab8500_fg_props[] = {
/*
* This array maps the raw hex value to lowbat voltage used by the AB8500
* Values taken from the UM0836
* Values taken from the UM0836, in microvolts.
*/
static int ab8500_fg_lowbat_voltage_map[] = {
2300 ,
2325 ,
2350 ,
2375 ,
2400 ,
2425 ,
2450 ,
2475 ,
2500 ,
2525 ,
2550 ,
2575 ,
2600 ,
2625 ,
2650 ,
2675 ,
2700 ,
2725 ,
2750 ,
2775 ,
2800 ,
2825 ,
2850 ,
2875 ,
2900 ,
2925 ,
2950 ,
2975 ,
3000 ,
3025 ,
3050 ,
3075 ,
3100 ,
3125 ,
3150 ,
3175 ,
3200 ,
3225 ,
3250 ,
3275 ,
3300 ,
3325 ,
3350 ,
3375 ,
3400 ,
3425 ,
3450 ,
3475 ,
3500 ,
3525 ,
3550 ,
3575 ,
3600 ,
3625 ,
3650 ,
3675 ,
3700 ,
3725 ,
3750 ,
3775 ,
3800 ,
3825 ,
3850 ,
3850 ,
2300000,
2325000,
2350000,
2375000,
2400000,
2425000,
2450000,
2475000,
2500000,
2525000,
2550000,
2575000,
2600000,
2625000,
2650000,
2675000,
2700000,
2725000,
2750000,
2775000,
2800000,
2825000,
2850000,
2875000,
2900000,
2925000,
2950000,
2975000,
3000000,
3025000,
3050000,
3075000,
3100000,
3125000,
3150000,
3175000,
3200000,
3225000,
3250000,
3275000,
3300000,
3325000,
3350000,
3375000,
3400000,
3425000,
3450000,
3475000,
3500000,
3525000,
3550000,
3575000,
3600000,
3625000,
3650000,
3675000,
3700000,
3725000,
3750000,
3775000,
3800000,
3825000,
3850000,
3850000,
};
static u8 ab8500_volt_to_regval(int voltage)
static u8 ab8500_volt_to_regval(int voltage_uv)
{
int i;
if (voltage < ab8500_fg_lowbat_voltage_map[0])
if (voltage_uv < ab8500_fg_lowbat_voltage_map[0])
return 0;
for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
if (voltage < ab8500_fg_lowbat_voltage_map[i])
if (voltage_uv < ab8500_fg_lowbat_voltage_map[i])
return (u8) i - 1;
}
@ -354,16 +353,16 @@ static u8 ab8500_volt_to_regval(int voltage)
/**
* ab8500_fg_is_low_curr() - Low or high current mode
* @di: pointer to the ab8500_fg structure
* @curr: the current to base or our decision on
* @curr_ua: the current to base or our decision on in microampere
*
* Low current mode if the current consumption is below a certain threshold
*/
static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr_ua)
{
/*
* We want to know if we're in low current mode
*/
if (curr > -di->bm->fg_params->high_curr_threshold)
if (curr_ua > -di->bm->fg_params->high_curr_threshold_ua)
return true;
else
return false;
@ -601,13 +600,13 @@ int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
/**
* ab8500_fg_inst_curr_finalize() - battery instantaneous current
* @di: pointer to the ab8500_fg structure
* @res: battery instantenous current(on success)
* @curr_ua: battery instantenous current in microampere (on success)
*
* Returns 0 or an error code
* Note: This is part "two" and has to be called at earliest 250 ms
* after ab8500_fg_inst_curr_start()
*/
int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *curr_ua)
{
u8 low, high;
int val;
@ -663,14 +662,13 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
/*
* Convert to unit value in mA
* Full scale input voltage is
* 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
* 63.160mV => LSB = 63.160mV/(4096*res) = 1.542.000 uA
* Given a 250ms conversion cycle time the LSB corresponds
* to 107.1 nAh. Convert to current by dividing by the conversion
* time in hours (250ms = 1 / (3600 * 4)h)
* 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm
*/
val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
(1000 * di->bm->fg_res);
val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) / di->bm->fg_res;
if (di->turn_off_fg) {
dev_dbg(di->dev, "%s Disable FG\n", __func__);
@ -688,7 +686,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
goto fail;
}
mutex_unlock(&di->cc_lock);
(*res) = val;
*curr_ua = val;
return 0;
fail:
@ -699,15 +697,15 @@ fail:
/**
* ab8500_fg_inst_curr_blocking() - battery instantaneous current
* @di: pointer to the ab8500_fg structure
* @res: battery instantenous current(on success)
*
* Returns 0 else error code
* Returns battery instantenous current in microampere (on success)
* else error code
*/
int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
{
int ret;
unsigned long timeout;
int res = 0;
int curr_ua = 0;
ret = ab8500_fg_inst_curr_start(di);
if (ret) {
@ -730,14 +728,14 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
}
}
ret = ab8500_fg_inst_curr_finalize(di, &res);
ret = ab8500_fg_inst_curr_finalize(di, &curr_ua);
if (ret) {
dev_err(di->dev, "Failed to finalize fg_inst\n");
return 0;
}
dev_dbg(di->dev, "%s instant current: %d", __func__, res);
return res;
dev_dbg(di->dev, "%s instant current: %d uA", __func__, curr_ua);
return curr_ua;
fail:
disable_irq(di->irq);
mutex_unlock(&di->cc_lock);
@ -797,13 +795,12 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
(100 * di->bm->fg_res);
/*
* Convert to unit value in mA
* Convert to unit value in uA
* by dividing by the conversion
* time in hours (= samples / (3600 * 4)h)
* and multiply with 1000
*/
di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
(1000 * di->bm->fg_res * (di->fg_samples / 4));
di->avg_curr_ua = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
(di->bm->fg_res * (di->fg_samples / 4));
di->flags.conv_done = true;
@ -825,7 +822,7 @@ exit:
* ab8500_fg_bat_voltage() - get battery voltage
* @di: pointer to the ab8500_fg structure
*
* Returns battery voltage(on success) else error code
* Returns battery voltage in microvolts (on success) else error code
*/
static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
{
@ -840,6 +837,8 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
return prev;
}
/* IIO returns millivolts but we want microvolts */
vbat *= 1000;
prev = vbat;
return vbat;
}
@ -847,41 +846,16 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
/**
* ab8500_fg_volt_to_capacity() - Voltage based capacity
* @di: pointer to the ab8500_fg structure
* @voltage: The voltage to convert to a capacity
* @voltage_uv: The voltage to convert to a capacity in microvolt
*
* Returns battery capacity in per mille based on voltage
*/
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage_uv)
{
int i, tbl_size;
const struct ab8500_v_to_cap *tbl;
int cap = 0;
struct power_supply_battery_info *bi = di->bm->bi;
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl;
tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements;
for (i = 0; i < tbl_size; ++i) {
if (voltage > tbl[i].voltage)
break;
}
if ((i > 0) && (i < tbl_size)) {
cap = fixp_linear_interpolate(
tbl[i].voltage,
tbl[i].capacity * 10,
tbl[i-1].voltage,
tbl[i-1].capacity * 10,
voltage);
} else if (i == 0) {
cap = 1000;
} else {
cap = 0;
}
dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
__func__, voltage, cap);
return cap;
/* Multiply by 10 because the capacity is tracked in per mille */
return power_supply_batinfo_ocv2cap(bi, voltage_uv, di->bat_temp) * 10;
}
/**
@ -893,8 +867,8 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
*/
static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
{
di->vbat = ab8500_fg_bat_voltage(di);
return ab8500_fg_volt_to_capacity(di, di->vbat);
di->vbat_uv = ab8500_fg_bat_voltage(di);
return ab8500_fg_volt_to_capacity(di, di->vbat_uv);
}
/**
@ -902,44 +876,35 @@ static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
* @di: pointer to the ab8500_fg structure
*
* Returns battery inner resistance added with the fuel gauge resistor value
* to get the total resistance in the whole link from gnd to bat+ node.
* to get the total resistance in the whole link from gnd to bat+ node
* in milliohm.
*/
static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
{
int i, tbl_size;
const struct batres_vs_temp *tbl;
int resist = 0;
struct power_supply_battery_info *bi = di->bm->bi;
int resistance_percent = 0;
int resistance;
tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements;
for (i = 0; i < tbl_size; ++i) {
if (di->bat_temp / 10 > tbl[i].temp)
break;
}
if ((i > 0) && (i < tbl_size)) {
resist = fixp_linear_interpolate(
tbl[i].temp,
tbl[i].resist,
tbl[i-1].temp,
tbl[i-1].resist,
di->bat_temp / 10);
} else if (i == 0) {
resist = tbl[0].resist;
} else {
resist = tbl[tbl_size - 1].resist;
}
resistance_percent = power_supply_temp2resist_simple(bi->resist_table,
bi->resist_table_size,
di->bat_temp / 10);
/*
* We get a percentage of factory resistance here so first get
* the factory resistance in milliohms then calculate how much
* resistance we have at this temperature.
*/
resistance = (bi->factory_internal_resistance_uohm / 1000);
resistance = resistance * resistance_percent / 100;
dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
" fg resistance %d, total: %d (mOhm)\n",
__func__, di->bat_temp, resist, di->bm->fg_res / 10,
(di->bm->fg_res / 10) + resist);
__func__, di->bat_temp, resistance, di->bm->fg_res / 10,
(di->bm->fg_res / 10) + resistance);
/* fg_res variable is in 0.1mOhm */
resist += di->bm->fg_res / 10;
resistance += di->bm->fg_res / 10;
return resist;
return resistance;
}
/**
@ -951,31 +916,34 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
*/
static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
{
int vbat_comp, res;
int vbat_comp_uv, res;
int i = 0;
int vbat = 0;
int vbat_uv = 0;
ab8500_fg_inst_curr_start(di);
do {
vbat += ab8500_fg_bat_voltage(di);
vbat_uv += ab8500_fg_bat_voltage(di);
i++;
usleep_range(5000, 6000);
} while (!ab8500_fg_inst_curr_done(di));
ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua);
di->vbat = vbat / i;
di->vbat_uv = vbat_uv / i;
res = ab8500_fg_battery_resistance(di);
/* Use Ohms law to get the load compensated voltage */
vbat_comp = di->vbat - (di->inst_curr * res) / 1000;
/*
* Use Ohms law to get the load compensated voltage.
* Divide by 1000 to get from milliohms to ohms.
*/
vbat_comp_uv = di->vbat_uv - (di->inst_curr_ua * res) / 1000;
dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
"R: %dmOhm, Current: %dmA Vbat Samples: %d\n",
__func__, di->vbat, vbat_comp, res, di->inst_curr, i);
dev_dbg(di->dev, "%s Measured Vbat: %d uV,Compensated Vbat %d uV, "
"R: %d mOhm, Current: %d uA Vbat Samples: %d\n",
__func__, di->vbat_uv, vbat_comp_uv, res, di->inst_curr_ua, i);
return ab8500_fg_volt_to_capacity(di, vbat_comp);
return ab8500_fg_volt_to_capacity(di, vbat_comp_uv);
}
/**
@ -1014,11 +982,16 @@ static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
u64 div_res;
u32 div_rem;
div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
div_rem = do_div(div_res, 1000);
/*
* Capacity is in milli ampere hours (10^-3)Ah
* Nominal voltage is in microvolts (10^-6)V
* divide by 1000000 after multiplication to get to mWh
*/
div_res = ((u64) cap_mah) * ((u64) di->vbat_nom_uv);
div_rem = do_div(div_res, 1000000);
/* Make sure to round upwards if necessary */
if (div_rem >= 1000 / 2)
if (div_rem >= 1000000 / 2)
div_res++;
return (int) div_res;
@ -1057,8 +1030,8 @@ static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
/* We need to update battery voltage and inst current when charging */
di->vbat = ab8500_fg_bat_voltage(di);
di->inst_curr = ab8500_fg_inst_curr_blocking(di);
di->vbat_uv = ab8500_fg_bat_voltage(di);
di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di);
return di->bat_cap.mah;
}
@ -1585,9 +1558,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
* RECOVERY_SLEEP if time left.
* If high, go to READOUT
*/
di->inst_curr = ab8500_fg_inst_curr_blocking(di);
di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di);
if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) {
if (di->recovery_cnt >
di->bm->fg_params->recovery_total_time) {
di->fg_samples = SEC_TO_SAMPLE(
@ -1620,9 +1593,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
break;
case AB8500_FG_DISCHARGE_READOUT:
di->inst_curr = ab8500_fg_inst_curr_blocking(di);
di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di);
if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) {
/* Detect mode change */
if (di->high_curr_mode) {
di->high_curr_mode = false;
@ -1768,9 +1741,9 @@ static void ab8500_fg_algorithm(struct ab8500_fg *di)
di->bat_cap.prev_mah,
di->bat_cap.prev_percent,
di->bat_cap.prev_level,
di->vbat,
di->inst_curr,
di->avg_curr,
di->vbat_uv,
di->inst_curr_ua,
di->avg_curr_ua,
di->accu_charge,
di->flags.charging,
di->charge_state,
@ -1863,15 +1836,15 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
*/
static void ab8500_fg_low_bat_work(struct work_struct *work)
{
int vbat;
int vbat_uv;
struct ab8500_fg *di = container_of(work, struct ab8500_fg,
fg_low_bat_work.work);
vbat = ab8500_fg_bat_voltage(di);
vbat_uv = ab8500_fg_bat_voltage(di);
/* Check if LOW_BAT still fulfilled */
if (vbat < di->bm->fg_params->lowbat_threshold) {
if (vbat_uv < di->bm->fg_params->lowbat_threshold_uv) {
/* Is it time to shut down? */
if (di->low_bat_cnt < 1) {
di->flags.low_bat = true;
@ -2101,15 +2074,15 @@ static int ab8500_fg_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (di->flags.bat_ovv)
val->intval = BATT_OVV_VALUE * 1000;
val->intval = BATT_OVV_VALUE;
else
val->intval = di->vbat * 1000;
val->intval = di->vbat_uv;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = di->inst_curr * 1000;
val->intval = di->inst_curr_ua;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
val->intval = di->avg_curr * 1000;
val->intval = di->avg_curr_ua;
break;
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
val->intval = ab8500_fg_convert_mah_to_uwh(di,
@ -2167,11 +2140,13 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
struct ab8500_fg *di;
struct power_supply_battery_info *bi;
union power_supply_propval ret;
int j;
psy = (struct power_supply *)data;
di = power_supply_get_drvdata(psy);
bi = di->bm->bi;
/*
* For all psy where the name of your driver
@ -2234,21 +2209,22 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
switch (ext->desc->type) {
case POWER_SUPPLY_TYPE_BATTERY:
if (!di->flags.batt_id_received &&
di->bm->batt_id != BATTERY_UNKNOWN) {
(bi && (bi->technology !=
POWER_SUPPLY_TECHNOLOGY_UNKNOWN))) {
const struct ab8500_battery_type *b;
b = &(di->bm->bat_type[di->bm->batt_id]);
b = di->bm->bat_type;
di->flags.batt_id_received = true;
di->bat_cap.max_mah_design =
MILLI_TO_MICRO *
b->charge_full_design;
di->bm->bi->charge_full_design_uah;
di->bat_cap.max_mah =
di->bat_cap.max_mah_design;
di->vbat_nom = b->nominal_voltage;
di->vbat_nom_uv =
di->bm->bi->voltage_max_design_uv;
}
if (ret.intval)
@ -2314,7 +2290,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
AB8500_SYS_CTRL2_BLOCK,
AB8500_LOW_BAT_REG,
ab8500_volt_to_regval(
di->bm->fg_params->lowbat_threshold) << 1 |
di->bm->fg_params->lowbat_threshold_uv) << 1 |
LOW_BAT_ENABLE);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
@ -3018,6 +2994,10 @@ static int ab8500_fg_bind(struct device *dev, struct device *master,
return -ENOMEM;
}
di->bat_cap.max_mah_design = di->bm->bi->charge_full_design_uah;
di->bat_cap.max_mah = di->bat_cap.max_mah_design;
di->vbat_nom_uv = di->bm->bi->voltage_max_design_uv;
/* Start the coulomb counter */
ab8500_fg_coulomb_counter(di, true);
/* Run the FG algorithm */
@ -3077,13 +3057,6 @@ static int ab8500_fg_probe(struct platform_device *pdev)
psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
psy_cfg.drv_data = di;
di->bat_cap.max_mah_design = MILLI_TO_MICRO *
di->bm->bat_type[di->bm->batt_id].charge_full_design;
di->bat_cap.max_mah = di->bat_cap.max_mah_design;
di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage;
di->init_capacity = true;
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);

View File

@ -561,7 +561,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
{
struct axp20x_batt_ps *axp20x_batt;
struct power_supply_config psy_cfg = {};
struct power_supply_battery_info info;
struct power_supply_battery_info *info;
struct device *dev = &pdev->dev;
if (!of_device_is_available(pdev->dev.of_node))
@ -615,8 +615,8 @@ static int axp20x_power_probe(struct platform_device *pdev)
}
if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
int vmin = info.voltage_min_design_uv;
int ccc = info.constant_charge_current_max_ua;
int vmin = info->voltage_min_design_uv;
int ccc = info->constant_charge_current_max_ua;
if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
vmin))

View File

@ -882,7 +882,7 @@ struct dt_init {
static int bd9995x_fw_probe(struct bd9995x_device *bd)
{
int ret;
struct power_supply_battery_info info;
struct power_supply_battery_info *info;
u32 property;
int i;
int regval;
@ -891,49 +891,41 @@ static int bd9995x_fw_probe(struct bd9995x_device *bd)
struct battery_init battery_inits[] = {
{
.name = "trickle-charging current",
.info_data = &info.tricklecharge_current_ua,
.range = &charging_current_ranges[0],
.ranges = 2,
.data = &init->itrich_set,
}, {
.name = "pre-charging current",
.info_data = &info.precharge_current_ua,
.range = &charging_current_ranges[0],
.ranges = 2,
.data = &init->iprech_set,
}, {
.name = "pre-to-trickle charge voltage threshold",
.info_data = &info.precharge_voltage_max_uv,
.range = &trickle_to_pre_threshold_ranges[0],
.ranges = 2,
.data = &init->vprechg_th_set,
}, {
.name = "charging termination current",
.info_data = &info.charge_term_current_ua,
.range = &charging_current_ranges[0],
.ranges = 2,
.data = &init->iterm_set,
}, {
.name = "charging re-start voltage",
.info_data = &info.charge_restart_voltage_uv,
.range = &charge_voltage_regulation_ranges[0],
.ranges = 2,
.data = &init->vrechg_set,
}, {
.name = "battery overvoltage limit",
.info_data = &info.overvoltage_limit_uv,
.range = &charge_voltage_regulation_ranges[0],
.ranges = 2,
.data = &init->vbatovp_set,
}, {
.name = "fast-charging max current",
.info_data = &info.constant_charge_current_max_ua,
.range = &fast_charge_current_ranges[0],
.ranges = 1,
.data = &init->ichg_set,
}, {
.name = "fast-charging voltage",
.info_data = &info.constant_charge_voltage_max_uv,
.range = &charge_voltage_regulation_ranges[0],
.ranges = 2,
.data = &init->vfastchg_reg_set1,
@ -966,6 +958,16 @@ static int bd9995x_fw_probe(struct bd9995x_device *bd)
if (ret < 0)
return ret;
/* Put pointers to the generic battery info */
battery_inits[0].info_data = &info->tricklecharge_current_ua;
battery_inits[1].info_data = &info->precharge_current_ua;
battery_inits[2].info_data = &info->precharge_voltage_max_uv;
battery_inits[3].info_data = &info->charge_term_current_ua;
battery_inits[4].info_data = &info->charge_restart_voltage_uv;
battery_inits[5].info_data = &info->overvoltage_limit_uv;
battery_inits[6].info_data = &info->constant_charge_current_max_ua;
battery_inits[7].info_data = &info->constant_charge_voltage_max_uv;
for (i = 0; i < ARRAY_SIZE(battery_inits); i++) {
int val = *battery_inits[i].info_data;
const struct linear_range *range = battery_inits[i].range;
@ -980,7 +982,7 @@ static int bd9995x_fw_probe(struct bd9995x_device *bd)
dev_err(bd->dev, "Unsupported value for %s\n",
battery_inits[i].name);
power_supply_put_battery_info(bd->charger, &info);
power_supply_put_battery_info(bd->charger, info);
return -EINVAL;
}
if (!found) {
@ -991,7 +993,7 @@ static int bd9995x_fw_probe(struct bd9995x_device *bd)
*(battery_inits[i].data) = regval;
}
power_supply_put_battery_info(bd->charger, &info);
power_supply_put_battery_info(bd->charger, info);
for (i = 0; i < ARRAY_SIZE(props); i++) {
ret = device_property_read_u32(bd->dev, props[i].prop,

View File

@ -1670,7 +1670,7 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)
static int bq24190_get_config(struct bq24190_dev_info *bdi)
{
const char * const s = "ti,system-minimum-microvolt";
struct power_supply_battery_info info = {};
struct power_supply_battery_info *info;
int v;
if (device_property_read_u32(bdi->dev, s, &v) == 0) {
@ -1684,7 +1684,7 @@ static int bq24190_get_config(struct bq24190_dev_info *bdi)
if (bdi->dev->of_node &&
!power_supply_get_battery_info(bdi->charger, &info)) {
v = info.precharge_current_ua / 1000;
v = info->precharge_current_ua / 1000;
if (v >= BQ24190_REG_PCTCC_IPRECHG_MIN
&& v <= BQ24190_REG_PCTCC_IPRECHG_MAX)
bdi->iprechg = v;
@ -1692,7 +1692,7 @@ static int bq24190_get_config(struct bq24190_dev_info *bdi)
dev_warn(bdi->dev, "invalid value for battery:precharge-current-microamp: %d\n",
v);
v = info.charge_term_current_ua / 1000;
v = info->charge_term_current_ua / 1000;
if (v >= BQ24190_REG_PCTCC_ITERM_MIN
&& v <= BQ24190_REG_PCTCC_ITERM_MAX)
bdi->iterm = v;

View File

@ -945,7 +945,7 @@ static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x,
static int bq2515x_hw_init(struct bq2515x_device *bq2515x)
{
int ret;
struct power_supply_battery_info bat_info = { };
struct power_supply_battery_info *bat_info;
ret = bq2515x_disable_watchdog_timers(bq2515x);
if (ret)
@ -969,13 +969,13 @@ static int bq2515x_hw_init(struct bq2515x_device *bq2515x)
} else {
bq2515x->init_data.ichg =
bat_info.constant_charge_current_max_ua;
bat_info->constant_charge_current_max_ua;
bq2515x->init_data.vbatreg =
bat_info.constant_charge_voltage_max_uv;
bat_info->constant_charge_voltage_max_uv;
bq2515x->init_data.iprechg =
bat_info.precharge_current_ua;
bat_info->precharge_current_ua;
}
ret = bq2515x_set_const_charge_current(bq2515x,

View File

@ -1504,7 +1504,7 @@ static int bq256xx_power_supply_init(struct bq256xx_device *bq,
static int bq256xx_hw_init(struct bq256xx_device *bq)
{
struct power_supply_battery_info bat_info = { };
struct power_supply_battery_info *bat_info;
int wd_reg_val = BQ256XX_WATCHDOG_DIS;
int ret = 0;
int i;
@ -1526,16 +1526,16 @@ static int bq256xx_hw_init(struct bq256xx_device *bq)
if (ret) {
dev_warn(bq->dev, "battery info missing, default values will be applied\n");
bat_info.constant_charge_current_max_ua =
bat_info->constant_charge_current_max_ua =
bq->chip_info->bq256xx_def_ichg;
bat_info.constant_charge_voltage_max_uv =
bat_info->constant_charge_voltage_max_uv =
bq->chip_info->bq256xx_def_vbatreg;
bat_info.precharge_current_ua =
bat_info->precharge_current_ua =
bq->chip_info->bq256xx_def_iprechg;
bat_info.charge_term_current_ua =
bat_info->charge_term_current_ua =
bq->chip_info->bq256xx_def_iterm;
bq->init_data.ichg_max =
@ -1545,10 +1545,10 @@ static int bq256xx_hw_init(struct bq256xx_device *bq)
bq->chip_info->bq256xx_max_vbatreg;
} else {
bq->init_data.ichg_max =
bat_info.constant_charge_current_max_ua;
bat_info->constant_charge_current_max_ua;
bq->init_data.vbatreg_max =
bat_info.constant_charge_voltage_max_uv;
bat_info->constant_charge_voltage_max_uv;
}
ret = bq->chip_info->bq256xx_set_vindpm(bq, bq->init_data.vindpm);
@ -1560,26 +1560,26 @@ static int bq256xx_hw_init(struct bq256xx_device *bq)
return ret;
ret = bq->chip_info->bq256xx_set_ichg(bq,
bat_info.constant_charge_current_max_ua);
bat_info->constant_charge_current_max_ua);
if (ret)
return ret;
ret = bq->chip_info->bq256xx_set_iprechg(bq,
bat_info.precharge_current_ua);
bat_info->precharge_current_ua);
if (ret)
return ret;
ret = bq->chip_info->bq256xx_set_vbatreg(bq,
bat_info.constant_charge_voltage_max_uv);
bat_info->constant_charge_voltage_max_uv);
if (ret)
return ret;
ret = bq->chip_info->bq256xx_set_iterm(bq,
bat_info.charge_term_current_ua);
bat_info->charge_term_current_ua);
if (ret)
return ret;
power_supply_put_battery_info(bq->charger, &bat_info);
power_supply_put_battery_info(bq->charger, bat_info);
return 0;
}

View File

@ -266,6 +266,7 @@ enum bq25890_table_ids {
/* lookup tables */
TBL_TREG,
TBL_BOOSTI,
TBL_TSPCT,
};
/* Thermal Regulation Threshold lookup table, in degrees Celsius */
@ -280,6 +281,28 @@ static const u32 bq25890_boosti_tbl[] = {
#define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl)
/* NTC 10K temperature lookup table in tenths of a degree */
static const u32 bq25890_tspct_tbl[] = {
850, 840, 830, 820, 810, 800, 790, 780,
770, 760, 750, 740, 730, 720, 710, 700,
690, 685, 680, 675, 670, 660, 650, 645,
640, 630, 620, 615, 610, 600, 590, 585,
580, 570, 565, 560, 550, 540, 535, 530,
520, 515, 510, 500, 495, 490, 480, 475,
470, 460, 455, 450, 440, 435, 430, 425,
420, 410, 405, 400, 390, 385, 380, 370,
365, 360, 355, 350, 340, 335, 330, 320,
310, 305, 300, 290, 285, 280, 275, 270,
260, 250, 245, 240, 230, 225, 220, 210,
205, 200, 190, 180, 175, 170, 160, 150,
145, 140, 130, 120, 115, 110, 100, 90,
80, 70, 60, 50, 40, 30, 20, 10,
0, -10, -20, -30, -40, -60, -70, -80,
-90, -10, -120, -140, -150, -170, -190, -210,
};
#define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl)
struct bq25890_range {
u32 min;
u32 max;
@ -308,7 +331,8 @@ static const union {
/* lookup tables */
[TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
[TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
[TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} },
[TBL_TSPCT] = { .lt = {bq25890_tspct_tbl, BQ25890_TSPCT_TBL_SIZE} }
};
static int bq25890_field_read(struct bq25890_device *bq,
@ -388,6 +412,7 @@ static bool bq25890_is_adc_property(enum power_supply_property psp)
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_TEMP:
return true;
default:
@ -528,6 +553,15 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
val->intval = ret * -50000;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = bq25890_field_read(bq, F_TSPCT);
if (ret < 0)
return ret;
/* convert TS percentage into rough temperature */
val->intval = bq25890_find_val(ret, TBL_TSPCT);
break;
default:
return -EINVAL;
}
@ -713,6 +747,7 @@ static const enum power_supply_property bq25890_power_supply_props[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
};
static char *bq25890_charger_supplied_to[] = {

View File

@ -1079,7 +1079,7 @@ static int bq25980_power_supply_init(struct bq25980_device *bq,
static int bq25980_hw_init(struct bq25980_device *bq)
{
struct power_supply_battery_info bat_info = { };
struct power_supply_battery_info *bat_info;
int wd_reg_val = BQ25980_WATCHDOG_DIS;
int wd_max_val = BQ25980_NUM_WD_VAL - 1;
int ret = 0;
@ -1112,8 +1112,8 @@ static int bq25980_hw_init(struct bq25980_device *bq)
return -EINVAL;
}
bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua;
bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv;
bq->init_data.ichg_max = bat_info->constant_charge_current_max_ua;
bq->init_data.vreg_max = bat_info->constant_charge_voltage_max_uv;
if (bq->state.bypass) {
ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,

View File

@ -1474,7 +1474,7 @@ static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
static void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
{
struct power_supply_battery_info info = {};
struct power_supply_battery_info *info;
unsigned int min, max;
if (power_supply_get_battery_info(di->bat, &info) < 0)
@ -1485,43 +1485,43 @@ static void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
return;
}
if (info.energy_full_design_uwh != info.charge_full_design_uah) {
if (info.energy_full_design_uwh == -EINVAL)
if (info->energy_full_design_uwh != info->charge_full_design_uah) {
if (info->energy_full_design_uwh == -EINVAL)
dev_warn(di->dev, "missing battery:energy-full-design-microwatt-hours\n");
else if (info.charge_full_design_uah == -EINVAL)
else if (info->charge_full_design_uah == -EINVAL)
dev_warn(di->dev, "missing battery:charge-full-design-microamp-hours\n");
}
/* assume min == 0 */
max = di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].max;
if (info.energy_full_design_uwh > max * 1000) {
if (info->energy_full_design_uwh > max * 1000) {
dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n",
info.energy_full_design_uwh);
info.energy_full_design_uwh = -EINVAL;
info->energy_full_design_uwh);
info->energy_full_design_uwh = -EINVAL;
}
/* assume min == 0 */
max = di->dm_regs[BQ27XXX_DM_DESIGN_CAPACITY].max;
if (info.charge_full_design_uah > max * 1000) {
if (info->charge_full_design_uah > max * 1000) {
dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n",
info.charge_full_design_uah);
info.charge_full_design_uah = -EINVAL;
info->charge_full_design_uah);
info->charge_full_design_uah = -EINVAL;
}
min = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].min;
max = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].max;
if ((info.voltage_min_design_uv < min * 1000 ||
info.voltage_min_design_uv > max * 1000) &&
info.voltage_min_design_uv != -EINVAL) {
if ((info->voltage_min_design_uv < min * 1000 ||
info->voltage_min_design_uv > max * 1000) &&
info->voltage_min_design_uv != -EINVAL) {
dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n",
info.voltage_min_design_uv);
info.voltage_min_design_uv = -EINVAL;
info->voltage_min_design_uv);
info->voltage_min_design_uv = -EINVAL;
}
if ((info.energy_full_design_uwh != -EINVAL &&
info.charge_full_design_uah != -EINVAL) ||
info.voltage_min_design_uv != -EINVAL)
bq27xxx_battery_set_config(di, &info);
if ((info->energy_full_design_uwh != -EINVAL &&
info->charge_full_design_uah != -EINVAL) ||
info->voltage_min_design_uv != -EINVAL)
bq27xxx_battery_set_config(di, info);
}
/*

View File

@ -61,7 +61,7 @@ struct cw_battery {
struct delayed_work battery_delay_work;
struct regmap *regmap;
struct power_supply *rk_bat;
struct power_supply_battery_info battery;
struct power_supply_battery_info *battery;
u8 *bat_profile;
bool charger_attached;
@ -505,22 +505,22 @@ 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;
if (cw_bat->battery->charge_full_design_uah > 0)
val->intval = cw_bat->battery->charge_full_design_uah;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = cw_bat->battery.charge_full_design_uah;
val->intval = cw_bat->battery->charge_full_design_uah;
val->intval = val->intval * cw_bat->soc / 100;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (cw_battery_valid_time_to_empty(cw_bat) &&
cw_bat->battery.charge_full_design_uah > 0) {
cw_bat->battery->charge_full_design_uah > 0) {
/* calculate remaining capacity */
val->intval = cw_bat->battery.charge_full_design_uah;
val->intval = cw_bat->battery->charge_full_design_uah;
val->intval = val->intval * cw_bat->soc / 100;
/* estimate current based on time to empty */
@ -687,6 +687,12 @@ static int cw_bat_probe(struct i2c_client *client)
ret = power_supply_get_battery_info(cw_bat->rk_bat, &cw_bat->battery);
if (ret) {
/* Allocate an empty battery */
cw_bat->battery = devm_kzalloc(&client->dev,
sizeof(cw_bat->battery),
GFP_KERNEL);
if (!cw_bat->battery)
return -ENOMEM;
dev_warn(cw_bat->dev,
"No monitored battery, some properties will be missing\n");
}
@ -724,7 +730,7 @@ static int cw_bat_remove(struct i2c_client *client)
struct cw_battery *cw_bat = i2c_get_clientdata(client);
cancel_delayed_work_sync(&cw_bat->battery_delay_work);
power_supply_put_battery_info(cw_bat->rk_bat, &cw_bat->battery);
power_supply_put_battery_info(cw_bat->rk_bat, cw_bat->battery);
return 0;
}

View File

@ -18,7 +18,7 @@ struct ingenic_battery {
struct iio_channel *channel;
struct power_supply_desc desc;
struct power_supply *battery;
struct power_supply_battery_info info;
struct power_supply_battery_info *info;
};
static int ingenic_battery_get_property(struct power_supply *psy,
@ -26,7 +26,7 @@ static int ingenic_battery_get_property(struct power_supply *psy,
union power_supply_propval *val)
{
struct ingenic_battery *bat = power_supply_get_drvdata(psy);
struct power_supply_battery_info *info = &bat->info;
struct power_supply_battery_info *info = bat->info;
int ret;
switch (psp) {
@ -80,7 +80,7 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat)
if (ret != IIO_AVAIL_LIST || scale_type != IIO_VAL_FRACTIONAL_LOG2)
return -EINVAL;
max_mV = bat->info.voltage_max_design_uv / 1000;
max_mV = bat->info->voltage_max_design_uv / 1000;
for (i = 0; i < scale_len; i += 2) {
u64 scale_mV = (max_raw * scale_raw[i]) >> scale_raw[i + 1];
@ -156,13 +156,13 @@ static int ingenic_battery_probe(struct platform_device *pdev)
dev_err(dev, "Unable to get battery info: %d\n", ret);
return ret;
}
if (bat->info.voltage_min_design_uv < 0) {
if (bat->info->voltage_min_design_uv < 0) {
dev_err(dev, "Unable to get voltage min design\n");
return bat->info.voltage_min_design_uv;
return bat->info->voltage_min_design_uv;
}
if (bat->info.voltage_max_design_uv < 0) {
if (bat->info->voltage_max_design_uv < 0) {
dev_err(dev, "Unable to get voltage max design\n");
return bat->info.voltage_max_design_uv;
return bat->info->voltage_max_design_uv;
}
return ingenic_battery_set_scale(bat);

View File

@ -0,0 +1,509 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* max77976_charger.c - Driver for the Maxim MAX77976 battery charger
*
* Copyright (C) 2021 Luca Ceresoli
* Author: Luca Ceresoli <luca@lucaceresoli.net>
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#define MAX77976_DRIVER_NAME "max77976-charger"
#define MAX77976_CHIP_ID 0x76
static const char *max77976_manufacturer = "Maxim Integrated";
static const char *max77976_model = "MAX77976";
/* --------------------------------------------------------------------------
* Register map
*/
#define MAX77976_REG_CHIP_ID 0x00
#define MAX77976_REG_CHIP_REVISION 0x01
#define MAX77976_REG_CHG_INT_OK 0x12
#define MAX77976_REG_CHG_DETAILS_01 0x14
#define MAX77976_REG_CHG_CNFG_00 0x16
#define MAX77976_REG_CHG_CNFG_02 0x18
#define MAX77976_REG_CHG_CNFG_06 0x1c
#define MAX77976_REG_CHG_CNFG_09 0x1f
/* CHG_DETAILS_01.CHG_DTLS values */
enum max77976_charging_state {
MAX77976_CHARGING_PREQUALIFICATION = 0x0,
MAX77976_CHARGING_FAST_CONST_CURRENT,
MAX77976_CHARGING_FAST_CONST_VOLTAGE,
MAX77976_CHARGING_TOP_OFF,
MAX77976_CHARGING_DONE,
MAX77976_CHARGING_RESERVED_05,
MAX77976_CHARGING_TIMER_FAULT,
MAX77976_CHARGING_SUSPENDED_QBATT_OFF,
MAX77976_CHARGING_OFF,
MAX77976_CHARGING_RESERVED_09,
MAX77976_CHARGING_THERMAL_SHUTDOWN,
MAX77976_CHARGING_WATCHDOG_EXPIRED,
MAX77976_CHARGING_SUSPENDED_JEITA,
MAX77976_CHARGING_SUSPENDED_THM_REMOVAL,
MAX77976_CHARGING_SUSPENDED_PIN,
MAX77976_CHARGING_RESERVED_0F,
};
/* CHG_DETAILS_01.BAT_DTLS values */
enum max77976_battery_state {
MAX77976_BATTERY_BATTERY_REMOVAL = 0x0,
MAX77976_BATTERY_PREQUALIFICATION,
MAX77976_BATTERY_TIMER_FAULT,
MAX77976_BATTERY_REGULAR_VOLTAGE,
MAX77976_BATTERY_LOW_VOLTAGE,
MAX77976_BATTERY_OVERVOLTAGE,
MAX77976_BATTERY_RESERVED,
MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present
};
/* CHG_CNFG_00.MODE values */
enum max77976_mode {
MAX77976_MODE_CHARGER_BUCK = 0x5,
MAX77976_MODE_BOOST = 0x9,
};
/* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */
#define MAX77976_CHG_CC_STEP 50000U
#define MAX77976_CHG_CC_MIN 100000U
#define MAX77976_CHG_CC_MAX 5500000U
/* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */
#define MAX77976_CHGIN_ILIM_STEP 100000U
#define MAX77976_CHGIN_ILIM_MIN 100000U
#define MAX77976_CHGIN_ILIM_MAX 3200000U
enum max77976_field_idx {
VERSION, REVISION, /* CHIP_REVISION */
CHGIN_OK, /* CHG_INT_OK */
BAT_DTLS, CHG_DTLS, /* CHG_DETAILS_01 */
MODE, /* CHG_CNFG_00 */
CHG_CC, /* CHG_CNFG_02 */
CHGPROT, /* CHG_CNFG_06 */
CHGIN_ILIM, /* CHG_CNFG_09 */
MAX77976_N_REGMAP_FIELDS
};
static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = {
[VERSION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 4, 7),
[REVISION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 0, 3),
[CHGIN_OK] = REG_FIELD(MAX77976_REG_CHG_INT_OK, 6, 6),
[CHG_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 0, 3),
[BAT_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 4, 6),
[MODE] = REG_FIELD(MAX77976_REG_CHG_CNFG_00, 0, 3),
[CHG_CC] = REG_FIELD(MAX77976_REG_CHG_CNFG_02, 0, 6),
[CHGPROT] = REG_FIELD(MAX77976_REG_CHG_CNFG_06, 2, 3),
[CHGIN_ILIM] = REG_FIELD(MAX77976_REG_CHG_CNFG_09, 0, 5),
};
static const struct regmap_config max77976_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x24,
};
/* --------------------------------------------------------------------------
* Data structures
*/
struct max77976 {
struct i2c_client *client;
struct regmap *regmap;
struct regmap_field *rfield[MAX77976_N_REGMAP_FIELDS];
};
/* --------------------------------------------------------------------------
* power_supply properties
*/
static int max77976_get_status(struct max77976 *chg, int *val)
{
unsigned int regval;
int err;
err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
if (err < 0)
return err;
switch (regval) {
case MAX77976_CHARGING_PREQUALIFICATION:
case MAX77976_CHARGING_FAST_CONST_CURRENT:
case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
case MAX77976_CHARGING_TOP_OFF:
*val = POWER_SUPPLY_STATUS_CHARGING;
break;
case MAX77976_CHARGING_DONE:
*val = POWER_SUPPLY_STATUS_FULL;
break;
case MAX77976_CHARGING_TIMER_FAULT:
case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
case MAX77976_CHARGING_SUSPENDED_JEITA:
case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
case MAX77976_CHARGING_SUSPENDED_PIN:
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case MAX77976_CHARGING_OFF:
case MAX77976_CHARGING_THERMAL_SHUTDOWN:
case MAX77976_CHARGING_WATCHDOG_EXPIRED:
*val = POWER_SUPPLY_STATUS_DISCHARGING;
break;
default:
*val = POWER_SUPPLY_STATUS_UNKNOWN;
}
return 0;
}
static int max77976_get_charge_type(struct max77976 *chg, int *val)
{
unsigned int regval;
int err;
err = regmap_field_read(chg->rfield[CHG_DTLS], &regval);
if (err < 0)
return err;
switch (regval) {
case MAX77976_CHARGING_PREQUALIFICATION:
*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case MAX77976_CHARGING_FAST_CONST_CURRENT:
case MAX77976_CHARGING_FAST_CONST_VOLTAGE:
*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case MAX77976_CHARGING_TOP_OFF:
*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
break;
case MAX77976_CHARGING_DONE:
case MAX77976_CHARGING_TIMER_FAULT:
case MAX77976_CHARGING_SUSPENDED_QBATT_OFF:
case MAX77976_CHARGING_OFF:
case MAX77976_CHARGING_THERMAL_SHUTDOWN:
case MAX77976_CHARGING_WATCHDOG_EXPIRED:
case MAX77976_CHARGING_SUSPENDED_JEITA:
case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL:
case MAX77976_CHARGING_SUSPENDED_PIN:
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
default:
*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
}
return 0;
}
static int max77976_get_health(struct max77976 *chg, int *val)
{
unsigned int regval;
int err;
err = regmap_field_read(chg->rfield[BAT_DTLS], &regval);
if (err < 0)
return err;
switch (regval) {
case MAX77976_BATTERY_BATTERY_REMOVAL:
*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
break;
case MAX77976_BATTERY_LOW_VOLTAGE:
case MAX77976_BATTERY_REGULAR_VOLTAGE:
*val = POWER_SUPPLY_HEALTH_GOOD;
break;
case MAX77976_BATTERY_TIMER_FAULT:
*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
break;
case MAX77976_BATTERY_OVERVOLTAGE:
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
break;
case MAX77976_BATTERY_PREQUALIFICATION:
case MAX77976_BATTERY_BATTERY_ONLY:
*val = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
default:
*val = POWER_SUPPLY_HEALTH_UNKNOWN;
}
return 0;
}
static int max77976_get_online(struct max77976 *chg, int *val)
{
unsigned int regval;
int err;
err = regmap_field_read(chg->rfield[CHGIN_OK], &regval);
if (err < 0)
return err;
*val = (regval ? 1 : 0);
return 0;
}
static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx,
unsigned int clamp_min, unsigned int clamp_max,
unsigned int mult, int *val)
{
unsigned int regval;
int err;
err = regmap_field_read(chg->rfield[fidx], &regval);
if (err < 0)
return err;
*val = clamp_val(regval * mult, clamp_min, clamp_max);
return 0;
}
static int max77976_set_integer(struct max77976 *chg, enum max77976_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 max77976_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max77976 *chg = power_supply_get_drvdata(psy);
int err = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
err = max77976_get_status(chg, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
err = max77976_get_charge_type(chg, &val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
err = max77976_get_health(chg, &val->intval);
break;
case POWER_SUPPLY_PROP_ONLINE:
err = max77976_get_online(chg, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
val->intval = MAX77976_CHG_CC_MAX;
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
err = max77976_get_integer(chg, CHG_CC,
MAX77976_CHG_CC_MIN,
MAX77976_CHG_CC_MAX,
MAX77976_CHG_CC_STEP,
&val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
err = max77976_get_integer(chg, CHGIN_ILIM,
MAX77976_CHGIN_ILIM_MIN,
MAX77976_CHGIN_ILIM_MAX,
MAX77976_CHGIN_ILIM_STEP,
&val->intval);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = max77976_model;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = max77976_manufacturer;
break;
default:
err = -EINVAL;
}
return err;
}
static int max77976_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct max77976 *chg = power_supply_get_drvdata(psy);
int err = 0;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
err = max77976_set_integer(chg, CHG_CC,
MAX77976_CHG_CC_MIN,
MAX77976_CHG_CC_MAX,
MAX77976_CHG_CC_STEP,
val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
err = max77976_set_integer(chg, CHGIN_ILIM,
MAX77976_CHGIN_ILIM_MIN,
MAX77976_CHGIN_ILIM_MAX,
MAX77976_CHGIN_ILIM_STEP,
val->intval);
break;
default:
err = -EINVAL;
}
return err;
};
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_INPUT_CURRENT_LIMIT:
return true;
default:
return false;
}
}
static enum power_supply_property max77976_psy_props[] = {
POWER_SUPPLY_PROP_STATUS,
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_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static const struct power_supply_desc max77976_psy_desc = {
.name = MAX77976_DRIVER_NAME,
.type = POWER_SUPPLY_TYPE_USB,
.properties = max77976_psy_props,
.num_properties = ARRAY_SIZE(max77976_psy_props),
.get_property = max77976_get_property,
.set_property = max77976_set_property,
.property_is_writeable = max77976_property_is_writeable,
};
/* --------------------------------------------------------------------------
* Entry point
*/
static int max77976_detect(struct max77976 *chg)
{
struct device *dev = &chg->client->dev;
unsigned int id, ver, rev;
int err;
err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id);
if (err)
return dev_err_probe(dev, err, "cannot read chip ID\n");
if (id != MAX77976_CHIP_ID)
return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id);
err = regmap_field_read(chg->rfield[VERSION], &ver);
if (!err)
err = regmap_field_read(chg->rfield[REVISION], &rev);
if (err)
return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n");
dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev);
return 0;
}
static int max77976_configure(struct max77976 *chg)
{
struct device *dev = &chg->client->dev;
int err;
/* Magic value to unlock writing to some registers */
err = regmap_field_write(chg->rfield[CHGPROT], 0x3);
if (err)
goto err;
/*
* Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF.
* Other modes are not implemented by this driver.
*/
err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK);
if (err)
goto err;
return 0;
err:
return dev_err_probe(dev, err, "error while configuring");
}
static int max77976_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct power_supply_config psy_cfg = {};
struct power_supply *psy;
struct max77976 *chg;
int err;
int i;
chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
if (!chg)
return -ENOMEM;
i2c_set_clientdata(client, chg);
psy_cfg.drv_data = chg;
chg->client = client;
chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config);
if (IS_ERR(chg->regmap))
return dev_err_probe(dev, PTR_ERR(chg->regmap),
"cannot allocate regmap\n");
for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) {
chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap,
max77976_reg_field[i]);
if (IS_ERR(chg->rfield[i]))
return dev_err_probe(dev, PTR_ERR(chg->rfield[i]),
"cannot allocate regmap field\n");
}
err = max77976_detect(chg);
if (err)
return err;
err = max77976_configure(chg);
if (err)
return err;
psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg);
if (IS_ERR(psy))
return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n");
return 0;
}
static const struct i2c_device_id max77976_i2c_id[] = {
{ MAX77976_DRIVER_NAME, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, max77976_i2c_id);
static const struct of_device_id max77976_of_id[] = {
{ .compatible = "maxim,max77976" },
{ },
};
MODULE_DEVICE_TABLE(of, max77976_of_id);
static struct i2c_driver max77976_driver = {
.driver = {
.name = MAX77976_DRIVER_NAME,
.of_match_table = max77976_of_id,
},
.probe_new = max77976_probe,
.id_table = max77976_i2c_id,
};
module_i2c_driver(max77976_driver);
MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
MODULE_DESCRIPTION("Maxim MAX77976 charger driver");
MODULE_LICENSE("GPL v2");

View File

@ -21,6 +21,7 @@
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/thermal.h>
#include <linux/fixp-arith.h>
#include "power_supply.h"
/* exported for the APM Power driver, APM emulation */
@ -563,14 +564,19 @@ EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
#endif /* CONFIG_OF */
int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info)
struct power_supply_battery_info **info_out)
{
struct power_supply_resistance_temp_table *resist_table;
struct power_supply_battery_info *info;
struct device_node *battery_np;
const char *value;
int err, len, index;
const __be32 *list;
info = devm_kmalloc(&psy->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL;
@ -580,6 +586,10 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->charge_term_current_ua = -EINVAL;
info->constant_charge_current_max_ua = -EINVAL;
info->constant_charge_voltage_max_uv = -EINVAL;
info->tricklecharge_current_ua = -EINVAL;
info->precharge_voltage_max_uv = -EINVAL;
info->charge_restart_voltage_uv = -EINVAL;
info->overvoltage_limit_uv = -EINVAL;
info->temp_ambient_alert_min = INT_MIN;
info->temp_ambient_alert_max = INT_MAX;
info->temp_alert_min = INT_MIN;
@ -727,7 +737,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
list = of_get_property(battery_np, "resistance-temp-table", &len);
if (!list || !len)
goto out_put_node;
goto out_ret_pointer;
info->resist_table_size = len / (2 * sizeof(__be32));
resist_table = info->resist_table = devm_kcalloc(&psy->dev,
@ -745,6 +755,10 @@ int power_supply_get_battery_info(struct power_supply *psy,
resist_table[index].resistance = be32_to_cpu(*list++);
}
out_ret_pointer:
/* Finally return the whole thing */
*info_out = info;
out_put_node:
of_node_put(battery_np);
return err;
@ -763,6 +777,8 @@ void power_supply_put_battery_info(struct power_supply *psy,
if (info->resist_table)
devm_kfree(&psy->dev, info->resist_table);
devm_kfree(&psy->dev, info);
}
EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
@ -783,26 +799,25 @@ EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
int table_len, int temp)
{
int i, resist;
int i, high, low;
for (i = 0; i < table_len; i++)
/* Break loop at table_len - 1 because that is the highest index */
for (i = 0; i < table_len - 1; i++)
if (temp > table[i].temp)
break;
if (i > 0 && i < table_len) {
int tmp;
/* The library function will deal with high == low */
if ((i == 0) || (i == (table_len - 1)))
high = i;
else
high = i - 1;
low = i;
tmp = (table[i - 1].resistance - table[i].resistance) *
(temp - table[i].temp);
tmp /= table[i - 1].temp - table[i].temp;
resist = tmp + table[i].resistance;
} else if (i == 0) {
resist = table[0].resistance;
} else {
resist = table[table_len - 1].resistance;
}
return resist;
return fixp_linear_interpolate(table[low].temp,
table[low].resistance,
table[high].temp,
table[high].resistance,
temp);
}
EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
@ -821,24 +836,25 @@ EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
int table_len, int ocv)
{
int i, cap, tmp;
int i, high, low;
for (i = 0; i < table_len; i++)
/* Break loop at table_len - 1 because that is the highest index */
for (i = 0; i < table_len - 1; i++)
if (ocv > table[i].ocv)
break;
if (i > 0 && i < table_len) {
tmp = (table[i - 1].capacity - table[i].capacity) *
(ocv - table[i].ocv);
tmp /= table[i - 1].ocv - table[i].ocv;
cap = tmp + table[i].capacity;
} else if (i == 0) {
cap = table[0].capacity;
} else {
cap = table[table_len - 1].capacity;
}
/* The library function will deal with high == low */
if ((i == 0) || (i == (table_len - 1)))
high = i - 1;
else
high = i; /* i.e. i == 0 */
low = i;
return cap;
return fixp_linear_interpolate(table[low].ocv,
table[low].capacity,
table[high].ocv,
table[high].capacity,
ocv);
}
EXPORT_SYMBOL_GPL(power_supply_ocv2cap_simple);

View File

@ -106,6 +106,7 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
[POWER_SUPPLY_HEALTH_WARM] = "Warm",
[POWER_SUPPLY_HEALTH_COOL] = "Cool",
[POWER_SUPPLY_HEALTH_HOT] = "Hot",
[POWER_SUPPLY_HEALTH_NO_BATTERY] = "No battery",
};
static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {

View File

@ -863,8 +863,8 @@ static int smbb_charger_probe(struct platform_device *pdev)
}
chg->revision += 1;
if (chg->revision != 2 && chg->revision != 3) {
dev_err(&pdev->dev, "v1 hardware not supported\n");
if (chg->revision != 1 && chg->revision != 2 && chg->revision != 3) {
dev_err(&pdev->dev, "v%d hardware not supported\n", chg->revision);
return -ENODEV;
}
dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
@ -1012,6 +1012,7 @@ static int smbb_charger_remove(struct platform_device *pdev)
}
static const struct of_device_id smbb_charger_id_table[] = {
{ .compatible = "qcom,pm8226-charger" },
{ .compatible = "qcom,pm8941-charger" },
{ }
};

View File

@ -368,7 +368,7 @@ static int sc2731_charger_usb_change(struct notifier_block *nb,
static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
{
struct power_supply_battery_info bat_info = { };
struct power_supply_battery_info *bat_info;
u32 term_currrent, term_voltage, cur_val, vol_val;
int ret;
@ -390,7 +390,7 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
cur_val = 0x2;
vol_val = 0x1;
} else {
term_currrent = bat_info.charge_term_current_ua / 1000;
term_currrent = bat_info->charge_term_current_ua / 1000;
if (term_currrent <= 90)
cur_val = 0;
@ -399,7 +399,7 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
else
cur_val = ((term_currrent - 90) / 25) + 1;
term_voltage = bat_info.constant_charge_voltage_max_uv / 1000;
term_voltage = bat_info->constant_charge_voltage_max_uv / 1000;
if (term_voltage > 4500)
term_voltage = 4500;
@ -409,7 +409,7 @@ static int sc2731_charger_hw_init(struct sc2731_charger_info *info)
else
vol_val = 0;
power_supply_put_battery_info(info->psy_usb, &bat_info);
power_supply_put_battery_info(info->psy_usb, bat_info);
}
/* Set charge termination current */

View File

@ -998,7 +998,7 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
{
struct power_supply_battery_info info = { };
struct power_supply_battery_info *info;
struct power_supply_battery_ocv_table *table;
int ret, delta_clbcnt, alarm_adc;
@ -1008,16 +1008,16 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
return ret;
}
data->total_cap = info.charge_full_design_uah / 1000;
data->max_volt = info.constant_charge_voltage_max_uv / 1000;
data->internal_resist = info.factory_internal_resistance_uohm / 1000;
data->min_volt = info.voltage_min_design_uv;
data->total_cap = info->charge_full_design_uah / 1000;
data->max_volt = info->constant_charge_voltage_max_uv / 1000;
data->internal_resist = info->factory_internal_resistance_uohm / 1000;
data->min_volt = info->voltage_min_design_uv;
/*
* For SC27XX fuel gauge device, we only use one ocv-capacity
* table in normal temperature 20 Celsius.
*/
table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
table = power_supply_find_ocv2cap_table(info, 20, &data->table_len);
if (!table)
return -EINVAL;
@ -1025,7 +1025,7 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
data->table_len * sizeof(*table),
GFP_KERNEL);
if (!data->cap_table) {
power_supply_put_battery_info(data->battery, &info);
power_supply_put_battery_info(data->battery, info);
return -ENOMEM;
}
@ -1035,19 +1035,19 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
if (!data->alarm_cap)
data->alarm_cap += 1;
data->resist_table_len = info.resist_table_size;
data->resist_table_len = info->resist_table_size;
if (data->resist_table_len > 0) {
data->resist_table = devm_kmemdup(data->dev, info.resist_table,
data->resist_table = devm_kmemdup(data->dev, info->resist_table,
data->resist_table_len *
sizeof(struct power_supply_resistance_temp_table),
GFP_KERNEL);
if (!data->resist_table) {
power_supply_put_battery_info(data->battery, &info);
power_supply_put_battery_info(data->battery, info);
return -ENOMEM;
}
}
power_supply_put_battery_info(data->battery, &info);
power_supply_put_battery_info(data->battery, info);
ret = sc27xx_fgu_calibration(data);
if (ret)

View File

@ -1281,7 +1281,7 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb)
static int smb347_get_battery_info(struct smb347_charger *smb)
{
struct power_supply_battery_info info = {};
struct power_supply_battery_info *info;
struct power_supply *supply;
int err;
@ -1296,29 +1296,29 @@ static int smb347_get_battery_info(struct smb347_charger *smb)
if (err)
return err;
if (info.constant_charge_current_max_ua != -EINVAL)
smb->max_charge_current = info.constant_charge_current_max_ua;
if (info->constant_charge_current_max_ua != -EINVAL)
smb->max_charge_current = info->constant_charge_current_max_ua;
if (info.constant_charge_voltage_max_uv != -EINVAL)
smb->max_charge_voltage = info.constant_charge_voltage_max_uv;
if (info->constant_charge_voltage_max_uv != -EINVAL)
smb->max_charge_voltage = info->constant_charge_voltage_max_uv;
if (info.precharge_current_ua != -EINVAL)
smb->pre_charge_current = info.precharge_current_ua;
if (info->precharge_current_ua != -EINVAL)
smb->pre_charge_current = info->precharge_current_ua;
if (info.charge_term_current_ua != -EINVAL)
smb->termination_current = info.charge_term_current_ua;
if (info->charge_term_current_ua != -EINVAL)
smb->termination_current = info->charge_term_current_ua;
if (info.temp_alert_min != INT_MIN)
smb->soft_cold_temp_limit = info.temp_alert_min;
if (info->temp_alert_min != INT_MIN)
smb->soft_cold_temp_limit = info->temp_alert_min;
if (info.temp_alert_max != INT_MAX)
smb->soft_hot_temp_limit = info.temp_alert_max;
if (info->temp_alert_max != INT_MAX)
smb->soft_hot_temp_limit = info->temp_alert_max;
if (info.temp_min != INT_MIN)
smb->hard_cold_temp_limit = info.temp_min;
if (info->temp_min != INT_MIN)
smb->hard_cold_temp_limit = info->temp_min;
if (info.temp_max != INT_MAX)
smb->hard_hot_temp_limit = info.temp_max;
if (info->temp_max != INT_MAX)
smb->hard_hot_temp_limit = info->temp_max;
/* Suspend when battery temperature is outside hard limits */
if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT ||

View File

@ -66,6 +66,7 @@ enum {
POWER_SUPPLY_HEALTH_WARM,
POWER_SUPPLY_HEALTH_COOL,
POWER_SUPPLY_HEALTH_HOT,
POWER_SUPPLY_HEALTH_NO_BATTERY,
};
enum {
@ -342,37 +343,206 @@ struct power_supply_resistance_temp_table {
#define POWER_SUPPLY_OCV_TEMP_MAX 20
/*
/**
* struct power_supply_battery_info - information about batteries
* @technology: from the POWER_SUPPLY_TECHNOLOGY_* enum
* @energy_full_design_uwh: energy content when fully charged in microwatt
* hours
* @charge_full_design_uah: charge content when fully charged in microampere
* hours
* @voltage_min_design_uv: minimum voltage across the poles when the battery
* is at minimum voltage level in microvolts. If the voltage drops below this
* level the battery will need precharging when using CC/CV charging.
* @voltage_max_design_uv: voltage across the poles when the battery is fully
* charged in microvolts. This is the "nominal voltage" i.e. the voltage
* printed on the label of the battery.
* @tricklecharge_current_ua: the tricklecharge current used when trickle
* charging the battery in microamperes. This is the charging phase when the
* battery is completely empty and we need to carefully trickle in some
* charge until we reach the precharging voltage.
* @precharge_current_ua: current to use in the precharge phase in microamperes,
* the precharge rate is limited by limiting the current to this value.
* @precharge_voltage_max_uv: the maximum voltage allowed when precharging in
* microvolts. When we pass this voltage we will nominally switch over to the
* CC (constant current) charging phase defined by constant_charge_current_ua
* and constant_charge_voltage_max_uv.
* @charge_term_current_ua: when the current in the CV (constant voltage)
* charging phase drops below this value in microamperes the charging will
* terminate completely and not restart until the voltage over the battery
* poles reach charge_restart_voltage_uv unless we use maintenance charging.
* @charge_restart_voltage_uv: when the battery has been fully charged by
* CC/CV charging and charging has been disabled, and the voltage subsequently
* drops below this value in microvolts, the charging will be restarted
* (typically using CV charging).
* @overvoltage_limit_uv: If the voltage exceeds the nominal voltage
* voltage_max_design_uv and we reach this voltage level, all charging must
* stop and emergency procedures take place, such as shutting down the system
* in some cases.
* @constant_charge_current_max_ua: current in microamperes to use in the CC
* (constant current) charging phase. The charging rate is limited
* by this current. This is the main charging phase and as the current is
* constant into the battery the voltage slowly ascends to
* constant_charge_voltage_max_uv.
* @constant_charge_voltage_max_uv: voltage in microvolts signifying the end of
* the CC (constant current) charging phase and the beginning of the CV
* (constant voltage) charging phase.
* @factory_internal_resistance_uohm: the internal resistance of the battery
* at fabrication time, expressed in microohms. This resistance will vary
* depending on the lifetime and charge of the battery, so this is just a
* nominal ballpark figure.
* @ocv_temp: array indicating the open circuit voltage (OCV) capacity
* temperature indices. This is an array of temperatures in degrees Celsius
* indicating which capacity table to use for a certain temperature, since
* the capacity for reasons of chemistry will be different at different
* temperatures. Determining capacity is a multivariate problem and the
* temperature is the first variable we determine.
* @temp_ambient_alert_min: the battery will go outside of operating conditions
* when the ambient temperature goes below this temperature in degrees
* Celsius.
* @temp_ambient_alert_max: the battery will go outside of operating conditions
* when the ambient temperature goes above this temperature in degrees
* Celsius.
* @temp_alert_min: the battery should issue an alert if the internal
* temperature goes below this temperature in degrees Celsius.
* @temp_alert_max: the battery should issue an alert if the internal
* temperature goes above this temperature in degrees Celsius.
* @temp_min: the battery will go outside of operating conditions when
* the internal temperature goes below this temperature in degrees Celsius.
* Normally this means the system should shut down.
* @temp_max: the battery will go outside of operating conditions when
* the internal temperature goes above this temperature in degrees Celsius.
* Normally this means the system should shut down.
* @ocv_table: for each entry in ocv_temp there is a corresponding entry in
* ocv_table and a size for each entry in ocv_table_size. These arrays
* determine the capacity in percent in relation to the voltage in microvolts
* at the indexed temperature.
* @ocv_table_size: for each entry in ocv_temp this array is giving the size of
* each entry in the array of capacity arrays in ocv_table.
* @resist_table: this is a table that correlates a battery temperature to the
* expected internal resistance at this temperature. The resistance is given
* as a percentage of factory_internal_resistance_uohm. Knowing the
* resistance of the battery is usually necessary for calculating the open
* circuit voltage (OCV) that is then used with the ocv_table to calculate
* the capacity of the battery. The resist_table must be ordered descending
* by temperature: highest temperature with lowest resistance first, lowest
* temperature with highest resistance last.
* @resist_table_size: the number of items in the resist_table.
*
* This is the recommended struct to manage static battery parameters,
* populated by power_supply_get_battery_info(). Most platform drivers should
* use these for consistency.
*
* Its field names must correspond to elements in enum power_supply_property.
* The default field value is -EINVAL.
* Power supply class itself doesn't use this.
*
* The charging parameters here assume a CC/CV charging scheme. This method
* is most common with Lithium Ion batteries (other methods are possible) and
* looks as follows:
*
* ^ Battery voltage
* | --- overvoltage_limit_uv
* |
* | ...................................................
* | .. constant_charge_voltage_max_uv
* | ..
* | .
* | .
* | .
* | .
* | .
* | .. precharge_voltage_max_uv
* | ..
* |. (trickle charging)
* +------------------------------------------------------------------> time
*
* ^ Current into the battery
* |
* | ............. constant_charge_current_max_ua
* | . .
* | . .
* | . .
* | . .
* | . ..
* | . ....
* | . .....
* | ... precharge_current_ua ....... charge_term_current_ua
* | . .
* | . .
* |.... tricklecharge_current_ua .
* | .
* +-----------------------------------------------------------------> time
*
* These diagrams are synchronized on time and the voltage and current
* follow each other.
*
* With CC/CV charging commence over time like this for an empty battery:
*
* 1. When the battery is completely empty it may need to be charged with
* an especially small current so that electrons just "trickle in",
* this is the tricklecharge_current_ua.
*
* 2. Next a small initial pre-charge current (precharge_current_ua)
* is applied if the voltage is below precharge_voltage_max_uv until we
* reach precharge_voltage_max_uv. CAUTION: in some texts this is referred
* to as "trickle charging" but the use in the Linux kernel is different
* see below!
*
* 3. Then the main charging current is applied, which is called the constant
* current (CC) phase. A current regulator is set up to allow
* constant_charge_current_max_ua of current to flow into the battery.
* The chemical reaction in the battery will make the voltage go up as
* charge goes into the battery. This current is applied until we reach
* the constant_charge_voltage_max_uv voltage.
*
* 4. At this voltage we switch over to the constant voltage (CV) phase. This
* means we allow current to go into the battery, but we keep the voltage
* fixed. This current will continue to charge the battery while keeping
* the voltage the same. A chemical reaction in the battery goes on
* storing energy without affecting the voltage. Over time the current
* will slowly drop and when we reach charge_term_current_ua we will
* end the constant voltage phase.
*
* After this the battery is fully charged, and if we do not support maintenance
* charging, the charging will not restart until power dissipation makes the
* voltage fall so that we reach charge_restart_voltage_uv and at this point
* we restart charging at the appropriate phase, usually this will be inside
* the CV phase.
*
* If we support maintenance charging the voltage is however kept high after
* the CV phase with a very low current. This is meant to let the same charge
* go in for usage while the charger is still connected, mainly for
* dissipation for the power consuming entity while connected to the
* charger.
*
* All charging MUST terminate if the overvoltage_limit_uv is ever reached.
* Overcharging Lithium Ion cells can be DANGEROUS and lead to fire or
* explosions.
*
* The power supply class itself doesn't use this struct as of now.
*/
struct power_supply_battery_info {
unsigned int technology; /* from the enum above */
int energy_full_design_uwh; /* microWatt-hours */
int charge_full_design_uah; /* microAmp-hours */
int voltage_min_design_uv; /* microVolts */
int voltage_max_design_uv; /* microVolts */
int tricklecharge_current_ua; /* microAmps */
int precharge_current_ua; /* microAmps */
int precharge_voltage_max_uv; /* microVolts */
int charge_term_current_ua; /* microAmps */
int charge_restart_voltage_uv; /* microVolts */
int overvoltage_limit_uv; /* microVolts */
int constant_charge_current_max_ua; /* microAmps */
int constant_charge_voltage_max_uv; /* microVolts */
int factory_internal_resistance_uohm; /* microOhms */
int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];/* celsius */
int temp_ambient_alert_min; /* celsius */
int temp_ambient_alert_max; /* celsius */
int temp_alert_min; /* celsius */
int temp_alert_max; /* celsius */
int temp_min; /* celsius */
int temp_max; /* celsius */
unsigned int technology;
int energy_full_design_uwh;
int charge_full_design_uah;
int voltage_min_design_uv;
int voltage_max_design_uv;
int tricklecharge_current_ua;
int precharge_current_ua;
int precharge_voltage_max_uv;
int charge_term_current_ua;
int charge_restart_voltage_uv;
int overvoltage_limit_uv;
int constant_charge_current_max_ua;
int constant_charge_voltage_max_uv;
int factory_internal_resistance_uohm;
int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];
int temp_ambient_alert_min;
int temp_ambient_alert_max;
int temp_alert_min;
int temp_alert_max;
int temp_min;
int temp_max;
struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX];
int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
struct power_supply_resistance_temp_table *resist_table;
@ -405,7 +575,7 @@ devm_power_supply_get_by_phandle(struct device *dev, const char *property)
#endif /* CONFIG_OF */
extern int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info);
struct power_supply_battery_info **info_out);
extern void power_supply_put_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info);
extern int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,