mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
power supply and reset changes for the 7.4 series
* power-supply core
- introduce LOAD_SWITCH property
- pack power_supply_desc to optimize memory
- prevent power-supply device unregistration while callback runs
- drop last CONFIG_OF build-time dependencies from the core
- documentation fixes
* power-supply drivers
- bq27xxx: add support for bq27z746, bq28z620
- ab8500: add AB8505 support
- bq257xx: fix probe time configuration race condition
- multiple: convert drivers with custom LOAD_SWITCH properties to the
new standard one
- cros_charge-control: restore EC state on resume
- qcom_battmgr: fix USB type reporting, notify charger online state,
report full batteries and correct capacity on X1E
- gpio-charger: always report as discharging if input power is missing
- bq24190_charger: don't reset registers across system suspend
- qcom_smbx: a lot of independent fixes
- lots of error and module removal path fixes
- misc. fixes
* reset drivers
- misc. fixes
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAmrdEVEACgkQ2O7X88g7
+pplTw//SLNG+0B8UHHPDyFndW7gbAlvAAPytFvh1EHhOyYBtuoogqhDPCfVjWkp
P2ZjTjjfGhYA2k5jaDTt+eP4xLOAuE4e1V5V1JcEbTXfhqWp6Tas+m9LWMK1Wqto
ozckezlQ4XEDdCRXUjdYKz3WBKk4tyfmZaUZ6+LX6N/a+Dz8VF9MyPitOIYeCTbD
USBfqcR9Aj98XAP4e9fgQbNVhIifqz6AQ/Vp+YcprIgy8zcYilwn4FwiEXtBsyND
aUbVrSs7ZGL5F7mgXdvPVR9ZSODWyE3Xpkfywc2dJrGcl2N2fF1lDoupOA781FqG
r3RQBlZSinO4LWf+idt71ge97e4/Leqd92c46UOHmySnJ4BtkpjamzroFI0++llV
Y3CjPxYvIZr0PzSBBtsX6xMYGuIJ5SnofZfAaCswHexSEV9Kawem08gYSGK246Il
fG5gZHC58w1F5ES/6qBOQNEuspJJupezeZvLnPAWI+QyTFeuOGxB8ClsKYSaD6P1
/1/58DzL/Rf1MCmVO1RaYI9HfUciLX2f/TiXLbgBkJUHWsw8XnUDqvmRmkWrZBky
HUsXXy8GGoquBdOS2C8X5jd3UZFYBZfn5KiUXwd7kB6f34hlIucc4mYlUJ6LAR5o
5mYNIk9hxBhuwYHx1Jj7clh/4i4uNAFSPzcwmBwoo91cTgi3t0Y=
=SDvw
-----END PGP SIGNATURE-----
Merge tag 'for-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
"Power-supply core:
- introduce LOAD_SWITCH property
- pack power_supply_desc to optimize memory
- prevent power-supply device unregistration while callback runs
- drop last CONFIG_OF build-time dependencies from the core
- documentation fixes
Power-supply drivers:
- bq27xxx: add support for bq27z746, bq28z620
- ab8500: add AB8505 support
- bq257xx: fix probe time configuration race condition
- multiple: convert drivers with custom LOAD_SWITCH properties to the
new standard one
- cros_charge-control: restore EC state on resume
- qcom_battmgr: fix USB type reporting, notify charger online state,
report full batteries and correct capacity on X1E
- gpio-charger: always report as discharging if input power is
missing
- bq24190_charger: don't reset registers across system suspend
- qcom_smbx: a lot of independent fixes
- lots of error and module removal path fixes
- misc fixes
Reset drivers
- misc fixes"
* tag 'for-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (77 commits)
power: supply: gpio-charger: Report discharging without input power
power: supply: qcom_battmgr: report full batteries
power_supply: serialize property access with unregister
power: supply: bq257xx: Describe the dev field in the driver data structure
power: supply: qcom_battmgr: Notify charger supplies of online changes
power: supply: generic-adc-battery: fix status with no supplier
power: supply: qcom_battmgr: report the USB type from USB_ADAP_TYPE
power: supply: sysfs: Ratelimit "unavailable enum value" warning
power: supply: cros_charge-control: restore EC state on resume
power: supply: qcom_smbx: set the charge enable after software control
power: supply: generic-adc-battery: Propagate charged GPIO errors
power: supply: sbs-battery: disable polling before supply teardown
power: supply: pf1550: drain IRQ work before unregistering supplies
power: supply: bq256xx: Expose VBUS state through extcon
power: supply: ab8500_fg: Avoid reserved AB8505 CCConf bit
power: supply: ab8500_fg: Finalize current measurements
power: supply: ab8500_charger: Recover inactive AB8505 charger
power: supply: ab8500_charger: Retry AB8505 USB detection
power: supply: ab8500_charger: Skip absent AB8505 main charger
power: supply: ab8500_charger: Handle detection errors
...
This commit is contained in:
commit
0ddbc782c0
|
|
@ -365,9 +365,12 @@ Contact: linux-pm@vger.kernel.org
|
|||
Description:
|
||||
Represents a battery percentage level, above which charging will
|
||||
stop. Not all hardware is capable of setting this to an arbitrary
|
||||
percentage. Drivers will round written values to the nearest
|
||||
supported value. Reading back the value will show the actual
|
||||
threshold set by the driver.
|
||||
value, instead providing different minimum, maximum, or step
|
||||
values. Drivers will round written values to the nearest supported
|
||||
value. Reading back the value will show the actual threshold set
|
||||
by the driver. For hardware that only supports a single fixed
|
||||
value, use charge_types with a value of "Long Life" (vs "Standard")
|
||||
instead'
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
|
|
@ -398,8 +401,9 @@ Description:
|
|||
when to start and stop charging. Advanced users
|
||||
can use this to drastically extend battery life.
|
||||
Long Life:
|
||||
The charger reduces its charging rate in order to
|
||||
prolong the battery health.
|
||||
The charger firmware reduces its charging rate and/or
|
||||
maximum charging percentage to a hardware specified
|
||||
fixed limit in order to prolong the battery health.
|
||||
Bypass:
|
||||
The charger bypasses the charging path around the
|
||||
integrated converter allowing for a "smart" wall
|
||||
|
|
@ -590,6 +594,31 @@ Description:
|
|||
|
||||
Valid values: 0 - 100 (percent)
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/load_switch
|
||||
Date: June 2026
|
||||
Contact: linux-pm@vger.kernel.org
|
||||
Description:
|
||||
Devices can control the connection from power to system load.
|
||||
For example, with a field-effect transistor between a battery
|
||||
and the system load (BATFET). This entry controls such switch.
|
||||
The obvious states are "on" and "off", i.e. there is a
|
||||
connection or not, respectively. However, many devices can also
|
||||
enter special modes such as "low-power", "shipping" or "deep
|
||||
sleep". In these modes the switch is usually off and the
|
||||
quiescent current quite low.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
Valid values:
|
||||
|
||||
============= ==================================
|
||||
"Unknown" (0) Unknown mode.
|
||||
"On" (1) Power is connected to the load.
|
||||
"Off" (2) Power is disconnected to the load.
|
||||
"Standby" (3) Low-power mode.
|
||||
"Ship" (4) Ship mode.
|
||||
============= ==================================
|
||||
|
||||
**USB Properties**
|
||||
|
||||
What: /sys/class/power_supply/<supply_name>/input_current_limit
|
||||
|
|
@ -600,8 +629,8 @@ Description:
|
|||
supply. Normally this is configured based on the type of
|
||||
connection made (e.g. A configured SDP should output a maximum
|
||||
of 500mA so the input current limit is set to the same value).
|
||||
Use preferably input_power_limit, and for problems that can be
|
||||
solved using power limit use input_current_limit.
|
||||
Use preferably input_power_limit, and for problems that can not
|
||||
be solved using power limit use input_current_limit.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
|
|
@ -618,8 +647,8 @@ Description:
|
|||
input power to 5V when the screen is on to meet Google's skin
|
||||
temperature targets). Note that this feature should not be
|
||||
used for safety critical things.
|
||||
Use preferably input_power_limit, and for problems that can be
|
||||
solved using power limit use input_voltage_limit.
|
||||
Use preferably input_power_limit, and for problems that can not
|
||||
be solved using power limit use input_voltage_limit.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
|
|
@ -633,7 +662,7 @@ Description:
|
|||
in the supply. Normally this is configured based on
|
||||
system-level knowledge or user input. Use preferably this
|
||||
feature to limit the incoming power and use current/voltage
|
||||
limit only for problems that can be solved using power limit.
|
||||
limit only for problems that can not be solved using power limit.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ Description:
|
|||
The ship mode, when armed, activates once the input voltage
|
||||
drops below 1V.
|
||||
|
||||
Note: use /sys/class/power_supply/ltc4162-l/load_switch instead.
|
||||
|
||||
Access: Read, Write
|
||||
|
||||
Valid values: 0 (disable) or 1 (enable)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
What: /sys/class/power_supply/mp2629_battery/batt_impedance_compen
|
||||
What: /sys/class/power_supply/mp2629_battery/batt_impedance_compensation
|
||||
Date: April 2020
|
||||
KernelVersion: 5.7
|
||||
Description:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ Description:
|
|||
'Disable' also can help to leave it, but it's more like to
|
||||
abort the action before the device really enter shipping mode.
|
||||
|
||||
Note: use /sys/class/power_supply/rt9467-*/load_switch instead.
|
||||
|
||||
Access: Read, Write
|
||||
Valid values:
|
||||
- 1: enabled
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ Description:
|
|||
mode. 'Disable' also can help to leave it, but it's more like to abort
|
||||
the action before the device really enter shipping mode.
|
||||
|
||||
Note: use /sys/class/power_supply/rt9471-*/load_switch instead.
|
||||
|
||||
Access: Read, Write
|
||||
Valid values:
|
||||
- 1: enabled
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ patternProperties:
|
|||
of the battery and corresponding battery capacity percent, which is used
|
||||
to look up battery capacity according to current OCV value. And the open
|
||||
circuit voltage unit is microvolt.
|
||||
maxItems: 100
|
||||
# One point for each capacity percent from 0 to 100
|
||||
maxItems: 101
|
||||
items:
|
||||
items:
|
||||
- description: open circuit voltage (OCV) in microvolts
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ properties:
|
|||
- ti,bq27441
|
||||
- ti,bq27621
|
||||
- ti,bq27z561
|
||||
- ti,bq27z746
|
||||
- ti,bq28z610
|
||||
- ti,bq28z620
|
||||
- ti,bq34z100
|
||||
- ti,bq78z100
|
||||
|
||||
|
|
@ -91,8 +93,8 @@ allOf:
|
|||
- ti,bq27742
|
||||
- ti,bq27545
|
||||
- ti,bq27411
|
||||
- ti,bq27z561
|
||||
- ti,bq28z610
|
||||
- ti,bq28z620
|
||||
- ti,bq34z100
|
||||
- ti,bq78z100
|
||||
then:
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ title: AB8500 Charger
|
|||
maintainers:
|
||||
- Sebastian Reichel <sre@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: stericsson,ab8500-charger
|
||||
oneOf:
|
||||
- const: stericsson,ab8500-charger
|
||||
- items:
|
||||
- const: stericsson,ab8505-charger
|
||||
- const: stericsson,ab8500-charger
|
||||
|
||||
monitored-battery: true
|
||||
|
||||
|
|
@ -28,27 +29,15 @@ properties:
|
|||
|
||||
autopower_cfg:
|
||||
type: boolean
|
||||
description: automatic poweron after powerloss
|
||||
description: AB8500 automatic poweron after powerloss
|
||||
|
||||
interrupts:
|
||||
minItems: 9
|
||||
maxItems: 14
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: MAIN_CH_UNPLUG_DET
|
||||
- const: MAIN_CHARGE_PLUG_DET
|
||||
- const: MAIN_EXT_CH_NOT_OK
|
||||
- const: MAIN_CH_TH_PROT_R
|
||||
- const: MAIN_CH_TH_PROT_F
|
||||
- const: VBUS_DET_F
|
||||
- const: VBUS_DET_R
|
||||
- const: USB_LINK_STATUS
|
||||
- const: USB_CH_TH_PROT_R
|
||||
- const: USB_CH_TH_PROT_F
|
||||
- const: USB_CHARGER_NOT_OKR
|
||||
- const: VBUS_OVV
|
||||
- const: CH_WD_EXP
|
||||
- const: VBUS_CH_DROP_END
|
||||
minItems: 9
|
||||
maxItems: 14
|
||||
|
||||
io-channels:
|
||||
minItems: 2
|
||||
|
|
@ -74,6 +63,53 @@ required:
|
|||
- io-channels
|
||||
- io-channel-names
|
||||
|
||||
allOf:
|
||||
- $ref: power-supply.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: stericsson,ab8505-charger
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 9
|
||||
maxItems: 9
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: VBUS_DET_F
|
||||
- const: VBUS_DET_R
|
||||
- const: USB_LINK_STATUS
|
||||
- const: USB_CH_TH_PROT_R
|
||||
- const: USB_CH_TH_PROT_F
|
||||
- const: USB_CHARGER_NOT_OKR
|
||||
- const: VBUS_OVV
|
||||
- const: CH_WD_EXP
|
||||
- const: VBUS_CH_DROP_END
|
||||
else:
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 14
|
||||
maxItems: 14
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: MAIN_CH_UNPLUG_DET
|
||||
- const: MAIN_CHARGE_PLUG_DET
|
||||
- const: MAIN_EXT_CH_NOT_OK
|
||||
- const: MAIN_CH_TH_PROT_R
|
||||
- const: MAIN_CH_TH_PROT_F
|
||||
- const: VBUS_DET_F
|
||||
- const: VBUS_DET_R
|
||||
- const: USB_LINK_STATUS
|
||||
- const: USB_CH_TH_PROT_R
|
||||
- const: USB_CH_TH_PROT_F
|
||||
- const: USB_CHARGER_NOT_OKR
|
||||
- const: VBUS_OVV
|
||||
- const: CH_WD_EXP
|
||||
- const: VBUS_CH_DROP_END
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ _NOW
|
|||
STATUS
|
||||
this attribute represents operating status (charging, full,
|
||||
discharging (i.e., powering a load), etc.). This corresponds to
|
||||
`BATTERY_STATUS_*` values, as defined in battery.h.
|
||||
`POWER_SUPPLY_STATUS_*` values, as defined in
|
||||
include/linux/power_supply.h.
|
||||
|
||||
CHARGE_TYPE
|
||||
batteries can typically charge at different rates.
|
||||
|
|
@ -95,8 +96,8 @@ AUTHENTIC
|
|||
to the platform is authentic(1) or non-authentic(0).
|
||||
|
||||
HEALTH
|
||||
represents health of the battery. Values corresponds to
|
||||
POWER_SUPPLY_HEALTH_*, defined in battery.h.
|
||||
represents health of the battery. Values correspond to
|
||||
POWER_SUPPLY_HEALTH_*, defined in include/linux/power_supply.h.
|
||||
|
||||
VOLTAGE_OCV
|
||||
open circuit voltage of the battery.
|
||||
|
|
@ -202,16 +203,17 @@ TEMP_AMBIENT_ALERT_MIN
|
|||
TEMP_AMBIENT_ALERT_MAX
|
||||
maximum ambient temperature alert.
|
||||
TEMP_MIN
|
||||
minimum operatable temperature
|
||||
minimum operable temperature
|
||||
TEMP_MAX
|
||||
maximum operatable temperature
|
||||
maximum operable temperature
|
||||
|
||||
TIME_TO_EMPTY
|
||||
TIME_TO_EMPTY_NOW, TIME_TO_EMPTY_AVG
|
||||
seconds left for battery to be considered empty
|
||||
(i.e., while battery powers a load)
|
||||
TIME_TO_FULL
|
||||
(i.e., while battery powers a load). *_NOW is a
|
||||
momentary reading; *_AVG is averaged over a fixed period.
|
||||
TIME_TO_FULL_NOW, TIME_TO_FULL_AVG
|
||||
seconds left for battery to be considered full
|
||||
(i.e., while battery is charging)
|
||||
(i.e., while battery is charging). Same now/avg distinction.
|
||||
|
||||
|
||||
Battery <-> external power supply interaction
|
||||
|
|
@ -253,9 +255,6 @@ A:
|
|||
The attributes available currently are the ones currently provided by the
|
||||
drivers written.
|
||||
|
||||
Good candidates to add in future: model/part#, cycle_time, manufacturer,
|
||||
etc.
|
||||
|
||||
|
||||
Q:
|
||||
I have some very specific attribute (e.g., battery color). Should I add
|
||||
|
|
|
|||
|
|
@ -112,8 +112,7 @@ static int rsctrl_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < WDT_MUX_NUMBER; i++) {
|
||||
ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val);
|
||||
if (ret == -EOVERFLOW && !i) {
|
||||
dev_err(dev, "ti,wdt-list property has to contain at"
|
||||
"least one entry\n");
|
||||
dev_err(dev, "ti,wdt-list property has to contain at least one entry\n");
|
||||
return -EINVAL;
|
||||
} else if (ret) {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -221,8 +221,8 @@ static int macsmc_reboot_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(cell)) {
|
||||
if (PTR_ERR(cell) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n",
|
||||
nvmem_names[i], PTR_ERR(cell));
|
||||
dev_warn(&pdev->dev, "Missing NVMEM cell %s (%pe)\n",
|
||||
nvmem_names[i], cell);
|
||||
/* Non fatal, we'll deal with it */
|
||||
cell = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -850,6 +850,7 @@ config CHARGER_BQ256XX
|
|||
tristate "TI BQ256XX battery charger driver"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on EXTCON || !EXTCON
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to enable support for the TI BQ256XX battery chargers. The
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@
|
|||
#define CH_OP_CUR_LVL_0P9 0x08
|
||||
#define CH_OP_CUR_LVL_1P4 0x0D
|
||||
#define CH_OP_CUR_LVL_1P5 0x0E
|
||||
#define CH_OP_CUR_LVL_1P6 0x0F
|
||||
#define CH_OP_CUR_LVL_2P 0x3F
|
||||
#define CH_OP_CUR_LVL_1P5_MAX 0x0F
|
||||
|
||||
/* BTEMP High thermal limits */
|
||||
#define BTEMP_HIGH_TH_57_0 0x00
|
||||
|
|
@ -233,7 +232,7 @@ enum bup_vch_sel {
|
|||
/* BatCtrl Current Source Constants */
|
||||
#define BAT_CTRL_7U_ENA 0x01
|
||||
#define BAT_CTRL_20U_ENA 0x02
|
||||
#define BAT_CTRL_18U_ENA 0x01
|
||||
#define BAT_CTRL_8U_ENA 0x01
|
||||
#define BAT_CTRL_16U_ENA 0x02
|
||||
#define BAT_CTRL_CMP_ENA 0x04
|
||||
#define FORCE_BAT_CTRL_CMP_HIGH 0x08
|
||||
|
|
@ -246,7 +245,6 @@ enum bup_vch_sel {
|
|||
#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
|
||||
#define AB8505_RTC_PCUT_TIME_REG 0x13
|
||||
#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14
|
||||
#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15
|
||||
#define AB8505_RTC_PCUT_RESTART_REG 0x16
|
||||
#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17
|
||||
|
||||
|
|
@ -287,7 +285,6 @@ struct ab8500_fg;
|
|||
* battery full while in maintenance, in per cent
|
||||
* @pcut_enable: Enable power cut feature in ab8505
|
||||
* @pcut_max_time: Max time threshold
|
||||
* @pcut_flag_time: Flagtime threshold
|
||||
* @pcut_max_restart: Max number of restarts
|
||||
* @pcut_debounce_time: Sets battery debounce time
|
||||
*/
|
||||
|
|
@ -308,7 +305,6 @@ struct ab8500_fg_parameters {
|
|||
int maint_thres;
|
||||
bool pcut_enable;
|
||||
u8 pcut_max_time;
|
||||
u8 pcut_flag_time;
|
||||
u8 pcut_max_restart;
|
||||
u8 pcut_debounce_time;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ static const struct ab8500_fg_parameters fg = {
|
|||
.user_cap_limit = 15,
|
||||
.pcut_enable = 1,
|
||||
.pcut_max_time = 127,
|
||||
.pcut_flag_time = 112,
|
||||
.pcut_max_restart = 15,
|
||||
.pcut_debounce_time = 2,
|
||||
};
|
||||
|
|
@ -174,7 +173,7 @@ int ab8500_bm_of_probe(struct power_supply *psy,
|
|||
if (bi->constant_charge_voltage_max_uv < 0)
|
||||
bi->constant_charge_voltage_max_uv = 4100000;
|
||||
|
||||
if (bi->charge_term_current_ua)
|
||||
if (bi->charge_term_current_ua < 0)
|
||||
/* Charging stops when we drop below this current */
|
||||
bi->charge_term_current_ua = 200000;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
#define BTEMP_BATCTRL_CURR_SRC_20UA 20
|
||||
|
||||
#define BTEMP_BATCTRL_CURR_SRC_16UA 16
|
||||
#define BTEMP_BATCTRL_CURR_SRC_18UA 18
|
||||
#define BTEMP_BATCTRL_CURR_SRC_8UA 8
|
||||
|
||||
#define BTEMP_BATCTRL_CURR_SRC_60UA 60
|
||||
#define BTEMP_BATCTRL_CURR_SRC_120UA 120
|
||||
|
|
@ -468,39 +468,29 @@ static void ab8500_btemp_periodic(struct ab8500_btemp *di,
|
|||
*/
|
||||
static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
|
||||
{
|
||||
int temp = 0;
|
||||
int temp = di->bat_temp;
|
||||
|
||||
/*
|
||||
* The BTEMP events are not reliabe on AB8500 cut3.3
|
||||
* and prior versions
|
||||
*/
|
||||
if (is_ab8500_3p3_or_earlier(di->parent)) {
|
||||
temp = di->bat_temp * 10;
|
||||
} else {
|
||||
if (!is_ab8500_3p3_or_earlier(di->parent)) {
|
||||
if (di->events.btemp_low) {
|
||||
if (temp > di->btemp_ranges.btemp_low_limit)
|
||||
temp = di->btemp_ranges.btemp_low_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
temp = di->btemp_ranges.btemp_low_limit;
|
||||
} else if (di->events.btemp_high) {
|
||||
if (temp < di->btemp_ranges.btemp_high_limit)
|
||||
temp = di->btemp_ranges.btemp_high_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
temp = di->btemp_ranges.btemp_high_limit;
|
||||
} else if (di->events.btemp_lowmed) {
|
||||
if (temp > di->btemp_ranges.btemp_med_limit)
|
||||
temp = di->btemp_ranges.btemp_med_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
temp = di->btemp_ranges.btemp_med_limit;
|
||||
} else if (di->events.btemp_medhigh) {
|
||||
if (temp < di->btemp_ranges.btemp_med_limit)
|
||||
temp = di->btemp_ranges.btemp_med_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
} else
|
||||
temp = di->bat_temp * 10;
|
||||
temp = di->btemp_ranges.btemp_med_limit;
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
|
||||
return temp * 10;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1241,12 +1241,9 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
|
|||
|
||||
charger_status = ab8500_chargalg_check_charger_connection(di);
|
||||
|
||||
if (is_ab8500(di->parent)) {
|
||||
ret = ab8500_chargalg_check_charger_enable(di);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "Checking charger is enabled error"
|
||||
": Returned Value %d\n", ret);
|
||||
}
|
||||
ret = ab8500_chargalg_check_charger_enable(di);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "Checking charger enable failed: %d\n", ret);
|
||||
|
||||
/*
|
||||
* First check if we have a charger connected.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@
|
|||
#define MAIN_CH_DET 0x01
|
||||
#define MAIN_CH_CV_ON 0x04
|
||||
#define USB_CH_CV_ON 0x08
|
||||
#define VBUS_DET_DBNC100 0x02
|
||||
#define USB_CH_ON 0x04
|
||||
#define VBUS_DET_DBNC_LONG 0x02
|
||||
#define VBUS_DET_DBNC1 0x01
|
||||
#define OTP_ENABLE_WD 0x01
|
||||
#define DROP_COUNT_RESET 0x01
|
||||
|
|
@ -66,7 +67,7 @@
|
|||
#define LED_INDICATOR_PWM_ENA 0x01
|
||||
#define LED_INDICATOR_PWM_DIS 0x00
|
||||
#define LED_IND_CUR_5MA 0x04
|
||||
#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
|
||||
#define LED_INDICATOR_PWM_DUTY_252_256 0xFB
|
||||
|
||||
/* HW failure constants */
|
||||
#define MAIN_CH_TH_PROT 0x02
|
||||
|
|
@ -99,6 +100,8 @@
|
|||
|
||||
#define CHARGER_STATUS_POLL 10 /* in ms */
|
||||
|
||||
#define AB8505_USB_CHARGER_NOT_OK_RETRIES 3
|
||||
|
||||
#define CHG_WD_INTERVAL (60 * HZ)
|
||||
|
||||
#define AB8500_SW_CONTROL_FALLBACK 0x03
|
||||
|
|
@ -136,6 +139,33 @@ enum ab8500_charger_link_status {
|
|||
USB_STAT_ACA_DOCK_CHARGER,
|
||||
};
|
||||
|
||||
/* UsbLink1Status register values used by AB8505 */
|
||||
enum ab8505_charger_link_status {
|
||||
AB8505_USB_STAT_OFF,
|
||||
AB8505_USB_STAT_SDP_NOT_CHARGING,
|
||||
AB8505_USB_STAT_SDP_CHARGING,
|
||||
AB8505_USB_STAT_SDP_SUSPENDED,
|
||||
AB8505_USB_STAT_CDP,
|
||||
AB8505_USB_STAT_RESERVED_5,
|
||||
AB8505_USB_STAT_RESERVED_6,
|
||||
AB8505_USB_STAT_DCP,
|
||||
AB8505_USB_STAT_ACA_RID_A,
|
||||
AB8505_USB_STAT_ACA_RID_B,
|
||||
AB8505_USB_STAT_ACA_RID_C,
|
||||
AB8505_USB_STAT_RESERVED_11,
|
||||
AB8505_USB_STAT_RESERVED_12,
|
||||
AB8505_USB_STAT_UPSTREAM_IDGND,
|
||||
AB8505_USB_STAT_CHARGER_NOT_OK,
|
||||
AB8505_USB_STAT_CHARGER_DM_HIGH,
|
||||
AB8505_USB_STAT_PHY_ENABLED,
|
||||
AB8505_USB_STAT_UPSTREAM_NO_IDGND,
|
||||
AB8505_USB_STAT_UPSTREAM_IDGND_VBUS,
|
||||
AB8505_USB_STAT_CHARGER_SE1,
|
||||
AB8505_USB_STAT_CARKIT_1,
|
||||
AB8505_USB_STAT_CARKIT_2,
|
||||
AB8505_USB_STAT_ACA_DOCK_CHARGER,
|
||||
};
|
||||
|
||||
enum ab8500_usb_state {
|
||||
AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
|
||||
AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
|
||||
|
|
@ -234,6 +264,8 @@ struct ab8500_charger_max_usb_in_curr {
|
|||
* @is_aca_rid: Incicate if accessory is ACA type
|
||||
* @current_stepping_sessions:
|
||||
* Counter for current stepping sessions
|
||||
* @usb_charger_not_ok_retries:
|
||||
* Number of AB8505 USB charger recovery attempts
|
||||
* @parent: Pointer to the struct ab8500
|
||||
* @adc_main_charger_v ADC channel for main charger voltage
|
||||
* @adc_main_charger_c ADC channel for main charger current
|
||||
|
|
@ -287,6 +319,7 @@ struct ab8500_charger {
|
|||
int invalid_charger_detect_state;
|
||||
int is_aca_rid;
|
||||
atomic_t current_stepping_sessions;
|
||||
unsigned int usb_charger_not_ok_retries;
|
||||
struct ab8500 *parent;
|
||||
struct iio_channel *adc_main_charger_v;
|
||||
struct iio_channel *adc_main_charger_c;
|
||||
|
|
@ -356,6 +389,10 @@ static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
|
|||
u8 bit;
|
||||
int ret;
|
||||
|
||||
/* SwControlFallback is reserved and must remain clear on AB8505. */
|
||||
if (is_ab8505(di->parent))
|
||||
return;
|
||||
|
||||
dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
|
||||
|
||||
if (is_ab8500(di->parent)) {
|
||||
|
|
@ -442,6 +479,9 @@ static void ab8500_power_supply_changed(struct ab8500_charger *di,
|
|||
static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
|
||||
bool connected)
|
||||
{
|
||||
if (!connected && !di->vbus_detected)
|
||||
di->usb_charger_not_ok_retries = 0;
|
||||
|
||||
if (connected != di->usb.charger_connected) {
|
||||
dev_dbg(di->dev, "USB connected:%i\n", connected);
|
||||
di->usb.charger_connected = connected;
|
||||
|
|
@ -661,27 +701,34 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
|
|||
int ret;
|
||||
u8 val;
|
||||
|
||||
/* Check for AC charger */
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
|
||||
AB8500_CH_STATUS1_REG, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
/* AB8505 has no integrated main charger. */
|
||||
if (!is_ab8505(di->parent)) {
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CH_STATUS1_REG, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val & MAIN_CH_DET)
|
||||
result = AC_PW_CONN;
|
||||
if (val & MAIN_CH_DET)
|
||||
result = AC_PW_CONN;
|
||||
}
|
||||
|
||||
/* Check for USB charger */
|
||||
|
||||
if (!probe) {
|
||||
/*
|
||||
* AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
|
||||
* AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC_LONG
|
||||
* when disconnecting ACA even though no
|
||||
* charger was connected. Try waiting a little
|
||||
* longer than the 100 ms of VBUS_DET_DBNC100...
|
||||
* longer than its 100 ms debounce. AB8505 uses
|
||||
* a 300 ms falling debounce for the same bit.
|
||||
*/
|
||||
msleep(110);
|
||||
if (is_ab8505(di->parent))
|
||||
msleep(310);
|
||||
else
|
||||
msleep(110);
|
||||
}
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT1_REG, &val);
|
||||
|
|
@ -692,27 +739,118 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
|
|||
dev_dbg(di->dev,
|
||||
"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
|
||||
val);
|
||||
if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
|
||||
if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC_LONG))
|
||||
result |= USB_PW_CONN;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_charger_max_usb_curr() - get the max curr for the USB type
|
||||
* ab8505_charger_max_usb_curr() - get the max current for the USB type
|
||||
* @di: pointer to the ab8500_charger structure
|
||||
* @link_status: the identified USB type
|
||||
*
|
||||
* Get the maximum current that is allowed to be drawn from the host
|
||||
* based on the USB type.
|
||||
* Decode AB8505-specific UsbLink1Status values and get the maximum current
|
||||
* that is allowed to be drawn based on the USB type.
|
||||
* Returns error code in case of failure else 0 on success
|
||||
*/
|
||||
static int ab8505_charger_max_usb_curr(struct ab8500_charger *di,
|
||||
u8 link_status)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
di->is_aca_rid = 0;
|
||||
|
||||
switch (link_status) {
|
||||
case AB8505_USB_STAT_SDP_NOT_CHARGING:
|
||||
case AB8505_USB_STAT_SDP_CHARGING:
|
||||
case AB8505_USB_STAT_SDP_SUSPENDED:
|
||||
case AB8505_USB_STAT_CDP:
|
||||
case AB8505_USB_STAT_CHARGER_DM_HIGH:
|
||||
case AB8505_USB_STAT_UPSTREAM_NO_IDGND:
|
||||
case AB8505_USB_STAT_UPSTREAM_IDGND_VBUS:
|
||||
case AB8505_USB_STAT_CHARGER_SE1:
|
||||
case AB8505_USB_STAT_CARKIT_1:
|
||||
case AB8505_USB_STAT_CARKIT_2:
|
||||
case AB8505_USB_STAT_ACA_DOCK_CHARGER:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5;
|
||||
break;
|
||||
case AB8505_USB_STAT_DCP:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5;
|
||||
break;
|
||||
case AB8505_USB_STAT_ACA_RID_A:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P5;
|
||||
di->is_aca_rid = 1;
|
||||
break;
|
||||
case AB8505_USB_STAT_ACA_RID_B:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P3;
|
||||
di->is_aca_rid = 1;
|
||||
break;
|
||||
case AB8505_USB_STAT_ACA_RID_C:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_1P5;
|
||||
di->is_aca_rid = 1;
|
||||
break;
|
||||
case AB8505_USB_STAT_OFF:
|
||||
if (di->vbus_detected) {
|
||||
di->usb_device_is_unrecognised = true;
|
||||
di->max_usb_in_curr.usb_type_max_ua =
|
||||
USB_CH_IP_CUR_LVL_1P5;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case AB8505_USB_STAT_UPSTREAM_IDGND:
|
||||
case AB8505_USB_STAT_PHY_ENABLED:
|
||||
dev_err(di->dev, "USB Type - Charging not allowed\n");
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
case AB8505_USB_STAT_CHARGER_NOT_OK:
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
|
||||
if (di->usb_charger_not_ok_retries <
|
||||
AB8505_USB_CHARGER_NOT_OK_RETRIES) {
|
||||
dev_warn(di->dev,
|
||||
"USB Type - VBUS has collapsed, retrying\n");
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->check_usbchgnotok_work, 0);
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
dev_err(di->dev,
|
||||
"USB Type - VBUS recovery failed\n");
|
||||
ret = -ENXIO;
|
||||
}
|
||||
break;
|
||||
case AB8505_USB_STAT_RESERVED_5:
|
||||
case AB8505_USB_STAT_RESERVED_6:
|
||||
case AB8505_USB_STAT_RESERVED_11:
|
||||
case AB8505_USB_STAT_RESERVED_12:
|
||||
default:
|
||||
dev_err(di->dev, "USB Type - Unknown\n");
|
||||
di->max_usb_in_curr.usb_type_max_ua = USB_CH_IP_CUR_LVL_0P05;
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_charger_max_usb_curr() - get the max current for the USB type
|
||||
* @di: pointer to the ab8500_charger structure
|
||||
* @link_status: the identified USB type
|
||||
*
|
||||
* Get the maximum current that is allowed to be drawn based on the USB type.
|
||||
* Returns error code in case of failure else 0 on success.
|
||||
*/
|
||||
static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
|
||||
enum ab8500_charger_link_status link_status)
|
||||
u8 link_status)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
di->usb_device_is_unrecognised = false;
|
||||
if (is_ab8505(di->parent)) {
|
||||
ret = ab8505_charger_max_usb_curr(di, link_status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform only supports USB 2.0.
|
||||
|
|
@ -721,7 +859,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
|
|||
* should set USB_CH_IP_CUR_LVL_0P5.
|
||||
*/
|
||||
|
||||
switch (link_status) {
|
||||
switch ((enum ab8500_charger_link_status)link_status) {
|
||||
case USB_STAT_STD_HOST_NC:
|
||||
case USB_STAT_STD_HOST_C_NS:
|
||||
case USB_STAT_STD_HOST_C_S:
|
||||
|
|
@ -825,6 +963,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
|
|||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
di->max_usb_in_curr.set_max_ua = di->max_usb_in_curr.usb_type_max_ua;
|
||||
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
|
||||
link_status, di->max_usb_in_curr.set_max_ua);
|
||||
|
|
@ -866,8 +1005,7 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
|
|||
val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
|
||||
else
|
||||
val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
|
||||
ret = ab8500_charger_max_usb_curr(di,
|
||||
(enum ab8500_charger_link_status) val);
|
||||
ret = ab8500_charger_max_usb_curr(di, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -929,8 +1067,7 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
|
|||
if (val)
|
||||
break;
|
||||
}
|
||||
ret = ab8500_charger_max_usb_curr(di,
|
||||
(enum ab8500_charger_link_status) val);
|
||||
ret = ab8500_charger_max_usb_curr(di, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1741,6 +1878,9 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
|
|||
int vset_uv, int iset_ua)
|
||||
{
|
||||
u8 usbch_ctrl1 = 0;
|
||||
u8 usbch_status1;
|
||||
u8 usbch_status2;
|
||||
bool reenable;
|
||||
int ret = 0;
|
||||
|
||||
struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
|
||||
|
|
@ -1756,7 +1896,52 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
|
|||
}
|
||||
dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
|
||||
|
||||
if (!(usbch_ctrl1 & USB_CH_ENA)) {
|
||||
reenable = !(usbch_ctrl1 & USB_CH_ENA);
|
||||
if (is_ab8505(di->parent)) {
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT1_REG,
|
||||
&usbch_status1);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "USB charger status read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!reenable && (usbch_status1 & USB_CH_ON))
|
||||
return 0;
|
||||
|
||||
if (!(usbch_status1 & VBUS_DET_DBNC_LONG))
|
||||
return 0;
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT2_REG,
|
||||
&usbch_status2);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "USB charger fault status read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((usbch_status2 &
|
||||
(VBUS_CH_NOK | USB_CH_TH_PROT | VBUS_OVV_TH)) ||
|
||||
di->flags.usbchargernotok || di->flags.usb_thermal_prot ||
|
||||
di->flags.vbus_ovv || di->usb.wd_expired)
|
||||
return 0;
|
||||
|
||||
if (!reenable) {
|
||||
dev_info(di->dev,
|
||||
"USB charger enabled but inactive, cycling it\n");
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_USBCH_CTRL1_REG,
|
||||
USB_CH_ENA, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
reenable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reenable) {
|
||||
dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
|
|
@ -1778,7 +1963,7 @@ static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
|
|||
}
|
||||
|
||||
/**
|
||||
* ab8500_charger_ac_check_enable() - enable usb charging
|
||||
* ab8500_charger_ac_check_enable() - enable AC charging
|
||||
* @charger: pointer to the ux500_charger structure
|
||||
* @vset_uv: charging voltage in microvolt
|
||||
* @iset_ua: charger output current in micrompere
|
||||
|
|
@ -1817,7 +2002,7 @@ static int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = ab8500_charger_ac_en(&di->usb_chg, true, vset_uv, iset_ua);
|
||||
ret = ab8500_charger_ac_en(&di->ac_chg, true, vset_uv, iset_ua);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "failed to enable AC charger %d\n",
|
||||
__LINE__);
|
||||
|
|
@ -2277,6 +2462,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
|
|||
int ret;
|
||||
u8 val;
|
||||
u8 link_status;
|
||||
u8 link_status_mask;
|
||||
|
||||
struct ab8500_charger *di = container_of(work,
|
||||
struct ab8500_charger, usb_link_status_work);
|
||||
|
|
@ -2303,20 +2489,22 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
|
|||
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
|
||||
AB8500_USB_LINK1_STAT_REG, &val);
|
||||
|
||||
if (ret >= 0)
|
||||
dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
|
||||
else
|
||||
dev_dbg(di->dev, "Error reading USB link status\n");
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "Error reading USB link status\n");
|
||||
return;
|
||||
}
|
||||
dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
|
||||
|
||||
if (is_ab8500(di->parent))
|
||||
link_status = AB8500_USB_LINK_STATUS;
|
||||
link_status_mask = AB8500_USB_LINK_STATUS;
|
||||
else
|
||||
link_status = AB8505_USB_LINK_STATUS;
|
||||
link_status_mask = AB8505_USB_LINK_STATUS;
|
||||
link_status = (val & link_status_mask) >> USB_LINK_STATUS_SHIFT;
|
||||
|
||||
if (detected_chargers & USB_PW_CONN) {
|
||||
if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
|
||||
USB_STAT_NOT_VALID_LINK &&
|
||||
di->invalid_charger_detect_state == 0) {
|
||||
if (is_ab8500(di->parent) &&
|
||||
link_status == USB_STAT_NOT_VALID_LINK &&
|
||||
di->invalid_charger_detect_state == 0) {
|
||||
dev_dbg(di->dev,
|
||||
"Invalid charger detected, state= 0\n");
|
||||
/*Enable charger*/
|
||||
|
|
@ -2339,18 +2527,21 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
|
|||
abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
|
||||
USB_CH_DET, 0x00);
|
||||
/*Check link status*/
|
||||
if (is_ab8500(di->parent))
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_USB, AB8500_USB_LINE_STAT_REG,
|
||||
&val);
|
||||
else
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_USB, AB8500_USB_LINK1_STAT_REG,
|
||||
&val);
|
||||
/* Check link status */
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_USB,
|
||||
AB8500_USB_LINE_STAT_REG,
|
||||
&val);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev,
|
||||
"Error reading USB link status\n");
|
||||
return;
|
||||
}
|
||||
|
||||
link_status = (val & link_status_mask) >>
|
||||
USB_LINK_STATUS_SHIFT;
|
||||
dev_dbg(di->dev, "USB link status= 0x%02x\n",
|
||||
(val & link_status) >> USB_LINK_STATUS_SHIFT);
|
||||
link_status);
|
||||
di->invalid_charger_detect_state = 2;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2462,6 +2653,48 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
static int ab8505_charger_retry_usb_detection(struct ab8500_charger *di)
|
||||
{
|
||||
u8 usbch_ctrl1;
|
||||
bool usb_enabled;
|
||||
int restore_ret;
|
||||
int ret;
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
|
||||
AB8500_USBCH_CTRL1_REG,
|
||||
&usbch_ctrl1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usb_enabled = usbch_ctrl1 & USB_CH_ENA;
|
||||
if (usb_enabled) {
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_USBCH_CTRL1_REG,
|
||||
USB_CH_ENA, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CHARGER_CTRL,
|
||||
DROP_COUNT_RESET,
|
||||
DROP_COUNT_RESET);
|
||||
|
||||
if (usb_enabled) {
|
||||
restore_ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_USBCH_CTRL1_REG,
|
||||
USB_CH_ENA,
|
||||
USB_CH_ENA);
|
||||
if (restore_ret < 0)
|
||||
return restore_ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
|
||||
* @work: pointer to the work_struct structure
|
||||
|
|
@ -2471,7 +2704,10 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
|
|||
static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
u8 link_status = 0;
|
||||
u8 reg_value;
|
||||
bool charger_not_ok;
|
||||
bool prev_collapse;
|
||||
bool prev_status;
|
||||
|
||||
struct ab8500_charger *di = container_of(work,
|
||||
|
|
@ -2484,19 +2720,61 @@ static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
|
|||
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
prev_status = di->flags.usbchargernotok;
|
||||
charger_not_ok = reg_value & VBUS_CH_NOK;
|
||||
if (is_ab8505(di->parent)) {
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
|
||||
AB8500_USB_LINK1_STAT_REG,
|
||||
&link_status);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s USB link status read failed\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (reg_value & VBUS_CH_NOK) {
|
||||
link_status = (link_status & AB8505_USB_LINK_STATUS) >>
|
||||
USB_LINK_STATUS_SHIFT;
|
||||
charger_not_ok |=
|
||||
link_status == AB8505_USB_STAT_CHARGER_NOT_OK;
|
||||
}
|
||||
|
||||
prev_status = di->flags.usbchargernotok;
|
||||
prev_collapse = di->flags.vbus_collapse;
|
||||
|
||||
if (is_ab8505(di->parent) &&
|
||||
link_status == AB8505_USB_STAT_CHARGER_NOT_OK &&
|
||||
di->usb_charger_not_ok_retries <
|
||||
AB8505_USB_CHARGER_NOT_OK_RETRIES) {
|
||||
di->usb_charger_not_ok_retries++;
|
||||
di->flags.usbchargernotok = false;
|
||||
di->flags.vbus_collapse = true;
|
||||
dev_warn(di->dev, "Retrying USB charger detection (%u/%u)\n",
|
||||
di->usb_charger_not_ok_retries,
|
||||
AB8505_USB_CHARGER_NOT_OK_RETRIES);
|
||||
|
||||
ret = ab8505_charger_retry_usb_detection(di);
|
||||
if (ret < 0)
|
||||
dev_err(di->dev, "USB charger retry failed: %d\n", ret);
|
||||
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->check_usbchgnotok_work, HZ);
|
||||
} else if (charger_not_ok) {
|
||||
di->flags.usbchargernotok = true;
|
||||
if (is_ab8505(di->parent) &&
|
||||
link_status == AB8505_USB_STAT_CHARGER_NOT_OK) {
|
||||
di->flags.vbus_collapse = false;
|
||||
ab8500_charger_set_usb_connected(di, false);
|
||||
}
|
||||
/* Check again in 1sec */
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->check_usbchgnotok_work, HZ);
|
||||
} else {
|
||||
di->flags.usbchargernotok = false;
|
||||
di->flags.vbus_collapse = false;
|
||||
di->usb_charger_not_ok_retries = 0;
|
||||
}
|
||||
|
||||
if (prev_status != di->flags.usbchargernotok)
|
||||
if (prev_status != di->flags.usbchargernotok ||
|
||||
prev_collapse != di->flags.vbus_collapse)
|
||||
ab8500_power_supply_changed(di, di->usb_chg.psy);
|
||||
}
|
||||
|
||||
|
|
@ -3063,7 +3341,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
|
|||
|
||||
ret = abx500_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
|
||||
CH_OP_CUR_LVL_1P6);
|
||||
CH_OP_CUR_LVL_1P5_MAX);
|
||||
if (ret) {
|
||||
dev_err(di->dev,
|
||||
"failed to set CH_OPT_CRNTLVL_MAX_REG\n");
|
||||
|
|
@ -3071,7 +3349,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
|
|||
}
|
||||
}
|
||||
|
||||
if (is_ab8505_2p0(di->parent))
|
||||
if (is_ab8505(di->parent))
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_USBCH_CTRL2_REG,
|
||||
|
|
@ -3340,6 +3618,10 @@ static int ab8500_charger_bind(struct device *dev)
|
|||
}
|
||||
|
||||
ch_stat = ab8500_charger_detect_chargers(di, false);
|
||||
if (ch_stat < 0) {
|
||||
destroy_workqueue(di->charger_wq);
|
||||
return ch_stat;
|
||||
}
|
||||
|
||||
if (ch_stat & AC_PW_CONN) {
|
||||
if (is_ab8500(di->parent))
|
||||
|
|
@ -3374,7 +3656,8 @@ static void ab8500_charger_unbind(struct device *dev)
|
|||
int ret;
|
||||
|
||||
/* Disable AC charging */
|
||||
ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
|
||||
if (di->ac_chg.enabled)
|
||||
ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
|
||||
|
||||
/* Disable USB charging */
|
||||
ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
|
||||
|
|
@ -3420,11 +3703,14 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
|
||||
di->bm = &ab8500_bm_data;
|
||||
|
||||
di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
|
||||
|
||||
/* get parent data */
|
||||
di->dev = dev;
|
||||
di->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
|
||||
if (di->autopower_cfg && is_ab8505(di->parent)) {
|
||||
dev_warn(dev, "autopower is not supported on AB8505\n");
|
||||
di->autopower_cfg = false;
|
||||
}
|
||||
|
||||
/* Get ADC channels */
|
||||
if (!is_ab8505(di->parent)) {
|
||||
|
|
@ -3595,7 +3881,13 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Request interrupts */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
|
||||
/* The first five interrupts belong to the AB8500 main charger. */
|
||||
if (is_ab8505(di->parent))
|
||||
i = 5;
|
||||
else
|
||||
i = 0;
|
||||
|
||||
for (; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
|
||||
irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
|
@ -3622,6 +3914,10 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
|
||||
/* Identify the connected charger types during startup */
|
||||
charger_status = ab8500_charger_detect_chargers(di, true);
|
||||
if (charger_status < 0) {
|
||||
ret = charger_status;
|
||||
goto remove_ab8500_bm;
|
||||
}
|
||||
if (charger_status & AC_PW_CONN) {
|
||||
di->ac.charger_connected = 1;
|
||||
di->ac_conn = true;
|
||||
|
|
|
|||
|
|
@ -451,6 +451,16 @@ static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
|
|||
avg->avg = sample;
|
||||
}
|
||||
|
||||
static u8 ab8500_fg_cc_config(struct ab8500_fg *di)
|
||||
{
|
||||
u8 config = CC_PWR_UP_ENA;
|
||||
|
||||
if (!is_ab8505(di->parent))
|
||||
config |= CC_DEEP_SLEEP_ENA;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_coulomb_counter() - enable coulomb counter
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
|
|
@ -481,7 +491,7 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
|
|||
/* Start the CC */
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8500_RTC_CC_CONF_REG,
|
||||
(CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
|
||||
ab8500_fg_cc_config(di));
|
||||
if (ret)
|
||||
goto cc_err;
|
||||
|
||||
|
|
@ -555,7 +565,7 @@ int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
|
|||
/* Start the CC */
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8500_RTC_CC_CONF_REG,
|
||||
(CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
|
||||
ab8500_fg_cc_config(di));
|
||||
if (ret)
|
||||
goto fail;
|
||||
} else {
|
||||
|
|
@ -934,9 +944,15 @@ static int ab8500_load_comp_fg_bat_voltage(struct ab8500_fg *di, bool always)
|
|||
int i = 0;
|
||||
int vbat_uv = 0;
|
||||
int rcomp;
|
||||
int ret;
|
||||
|
||||
/* Average the instant current to get a stable current measurement */
|
||||
ab8500_fg_inst_curr_start(di);
|
||||
ret = ab8500_fg_inst_curr_start(di);
|
||||
if (ret) {
|
||||
dev_err(di->dev, "failed to start instantaneous current measurement\n");
|
||||
di->vbat_uv = ab8500_fg_bat_voltage(di);
|
||||
return di->vbat_uv;
|
||||
}
|
||||
|
||||
do {
|
||||
vbat_uv += ab8500_fg_bat_voltage(di);
|
||||
|
|
@ -945,15 +961,14 @@ static int ab8500_load_comp_fg_bat_voltage(struct ab8500_fg *di, bool always)
|
|||
} while (!ab8500_fg_inst_curr_done(di) &&
|
||||
i <= WAIT_FOR_INST_CURRENT_MAX);
|
||||
|
||||
if (i > WAIT_FOR_INST_CURRENT_MAX) {
|
||||
ret = ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua);
|
||||
if (ret) {
|
||||
dev_err(di->dev,
|
||||
"TIMEOUT: return uncompensated measurement of VBAT\n");
|
||||
di->vbat_uv = vbat_uv / i;
|
||||
return di->vbat_uv;
|
||||
}
|
||||
|
||||
ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua);
|
||||
|
||||
/*
|
||||
* If there is too high current dissipation, the compensation cannot be
|
||||
* trusted so return an error unless we must return something here, as
|
||||
|
|
@ -1387,6 +1402,13 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
|||
}
|
||||
}
|
||||
|
||||
/* Keep the charge properties current between percentage changes. */
|
||||
if (di->bat_cap.prev_percent == percent &&
|
||||
di->bat_cap.prev_mah != di->bat_cap.mah) {
|
||||
di->bat_cap.prev_mah = di->bat_cap.mah;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (di->bm->capacity_scaling) {
|
||||
di->bat_cap.cap_scale.scaled_cap =
|
||||
|
|
@ -2205,42 +2227,36 @@ static int ab8500_fg_get_ext_psy_data(struct power_supply *ext, void *data)
|
|||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
switch (ext->desc->type) {
|
||||
case POWER_SUPPLY_TYPE_BATTERY:
|
||||
switch (ret.intval) {
|
||||
case POWER_SUPPLY_STATUS_UNKNOWN:
|
||||
case POWER_SUPPLY_STATUS_DISCHARGING:
|
||||
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||||
if (!di->flags.charging)
|
||||
break;
|
||||
di->flags.charging = false;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
switch (ret.intval) {
|
||||
case POWER_SUPPLY_STATUS_UNKNOWN:
|
||||
case POWER_SUPPLY_STATUS_DISCHARGING:
|
||||
case POWER_SUPPLY_STATUS_NOT_CHARGING:
|
||||
if (!di->flags.charging)
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_FULL:
|
||||
if (di->flags.fully_charged)
|
||||
break;
|
||||
di->flags.fully_charged = true;
|
||||
di->flags.force_full = true;
|
||||
/* Save current capacity as maximum */
|
||||
di->bat_cap.max_mah = di->bat_cap.mah;
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_CHARGING:
|
||||
if (di->flags.charging &&
|
||||
!di->flags.fully_charged)
|
||||
break;
|
||||
di->flags.charging = true;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
}
|
||||
di->flags.charging = false;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
default:
|
||||
case POWER_SUPPLY_STATUS_FULL:
|
||||
if (di->flags.fully_charged)
|
||||
break;
|
||||
di->flags.fully_charged = true;
|
||||
di->flags.force_full = true;
|
||||
/* Save current capacity as maximum */
|
||||
di->bat_cap.max_mah = di->bat_cap.mah;
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_CHARGING:
|
||||
if (di->flags.charging &&
|
||||
!di->flags.fully_charged)
|
||||
break;
|
||||
di->flags.charging = true;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -2354,14 +2370,6 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
|
||||
|
||||
|
|
@ -2568,56 +2576,6 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_to_psy(dev);
|
||||
struct ab8500_fg *di = power_supply_get_drvdata(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%d\n", (reg_value & 0x7F));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
int reg_value;
|
||||
struct power_supply *psy = dev_to_psy(dev);
|
||||
struct ab8500_fg *di = power_supply_get_drvdata(psy);
|
||||
|
||||
if (kstrtoint(buf, 10, ®_value))
|
||||
goto fail;
|
||||
|
||||
if (reg_value > 0x7F) {
|
||||
dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
|
||||
|
||||
fail:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -2814,30 +2772,6 @@ static ssize_t ab8505_powercut_write(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_flag_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
||||
int ret;
|
||||
u8 reg_value;
|
||||
struct power_supply *psy = dev_to_psy(dev);
|
||||
struct ab8500_fg *di = power_supply_get_drvdata(psy);
|
||||
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%d\n", ((reg_value & 0x10) >> 4));
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ab8505_powercut_debounce_read(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -2912,8 +2846,6 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
|
|||
}
|
||||
|
||||
static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
|
||||
__ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
|
||||
__ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
|
||||
__ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
|
|
@ -2923,7 +2855,6 @@ static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
|
|||
ab8505_powercut_restart_counter_read, NULL),
|
||||
__ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_read, ab8505_powercut_write),
|
||||
__ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
|
||||
__ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
|
||||
__ATTR(powercut_enable_status, S_IRUGO,
|
||||
|
|
|
|||
|
|
@ -326,9 +326,6 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
|||
const struct axp_data *axp_data;
|
||||
int i, irq, ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
return -ENODEV;
|
||||
|
||||
if (!axp20x) {
|
||||
dev_err(&pdev->dev, "Parent drvdata not set\n");
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -1097,9 +1097,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
return -ENODEV;
|
||||
|
||||
axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt),
|
||||
GFP_KERNEL);
|
||||
if (!axp20x_batt)
|
||||
|
|
|
|||
|
|
@ -918,73 +918,68 @@ static void axp20x_usb_power_parse_dt(struct device *dev,
|
|||
|
||||
static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(dev->parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct axp20x_usb_power *power;
|
||||
const struct axp_data *axp_data;
|
||||
int i, irq, ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
return -ENODEV;
|
||||
if (!axp20x)
|
||||
return dev_err_probe(dev, -EINVAL, "Parent drvdata not set\n");
|
||||
|
||||
if (!axp20x) {
|
||||
dev_err(&pdev->dev, "Parent drvdata not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
axp_data = of_device_get_match_data(dev);
|
||||
|
||||
axp_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
power = devm_kzalloc(&pdev->dev,
|
||||
struct_size(power, irqs, axp_data->num_irq_names),
|
||||
power = devm_kzalloc(dev, struct_size(power, irqs, axp_data->num_irq_names),
|
||||
GFP_KERNEL);
|
||||
if (!power)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, power);
|
||||
|
||||
power->dev = &pdev->dev;
|
||||
power->dev = dev;
|
||||
power->axp_data = axp_data;
|
||||
power->regmap = axp20x->regmap;
|
||||
power->num_irqs = axp_data->num_irq_names;
|
||||
|
||||
power->curr_lim_fld = devm_regmap_field_alloc(&pdev->dev, power->regmap,
|
||||
power->curr_lim_fld = devm_regmap_field_alloc(dev, power->regmap,
|
||||
axp_data->curr_lim_fld);
|
||||
if (IS_ERR(power->curr_lim_fld))
|
||||
return PTR_ERR(power->curr_lim_fld);
|
||||
|
||||
axp20x_usb_power_parse_dt(&pdev->dev, power);
|
||||
axp20x_usb_power_parse_dt(dev, power);
|
||||
|
||||
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
|
||||
ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
|
||||
axp_data->vbus_valid_bit,
|
||||
&power->vbus_valid_bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
|
||||
ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
|
||||
axp_data->vbus_mon_bit,
|
||||
&power->vbus_mon_bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
|
||||
ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
|
||||
axp_data->usb_bc_en_bit,
|
||||
&power->usb_bc_en_bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
|
||||
ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
|
||||
axp_data->usb_bc_det_fld,
|
||||
&power->usb_bc_det_fld);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
|
||||
ret = axp20x_regmap_field_alloc_optional(dev, power->regmap,
|
||||
axp_data->vbus_disable_bit,
|
||||
&power->vbus_disable_bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
|
||||
ret = devm_delayed_work_autocancel(dev, &power->vbus_detect,
|
||||
axp_data->axp20x_read_vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1001,7 +996,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
|||
ret = axp_data->axp20x_cfg_adc_reg(power);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to initialize for %s usage\n",
|
||||
IS_ENABLED(CONFIG_AXP20X_ADC) ? "iio" : "adc");
|
||||
}
|
||||
|
||||
if (power->usb_bc_en_bit) {
|
||||
|
|
@ -1011,26 +1008,29 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
psy_cfg.fwnode = dev_fwnode(&pdev->dev);
|
||||
psy_cfg.fwnode = dev_fwnode(dev);
|
||||
psy_cfg.drv_data = power;
|
||||
|
||||
power->supply = devm_power_supply_register(&pdev->dev,
|
||||
power->supply = devm_power_supply_register(dev,
|
||||
axp_data->power_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(power->supply))
|
||||
return PTR_ERR(power->supply);
|
||||
return dev_err_probe(dev, PTR_ERR(power->supply),
|
||||
"Failed to register power supply\n");
|
||||
|
||||
/* Request irqs after registering, as irqs may trigger immediately */
|
||||
for (i = 0; i < axp_data->num_irq_names; i++) {
|
||||
irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
|
||||
if (irq < 0)
|
||||
/* platform_get_irq_byname() already produced an error message */
|
||||
return irq;
|
||||
|
||||
power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
|
||||
ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
|
||||
ret = devm_request_any_context_irq(dev, power->irqs[i],
|
||||
axp20x_usb_power_irq, 0,
|
||||
DRVNAME, power);
|
||||
if (ret < 0)
|
||||
/* devm_request_any_context_irq() already produced an error message */
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/power/bq24190_charger.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
|
@ -1294,6 +1295,58 @@ static int bq24190_charger_set_iinlimit(struct bq24190_dev_info *bdi,
|
|||
ARRAY_SIZE(bq24190_isc_iinlim_values), val->intval);
|
||||
}
|
||||
|
||||
static int bq24190_charger_get_batfet_ctrl(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
u8 regval;
|
||||
int ret;
|
||||
|
||||
ret = bq24190_read_mask(bdi, BQ24190_REG_MOC,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_MASK,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT,
|
||||
®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Datasheet defines shipping mode as BATFET off _and_ the watchdog
|
||||
* disabled. Since the watchdog is disabled during the whole lifetime
|
||||
* (from probe), shipping mode is the only "off" state that can be
|
||||
* achievable.
|
||||
*/
|
||||
val->intval = regval ? POWER_SUPPLY_LOAD_SWITCH_SHIP :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bq24190_charger_set_batfet_ctrl(struct bq24190_dev_info *bdi,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u8 regval;
|
||||
|
||||
/*
|
||||
* Datasheet defines shipping mode as BATFET off _and_ the watchdog
|
||||
* disabled. Since the watchdog is disabled during the whole lifetime
|
||||
* (from probe), shipping mode is the only "off" state that can be
|
||||
* achievable.
|
||||
*/
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_SHIP:
|
||||
regval = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bq24190_write_mask(bdi, BQ24190_REG_MOC,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_MASK,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, regval);
|
||||
}
|
||||
|
||||
static int bq24190_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp, union power_supply_propval *val)
|
||||
{
|
||||
|
|
@ -1350,6 +1403,9 @@ static int bq24190_charger_get_property(struct power_supply *psy,
|
|||
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
||||
ret = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
ret = bq24190_charger_get_batfet_ctrl(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = bdi->model_name;
|
||||
ret = 0;
|
||||
|
|
@ -1400,6 +1456,9 @@ static int bq24190_charger_set_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = bq24190_charger_set_iinlimit(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
ret = bq24190_charger_set_batfet_ctrl(bdi, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
|
@ -1420,6 +1479,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -1480,6 +1540,7 @@ static enum power_supply_property bq24190_charger_properties[] = {
|
|||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
};
|
||||
|
||||
static char *bq24190_charger_supplied_to[] = {
|
||||
|
|
@ -1498,6 +1559,8 @@ static const struct power_supply_desc bq24190_charger_desc = {
|
|||
.charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_NONE) |
|
||||
BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) |
|
||||
BIT(POWER_SUPPLY_CHARGE_TYPE_FAST),
|
||||
.load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_SHIP),
|
||||
};
|
||||
|
||||
/* Battery power supply property routines */
|
||||
|
|
@ -2168,7 +2231,13 @@ static int bq24190_probe(struct i2c_client *client)
|
|||
if (ret < 0)
|
||||
goto out_charger;
|
||||
|
||||
enable_irq_wake(client->irq);
|
||||
ret = devm_device_init_wakeup(dev);
|
||||
if (ret < 0)
|
||||
goto out_charger;
|
||||
|
||||
ret = devm_pm_set_wake_irq(dev, client->irq);
|
||||
if (ret < 0)
|
||||
goto out_charger;
|
||||
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
|
|
@ -2242,25 +2311,6 @@ static __maybe_unused int bq24190_runtime_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static __maybe_unused int bq24190_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
error = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (error < 0)
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
|
||||
bq24190_register_reset(bdi);
|
||||
|
||||
if (error >= 0) {
|
||||
pm_runtime_put_autosuspend(bdi->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __maybe_unused int bq24190_pm_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
|
@ -2274,7 +2324,14 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
|
|||
if (error < 0)
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
|
||||
bq24190_register_reset(bdi);
|
||||
/*
|
||||
* The chip kept its configuration through the sleep: it is in host
|
||||
* mode with the i2c watchdog off, so nothing expired and nothing was
|
||||
* reset. Do not reset it here either -- a userspace setting such as
|
||||
* EN_HIZ or IINLIM would otherwise be silently lost on every resume.
|
||||
* Re-applying the probe-time configuration is idempotent and cheap,
|
||||
* and covers a part that did somehow fall back to default mode.
|
||||
*/
|
||||
bq24190_set_config(bdi);
|
||||
bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
|
||||
|
||||
|
|
@ -2293,7 +2350,7 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
|
|||
static const struct dev_pm_ops bq24190_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(bq24190_runtime_suspend, bq24190_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bq24190_pm_suspend, bq24190_pm_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, bq24190_pm_resume)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id bq24190_i2c_ids[] = {
|
||||
|
|
|
|||
|
|
@ -295,6 +295,40 @@ static int bq24257_set_input_current_limit(struct bq24257_device *bq,
|
|||
BQ24257_IILIMIT_MAP_SIZE));
|
||||
}
|
||||
|
||||
static int bq24257_get_sys_switch(struct bq24257_device *bq,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bq24257_field_read(bq, F_SYSOFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = ret ? POWER_SUPPLY_LOAD_SWITCH_OFF :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq24257_set_sys_switch(struct bq24257_device *bq,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u8 regval;
|
||||
|
||||
switch (val->intval) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_OFF:
|
||||
regval = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bq24257_field_write(bq, F_SYSOFF, regval);
|
||||
}
|
||||
|
||||
static int bq24257_power_supply_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
|
@ -381,7 +415,8 @@ static int bq24257_power_supply_get_property(struct power_supply *psy,
|
|||
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return bq24257_get_input_current_limit(bq, val);
|
||||
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return bq24257_get_sys_switch(bq, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -398,6 +433,8 @@ static int bq24257_power_supply_set_property(struct power_supply *psy,
|
|||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return bq24257_set_input_current_limit(bq, val);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return bq24257_set_sys_switch(bq, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -408,6 +445,7 @@ static int bq24257_power_supply_property_is_writeable(struct power_supply *psy,
|
|||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -740,6 +778,7 @@ static enum power_supply_property bq24257_power_supply_props[] = {
|
|||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
};
|
||||
|
||||
static char *bq24257_charger_supplied_to[] = {
|
||||
|
|
@ -749,6 +788,8 @@ static char *bq24257_charger_supplied_to[] = {
|
|||
static const struct power_supply_desc bq24257_power_supply_desc = {
|
||||
.name = "bq24257-charger",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_OFF),
|
||||
.properties = bq24257_power_supply_props,
|
||||
.num_properties = ARRAY_SIZE(bq24257_power_supply_props),
|
||||
.get_property = bq24257_power_supply_get_property,
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x)
|
|||
BQ2515X_ICHG_DIVISOR);
|
||||
}
|
||||
|
||||
static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
|
||||
static int bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
|
||||
{
|
||||
int ret;
|
||||
int ce_pin;
|
||||
|
|
@ -428,6 +428,8 @@ static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
|
|||
int charger_disable;
|
||||
|
||||
ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio);
|
||||
if (ce_pin < 0)
|
||||
return ce_pin;
|
||||
|
||||
ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2);
|
||||
if (ret)
|
||||
|
|
@ -435,10 +437,7 @@ static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
|
|||
|
||||
charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE;
|
||||
|
||||
if (charger_disable || ce_pin)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return charger_disable || ce_pin;
|
||||
}
|
||||
|
||||
static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val)
|
||||
|
|
@ -615,7 +614,11 @@ static int bq2515x_charging_status(struct bq2515x_device *bq2515x,
|
|||
else
|
||||
status1_no_fault = false;
|
||||
|
||||
ce_status = (!bq2515x_get_charge_disable(bq2515x));
|
||||
ret = bq2515x_get_charge_disable(bq2515x);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ce_status = !ret;
|
||||
|
||||
/*
|
||||
* If there are no faults and charging is enabled, then status is
|
||||
|
|
|
|||
|
|
@ -112,6 +112,12 @@
|
|||
#define BQ25630_ITERM_MAX 1000000
|
||||
#define BQ25630_ITERM_STEP 10000
|
||||
|
||||
/* BATFET control modes. */
|
||||
#define BQ25630_BATFET_CTRL_IDLE 0x00
|
||||
#define BQ25630_BATFET_CTRL_SHUTDOWN 0x01
|
||||
#define BQ25630_BATFET_CTRL_SHIP 0x02
|
||||
#define BQ25630_BATFET_CTRL_STANDBY 0x03
|
||||
|
||||
/* Charge types. */
|
||||
#define BQ25630_CHG_STAT_NOT_CHARGING 0x00
|
||||
#define BQ25630_CHG_STAT_TRICKLE_CHARGE 0x01
|
||||
|
|
@ -587,6 +593,71 @@ static int bq25630_read_vbus(struct bq25630_data *data, int *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bq25630_read_batfet_ctrl(struct bq25630_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(data->regfields[BQ25630_REGF_BATFET_CTRL],
|
||||
®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read BATFET control (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (regval) {
|
||||
case BQ25630_BATFET_CTRL_IDLE:
|
||||
*val = POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
break;
|
||||
case BQ25630_BATFET_CTRL_SHUTDOWN:
|
||||
*val = POWER_SUPPLY_LOAD_SWITCH_OFF;
|
||||
break;
|
||||
case BQ25630_BATFET_CTRL_SHIP:
|
||||
*val = POWER_SUPPLY_LOAD_SWITCH_SHIP;
|
||||
break;
|
||||
case BQ25630_BATFET_CTRL_STANDBY:
|
||||
*val = POWER_SUPPLY_LOAD_SWITCH_STANDBY;
|
||||
break;
|
||||
default:
|
||||
*val = POWER_SUPPLY_LOAD_SWITCH_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25630_write_batfet_ctrl(struct bq25630_data *data, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
switch (val) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = BQ25630_BATFET_CTRL_IDLE;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_OFF:
|
||||
regval = BQ25630_BATFET_CTRL_SHUTDOWN;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_SHIP:
|
||||
regval = BQ25630_BATFET_CTRL_SHIP;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_STANDBY:
|
||||
regval = BQ25630_BATFET_CTRL_STANDBY;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_field_write(data->regfields[BQ25630_REGF_BATFET_CTRL],
|
||||
regval);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write BATFET control (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25630_get_status(struct bq25630_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
|
@ -845,6 +916,9 @@ static int bq25630_charger_get_property(struct power_supply *psy,
|
|||
BQ25630_ITERM_MIN_REGVAL,
|
||||
&val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
ret = bq25630_read_batfet_ctrl(data, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = "BQ25630";
|
||||
break;
|
||||
|
|
@ -913,6 +987,9 @@ static int bq25630_charger_set_property(struct power_supply *psy,
|
|||
BQ25630_ITERM_MIN_REGVAL,
|
||||
val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
ret = bq25630_write_batfet_ctrl(data, val->intval);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -932,6 +1009,7 @@ static int bq25630_charger_property_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -954,6 +1032,7 @@ static const enum power_supply_property bq25630_charger_properties[] = {
|
|||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
|
@ -972,6 +1051,11 @@ static const struct power_supply_desc bq25630_charger_psy_desc = {
|
|||
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_CDP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_C),
|
||||
.load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_UNKNOWN) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_OFF) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_STANDBY) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_SHIP),
|
||||
.properties = bq25630_charger_properties,
|
||||
.num_properties = ARRAY_SIZE(bq25630_charger_properties),
|
||||
.get_property = bq25630_charger_get_property,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/extcon-provider.h>
|
||||
|
||||
#define BQ256XX_MANUFACTURER "Texas Instruments"
|
||||
|
||||
|
|
@ -209,6 +210,7 @@ enum bq256xx_id {
|
|||
* @client: i2c client structure
|
||||
* @regmap: register map structure
|
||||
* @dev: device structure
|
||||
* @edev: extcon device registered to report vbus state changes
|
||||
* @charger: power supply registered for the charger
|
||||
* @battery: power supply registered for the battery
|
||||
* @lock: mutex lock structure
|
||||
|
|
@ -229,6 +231,7 @@ enum bq256xx_id {
|
|||
struct bq256xx_device {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct extcon_dev *edev;
|
||||
struct power_supply *charger;
|
||||
struct power_supply *battery;
|
||||
struct mutex lock;
|
||||
|
|
@ -1164,8 +1167,14 @@ static int bq256xx_get_charger_property(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool bq256xx_state_changed(struct bq256xx_device *bq,
|
||||
struct bq256xx_state *new_state)
|
||||
enum bq256xx_state_change {
|
||||
CHANGED_NONE,
|
||||
CHANGED_VBUS,
|
||||
CHANGED_OTHER
|
||||
};
|
||||
|
||||
static enum bq256xx_state_change bq256xx_state_changed(struct bq256xx_device *bq,
|
||||
struct bq256xx_state *new_state)
|
||||
{
|
||||
struct bq256xx_state old_state;
|
||||
|
||||
|
|
@ -1173,27 +1182,92 @@ static bool bq256xx_state_changed(struct bq256xx_device *bq,
|
|||
old_state = bq->state;
|
||||
mutex_unlock(&bq->lock);
|
||||
|
||||
return memcmp(&old_state, new_state, sizeof(struct bq256xx_state)) != 0;
|
||||
if (new_state->online != old_state.online)
|
||||
return CHANGED_VBUS;
|
||||
|
||||
if (memcmp(&old_state, new_state, sizeof(struct bq256xx_state)) != 0)
|
||||
return CHANGED_OTHER;
|
||||
|
||||
return CHANGED_NONE;
|
||||
}
|
||||
|
||||
static const unsigned int bq256xx_usb_extcon_cable[] = {
|
||||
EXTCON_USB,
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static void bq256xx_extcon_update(struct bq256xx_device *bq, bool online)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!bq->edev)
|
||||
return;
|
||||
|
||||
ret = extcon_set_state_sync(bq->edev, EXTCON_USB, online);
|
||||
if (ret)
|
||||
dev_err(bq->dev, "Failed to update extcon state: %d\n", ret);
|
||||
}
|
||||
|
||||
static int bq256xx_extcon_init(struct bq256xx_device *bq)
|
||||
{
|
||||
unsigned int charger_status_0;
|
||||
int ret;
|
||||
|
||||
if (!IS_REACHABLE(CONFIG_EXTCON)) {
|
||||
dev_dbg(bq->dev, "Extcon support is disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bq->edev = devm_extcon_dev_allocate(bq->dev, bq256xx_usb_extcon_cable);
|
||||
if (IS_ERR(bq->edev)) {
|
||||
dev_err(bq->dev, "Failed to allocate extcon device\n");
|
||||
return PTR_ERR(bq->edev);
|
||||
}
|
||||
|
||||
ret = devm_extcon_dev_register(bq->dev, bq->edev);
|
||||
if (ret < 0) {
|
||||
dev_err(bq->dev, "Failed to register extcon device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_0,
|
||||
&charger_status_0);
|
||||
if (ret) {
|
||||
dev_err(bq->dev, "Failed to read charger status\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bq256xx_extcon_update(bq, !!(charger_status_0 & BQ256XX_PG_STAT_MASK));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct bq256xx_device *bq = private;
|
||||
struct bq256xx_state state;
|
||||
enum bq256xx_state_change changed;
|
||||
int ret;
|
||||
|
||||
ret = bq256xx_get_state(bq, &state);
|
||||
if (ret < 0)
|
||||
goto irq_out;
|
||||
|
||||
if (!bq256xx_state_changed(bq, &state))
|
||||
changed = bq256xx_state_changed(bq, &state);
|
||||
switch (changed) {
|
||||
case CHANGED_NONE:
|
||||
goto irq_out;
|
||||
|
||||
mutex_lock(&bq->lock);
|
||||
bq->state = state;
|
||||
mutex_unlock(&bq->lock);
|
||||
case CHANGED_VBUS:
|
||||
bq256xx_extcon_update(bq, state.online);
|
||||
fallthrough;
|
||||
case CHANGED_OTHER:
|
||||
mutex_lock(&bq->lock);
|
||||
bq->state = state;
|
||||
mutex_unlock(&bq->lock);
|
||||
|
||||
power_supply_changed(bq->charger);
|
||||
power_supply_changed(bq->charger);
|
||||
}
|
||||
|
||||
irq_out:
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -1743,6 +1817,12 @@ static int bq256xx_probe(struct i2c_client *client)
|
|||
if (!IS_ERR_OR_NULL(bq->usb3_phy))
|
||||
usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
|
||||
|
||||
ret = bq256xx_extcon_init(bq);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register extcon device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
bq256xx_irq_handler_thread,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ struct bq257xx_chip_info {
|
|||
* @chip: hw specific functions
|
||||
* @bq: parent MFD device
|
||||
* @charger: power supply device
|
||||
* @dev: charger platform device
|
||||
* @online: charger input is present
|
||||
* @charging: charger is actively charging the battery
|
||||
* @fast_charge: charger is in fast charge mode
|
||||
|
|
@ -73,6 +74,7 @@ struct bq257xx_chg {
|
|||
const struct bq257xx_chip_info *chip;
|
||||
struct bq257xx_device *bq;
|
||||
struct power_supply *charger;
|
||||
struct device *dev;
|
||||
bool online;
|
||||
bool charging;
|
||||
bool fast_charge;
|
||||
|
|
@ -1055,7 +1057,7 @@ static void bq257xx_external_power_changed(struct power_supply *psy)
|
|||
|
||||
pdata->chip->bq257xx_get_state(pdata);
|
||||
|
||||
pdata->supplied = power_supply_am_i_supplied(pdata->charger);
|
||||
pdata->supplied = power_supply_am_i_supplied(psy);
|
||||
if (pdata->supplied < 0)
|
||||
return;
|
||||
|
||||
|
|
@ -1112,6 +1114,54 @@ static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_charger_init() - Initialization for charger power supply device
|
||||
* @psy: power supply device
|
||||
*
|
||||
* Set device parameters during registration, before the power supply becomes
|
||||
* accessible to the broader system.
|
||||
*
|
||||
* Return: Returns 0 on success or error code on failure.
|
||||
*/
|
||||
static int bq257xx_charger_init(struct power_supply *psy)
|
||||
{
|
||||
struct bq257xx_chg *pdata = power_supply_get_drvdata(psy);
|
||||
struct power_supply_battery_info *bat_info;
|
||||
struct device *dev = pdata->dev;
|
||||
int ret;
|
||||
|
||||
ret = power_supply_get_battery_info(psy, &bat_info);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Unable to get battery info\n");
|
||||
|
||||
if ((bat_info->voltage_min_design_uv <= 0) ||
|
||||
(bat_info->constant_charge_voltage_max_uv <= 0) ||
|
||||
(bat_info->constant_charge_current_max_ua <= 0)) {
|
||||
power_supply_put_battery_info(psy, bat_info);
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Required bat info missing or invalid\n");
|
||||
}
|
||||
|
||||
pdata->vsys_min = bat_info->voltage_min_design_uv;
|
||||
pdata->vbat_max = bat_info->constant_charge_voltage_max_uv;
|
||||
pdata->ichg_max = bat_info->constant_charge_current_max_ua;
|
||||
|
||||
power_supply_put_battery_info(psy, bat_info);
|
||||
|
||||
ret = device_property_read_u32(dev,
|
||||
"input-current-limit-microamp",
|
||||
&pdata->iindpm_max);
|
||||
if (ret)
|
||||
pdata->iindpm_max = pdata->chip->default_iindpm_uA;
|
||||
|
||||
ret = pdata->chip->bq257xx_hw_init(pdata);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot initialize the charger\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc bq257xx_power_supply_desc = {
|
||||
.name = "bq257xx-charger",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
|
|
@ -1126,6 +1176,7 @@ static const struct power_supply_desc bq257xx_power_supply_desc = {
|
|||
.set_property = bq257xx_set_charger_property,
|
||||
.property_is_writeable = bq257xx_property_is_writeable,
|
||||
.external_power_changed = bq257xx_external_power_changed,
|
||||
.init = bq257xx_charger_init,
|
||||
};
|
||||
|
||||
static const struct bq257xx_chip_info bq25703_chip_info = {
|
||||
|
|
@ -1160,58 +1211,12 @@ static const struct bq257xx_chip_info bq25792_chip_info = {
|
|||
.bq257xx_get_min_vsys = &bq25792_get_min_vsys,
|
||||
};
|
||||
|
||||
/**
|
||||
* bq257xx_parse_dt() - Parse the device tree for required properties
|
||||
* @pdata: driver platform data
|
||||
* @psy_cfg: power supply config data
|
||||
* @dev: device struct
|
||||
*
|
||||
* Read the device tree to identify the minimum system voltage, the
|
||||
* maximum charge current, the maximum charge voltage, and the maximum
|
||||
* input current.
|
||||
*
|
||||
* Return: Returns 0 on success or error code on error.
|
||||
*/
|
||||
static int bq257xx_parse_dt(struct bq257xx_chg *pdata,
|
||||
struct power_supply_config *psy_cfg, struct device *dev)
|
||||
{
|
||||
struct power_supply_battery_info *bat_info;
|
||||
int ret;
|
||||
|
||||
ret = power_supply_get_battery_info(pdata->charger,
|
||||
&bat_info);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Unable to get battery info\n");
|
||||
|
||||
if ((bat_info->voltage_min_design_uv <= 0) ||
|
||||
(bat_info->constant_charge_voltage_max_uv <= 0) ||
|
||||
(bat_info->constant_charge_current_max_ua <= 0))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Required bat info missing or invalid\n");
|
||||
|
||||
pdata->vsys_min = bat_info->voltage_min_design_uv;
|
||||
pdata->vbat_max = bat_info->constant_charge_voltage_max_uv;
|
||||
pdata->ichg_max = bat_info->constant_charge_current_max_ua;
|
||||
|
||||
power_supply_put_battery_info(pdata->charger, bat_info);
|
||||
|
||||
ret = device_property_read_u32(dev,
|
||||
"input-current-limit-microamp",
|
||||
&pdata->iindpm_max);
|
||||
if (ret)
|
||||
pdata->iindpm_max = pdata->chip->default_iindpm_uA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bq257xx_charger_probe() - Probe routine for charger platform device
|
||||
* @pdev: platform device
|
||||
*
|
||||
* Probe the charger device, allocate driver data structure, select the
|
||||
* appropriate chip-specific function pointers, register the power supply,
|
||||
* parse device tree properties for battery limits, initialize hardware,
|
||||
* and set up the interrupt handler if available.
|
||||
*
|
||||
* Return: Returns 0 on success or error code on failure.
|
||||
|
|
@ -1222,7 +1227,6 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
|
|||
struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);
|
||||
struct bq257xx_chg *pdata;
|
||||
struct power_supply_config psy_cfg = { };
|
||||
int ret;
|
||||
|
||||
device_set_of_node_from_dev(dev, pdev->dev.parent);
|
||||
|
||||
|
|
@ -1231,6 +1235,7 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pdata->bq = bq;
|
||||
pdata->dev = dev;
|
||||
|
||||
switch (bq->type) {
|
||||
case BQ25703A:
|
||||
|
|
@ -1255,26 +1260,16 @@ static int bq257xx_charger_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(dev, PTR_ERR(pdata->charger),
|
||||
"Power supply register charger failed\n");
|
||||
|
||||
ret = bq257xx_parse_dt(pdata, &psy_cfg, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pdata->chip->bq257xx_hw_init(pdata);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot initialize the charger\n");
|
||||
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
if (bq->client->irq) {
|
||||
ret = devm_request_threaded_irq(dev, bq->client->irq, NULL,
|
||||
bq257xx_irq_handler_thread,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&bq->client->dev), pdata);
|
||||
return devm_request_threaded_irq(dev, bq->client->irq, NULL,
|
||||
bq257xx_irq_handler_thread,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&bq->client->dev), pdata);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1590,7 +1590,7 @@ static int bq25890_suspend(struct device *dev)
|
|||
struct bq25890_device *bq = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* If charger is removed, while in suspend, make sure ADC is diabled
|
||||
* If charger is removed, while in suspend, make sure ADC is disabled
|
||||
* since it consumes slightly more power.
|
||||
*/
|
||||
return bq25890_field_write(bq, F_CONV_RATE, 0);
|
||||
|
|
|
|||
|
|
@ -37,12 +37,15 @@
|
|||
* https://www.ti.com/product/bq27441-g1
|
||||
* https://www.ti.com/product/bq27621-g1
|
||||
* https://www.ti.com/product/bq27z561
|
||||
* https://www.ti.com/product/bq27z746
|
||||
* https://www.ti.com/product/bq28z610
|
||||
* https://www.ti.com/product/bq28z620
|
||||
* https://www.ti.com/product/bq34z100-g1
|
||||
* https://www.ti.com/product/bq78z100
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/param.h>
|
||||
|
|
@ -501,6 +504,7 @@ static u8
|
|||
[BQ27XXX_REG_AP] = 0x22,
|
||||
BQ27XXX_DM_REG_ROWS,
|
||||
},
|
||||
#define bq27z746_regs bq27z561_regs
|
||||
bq28z610_regs[BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
|
|
@ -522,6 +526,7 @@ static u8
|
|||
[BQ27XXX_REG_AP] = 0x22,
|
||||
BQ27XXX_DM_REG_ROWS,
|
||||
},
|
||||
#define bq28z620_regs bq28z610_regs
|
||||
bq34z100_regs[BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x0c,
|
||||
|
|
@ -836,6 +841,8 @@ static enum power_supply_property bq27z561_props[] = {
|
|||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
#define bq27z746_props bq27z561_props
|
||||
|
||||
static enum power_supply_property bq28z610_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
|
|
@ -856,6 +863,8 @@ static enum power_supply_property bq28z610_props[] = {
|
|||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
#define bq28z620_props bq28z610_props
|
||||
|
||||
static enum power_supply_property bq34z100_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
|
|
@ -994,7 +1003,9 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
|
|||
#endif
|
||||
|
||||
#define bq27z561_dm_regs NULL
|
||||
#define bq27z746_dm_regs NULL
|
||||
#define bq28z610_dm_regs NULL
|
||||
#define bq28z620_dm_regs NULL
|
||||
#define bq34z100_dm_regs NULL
|
||||
#define bq78z100_dm_regs NULL
|
||||
|
||||
|
|
@ -1052,7 +1063,9 @@ static struct {
|
|||
[BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
|
||||
[BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
|
||||
[BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS),
|
||||
[BQ27Z746] = BQ27XXX_DATA(bq27z746, 0 , BQ27Z561_O_BITS),
|
||||
[BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS),
|
||||
[BQ28Z620] = BQ27XXX_DATA(bq28z620, 0 , BQ27Z561_O_BITS),
|
||||
[BQ34Z100] = BQ27XXX_DATA(bq34z100, 0 , BQ27XXX_O_OTDC | BQ27XXX_O_SOC_SI | \
|
||||
BQ27XXX_O_HAS_CI | BQ27XXX_O_MUL_CHEM),
|
||||
[BQ78Z100] = BQ27XXX_DATA(bq78z100, 0 , BQ27Z561_O_BITS),
|
||||
|
|
@ -2234,7 +2247,6 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|||
};
|
||||
int ret;
|
||||
|
||||
INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
|
||||
ret = devm_mutex_init(di->dev, &di->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2255,6 +2267,12 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|||
psy_desc->get_property = bq27xxx_battery_get_property;
|
||||
psy_desc->external_power_changed = bq27xxx_external_power_changed;
|
||||
|
||||
/* Cancel the poll work after the power_supply is unregistered. */
|
||||
ret = devm_delayed_work_autocancel(di->dev, &di->work,
|
||||
bq27xxx_battery_poll);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
di->bat = devm_power_supply_register(di->dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(di->bat))
|
||||
return dev_err_probe(di->dev, PTR_ERR(di->bat),
|
||||
|
|
@ -2281,8 +2299,6 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
|
|||
mutex_lock(&di->lock);
|
||||
di->removed = true;
|
||||
mutex_unlock(&di->lock);
|
||||
|
||||
cancel_delayed_work_sync(&di->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
|
||||
|
||||
|
|
@ -2291,7 +2307,7 @@ static int bq27xxx_battery_suspend(struct device *dev)
|
|||
{
|
||||
struct bq27xxx_device_info *di = dev_get_drvdata(dev);
|
||||
|
||||
cancel_delayed_work(&di->work);
|
||||
cancel_delayed_work_sync(&di->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -253,7 +253,9 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
|||
{ .name = "bq27441", .driver_data = BQ27441 },
|
||||
{ .name = "bq27621", .driver_data = BQ27621 },
|
||||
{ .name = "bq27z561", .driver_data = BQ27Z561 },
|
||||
{ .name = "bq27z746", .driver_data = BQ27Z746 },
|
||||
{ .name = "bq28z610", .driver_data = BQ28Z610 },
|
||||
{ .name = "bq28z620", .driver_data = BQ28Z620 },
|
||||
{ .name = "bq34z100", .driver_data = BQ34Z100 },
|
||||
{ .name = "bq78z100", .driver_data = BQ78Z100 },
|
||||
{ }
|
||||
|
|
@ -290,7 +292,9 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
|
|||
{ .compatible = "ti,bq27441" },
|
||||
{ .compatible = "ti,bq27621" },
|
||||
{ .compatible = "ti,bq27z561" },
|
||||
{ .compatible = "ti,bq27z746" },
|
||||
{ .compatible = "ti,bq28z610" },
|
||||
{ .compatible = "ti,bq28z620" },
|
||||
{ .compatible = "ti,bq34z100" },
|
||||
{ .compatible = "ti,bq78z100" },
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -904,7 +904,7 @@ static bool cm_setup_timer(void)
|
|||
}
|
||||
|
||||
/**
|
||||
* charger_extcon_work - enable/diable charger according to the state
|
||||
* charger_extcon_work - enable/disable charger according to the state
|
||||
* of charger cable
|
||||
*
|
||||
* @work: work_struct of the function charger_extcon_work.
|
||||
|
|
|
|||
|
|
@ -808,8 +808,8 @@ static void cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata *ddata)
|
|||
ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode",
|
||||
i, GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ddata->gpio[i])) {
|
||||
dev_info(ddata->dev, "no mode change GPIO%i: %li\n",
|
||||
i, PTR_ERR(ddata->gpio[i]));
|
||||
dev_info(ddata->dev, "no mode change GPIO%i: %pe\n",
|
||||
i, ddata->gpio[i]);
|
||||
ddata->gpio[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/platform_data/cros_ec_commands.h>
|
||||
#include <linux/platform_data/cros_ec_proto.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define EC_CHARGE_CONTROL_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \
|
||||
|
|
@ -21,12 +22,22 @@
|
|||
|
||||
/*
|
||||
* Semantics of data *returned* from the EC API and Linux sysfs differ
|
||||
* slightly, also the v1 API can not return any data.
|
||||
* To match the expected sysfs API, data is never read back from the EC but
|
||||
* cached in the driver.
|
||||
* slightly:
|
||||
* - EC sustainer off is lower=upper=-1
|
||||
* - Linux "no limit" is start=0, end=100
|
||||
* Also the v1 API can not return any data.
|
||||
*
|
||||
* Changes to the EC bypassing the driver will not be reflected in sysfs.
|
||||
* Any change to "charge_behaviour" will synchronize the EC with the driver state.
|
||||
* While the sustainer is active the EC may report IDLE or DISCHARGE as the
|
||||
* current charge-control mode; that is an internal hold/discharge step, not a
|
||||
* Linux inhibit-charge / force-discharge request. Valid sustainer limits are
|
||||
* therefore adopted as AUTO with the reported thresholds.
|
||||
*
|
||||
* Sysfs reads come from a driver-side cache. On probe, command versions that
|
||||
* support GET (v2+) are initialized from the EC so firmware or firmware-setup
|
||||
* programmed limits and modes are preserved (unlike earlier behaviour that
|
||||
* always forced AUTO with no limits). v1 still forces a well-known EC state.
|
||||
* Subsequent sysfs writes keep the cache and EC in sync; changes that bypass
|
||||
* the driver are not reflected until the next probe.
|
||||
*/
|
||||
|
||||
struct cros_chctl_priv {
|
||||
|
|
@ -44,18 +55,23 @@ struct cros_chctl_priv {
|
|||
};
|
||||
|
||||
static int cros_chctl_send_charge_control_cmd(struct cros_ec_device *cros_ec,
|
||||
u8 cmd_version, struct ec_params_charge_control *req)
|
||||
u8 cmd_version,
|
||||
struct ec_params_charge_control *req,
|
||||
struct ec_response_charge_control *resp)
|
||||
{
|
||||
int ret;
|
||||
static const u8 outsizes[] = {
|
||||
[1] = offsetof(struct ec_params_charge_control, cmd),
|
||||
[2] = sizeof(struct ec_params_charge_control),
|
||||
[3] = sizeof(struct ec_params_charge_control),
|
||||
};
|
||||
size_t insize = resp ? sizeof(*resp) : 0;
|
||||
int ret;
|
||||
|
||||
if (resp)
|
||||
*resp = (struct ec_response_charge_control){};
|
||||
|
||||
ret = cros_ec_cmd(cros_ec, cmd_version, EC_CMD_CHARGE_CONTROL, req,
|
||||
outsizes[cmd_version], NULL, 0);
|
||||
|
||||
outsizes[cmd_version], resp, insize);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -94,7 +110,120 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
|
|||
req.sustain_soc.upper = -1;
|
||||
}
|
||||
|
||||
return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version, &req);
|
||||
return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version,
|
||||
&req, NULL);
|
||||
}
|
||||
|
||||
static int cros_chctl_get_ec_status(struct cros_chctl_priv *priv,
|
||||
struct ec_response_charge_control *resp)
|
||||
{
|
||||
struct ec_params_charge_control req = {
|
||||
.cmd = EC_CHARGE_CONTROL_CMD_GET,
|
||||
};
|
||||
|
||||
return cros_chctl_send_charge_control_cmd(priv->cros_ec, priv->cmd_version,
|
||||
&req, resp);
|
||||
}
|
||||
|
||||
static void cros_chctl_set_default_state(struct cros_chctl_priv *priv)
|
||||
{
|
||||
lockdep_assert_held(&priv->lock);
|
||||
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
||||
priv->current_start_threshold = 0;
|
||||
priv->current_end_threshold = 100;
|
||||
}
|
||||
|
||||
static bool cros_chctl_sustainer_limits_valid(s8 lower, s8 upper)
|
||||
{
|
||||
return lower >= 0 && upper >= 0 && lower <= 100 && upper <= 100 && lower <= upper;
|
||||
}
|
||||
|
||||
static int cros_chctl_adopt_ec_mode(struct cros_chctl_priv *priv, u32 mode)
|
||||
{
|
||||
lockdep_assert_held(&priv->lock);
|
||||
|
||||
switch (mode) {
|
||||
case CHARGE_CONTROL_NORMAL:
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
||||
return 0;
|
||||
case CHARGE_CONTROL_IDLE:
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
|
||||
return 0;
|
||||
case CHARGE_CONTROL_DISCHARGE:
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
|
||||
return 0;
|
||||
default:
|
||||
dev_warn(priv->dev, "unknown charge control mode %u\n", mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cros_chctl_init_state(struct cros_chctl_priv *priv)
|
||||
{
|
||||
struct ec_response_charge_control resp;
|
||||
s8 lower, upper;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&priv->lock);
|
||||
|
||||
/* v1 cannot report current state; force a well-known EC configuration. */
|
||||
if (priv->cmd_version < 2) {
|
||||
cros_chctl_set_default_state(priv);
|
||||
return cros_chctl_configure_ec(priv);
|
||||
}
|
||||
|
||||
ret = cros_chctl_get_ec_status(priv, &resp);
|
||||
if (ret < 0) {
|
||||
dev_warn(priv->dev,
|
||||
"failed to read EC charge state (%pe), applying defaults\n",
|
||||
ERR_PTR(ret));
|
||||
goto defaults;
|
||||
}
|
||||
|
||||
lower = resp.sustain_soc.lower;
|
||||
upper = resp.sustain_soc.upper;
|
||||
|
||||
/*
|
||||
* Valid sustainer limits mean "auto with thresholds". The EC mode may
|
||||
* be IDLE/DISCHARGE while the sustainer holds or bleeds SoC; do not
|
||||
* expose that as inhibit-charge / force-discharge.
|
||||
*/
|
||||
if (cros_chctl_sustainer_limits_valid(lower, upper)) {
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
||||
priv->current_start_threshold = lower;
|
||||
priv->current_end_threshold = upper;
|
||||
} else {
|
||||
ret = cros_chctl_adopt_ec_mode(priv, resp.mode);
|
||||
if (ret < 0)
|
||||
goto defaults;
|
||||
|
||||
/*
|
||||
* Sustainer off is lower=upper=-1 → Linux "no limit" (0/100).
|
||||
* Any other non-valid pair is unexpected; remap, warn, and
|
||||
* push the cleaned state back to the EC.
|
||||
*/
|
||||
priv->current_start_threshold = 0;
|
||||
priv->current_end_threshold = 100;
|
||||
|
||||
if (!(lower == -1 && upper == -1)) {
|
||||
dev_warn(priv->dev,
|
||||
"invalid EC sustainer limits (%d/%d), treating as no limit\n",
|
||||
lower, upper);
|
||||
return cros_chctl_configure_ec(priv);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(priv->dev,
|
||||
"adopted EC charge state: behaviour=%d start=%u end=%u (ec mode=%u)\n",
|
||||
priv->current_behaviour, priv->current_start_threshold,
|
||||
priv->current_end_threshold, resp.mode);
|
||||
|
||||
return 0;
|
||||
|
||||
defaults:
|
||||
cros_chctl_set_default_state(priv);
|
||||
return cros_chctl_configure_ec(priv);
|
||||
}
|
||||
|
||||
static int cros_chctl_psy_ext_get_prop(struct power_supply *psy,
|
||||
|
|
@ -152,7 +281,6 @@ static int cros_chctl_psy_ext_set_threshold(struct cros_chctl_priv *priv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int cros_chctl_psy_ext_set_prop(struct power_supply *psy,
|
||||
const struct power_supply_ext *ext,
|
||||
void *data,
|
||||
|
|
@ -293,6 +421,7 @@ static int cros_chctl_probe(struct platform_device *pdev)
|
|||
|
||||
priv->dev = dev;
|
||||
priv->cros_ec = cros_ec;
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
if (priv->cmd_version == 1)
|
||||
priv->psy_ext = &cros_chctl_psy_ext_v1;
|
||||
|
|
@ -305,19 +434,35 @@ static int cros_chctl_probe(struct platform_device *pdev)
|
|||
priv->battery_hook.add_battery = cros_chctl_add_battery;
|
||||
priv->battery_hook.remove_battery = cros_chctl_remove_battery;
|
||||
|
||||
priv->current_behaviour = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
||||
priv->current_start_threshold = 0;
|
||||
priv->current_end_threshold = 100;
|
||||
|
||||
/* Bring EC into well-known state */
|
||||
scoped_guard(mutex, &priv->lock)
|
||||
ret = cros_chctl_configure_ec(priv);
|
||||
ret = cros_chctl_init_state(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_battery_hook_register(dev, &priv->battery_hook);
|
||||
}
|
||||
|
||||
static int cros_chctl_resume(struct device *dev)
|
||||
{
|
||||
struct cros_chctl_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The EC resets its charge control state when the AP powers off, so the
|
||||
* configuration is lost across hibernation, while the values exposed to
|
||||
* userspace still describe it. The cached state is the only remaining
|
||||
* record of that configuration: reprogram the EC from it rather than
|
||||
* re-reading the EC, which would adopt the reset defaults instead.
|
||||
*/
|
||||
guard(mutex)(&priv->lock);
|
||||
ret = cros_chctl_configure_ec(priv);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to restore charge control state: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(cros_chctl_pm_ops, NULL, cros_chctl_resume);
|
||||
|
||||
static const struct platform_device_id cros_chctl_id[] = {
|
||||
{ .name = "cros-charge-control" },
|
||||
{ }
|
||||
|
|
@ -325,7 +470,10 @@ static const struct platform_device_id cros_chctl_id[] = {
|
|||
MODULE_DEVICE_TABLE(platform, cros_chctl_id);
|
||||
|
||||
static struct platform_driver cros_chctl_driver = {
|
||||
.driver.name = "cros-charge-control",
|
||||
.driver = {
|
||||
.name = "cros-charge-control",
|
||||
.pm = pm_sleep_ptr(&cros_chctl_pm_ops),
|
||||
},
|
||||
.probe = cros_chctl_probe,
|
||||
.id_table = cros_chctl_id,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ static void da9030_charger_check_state(struct da9030_charger *charger)
|
|||
} else if (charger->adc.vbat_res >
|
||||
charger->thresholds.vbat_low) {
|
||||
/* we are charging and passed LOW_THRESH,
|
||||
so upate DA9030 VBAT threshold
|
||||
so update DA9030 VBAT threshold
|
||||
*/
|
||||
da903x_write(charger->master, DA9030_VBATMON,
|
||||
charger->thresholds.vbat_low);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ static void gab_work(struct work_struct *work)
|
|||
adc_bat = container_of(delayed_work, struct gab, bat_work);
|
||||
status = adc_bat->status;
|
||||
|
||||
if (!power_supply_am_i_supplied(adc_bat->psy))
|
||||
if (power_supply_am_i_supplied(adc_bat->psy) <= 0)
|
||||
adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else if (gab_charge_finished(adc_bat))
|
||||
adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
|
@ -243,6 +243,9 @@ static int gab_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(&pdev->dev, ret, "Failed to register delayed work\n");
|
||||
|
||||
adc_bat->charge_finished = devm_gpiod_get_optional(&pdev->dev, "charged", GPIOD_IN);
|
||||
if (IS_ERR(adc_bat->charge_finished))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(adc_bat->charge_finished),
|
||||
"failed to get charged GPIO\n");
|
||||
if (adc_bat->charge_finished) {
|
||||
int irq;
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,10 @@ static int gpio_charger_get_property(struct power_supply *psy,
|
|||
val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
if (gpiod_get_value_cansleep(gpio_charger->charge_status))
|
||||
/* Without input power the battery discharges, whatever the status pin says */
|
||||
if (gpio_charger->gpiod && !gpiod_get_value_cansleep(gpio_charger->gpiod))
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else if (gpiod_get_value_cansleep(gpio_charger->charge_status))
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ static int dc_ti_battery_probe(struct platform_device *pdev)
|
|||
|
||||
chip->vbat_channel = devm_iio_channel_get(dev, "VBAT");
|
||||
if (IS_ERR(chip->vbat_channel)) {
|
||||
dev_dbg(dev, "devm_iio_channel_get() ret %ld\n", PTR_ERR(chip->vbat_channel));
|
||||
dev_dbg(dev, "devm_iio_channel_get() ret %pe\n", chip->vbat_channel);
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for VBAT IIO channel\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -464,9 +464,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client)
|
|||
/* r_sense can be negative, when sense+ is connected to the battery
|
||||
* instead of the sense-. This results in reversed measurements. */
|
||||
ret = of_property_read_s32(np, "lltc,resistor-sense", &r_sense);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
of_node_put(np);
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Could not find lltc,resistor-sense in devicetree\n");
|
||||
}
|
||||
info->r_sense = r_sense;
|
||||
|
||||
ret = of_property_read_u32(np, "lltc,prescaler-exponent",
|
||||
|
|
@ -477,6 +479,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client)
|
|||
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
if (info->id == LTC2943_ID) {
|
||||
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
|
||||
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
|
||||
|
|
|
|||
|
|
@ -755,6 +755,41 @@ static int ltc4162l_set_term_current(struct ltc4162l_info *info,
|
|||
BIT(2), BIT(2));
|
||||
}
|
||||
|
||||
static int ltc4162l_get_ship_mode(struct ltc4162l_info *info,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->regmap, LTC4162L_ARM_SHIP_MODE, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = regval == LTC4162L_ARM_SHIP_MODE_MAGIC ?
|
||||
POWER_SUPPLY_LOAD_SWITCH_SHIP :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc4162l_set_ship_mode(struct ltc4162l_info *info, int value)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
switch (value) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_SHIP:
|
||||
regval = LTC4162L_ARM_SHIP_MODE_MAGIC;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_write(info->regmap, LTC4162L_ARM_SHIP_MODE, regval);
|
||||
}
|
||||
|
||||
/* Custom properties */
|
||||
static const char * const ltc4162l_charge_status_name[] = {
|
||||
"ilim_reg_active", /* 32 */
|
||||
|
|
@ -890,15 +925,15 @@ static ssize_t arm_ship_mode_show(struct device *dev,
|
|||
{
|
||||
struct power_supply *psy = to_power_supply(dev);
|
||||
struct ltc4162l_info *info = power_supply_get_drvdata(psy);
|
||||
unsigned int regval;
|
||||
union power_supply_propval val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->regmap, LTC4162L_ARM_SHIP_MODE, ®val);
|
||||
if (ret)
|
||||
ret = ltc4162l_get_ship_mode(info, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
regval == LTC4162L_ARM_SHIP_MODE_MAGIC ? 1 : 0);
|
||||
val.intval == POWER_SUPPLY_LOAD_SWITCH_SHIP ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t arm_ship_mode_store(struct device *dev,
|
||||
|
|
@ -915,8 +950,9 @@ static ssize_t arm_ship_mode_store(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(info->regmap, LTC4162L_ARM_SHIP_MODE,
|
||||
value ? LTC4162L_ARM_SHIP_MODE_MAGIC : 0);
|
||||
ret = ltc4162l_set_ship_mode(info,
|
||||
value ? POWER_SUPPLY_LOAD_SWITCH_SHIP :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -981,6 +1017,8 @@ static int ltc4162l_get_property(struct power_supply *psy,
|
|||
return chip_info->get_die_temp(info, val);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return ltc4162l_get_term_current(info, val);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return ltc4162l_get_ship_mode(info, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1003,6 +1041,8 @@ static int ltc4162l_set_property(struct power_supply *psy,
|
|||
return ltc4162l_set_iin_limit(info, val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return ltc4162l_set_term_current(info, val->intval);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return ltc4162l_set_ship_mode(info, val->intval);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1016,6 +1056,7 @@ static int ltc4162l_property_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -1037,10 +1078,13 @@ static enum power_supply_property ltc4162l_properties[] = {
|
|||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc ltc4162l_desc = {
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_SHIP),
|
||||
.properties = ltc4162l_properties,
|
||||
.num_properties = ARRAY_SIZE(ltc4162l_properties),
|
||||
.get_property = ltc4162l_get_property,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
|
@ -57,10 +58,11 @@
|
|||
|
||||
#define MAX17042_CURRENT_LSB 1562500ll /* 1.5625µV/Rsense */
|
||||
#define MAX17042_CAPACITY_LSB 5000000ll /* 5.0µVH/Rsense */
|
||||
#define MAX17042_TIME_LSB 5625 / 1000 /* s */
|
||||
#define MAX17042_VOLTAGE_LSB 625 / 8 /* µV */
|
||||
#define MAX17042_RESISTANCE_LSB 1 / 4096 /* Ω */
|
||||
#define MAX17042_TEMPERATURE_LSB 1 / 256 /* °C */
|
||||
|
||||
#define MAX17042_TIME_LSB(val) mult_frac(val, 5625, 1000) /* s */
|
||||
#define MAX17042_VOLTAGE_LSB(val) mult_frac(val, 625, 8) /* µV */
|
||||
#define MAX17042_RESISTANCE_LSB(val) mult_frac(val, 1, 4096) /* Ω */
|
||||
#define MAX17042_TEMPERATURE_LSB(val) mult_frac(val, 1, 256) /* °C */
|
||||
|
||||
#define MAX17055_DQACC_DIV 32
|
||||
#define MAX17055_DPACC_FACTOR 44138
|
||||
|
|
@ -138,7 +140,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
|
|||
|
||||
*temp = sign_extend32(data, 15);
|
||||
/* The value is converted into deci-centigrade scale */
|
||||
*temp = *temp * 10 * MAX17042_TEMPERATURE_LSB;
|
||||
*temp = MAX17042_TEMPERATURE_LSB(*temp * 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +216,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
|
|||
goto health_error;
|
||||
|
||||
/* bits [0-3] unused */
|
||||
vavg = val * MAX17042_VOLTAGE_LSB;
|
||||
vavg = MAX17042_VOLTAGE_LSB(val);
|
||||
/* Convert to millivolts */
|
||||
vavg /= 1000;
|
||||
|
||||
|
|
@ -223,7 +225,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
|
|||
goto health_error;
|
||||
|
||||
/* bits [0-3] unused */
|
||||
vbatt = val * MAX17042_VOLTAGE_LSB;
|
||||
vbatt = MAX17042_VOLTAGE_LSB(val);
|
||||
/* Convert to millivolts */
|
||||
vbatt /= 1000;
|
||||
|
||||
|
|
@ -330,21 +332,21 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data * MAX17042_VOLTAGE_LSB;
|
||||
val->intval = MAX17042_VOLTAGE_LSB(data);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
ret = regmap_read(map, MAX17042_AvgVCELL, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data * MAX17042_VOLTAGE_LSB;
|
||||
val->intval = MAX17042_VOLTAGE_LSB(data);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
ret = regmap_read(map, MAX17042_OCVInternal, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data * MAX17042_VOLTAGE_LSB;
|
||||
val->intval = MAX17042_VOLTAGE_LSB(data);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
if (chip->enable_current_sense)
|
||||
|
|
@ -473,7 +475,7 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (data == U16_MAX)
|
||||
return -ENODATA;
|
||||
|
||||
val->intval = data * MAX17042_TIME_LSB;
|
||||
val->intval = MAX17042_TIME_LSB(data);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
|
||||
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 &&
|
||||
|
|
@ -488,7 +490,7 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (data == U16_MAX)
|
||||
return -ENODATA;
|
||||
|
||||
val->intval = data * MAX17042_TIME_LSB;
|
||||
val->intval = MAX17042_TIME_LSB(data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -1080,7 +1082,7 @@ static int max17042_init_chip(struct max17042_chip *chip)
|
|||
/* update capacity params */
|
||||
max17042_update_capacity_regs(chip);
|
||||
|
||||
/* delay must be atleast 350mS to allow VFSOC
|
||||
/* delay must be at least 350mS to allow VFSOC
|
||||
* to be calculated from the new configuration
|
||||
*/
|
||||
msleep(350);
|
||||
|
|
|
|||
|
|
@ -293,6 +293,7 @@ static const struct of_device_id mm8013_match_table[] = {
|
|||
{ .compatible = "mitsumi,mm8013" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mm8013_match_table);
|
||||
|
||||
static struct i2c_driver mm8013_i2c_driver = {
|
||||
.probe = mm8013_probe,
|
||||
|
|
|
|||
|
|
@ -566,6 +566,22 @@ static int pf1550_charger_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, chg);
|
||||
|
||||
psy_cfg.drv_data = chg;
|
||||
|
||||
chg->charger = devm_power_supply_register(&pdev->dev,
|
||||
&pf1550_charger_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(chg->charger))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
|
||||
"failed: power supply register\n");
|
||||
|
||||
chg->battery = devm_power_supply_register(&pdev->dev,
|
||||
&pf1550_battery_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(chg->battery))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
|
||||
"failed: power supply register\n");
|
||||
|
||||
ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work,
|
||||
pf1550_chg_vbus_work);
|
||||
if (ret)
|
||||
|
|
@ -584,22 +600,6 @@ static int pf1550_charger_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(chg->dev, ret,
|
||||
"failed to add battery sense work\n");
|
||||
|
||||
psy_cfg.drv_data = chg;
|
||||
|
||||
chg->charger = devm_power_supply_register(&pdev->dev,
|
||||
&pf1550_charger_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(chg->charger))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
|
||||
"failed: power supply register\n");
|
||||
|
||||
chg->battery = devm_power_supply_register(&pdev->dev,
|
||||
&pf1550_battery_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(chg->battery))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
|
||||
"failed: power supply register\n");
|
||||
|
||||
for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) {
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq < 0)
|
||||
|
|
|
|||
|
|
@ -190,7 +190,35 @@ static void power_supply_deferred_register_work(struct work_struct *work)
|
|||
device_unlock(psy->dev.parent);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int power_supply_check_supplies_by_name(struct power_supply *psy)
|
||||
{
|
||||
struct device *parent = psy->dev.parent;
|
||||
int nval, ret;
|
||||
|
||||
if (!parent)
|
||||
return 0;
|
||||
|
||||
nval = device_property_string_array_count(parent, "supplied-from");
|
||||
if (nval <= 0)
|
||||
return 0;
|
||||
|
||||
psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
|
||||
sizeof(*psy->supplied_from),
|
||||
GFP_KERNEL);
|
||||
if (!psy->supplied_from)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_string_array(parent, "supplied-from",
|
||||
(const char **)psy->supplied_from,
|
||||
nval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
psy->num_supplies = nval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __power_supply_populate_supplied_from(struct power_supply *epsy,
|
||||
void *data)
|
||||
{
|
||||
|
|
@ -262,22 +290,21 @@ static int power_supply_find_supply_from_fwnode(struct fwnode_handle *supply_nod
|
|||
static int power_supply_check_supplies(struct power_supply *psy)
|
||||
{
|
||||
struct fwnode_handle *np;
|
||||
int cnt = 0;
|
||||
int cnt, ret;
|
||||
|
||||
/* If there is already a list honor it */
|
||||
if (psy->supplied_from && psy->num_supplies > 0)
|
||||
return 0;
|
||||
|
||||
/* No device node found, nothing to do */
|
||||
if (!psy->dev.fwnode)
|
||||
return 0;
|
||||
for (cnt = 0; psy->dev.fwnode; cnt++) {
|
||||
np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt);
|
||||
if (IS_ERR(np)) {
|
||||
ret = PTR_ERR(np);
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
int ret;
|
||||
|
||||
np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt++);
|
||||
if (IS_ERR(np))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = power_supply_find_supply_from_fwnode(np);
|
||||
fwnode_handle_put(np);
|
||||
|
|
@ -286,48 +313,21 @@ static int power_supply_check_supplies(struct power_supply *psy)
|
|||
dev_dbg(&psy->dev, "Failed to find supply!\n");
|
||||
return ret;
|
||||
}
|
||||
} while (!IS_ERR(np));
|
||||
}
|
||||
|
||||
/* Missing valid "power-supplies" entries */
|
||||
if (cnt == 1)
|
||||
return 0;
|
||||
/* Fall back to the name-based property if no references were specified. */
|
||||
if (!cnt)
|
||||
return power_supply_check_supplies_by_name(psy);
|
||||
|
||||
/* All supplies found, allocate char * array for filling */
|
||||
psy->supplied_from = devm_kcalloc(&psy->dev,
|
||||
cnt - 1, sizeof(*psy->supplied_from),
|
||||
cnt, sizeof(*psy->supplied_from),
|
||||
GFP_KERNEL);
|
||||
if (!psy->supplied_from)
|
||||
return -ENOMEM;
|
||||
|
||||
return power_supply_populate_supplied_from(psy);
|
||||
}
|
||||
#else
|
||||
static int power_supply_check_supplies(struct power_supply *psy)
|
||||
{
|
||||
int nval, ret;
|
||||
|
||||
if (!psy->dev.parent)
|
||||
return 0;
|
||||
|
||||
nval = device_property_string_array_count(psy->dev.parent, "supplied-from");
|
||||
if (nval <= 0)
|
||||
return 0;
|
||||
|
||||
psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
|
||||
sizeof(char *), GFP_KERNEL);
|
||||
if (!psy->supplied_from)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = device_property_read_string_array(psy->dev.parent,
|
||||
"supplied-from", (const char **)psy->supplied_from, nval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
psy->num_supplies = nval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct psy_am_i_supplied_data {
|
||||
struct power_supply *psy;
|
||||
|
|
@ -907,7 +907,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
|||
u32 *propdata __free(kfree) = kzalloc_objs(*propdata, proplen);
|
||||
if (!propdata) {
|
||||
power_supply_put_battery_info(psy, info);
|
||||
err = -EINVAL;
|
||||
err = -ENOMEM;
|
||||
goto out_put_node;
|
||||
}
|
||||
err = fwnode_property_read_u32_array(fwnode, propname, propdata, proplen);
|
||||
|
|
@ -1422,6 +1422,12 @@ static int __power_supply_get_property(struct power_supply *psy, enum power_supp
|
|||
int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
/* Protect driver data from being free'd */
|
||||
guard(rwsem_read)(&psy->epc_sem);
|
||||
|
||||
if (psy->removing)
|
||||
return -ENODEV;
|
||||
|
||||
return __power_supply_get_property(psy, psp, val, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_get_property);
|
||||
|
|
@ -1442,7 +1448,7 @@ EXPORT_SYMBOL_GPL(power_supply_get_property);
|
|||
int power_supply_get_property_direct(struct power_supply *psy, enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
return __power_supply_get_property(psy, psp, val, false);
|
||||
return __power_supply_get_property(psy, psp, val, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
|
||||
|
||||
|
|
@ -1479,6 +1485,12 @@ static int __power_supply_set_property(struct power_supply *psy, enum power_supp
|
|||
int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
/* Protect driver data from being free'd */
|
||||
guard(rwsem_read)(&psy->epc_sem);
|
||||
|
||||
if (psy->removing)
|
||||
return -ENODEV;
|
||||
|
||||
return __power_supply_set_property(psy, psp, val, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(power_supply_set_property);
|
||||
|
|
@ -1608,6 +1620,16 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
|
|||
|
||||
sysfs_hwmon_failed:
|
||||
power_supply_sysfs_remove_extension(psy, ext);
|
||||
list_del(®->list_head);
|
||||
kfree(reg);
|
||||
/*
|
||||
* update_sysfs_and_hwmon() may have already torn down hwmon before
|
||||
* failing to recreate it. Recreate without the failed extension.
|
||||
*/
|
||||
if (power_supply_add_hwmon_sysfs(psy))
|
||||
dev_warn(&psy->dev, "failed to restore hwmon after extension error\n");
|
||||
return ret;
|
||||
|
||||
sysfs_add_failed:
|
||||
list_del(®->list_head);
|
||||
kfree(reg);
|
||||
|
|
|
|||
|
|
@ -152,6 +152,14 @@ static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = {
|
|||
[POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge",
|
||||
};
|
||||
|
||||
static const char *const POWER_SUPPLY_LOAD_SWITCH_TEXT[] = {
|
||||
[POWER_SUPPLY_LOAD_SWITCH_UNKNOWN] = "Unknown",
|
||||
[POWER_SUPPLY_LOAD_SWITCH_ON] = "On",
|
||||
[POWER_SUPPLY_LOAD_SWITCH_OFF] = "Off",
|
||||
[POWER_SUPPLY_LOAD_SWITCH_STANDBY] = "Standby",
|
||||
[POWER_SUPPLY_LOAD_SWITCH_SHIP] = "Ship",
|
||||
};
|
||||
|
||||
static struct power_supply_attr power_supply_attrs[] __ro_after_init = {
|
||||
/* Properties of type `int' */
|
||||
POWER_SUPPLY_ENUM_ATTR(STATUS),
|
||||
|
|
@ -231,6 +239,7 @@ static struct power_supply_attr power_supply_attrs[] __ro_after_init = {
|
|||
POWER_SUPPLY_ATTR(MANUFACTURE_DAY),
|
||||
POWER_SUPPLY_ATTR(INTERNAL_RESISTANCE),
|
||||
POWER_SUPPLY_ATTR(STATE_OF_HEALTH),
|
||||
POWER_SUPPLY_ENUM_ATTR(LOAD_SWITCH),
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_ATTR(MODEL_NAME),
|
||||
POWER_SUPPLY_ATTR(MANUFACTURER),
|
||||
|
|
@ -301,7 +310,8 @@ static ssize_t power_supply_show_enum_with_available(
|
|||
}
|
||||
|
||||
if (!match) {
|
||||
dev_warn(dev, "driver reporting unavailable enum value %d\n", value);
|
||||
dev_warn_ratelimited(dev, "driver reporting unavailable enum value %d\n",
|
||||
value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -400,6 +410,14 @@ static ssize_t power_supply_format_property(struct device *dev,
|
|||
ret = power_supply_show_charge_types(dev, psy,
|
||||
value.intval, buf);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
if (uevent) /* no possible values in uevents */
|
||||
goto default_format;
|
||||
ret = power_supply_show_enum_with_available(
|
||||
dev, POWER_SUPPLY_LOAD_SWITCH_TEXT,
|
||||
ARRAY_SIZE(POWER_SUPPLY_LOAD_SWITCH_TEXT),
|
||||
psy->desc->load_switches, value.intval, buf);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||
ret = sysfs_emit(buf, "%s\n", value.strval);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -830,6 +830,7 @@ static const enum power_supply_property x1e80100_bat_props[] = {
|
|||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
|
|
@ -975,7 +976,7 @@ static const u8 sm8350_usb_prop_map[] = {
|
|||
[POWER_SUPPLY_PROP_CURRENT_NOW] = USB_CURR_NOW,
|
||||
[POWER_SUPPLY_PROP_CURRENT_MAX] = USB_CURR_MAX,
|
||||
[POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT] = USB_INPUT_CURR_LIMIT,
|
||||
[POWER_SUPPLY_PROP_USB_TYPE] = USB_TYPE,
|
||||
[POWER_SUPPLY_PROP_USB_TYPE] = USB_ADAP_TYPE,
|
||||
};
|
||||
|
||||
static int qcom_battmgr_usb_sm8350_update(struct qcom_battmgr *battmgr,
|
||||
|
|
@ -1254,11 +1255,29 @@ static unsigned int qcom_battmgr_sc8280xp_convert_temp(unsigned int temperature)
|
|||
return DIV_ROUND_CLOSEST(temperature, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* The charger supplies' online state comes from the battery status response,
|
||||
* and the firmware sends no notification of its own when it changes, so
|
||||
* announce the change here. A supply that is not registered yet announces its
|
||||
* state when it is.
|
||||
*/
|
||||
static void qcom_battmgr_sc8280xp_set_online(struct power_supply *psy,
|
||||
bool *online, bool value)
|
||||
{
|
||||
if (*online == value)
|
||||
return;
|
||||
|
||||
*online = value;
|
||||
if (psy)
|
||||
power_supply_changed(psy);
|
||||
}
|
||||
|
||||
static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
|
||||
const struct qcom_battmgr_message *resp,
|
||||
size_t len)
|
||||
{
|
||||
unsigned int opcode = le32_to_cpu(resp->hdr.opcode);
|
||||
unsigned int power_state;
|
||||
unsigned int source;
|
||||
unsigned int state;
|
||||
size_t payload_len = len - sizeof(struct pmic_glink_hdr);
|
||||
|
|
@ -1309,23 +1328,31 @@ static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
|
|||
return;
|
||||
}
|
||||
|
||||
state = le32_to_cpu(resp->status.battery_state);
|
||||
if (state & BIT(0))
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else if (state & BIT(1))
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
||||
battmgr->status.capacity = le32_to_cpu(resp->status.capacity) * 1000;
|
||||
battmgr->status.power_now = le32_to_cpu(resp->status.rate) * 1000;
|
||||
battmgr->status.voltage_now = le32_to_cpu(resp->status.battery_voltage) * 1000;
|
||||
battmgr->status.temperature = qcom_battmgr_sc8280xp_convert_temp(le32_to_cpu(resp->status.temperature));
|
||||
|
||||
state = le32_to_cpu(resp->status.battery_state);
|
||||
power_state = le32_to_cpu(resp->status.power_state);
|
||||
if (state & BIT(0))
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else if (state & BIT(1))
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else if ((power_state & BIT(0)) &&
|
||||
battmgr->info.last_full_capacity &&
|
||||
battmgr->status.capacity >= battmgr->info.last_full_capacity)
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
battmgr->status.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
|
||||
source = le32_to_cpu(resp->status.charging_source);
|
||||
battmgr->ac.online = source == BATTMGR_CHARGING_SOURCE_AC;
|
||||
battmgr->usb.online = source == BATTMGR_CHARGING_SOURCE_USB;
|
||||
battmgr->wireless.online = source == BATTMGR_CHARGING_SOURCE_WIRELESS;
|
||||
qcom_battmgr_sc8280xp_set_online(battmgr->ac_psy, &battmgr->ac.online,
|
||||
source == BATTMGR_CHARGING_SOURCE_AC);
|
||||
qcom_battmgr_sc8280xp_set_online(battmgr->usb_psy, &battmgr->usb.online,
|
||||
source == BATTMGR_CHARGING_SOURCE_USB);
|
||||
qcom_battmgr_sc8280xp_set_online(battmgr->wls_psy, &battmgr->wireless.online,
|
||||
source == BATTMGR_CHARGING_SOURCE_WIRELESS);
|
||||
if (battmgr->info.last_full_capacity != 0) {
|
||||
/*
|
||||
* 100 * battmgr->status.capacity can overflow a 32bit
|
||||
|
|
@ -1513,7 +1540,7 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
|
|||
case USB_INPUT_CURR_LIMIT:
|
||||
battmgr->usb.current_limit = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
case USB_TYPE:
|
||||
case USB_ADAP_TYPE:
|
||||
battmgr->usb.usb_type = le32_to_cpu(resp->intval.value);
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -352,6 +352,9 @@
|
|||
|
||||
/* pmi8998 registers represent current in increments of 1/40th of an amp */
|
||||
#define CURRENT_SCALE_FACTOR 25000
|
||||
#define SMB2_FLOAT_VOLTAGE_MIN_UV 3487500
|
||||
#define SMB2_FLOAT_VOLTAGE_MAX_UV 4920000
|
||||
#define SMB2_FLOAT_VOLTAGE_STEP_UV 7500
|
||||
/* clang-format on */
|
||||
|
||||
enum charger_status {
|
||||
|
|
@ -378,6 +381,7 @@ struct smb_init_register {
|
|||
* @base: Base address for smb registers
|
||||
* @regmap: Register map
|
||||
* @batt_info: Battery data from DT
|
||||
* @initial_charge_enable: Charging enable state before hardware setup
|
||||
* @status_change_work: Worker to handle plug/unplug events
|
||||
* @cable_irq: USB plugin IRQ
|
||||
* @wakeup_enabled: If the cable IRQ will cause a wakeup
|
||||
|
|
@ -391,6 +395,7 @@ struct smb_chip {
|
|||
unsigned int base;
|
||||
struct regmap *regmap;
|
||||
struct power_supply_battery_info *batt_info;
|
||||
u8 initial_charge_enable;
|
||||
|
||||
struct delayed_work status_change_work;
|
||||
int cable_irq;
|
||||
|
|
@ -613,7 +618,7 @@ static int smb_get_iio_chan(struct smb_chip *chip, struct iio_channel *chan,
|
|||
}
|
||||
|
||||
if (IS_ERR(chan)) {
|
||||
dev_err(chip->dev, "Failed to chan, err = %li", PTR_ERR(chan));
|
||||
dev_err(chip->dev, "Failed to chan, err = %pe", chan);
|
||||
return PTR_ERR(chan);
|
||||
}
|
||||
|
||||
|
|
@ -632,26 +637,20 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val)
|
|||
return rc;
|
||||
}
|
||||
|
||||
switch (stat) {
|
||||
case CHARGER_ERROR_STATUS_BAT_OV_BIT:
|
||||
if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT)
|
||||
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
return 0;
|
||||
case BAT_TEMP_STATUS_TOO_COLD_BIT:
|
||||
else if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT)
|
||||
*val = POWER_SUPPLY_HEALTH_COLD;
|
||||
return 0;
|
||||
case BAT_TEMP_STATUS_TOO_HOT_BIT:
|
||||
else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT)
|
||||
*val = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
return 0;
|
||||
case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT:
|
||||
else if (stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT)
|
||||
*val = POWER_SUPPLY_HEALTH_COOL;
|
||||
return 0;
|
||||
case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT:
|
||||
else if (stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT)
|
||||
*val = POWER_SUPPLY_HEALTH_WARM;
|
||||
return 0;
|
||||
default:
|
||||
else
|
||||
*val = POWER_SUPPLY_HEALTH_GOOD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_get_property(struct power_supply *psy,
|
||||
|
|
@ -723,16 +722,22 @@ static irqreturn_t smb_handle_batt_overvoltage(int irq, void *data)
|
|||
{
|
||||
struct smb_chip *chip = data;
|
||||
unsigned int status;
|
||||
int rc;
|
||||
|
||||
regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2,
|
||||
&status);
|
||||
rc = regmap_read(chip->regmap,
|
||||
chip->base + BATTERY_CHARGER_STATUS_2, &status);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev, "Couldn't read charger status: %d\n", rc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
|
||||
/* The hardware stops charging automatically */
|
||||
dev_err(chip->dev, "battery overvoltage detected\n");
|
||||
power_supply_changed(chip->chg_psy);
|
||||
}
|
||||
|
||||
power_supply_changed(chip->chg_psy);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -764,7 +769,7 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data)
|
|||
|
||||
power_supply_changed(chip->chg_psy);
|
||||
|
||||
rc = regmap_write(chip->regmap, BARK_BITE_WDOG_PET,
|
||||
rc = regmap_write(chip->regmap, chip->base + BARK_BITE_WDOG_PET,
|
||||
BARK_BITE_WDOG_PET_BIT);
|
||||
if (rc < 0)
|
||||
dev_err(chip->dev, "Couldn't pet the dog rc=%d\n", rc);
|
||||
|
|
@ -814,11 +819,7 @@ static const struct smb_init_register smb_init_seq[] = {
|
|||
.mask = SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT |
|
||||
VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT,
|
||||
.val = VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT },
|
||||
/* Enable charging */
|
||||
{ .addr = USBIN_OPTIONS_1_CFG, .mask = HVDCP_EN_BIT, .val = 0 },
|
||||
{ .addr = CHARGING_ENABLE_CMD,
|
||||
.mask = CHARGING_ENABLE_CMD_BIT,
|
||||
.val = CHARGING_ENABLE_CMD_BIT },
|
||||
/*
|
||||
* Match downstream defaults
|
||||
* CHG_EN_SRC_BIT - charger enable is controlled by software
|
||||
|
|
@ -839,6 +840,13 @@ static const struct smb_init_register smb_init_seq[] = {
|
|||
AUTO_RECHG_BIT | EN_ANALOG_DROP_IN_VBATT_BIT |
|
||||
CHARGER_INHIBIT_BIT,
|
||||
.val = CHARGER_INHIBIT_BIT },
|
||||
/*
|
||||
* Set the enable command only once CHGR_CFG2 has selected software
|
||||
* control, or a charger its watchdog has disabled stays disabled.
|
||||
*/
|
||||
{ .addr = CHARGING_ENABLE_CMD,
|
||||
.mask = CHARGING_ENABLE_CMD_BIT,
|
||||
.val = CHARGING_ENABLE_CMD_BIT },
|
||||
/* STAT pin software override, match downstream. Parallel charging? */
|
||||
{ .addr = STAT_CFG,
|
||||
.mask = STAT_SW_OVERRIDE_CFG_BIT,
|
||||
|
|
@ -909,6 +917,19 @@ static int smb_init_hw(struct smb_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void smb_restore_charge_enable(void *data)
|
||||
{
|
||||
struct smb_chip *chip = data;
|
||||
int rc;
|
||||
|
||||
rc = regmap_update_bits(chip->regmap,
|
||||
chip->base + CHARGING_ENABLE_CMD,
|
||||
CHARGING_ENABLE_CMD_BIT,
|
||||
chip->initial_charge_enable);
|
||||
if (rc < 0)
|
||||
dev_err(chip->dev, "Couldn't restore charging state: %d\n", rc);
|
||||
}
|
||||
|
||||
static int smb_init_irq(struct smb_chip *chip, int *irq, const char *name,
|
||||
irqreturn_t (*handler)(int irq, void *data))
|
||||
{
|
||||
|
|
@ -935,6 +956,9 @@ static int smb_probe(struct platform_device *pdev)
|
|||
struct power_supply_config supply_config = {};
|
||||
struct power_supply_desc *desc;
|
||||
struct smb_chip *chip;
|
||||
unsigned int charge_enable;
|
||||
unsigned int float_voltage_sel;
|
||||
int float_voltage_uv;
|
||||
int rc, irq;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
|
|
@ -965,6 +989,18 @@ static int smb_probe(struct platform_device *pdev)
|
|||
"Couldn't get usbin_i IIO channel\n");
|
||||
}
|
||||
|
||||
rc = regmap_read(chip->regmap, chip->base + CHARGING_ENABLE_CMD,
|
||||
&charge_enable);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(chip->dev, rc,
|
||||
"Couldn't read charging state\n");
|
||||
|
||||
chip->initial_charge_enable = charge_enable & CHARGING_ENABLE_CMD_BIT;
|
||||
rc = devm_add_action_or_reset(chip->dev, smb_restore_charge_enable, chip);
|
||||
if (rc)
|
||||
return dev_err_probe(chip->dev, rc,
|
||||
"Couldn't register charging state rollback\n");
|
||||
|
||||
rc = smb_init_hw(chip);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
|
@ -972,7 +1008,7 @@ static int smb_probe(struct platform_device *pdev)
|
|||
supply_config.drv_data = chip;
|
||||
supply_config.fwnode = dev_fwnode(&pdev->dev);
|
||||
|
||||
desc = devm_kzalloc(chip->dev, sizeof(smb_psy_desc), GFP_KERNEL);
|
||||
desc = devm_kzalloc(chip->dev, sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
memcpy(desc, &smb_psy_desc, sizeof(smb_psy_desc));
|
||||
|
|
@ -999,12 +1035,35 @@ static int smb_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(chip->dev, rc,
|
||||
"Failed to init status change work\n");
|
||||
|
||||
rc = (chip->batt_info->voltage_max_design_uv - 3487500) / 7500 + 1;
|
||||
if (power_supply_battery_info_has_prop(chip->batt_info,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX)) {
|
||||
float_voltage_uv =
|
||||
chip->batt_info->constant_charge_voltage_max_uv;
|
||||
} else if (power_supply_battery_info_has_prop(chip->batt_info,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN)) {
|
||||
float_voltage_uv = chip->batt_info->voltage_max_design_uv;
|
||||
} else {
|
||||
dev_warn(chip->dev, "No battery float voltage; preserving hardware setting\n");
|
||||
goto skip_float_voltage;
|
||||
}
|
||||
|
||||
if (float_voltage_uv < SMB2_FLOAT_VOLTAGE_MIN_UV ||
|
||||
float_voltage_uv > SMB2_FLOAT_VOLTAGE_MAX_UV)
|
||||
return dev_err_probe(chip->dev, -EINVAL,
|
||||
"float voltage %d uV outside %d-%d uV\n",
|
||||
float_voltage_uv,
|
||||
SMB2_FLOAT_VOLTAGE_MIN_UV,
|
||||
SMB2_FLOAT_VOLTAGE_MAX_UV);
|
||||
|
||||
float_voltage_sel =
|
||||
(float_voltage_uv - SMB2_FLOAT_VOLTAGE_MIN_UV) /
|
||||
SMB2_FLOAT_VOLTAGE_STEP_UV;
|
||||
rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG,
|
||||
FLOAT_VOLTAGE_SETTING_MASK, rc);
|
||||
FLOAT_VOLTAGE_SETTING_MASK, float_voltage_sel);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n");
|
||||
|
||||
skip_float_voltage:
|
||||
rc = smb_init_irq(chip, &irq, "bat-ov", smb_handle_batt_overvoltage);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
|
@ -1028,6 +1087,8 @@ static int smb_probe(struct platform_device *pdev)
|
|||
if (rc < 0)
|
||||
return dev_err_probe(chip->dev, rc, "Couldn't set wake irq\n");
|
||||
|
||||
devm_remove_action(chip->dev, smb_restore_charge_enable, chip);
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
|
||||
/* Initialise charger state */
|
||||
|
|
|
|||
|
|
@ -633,6 +633,39 @@ static int rt9467_psy_set_ieoc(struct rt9467_chg_data *data, int microamp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rt9467_set_batfet(struct rt9467_chg_data *data, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
switch (val) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_OFF:
|
||||
regval = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_field_write(data->rm_field[F_SHIP_MODE], regval);
|
||||
}
|
||||
|
||||
static int rt9467_get_batfet(struct rt9467_chg_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(data->rm_field[F_SHIP_MODE], ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = regval ? POWER_SUPPLY_LOAD_SWITCH_OFF :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const enum power_supply_property rt9467_chg_properties[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
|
|
@ -648,6 +681,7 @@ static const enum power_supply_property rt9467_chg_properties[] = {
|
|||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
};
|
||||
|
||||
static int rt9467_psy_get_property(struct power_supply *psy,
|
||||
|
|
@ -711,6 +745,8 @@ static int rt9467_psy_get_property(struct power_supply *psy,
|
|||
val->intval = data->ieoc_ua;
|
||||
mutex_unlock(&data->ichg_ieoc_lock);
|
||||
return 0;
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return rt9467_get_batfet(data, &val->intval);
|
||||
default:
|
||||
return -ENODATA;
|
||||
}
|
||||
|
|
@ -747,6 +783,8 @@ static int rt9467_psy_set_property(struct power_supply *psy,
|
|||
RT9467_RANGE_IPREC, val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return rt9467_psy_set_ieoc(data, val->intval);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return rt9467_set_batfet(data, val->intval);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -764,6 +802,7 @@ static int rt9467_chg_prop_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -777,6 +816,8 @@ static const struct power_supply_desc rt9467_chg_psy_desc = {
|
|||
BIT(POWER_SUPPLY_USB_TYPE_CDP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN),
|
||||
.load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_OFF),
|
||||
.properties = rt9467_chg_properties,
|
||||
.num_properties = ARRAY_SIZE(rt9467_chg_properties),
|
||||
.property_is_writeable = rt9467_chg_prop_is_writeable,
|
||||
|
|
@ -793,14 +834,14 @@ static ssize_t sysoff_enable_show(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rt9467_chg_data *data = psy_device_to_chip(dev);
|
||||
unsigned int sysoff_enable;
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(data->rm_field[F_SHIP_MODE], &sysoff_enable);
|
||||
if (ret)
|
||||
ret = rt9467_get_batfet(data, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", sysoff_enable);
|
||||
return sysfs_emit(buf, "%d\n", val == POWER_SUPPLY_LOAD_SWITCH_OFF);
|
||||
}
|
||||
|
||||
static ssize_t sysoff_enable_store(struct device *dev,
|
||||
|
|
@ -815,8 +856,9 @@ static ssize_t sysoff_enable_store(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(data->rm_field[F_SHIP_MODE], !!tmp);
|
||||
if (ret)
|
||||
ret = rt9467_set_batfet(data, tmp ? POWER_SUPPLY_LOAD_SWITCH_OFF :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
|
|
|
|||
|
|
@ -265,6 +265,39 @@ static int rt9471_get_ieoc(struct rt9471_chip *chip, int *microamp)
|
|||
return rt9471_get_value_by_field_range(chip, F_IEOC_CHG, RT9471_RANGE_IEOC, microamp);
|
||||
}
|
||||
|
||||
static int rt9471_set_batfet(struct rt9471_chip *chip, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
switch (val) {
|
||||
case POWER_SUPPLY_LOAD_SWITCH_ON:
|
||||
regval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_LOAD_SWITCH_OFF:
|
||||
regval = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_field_write(chip->rm_fields[F_BATFET_DIS], regval);
|
||||
}
|
||||
|
||||
static int rt9471_get_batfet(struct rt9471_chip *chip, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(chip->rm_fields[F_BATFET_DIS], ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = regval ? POWER_SUPPLY_LOAD_SWITCH_OFF :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt9471_get_status(struct rt9471_chip *chip, int *status)
|
||||
{
|
||||
unsigned int ic_stat;
|
||||
|
|
@ -342,6 +375,7 @@ static enum power_supply_property rt9471_charger_properties[] = {
|
|||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
|
@ -358,6 +392,7 @@ static int rt9471_charger_property_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -393,6 +428,8 @@ static int rt9471_charger_set_property(struct power_supply *psy,
|
|||
chip, F_IPRE_CHG, RT9471_RANGE_IPRE, val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return rt9471_set_ieoc(chip, val->intval);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return rt9471_set_batfet(chip, val->intval);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -439,6 +476,8 @@ static int rt9471_charger_get_property(struct power_supply *psy,
|
|||
chip, F_IPRE_CHG, RT9471_RANGE_IPRE, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return rt9471_get_ieoc(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_LOAD_SWITCH:
|
||||
return rt9471_get_batfet(chip, &val->intval);
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = rt9471_model;
|
||||
return 0;
|
||||
|
|
@ -649,14 +688,14 @@ static ssize_t sysoff_enable_show(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rt9471_chip *chip = psy_device_to_chip(dev);
|
||||
unsigned int sysoff_enable;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(chip->rm_fields[F_BATFET_DIS], &sysoff_enable);
|
||||
if (ret)
|
||||
ret = rt9471_get_batfet(chip, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", sysoff_enable);
|
||||
return sysfs_emit(buf, "%d\n", val == POWER_SUPPLY_LOAD_SWITCH_OFF);
|
||||
}
|
||||
|
||||
static ssize_t sysoff_enable_store(struct device *dev,
|
||||
|
|
@ -671,8 +710,9 @@ static ssize_t sysoff_enable_store(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(chip->rm_fields[F_BATFET_DIS], !!tmp);
|
||||
if (ret)
|
||||
ret = rt9471_set_batfet(chip, tmp ? POWER_SUPPLY_LOAD_SWITCH_OFF :
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
|
|
@ -744,6 +784,8 @@ static int rt9471_register_psy(struct rt9471_chip *chip)
|
|||
BIT(POWER_SUPPLY_USB_TYPE_DCP) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID) |
|
||||
BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN);
|
||||
desc->load_switches = BIT(POWER_SUPPLY_LOAD_SWITCH_ON) |
|
||||
BIT(POWER_SUPPLY_LOAD_SWITCH_OFF);
|
||||
desc->properties = rt9471_charger_properties;
|
||||
desc->num_properties = ARRAY_SIZE(rt9471_charger_properties);
|
||||
desc->get_property = rt9471_charger_get_property;
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ static int s2mu005_fg_i2c_probe(struct i2c_client *client)
|
|||
|
||||
ret = devm_mutex_init(dev, &priv->monout_mutex);
|
||||
if (ret)
|
||||
dev_err_probe(dev, ret, "failed to initialize MONOUT mutex\n");
|
||||
return dev_err_probe(dev, ret, "failed to initialize MONOUT mutex\n");
|
||||
|
||||
psy_desc = device_get_match_data(dev);
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ static int s2mu005_fg_i2c_probe(struct i2c_client *client)
|
|||
s2mu005_handle_irq, IRQF_ONESHOT,
|
||||
psy_desc->name, priv);
|
||||
if (ret)
|
||||
dev_err_probe(dev, ret, "failed to request IRQ\n");
|
||||
return dev_err_probe(dev, ret, "failed to request IRQ\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1068,6 +1068,13 @@ static void sbs_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
|
|||
sbs_supply_changed(i2c_get_clientdata(client));
|
||||
}
|
||||
|
||||
static void sbs_disable_work(void *data)
|
||||
{
|
||||
struct sbs_info *chip = data;
|
||||
|
||||
disable_delayed_work_sync(&chip->work);
|
||||
}
|
||||
|
||||
static void sbs_external_power_changed(struct power_supply *psy)
|
||||
{
|
||||
struct sbs_info *chip = power_supply_get_drvdata(psy);
|
||||
|
|
@ -1208,6 +1215,10 @@ static int sbs_probe(struct i2c_client *client)
|
|||
return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply),
|
||||
"Failed to register power supply\n");
|
||||
|
||||
rc = devm_add_action_or_reset(&client->dev, sbs_disable_work, chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!chip->gpio_detect)
|
||||
goto out;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Prodys S.L.
|
||||
*
|
||||
* This adds support for sbs-charger compilant chips as defined here:
|
||||
* This adds support for sbs-charger compliant chips as defined here:
|
||||
* http://sbs-forum.org/specs/sbc110.pdf
|
||||
*
|
||||
* Implemetation based on sbs-battery.c
|
||||
* Implementation based on sbs-battery.c
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@
|
|||
* @pre_charge_current: current (in uA) to use in pre-charging phase
|
||||
* @termination_current: current (in uA) used to determine when the
|
||||
* charging cycle terminates
|
||||
* @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to
|
||||
* @pre_to_fast_voltage: voltage (in uV) threshold used for transitioning to
|
||||
* pre-charge to fast charge mode
|
||||
* @mains_current_limit: maximum input current drawn from AC/DC input (in uA)
|
||||
* @usb_hc_current_limit: maximum input high current (in uA) drawn from USB
|
||||
|
|
@ -302,7 +302,7 @@ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
|
|||
*
|
||||
* Function checks whether any power source is connected to the charger and
|
||||
* updates internal state accordingly. If there is a change to previous state
|
||||
* function returns %1, otherwise %0 and negative errno in case of errror.
|
||||
* function returns %1, otherwise %0 and negative errno in case of error.
|
||||
*/
|
||||
static int smb347_update_ps_status(struct smb347_charger *smb)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ enum bq27xxx_chip {
|
|||
BQ27441,
|
||||
BQ27621,
|
||||
BQ27Z561,
|
||||
BQ27Z746,
|
||||
BQ28Z610,
|
||||
BQ28Z620,
|
||||
BQ34Z100,
|
||||
BQ78Z100,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -104,6 +104,14 @@ enum {
|
|||
POWER_SUPPLY_SCOPE_DEVICE,
|
||||
};
|
||||
|
||||
enum {
|
||||
POWER_SUPPLY_LOAD_SWITCH_UNKNOWN = 0,
|
||||
POWER_SUPPLY_LOAD_SWITCH_ON,
|
||||
POWER_SUPPLY_LOAD_SWITCH_OFF,
|
||||
POWER_SUPPLY_LOAD_SWITCH_STANDBY,
|
||||
POWER_SUPPLY_LOAD_SWITCH_SHIP,
|
||||
};
|
||||
|
||||
enum power_supply_property {
|
||||
/* Properties of type `int' */
|
||||
POWER_SUPPLY_PROP_STATUS = 0,
|
||||
|
|
@ -182,6 +190,7 @@ enum power_supply_property {
|
|||
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
|
||||
POWER_SUPPLY_PROP_INTERNAL_RESISTANCE,
|
||||
POWER_SUPPLY_PROP_STATE_OF_HEALTH,
|
||||
POWER_SUPPLY_PROP_LOAD_SWITCH,
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
|
|
@ -259,9 +268,9 @@ struct power_supply_config {
|
|||
struct power_supply_desc {
|
||||
const char *name;
|
||||
enum power_supply_type type;
|
||||
u8 charge_behaviours;
|
||||
u32 charge_types;
|
||||
u32 usb_types;
|
||||
u32 load_switches;
|
||||
const enum power_supply_property *properties;
|
||||
size_t num_properties;
|
||||
|
||||
|
|
@ -295,14 +304,17 @@ struct power_supply_desc {
|
|||
*/
|
||||
int (*init)(struct power_supply *psy);
|
||||
|
||||
/* For APM emulation, think legacy userspace. */
|
||||
int use_for_apm;
|
||||
|
||||
/*
|
||||
* Set if thermal zone should not be created for this power supply.
|
||||
* For example for virtual supplies forwarding calls to actual
|
||||
* sensors or other supplies.
|
||||
*/
|
||||
bool no_thermal;
|
||||
/* For APM emulation, think legacy userspace. */
|
||||
int use_for_apm;
|
||||
|
||||
u8 charge_behaviours;
|
||||
};
|
||||
|
||||
struct power_supply_ext {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user