mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
power supply and reset changes for the v5.14 series
battery/charger driver changes: * convert charger-manager binding to YAML * drop bd70528-charger driver * drop pm2301-charger driver * introduce rt5033-battery driver * misc. improvements and fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmDdZH0ACgkQ2O7X88g7 +pqLpxAAixSqw2UrKCipLUaWWH1vanwPeU78z8XEzpHdNayrTR9PujBMhWH+ps+R IgHNjsQuJsnvbedWfmzkgjrZe5amAuN6Va8OkVDSVzEY+RZvLmXfj9POC5d4LmNK wpIBM/Okjie097j3ZWz7CJp47rsQnkS9EvRY4FevNjz1zt1VSpQNyDHAjemsn+j9 1F9BnBMr2gzgTMxLDIloa71VMEaA8cZlAWulIKwxaN5FaSwoacK0NfestjM1R/Bc Z50pfqzAXBrofm14WKQRIgSEkf9zM6S1AjHG/y4b/C69XbzmBJxn+FK1F+yukWfN Kiq1kGvE9zWqOrDYPd7LxYDetTJ9JgNDxMreLIP7N+syurFYizl7v0JU9TL6BKsh EXCklOx/xaCs0Y3a3kBH8dJ72MBppW1loE0XbAqhkMDkzQybTxg9xUI1gnhVTIlb 5b6KbU5kUo4AghJ279zSNxYNHABKxyu/WCZKNafsLT9pC41muGIPT8ZNgAs2Z3fG V60RSiAFpRrHU7efuizbXYchLEv+vWJG7kfdWmHWawMIqE11cUHgMIjFvkv/41t/ zD3ZnlvvQe/ZZFO16aasvpj99BsZkI5tmNjmVnfPc2cOsLw/HFHIObSe+7x2PpVe 7peflSz6DHL48NAsoq71WxeoPVPm3YYMMA9kr4tt9ZnVBbLJaCM= =EFOr -----END PGP SIGNATURE----- Merge tag 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: "Battery/charger driver changes: - convert charger-manager binding to YAML - drop bd70528-charger driver - drop pm2301-charger driver - introduce rt5033-battery driver - misc improvements and fixes" * tag 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (42 commits) power: supply: ab8500: Fix an old bug power: supply: axp288_fuel_gauge: remove redundant continue statement power: supply: axp288_fuel_gauge: Make "T3 MRD" no_battery_list DMI entry more generic power: supply: axp288_fuel_gauge: Rename fuel_gauge_blacklist to no_battery_list power: supply: bq24190_charger: drop of_match_ptr() from device ID table drivers: power: add missing MODULE_DEVICE_TABLE in keystone-reset.c power: supply: ab8500: add missing MODULE_DEVICE_TABLE power: supply: charger-manager: add missing MODULE_DEVICE_TABLE power: reset: regulator-poweroff: add missing MODULE_DEVICE_TABLE power: supply: cpcap-charger: get the battery inserted infomation from cpcap-battery power: supply: cpcap-battery: invalidate config when incompatible measurements are read power: supply: axp20x_battery: allow disabling battery charging power: supply: max17040: drop unused platform data support power: supply: max17040: simplify POWER_SUPPLY_PROP_ONLINE power: supply: max17040: remove non-working POWER_SUPPLY_PROP_STATUS power: reset: at91-sama5d2_shdwc: Remove redundant error printing in at91_shdwc_probe() power: reset: gpio-poweroff: add missing MODULE_DEVICE_TABLE power: supply: rt5033_battery: Fix device tree enumeration dt-bindings: power: supply: Add DT schema for richtek,rt5033-battery power: supply: Drop BD70528 support ...
This commit is contained in:
commit
c6e8c51f69
@ -1,91 +0,0 @@
|
||||
charger-manager bindings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Required properties :
|
||||
- compatible : "charger-manager"
|
||||
- <>-supply : for regulator consumer, named according to cm-regulator-name
|
||||
- cm-chargers : name of chargers
|
||||
- cm-fuel-gauge : name of battery fuel gauge
|
||||
- subnode <regulator> :
|
||||
- cm-regulator-name : name of charger regulator
|
||||
- subnode <cable> :
|
||||
- cm-cable-name : name of charger cable - one of USB, USB-HOST,
|
||||
SDP, DCP, CDP, ACA, FAST-CHARGER, SLOW-CHARGER, WPT,
|
||||
PD, DOCK, JIG, or MECHANICAL
|
||||
- cm-cable-extcon : name of extcon dev
|
||||
(optional) - cm-cable-min : minimum current of cable
|
||||
(optional) - cm-cable-max : maximum current of cable
|
||||
|
||||
Optional properties :
|
||||
- cm-name : charger manager's name (default : "battery")
|
||||
- cm-poll-mode : polling mode - 0 for disabled, 1 for always, 2 for when
|
||||
external power is connected, or 3 for when charging. If not present,
|
||||
then polling is disabled
|
||||
- cm-poll-interval : polling interval (in ms)
|
||||
- cm-battery-stat : battery status - 0 for battery always present, 1 for no
|
||||
battery, 2 to check presence via fuel gauge, or 3 to check presence
|
||||
via charger
|
||||
- cm-fullbatt-vchkdrop-volt : voltage drop (in uV) before restarting charging
|
||||
- cm-fullbatt-voltage : voltage (in uV) of full battery
|
||||
- cm-fullbatt-soc : state of charge to consider as full battery
|
||||
- cm-fullbatt-capacity : capcity (in uAh) to consider as full battery
|
||||
- cm-thermal-zone : name of external thermometer's thermal zone
|
||||
- cm-battery-* : threshold battery temperature for charging
|
||||
-cold : critical cold temperature of battery for charging
|
||||
-cold-in-minus : flag that cold temperature is in minus degrees
|
||||
-hot : critical hot temperature of battery for charging
|
||||
-temp-diff : temperature difference to allow recharging
|
||||
- cm-dis/charging-max = limits of charging duration
|
||||
|
||||
Deprecated properties:
|
||||
- cm-num-chargers
|
||||
- cm-fullbatt-vchkdrop-ms
|
||||
|
||||
Example :
|
||||
charger-manager@0 {
|
||||
compatible = "charger-manager";
|
||||
chg-reg-supply = <&charger_regulator>;
|
||||
|
||||
cm-name = "battery";
|
||||
/* Always polling ON : 30s */
|
||||
cm-poll-mode = <1>;
|
||||
cm-poll-interval = <30000>;
|
||||
|
||||
cm-fullbatt-vchkdrop-volt = <150000>;
|
||||
cm-fullbatt-soc = <100>;
|
||||
|
||||
cm-battery-stat = <3>;
|
||||
|
||||
cm-chargers = "charger0", "charger1", "charger2";
|
||||
|
||||
cm-fuel-gauge = "fuelgauge0";
|
||||
|
||||
cm-thermal-zone = "thermal_zone.1"
|
||||
/* in deci centigrade */
|
||||
cm-battery-cold = <50>;
|
||||
cm-battery-cold-in-minus;
|
||||
cm-battery-hot = <800>;
|
||||
cm-battery-temp-diff = <100>;
|
||||
|
||||
/* Allow charging for 5hr */
|
||||
cm-charging-max = <18000000>;
|
||||
/* Allow discharging for 2hr */
|
||||
cm-discharging-max = <7200000>;
|
||||
|
||||
regulator@0 {
|
||||
cm-regulator-name = "chg-reg";
|
||||
cable@0 {
|
||||
cm-cable-name = "USB";
|
||||
cm-cable-extcon = "extcon-dev.0";
|
||||
cm-cable-min = <475000>;
|
||||
cm-cable-max = <500000>;
|
||||
};
|
||||
cable@1 {
|
||||
cm-cable-name = "SDP";
|
||||
cm-cable-extcon = "extcon-dev.0";
|
||||
cm-cable-min = <650000>;
|
||||
cm-cable-max = <675000>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
@ -0,0 +1,215 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/charger-manager.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Charger Manager
|
||||
|
||||
maintainers:
|
||||
- Sebastian Reichel <sre@kernel.org>
|
||||
|
||||
description: |
|
||||
Binding for the legacy charger manager driver.
|
||||
Please do not use for new products.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: charger-manager
|
||||
|
||||
cm-chargers:
|
||||
description: name of chargers
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
|
||||
cm-num-chargers:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
deprecated: true
|
||||
|
||||
cm-fuel-gauge:
|
||||
description: name of battery fuel gauge
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
cm-name:
|
||||
description: name of the charger manager
|
||||
default: battery
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
cm-poll-mode:
|
||||
description: polling mode
|
||||
default: 0
|
||||
enum:
|
||||
- 0 # disabled
|
||||
- 1 # always
|
||||
- 2 # when external power is connected
|
||||
- 3 # when charging
|
||||
|
||||
cm-poll-interval:
|
||||
description: polling interval (in ms)
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-battery-stat:
|
||||
description: battery status
|
||||
enum:
|
||||
- 0 # battery always present
|
||||
- 1 # no battery
|
||||
- 2 # check presence via fuel gauge
|
||||
- 3 # check presence via charger
|
||||
|
||||
cm-fullbatt-vchkdrop-volt:
|
||||
description: voltage drop before restarting charging in uV
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-fullbatt-vchkdrop-ms:
|
||||
deprecated: true
|
||||
|
||||
cm-fullbatt-voltage:
|
||||
description: voltage of full battery in uV
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-fullbatt-soc:
|
||||
description: state of charge to consider as full battery in %
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-fullbatt-capacity:
|
||||
description: capcity to consider as full battery in uAh
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-thermal-zone:
|
||||
description: name of external thermometer's thermal zone
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
cm-discharging-max:
|
||||
description: limits of discharging duration in ms
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-charging-max:
|
||||
description: limits of charging duration in ms
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-battery-cold:
|
||||
description: critical cold temperature of battery for charging in deci-degree celsius
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-battery-cold-in-minus:
|
||||
description: if set cm-battery-cold temperature is in minus degrees
|
||||
type: boolean
|
||||
|
||||
cm-battery-hot:
|
||||
description: critical hot temperature of battery for charging in deci-degree celsius
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-battery-temp-diff:
|
||||
description: temperature difference to allow recharging in deci-degree celsius
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
patternProperties:
|
||||
"-supply$":
|
||||
description: regulator consumer, named according to cm-regulator-name
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
"^regulator[@-][0-9]$":
|
||||
type: object
|
||||
properties:
|
||||
cm-regulator-name:
|
||||
description: name of charger regulator
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
required:
|
||||
- cm-regulator-name
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
patternProperties:
|
||||
"^cable[@-][0-9]$":
|
||||
type: object
|
||||
properties:
|
||||
cm-cable-name:
|
||||
description: name of charger cable
|
||||
enum:
|
||||
- USB
|
||||
- USB-HOST
|
||||
- SDP
|
||||
- DCP
|
||||
- CDP
|
||||
- ACA
|
||||
- FAST-CHARGER
|
||||
- SLOW-CHARGER
|
||||
- WPT
|
||||
- PD
|
||||
- DOCK
|
||||
- JIG
|
||||
- MECHANICAL
|
||||
|
||||
cm-cable-extcon:
|
||||
description: name of extcon dev
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
cm-cable-min:
|
||||
description: minimum current of cable in uA
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
cm-cable-max:
|
||||
description: maximum current of cable in uA
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
required:
|
||||
- cm-cable-name
|
||||
- cm-cable-extcon
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- cm-chargers
|
||||
- cm-fuel-gauge
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
charger-manager {
|
||||
compatible = "charger-manager";
|
||||
chg-reg-supply = <&charger_regulator>;
|
||||
|
||||
cm-name = "battery";
|
||||
/* Always polling ON : 30s */
|
||||
cm-poll-mode = <1>;
|
||||
cm-poll-interval = <30000>;
|
||||
|
||||
cm-fullbatt-vchkdrop-volt = <150000>;
|
||||
cm-fullbatt-soc = <100>;
|
||||
|
||||
cm-battery-stat = <3>;
|
||||
|
||||
cm-chargers = "charger0", "charger1", "charger2";
|
||||
|
||||
cm-fuel-gauge = "fuelgauge0";
|
||||
|
||||
cm-thermal-zone = "thermal_zone.1";
|
||||
/* in deci centigrade */
|
||||
cm-battery-cold = <50>;
|
||||
cm-battery-cold-in-minus;
|
||||
cm-battery-hot = <800>;
|
||||
cm-battery-temp-diff = <100>;
|
||||
|
||||
/* Allow charging for 5hr */
|
||||
cm-charging-max = <18000000>;
|
||||
/* Allow discharging for 2hr */
|
||||
cm-discharging-max = <7200000>;
|
||||
|
||||
regulator-0 {
|
||||
cm-regulator-name = "chg-reg";
|
||||
cable-0 {
|
||||
cm-cable-name = "USB";
|
||||
cm-cable-extcon = "extcon-dev.0";
|
||||
cm-cable-min = <475000>;
|
||||
cm-cable-max = <500000>;
|
||||
};
|
||||
cable-1 {
|
||||
cm-cable-name = "SDP";
|
||||
cm-cable-extcon = "extcon-dev.0";
|
||||
cm-cable-min = <650000>;
|
||||
cm-cable-max = <675000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -89,7 +89,7 @@ examples:
|
||||
reg = <0x36>;
|
||||
maxim,alert-low-soc-level = <10>;
|
||||
interrupt-parent = <&gpio7>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
wakeup-source;
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/power/supply/richtek,rt5033-battery.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Richtek RT5033 PMIC Fuel Gauge
|
||||
|
||||
maintainers:
|
||||
- Stephan Gerhold <stephan@gerhold.net>
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: richtek,rt5033-battery
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
battery@35 {
|
||||
compatible = "richtek,rt5033-battery";
|
||||
reg = <0x35>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
battery@35 {
|
||||
compatible = "richtek,rt5033-battery";
|
||||
reg = <0x35>;
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <121 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
@ -14827,6 +14827,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git
|
||||
F: Documentation/ABI/testing/sysfs-class-power
|
||||
F: Documentation/devicetree/bindings/power/supply/
|
||||
F: drivers/power/supply/
|
||||
F: include/linux/power/
|
||||
F: include/linux/power_supply.h
|
||||
|
||||
POWERNV OPERATOR PANEL LCD DISPLAY DRIVER
|
||||
@ -16213,7 +16214,7 @@ W: http://www.ibm.com/developerworks/linux/linux390/
|
||||
F: drivers/s390/scsi/zfcp_*
|
||||
|
||||
S3C ADC BATTERY DRIVER
|
||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
M: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
|
||||
L: linux-samsung-soc@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
F: drivers/power/supply/s3c_adc_battery.c
|
||||
|
@ -351,10 +351,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(at91_shdwc->shdwc_base)) {
|
||||
dev_err(&pdev->dev, "Could not map reset controller address\n");
|
||||
if (IS_ERR(at91_shdwc->shdwc_base))
|
||||
return PTR_ERR(at91_shdwc->shdwc_base);
|
||||
}
|
||||
|
||||
match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node);
|
||||
at91_shdwc->rcfg = match->data;
|
||||
|
@ -90,6 +90,7 @@ static const struct of_device_id of_gpio_poweroff_match[] = {
|
||||
{ .compatible = "gpio-poweroff", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);
|
||||
|
||||
static struct platform_driver gpio_poweroff_driver = {
|
||||
.probe = gpio_poweroff_probe,
|
||||
|
@ -71,6 +71,7 @@ static const struct of_device_id rsctrl_of_match[] = {
|
||||
{.compatible = "ti,keystone-reset", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rsctrl_of_match);
|
||||
|
||||
static int rsctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -64,6 +64,7 @@ static const struct of_device_id of_regulator_poweroff_match[] = {
|
||||
{ .compatible = "regulator-poweroff", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_regulator_poweroff_match);
|
||||
|
||||
static struct platform_driver regulator_poweroff_driver = {
|
||||
.probe = regulator_poweroff_probe,
|
||||
|
@ -712,7 +712,8 @@ config BATTERY_GOLDFISH
|
||||
|
||||
config BATTERY_RT5033
|
||||
tristate "RT5033 fuel gauge support"
|
||||
depends on MFD_RT5033
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This adds support for battery fuel gauge in Richtek RT5033 PMIC.
|
||||
The fuelgauge calculates and determines the battery state of charge
|
||||
@ -760,15 +761,6 @@ config CHARGER_UCS1002
|
||||
Say Y to enable support for Microchip UCS1002 Programmable
|
||||
USB Port Power Controller with Charger Emulation.
|
||||
|
||||
config CHARGER_BD70528
|
||||
tristate "ROHM bd70528 charger driver"
|
||||
depends on MFD_ROHM_BD70528
|
||||
select LINEAR_RANGES
|
||||
help
|
||||
Say Y here to enable support for getting battery status
|
||||
information and altering charger configurations from charger
|
||||
block of the ROHM BD70528 Power Management IC.
|
||||
|
||||
config CHARGER_BD99954
|
||||
tristate "ROHM bd99954 charger driver"
|
||||
depends on I2C
|
||||
|
@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
|
||||
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
|
||||
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
||||
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
|
||||
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
|
||||
@ -96,7 +96,6 @@ obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
|
||||
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
|
||||
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
|
||||
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
|
||||
obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o
|
||||
obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o
|
||||
obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o
|
||||
obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o
|
||||
|
@ -506,9 +506,6 @@ struct abx500_bm_data {
|
||||
int usb_safety_tmr_h;
|
||||
int bkup_bat_v;
|
||||
int bkup_bat_i;
|
||||
bool autopower_cfg;
|
||||
bool ac_enabled;
|
||||
bool usb_enabled;
|
||||
bool no_maintenance;
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
@ -730,4 +727,8 @@ int ab8500_bm_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data *bm);
|
||||
|
||||
extern struct platform_driver ab8500_fg_driver;
|
||||
extern struct platform_driver ab8500_btemp_driver;
|
||||
extern struct platform_driver abx500_chargalg_driver;
|
||||
|
||||
#endif /* _AB8500_CHARGER_H_ */
|
||||
|
@ -15,7 +15,7 @@
|
||||
* - POWER_SUPPLY_TYPE_USB,
|
||||
* because only them store as drv_data pointer to struct ux500_charger.
|
||||
*/
|
||||
#define psy_to_ux500_charger(x) power_supply_get_drvdata(psy)
|
||||
#define psy_to_ux500_charger(x) power_supply_get_drvdata(x)
|
||||
|
||||
/* Forward declaration */
|
||||
struct ux500_charger;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
@ -932,26 +933,6 @@ static int __maybe_unused ab8500_btemp_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_btemp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_btemp *di = platform_get_drvdata(pdev);
|
||||
int i, irq;
|
||||
|
||||
/* Disable interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
|
||||
free_irq(irq, di);
|
||||
}
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->btemp_wq);
|
||||
|
||||
flush_scheduled_work();
|
||||
power_supply_unregister(di->btemp_psy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *supply_interface[] = {
|
||||
"ab8500_chargalg",
|
||||
"ab8500_fg",
|
||||
@ -966,9 +947,42 @@ static const struct power_supply_desc ab8500_btemp_desc = {
|
||||
.external_power_changed = ab8500_btemp_external_power_changed,
|
||||
};
|
||||
|
||||
static int ab8500_btemp_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct ab8500_btemp *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Create a work queue for the btemp */
|
||||
di->btemp_wq =
|
||||
alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);
|
||||
if (di->btemp_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Kick off periodic temperature measurements */
|
||||
ab8500_btemp_periodic(di, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_btemp_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct ab8500_btemp *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->btemp_wq);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
|
||||
static const struct component_ops ab8500_btemp_component_ops = {
|
||||
.bind = ab8500_btemp_bind,
|
||||
.unbind = ab8500_btemp_unbind,
|
||||
};
|
||||
|
||||
static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ab8500_btemp *di;
|
||||
@ -981,12 +995,6 @@ static int ab8500_btemp_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;
|
||||
}
|
||||
|
||||
/* get parent data */
|
||||
di->dev = dev;
|
||||
di->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -1011,14 +1019,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
|
||||
psy_cfg.drv_data = di;
|
||||
|
||||
/* Create a work queue for the btemp */
|
||||
di->btemp_wq =
|
||||
alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);
|
||||
if (di->btemp_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Init work for measuring temperature periodically */
|
||||
INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
|
||||
ab8500_btemp_periodic_work);
|
||||
@ -1031,7 +1031,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
AB8500_BTEMP_HIGH_TH, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s ab8500 read failed\n", __func__);
|
||||
goto free_btemp_wq;
|
||||
return ret;
|
||||
}
|
||||
switch (val) {
|
||||
case BTEMP_HIGH_TH_57_0:
|
||||
@ -1050,30 +1050,28 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Register BTEMP power supply class */
|
||||
di->btemp_psy = power_supply_register(dev, &ab8500_btemp_desc,
|
||||
&psy_cfg);
|
||||
di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(di->btemp_psy)) {
|
||||
dev_err(dev, "failed to register BTEMP psy\n");
|
||||
ret = PTR_ERR(di->btemp_psy);
|
||||
goto free_btemp_wq;
|
||||
return PTR_ERR(di->btemp_psy);
|
||||
}
|
||||
|
||||
/* Register interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto free_irq;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
ab8500_btemp_irq[i].isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_btemp_irq[i].name, di);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n"
|
||||
, ab8500_btemp_irq[i].name, irq, ret);
|
||||
goto free_irq;
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_btemp_irq[i].name, irq, ret);
|
||||
@ -1081,23 +1079,16 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, di);
|
||||
|
||||
/* Kick off periodic temperature measurements */
|
||||
ab8500_btemp_periodic(di, true);
|
||||
list_add_tail(&di->node, &ab8500_btemp_list);
|
||||
|
||||
return ret;
|
||||
return component_add(dev, &ab8500_btemp_component_ops);
|
||||
}
|
||||
|
||||
free_irq:
|
||||
/* We also have to free all successfully registered irqs */
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
|
||||
free_irq(irq, di);
|
||||
}
|
||||
static int ab8500_btemp_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &ab8500_btemp_component_ops);
|
||||
|
||||
power_supply_unregister(di->btemp_psy);
|
||||
free_btemp_wq:
|
||||
destroy_workqueue(di->btemp_wq);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume);
|
||||
@ -1106,8 +1097,9 @@ static const struct of_device_id ab8500_btemp_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-btemp", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ab8500_btemp_match);
|
||||
|
||||
static struct platform_driver ab8500_btemp_driver = {
|
||||
struct platform_driver ab8500_btemp_driver = {
|
||||
.probe = ab8500_btemp_probe,
|
||||
.remove = ab8500_btemp_remove,
|
||||
.driver = {
|
||||
@ -1116,20 +1108,6 @@ static struct platform_driver ab8500_btemp_driver = {
|
||||
.pm = &ab8500_btemp_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ab8500_btemp_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_btemp_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_btemp_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_btemp_driver);
|
||||
}
|
||||
|
||||
device_initcall(ab8500_btemp_init);
|
||||
module_exit(ab8500_btemp_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
|
||||
MODULE_ALIAS("platform:ab8500-btemp");
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/notifier.h>
|
||||
@ -414,6 +415,14 @@ disable_otp:
|
||||
static void ab8500_power_supply_changed(struct ab8500_charger *di,
|
||||
struct power_supply *psy)
|
||||
{
|
||||
/*
|
||||
* This happens if we get notifications or interrupts and
|
||||
* the platform has been configured not to support one or
|
||||
* other type of charging.
|
||||
*/
|
||||
if (!psy)
|
||||
return;
|
||||
|
||||
if (di->autopower_cfg) {
|
||||
if (!di->usb.charger_connected &&
|
||||
!di->ac.charger_connected &&
|
||||
@ -440,7 +449,15 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
|
||||
if (!connected)
|
||||
di->flags.vbus_drop_end = false;
|
||||
|
||||
sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, "present");
|
||||
/*
|
||||
* Sometimes the platform is configured not to support
|
||||
* USB charging and no psy has been created, but we still
|
||||
* will get these notifications.
|
||||
*/
|
||||
if (di->usb_chg.psy) {
|
||||
sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL,
|
||||
"present");
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
@ -3171,9 +3188,6 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
|
||||
enum ab8500_usb_state bm_usb_state;
|
||||
unsigned mA = *((unsigned *)power);
|
||||
|
||||
if (!di)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (event != USB_EVENT_VBUS) {
|
||||
dev_dbg(di->dev, "not a standard host, returning\n");
|
||||
return NOTIFY_DONE;
|
||||
@ -3276,50 +3290,6 @@ static struct notifier_block charger_nb = {
|
||||
.notifier_call = ab8500_external_charger_prepare,
|
||||
};
|
||||
|
||||
static int ab8500_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_charger *di = platform_get_drvdata(pdev);
|
||||
int i, irq, ret;
|
||||
|
||||
/* Disable AC charging */
|
||||
ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
|
||||
|
||||
/* Disable USB charging */
|
||||
ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
|
||||
|
||||
/* Disable interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
|
||||
free_irq(irq, di);
|
||||
}
|
||||
|
||||
/* Backup battery voltage and current disable */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "%s mask and set failed\n", __func__);
|
||||
|
||||
usb_unregister_notifier(di->usb_phy, &di->nb);
|
||||
usb_put_phy(di->usb_phy);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->charger_wq);
|
||||
|
||||
/* Unregister external charger enable notifier */
|
||||
if (!di->ac_chg.enabled)
|
||||
blocking_notifier_chain_unregister(
|
||||
&charger_notifier_list, &charger_nb);
|
||||
|
||||
flush_scheduled_work();
|
||||
if (di->usb_chg.enabled)
|
||||
power_supply_unregister(di->usb_chg.psy);
|
||||
|
||||
if (di->ac_chg.enabled && !di->ac_chg.external)
|
||||
power_supply_unregister(di->ac_chg.psy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *supply_interface[] = {
|
||||
"ab8500_chargalg",
|
||||
"ab8500_fg",
|
||||
@ -3342,13 +3312,100 @@ static const struct power_supply_desc ab8500_usb_chg_desc = {
|
||||
.get_property = ab8500_charger_usb_get_property,
|
||||
};
|
||||
|
||||
static int ab8500_charger_bind(struct device *dev)
|
||||
{
|
||||
struct ab8500_charger *di = dev_get_drvdata(dev);
|
||||
int ch_stat;
|
||||
int ret;
|
||||
|
||||
/* Create a work queue for the charger */
|
||||
di->charger_wq = alloc_ordered_workqueue("ab8500_charger_wq",
|
||||
WQ_MEM_RECLAIM);
|
||||
if (di->charger_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ch_stat = ab8500_charger_detect_chargers(di, false);
|
||||
|
||||
if (ch_stat & AC_PW_CONN) {
|
||||
if (is_ab8500(di->parent))
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->ac_charger_attached_work,
|
||||
HZ);
|
||||
}
|
||||
if (ch_stat & USB_PW_CONN) {
|
||||
if (is_ab8500(di->parent))
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->usb_charger_attached_work,
|
||||
HZ);
|
||||
di->vbus_detected = true;
|
||||
di->vbus_detected_start = true;
|
||||
queue_work(di->charger_wq,
|
||||
&di->detect_usb_type_work);
|
||||
}
|
||||
|
||||
ret = component_bind_all(dev, di);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't bind component devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_charger_unbind(struct device *dev)
|
||||
{
|
||||
struct ab8500_charger *di = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Disable AC charging */
|
||||
ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
|
||||
|
||||
/* Disable USB charging */
|
||||
ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
|
||||
|
||||
/* Backup battery voltage and current disable */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "%s mask and set failed\n", __func__);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->charger_wq);
|
||||
|
||||
flush_scheduled_work();
|
||||
|
||||
/* Unbind fg, btemp, algorithm */
|
||||
component_unbind_all(dev, di);
|
||||
}
|
||||
|
||||
static const struct component_master_ops ab8500_charger_comp_ops = {
|
||||
.bind = ab8500_charger_bind,
|
||||
.unbind = ab8500_charger_unbind,
|
||||
};
|
||||
|
||||
static struct platform_driver *const ab8500_charger_component_drivers[] = {
|
||||
&ab8500_fg_driver,
|
||||
&ab8500_btemp_driver,
|
||||
&abx500_chargalg_driver,
|
||||
};
|
||||
|
||||
static int ab8500_charger_compare_dev(struct device *dev, void *data)
|
||||
{
|
||||
return dev == data;
|
||||
}
|
||||
|
||||
static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct component_match *match = NULL;
|
||||
struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
|
||||
struct ab8500_charger *di;
|
||||
int irq, i, charger_status, ret = 0, ch_stat;
|
||||
struct device *dev = &pdev->dev;
|
||||
int charger_status;
|
||||
int i, irq;
|
||||
int ret;
|
||||
|
||||
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
@ -3393,6 +3450,38 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* VDD ADC supply needs to be enabled from this driver when there
|
||||
* is a charger connected to avoid erroneous BTEMP_HIGH/LOW
|
||||
* interrupts during charging
|
||||
*/
|
||||
di->regu = devm_regulator_get(dev, "vddadc");
|
||||
if (IS_ERR(di->regu)) {
|
||||
ret = PTR_ERR(di->regu);
|
||||
dev_err(dev, "failed to get vddadc regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev,
|
||||
irq, NULL, ab8500_charger_irq[i].isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_charger_irq[i].name, di);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n"
|
||||
, ab8500_charger_irq[i].name, irq, ret);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_charger_irq[i].name, irq, ret);
|
||||
}
|
||||
|
||||
/* initialize lock */
|
||||
spin_lock_init(&di->usb_state.usb_lock);
|
||||
mutex_init(&di->usb_ipt_crnt_lock);
|
||||
@ -3419,14 +3508,16 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
di->ac_chg.max_out_curr =
|
||||
di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
|
||||
di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
|
||||
di->ac_chg.enabled = di->bm->ac_enabled;
|
||||
/*
|
||||
* The AB8505 only supports USB charging. If we are not the
|
||||
* AB8505, register an AC charger.
|
||||
*
|
||||
* TODO: if this should be opt-in, add DT properties for this.
|
||||
*/
|
||||
if (!is_ab8505(di->parent))
|
||||
di->ac_chg.enabled = true;
|
||||
di->ac_chg.external = false;
|
||||
|
||||
/*notifier for external charger enabling*/
|
||||
if (!di->ac_chg.enabled)
|
||||
blocking_notifier_chain_register(
|
||||
&charger_notifier_list, &charger_nb);
|
||||
|
||||
/* USB supply */
|
||||
/* ux500_charger sub-class */
|
||||
di->usb_chg.ops.enable = &ab8500_charger_usb_en;
|
||||
@ -3438,18 +3529,9 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
di->usb_chg.max_out_curr =
|
||||
di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
|
||||
di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
|
||||
di->usb_chg.enabled = di->bm->usb_enabled;
|
||||
di->usb_chg.external = false;
|
||||
di->usb_state.usb_current = -1;
|
||||
|
||||
/* Create a work queue for the charger */
|
||||
di->charger_wq = alloc_ordered_workqueue("ab8500_charger_wq",
|
||||
WQ_MEM_RECLAIM);
|
||||
if (di->charger_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&di->charger_attached_mutex);
|
||||
|
||||
/* Init work for HW failure check */
|
||||
@ -3500,61 +3582,32 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
INIT_WORK(&di->check_usb_thermal_prot_work,
|
||||
ab8500_charger_check_usb_thermal_prot_work);
|
||||
|
||||
/*
|
||||
* VDD ADC supply needs to be enabled from this driver when there
|
||||
* is a charger connected to avoid erroneous BTEMP_HIGH/LOW
|
||||
* interrupts during charging
|
||||
*/
|
||||
di->regu = devm_regulator_get(dev, "vddadc");
|
||||
if (IS_ERR(di->regu)) {
|
||||
ret = PTR_ERR(di->regu);
|
||||
dev_err(dev, "failed to get vddadc regulator\n");
|
||||
goto free_charger_wq;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize OVV, and other registers */
|
||||
ret = ab8500_charger_init_hw_registers(di);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize ABB registers\n");
|
||||
goto free_charger_wq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register AC charger class */
|
||||
if (di->ac_chg.enabled) {
|
||||
di->ac_chg.psy = power_supply_register(dev,
|
||||
di->ac_chg.psy = devm_power_supply_register(dev,
|
||||
&ab8500_ac_chg_desc,
|
||||
&ac_psy_cfg);
|
||||
if (IS_ERR(di->ac_chg.psy)) {
|
||||
dev_err(dev, "failed to register AC charger\n");
|
||||
ret = PTR_ERR(di->ac_chg.psy);
|
||||
goto free_charger_wq;
|
||||
return PTR_ERR(di->ac_chg.psy);
|
||||
}
|
||||
}
|
||||
|
||||
/* Register USB charger class */
|
||||
if (di->usb_chg.enabled) {
|
||||
di->usb_chg.psy = power_supply_register(dev,
|
||||
&ab8500_usb_chg_desc,
|
||||
&usb_psy_cfg);
|
||||
if (IS_ERR(di->usb_chg.psy)) {
|
||||
dev_err(dev, "failed to register USB charger\n");
|
||||
ret = PTR_ERR(di->usb_chg.psy);
|
||||
goto free_ac;
|
||||
}
|
||||
}
|
||||
|
||||
di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR_OR_NULL(di->usb_phy)) {
|
||||
dev_err(dev, "failed to get usb transceiver\n");
|
||||
ret = -EINVAL;
|
||||
goto free_usb;
|
||||
}
|
||||
di->nb.notifier_call = ab8500_charger_usb_notifier_call;
|
||||
ret = usb_register_notifier(di->usb_phy, &di->nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register usb notifier\n");
|
||||
goto put_usb_phy;
|
||||
di->usb_chg.psy = devm_power_supply_register(dev,
|
||||
&ab8500_usb_chg_desc,
|
||||
&usb_psy_cfg);
|
||||
if (IS_ERR(di->usb_chg.psy)) {
|
||||
dev_err(dev, "failed to register USB charger\n");
|
||||
return PTR_ERR(di->usb_chg.psy);
|
||||
}
|
||||
|
||||
/* Identify the connected charger types during startup */
|
||||
@ -3566,84 +3619,93 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
||||
sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
|
||||
}
|
||||
|
||||
if (charger_status & USB_PW_CONN) {
|
||||
di->vbus_detected = true;
|
||||
di->vbus_detected_start = true;
|
||||
queue_work(di->charger_wq,
|
||||
&di->detect_usb_type_work);
|
||||
}
|
||||
|
||||
/* Register interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto free_irq;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_charger_irq[i].name, di);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n"
|
||||
, ab8500_charger_irq[i].name, irq, ret);
|
||||
goto free_irq;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_charger_irq[i].name, irq, ret);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, di);
|
||||
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
/* Create something that will match the subdrivers when we bind */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_charger_component_drivers); i++) {
|
||||
struct device_driver *drv = &ab8500_charger_component_drivers[i]->driver;
|
||||
struct device *p = NULL, *d;
|
||||
|
||||
ch_stat = ab8500_charger_detect_chargers(di, false);
|
||||
|
||||
if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
|
||||
if (is_ab8500(di->parent))
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->ac_charger_attached_work,
|
||||
HZ);
|
||||
while ((d = platform_find_device_by_driver(p, drv))) {
|
||||
put_device(p);
|
||||
component_match_add(dev, &match,
|
||||
ab8500_charger_compare_dev, d);
|
||||
p = d;
|
||||
}
|
||||
put_device(p);
|
||||
}
|
||||
if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
|
||||
if (is_ab8500(di->parent))
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->usb_charger_attached_work,
|
||||
HZ);
|
||||
if (!match) {
|
||||
dev_err(dev, "no matching components\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (IS_ERR(match)) {
|
||||
dev_err(dev, "could not create component match\n");
|
||||
return PTR_ERR(match);
|
||||
}
|
||||
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
/* Notifier for external charger enabling */
|
||||
if (!di->ac_chg.enabled)
|
||||
blocking_notifier_chain_register(
|
||||
&charger_notifier_list, &charger_nb);
|
||||
|
||||
return ret;
|
||||
|
||||
free_irq:
|
||||
di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR_OR_NULL(di->usb_phy)) {
|
||||
dev_err(dev, "failed to get usb transceiver\n");
|
||||
ret = -EINVAL;
|
||||
goto out_charger_notifier;
|
||||
}
|
||||
di->nb.notifier_call = ab8500_charger_usb_notifier_call;
|
||||
ret = usb_register_notifier(di->usb_phy, &di->nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register usb notifier\n");
|
||||
goto put_usb_phy;
|
||||
}
|
||||
|
||||
|
||||
ret = component_master_add_with_match(&pdev->dev,
|
||||
&ab8500_charger_comp_ops,
|
||||
match);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add component master\n");
|
||||
goto free_notifier;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_notifier:
|
||||
usb_unregister_notifier(di->usb_phy, &di->nb);
|
||||
|
||||
/* We also have to free all successfully registered irqs */
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
|
||||
free_irq(irq, di);
|
||||
}
|
||||
put_usb_phy:
|
||||
usb_put_phy(di->usb_phy);
|
||||
free_usb:
|
||||
if (di->usb_chg.enabled)
|
||||
power_supply_unregister(di->usb_chg.psy);
|
||||
free_ac:
|
||||
if (di->ac_chg.enabled)
|
||||
power_supply_unregister(di->ac_chg.psy);
|
||||
free_charger_wq:
|
||||
destroy_workqueue(di->charger_wq);
|
||||
out_charger_notifier:
|
||||
if (!di->ac_chg.enabled)
|
||||
blocking_notifier_chain_unregister(
|
||||
&charger_notifier_list, &charger_nb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_charger *di = platform_get_drvdata(pdev);
|
||||
|
||||
component_master_del(&pdev->dev, &ab8500_charger_comp_ops);
|
||||
|
||||
usb_unregister_notifier(di->usb_phy, &di->nb);
|
||||
usb_put_phy(di->usb_phy);
|
||||
if (!di->ac_chg.enabled)
|
||||
blocking_notifier_chain_unregister(
|
||||
&charger_notifier_list, &charger_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ab8500_charger_pm_ops, ab8500_charger_suspend, ab8500_charger_resume);
|
||||
|
||||
static const struct of_device_id ab8500_charger_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-charger", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ab8500_charger_match);
|
||||
|
||||
static struct platform_driver ab8500_charger_driver = {
|
||||
.probe = ab8500_charger_probe,
|
||||
@ -3657,15 +3719,24 @@ static struct platform_driver ab8500_charger_driver = {
|
||||
|
||||
static int __init ab8500_charger_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_register_drivers(ab8500_charger_component_drivers,
|
||||
ARRAY_SIZE(ab8500_charger_component_drivers));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&ab8500_charger_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_charger_exit(void)
|
||||
{
|
||||
platform_unregister_drivers(ab8500_charger_component_drivers,
|
||||
ARRAY_SIZE(ab8500_charger_component_drivers));
|
||||
platform_driver_unregister(&ab8500_charger_driver);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(ab8500_charger_init);
|
||||
module_init(ab8500_charger_init);
|
||||
module_exit(ab8500_charger_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -59,7 +60,7 @@
|
||||
((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
|
||||
|
||||
/**
|
||||
* struct ab8500_fg_interrupts - ab8500 fg interupts
|
||||
* struct ab8500_fg_interrupts - ab8500 fg interrupts
|
||||
* @name: name of the interrupt
|
||||
* @isr function pointer to the isr
|
||||
*/
|
||||
@ -2980,27 +2981,6 @@ static int __maybe_unused ab8500_fg_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_fg_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ab8500_fg *di = platform_get_drvdata(pdev);
|
||||
|
||||
list_del(&di->node);
|
||||
|
||||
/* Disable coulomb counter */
|
||||
ret = ab8500_fg_coulomb_counter(di, false);
|
||||
if (ret)
|
||||
dev_err(di->dev, "failed to disable coulomb counter\n");
|
||||
|
||||
destroy_workqueue(di->fg_wq);
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
|
||||
flush_scheduled_work();
|
||||
ab8500_fg_sysfs_psy_remove_attrs(di);
|
||||
power_supply_unregister(di->fg_psy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ab8500 fg driver interrupts and their respective isr */
|
||||
static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
|
||||
{"NCONV_ACCU", ab8500_fg_cc_convend_handler},
|
||||
@ -3024,11 +3004,50 @@ static const struct power_supply_desc ab8500_fg_desc = {
|
||||
.external_power_changed = ab8500_fg_external_power_changed,
|
||||
};
|
||||
|
||||
static int ab8500_fg_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct ab8500_fg *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Create a work queue for running the FG algorithm */
|
||||
di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM);
|
||||
if (di->fg_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Start the coulomb counter */
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
/* Run the FG algorithm */
|
||||
queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_fg_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct ab8500_fg *di = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Disable coulomb counter */
|
||||
ret = ab8500_fg_coulomb_counter(di, false);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to disable coulomb counter\n");
|
||||
|
||||
destroy_workqueue(di->fg_wq);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
|
||||
static const struct component_ops ab8500_fg_component_ops = {
|
||||
.bind = ab8500_fg_bind,
|
||||
.unbind = ab8500_fg_unbind,
|
||||
};
|
||||
|
||||
static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct ab8500_fg *di;
|
||||
int i, irq;
|
||||
int ret = 0;
|
||||
@ -3039,12 +3058,6 @@ static int ab8500_fg_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;
|
||||
}
|
||||
|
||||
mutex_init(&di->cc_lock);
|
||||
|
||||
/* get parent data */
|
||||
@ -3074,13 +3087,6 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
|
||||
ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
|
||||
|
||||
/* Create a work queue for running the FG algorithm */
|
||||
di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM);
|
||||
if (di->fg_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Init work for running the fg algorithm instantly */
|
||||
INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
|
||||
|
||||
@ -3113,7 +3119,7 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
ret = ab8500_fg_init_hw_registers(di);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize registers\n");
|
||||
goto free_inst_curr_wq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Consider battery unknown until we're informed otherwise */
|
||||
@ -3121,15 +3127,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
di->flags.batt_id_received = false;
|
||||
|
||||
/* Register FG power supply class */
|
||||
di->fg_psy = power_supply_register(dev, &ab8500_fg_desc, &psy_cfg);
|
||||
di->fg_psy = devm_power_supply_register(dev, &ab8500_fg_desc, &psy_cfg);
|
||||
if (IS_ERR(di->fg_psy)) {
|
||||
dev_err(dev, "failed to register FG psy\n");
|
||||
ret = PTR_ERR(di->fg_psy);
|
||||
goto free_inst_curr_wq;
|
||||
return PTR_ERR(di->fg_psy);
|
||||
}
|
||||
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
|
||||
/*
|
||||
* Initialize completion used to notify completion and start
|
||||
@ -3141,19 +3145,18 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
/* Register primary interrupt handlers */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto free_irq;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr,
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
ab8500_fg_irq[i].isr,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
ab8500_fg_irq[i].name, di);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request %s IRQ %d: %d\n",
|
||||
ab8500_fg_irq[i].name, irq, ret);
|
||||
goto free_irq;
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Requested %s IRQ %d: %d\n",
|
||||
ab8500_fg_irq[i].name, irq, ret);
|
||||
@ -3168,14 +3171,14 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
ret = ab8500_fg_sysfs_init(di);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create sysfs entry\n");
|
||||
goto free_irq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ab8500_fg_sysfs_psy_create_attrs(di);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create FG psy\n");
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
goto free_irq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Calibrate the fg first time */
|
||||
@ -3185,24 +3188,21 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
/* Use room temp as default value until we get an update from driver. */
|
||||
di->bat_temp = 210;
|
||||
|
||||
/* Run the FG algorithm */
|
||||
queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
|
||||
|
||||
list_add_tail(&di->node, &ab8500_fg_list);
|
||||
|
||||
return ret;
|
||||
return component_add(dev, &ab8500_fg_component_ops);
|
||||
}
|
||||
|
||||
free_irq:
|
||||
/* We also have to free all registered irqs */
|
||||
while (--i >= 0) {
|
||||
/* Last assignment of i from primary interrupt handlers */
|
||||
irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
|
||||
free_irq(irq, di);
|
||||
}
|
||||
static int ab8500_fg_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ab8500_fg *di = platform_get_drvdata(pdev);
|
||||
|
||||
component_del(&pdev->dev, &ab8500_fg_component_ops);
|
||||
list_del(&di->node);
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
ab8500_fg_sysfs_psy_remove_attrs(di);
|
||||
|
||||
power_supply_unregister(di->fg_psy);
|
||||
free_inst_curr_wq:
|
||||
destroy_workqueue(di->fg_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3212,8 +3212,9 @@ static const struct of_device_id ab8500_fg_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-fg", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ab8500_fg_match);
|
||||
|
||||
static struct platform_driver ab8500_fg_driver = {
|
||||
struct platform_driver ab8500_fg_driver = {
|
||||
.probe = ab8500_fg_probe,
|
||||
.remove = ab8500_fg_remove,
|
||||
.driver = {
|
||||
@ -3222,20 +3223,6 @@ static struct platform_driver ab8500_fg_driver = {
|
||||
.pm = &ab8500_fg_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ab8500_fg_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_fg_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_fg_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_fg_driver);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(ab8500_fg_init);
|
||||
module_exit(ab8500_fg_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
|
||||
MODULE_ALIAS("platform:ab8500-fg");
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
@ -1943,28 +1944,6 @@ static int __maybe_unused abx500_chargalg_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int abx500_chargalg_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct abx500_chargalg *di = platform_get_drvdata(pdev);
|
||||
|
||||
/* sysfs interface to enable/disbale charging from user space */
|
||||
abx500_chargalg_sysfs_exit(di);
|
||||
|
||||
hrtimer_cancel(&di->safety_timer);
|
||||
hrtimer_cancel(&di->maintenance_timer);
|
||||
|
||||
cancel_delayed_work_sync(&di->chargalg_periodic_work);
|
||||
cancel_delayed_work_sync(&di->chargalg_wd_work);
|
||||
cancel_work_sync(&di->chargalg_work);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->chargalg_wq);
|
||||
|
||||
power_supply_unregister(di->chargalg_psy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *supply_interface[] = {
|
||||
"ab8500_fg",
|
||||
};
|
||||
@ -1978,29 +1957,63 @@ static const struct power_supply_desc abx500_chargalg_desc = {
|
||||
.external_power_changed = abx500_chargalg_external_power_changed,
|
||||
};
|
||||
|
||||
static int abx500_chargalg_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct abx500_chargalg *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Create a work queue for the chargalg */
|
||||
di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq",
|
||||
WQ_MEM_RECLAIM);
|
||||
if (di->chargalg_wq == NULL) {
|
||||
dev_err(di->dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Run the charging algorithm */
|
||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void abx500_chargalg_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct abx500_chargalg *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Stop all timers and work */
|
||||
hrtimer_cancel(&di->safety_timer);
|
||||
hrtimer_cancel(&di->maintenance_timer);
|
||||
|
||||
cancel_delayed_work_sync(&di->chargalg_periodic_work);
|
||||
cancel_delayed_work_sync(&di->chargalg_wd_work);
|
||||
cancel_work_sync(&di->chargalg_work);
|
||||
|
||||
/* Delete the work queue */
|
||||
destroy_workqueue(di->chargalg_wq);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
|
||||
static const struct component_ops abx500_chargalg_component_ops = {
|
||||
.bind = abx500_chargalg_bind,
|
||||
.unbind = abx500_chargalg_unbind,
|
||||
};
|
||||
|
||||
static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct abx500_chargalg *di;
|
||||
int ret = 0;
|
||||
|
||||
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
|
||||
if (!di) {
|
||||
dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
|
||||
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
|
||||
if (!di)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
di->bm = &ab8500_bm_data;
|
||||
|
||||
ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* get device struct and parent */
|
||||
di->dev = &pdev->dev;
|
||||
di->dev = dev;
|
||||
di->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
psy_cfg.supplied_to = supply_interface;
|
||||
@ -2016,14 +2029,6 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
di->maintenance_timer.function =
|
||||
abx500_chargalg_maintenance_timer_expired;
|
||||
|
||||
/* Create a work queue for the chargalg */
|
||||
di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq",
|
||||
WQ_MEM_RECLAIM);
|
||||
if (di->chargalg_wq == NULL) {
|
||||
dev_err(di->dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Init work for chargalg */
|
||||
INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work,
|
||||
abx500_chargalg_periodic_work);
|
||||
@ -2037,12 +2042,12 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
di->chg_info.prev_conn_chg = -1;
|
||||
|
||||
/* Register chargalg power supply class */
|
||||
di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc,
|
||||
di->chargalg_psy = devm_power_supply_register(di->dev,
|
||||
&abx500_chargalg_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(di->chargalg_psy)) {
|
||||
dev_err(di->dev, "failed to register chargalg psy\n");
|
||||
ret = PTR_ERR(di->chargalg_psy);
|
||||
goto free_chargalg_wq;
|
||||
return PTR_ERR(di->chargalg_psy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, di);
|
||||
@ -2051,21 +2056,24 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
ret = abx500_chargalg_sysfs_init(di);
|
||||
if (ret) {
|
||||
dev_err(di->dev, "failed to create sysfs entry\n");
|
||||
goto free_psy;
|
||||
return ret;
|
||||
}
|
||||
di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
|
||||
|
||||
/* Run the charging algorithm */
|
||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
|
||||
|
||||
dev_info(di->dev, "probe success\n");
|
||||
return ret;
|
||||
return component_add(dev, &abx500_chargalg_component_ops);
|
||||
}
|
||||
|
||||
free_psy:
|
||||
power_supply_unregister(di->chargalg_psy);
|
||||
free_chargalg_wq:
|
||||
destroy_workqueue(di->chargalg_wq);
|
||||
return ret;
|
||||
static int abx500_chargalg_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct abx500_chargalg *di = platform_get_drvdata(pdev);
|
||||
|
||||
component_del(&pdev->dev, &abx500_chargalg_component_ops);
|
||||
|
||||
/* sysfs interface to enable/disable charging from user space */
|
||||
abx500_chargalg_sysfs_exit(di);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(abx500_chargalg_pm_ops, abx500_chargalg_suspend, abx500_chargalg_resume);
|
||||
@ -2075,7 +2083,7 @@ static const struct of_device_id ab8500_chargalg_match[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver abx500_chargalg_driver = {
|
||||
struct platform_driver abx500_chargalg_driver = {
|
||||
.probe = abx500_chargalg_probe,
|
||||
.remove = abx500_chargalg_remove,
|
||||
.driver = {
|
||||
@ -2084,9 +2092,6 @@ static struct platform_driver abx500_chargalg_driver = {
|
||||
.pm = &abx500_chargalg_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(abx500_chargalg_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
|
||||
MODULE_ALIAS("platform:abx500-chargalg");
|
||||
|
@ -40,6 +40,7 @@
|
||||
#define AXP209_FG_PERCENT GENMASK(6, 0)
|
||||
#define AXP22X_FG_VALID BIT(7)
|
||||
|
||||
#define AXP20X_CHRG_CTRL1_ENABLE BIT(7)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5)
|
||||
@ -468,7 +469,18 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
return axp20x_set_max_constant_charge_current(axp20x_batt,
|
||||
val->intval);
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_STATUS_CHARGING:
|
||||
return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
|
||||
AXP20X_CHRG_CTRL1_ENABLE, AXP20X_CHRG_CTRL1_ENABLE);
|
||||
|
||||
case POWER_SUPPLY_STATUS_DISCHARGING:
|
||||
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||||
return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
|
||||
AXP20X_CHRG_CTRL1_ENABLE, 0);
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -491,7 +503,8 @@ static enum power_supply_property axp20x_battery_props[] = {
|
||||
static int axp20x_battery_prop_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
|
||||
return psp == POWER_SUPPLY_PROP_STATUS ||
|
||||
psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
|
||||
psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
|
||||
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
|
||||
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
|
||||
|
@ -142,9 +142,7 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
|
||||
|
||||
for (i = 0; i < NR_RETRY_CNT; i++) {
|
||||
ret = regmap_read(info->regmap, reg, &val);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
else
|
||||
if (ret != -EBUSY)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -676,7 +674,7 @@ intr_failed:
|
||||
* detection reports one despite it not being there.
|
||||
* Please keep this listed sorted alphabetically.
|
||||
*/
|
||||
static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
|
||||
static const struct dmi_system_id axp288_no_battery_list[] = {
|
||||
{
|
||||
/* ACEPC T8 Cherry Trail Z8350 mini PC */
|
||||
.matches = {
|
||||
@ -723,15 +721,6 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Meegopad T08 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
|
||||
},
|
||||
},
|
||||
{ /* Mele PCG03 Mini PC */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"),
|
||||
@ -745,6 +734,15 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
}
|
||||
},
|
||||
{
|
||||
/* Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "3"),
|
||||
DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "5.11"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -764,7 +762,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||
};
|
||||
unsigned int val;
|
||||
|
||||
if (dmi_check_system(axp288_fuel_gauge_blacklist))
|
||||
if (dmi_check_system(axp288_no_battery_list))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
|
@ -1,710 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
//
|
||||
// Copyright (C) 2018 ROHM Semiconductors
|
||||
//
|
||||
// power-supply driver for ROHM BD70528 PMIC
|
||||
|
||||
/*
|
||||
* BD70528 charger HW state machine.
|
||||
*
|
||||
* The thermal shutdown state is not drawn. From any other state but
|
||||
* battery error and suspend it is possible to go to TSD/TMP states
|
||||
* if temperature is out of bounds.
|
||||
*
|
||||
* CHG_RST = H
|
||||
* or CHG_EN=L
|
||||
* or (DCIN2_UVLO=L && DCIN1_UVLO=L)
|
||||
* or (DCIN2_OVLO=H & DCIN1_UVKLO=L)
|
||||
*
|
||||
* +--------------+ +--------------+
|
||||
* | | | |
|
||||
* | Any state +-------> | Suspend |
|
||||
* | | | |
|
||||
* +--------------+ +------+-------+
|
||||
* |
|
||||
* CHG_EN = H && BAT_DET = H && |
|
||||
* No errors (temp, bat_ov, UVLO, |
|
||||
* OVLO...) |
|
||||
* |
|
||||
* BAT_OV or +---------v----------+
|
||||
* (DBAT && TTRI) | |
|
||||
* +-----------------+ Trickle Charge | <---------------+
|
||||
* | | | |
|
||||
* | +-------+------------+ |
|
||||
* | | |
|
||||
* | | ^ |
|
||||
* | V_BAT > VTRI_TH | | VBAT < VTRI_TH - 50mV |
|
||||
* | | | |
|
||||
* | v | |
|
||||
* | | |
|
||||
* | BAT_OV or +----------+----+ |
|
||||
* | (DBAT && TFST) | | |
|
||||
* | +----------------+ Fast Charge | |
|
||||
* | | | | |
|
||||
* v v +----+----------+ |
|
||||
* | |
|
||||
*+----------------+ ILIM_DET=L | ^ ILIM_DET |
|
||||
*| | & CV_DET=H | | or CV_DET=L |
|
||||
*| Battery Error | & VBAT > | | or VBAT < VRECHG_TH |
|
||||
*| | VRECHG_TH | | or IBAT > IFST/x |
|
||||
*+----------------+ & IBAT < | | |
|
||||
* IFST/x v | |
|
||||
* ^ | |
|
||||
* | +---------+-+ |
|
||||
* | | | |
|
||||
* +-------------------+ Top OFF | |
|
||||
* BAT_OV = H or | | |
|
||||
* (DBAT && TFST) +-----+-----+ |
|
||||
* | |
|
||||
* Stay top-off for 15s | |
|
||||
* v |
|
||||
* |
|
||||
* +--------+ |
|
||||
* | | |
|
||||
* | Done +-------------------------+
|
||||
* | |
|
||||
* +--------+ VBAT < VRECHG_TH
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/rohm-bd70528.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/linear_range.h>
|
||||
|
||||
#define CHG_STAT_SUSPEND 0x0
|
||||
#define CHG_STAT_TRICKLE 0x1
|
||||
#define CHG_STAT_FAST 0x3
|
||||
#define CHG_STAT_TOPOFF 0xe
|
||||
#define CHG_STAT_DONE 0xf
|
||||
#define CHG_STAT_OTP_TRICKLE 0x10
|
||||
#define CHG_STAT_OTP_FAST 0x11
|
||||
#define CHG_STAT_OTP_DONE 0x12
|
||||
#define CHG_STAT_TSD_TRICKLE 0x20
|
||||
#define CHG_STAT_TSD_FAST 0x21
|
||||
#define CHG_STAT_TSD_TOPOFF 0x22
|
||||
#define CHG_STAT_BAT_ERR 0x7f
|
||||
|
||||
static const char *bd70528_charger_model = "BD70528";
|
||||
static const char *bd70528_charger_manufacturer = "ROHM Semiconductors";
|
||||
|
||||
#define BD_ERR_IRQ_HND(_name_, _wrn_) \
|
||||
static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
|
||||
{ \
|
||||
struct power_supply *psy = (struct power_supply *)arg; \
|
||||
\
|
||||
power_supply_changed(psy); \
|
||||
dev_err(&psy->dev, (_wrn_)); \
|
||||
\
|
||||
return IRQ_HANDLED; \
|
||||
}
|
||||
|
||||
#define BD_INFO_IRQ_HND(_name_, _wrn_) \
|
||||
static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \
|
||||
{ \
|
||||
struct power_supply *psy = (struct power_supply *)arg; \
|
||||
\
|
||||
power_supply_changed(psy); \
|
||||
dev_dbg(&psy->dev, (_wrn_)); \
|
||||
\
|
||||
return IRQ_HANDLED; \
|
||||
}
|
||||
|
||||
#define BD_IRQ_HND(_name_) bd0528_##_name_##_interrupt
|
||||
|
||||
struct bd70528_psy {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct power_supply *psy;
|
||||
};
|
||||
|
||||
BD_ERR_IRQ_HND(BAT_OV_DET, "Battery overvoltage detected\n");
|
||||
BD_ERR_IRQ_HND(DBAT_DET, "Dead battery detected\n");
|
||||
BD_ERR_IRQ_HND(COLD_DET, "Battery cold\n");
|
||||
BD_ERR_IRQ_HND(HOT_DET, "Battery hot\n");
|
||||
BD_ERR_IRQ_HND(CHG_TSD, "Charger thermal shutdown\n");
|
||||
BD_ERR_IRQ_HND(DCIN2_OV_DET, "DCIN2 overvoltage detected\n");
|
||||
|
||||
BD_INFO_IRQ_HND(BAT_OV_RES, "Battery voltage back to normal\n");
|
||||
BD_INFO_IRQ_HND(COLD_RES, "Battery temperature back to normal\n");
|
||||
BD_INFO_IRQ_HND(HOT_RES, "Battery temperature back to normal\n");
|
||||
BD_INFO_IRQ_HND(BAT_RMV, "Battery removed\n");
|
||||
BD_INFO_IRQ_HND(BAT_DET, "Battery detected\n");
|
||||
BD_INFO_IRQ_HND(DCIN2_OV_RES, "DCIN2 voltage back to normal\n");
|
||||
BD_INFO_IRQ_HND(DCIN2_RMV, "DCIN2 removed\n");
|
||||
BD_INFO_IRQ_HND(DCIN2_DET, "DCIN2 detected\n");
|
||||
BD_INFO_IRQ_HND(DCIN1_RMV, "DCIN1 removed\n");
|
||||
BD_INFO_IRQ_HND(DCIN1_DET, "DCIN1 detected\n");
|
||||
|
||||
struct irq_name_pair {
|
||||
const char *n;
|
||||
irqreturn_t (*h)(int irq, void *arg);
|
||||
};
|
||||
|
||||
static int bd70528_get_irqs(struct platform_device *pdev,
|
||||
struct bd70528_psy *bdpsy)
|
||||
{
|
||||
int irq, i, ret;
|
||||
unsigned int mask;
|
||||
static const struct irq_name_pair bd70528_chg_irqs[] = {
|
||||
{ .n = "bd70528-bat-ov-res", .h = BD_IRQ_HND(BAT_OV_RES) },
|
||||
{ .n = "bd70528-bat-ov-det", .h = BD_IRQ_HND(BAT_OV_DET) },
|
||||
{ .n = "bd70528-bat-dead", .h = BD_IRQ_HND(DBAT_DET) },
|
||||
{ .n = "bd70528-bat-warmed", .h = BD_IRQ_HND(COLD_RES) },
|
||||
{ .n = "bd70528-bat-cold", .h = BD_IRQ_HND(COLD_DET) },
|
||||
{ .n = "bd70528-bat-cooled", .h = BD_IRQ_HND(HOT_RES) },
|
||||
{ .n = "bd70528-bat-hot", .h = BD_IRQ_HND(HOT_DET) },
|
||||
{ .n = "bd70528-chg-tshd", .h = BD_IRQ_HND(CHG_TSD) },
|
||||
{ .n = "bd70528-bat-removed", .h = BD_IRQ_HND(BAT_RMV) },
|
||||
{ .n = "bd70528-bat-detected", .h = BD_IRQ_HND(BAT_DET) },
|
||||
{ .n = "bd70528-dcin2-ov-res", .h = BD_IRQ_HND(DCIN2_OV_RES) },
|
||||
{ .n = "bd70528-dcin2-ov-det", .h = BD_IRQ_HND(DCIN2_OV_DET) },
|
||||
{ .n = "bd70528-dcin2-removed", .h = BD_IRQ_HND(DCIN2_RMV) },
|
||||
{ .n = "bd70528-dcin2-detected", .h = BD_IRQ_HND(DCIN2_DET) },
|
||||
{ .n = "bd70528-dcin1-removed", .h = BD_IRQ_HND(DCIN1_RMV) },
|
||||
{ .n = "bd70528-dcin1-detected", .h = BD_IRQ_HND(DCIN1_DET) },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bd70528_chg_irqs); i++) {
|
||||
irq = platform_get_irq_byname(pdev, bd70528_chg_irqs[i].n);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Bad IRQ information for %s (%d)\n",
|
||||
bd70528_chg_irqs[i].n, irq);
|
||||
return irq;
|
||||
}
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
bd70528_chg_irqs[i].h,
|
||||
IRQF_ONESHOT,
|
||||
bd70528_chg_irqs[i].n,
|
||||
bdpsy->psy);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* BD70528 irq controller is not touching the main mask register.
|
||||
* So enable the charger block interrupts at main level. We can just
|
||||
* leave them enabled as irq-controller should disable irqs
|
||||
* from sub-registers when IRQ is disabled or freed.
|
||||
*/
|
||||
mask = BD70528_REG_INT_BAT1_MASK | BD70528_REG_INT_BAT2_MASK;
|
||||
ret = regmap_update_bits(bdpsy->regmap,
|
||||
BD70528_REG_INT_MAIN_MASK, mask, 0);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to enable charger IRQs\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bd70528_get_charger_status(struct bd70528_psy *bdpsy, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int v;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "Charger state read failure %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (v & BD70528_MASK_CHG_STAT) {
|
||||
case CHG_STAT_SUSPEND:
|
||||
/* Maybe we should check the CHG_TTRI_EN? */
|
||||
case CHG_STAT_OTP_TRICKLE:
|
||||
case CHG_STAT_OTP_FAST:
|
||||
case CHG_STAT_OTP_DONE:
|
||||
case CHG_STAT_TSD_TRICKLE:
|
||||
case CHG_STAT_TSD_FAST:
|
||||
case CHG_STAT_TSD_TOPOFF:
|
||||
case CHG_STAT_BAT_ERR:
|
||||
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
case CHG_STAT_DONE:
|
||||
*val = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
case CHG_STAT_TRICKLE:
|
||||
case CHG_STAT_FAST:
|
||||
case CHG_STAT_TOPOFF:
|
||||
*val = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
default:
|
||||
*val = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd70528_get_charge_type(struct bd70528_psy *bdpsy, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int v;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "Charger state read failure %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (v & BD70528_MASK_CHG_STAT) {
|
||||
case CHG_STAT_TRICKLE:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
break;
|
||||
case CHG_STAT_FAST:
|
||||
case CHG_STAT_TOPOFF:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
break;
|
||||
case CHG_STAT_DONE:
|
||||
case CHG_STAT_SUSPEND:
|
||||
/* Maybe we should check the CHG_TTRI_EN? */
|
||||
case CHG_STAT_OTP_TRICKLE:
|
||||
case CHG_STAT_OTP_FAST:
|
||||
case CHG_STAT_OTP_DONE:
|
||||
case CHG_STAT_TSD_TRICKLE:
|
||||
case CHG_STAT_TSD_FAST:
|
||||
case CHG_STAT_TSD_TOPOFF:
|
||||
case CHG_STAT_BAT_ERR:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
default:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd70528_get_battery_health(struct bd70528_psy *bdpsy, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int v;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "Battery state read failure %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
/* No battery? */
|
||||
if (!(v & BD70528_MASK_CHG_BAT_DETECT))
|
||||
*val = POWER_SUPPLY_HEALTH_DEAD;
|
||||
else if (v & BD70528_MASK_CHG_BAT_OVERVOLT)
|
||||
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
else if (v & BD70528_MASK_CHG_BAT_TIMER)
|
||||
*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
|
||||
else
|
||||
*val = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd70528_get_online(struct bd70528_psy *bdpsy, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int v;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_IN_STAT, &v);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "DC1 IN state read failure %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = (v & BD70528_MASK_CHG_DCIN1_UVLO) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int v;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "Battery state read failure %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = (v & BD70528_MASK_CHG_BAT_DETECT) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct linear_range current_limit_ranges[] = {
|
||||
{
|
||||
.min = 5,
|
||||
.step = 1,
|
||||
.min_sel = 0,
|
||||
.max_sel = 0x22,
|
||||
},
|
||||
{
|
||||
.min = 40,
|
||||
.step = 5,
|
||||
.min_sel = 0x23,
|
||||
.max_sel = 0x26,
|
||||
},
|
||||
{
|
||||
.min = 60,
|
||||
.step = 20,
|
||||
.min_sel = 0x27,
|
||||
.max_sel = 0x2d,
|
||||
},
|
||||
{
|
||||
.min = 200,
|
||||
.step = 50,
|
||||
.min_sel = 0x2e,
|
||||
.max_sel = 0x34,
|
||||
},
|
||||
{
|
||||
.min = 500,
|
||||
.step = 0,
|
||||
.min_sel = 0x35,
|
||||
.max_sel = 0x3f,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* BD70528 would support setting and getting own charge current/
|
||||
* voltage for low temperatures. The driver currently only reads
|
||||
* the charge current at room temperature. We do set both though.
|
||||
*/
|
||||
static const struct linear_range warm_charge_curr[] = {
|
||||
{
|
||||
.min = 10,
|
||||
.step = 10,
|
||||
.min_sel = 0,
|
||||
.max_sel = 0x12,
|
||||
},
|
||||
{
|
||||
.min = 200,
|
||||
.step = 25,
|
||||
.min_sel = 0x13,
|
||||
.max_sel = 0x1f,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Cold charge current selectors are identical to warm charge current
|
||||
* selectors. The difference is that only smaller currents are available
|
||||
* at cold charge range.
|
||||
*/
|
||||
#define MAX_COLD_CHG_CURR_SEL 0x15
|
||||
#define MAX_WARM_CHG_CURR_SEL 0x1f
|
||||
#define MIN_CHG_CURR_SEL 0x0
|
||||
|
||||
static int get_charge_current(struct bd70528_psy *bdpsy, int *ma)
|
||||
{
|
||||
unsigned int sel;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CHG_CURR_WARM,
|
||||
&sel);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Charge current reading failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sel &= BD70528_MASK_CHG_CHG_CURR;
|
||||
|
||||
ret = linear_range_get_value_array(&warm_charge_curr[0],
|
||||
ARRAY_SIZE(warm_charge_curr),
|
||||
sel, ma);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Unknown charge current value 0x%x\n",
|
||||
sel);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_current_limit(struct bd70528_psy *bdpsy, int *ma)
|
||||
{
|
||||
unsigned int sel;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_DCIN_ILIM,
|
||||
&sel);
|
||||
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Input current limit reading failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sel &= BD70528_MASK_CHG_DCIN_ILIM;
|
||||
|
||||
ret = linear_range_get_value_array(¤t_limit_ranges[0],
|
||||
ARRAY_SIZE(current_limit_ranges),
|
||||
sel, ma);
|
||||
if (ret) {
|
||||
/* Unspecified values mean 500 mA */
|
||||
*ma = 500;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property bd70528_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static int bd70528_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
return bd70528_get_charger_status(bdpsy, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
return bd70528_get_charge_type(bdpsy, &val->intval);
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
return bd70528_get_battery_health(bdpsy, &val->intval);
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
return bd70528_get_present(bdpsy, &val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = get_current_limit(bdpsy, &val->intval);
|
||||
val->intval *= 1000;
|
||||
return ret;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
ret = get_charge_current(bdpsy, &val->intval);
|
||||
val->intval *= 1000;
|
||||
return ret;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return bd70528_get_online(bdpsy, &val->intval);
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = bd70528_charger_model;
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = bd70528_charger_manufacturer;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bd70528_prop_is_writable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_charge_current(struct bd70528_psy *bdpsy, int ma)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret = 0, tmpret;
|
||||
bool found;
|
||||
|
||||
if (ma > 500) {
|
||||
dev_warn(bdpsy->dev,
|
||||
"Requested charge current %u exceed maximum (500mA)\n",
|
||||
ma);
|
||||
reg = MAX_WARM_CHG_CURR_SEL;
|
||||
goto set;
|
||||
}
|
||||
if (ma < 10) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Requested charge current %u smaller than min (10mA)\n",
|
||||
ma);
|
||||
reg = MIN_CHG_CURR_SEL;
|
||||
ret = -EINVAL;
|
||||
goto set;
|
||||
}
|
||||
|
||||
/*
|
||||
* For BD70528 voltage/current limits we happily accept any value which
|
||||
* belongs the range. We could check if value matching the selector is
|
||||
* desired by computing the range min + (sel - sel_low) * range step - but
|
||||
* I guess it is enough if we use voltage/current which is closest (below)
|
||||
* the requested?
|
||||
*/
|
||||
|
||||
ret = linear_range_get_selector_low_array(warm_charge_curr,
|
||||
ARRAY_SIZE(warm_charge_curr),
|
||||
ma, ®, &found);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Unsupported charge current %u mA\n", ma);
|
||||
reg = MIN_CHG_CURR_SEL;
|
||||
goto set;
|
||||
}
|
||||
if (!found) {
|
||||
/*
|
||||
* There was a gap in supported values and we hit it.
|
||||
* Yet a smaller value was found so we use it.
|
||||
*/
|
||||
dev_warn(bdpsy->dev,
|
||||
"Unsupported charge current %u mA\n", ma);
|
||||
}
|
||||
set:
|
||||
|
||||
tmpret = regmap_update_bits(bdpsy->regmap,
|
||||
BD70528_REG_CHG_CHG_CURR_WARM,
|
||||
BD70528_MASK_CHG_CHG_CURR, reg);
|
||||
if (tmpret)
|
||||
dev_err(bdpsy->dev,
|
||||
"Charge current write failure (%d)\n", tmpret);
|
||||
|
||||
if (reg > MAX_COLD_CHG_CURR_SEL)
|
||||
reg = MAX_COLD_CHG_CURR_SEL;
|
||||
|
||||
if (!tmpret)
|
||||
tmpret = regmap_update_bits(bdpsy->regmap,
|
||||
BD70528_REG_CHG_CHG_CURR_COLD,
|
||||
BD70528_MASK_CHG_CHG_CURR, reg);
|
||||
|
||||
if (!ret)
|
||||
ret = tmpret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_CURR_LIMIT_SEL 0x34
|
||||
#define MIN_CURR_LIMIT_SEL 0x0
|
||||
|
||||
static int set_current_limit(struct bd70528_psy *bdpsy, int ma)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret = 0, tmpret;
|
||||
bool found;
|
||||
|
||||
if (ma > 500) {
|
||||
dev_warn(bdpsy->dev,
|
||||
"Requested current limit %u exceed maximum (500mA)\n",
|
||||
ma);
|
||||
reg = MAX_CURR_LIMIT_SEL;
|
||||
goto set;
|
||||
}
|
||||
if (ma < 5) {
|
||||
dev_err(bdpsy->dev,
|
||||
"Requested current limit %u smaller than min (5mA)\n",
|
||||
ma);
|
||||
reg = MIN_CURR_LIMIT_SEL;
|
||||
ret = -EINVAL;
|
||||
goto set;
|
||||
}
|
||||
|
||||
ret = linear_range_get_selector_low_array(current_limit_ranges,
|
||||
ARRAY_SIZE(current_limit_ranges),
|
||||
ma, ®, &found);
|
||||
if (ret) {
|
||||
dev_err(bdpsy->dev, "Unsupported current limit %umA\n", ma);
|
||||
reg = MIN_CURR_LIMIT_SEL;
|
||||
goto set;
|
||||
}
|
||||
if (!found) {
|
||||
/*
|
||||
* There was a gap in supported values and we hit it.
|
||||
* We found a smaller value from ranges and use it.
|
||||
* Warn user though.
|
||||
*/
|
||||
dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", ma);
|
||||
}
|
||||
|
||||
set:
|
||||
tmpret = regmap_update_bits(bdpsy->regmap,
|
||||
BD70528_REG_CHG_DCIN_ILIM,
|
||||
BD70528_MASK_CHG_DCIN_ILIM, reg);
|
||||
|
||||
if (!ret)
|
||||
ret = tmpret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bd70528_charger_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return set_current_limit(bdpsy, val->intval / 1000);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return set_charge_current(bdpsy, val->intval / 1000);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc bd70528_charger_desc = {
|
||||
.name = "bd70528-charger",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.properties = bd70528_charger_props,
|
||||
.num_properties = ARRAY_SIZE(bd70528_charger_props),
|
||||
.get_property = bd70528_charger_get_property,
|
||||
.set_property = bd70528_charger_set_property,
|
||||
.property_is_writeable = bd70528_prop_is_writable,
|
||||
};
|
||||
|
||||
static int bd70528_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bd70528_psy *bdpsy;
|
||||
struct power_supply_config cfg = {};
|
||||
|
||||
bdpsy = devm_kzalloc(&pdev->dev, sizeof(*bdpsy), GFP_KERNEL);
|
||||
if (!bdpsy)
|
||||
return -ENOMEM;
|
||||
|
||||
bdpsy->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!bdpsy->regmap) {
|
||||
dev_err(&pdev->dev, "No regmap found for chip\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
bdpsy->dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, bdpsy);
|
||||
cfg.drv_data = bdpsy;
|
||||
cfg.of_node = pdev->dev.parent->of_node;
|
||||
|
||||
bdpsy->psy = devm_power_supply_register(&pdev->dev,
|
||||
&bd70528_charger_desc, &cfg);
|
||||
if (IS_ERR(bdpsy->psy)) {
|
||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
||||
return PTR_ERR(bdpsy->psy);
|
||||
}
|
||||
|
||||
return bd70528_get_irqs(pdev, bdpsy);
|
||||
}
|
||||
|
||||
static struct platform_driver bd70528_power = {
|
||||
.driver = {
|
||||
.name = "bd70528-power"
|
||||
},
|
||||
.probe = bd70528_power_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(bd70528_power);
|
||||
|
||||
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
||||
MODULE_DESCRIPTION("BD70528 power-supply driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:bd70528-power");
|
@ -5,11 +5,10 @@
|
||||
* Author: Mark A. Greer <mgreer@animalcreek.com>
|
||||
*/
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/power/bq24190_charger.h>
|
||||
@ -1959,7 +1958,6 @@ static const struct i2c_device_id bq24190_i2c_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bq24190_of_match[] = {
|
||||
{ .compatible = "ti,bq24190", },
|
||||
{ .compatible = "ti,bq24192", },
|
||||
@ -1968,11 +1966,6 @@ static const struct of_device_id bq24190_of_match[] = {
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bq24190_of_match);
|
||||
#else
|
||||
static const struct of_device_id bq24190_of_match[] = {
|
||||
{ },
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct i2c_driver bq24190_driver = {
|
||||
.probe = bq24190_probe,
|
||||
@ -1981,7 +1974,7 @@ static struct i2c_driver bq24190_driver = {
|
||||
.driver = {
|
||||
.name = "bq24190-charger",
|
||||
.pm = &bq24190_pm_ops,
|
||||
.of_match_table = of_match_ptr(bq24190_of_match),
|
||||
.of_match_table = bq24190_of_match,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(bq24190_driver);
|
||||
|
@ -1279,6 +1279,7 @@ static const struct of_device_id charger_manager_match[] = {
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, charger_manager_match);
|
||||
|
||||
static struct charger_desc *of_cm_parse_desc(struct device *dev)
|
||||
{
|
||||
|
@ -667,10 +667,23 @@ static int cpcap_battery_get_property(struct power_supply *psy,
|
||||
if (!empty->voltage)
|
||||
return -ENODATA;
|
||||
val->intval = empty->counter_uah - latest->counter_uah;
|
||||
if (val->intval < 0)
|
||||
if (val->intval < 0) {
|
||||
/* Assume invalid config if CHARGE_NOW is -20% */
|
||||
if (ddata->charge_full && abs(val->intval) > ddata->charge_full/5) {
|
||||
empty->voltage = 0;
|
||||
ddata->charge_full = 0;
|
||||
return -ENODATA;
|
||||
}
|
||||
val->intval = 0;
|
||||
else if (ddata->charge_full && ddata->charge_full < val->intval)
|
||||
} else if (ddata->charge_full && ddata->charge_full < val->intval) {
|
||||
/* Assume invalid config if CHARGE_NOW exceeds CHARGE_FULL by 20% */
|
||||
if (val->intval > (6*ddata->charge_full)/5) {
|
||||
empty->voltage = 0;
|
||||
ddata->charge_full = 0;
|
||||
return -ENODATA;
|
||||
}
|
||||
val->intval = ddata->charge_full;
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
if (!ddata->charge_full)
|
||||
@ -747,7 +760,7 @@ static int cpcap_battery_set_property(struct power_supply *psy,
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
if (val->intval < 0)
|
||||
return -EINVAL;
|
||||
if (val->intval > ddata->config.info.charge_full_design)
|
||||
if (val->intval > (6*ddata->config.info.charge_full_design)/5)
|
||||
return -EINVAL;
|
||||
|
||||
ddata->charge_full = val->intval;
|
||||
|
@ -173,23 +173,6 @@ static enum power_supply_property cpcap_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
};
|
||||
|
||||
/* No battery always shows temperature of -40000 */
|
||||
static bool cpcap_charger_battery_found(struct cpcap_charger_ddata *ddata)
|
||||
{
|
||||
struct iio_channel *channel;
|
||||
int error, temperature;
|
||||
|
||||
channel = ddata->channels[CPCAP_CHARGER_IIO_BATTDET];
|
||||
error = iio_read_channel_processed(channel, &temperature);
|
||||
if (error < 0) {
|
||||
dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return temperature > -20000 && temperature < 60000;
|
||||
}
|
||||
|
||||
static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata)
|
||||
{
|
||||
struct iio_channel *channel;
|
||||
@ -700,11 +683,29 @@ static void cpcap_usb_detect(struct work_struct *work)
|
||||
|
||||
if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) &&
|
||||
s.chrgcurr1) {
|
||||
int max_current = 532000;
|
||||
int max_current;
|
||||
int vchrg, ichrg;
|
||||
union power_supply_propval val;
|
||||
struct power_supply *battery;
|
||||
|
||||
if (cpcap_charger_battery_found(ddata))
|
||||
battery = power_supply_get_by_name("battery");
|
||||
if (IS_ERR_OR_NULL(battery)) {
|
||||
dev_err(ddata->dev, "battery power_supply not available %li\n",
|
||||
PTR_ERR(battery));
|
||||
return;
|
||||
}
|
||||
|
||||
error = power_supply_get_property(battery, POWER_SUPPLY_PROP_PRESENT, &val);
|
||||
power_supply_put(battery);
|
||||
if (error)
|
||||
goto out_err;
|
||||
|
||||
if (val.intval) {
|
||||
max_current = 1596000;
|
||||
} else {
|
||||
dev_info(ddata->dev, "battery not inserted, charging disabled\n");
|
||||
max_current = 0;
|
||||
}
|
||||
|
||||
if (max_current > ddata->limit_current)
|
||||
max_current = ddata->limit_current;
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/max17040_battery.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -142,13 +141,10 @@ struct max17040_chip {
|
||||
struct regmap *regmap;
|
||||
struct delayed_work work;
|
||||
struct power_supply *battery;
|
||||
struct max17040_platform_data *pdata;
|
||||
struct chip_data data;
|
||||
|
||||
/* battery capacity */
|
||||
int soc;
|
||||
/* State Of Charge */
|
||||
int status;
|
||||
/* Low alert threshold from 32% to 1% of the State of Charge */
|
||||
u32 low_soc_alert;
|
||||
/* some devices return twice the capacity */
|
||||
@ -221,26 +217,7 @@ static int max17040_get_version(struct max17040_chip *chip)
|
||||
|
||||
static int max17040_get_online(struct max17040_chip *chip)
|
||||
{
|
||||
return chip->pdata && chip->pdata->battery_online ?
|
||||
chip->pdata->battery_online() : 1;
|
||||
}
|
||||
|
||||
static int max17040_get_status(struct max17040_chip *chip)
|
||||
{
|
||||
if (!chip->pdata || !chip->pdata->charger_online
|
||||
|| !chip->pdata->charger_enable)
|
||||
return POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
|
||||
if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL)
|
||||
return POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
if (chip->pdata->charger_online())
|
||||
if (chip->pdata->charger_enable())
|
||||
return POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
else
|
||||
return POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int max17040_get_of_data(struct max17040_chip *chip)
|
||||
@ -283,7 +260,6 @@ static int max17040_get_of_data(struct max17040_chip *chip)
|
||||
static void max17040_check_changes(struct max17040_chip *chip)
|
||||
{
|
||||
chip->soc = max17040_get_soc(chip);
|
||||
chip->status = max17040_get_status(chip);
|
||||
}
|
||||
|
||||
static void max17040_queue_work(struct max17040_chip *chip)
|
||||
@ -302,17 +278,16 @@ static void max17040_stop_work(void *data)
|
||||
static void max17040_work(struct work_struct *work)
|
||||
{
|
||||
struct max17040_chip *chip;
|
||||
int last_soc, last_status;
|
||||
int last_soc;
|
||||
|
||||
chip = container_of(work, struct max17040_chip, work.work);
|
||||
|
||||
/* store SOC and status to check changes */
|
||||
/* store SOC to check changes */
|
||||
last_soc = chip->soc;
|
||||
last_status = chip->status;
|
||||
max17040_check_changes(chip);
|
||||
|
||||
/* check changes and send uevent */
|
||||
if (last_soc != chip->soc || last_status != chip->status)
|
||||
if (last_soc != chip->soc)
|
||||
power_supply_changed(chip->battery);
|
||||
|
||||
max17040_queue_work(chip);
|
||||
@ -361,12 +336,10 @@ static irqreturn_t max17040_thread_handler(int id, void *dev)
|
||||
static int max17040_enable_alert_irq(struct max17040_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
unsigned int flags;
|
||||
int ret;
|
||||
|
||||
flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
max17040_thread_handler, flags,
|
||||
max17040_thread_handler, IRQF_ONESHOT,
|
||||
chip->battery->desc->name, chip);
|
||||
|
||||
return ret;
|
||||
@ -415,9 +388,6 @@ static int max17040_get_property(struct power_supply *psy,
|
||||
struct max17040_chip *chip = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = max17040_get_status(chip);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = max17040_get_online(chip);
|
||||
break;
|
||||
@ -444,7 +414,6 @@ static const struct regmap_config max17040_regmap = {
|
||||
};
|
||||
|
||||
static enum power_supply_property max17040_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
@ -480,7 +449,6 @@ static int max17040_probe(struct i2c_client *client,
|
||||
|
||||
chip->client = client;
|
||||
chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap);
|
||||
chip->pdata = client->dev.platform_data;
|
||||
chip_id = (enum chip_id) id->driver_data;
|
||||
if (client->dev.of_node) {
|
||||
ret = max17040_get_of_data(chip);
|
||||
|
@ -1104,7 +1104,7 @@ static int max17042_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||
unsigned int flags = IRQF_ONESHOT;
|
||||
|
||||
/*
|
||||
* On ACPI systems the IRQ may be handled by ACPI-event code,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,8 +37,27 @@
|
||||
#define CHG_STATE_NO_BAT2 13
|
||||
#define CHG_STATE_CHG_READY_VUSB 14
|
||||
|
||||
#define GCHGDET_TYPE_MASK 0x30
|
||||
#define GCHGDET_TYPE_SDP 0x00
|
||||
#define GCHGDET_TYPE_CDP 0x10
|
||||
#define GCHGDET_TYPE_DCP 0x20
|
||||
|
||||
#define FG_ENABLE 1
|
||||
|
||||
/*
|
||||
* Formula seems accurate for battery current, but for USB current around 70mA
|
||||
* per step was seen on Kobo Clara HD but all sources show the same formula
|
||||
* also fur USB current. To avoid accidentially unwanted high currents we stick
|
||||
* to that formula
|
||||
*/
|
||||
#define TO_CUR_REG(x) ((x) / 100000 - 1)
|
||||
#define FROM_CUR_REG(x) ((((x) & 0x1f) + 1) * 100000)
|
||||
#define CHG_MIN_CUR 100000
|
||||
#define CHG_MAX_CUR 1800000
|
||||
#define ADP_MAX_CUR 2500000
|
||||
#define USB_MAX_CUR 1400000
|
||||
|
||||
|
||||
struct rn5t618_power_info {
|
||||
struct rn5t618 *rn5t618;
|
||||
struct platform_device *pdev;
|
||||
@ -48,12 +67,24 @@ struct rn5t618_power_info {
|
||||
int irq;
|
||||
};
|
||||
|
||||
static enum power_supply_usb_type rn5t618_usb_types[] = {
|
||||
POWER_SUPPLY_USB_TYPE_SDP,
|
||||
POWER_SUPPLY_USB_TYPE_DCP,
|
||||
POWER_SUPPLY_USB_TYPE_CDP,
|
||||
POWER_SUPPLY_USB_TYPE_UNKNOWN
|
||||
};
|
||||
|
||||
static enum power_supply_property rn5t618_usb_props[] = {
|
||||
/* input current limit is not very accurate */
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
||||
static enum power_supply_property rn5t618_adp_props[] = {
|
||||
/* input current limit is not very accurate */
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
@ -69,6 +100,7 @@ static enum power_supply_property rn5t618_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
};
|
||||
@ -258,6 +290,36 @@ static int rn5t618_battery_ttf(struct rn5t618_power_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_battery_set_current_limit(struct rn5t618_power_info *info,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
if (val->intval < CHG_MIN_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval >= CHG_MAX_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(info->rn5t618->regmap,
|
||||
RN5T618_CHGISET,
|
||||
0x1F, TO_CUR_REG(val->intval));
|
||||
}
|
||||
|
||||
static int rn5t618_battery_get_current_limit(struct rn5t618_power_info *info,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGISET,
|
||||
®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = FROM_CUR_REG(regval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_battery_charge_full(struct rn5t618_power_info *info,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
@ -323,6 +385,9 @@ static int rn5t618_battery_get_property(struct power_supply *psy,
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
ret = rn5t618_battery_get_current_limit(info, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
ret = rn5t618_battery_charge_full(info, val);
|
||||
break;
|
||||
@ -336,12 +401,38 @@ static int rn5t618_battery_get_property(struct power_supply *psy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rn5t618_battery_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
return rn5t618_battery_set_current_limit(info, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rn5t618_battery_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rn5t618_adp_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
|
||||
unsigned int chgstate;
|
||||
unsigned int regval;
|
||||
bool online;
|
||||
int ret;
|
||||
|
||||
@ -364,6 +455,14 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
|
||||
if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = regmap_read(info->rn5t618->regmap,
|
||||
RN5T618_REGISET1, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = FROM_CUR_REG(regval);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -372,12 +471,79 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_adp_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
if (val->intval > ADP_MAX_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval < CHG_MIN_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET1,
|
||||
TO_CUR_REG(val->intval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_adp_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int rc5t619_usb_get_type(struct rn5t618_power_info *info,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->rn5t618->regmap, RN5T618_GCHGDET, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (regval & GCHGDET_TYPE_MASK) {
|
||||
case GCHGDET_TYPE_SDP:
|
||||
val->intval = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
break;
|
||||
case GCHGDET_TYPE_CDP:
|
||||
val->intval = POWER_SUPPLY_USB_TYPE_CDP;
|
||||
break;
|
||||
case GCHGDET_TYPE_DCP:
|
||||
val->intval = POWER_SUPPLY_USB_TYPE_DCP;
|
||||
break;
|
||||
default:
|
||||
val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_usb_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
|
||||
unsigned int chgstate;
|
||||
unsigned int regval;
|
||||
bool online;
|
||||
int ret;
|
||||
|
||||
@ -400,6 +566,28 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
|
||||
if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
if (!online || (info->rn5t618->variant != RC5T619))
|
||||
return -ENODATA;
|
||||
|
||||
return rc5t619_usb_get_type(info, val);
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGCTL1,
|
||||
®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = 0;
|
||||
if (regval & 2) {
|
||||
ret = regmap_read(info->rn5t618->regmap,
|
||||
RN5T618_REGISET2,
|
||||
®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = FROM_CUR_REG(regval);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -408,12 +596,53 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_usb_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
if (val->intval > USB_MAX_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->intval < CHG_MIN_CUR)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET2,
|
||||
0xE0 | TO_CUR_REG(val->intval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rn5t618_usb_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct power_supply_desc rn5t618_battery_desc = {
|
||||
.name = "rn5t618-battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = rn5t618_battery_props,
|
||||
.num_properties = ARRAY_SIZE(rn5t618_battery_props),
|
||||
.get_property = rn5t618_battery_get_property,
|
||||
.set_property = rn5t618_battery_set_property,
|
||||
.property_is_writeable = rn5t618_battery_property_is_writeable,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc rn5t618_adp_desc = {
|
||||
@ -422,14 +651,20 @@ static const struct power_supply_desc rn5t618_adp_desc = {
|
||||
.properties = rn5t618_adp_props,
|
||||
.num_properties = ARRAY_SIZE(rn5t618_adp_props),
|
||||
.get_property = rn5t618_adp_get_property,
|
||||
.set_property = rn5t618_adp_set_property,
|
||||
.property_is_writeable = rn5t618_adp_property_is_writeable,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc rn5t618_usb_desc = {
|
||||
.name = "rn5t618-usb",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.usb_types = rn5t618_usb_types,
|
||||
.num_usb_types = ARRAY_SIZE(rn5t618_usb_types),
|
||||
.properties = rn5t618_usb_props,
|
||||
.num_properties = ARRAY_SIZE(rn5t618_usb_props),
|
||||
.get_property = rn5t618_usb_get_property,
|
||||
.set_property = rn5t618_usb_set_property,
|
||||
.property_is_writeable = rn5t618_usb_property_is_writeable,
|
||||
};
|
||||
|
||||
static irqreturn_t rn5t618_charger_irq(int irq, void *data)
|
||||
|
@ -164,9 +164,16 @@ static const struct i2c_device_id rt5033_battery_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
|
||||
|
||||
static const struct of_device_id rt5033_battery_of_match[] = {
|
||||
{ .compatible = "richtek,rt5033-battery", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rt5033_battery_of_match);
|
||||
|
||||
static struct i2c_driver rt5033_battery_driver = {
|
||||
.driver = {
|
||||
.name = "rt5033-battery",
|
||||
.of_match_table = rt5033_battery_of_match,
|
||||
},
|
||||
.probe = rt5033_battery_probe,
|
||||
.remove = rt5033_battery_remove,
|
||||
|
@ -189,6 +189,14 @@ static const enum power_supply_property sbs_properties[] = {
|
||||
/* Supports special manufacturer commands from TI BQ20Z65 and BQ20Z75 IC. */
|
||||
#define SBS_FLAGS_TI_BQ20ZX5 BIT(0)
|
||||
|
||||
static const enum power_supply_property string_properties[] = {
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
};
|
||||
|
||||
#define NR_STRING_BUFFERS ARRAY_SIZE(string_properties)
|
||||
|
||||
struct sbs_info {
|
||||
struct i2c_client *client;
|
||||
struct power_supply *power_supply;
|
||||
@ -202,11 +210,32 @@ struct sbs_info {
|
||||
struct delayed_work work;
|
||||
struct mutex mode_lock;
|
||||
u32 flags;
|
||||
int technology;
|
||||
char strings[NR_STRING_BUFFERS][I2C_SMBUS_BLOCK_MAX + 1];
|
||||
};
|
||||
|
||||
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
static char chemistry[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
static char *sbs_get_string_buf(struct sbs_info *chip,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < NR_STRING_BUFFERS; i++)
|
||||
if (string_properties[i] == psp)
|
||||
return chip->strings[i];
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void sbs_invalidate_cached_props(struct sbs_info *chip)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
chip->technology = -1;
|
||||
|
||||
for (i = 0; i < NR_STRING_BUFFERS; i++)
|
||||
chip->strings[i][0] = 0;
|
||||
}
|
||||
|
||||
static bool force_load;
|
||||
|
||||
static int sbs_read_word_data(struct i2c_client *client, u8 address);
|
||||
@ -244,6 +273,7 @@ static int sbs_update_presence(struct sbs_info *chip, bool is_present)
|
||||
chip->is_present = false;
|
||||
/* Disable PEC when no device is present */
|
||||
client->flags &= ~I2C_CLIENT_PEC;
|
||||
sbs_invalidate_cached_props(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -640,17 +670,45 @@ static int sbs_get_battery_property(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_get_battery_string_property(struct i2c_client *client,
|
||||
int reg_offset, enum power_supply_property psp, char *val)
|
||||
static int sbs_get_property_index(struct i2c_client *client,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
s32 ret;
|
||||
int count;
|
||||
|
||||
ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val);
|
||||
for (count = 0; count < ARRAY_SIZE(sbs_data); count++)
|
||||
if (psp == sbs_data[count].psp)
|
||||
return count;
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_warn(&client->dev,
|
||||
"%s: Invalid Property - %d\n", __func__, psp);
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char *sbs_get_constant_string(struct sbs_info *chip,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
int ret;
|
||||
char *buf;
|
||||
u8 addr;
|
||||
|
||||
buf = sbs_get_string_buf(chip, psp);
|
||||
if (IS_ERR(buf))
|
||||
return buf;
|
||||
|
||||
if (!buf[0]) {
|
||||
ret = sbs_get_property_index(chip->client, psp);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
addr = sbs_data[ret].addr;
|
||||
|
||||
ret = sbs_read_string_data(chip->client, addr, buf);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void sbs_unit_adjustment(struct i2c_client *client,
|
||||
@ -773,48 +831,36 @@ static int sbs_get_battery_serial_number(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_get_property_index(struct i2c_client *client,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
int count;
|
||||
for (count = 0; count < ARRAY_SIZE(sbs_data); count++)
|
||||
if (psp == sbs_data[count].psp)
|
||||
return count;
|
||||
|
||||
dev_warn(&client->dev,
|
||||
"%s: Invalid Property - %d\n", __func__, psp);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sbs_get_chemistry(struct i2c_client *client,
|
||||
static int sbs_get_chemistry(struct sbs_info *chip,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
enum power_supply_property psp = POWER_SUPPLY_PROP_TECHNOLOGY;
|
||||
int ret;
|
||||
const char *chemistry;
|
||||
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (chip->technology != -1) {
|
||||
val->intval = chip->technology;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sbs_get_battery_string_property(client, ret, psp,
|
||||
chemistry);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
chemistry = sbs_get_constant_string(chip, POWER_SUPPLY_PROP_TECHNOLOGY);
|
||||
|
||||
if (IS_ERR(chemistry))
|
||||
return PTR_ERR(chemistry);
|
||||
|
||||
if (!strncasecmp(chemistry, "LION", 4))
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
else if (!strncasecmp(chemistry, "LiP", 3))
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
else if (!strncasecmp(chemistry, "NiCd", 4))
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
|
||||
else if (!strncasecmp(chemistry, "NiMH", 4))
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
|
||||
chip->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
|
||||
|
||||
if (val->intval == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
|
||||
dev_warn(&client->dev, "Unknown chemistry: %s\n", chemistry);
|
||||
if (chip->technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
|
||||
dev_warn(&chip->client->dev, "Unknown chemistry: %s\n", chemistry);
|
||||
|
||||
val->intval = chip->technology;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -858,6 +904,7 @@ static int sbs_get_property(struct power_supply *psy,
|
||||
int ret = 0;
|
||||
struct sbs_info *chip = power_supply_get_drvdata(psy);
|
||||
struct i2c_client *client = chip->client;
|
||||
const char *str;
|
||||
|
||||
if (chip->gpio_detect) {
|
||||
ret = gpiod_get_value_cansleep(chip->gpio_detect);
|
||||
@ -883,7 +930,7 @@ static int sbs_get_property(struct power_supply *psy,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
ret = sbs_get_chemistry(client, val);
|
||||
ret = sbs_get_chemistry(chip, val);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
@ -935,23 +982,12 @@ static int sbs_get_property(struct power_supply *psy,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = sbs_get_battery_string_property(client, ret, psp,
|
||||
model_name);
|
||||
val->strval = model_name;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = sbs_get_battery_string_property(client, ret, psp,
|
||||
manufacturer);
|
||||
val->strval = manufacturer;
|
||||
str = sbs_get_constant_string(chip, psp);
|
||||
if (IS_ERR(str))
|
||||
ret = PTR_ERR(str);
|
||||
else
|
||||
val->strval = str;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
|
||||
@ -1098,6 +1134,7 @@ static int sbs_probe(struct i2c_client *client)
|
||||
psy_cfg.of_node = client->dev.of_node;
|
||||
psy_cfg.drv_data = chip;
|
||||
chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
sbs_invalidate_cached_props(chip);
|
||||
mutex_init(&chip->mode_lock);
|
||||
|
||||
/* use pdata if available, fall back to DT properties,
|
||||
|
@ -524,6 +524,7 @@ static const struct of_device_id sc2731_charger_of_match[] = {
|
||||
{ .compatible = "sprd,sc2731-charger", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sc2731_charger_of_match);
|
||||
|
||||
static struct platform_driver sc2731_charger_driver = {
|
||||
.driver = {
|
||||
|
@ -1342,6 +1342,7 @@ static const struct of_device_id sc27xx_fgu_of_match[] = {
|
||||
{ .compatible = "sprd,sc2731-fgu", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match);
|
||||
|
||||
static struct platform_driver sc27xx_fgu_driver = {
|
||||
.probe = sc27xx_fgu_probe,
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -345,6 +345,16 @@ static u32 spwr_notify_bat(struct ssam_event_notifier *nf, const struct ssam_eve
|
||||
struct spwr_battery_device *bat = container_of(nf, struct spwr_battery_device, notif);
|
||||
int status;
|
||||
|
||||
/*
|
||||
* We cannot use strict matching when registering the notifier as the
|
||||
* EC expects us to register it against instance ID 0. Strict matching
|
||||
* would thus drop events, as those may have non-zero instance IDs in
|
||||
* this subsystem. So we need to check the instance ID of the event
|
||||
* here manually.
|
||||
*/
|
||||
if (event->instance_id != bat->sdev->uid.instance)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&bat->sdev->dev, "power event (cid = %#04x, iid = %#04x, tid = %#04x)\n",
|
||||
event->command_id, event->instance_id, event->target_id);
|
||||
|
||||
@ -720,8 +730,8 @@ static void spwr_battery_init(struct spwr_battery_device *bat, struct ssam_devic
|
||||
bat->notif.base.fn = spwr_notify_bat;
|
||||
bat->notif.event.reg = registry;
|
||||
bat->notif.event.id.target_category = sdev->uid.category;
|
||||
bat->notif.event.id.instance = 0;
|
||||
bat->notif.event.mask = SSAM_EVENT_MASK_STRICT;
|
||||
bat->notif.event.id.instance = 0; /* need to register with instance 0 */
|
||||
bat->notif.event.mask = SSAM_EVENT_MASK_TARGET;
|
||||
bat->notif.event.flags = SSAM_EVENT_SEQUENCED;
|
||||
|
||||
bat->psy_desc.name = bat->name;
|
||||
|
@ -66,7 +66,7 @@ struct spwr_ac_device {
|
||||
|
||||
static int spwr_ac_update_unlocked(struct spwr_ac_device *ac)
|
||||
{
|
||||
u32 old = ac->state;
|
||||
__le32 old = ac->state;
|
||||
int status;
|
||||
|
||||
lockdep_assert_held(&ac->lock);
|
||||
|
@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
*/
|
||||
|
||||
#ifndef __MAX17040_BATTERY_H_
|
||||
#define __MAX17040_BATTERY_H_
|
||||
|
||||
struct max17040_platform_data {
|
||||
int (*battery_online)(void);
|
||||
int (*charger_online)(void);
|
||||
int (*charger_enable)(void);
|
||||
};
|
||||
|
||||
#endif
|
@ -1,48 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* PM2301 charger driver.
|
||||
*
|
||||
* Copyright (C) 2012 ST Ericsson Corporation
|
||||
*
|
||||
* Contact: Olivier LAUNAY (olivier.launay@stericsson.com
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PM2301_H
|
||||
#define __LINUX_PM2301_H
|
||||
|
||||
/**
|
||||
* struct pm2xxx_bm_charger_parameters - Charger specific parameters
|
||||
* @ac_volt_max: maximum allowed AC charger voltage in mV
|
||||
* @ac_curr_max: maximum allowed AC charger current in mA
|
||||
*/
|
||||
struct pm2xxx_bm_charger_parameters {
|
||||
int ac_volt_max;
|
||||
int ac_curr_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pm2xxx_bm_data - pm2xxx battery management data
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
* @chg_params charger parameters
|
||||
*/
|
||||
struct pm2xxx_bm_data {
|
||||
bool enable_overshoot;
|
||||
const struct pm2xxx_bm_charger_parameters *chg_params;
|
||||
};
|
||||
|
||||
struct pm2xxx_charger_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
int i2c_bus;
|
||||
const char *label;
|
||||
int gpio_irq_number;
|
||||
unsigned int lpn_gpio;
|
||||
int irq_type;
|
||||
};
|
||||
|
||||
struct pm2xxx_platform_data {
|
||||
struct pm2xxx_charger_platform_data *wall_charger;
|
||||
struct pm2xxx_bm_data *battery;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PM2301_H */
|
@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson 2013
|
||||
* Author: Hongbo Zhang <hongbo.zhang@linaro.com>
|
||||
*/
|
||||
|
||||
#ifndef PWR_AB8500_H
|
||||
#define PWR_AB8500_H
|
||||
|
||||
extern const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[];
|
||||
extern const int ab8500_temp_tbl_a_size;
|
||||
|
||||
extern const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[];
|
||||
extern const int ab8500_temp_tbl_b_size;
|
||||
|
||||
#endif /* PWR_AB8500_H */
|
Loading…
Reference in New Issue
Block a user