diff options
34 files changed, 1847 insertions, 1388 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index f7904efc4cfa..a0b2a4280e38 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -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 diff --git a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt deleted file mode 100644 index 3e56c1b34a4c..000000000000 --- a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt +++ /dev/null @@ -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>; -}; diff --git a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.yaml b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.yaml new file mode 100644 index 000000000000..45d66c775115 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.yaml @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77976.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77976.yaml new file mode 100644 index 000000000000..675b9b26d233 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77976.yaml @@ -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>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml index bc8904872d1b..caeff68c66d5 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml @@ -11,7 +11,9 @@ maintainers: properties: compatible: - const: qcom,pm8941-charger + enum: + - qcom,pm8226-charger + - qcom,pm8941-charger reg: maxItems: 1 diff --git a/MAINTAINERS b/MAINTAINERS index 3b53d0ef7ebb..2e8a3405a033 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/power/reset/mt6323-poweroff.c b/drivers/power/reset/mt6323-poweroff.c index 0532803e6cbc..d90e76fcb938 100644 --- a/drivers/power/reset/mt6323-poweroff.c +++ b/drivers/power/reset/mt6323-poweroff.c @@ -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; diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 5cf5bb56d2e3..b366e2fd8e97 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -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 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 4e55a11aab79..2c1b264b2046 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -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 diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h index d11405b7ee1a..56a5aaf9a27a 100644 --- a/drivers/power/supply/ab8500-bm.h +++ b/drivers/power/supply/ab8500-bm.h @@ -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; diff --git a/drivers/power/supply/ab8500-chargalg.h b/drivers/power/supply/ab8500-chargalg.h index 07e6ff50084f..f47a0061c36a 100644 --- a/drivers/power/supply/ab8500-chargalg.h +++ b/drivers/power/supply/ab8500-chargalg.h @@ -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; diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index bfc1245d7912..7ae95f537580 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -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); - -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}, +/* 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 + +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. - */ -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}, -}; - -/* - * 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. + * temperature values to work. Factory resistance is 300 mOhm and the + * resistance values to the right are percentages of 300 mOhm. */ - { - .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, - }, +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 */ }, +}; + +/* 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; - - battery_node = of_parse_phandle(np, "monitored-battery", 0); - if (!battery_node) { - dev_err(dev, "battery node or reference missing\n"); - return -EINVAL; + struct power_supply_battery_info *bi; + struct device *dev = &psy->dev; + int ret; + + ret = power_supply_get_battery_info(psy, &bm->bi); + if (ret) { + dev_err(dev, "cannot retrieve battery info\n"); + return ret; } - - 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; + 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; } - 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->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 (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; + 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); } - /* 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); +} diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index b6c9111d77d7..cc33c5187fbb 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -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); diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index ff4b26b1ceca..c4a2fe07126c 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -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); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 15eadaf46f14..ce074c018dcb 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -145,23 +145,23 @@ enum ab8500_usb_state { AB8500_BM_USB_STATE_MAX, }; -/* VBUS input current limits supported in AB8500 in mA */ -#define USB_CH_IP_CUR_LVL_0P05 50 -#define USB_CH_IP_CUR_LVL_0P09 98 -#define USB_CH_IP_CUR_LVL_0P19 193 -#define USB_CH_IP_CUR_LVL_0P29 290 -#define USB_CH_IP_CUR_LVL_0P38 380 -#define USB_CH_IP_CUR_LVL_0P45 450 -#define USB_CH_IP_CUR_LVL_0P5 500 -#define USB_CH_IP_CUR_LVL_0P6 600 -#define USB_CH_IP_CUR_LVL_0P7 700 -#define USB_CH_IP_CUR_LVL_0P8 800 -#define USB_CH_IP_CUR_LVL_0P9 900 -#define USB_CH_IP_CUR_LVL_1P0 1000 -#define USB_CH_IP_CUR_LVL_1P1 1100 -#define USB_CH_IP_CUR_LVL_1P3 1300 -#define USB_CH_IP_CUR_LVL_1P4 1400 -#define USB_CH_IP_CUR_LVL_1P5 1500 +/* VBUS input current limits supported in AB8500 in uA */ +#define USB_CH_IP_CUR_LVL_0P05 50000 +#define USB_CH_IP_CUR_LVL_0P09 98000 +#define USB_CH_IP_CUR_LVL_0P19 193000 +#define USB_CH_IP_CUR_LVL_0P29 290000 +#define USB_CH_IP_CUR_LVL_0P38 380000 +#define USB_CH_IP_CUR_LVL_0P45 450000 +#define USB_CH_IP_CUR_LVL_0P5 500000 +#define USB_CH_IP_CUR_LVL_0P6 600000 +#define USB_CH_IP_CUR_LVL_0P7 700000 +#define USB_CH_IP_CUR_LVL_0P8 800000 +#define USB_CH_IP_CUR_LVL_0P9 900000 +#define USB_CH_IP_CUR_LVL_1P0 1000000 +#define USB_CH_IP_CUR_LVL_1P1 1100000 +#define USB_CH_IP_CUR_LVL_1P3 1300000 +#define USB_CH_IP_CUR_LVL_1P4 1400000 +#define USB_CH_IP_CUR_LVL_1P5 1500000 #define VBAT_TRESH_IP_CUR_RED 3800 @@ -183,10 +183,10 @@ struct ab8500_charger_interrupts { struct ab8500_charger_info { int charger_connected; int charger_online; - int charger_voltage; + int charger_voltage_uv; int cv_active; bool wd_expired; - int charger_current; + int charger_current_ua; }; struct ab8500_charger_event_flags { @@ -201,17 +201,17 @@ struct ab8500_charger_event_flags { }; struct ab8500_charger_usb_state { - int usb_current; - int usb_current_tmp; + int usb_current_ua; + int usb_current_tmp_ua; enum ab8500_usb_state state; enum ab8500_usb_state state_tmp; spinlock_t usb_lock; }; struct ab8500_charger_max_usb_in_curr { - int usb_type_max; - int set_max; - int calculated_max; + int usb_type_max_ua; + int set_max_ua; + int calculated_max_ua; }; /** @@ -479,7 +479,7 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, * ab8500_charger_get_ac_voltage() - get ac charger voltage * @di: pointer to the ab8500_charger structure * - * Returns ac charger voltage (on success) + * Returns ac charger voltage in microvolt (on success) */ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) { @@ -493,7 +493,8 @@ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) } else { vch = 0; } - return vch; + /* Convert to microvolt, IIO returns millivolt */ + return vch * 1000; } /** @@ -530,7 +531,7 @@ static int ab8500_charger_ac_cv(struct ab8500_charger *di) * @di: pointer to the ab8500_charger structure * * This function returns the vbus voltage. - * Returns vbus voltage (on success) + * Returns vbus voltage in microvolt (on success) */ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) { @@ -544,7 +545,8 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) } else { vch = 0; } - return vch; + /* Convert to microvolt, IIO returns millivolt */ + return vch * 1000; } /** @@ -552,7 +554,7 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) * @di: pointer to the ab8500_charger structure * * This function returns the usb charger current. - * Returns usb current (on success) and error code on failure + * Returns usb current in microamperes (on success) and error code on failure */ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) { @@ -566,7 +568,8 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) } else { ich = 0; } - return ich; + /* Return microamperes */ + return ich * 1000; } /** @@ -574,7 +577,7 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) * @di: pointer to the ab8500_charger structure * * This function returns the ac charger current. - * Returns ac current (on success) and error code on failure. + * Returns ac current in microamperes (on success) and error code on failure. */ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) { @@ -588,7 +591,8 @@ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) } else { ich = 0; } - return ich; + /* Return microamperes */ + return ich * 1000; } /** @@ -711,19 +715,19 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, case USB_STAT_STD_HOST_C_S: dev_dbg(di->dev, "USB Type - Standard host is " "detected through USB driver\n"); - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; di->is_aca_rid = 0; break; case USB_STAT_HOST_CHG_HS_CHIRP: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; di->is_aca_rid = 0; break; case USB_STAT_HOST_CHG_HS: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; di->is_aca_rid = 0; break; case USB_STAT_ACA_RID_C_HS: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P9; di->is_aca_rid = 0; break; case USB_STAT_ACA_RID_A: @@ -732,7 +736,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, * can consume (900mA). Closest level is 500mA */ dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n"); - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; di->is_aca_rid = 1; break; case USB_STAT_ACA_RID_B: @@ -740,36 +744,36 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, * Dedicated charger level minus 120mA (20mA for ACA and * 100mA for potential accessory). Closest level is 1300mA */ - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P3; dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); di->is_aca_rid = 1; break; case USB_STAT_HOST_CHG_NM: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; di->is_aca_rid = 0; break; case USB_STAT_DEDICATED_CHG: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5; di->is_aca_rid = 0; break; case USB_STAT_ACA_RID_C_HS_CHIRP: case USB_STAT_ACA_RID_C_NM: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5; di->is_aca_rid = 1; break; case USB_STAT_NOT_CONFIGURED: if (di->vbus_detected) { di->usb_device_is_unrecognised = true; dev_dbg(di->dev, "USB Type - Legacy charger.\n"); - di->max_usb_in_curr.usb_type_max = + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5; break; } fallthrough; case USB_STAT_HM_IDGND: dev_err(di->dev, "USB Type - Charging not allowed\n"); - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05; ret = -ENXIO; break; case USB_STAT_RESERVED: @@ -781,11 +785,11 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, break; } else { dev_dbg(di->dev, "USB Type - Charging not allowed\n"); - di->max_usb_in_curr.usb_type_max = + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05; dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); ret = -ENXIO; break; } @@ -793,25 +797,25 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, case USB_STAT_CARKIT_2: case USB_STAT_ACA_DOCK_CHARGER: case USB_STAT_CHARGER_LINE_1: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); break; case USB_STAT_NOT_VALID_LINK: dev_err(di->dev, "USB Type invalid - try charging anyway\n"); - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; break; default: dev_err(di->dev, "USB Type - Unknown\n"); - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05; ret = -ENXIO; break; } - di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; + di->max_usb_in_curr.set_max_ua = di->max_usb_in_curr.usb_type_max_ua; dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", - link_status, di->max_usb_in_curr.set_max); + link_status, di->max_usb_in_curr.set_max_ua); return ret; } @@ -921,145 +925,157 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di) /* * This array maps the raw hex value to charger voltage used by the AB8500 - * Values taken from the UM0836 + * Values taken from the UM0836, in microvolt. */ static int ab8500_charger_voltage_map[] = { - 3500 , - 3525 , - 3550 , - 3575 , - 3600 , - 3625 , - 3650 , - 3675 , - 3700 , - 3725 , - 3750 , - 3775 , - 3800 , - 3825 , - 3850 , - 3875 , - 3900 , - 3925 , - 3950 , - 3975 , - 4000 , - 4025 , - 4050 , - 4060 , - 4070 , - 4080 , - 4090 , - 4100 , - 4110 , - 4120 , - 4130 , - 4140 , - 4150 , - 4160 , - 4170 , - 4180 , - 4190 , - 4200 , - 4210 , - 4220 , - 4230 , - 4240 , - 4250 , - 4260 , - 4270 , - 4280 , - 4290 , - 4300 , - 4310 , - 4320 , - 4330 , - 4340 , - 4350 , - 4360 , - 4370 , - 4380 , - 4390 , - 4400 , - 4410 , - 4420 , - 4430 , - 4440 , - 4450 , - 4460 , - 4470 , - 4480 , - 4490 , - 4500 , - 4510 , - 4520 , - 4530 , - 4540 , - 4550 , - 4560 , - 4570 , - 4580 , - 4590 , - 4600 , + 3500000, + 3525000, + 3550000, + 3575000, + 3600000, + 3625000, + 3650000, + 3675000, + 3700000, + 3725000, + 3750000, + 3775000, + 3800000, + 3825000, + 3850000, + 3875000, + 3900000, + 3925000, + 3950000, + 3975000, + 4000000, + 4025000, + 4050000, + 4060000, + 4070000, + 4080000, + 4090000, + 4100000, + 4110000, + 4120000, + 4130000, + 4140000, + 4150000, + 4160000, + 4170000, + 4180000, + 4190000, + 4200000, + 4210000, + 4220000, + 4230000, + 4240000, + 4250000, + 4260000, + 4270000, + 4280000, + 4290000, + 4300000, + 4310000, + 4320000, + 4330000, + 4340000, + 4350000, + 4360000, + 4370000, + 4380000, + 4390000, + 4400000, + 4410000, + 4420000, + 4430000, + 4440000, + 4450000, + 4460000, + 4470000, + 4480000, + 4490000, + 4500000, + 4510000, + 4520000, + 4530000, + 4540000, + 4550000, + 4560000, + 4570000, + 4580000, + 4590000, + 4600000, }; -static int ab8500_voltage_to_regval(int voltage) +static int ab8500_voltage_to_regval(int voltage_uv) { int i; /* Special case for voltage below 3.5V */ - if (voltage < ab8500_charger_voltage_map[0]) + if (voltage_uv < ab8500_charger_voltage_map[0]) return LOW_VOLT_REG; for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) { - if (voltage < ab8500_charger_voltage_map[i]) + if (voltage_uv < ab8500_charger_voltage_map[i]) return i - 1; } /* If not last element, return error */ i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1; - if (voltage == ab8500_charger_voltage_map[i]) + if (voltage_uv == ab8500_charger_voltage_map[i]) return i; else return -1; } -static int ab8500_current_to_regval(struct ab8500_charger *di, int curr) +/* This array maps the raw register value to charger input current */ +static int ab8500_charge_input_curr_map[] = { + 50000, 98000, 193000, 290000, 380000, 450000, 500000, 600000, + 700000, 800000, 900000, 1000000, 1100000, 1300000, 1400000, 1500000, +}; + +/* This array maps the raw register value to charger output current */ +static int ab8500_charge_output_curr_map[] = { + 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, + 900000, 1000000, 1100000, 1200000, 1300000, 1400000, 1500000, 1500000, +}; + +static int ab8500_current_to_regval(struct ab8500_charger *di, int curr_ua) { int i; - if (curr < di->bm->chg_output_curr[0]) + if (curr_ua < ab8500_charge_output_curr_map[0]) return 0; - for (i = 0; i < di->bm->n_chg_out_curr; i++) { - if (curr < di->bm->chg_output_curr[i]) + for (i = 0; i < ARRAY_SIZE(ab8500_charge_output_curr_map); i++) { + if (curr_ua < ab8500_charge_output_curr_map[i]) return i - 1; } /* If not last element, return error */ - i = di->bm->n_chg_out_curr - 1; - if (curr == di->bm->chg_output_curr[i]) + i = ARRAY_SIZE(ab8500_charge_output_curr_map) - 1; + if (curr_ua == ab8500_charge_output_curr_map[i]) return i; else return -1; } -static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr) +static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr_ua) { int i; - if (curr < di->bm->chg_input_curr[0]) + if (curr_ua < ab8500_charge_input_curr_map[0]) return 0; - for (i = 0; i < di->bm->n_chg_in_curr; i++) { - if (curr < di->bm->chg_input_curr[i]) + for (i = 0; i < ARRAY_SIZE(ab8500_charge_input_curr_map); i++) { + if (curr_ua < ab8500_charge_input_curr_map[i]) return i - 1; } /* If not last element, return error */ - i = di->bm->n_chg_in_curr - 1; - if (curr == di->bm->chg_input_curr[i]) + i = ARRAY_SIZE(ab8500_charge_input_curr_map) - 1; + if (curr_ua == ab8500_charge_input_curr_map[i]) return i; else return -1; @@ -1070,35 +1086,35 @@ static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr) * @di: pointer to the ab8500_charger structre * * The usb stack provides the maximum current that can be drawn from - * the standard usb host. This will be in mA. - * This function converts current in mA to a value that can be written + * the standard usb host. This will be in uA. + * This function converts current in uA to a value that can be written * to the register. Returns -1 if charging is not allowed */ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di) { int ret = 0; - switch (di->usb_state.usb_current) { - case 100: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09; + switch (di->usb_state.usb_current_ua) { + case 100000: + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P09; break; - case 200: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19; + case 200000: + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P19; break; - case 300: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29; + case 300000: + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P29; break; - case 400: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38; + case 400000: + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P38; break; - case 500: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5; + case 500000: + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5; break; default: - di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; + di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05; ret = -EPERM; break; } - di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; + di->max_usb_in_curr.set_max_ua = di->max_usb_in_curr.usb_type_max_ua; return ret; } @@ -1123,7 +1139,7 @@ static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di, /** * ab8500_charger_set_current() - set charger current * @di: pointer to the ab8500_charger structure - * @ich: charger current, in mA + * @ich_ua: charger current, in uA * @reg: select what charger register to set * * Set charger current. @@ -1134,7 +1150,7 @@ static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di, * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_set_current(struct ab8500_charger *di, - int ich, int reg) + int ich_ua, int reg) { int ret = 0; int curr_index, prev_curr_index, shift_value, i; @@ -1155,7 +1171,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, case AB8500_MCH_IPT_CURLVL_REG: shift_value = MAIN_CH_INPUT_CURR_SHIFT; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_current_to_regval(di, ich); + curr_index = ab8500_current_to_regval(di, ich_ua); step_udelay = STEP_UDELAY; if (!di->ac.charger_connected) no_stepping = true; @@ -1163,7 +1179,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, case AB8500_USBCH_IPT_CRNTLVL_REG: shift_value = VBUS_IN_CURR_LIM_SHIFT; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_vbus_in_curr_to_regval(di, ich); + curr_index = ab8500_vbus_in_curr_to_regval(di, ich_ua); step_udelay = STEP_UDELAY * 100; if (!di->usb.charger_connected) @@ -1172,7 +1188,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, case AB8500_CH_OPT_CRNTLVL_REG: shift_value = 0; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_current_to_regval(di, ich); + curr_index = ab8500_current_to_regval(di, ich_ua); step_udelay = STEP_UDELAY; if (curr_index && (curr_index - prev_curr_index) > 1) step_udelay *= 100; @@ -1201,8 +1217,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, goto exit_set_current; } - dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n", - __func__, ich, reg); + dev_dbg(di->dev, "%s set charger current: %d uA for reg: 0x%02x\n", + __func__, ich_ua, reg); if (no_stepping) { ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, @@ -1249,31 +1265,31 @@ exit_set_current: /** * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit * @di: pointer to the ab8500_charger structure - * @ich_in: charger input current limit + * @ich_in_ua: charger input current limit in microampere * * Sets the current that can be drawn from the USB host * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, - int ich_in) + int ich_in_ua) { int min_value; int ret; /* We should always use to lowest current limit */ - min_value = min(di->bm->chg_params->usb_curr_max, ich_in); - if (di->max_usb_in_curr.set_max > 0) - min_value = min(di->max_usb_in_curr.set_max, min_value); + min_value = min(di->bm->chg_params->usb_curr_max_ua, ich_in_ua); + if (di->max_usb_in_curr.set_max_ua > 0) + min_value = min(di->max_usb_in_curr.set_max_ua, min_value); - if (di->usb_state.usb_current >= 0) - min_value = min(di->usb_state.usb_current, min_value); + if (di->usb_state.usb_current_ua >= 0) + min_value = min(di->usb_state.usb_current_ua, min_value); switch (min_value) { - case 100: + case 100000: if (di->vbat < VBAT_TRESH_IP_CUR_RED) min_value = USB_CH_IP_CUR_LVL_0P05; break; - case 500: + case 500000: if (di->vbat < VBAT_TRESH_IP_CUR_RED) min_value = USB_CH_IP_CUR_LVL_0P45; break; @@ -1281,7 +1297,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, break; } - dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value); + dev_info(di->dev, "VBUS input current limit set to %d uA\n", min_value); mutex_lock(&di->usb_ipt_crnt_lock); ret = ab8500_charger_set_current(di, min_value, @@ -1294,30 +1310,30 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di, /** * ab8500_charger_set_main_in_curr() - set main charger input current * @di: pointer to the ab8500_charger structure - * @ich_in: input charger current, in mA + * @ich_in_ua: input charger current, in uA * * Set main charger input current. * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di, - int ich_in) + int ich_in_ua) { - return ab8500_charger_set_current(di, ich_in, + return ab8500_charger_set_current(di, ich_in_ua, AB8500_MCH_IPT_CURLVL_REG); } /** * ab8500_charger_set_output_curr() - set charger output current * @di: pointer to the ab8500_charger structure - * @ich_out: output charger current, in mA + * @ich_out_ua: output charger current, in uA * * Set charger output current. * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_set_output_curr(struct ab8500_charger *di, - int ich_out) + int ich_out_ua) { - return ab8500_charger_set_current(di, ich_out, + return ab8500_charger_set_current(di, ich_out_ua, AB8500_CH_OPT_CRNTLVL_REG); } @@ -1368,14 +1384,14 @@ static int ab8500_charger_led_en(struct ab8500_charger *di, int on) * ab8500_charger_ac_en() - enable or disable ac charging * @di: pointer to the ab8500_charger structure * @enable: enable/disable flag - * @vset: charging voltage - * @iset: charging current + * @vset_uv: charging voltage in microvolt + * @iset_ua: charging current in microampere * * Enable/Disable AC/Mains charging and turns on/off the charging led * respectively. **/ static int ab8500_charger_ac_en(struct ux500_charger *charger, - int enable, int vset, int iset) + int enable, int vset_uv, int iset_ua) { int ret; int volt_index; @@ -1393,7 +1409,7 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, } /* Enable AC charging */ - dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset); + dev_dbg(di->dev, "Enable AC: %duV %duA\n", vset_uv, iset_ua); /* * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts @@ -1415,10 +1431,10 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, } /* Check if the requested voltage or current is valid */ - volt_index = ab8500_voltage_to_regval(vset); - curr_index = ab8500_current_to_regval(di, iset); + volt_index = ab8500_voltage_to_regval(vset_uv); + curr_index = ab8500_current_to_regval(di, iset_ua); input_curr_index = ab8500_current_to_regval(di, - di->bm->chg_params->ac_curr_max); + di->bm->chg_params->ac_curr_max_ua); if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) { dev_err(di->dev, "Charger voltage or current too high, " @@ -1435,14 +1451,14 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, } /* MainChInputCurr: current that can be drawn from the charger*/ ret = ab8500_charger_set_main_in_curr(di, - di->bm->chg_params->ac_curr_max); + di->bm->chg_params->ac_curr_max_ua); if (ret) { dev_err(di->dev, "%s Failed to set MainChInputCurr\n", __func__); return ret; } /* ChOutputCurentLevel: protected output current */ - ret = ab8500_charger_set_output_curr(di, iset); + ret = ab8500_charger_set_output_curr(di, iset_ua); if (ret) { dev_err(di->dev, "%s " "Failed to set ChOutputCurentLevel\n", @@ -1545,14 +1561,14 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, * ab8500_charger_usb_en() - enable usb charging * @di: pointer to the ab8500_charger structure * @enable: enable/disable flag - * @vset: charging voltage - * @ich_out: charger output current + * @vset_uv: charging voltage in microvolt + * @ich_out_ua: charger output current in microampere * * Enable/Disable USB charging and turns on/off the charging led respectively. * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_usb_en(struct ux500_charger *charger, - int enable, int vset, int ich_out) + int enable, int vset_uv, int ich_out_ua) { int ret; int volt_index; @@ -1588,11 +1604,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, } /* Enable USB charging */ - dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out); + dev_dbg(di->dev, "Enable USB: %d uV %d uA\n", vset_uv, ich_out_ua); /* Check if the requested voltage or current is valid */ - volt_index = ab8500_voltage_to_regval(vset); - curr_index = ab8500_current_to_regval(di, ich_out); + volt_index = ab8500_voltage_to_regval(vset_uv); + curr_index = ab8500_current_to_regval(di, ich_out_ua); if (volt_index < 0 || curr_index < 0) { dev_err(di->dev, "Charger voltage or current too high, " @@ -1633,14 +1649,14 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, /* USBChInputCurr: current that can be drawn from the usb */ ret = ab8500_charger_set_vbus_in_curr(di, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); if (ret) { dev_err(di->dev, "setting USBChInputCurr failed\n"); return ret; } /* ChOutputCurentLevel: protected output current */ - ret = ab8500_charger_set_output_curr(di, ich_out); + ret = ab8500_charger_set_output_curr(di, ich_out_ua); if (ret) { dev_err(di->dev, "%s " "Failed to set ChOutputCurentLevel\n", @@ -1726,14 +1742,14 @@ out: /** * ab8500_charger_usb_check_enable() - enable usb charging * @charger: pointer to the ux500_charger structure - * @vset: charging voltage - * @iset: charger output current + * @vset_uv: charging voltage in microvolt + * @iset_ua: charger output current in microampere * * Check if the VBUS charger has been disconnected and reconnected without * AB8500 rising an interrupt. Returns 0 on success. */ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger, - int vset, int iset) + int vset_uv, int iset_ua) { u8 usbch_ctrl1 = 0; int ret = 0; @@ -1762,7 +1778,7 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger, return ret; } - ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset); + ret = ab8500_charger_usb_en(&di->usb_chg, true, vset_uv, iset_ua); if (ret < 0) { dev_err(di->dev, "Failed to enable VBUS charger %d\n", __LINE__); @@ -1775,14 +1791,14 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger, /** * ab8500_charger_ac_check_enable() - enable usb charging * @charger: pointer to the ux500_charger structure - * @vset: charging voltage - * @iset: charger output current + * @vset_uv: charging voltage in microvolt + * @iset_ua: charger output current in micrompere * * Check if the AC charger has been disconnected and reconnected without * AB8500 rising an interrupt. Returns 0 on success. */ static int ab8500_charger_ac_check_enable(struct ux500_charger *charger, - int vset, int iset) + int vset_uv, int iset_ua) { u8 mainch_ctrl1 = 0; int ret = 0; @@ -1812,7 +1828,7 @@ static int ab8500_charger_ac_check_enable(struct ux500_charger *charger, return ret; } - ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset); + ret = ab8500_charger_ac_en(&di->usb_chg, true, vset_uv, iset_ua); if (ret < 0) { dev_err(di->dev, "failed to enable AC charger %d\n", __LINE__); @@ -1851,13 +1867,14 @@ static int ab8500_charger_watchdog_kick(struct ux500_charger *charger) /** * ab8500_charger_update_charger_current() - update charger current - * @di: pointer to the ab8500_charger structure + * @charger: pointer to the ab8500_charger structure + * @ich_out_ua: desired output current in microampere * * Update the charger output current for the specified charger * Returns error code in case of failure else 0(on success) */ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, - int ich_out) + int ich_out_ua) { int ret; struct ab8500_charger *di; @@ -1869,7 +1886,7 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, else return -ENXIO; - ret = ab8500_charger_set_output_curr(di, ich_out); + ret = ab8500_charger_set_output_curr(di, ich_out_ua); if (ret) { dev_err(di->dev, "%s " "Failed to set ChOutputCurentLevel\n", @@ -1961,10 +1978,10 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work) di->vbat > VBAT_TRESH_IP_CUR_RED))) { dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d," - " old: %d\n", di->max_usb_in_curr.usb_type_max, + " old: %d\n", di->max_usb_in_curr.usb_type_max_ua, di->vbat, di->old_vbat); ab8500_charger_set_vbus_in_curr(di, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); power_supply_changed(di->usb_chg.psy); } @@ -2245,7 +2262,7 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work) /* Update maximum input current if USB enumeration is not detected */ if (!di->usb.charger_online) { ret = ab8500_charger_set_vbus_in_curr(di, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); if (ret) return; } @@ -2407,11 +2424,11 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) spin_lock_irqsave(&di->usb_state.usb_lock, flags); di->usb_state.state = di->usb_state.state_tmp; - di->usb_state.usb_current = di->usb_state.usb_current_tmp; + di->usb_state.usb_current_ua = di->usb_state.usb_current_tmp_ua; spin_unlock_irqrestore(&di->usb_state.usb_lock, flags); - dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n", - __func__, di->usb_state.state, di->usb_state.usb_current); + dev_dbg(di->dev, "%s USB state: 0x%02x uA: %d\n", + __func__, di->usb_state.state, di->usb_state.usb_current_ua); switch (di->usb_state.state) { case AB8500_BM_USB_STATE_RESET_HS: @@ -2437,7 +2454,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) if (!ab8500_charger_get_usb_cur(di)) { /* Update maximum input current */ ret = ab8500_charger_set_vbus_in_curr(di, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); if (ret) return; @@ -2657,7 +2674,7 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work) { struct ab8500_charger *di = container_of(work, struct ab8500_charger, vbus_drop_end_work.work); - int ret, curr; + int ret, curr_ua; u8 reg_value; di->flags.vbus_drop_end = false; @@ -2673,30 +2690,30 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work) return; } - curr = di->bm->chg_input_curr[ + curr_ua = ab8500_charge_input_curr_map[ reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT]; - if (di->max_usb_in_curr.calculated_max != curr) { + if (di->max_usb_in_curr.calculated_max_ua != curr_ua) { /* USB source is collapsing */ - di->max_usb_in_curr.calculated_max = curr; + di->max_usb_in_curr.calculated_max_ua = curr_ua; dev_dbg(di->dev, - "VBUS input current limiting to %d mA\n", - di->max_usb_in_curr.calculated_max); + "VBUS input current limiting to %d uA\n", + di->max_usb_in_curr.calculated_max_ua); } else { /* * USB source can not give more than this amount. * Taking more will collapse the source. */ - di->max_usb_in_curr.set_max = - di->max_usb_in_curr.calculated_max; + di->max_usb_in_curr.set_max_ua = + di->max_usb_in_curr.calculated_max_ua; dev_dbg(di->dev, - "VBUS input current limited to %d mA\n", - di->max_usb_in_curr.set_max); + "VBUS input current limited to %d uA\n", + di->max_usb_in_curr.set_max_ua); } if (di->usb.charger_connected) ab8500_charger_set_vbus_in_curr(di, - di->max_usb_in_curr.usb_type_max); + di->max_usb_in_curr.usb_type_max_ua); } /** @@ -2926,9 +2943,9 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = ab8500_charger_get_ac_voltage(di); if (ret >= 0) - di->ac.charger_voltage = ret; + di->ac.charger_voltage_uv = ret; /* On error, use previous value */ - val->intval = di->ac.charger_voltage * 1000; + val->intval = di->ac.charger_voltage_uv; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: /* @@ -2941,8 +2958,8 @@ static int ab8500_charger_ac_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: ret = ab8500_charger_get_ac_current(di); if (ret >= 0) - di->ac.charger_current = ret; - val->intval = di->ac.charger_current * 1000; + di->ac.charger_current_ua = ret; + val->intval = di->ac.charger_current_ua; break; default: return -EINVAL; @@ -2995,8 +3012,8 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = ab8500_charger_get_vbus_voltage(di); if (ret >= 0) - di->usb.charger_voltage = ret; - val->intval = di->usb.charger_voltage * 1000; + di->usb.charger_voltage_uv = ret; + val->intval = di->usb.charger_voltage_uv; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: /* @@ -3009,8 +3026,8 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: ret = ab8500_charger_get_usb_current(di); if (ret >= 0) - di->usb.charger_current = ret; - val->intval = di->usb.charger_current * 1000; + di->usb.charger_current_ua = ret; + val->intval = di->usb.charger_current_ua; break; case POWER_SUPPLY_PROP_CURRENT_AVG: /* @@ -3186,6 +3203,11 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb, struct ab8500_charger *di = container_of(nb, struct ab8500_charger, nb); enum ab8500_usb_state bm_usb_state; + /* + * FIXME: it appears the AB8500 PHY never sends what it should here. + * Fix the PHY driver to properly notify the desired current. + * Also broadcast microampere and not milliampere. + */ unsigned mA = *((unsigned *)power); if (event != USB_EVENT_VBUS) { @@ -3196,7 +3218,7 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb, /* TODO: State is fabricate here. See if charger really needs USB * state or if mA is enough */ - if ((di->usb_state.usb_current == 2) && (mA > 2)) + if ((di->usb_state.usb_current_ua == 2000) && (mA > 2)) bm_usb_state = AB8500_BM_USB_STATE_RESUME; else if (mA == 0) bm_usb_state = AB8500_BM_USB_STATE_RESET_HS; @@ -3212,7 +3234,8 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb, spin_lock(&di->usb_state.usb_lock); di->usb_state.state_tmp = bm_usb_state; - di->usb_state.usb_current_tmp = mA; + /* FIXME: broadcast ua instead, see above */ + di->usb_state.usb_current_tmp_ua = mA * 1000; spin_unlock(&di->usb_state.usb_lock); /* @@ -3413,11 +3436,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->bm = &ab8500_bm_data; - ret = ab8500_bm_of_probe(dev, np, di->bm); - if (ret) { - dev_err(dev, "failed to get battery information\n"); - return ret; - } di->autopower_cfg = of_property_read_bool(np, "autopower_cfg"); /* get parent data */ @@ -3490,9 +3508,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->invalid_charger_detect_state = 0; /* AC and USB supply config */ + ac_psy_cfg.of_node = np; ac_psy_cfg.supplied_to = supply_interface; ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); ac_psy_cfg.drv_data = &di->ac_chg; + usb_psy_cfg.of_node = np; usb_psy_cfg.supplied_to = supply_interface; usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); usb_psy_cfg.drv_data = &di->usb_chg; @@ -3503,10 +3523,10 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current; - di->ac_chg.max_out_volt = ab8500_charger_voltage_map[ + di->ac_chg.max_out_volt_uv = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; - di->ac_chg.max_out_curr = - di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; + di->ac_chg.max_out_curr_ua = + ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1]; di->ac_chg.wdt_refresh = CHG_WD_INTERVAL; /* * The AB8505 only supports USB charging. If we are not the @@ -3524,13 +3544,13 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current; - di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ + di->usb_chg.max_out_volt_uv = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; - di->usb_chg.max_out_curr = - di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; + di->usb_chg.max_out_curr_ua = + ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1]; di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; di->usb_chg.external = false; - di->usb_state.usb_current = -1; + di->usb_state.usb_current_ua = -1; mutex_init(&di->charger_attached_mutex); @@ -3610,6 +3630,15 @@ static int ab8500_charger_probe(struct platform_device *pdev) return PTR_ERR(di->usb_chg.psy); } + /* + * Check what battery we have, since we always have the USB + * psy, use that as a handle. + */ + ret = ab8500_bm_of_probe(di->usb_chg.psy, di->bm); + if (ret) + return dev_err_probe(dev, ret, + "failed to get battery information\n"); + /* Identify the connected charger types during startup */ charger_status = ab8500_charger_detect_chargers(di, true); if (charger_status & AC_PW_CONN) { @@ -3636,11 +3665,13 @@ static int ab8500_charger_probe(struct platform_device *pdev) } if (!match) { dev_err(dev, "no matching components\n"); - return -ENODEV; + ret = -ENODEV; + goto remove_ab8500_bm; } if (IS_ERR(match)) { dev_err(dev, "could not create component match\n"); - return PTR_ERR(match); + ret = PTR_ERR(match); + goto remove_ab8500_bm; } /* Notifier for external charger enabling */ @@ -3681,6 +3712,8 @@ out_charger_notifier: if (!di->ac_chg.enabled) blocking_notifier_chain_unregister( &charger_notifier_list, &charger_nb); +remove_ab8500_bm: + ab8500_bm_of_remove(di->usb_chg.psy, di->bm); return ret; } @@ -3691,6 +3724,7 @@ static int ab8500_charger_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &ab8500_charger_comp_ops); usb_unregister_notifier(di->usb_phy, &di->nb); + ab8500_bm_of_remove(di->usb_chg.psy, di->bm); usb_put_phy(di->usb_phy); if (!di->ac_chg.enabled) blocking_notifier_chain_unregister( diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 05fe9724ba50..b0919a6a6587 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -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; - - 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); + struct power_supply_battery_info *bi = di->bm->bi; - 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); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 18a9db0df4b1..5d197141f476 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -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)) diff --git a/drivers/power/supply/bd99954-charger.c b/drivers/power/supply/bd99954-charger.c index ffd8bfa08179..96e93e1b8094 100644 --- a/drivers/power/supply/bd99954-charger.c +++ b/drivers/power/supply/bd99954-charger.c @@ -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, diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 35ff0c8fe96f..06c34b09349c 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -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; diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 374b112f712a..4f76ad9c2f18 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -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, diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index f501ecd49202..b274942dc46a 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -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; } diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index b7eac5428083..e62da9dc4f35 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -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[] = { diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index 0008c229fd9c..9daa6d14db4d 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -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, diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 7e5e24b585d8..72e727cd31e8 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -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); } /* diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index 091868e9e9e8..0c87ad0dbf71 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -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; } diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index 8b18219ebe90..2e7fdfde47ec 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -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); diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c new file mode 100644 index 000000000000..8b6c8cfa7503 --- /dev/null +++ b/drivers/power/supply/max77976_charger.c @@ -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], ®val); + 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], ®val); + 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], ®val); + 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], ®val); + 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], ®val); + 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"); diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 6093754cebd5..ec838c9bcc0a 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -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; - - 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; + /* The library function will deal with high == low */ + if ((i == 0) || (i == (table_len - 1))) + high = i; + else + high = i - 1; + low = i; + + 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; - } - - return cap; + /* 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 fixp_linear_interpolate(table[low].ocv, + table[low].capacity, + table[high].ocv, + table[high].capacity, + ocv); } EXPORT_SYMBOL_GPL(power_supply_ocv2cap_simple); diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index c3d7cbcd4fad..6ac88fbee3cb 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -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[] = { diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index 84cc9fba029d..bd50124eef9f 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -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" }, { } }; diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c index 288b79836c13..9ac17cf7a126 100644 --- a/drivers/power/supply/sc2731_charger.c +++ b/drivers/power/supply/sc2731_charger.c @@ -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 */ diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index ae45069bd5e1..632977f84b95 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -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) diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 753944e774c4..d56e469043bb 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -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 || diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 9ca1f120a211..86b4d5c4dab9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -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, |