Commit a897a101 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset updates from Sebastian Reichel:
 "New drivers:
   - Linear ltc3651 charger driver
   - Motorola CPCAP battery fuel-gauge driver

  New chip/feature support:
   - bq27xxx: prepare for chip data setup
   - axp20x_battery: support max charge current setup

  New core features:
   - add Apple Brick ID type
   - support "supplied-from" device property for generic ACPI/pdata support
   - support strings for sysfs properties representing enums
   - introduce battery-info (backend is DT only for now)
   - provide reboot-mode header globally

  .. and misc fixes"

* tag 'for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (39 commits)
  power: supply: sbs-battery: Don't needlessly set CAPACITY_MODE
  power: supply: sbs-battery: Prevent CAPACITY_MODE races
  power: supply: bq24735: remove incorrect le16_to_cpu calls
  power: supply: sbs-battery: remove incorrect le16_to_cpu calls
  power: supply: cpcap-charger: Add missing power_supply_config
  power: supply: twl4030-charger: move allocation of iio channel to the beginning
  power: supply: twl4030-charger: allocate iio by devm_iio_channel_get() and fix error path
  power: supply: core: constify psy_tcd_ops.
  dt-bindings: power: supply: cpcap-battery: Add power-supplies property
  dt-bindings: power: supply: move max8903-charger.txt to proper location
  dt-bindings: power: supply: move maxim,max14656.txt to proper location
  power: supply: twl4030_charger: Use sysfs_match_string() helper
  power: reset: reboot-mode: Make include file global
  power: supply: axp20x_battery: add DT support for battery max constant charge current
  power: supply: axp20x_battery: add support for DT battery
  power: supply: bq27xxx: Add power_supply_battery_info support
  power: supply: bq27xxx: Add chip data memory read/write support
  power: supply: bq27xxx: Add bulk transfer bus methods
  dt-bindings: power: supply: bq27xxx: Add monitored-battery documentation
  power: supply: core: Add power_supply_prop_precharge
  ...
parents eceeae41 bfa953d3
What: /sys/class/power_supply/twl4030_ac/max_current
/sys/class/power_supply/twl4030_usb/max_current
Description:
Read/Write limit on current which may
be drawn from the ac (Accessory Charger) or
USB port.
Value is in micro-Amps.
Value is set automatically to an appropriate
value when a cable is plugged or unplugged.
Value can the set by writing to the attribute.
The change will only persist until the next
plug event. These event are reported via udev.
What: /sys/class/power_supply/twl4030_usb/mode What: /sys/class/power_supply/twl4030_usb/mode
Description: Description:
Changing mode for USB port. Changing mode for USB port.
......
Battery Characteristics
The devicetree battery node provides static battery characteristics.
In smart batteries, these are typically stored in non-volatile memory
on a fuel gauge chip. The battery node should be used where there is
no appropriate non-volatile memory, or it is unprogrammed/incorrect.
Upstream dts files should not include battery nodes, unless the battery
represented cannot easily be replaced in the system by one of a
different type. This prevents unpredictable, potentially harmful,
behavior should a replacement that changes the battery type occur
without a corresponding update to the dtb.
Required Properties:
- compatible: Must be "simple-battery"
Optional Properties:
- voltage-min-design-microvolt: drained battery voltage
- energy-full-design-microwatt-hours: battery design energy
- charge-full-design-microamp-hours: battery design capacity
- precharge-current-microamp: current for pre-charge phase
- charge-term-current-microamp: current for charge termination phase
- constant-charge-current-max-microamp: maximum constant input current
- constant-charge-voltage-max-microvolt: maximum constant input voltage
Battery properties are named, where possible, for the corresponding
elements in enum power_supply_property, defined in
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/power_supply.h
Batteries must be referenced by chargers and/or fuel-gauges
using a phandle. The phandle's property should be named
"monitored-battery".
Example:
bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
precharge-current-microamp = <256000>;
charge-term-current-microamp = <128000>;
constant-charge-current-max-microamp = <900000>;
constant-charge-voltage-max-microvolt = <4200000>;
};
charger: charger@11 {
....
monitored-battery = <&bat>;
...
};
fuel_gauge: fuel-gauge@22 {
....
monitored-battery = <&bat>;
...
};
Binding for TI BQ27XXX fuel gauge family TI BQ27XXX fuel gauge family
Required properties: Required properties:
- compatible: Should contain one of the following: - compatible: contains one of the following:
* "ti,bq27200" - BQ27200 * "ti,bq27200" - BQ27200
* "ti,bq27210" - BQ27210 * "ti,bq27210" - BQ27210
* "ti,bq27500" - deprecated, use revision specific property below * "ti,bq27500" - deprecated, use revision specific property below
...@@ -26,11 +26,28 @@ Required properties: ...@@ -26,11 +26,28 @@ Required properties:
* "ti,bq27425" - BQ27425 * "ti,bq27425" - BQ27425
* "ti,bq27441" - BQ27441 * "ti,bq27441" - BQ27441
* "ti,bq27621" - BQ27621 * "ti,bq27621" - BQ27621
- reg: integer, i2c address of the device. - reg: integer, I2C address of the fuel gauge.
Optional properties:
- monitored-battery: phandle of battery characteristics node
The fuel gauge uses the following battery properties:
+ energy-full-design-microwatt-hours
+ charge-full-design-microamp-hours
+ voltage-min-design-microvolt
Both or neither of the *-full-design-*-hours properties must be set.
See Documentation/devicetree/bindings/power/supply/battery.txt
Example: Example:
bq27510g3 { bat: battery {
compatible = "simple-battery";
voltage-min-design-microvolt = <3200000>;
energy-full-design-microwatt-hours = <5290000>;
charge-full-design-microamp-hours = <1430000>;
};
bq27510g3: fuel-gauge@55 {
compatible = "ti,bq27510g3"; compatible = "ti,bq27510g3";
reg = <0x55>; reg = <0x55>;
}; monitored-battery = <&bat>;
};
Motorola CPCAP PMIC battery driver binding
Required properties:
- compatible: Shall be "motorola,cpcap-battery"
- interrupts: Interrupt specifier for each name in interrupt-names
- interrupt-names: Should contain the following entries:
"lowbph", "lowbpl", "chrgcurr1", "battdetb"
- io-channels: IIO ADC channel specifier for each name in io-channel-names
- io-channel-names: Should contain the following entries:
"battdetb", "battp", "chg_isense", "batti"
- power-supplies: List of phandles for power-supplying devices, as
described in power_supply.txt. Typically a reference
to cpcap_charger.
Example:
cpcap_battery: battery {
compatible = "motorola,cpcap-battery";
interrupts-extended = <
&cpcap 5 0 &cpcap 3 0
&cpcap 20 0 &cpcap 54 0
>;
interrupt-names =
"lowbph", "lowbpl",
"chrgcurr1", "battdetb";
io-channels = <&cpcap_adc 0 &cpcap_adc 1
&cpcap_adc 5 &cpcap_adc 6>;
io-channel-names = "battdetb", "battp",
"chg_isense", "batti";
power-supplies = <&cpcap_charger>;
};
ltc3651-charger
Required properties:
- compatible: "lltc,ltc3651-charger"
- lltc,acpr-gpios: Connect to ACPR output. See remark below.
Optional properties:
- lltc,fault-gpios: Connect to FAULT output. See remark below.
- lltc,chrg-gpios: Connect to CHRG output. See remark below.
The ltc3651 outputs are open-drain type and active low. The driver assumes the
GPIO reports "active" when the output is asserted, so if the pins have been
connected directly, the GPIO flags should be set to active low also.
The driver will attempt to aquire interrupts for all GPIOs to detect changes in
line state. If the system is not capabale of providing interrupts, the driver
cannot report changes and userspace will need to periodically read the sysfs
attributes to detect changes.
Example:
charger: battery-charger {
compatible = "lltc,ltc3651-charger";
lltc,acpr-gpios = <&gpio0 68 GPIO_ACTIVE_LOW>;
lltc,fault-gpios = <&gpio0 64 GPIO_ACTIVE_LOW>;
lltc,chrg-gpios = <&gpio0 63 GPIO_ACTIVE_LOW>;
};
...@@ -25,8 +25,10 @@ Distance ...@@ -25,8 +25,10 @@ Distance
Electricity Electricity
---------------------------------------- ----------------------------------------
-microamp : micro amps -microamp : micro amps
-microamp-hours : micro amp-hours
-ohms : Ohms -ohms : Ohms
-micro-ohms : micro Ohms -micro-ohms : micro Ohms
-microwatt-hours: micro Watt-hours
-microvolt : micro volts -microvolt : micro volts
Temperature Temperature
......
...@@ -115,28 +115,33 @@ of charge when battery became full/empty". It also could mean "value of ...@@ -115,28 +115,33 @@ of charge when battery became full/empty". It also could mean "value of
charge when battery considered full/empty at given conditions (temperature, charge when battery considered full/empty at given conditions (temperature,
age)". I.e. these attributes represents real thresholds, not design values. age)". I.e. these attributes represents real thresholds, not design values.
ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
CHARGE_COUNTER - the current charge counter (in µAh). This could easily CHARGE_COUNTER - the current charge counter (in µAh). This could easily
be negative; there is no empty or full value. It is only useful for be negative; there is no empty or full value. It is only useful for
relative, time-based measurements. relative, time-based measurements.
PRECHARGE_CURRENT - the maximum charge current during precharge phase
of charge cycle (typically 20% of battery capacity).
CHARGE_TERM_CURRENT - Charge termination current. The charge cycle
terminates when battery voltage is above recharge threshold, and charge
current is below this setting (typically 10% of battery capacity).
CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger.
CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the
power supply object. power supply object.
INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates
the current drawn from a charging source.
CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge
condition.
CALIBRATE - battery or coulomb counter calibration status
CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
power supply object. power supply object.
INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates
the current drawn from a charging source.
CHARGE_CONTROL_LIMIT - current charge control limit setting CHARGE_CONTROL_LIMIT - current charge control limit setting
CHARGE_CONTROL_LIMIT_MAX - maximum charge control limit setting CHARGE_CONTROL_LIMIT_MAX - maximum charge control limit setting
ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CALIBRATE - battery or coulomb counter calibration status
CAPACITY - capacity in percents. CAPACITY - capacity in percents.
CAPACITY_ALERT_MIN - minimum capacity alert value in percents. CAPACITY_ALERT_MIN - minimum capacity alert value in percents.
...@@ -174,6 +179,18 @@ issued by external power supply will notify supplicants via ...@@ -174,6 +179,18 @@ issued by external power supply will notify supplicants via
external_power_changed callback. external_power_changed callback.
Devicetree battery characteristics
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drivers should call power_supply_get_battery_info() to obtain battery
characteristics from a devicetree battery node, defined in
Documentation/devicetree/bindings/power/supply/battery.txt. This is
implemented in drivers/power/supply/bq27xxx_battery.c.
Properties in struct power_supply_battery_info and their counterparts in the
battery node have names corresponding to elements in enum power_supply_property,
for naming consistency between sysfs attributes and battery node properties.
QA QA
~~ ~~
Q: Where is POWER_SUPPLY_PROP_XYZ attribute? Q: Where is POWER_SUPPLY_PROP_XYZ attribute?
......
...@@ -58,9 +58,9 @@ config POWER_RESET_BRCMKONA ...@@ -58,9 +58,9 @@ config POWER_RESET_BRCMKONA
config POWER_RESET_BRCMSTB config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver" bool "Broadcom STB reset driver"
depends on ARM || MIPS || COMPILE_TEST depends on ARM || ARM64 || MIPS || COMPILE_TEST
depends on MFD_SYSCON depends on MFD_SYSCON
default ARCH_BRCMSTB default ARCH_BRCMSTB || BMIPS_GENERIC
help help
This driver provides restart support for Broadcom STB boards. This driver provides restart support for Broadcom STB boards.
......
...@@ -97,7 +97,7 @@ static void at91_lpddr_poweroff(void) ...@@ -97,7 +97,7 @@ static void at91_lpddr_poweroff(void)
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
"r" (at91_shdwc_base), "r" (at91_shdwc_base),
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
: "r0"); : "r6");
} }
static int at91_poweroff_get_wakeup_mode(struct device_node *np) static int at91_poweroff_get_wakeup_mode(struct device_node *np)
......
...@@ -132,7 +132,7 @@ static void at91_lpddr_poweroff(void) ...@@ -132,7 +132,7 @@ static void at91_lpddr_poweroff(void)
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
"r" (at91_shdwc->at91_shdwc_base), "r" (at91_shdwc->at91_shdwc_base),
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
: "r0"); : "r6");
} }
static u32 at91_shdwc_debouncer_value(struct platform_device *pdev, static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include "reboot-mode.h" #include <linux/reboot-mode.h>
#define PREFIX "mode-" #define PREFIX "mode-"
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include "reboot-mode.h" #include <linux/reboot-mode.h>
struct syscon_reboot_mode { struct syscon_reboot_mode {
struct regmap *map; struct regmap *map;
......
...@@ -82,6 +82,14 @@ config BATTERY_ACT8945A ...@@ -82,6 +82,14 @@ config BATTERY_ACT8945A
Say Y here to enable support for power supply provided by Say Y here to enable support for power supply provided by
Active-semi ActivePath ACT8945A charger. Active-semi ActivePath ACT8945A charger.
config BATTERY_CPCAP
tristate "Motorola CPCAP PMIC battery driver"
depends on MFD_CPCAP && IIO
default MFD_CPCAP
help
Say Y here to enable support for battery on Motorola
phones and tablets such as droid 4.
config BATTERY_DS2760 config BATTERY_DS2760
tristate "DS2760 battery driver (HP iPAQ & others)" tristate "DS2760 battery driver (HP iPAQ & others)"
depends on W1 && W1_SLAVE_DS2760 depends on W1 && W1_SLAVE_DS2760
...@@ -190,6 +198,17 @@ config BATTERY_BQ27XXX_I2C ...@@ -190,6 +198,17 @@ config BATTERY_BQ27XXX_I2C
Say Y here to enable support for batteries with BQ27xxx chips Say Y here to enable support for batteries with BQ27xxx chips
connected over an I2C bus. connected over an I2C bus.
config BATTERY_BQ27XXX_DT_UPDATES_NVM
bool "BQ27xxx support for update of NVM/flash data memory"
depends on BATTERY_BQ27XXX_I2C
help
Say Y here to enable devicetree monitored-battery config to update
NVM/flash data memory. Only enable this option for devices with a
fuel gauge mounted on the circuit board, and a battery that cannot
easily be replaced with one of a different type. Not for
general-purpose kernels, as this can cause misconfiguration of a
smart battery with embedded NVM/flash.
config BATTERY_DA9030 config BATTERY_DA9030
tristate "DA9030 battery driver" tristate "DA9030 battery driver"
depends on PMIC_DA903X depends on PMIC_DA903X
...@@ -408,6 +427,13 @@ config CHARGER_MANAGER ...@@ -408,6 +427,13 @@ config CHARGER_MANAGER
runtime and in suspend-to-RAM by waking up the system periodically runtime and in suspend-to-RAM by waking up the system periodically
with help of suspend_again support. with help of suspend_again support.
config CHARGER_LTC3651
tristate "LTC3651 charger"
depends on GPIOLIB
help
Say Y to include support for the LTC3651 battery charger which reports
its status via GPIO lines.
config CHARGER_MAX14577 config CHARGER_MAX14577
tristate "Maxim MAX14577/77836 battery charger driver" tristate "Maxim MAX14577/77836 battery charger driver"
depends on MFD_MAX14577 depends on MFD_MAX14577
......
...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o
obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
obj-$(CONFIG_BATTERY_CPCAP) += cpcap-battery.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
...@@ -61,6 +62,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o ...@@ -61,6 +62,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_LTC3651) += ltc3651-charger.o
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
......
...@@ -60,6 +60,8 @@ struct axp20x_batt_ps { ...@@ -60,6 +60,8 @@ struct axp20x_batt_ps {
struct iio_channel *batt_chrg_i; struct iio_channel *batt_chrg_i;
struct iio_channel *batt_dischrg_i; struct iio_channel *batt_dischrg_i;
struct iio_channel *batt_v; struct iio_channel *batt_v;
/* Maximum constant charge current */
unsigned int max_ccc;
u8 axp_id; u8 axp_id;
}; };
...@@ -129,6 +131,14 @@ static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val) ...@@ -129,6 +131,14 @@ static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val)
*val = *val * 150000 + 300000; *val = *val * 150000 + 300000;
} }
static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
{
if (axp->axp_id == AXP209_ID)
*val = (*val - 300000) / 100000;
else
*val = (*val - 300000) / 150000;
}
static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
int *val) int *val)
{ {
...@@ -221,9 +231,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy, ...@@ -221,9 +231,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
val->intval = AXP20X_CHRG_CTRL1_TGT_CURR; val->intval = axp20x_batt->max_ccc;
raw_to_constant_charge_current(axp20x_batt, &val->intval);
break; break;
case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW:
...@@ -340,10 +348,10 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, ...@@ -340,10 +348,10 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
int charge_current) int charge_current)
{ {
if (axp_batt->axp_id == AXP209_ID) if (charge_current > axp_batt->max_ccc)
charge_current = (charge_current - 300000) / 100000; return -EINVAL;
else
charge_current = (charge_current - 300000) / 150000; constant_charge_current_to_raw(axp_batt, &charge_current);
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL; return -EINVAL;
...@@ -352,6 +360,36 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, ...@@ -352,6 +360,36 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
AXP20X_CHRG_CTRL1_TGT_CURR, charge_current); AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
} }
static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
int charge_current)
{
bool lower_max = false;
constant_charge_current_to_raw(axp, &charge_current);
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL;
raw_to_constant_charge_current(axp, &charge_current);
if (charge_current > axp->max_ccc)
dev_warn(axp->dev,
"Setting max constant charge current higher than previously defined. Note that increasing the constant charge current may damage your battery.\n");
else
lower_max = true;
axp->max_ccc = charge_current;
if (lower_max) {
int current_cc;
axp20x_get_constant_charge_current(axp, &current_cc);
if (current_cc > charge_current)
axp20x_set_constant_charge_current(axp, charge_current);
}
return 0;
}
static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
int min_voltage) int min_voltage)
{ {
...@@ -380,6 +418,9 @@ static int axp20x_battery_set_prop(struct power_supply *psy, ...@@ -380,6 +418,9 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return axp20x_set_constant_charge_current(axp20x_batt, return axp20x_set_constant_charge_current(axp20x_batt,
val->intval); val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
return axp20x_set_max_constant_charge_current(axp20x_batt,
val->intval);
default: default:
return -EINVAL; return -EINVAL;
...@@ -405,7 +446,8 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy, ...@@ -405,7 +446,8 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
{ {
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT; psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
} }
static const struct power_supply_desc axp20x_batt_ps_desc = { static const struct power_supply_desc axp20x_batt_ps_desc = {
...@@ -433,6 +475,7 @@ static int axp20x_power_probe(struct platform_device *pdev) ...@@ -433,6 +475,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
{ {
struct axp20x_batt_ps *axp20x_batt; struct axp20x_batt_ps *axp20x_batt;
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct power_supply_battery_info info;
if (!of_device_is_available(pdev->dev.of_node)) if (!of_device_is_available(pdev->dev.of_node))
return -ENODEV; return -ENODEV;
...@@ -484,6 +527,35 @@ static int axp20x_power_probe(struct platform_device *pdev) ...@@ -484,6 +527,35 @@ static int axp20x_power_probe(struct platform_device *pdev)
return PTR_ERR(axp20x_batt->batt); return PTR_ERR(axp20x_batt->batt);
} }
if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
int vmin = info.voltage_min_design_uv;
int ccc = info.constant_charge_current_max_ua;
if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
vmin))
dev_err(&pdev->dev,
"couldn't set voltage_min_design\n");
/* Set max to unverified value to be able to set CCC */
axp20x_batt->max_ccc = ccc;
if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt,
ccc)) {
dev_err(&pdev->dev,
"couldn't set constant charge current from DT: fallback to minimum value\n");
ccc = 300000;
axp20x_batt->max_ccc = ccc;
axp20x_set_constant_charge_current(axp20x_batt, ccc);
}
}
/*
* Update max CCC to a valid value if battery info is present or set it
* to current register value by default.
*/
axp20x_get_constant_charge_current(axp20x_batt,
&axp20x_batt->max_ccc);
return 0; return 0;
} }
......
...@@ -339,7 +339,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) ...@@ -339,7 +339,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
"VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL }; "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL };
static const char * const axp22x_irq_names[] = { static const char * const axp22x_irq_names[] = {
"VBUS_PLUGIN", "VBUS_REMOVAL", NULL }; "VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
static const char * const *irq_names; const char * const *irq_names;
const struct power_supply_desc *usb_power_desc; const struct power_supply_desc *usb_power_desc;
int i, irq, ret; int i, irq, ret;
......
...@@ -81,14 +81,12 @@ static int bq24735_charger_property_is_writeable(struct power_supply *psy, ...@@ -81,14 +81,12 @@ static int bq24735_charger_property_is_writeable(struct power_supply *psy,
static inline int bq24735_write_word(struct i2c_client *client, u8 reg, static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
u16 value) u16 value)
{ {
return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value)); return i2c_smbus_write_word_data(client, reg, value);
} }
static inline int bq24735_read_word(struct i2c_client *client, u8 reg) static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
{ {
s32 ret = i2c_smbus_read_word_data(client, reg); return i2c_smbus_read_word_data(client, reg);
return ret < 0 ? ret : le16_to_cpu(ret);
} }
static int bq24735_update_word(struct i2c_client *client, u8 reg, static int bq24735_update_word(struct i2c_client *client, u8 reg,
......
This diff is collapsed.
...@@ -38,7 +38,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg, ...@@ -38,7 +38,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
{ {
struct i2c_client *client = to_i2c_client(di->dev); struct i2c_client *client = to_i2c_client(di->dev);
struct i2c_msg msg[2]; struct i2c_msg msg[2];
unsigned char data[2]; u8 data[2];
int ret; int ret;
if (!client->adapter) if (!client->adapter)
...@@ -68,6 +68,82 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg, ...@@ -68,6 +68,82 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
return ret; return ret;
} }
static int bq27xxx_battery_i2c_write(struct bq27xxx_device_info *di, u8 reg,
int value, bool single)
{
struct i2c_client *client = to_i2c_client(di->dev);
struct i2c_msg msg;
u8 data[4];
int ret;
if (!client->adapter)
return -ENODEV;
data[0] = reg;
if (single) {
data[1] = (u8) value;
msg.len = 2;
} else {
put_unaligned_le16(value, &data[1]);
msg.len = 3;
}
msg.buf = data;
msg.addr = client->addr;
msg.flags = 0;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
return ret;
if (ret != 1)
return -EINVAL;
return 0;
}
static int bq27xxx_battery_i2c_bulk_read(struct bq27xxx_device_info *di, u8 reg,
u8 *data, int len)
{
struct i2c_client *client = to_i2c_client(di->dev);
int ret;
if (!client->adapter)
return -ENODEV;
ret = i2c_smbus_read_i2c_block_data(client, reg, len, data);
if (ret < 0)
return ret;
if (ret != len)
return -EINVAL;
return 0;
}
static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
u8 reg, u8 *data, int len)
{
struct i2c_client *client = to_i2c_client(di->dev);
struct i2c_msg msg;
u8 buf[33];
int ret;
if (!client->adapter)
return -ENODEV;
buf[0] = reg;
memcpy(&buf[1], data, len);
msg.buf = buf;
msg.addr = client->addr;
msg.flags = 0;
msg.len = len + 1;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
return ret;
if (ret != 1)
return -EINVAL;
return 0;
}
static int bq27xxx_battery_i2c_probe(struct i2c_client *client, static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -95,7 +171,11 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client, ...@@ -95,7 +171,11 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
di->dev = &client->dev; di->dev = &client->dev;
di->chip = id->driver_data; di->chip = id->driver_data;
di->name = name; di->name = name;
di->bus.read = bq27xxx_battery_i2c_read; di->bus.read = bq27xxx_battery_i2c_read;
di->bus.write = bq27xxx_battery_i2c_write;
di->bus.read_bulk = bq27xxx_battery_i2c_bulk_read;
di->bus.write_bulk = bq27xxx_battery_i2c_bulk_write;
ret = bq27xxx_battery_setup(di); ret = bq27xxx_battery_setup(di);
if (ret) if (ret)
......
This diff is collapsed.
...@@ -38,20 +38,27 @@ ...@@ -38,20 +38,27 @@
#include <linux/iio/consumer.h> #include <linux/iio/consumer.h>
#include <linux/mfd/motorola-cpcap.h> #include <linux/mfd/motorola-cpcap.h>
/* CPCAP_REG_CRM register bits */ /*
* CPCAP_REG_CRM register bits. For documentation of somewhat similar hardware,
* see NXP "MC13783 Power Management and Audio Circuit Users's Guide"
* MC13783UG.pdf chapter "8.5 Battery Interface Register Summary". The registers
* and values for CPCAP are different, but some of the internal components seem
* similar. Also see the Motorola Linux kernel cpcap-regbits.h. CPCAP_REG_CHRGR_1
* bits that seem to describe the CRM register.
*/
#define CPCAP_REG_CRM_UNUSED_641_15 BIT(15) /* 641 = register number */ #define CPCAP_REG_CRM_UNUSED_641_15 BIT(15) /* 641 = register number */
#define CPCAP_REG_CRM_UNUSED_641_14 BIT(14) /* 641 = register number */ #define CPCAP_REG_CRM_UNUSED_641_14 BIT(14) /* 641 = register number */
#define CPCAP_REG_CRM_CHRG_LED_EN BIT(13) #define CPCAP_REG_CRM_CHRG_LED_EN BIT(13) /* Charger LED */
#define CPCAP_REG_CRM_RVRSMODE BIT(12) #define CPCAP_REG_CRM_RVRSMODE BIT(12) /* USB VBUS output enable */
#define CPCAP_REG_CRM_ICHRG_TR1 BIT(11) #define CPCAP_REG_CRM_ICHRG_TR1 BIT(11) /* Trickle charge current */
#define CPCAP_REG_CRM_ICHRG_TR0 BIT(10) #define CPCAP_REG_CRM_ICHRG_TR0 BIT(10)
#define CPCAP_REG_CRM_FET_OVRD BIT(9) #define CPCAP_REG_CRM_FET_OVRD BIT(9) /* 0 = hardware, 1 = FET_CTRL */
#define CPCAP_REG_CRM_FET_CTRL BIT(8) #define CPCAP_REG_CRM_FET_CTRL BIT(8) /* BPFET 1 if FET_OVRD set */
#define CPCAP_REG_CRM_VCHRG3 BIT(7) #define CPCAP_REG_CRM_VCHRG3 BIT(7) /* Charge voltage bits */
#define CPCAP_REG_CRM_VCHRG2 BIT(6) #define CPCAP_REG_CRM_VCHRG2 BIT(6)
#define CPCAP_REG_CRM_VCHRG1 BIT(5) #define CPCAP_REG_CRM_VCHRG1 BIT(5)
#define CPCAP_REG_CRM_VCHRG0 BIT(4) #define CPCAP_REG_CRM_VCHRG0 BIT(4)
#define CPCAP_REG_CRM_ICHRG3 BIT(3) #define CPCAP_REG_CRM_ICHRG3 BIT(3) /* Charge current bits */
#define CPCAP_REG_CRM_ICHRG2 BIT(2) #define CPCAP_REG_CRM_ICHRG2 BIT(2)
#define CPCAP_REG_CRM_ICHRG1 BIT(1) #define CPCAP_REG_CRM_ICHRG1 BIT(1)
#define CPCAP_REG_CRM_ICHRG0 BIT(0) #define CPCAP_REG_CRM_ICHRG0 BIT(0)
...@@ -63,42 +70,50 @@ ...@@ -63,42 +70,50 @@
#define CPCAP_REG_CRM_TR_0A48 CPCAP_REG_CRM_TR(0x2) #define CPCAP_REG_CRM_TR_0A48 CPCAP_REG_CRM_TR(0x2)
#define CPCAP_REG_CRM_TR_0A72 CPCAP_REG_CRM_TR(0x4) #define CPCAP_REG_CRM_TR_0A72 CPCAP_REG_CRM_TR(0x4)
/* CPCAP_REG_CRM charge voltages */ /*
* CPCAP_REG_CRM charge voltages based on the ADC channel 1 values.
* Note that these register bits don't match MC13783UG.pdf VCHRG
* register bits.
*/
#define CPCAP_REG_CRM_VCHRG(val) (((val) & 0xf) << 4) #define CPCAP_REG_CRM_VCHRG(val) (((val) & 0xf) << 4)
#define CPCAP_REG_CRM_VCHRG_3V80 CPCAP_REG_CRM_VCHRG(0x0) #define CPCAP_REG_CRM_VCHRG_3V80 CPCAP_REG_CRM_VCHRG(0x0)
#define CPCAP_REG_CRM_VCHRG_4V10 CPCAP_REG_CRM_VCHRG(0x1) #define CPCAP_REG_CRM_VCHRG_4V10 CPCAP_REG_CRM_VCHRG(0x1)
#define CPCAP_REG_CRM_VCHRG_4V15 CPCAP_REG_CRM_VCHRG(0x2) #define CPCAP_REG_CRM_VCHRG_4V12 CPCAP_REG_CRM_VCHRG(0x2)
#define CPCAP_REG_CRM_VCHRG_4V20 CPCAP_REG_CRM_VCHRG(0x3) #define CPCAP_REG_CRM_VCHRG_4V15 CPCAP_REG_CRM_VCHRG(0x3)
#define CPCAP_REG_CRM_VCHRG_4V22 CPCAP_REG_CRM_VCHRG(0x4) #define CPCAP_REG_CRM_VCHRG_4V17 CPCAP_REG_CRM_VCHRG(0x4)
#define CPCAP_REG_CRM_VCHRG_4V24 CPCAP_REG_CRM_VCHRG(0x5) #define CPCAP_REG_CRM_VCHRG_4V20 CPCAP_REG_CRM_VCHRG(0x5)
#define CPCAP_REG_CRM_VCHRG_4V26 CPCAP_REG_CRM_VCHRG(0x6) #define CPCAP_REG_CRM_VCHRG_4V23 CPCAP_REG_CRM_VCHRG(0x6)
#define CPCAP_REG_CRM_VCHRG_4V28 CPCAP_REG_CRM_VCHRG(0x7) #define CPCAP_REG_CRM_VCHRG_4V25 CPCAP_REG_CRM_VCHRG(0x7)
#define CPCAP_REG_CRM_VCHRG_4V30 CPCAP_REG_CRM_VCHRG(0x8) #define CPCAP_REG_CRM_VCHRG_4V27 CPCAP_REG_CRM_VCHRG(0x8)
#define CPCAP_REG_CRM_VCHRG_4V32 CPCAP_REG_CRM_VCHRG(0x9) #define CPCAP_REG_CRM_VCHRG_4V30 CPCAP_REG_CRM_VCHRG(0x9)
#define CPCAP_REG_CRM_VCHRG_4V34 CPCAP_REG_CRM_VCHRG(0xa) #define CPCAP_REG_CRM_VCHRG_4V33 CPCAP_REG_CRM_VCHRG(0xa)
#define CPCAP_REG_CRM_VCHRG_4V35 CPCAP_REG_CRM_VCHRG(0xb) #define CPCAP_REG_CRM_VCHRG_4V35 CPCAP_REG_CRM_VCHRG(0xb)
#define CPCAP_REG_CRM_VCHRG_4V38 CPCAP_REG_CRM_VCHRG(0xc) #define CPCAP_REG_CRM_VCHRG_4V38 CPCAP_REG_CRM_VCHRG(0xc)
#define CPCAP_REG_CRM_VCHRG_4V40 CPCAP_REG_CRM_VCHRG(0xd) #define CPCAP_REG_CRM_VCHRG_4V40 CPCAP_REG_CRM_VCHRG(0xd)
#define CPCAP_REG_CRM_VCHRG_4V42 CPCAP_REG_CRM_VCHRG(0xe) #define CPCAP_REG_CRM_VCHRG_4V42 CPCAP_REG_CRM_VCHRG(0xe)
#define CPCAP_REG_CRM_VCHRG_4V44 CPCAP_REG_CRM_VCHRG(0xf) #define CPCAP_REG_CRM_VCHRG_4V44 CPCAP_REG_CRM_VCHRG(0xf)
/* CPCAP_REG_CRM charge currents */ /*
* CPCAP_REG_CRM charge currents. These seem to match MC13783UG.pdf
* values in "Table 8-3. Charge Path Regulator Current Limit
* Characteristics" for the nominal values.
*/
#define CPCAP_REG_CRM_ICHRG(val) (((val) & 0xf) << 0) #define CPCAP_REG_CRM_ICHRG(val) (((val) & 0xf) << 0)
#define CPCAP_REG_CRM_ICHRG_0A000 CPCAP_REG_CRM_ICHRG(0x0) #define CPCAP_REG_CRM_ICHRG_0A000 CPCAP_REG_CRM_ICHRG(0x0)
#define CPCAP_REG_CRM_ICHRG_0A070 CPCAP_REG_CRM_ICHRG(0x1) #define CPCAP_REG_CRM_ICHRG_0A070 CPCAP_REG_CRM_ICHRG(0x1)
#define CPCAP_REG_CRM_ICHRG_0A176 CPCAP_REG_CRM_ICHRG(0x2) #define CPCAP_REG_CRM_ICHRG_0A177 CPCAP_REG_CRM_ICHRG(0x2)
#define CPCAP_REG_CRM_ICHRG_0A264 CPCAP_REG_CRM_ICHRG(0x3) #define CPCAP_REG_CRM_ICHRG_0A266 CPCAP_REG_CRM_ICHRG(0x3)
#define CPCAP_REG_CRM_ICHRG_0A352 CPCAP_REG_CRM_ICHRG(0x4) #define CPCAP_REG_CRM_ICHRG_0A355 CPCAP_REG_CRM_ICHRG(0x4)
#define CPCAP_REG_CRM_ICHRG_0A440 CPCAP_REG_CRM_ICHRG(0x5) #define CPCAP_REG_CRM_ICHRG_0A443 CPCAP_REG_CRM_ICHRG(0x5)
#define CPCAP_REG_CRM_ICHRG_0A528 CPCAP_REG_CRM_ICHRG(0x6) #define CPCAP_REG_CRM_ICHRG_0A532 CPCAP_REG_CRM_ICHRG(0x6)
#define CPCAP_REG_CRM_ICHRG_0A616 CPCAP_REG_CRM_ICHRG(0x7) #define CPCAP_REG_CRM_ICHRG_0A621 CPCAP_REG_CRM_ICHRG(0x7)
#define CPCAP_REG_CRM_ICHRG_0A704 CPCAP_REG_CRM_ICHRG(0x8) #define CPCAP_REG_CRM_ICHRG_0A709 CPCAP_REG_CRM_ICHRG(0x8)
#define CPCAP_REG_CRM_ICHRG_0A792 CPCAP_REG_CRM_ICHRG(0x9) #define CPCAP_REG_CRM_ICHRG_0A798 CPCAP_REG_CRM_ICHRG(0x9)
#define CPCAP_REG_CRM_ICHRG_0A880 CPCAP_REG_CRM_ICHRG(0xa) #define CPCAP_REG_CRM_ICHRG_0A886 CPCAP_REG_CRM_ICHRG(0xa)
#define CPCAP_REG_CRM_ICHRG_0A968 CPCAP_REG_CRM_ICHRG(0xb) #define CPCAP_REG_CRM_ICHRG_0A975 CPCAP_REG_CRM_ICHRG(0xb)
#define CPCAP_REG_CRM_ICHRG_1A056 CPCAP_REG_CRM_ICHRG(0xc) #define CPCAP_REG_CRM_ICHRG_1A064 CPCAP_REG_CRM_ICHRG(0xc)
#define CPCAP_REG_CRM_ICHRG_1A144 CPCAP_REG_CRM_ICHRG(0xd) #define CPCAP_REG_CRM_ICHRG_1A152 CPCAP_REG_CRM_ICHRG(0xd)
#define CPCAP_REG_CRM_ICHRG_1A584 CPCAP_REG_CRM_ICHRG(0xe) #define CPCAP_REG_CRM_ICHRG_1A596 CPCAP_REG_CRM_ICHRG(0xe)
#define CPCAP_REG_CRM_ICHRG_NO_LIMIT CPCAP_REG_CRM_ICHRG(0xf) #define CPCAP_REG_CRM_ICHRG_NO_LIMIT CPCAP_REG_CRM_ICHRG(0xf)
enum { enum {
...@@ -428,9 +443,9 @@ static void cpcap_usb_detect(struct work_struct *work) ...@@ -428,9 +443,9 @@ static void cpcap_usb_detect(struct work_struct *work)
int max_current; int max_current;
if (cpcap_charger_battery_found(ddata)) if (cpcap_charger_battery_found(ddata))
max_current = CPCAP_REG_CRM_ICHRG_1A584; max_current = CPCAP_REG_CRM_ICHRG_1A596;
else else
max_current = CPCAP_REG_CRM_ICHRG_0A528; max_current = CPCAP_REG_CRM_ICHRG_0A532;
error = cpcap_charger_set_state(ddata, error = cpcap_charger_set_state(ddata,
CPCAP_REG_CRM_VCHRG_4V35, CPCAP_REG_CRM_VCHRG_4V35,
...@@ -586,6 +601,7 @@ static int cpcap_charger_probe(struct platform_device *pdev) ...@@ -586,6 +601,7 @@ static int cpcap_charger_probe(struct platform_device *pdev)
{ {
struct cpcap_charger_ddata *ddata; struct cpcap_charger_ddata *ddata;
const struct of_device_id *of_id; const struct of_device_id *of_id;
struct power_supply_config psy_cfg = {};
int error; int error;
of_id = of_match_device(of_match_ptr(cpcap_charger_id_table), of_id = of_match_device(of_match_ptr(cpcap_charger_id_table),
...@@ -614,9 +630,12 @@ static int cpcap_charger_probe(struct platform_device *pdev) ...@@ -614,9 +630,12 @@ static int cpcap_charger_probe(struct platform_device *pdev)
atomic_set(&ddata->active, 1); atomic_set(&ddata->active, 1);
psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = ddata;
ddata->usb = devm_power_supply_register(ddata->dev, ddata->usb = devm_power_supply_register(ddata->dev,
&cpcap_charger_usb_desc, &cpcap_charger_usb_desc,
NULL); &psy_cfg);
if (IS_ERR(ddata->usb)) { if (IS_ERR(ddata->usb)) {
error = PTR_ERR(ddata->usb); error = PTR_ERR(ddata->usb);
dev_err(ddata->dev, "failed to register USB charger: %i\n", dev_err(ddata->dev, "failed to register USB charger: %i\n",
......
/*
* Copyright (C) 2017, Topic Embedded Products
* Driver for LTC3651 charger IC.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/of.h>
struct ltc3651_charger {
struct power_supply *charger;
struct power_supply_desc charger_desc;
struct gpio_desc *acpr_gpio;
struct gpio_desc *fault_gpio;
struct gpio_desc *chrg_gpio;
};
static irqreturn_t ltc3651_charger_irq(int irq, void *devid)
{
struct power_supply *charger = devid;
power_supply_changed(charger);
return IRQ_HANDLED;
}
static inline struct ltc3651_charger *psy_to_ltc3651_charger(
struct power_supply *psy)
{
return power_supply_get_drvdata(psy);
}
static int ltc3651_charger_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
struct ltc3651_charger *ltc3651_charger = psy_to_ltc3651_charger(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (!ltc3651_charger->chrg_gpio) {
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
break;
}
if (gpiod_get_value(ltc3651_charger->chrg_gpio))
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = gpiod_get_value(ltc3651_charger->acpr_gpio);
break;
case POWER_SUPPLY_PROP_HEALTH:
if (!ltc3651_charger->fault_gpio) {
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
}
if (!gpiod_get_value(ltc3651_charger->fault_gpio)) {
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
}
/*
* If the fault pin is active, the chrg pin explains the type
* of failure.
*/
if (!ltc3651_charger->chrg_gpio) {
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
}
val->intval = gpiod_get_value(ltc3651_charger->chrg_gpio) ?
POWER_SUPPLY_HEALTH_OVERHEAT :
POWER_SUPPLY_HEALTH_DEAD;
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property ltc3651_charger_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_HEALTH,
};
static int ltc3651_charger_probe(struct platform_device *pdev)
{
struct power_supply_config psy_cfg = {};
struct ltc3651_charger *ltc3651_charger;
struct power_supply_desc *charger_desc;
int ret;
ltc3651_charger = devm_kzalloc(&pdev->dev, sizeof(*ltc3651_charger),
GFP_KERNEL);
if (!ltc3651_charger)
return -ENOMEM;
ltc3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
"lltc,acpr", GPIOD_IN);
if (IS_ERR(ltc3651_charger->acpr_gpio)) {
ret = PTR_ERR(ltc3651_charger->acpr_gpio);
dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
return ret;
}
ltc3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
"lltc,fault", GPIOD_IN);
if (IS_ERR(ltc3651_charger->fault_gpio)) {
ret = PTR_ERR(ltc3651_charger->fault_gpio);
dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
return ret;
}
ltc3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
"lltc,chrg", GPIOD_IN);
if (IS_ERR(ltc3651_charger->chrg_gpio)) {
ret = PTR_ERR(ltc3651_charger->chrg_gpio);
dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
return ret;
}
charger_desc = &ltc3651_charger->charger_desc;
charger_desc->name = pdev->dev.of_node->name;
charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
charger_desc->properties = ltc3651_charger_properties;
charger_desc->num_properties = ARRAY_SIZE(ltc3651_charger_properties);
charger_desc->get_property = ltc3651_charger_get_property;
psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = ltc3651_charger;
ltc3651_charger->charger = devm_power_supply_register(&pdev->dev,
charger_desc, &psy_cfg);
if (IS_ERR(ltc3651_charger->charger)) {
ret = PTR_ERR(ltc3651_charger->charger);
dev_err(&pdev->dev, "Failed to register power supply: %d\n",
ret);
return ret;
}
/*
* Acquire IRQs for the GPIO pins if possible. If the system does not
* support IRQs on these pins, userspace will have to poll the sysfs
* files manually.
*/
if (ltc3651_charger->acpr_gpio) {
ret = gpiod_to_irq(ltc3651_charger->acpr_gpio);
if (ret >= 0)
ret = devm_request_any_context_irq(&pdev->dev, ret,
ltc3651_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), ltc3651_charger->charger);
if (ret < 0)
dev_warn(&pdev->dev, "Failed to request acpr irq\n");
}
if (ltc3651_charger->fault_gpio) {
ret = gpiod_to_irq(ltc3651_charger->fault_gpio);
if (ret >= 0)
ret = devm_request_any_context_irq(&pdev->dev, ret,
ltc3651_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), ltc3651_charger->charger);
if (ret < 0)
dev_warn(&pdev->dev, "Failed to request fault irq\n");
}
if (ltc3651_charger->chrg_gpio) {
ret = gpiod_to_irq(ltc3651_charger->chrg_gpio);
if (ret >= 0)
ret = devm_request_any_context_irq(&pdev->dev, ret,
ltc3651_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), ltc3651_charger->charger);
if (ret < 0)
dev_warn(&pdev->dev, "Failed to request chrg irq\n");
}
platform_set_drvdata(pdev, ltc3651_charger);
return 0;
}
static const struct of_device_id ltc3651_charger_match[] = {
{ .compatible = "lltc,ltc3651-charger" },
{ }
};
MODULE_DEVICE_TABLE(of, ltc3651_charger_match);
static struct platform_driver ltc3651_charger_driver = {
.probe = ltc3651_charger_probe,
.driver = {
.name = "ltc3651-charger",
.of_match_table = ltc3651_charger_match,
},
};
module_platform_driver(ltc3651_charger_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Driver for LTC3651 charger");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ltc3651-charger");
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include "power_supply.h" #include "power_supply.h"
...@@ -274,8 +275,30 @@ static int power_supply_check_supplies(struct power_supply *psy) ...@@ -274,8 +275,30 @@ static int power_supply_check_supplies(struct power_supply *psy)
return power_supply_populate_supplied_from(psy); return power_supply_populate_supplied_from(psy);
} }
#else #else
static inline int power_supply_check_supplies(struct power_supply *psy) static int power_supply_check_supplies(struct power_supply *psy)
{ {
int nval, ret;
if (!psy->dev.parent)
return 0;
nval = device_property_read_string_array(psy->dev.parent,
"supplied-from", NULL, 0);
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; return 0;
} }
#endif #endif
...@@ -497,6 +520,62 @@ struct power_supply *devm_power_supply_get_by_phandle(struct device *dev, ...@@ -497,6 +520,62 @@ struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle); EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info)
{
struct device_node *battery_np;
const char *value;
int err;
info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL;
info->voltage_min_design_uv = -EINVAL;
info->precharge_current_ua = -EINVAL;
info->charge_term_current_ua = -EINVAL;
info->constant_charge_current_max_ua = -EINVAL;
info->constant_charge_voltage_max_uv = -EINVAL;
if (!psy->of_node) {
dev_warn(&psy->dev, "%s currently only supports devicetree\n",
__func__);
return -ENXIO;
}
battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0);
if (!battery_np)
return -ENODEV;
err = of_property_read_string(battery_np, "compatible", &value);
if (err)
return err;
if (strcmp("simple-battery", value))
return -ENODEV;
/* The property and field names below must correspond to elements
* in enum power_supply_property. For reasoning, see
* Documentation/power/power_supply_class.txt.
*/
of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
&info->energy_full_design_uwh);
of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
&info->charge_full_design_uah);
of_property_read_u32(battery_np, "voltage-min-design-microvolt",
&info->voltage_min_design_uv);
of_property_read_u32(battery_np, "precharge-current-microamp",
&info->precharge_current_ua);
of_property_read_u32(battery_np, "charge-term-current-microamp",
&info->charge_term_current_ua);
of_property_read_u32(battery_np, "constant_charge_current_max_microamp",
&info->constant_charge_current_max_ua);
of_property_read_u32(battery_np, "constant_charge_voltage_max_microvolt",
&info->constant_charge_voltage_max_uv);
return 0;
}
EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
int power_supply_get_property(struct power_supply *psy, int power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp, enum power_supply_property psp,
union power_supply_propval *val) union power_supply_propval *val)
...@@ -669,7 +748,7 @@ static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, ...@@ -669,7 +748,7 @@ static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
return ret; return ret;
} }
static struct thermal_cooling_device_ops psy_tcd_ops = { static const struct thermal_cooling_device_ops psy_tcd_ops = {
.get_max_state = ps_get_max_charge_cntl_limit, .get_max_state = ps_get_max_charge_cntl_limit,
.get_cur_state = ps_get_cur_chrage_cntl_limit, .get_cur_state = ps_get_cur_chrage_cntl_limit,
.set_cur_state = ps_set_cur_charge_cntl_limit, .set_cur_state = ps_set_cur_charge_cntl_limit,
......
...@@ -40,35 +40,42 @@ ...@@ -40,35 +40,42 @@
static struct device_attribute power_supply_attrs[]; static struct device_attribute power_supply_attrs[];
static ssize_t power_supply_show_property(struct device *dev, static const char * const power_supply_type_text[] = {
struct device_attribute *attr,
char *buf) {
static char *type_text[] = {
"Unknown", "Battery", "UPS", "Mains", "USB", "Unknown", "Battery", "UPS", "Mains", "USB",
"USB_DCP", "USB_CDP", "USB_ACA", "USB_C", "USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
"USB_PD", "USB_PD_DRP" "USB_PD", "USB_PD_DRP", "BrickID"
}; };
static char *status_text[] = {
static const char * const power_supply_status_text[] = {
"Unknown", "Charging", "Discharging", "Not charging", "Full" "Unknown", "Charging", "Discharging", "Not charging", "Full"
}; };
static char *charge_type[] = {
static const char * const power_supply_charge_type_text[] = {
"Unknown", "N/A", "Trickle", "Fast" "Unknown", "N/A", "Trickle", "Fast"
}; };
static char *health_text[] = {
static const char * const power_supply_health_text[] = {
"Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unknown", "Good", "Overheat", "Dead", "Over voltage",
"Unspecified failure", "Cold", "Watchdog timer expire", "Unspecified failure", "Cold", "Watchdog timer expire",
"Safety timer expire" "Safety timer expire"
}; };
static char *technology_text[] = {
static const char * const power_supply_technology_text[] = {
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
"LiMn" "LiMn"
}; };
static char *capacity_level_text[] = {
static const char * const power_supply_capacity_level_text[] = {
"Unknown", "Critical", "Low", "Normal", "High", "Full" "Unknown", "Critical", "Low", "Normal", "High", "Full"
}; };
static char *scope_text[] = {
static const char * const power_supply_scope_text[] = {
"Unknown", "System", "Device" "Unknown", "System", "Device"
}; };
static ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf) {
ssize_t ret = 0; ssize_t ret = 0;
struct power_supply *psy = dev_get_drvdata(dev); struct power_supply *psy = dev_get_drvdata(dev);
const ptrdiff_t off = attr - power_supply_attrs; const ptrdiff_t off = attr - power_supply_attrs;
...@@ -91,19 +98,26 @@ static ssize_t power_supply_show_property(struct device *dev, ...@@ -91,19 +98,26 @@ static ssize_t power_supply_show_property(struct device *dev,
} }
if (off == POWER_SUPPLY_PROP_STATUS) if (off == POWER_SUPPLY_PROP_STATUS)
return sprintf(buf, "%s\n", status_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_status_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE) else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
return sprintf(buf, "%s\n", charge_type[value.intval]); return sprintf(buf, "%s\n",
power_supply_charge_type_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_HEALTH) else if (off == POWER_SUPPLY_PROP_HEALTH)
return sprintf(buf, "%s\n", health_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_health_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
return sprintf(buf, "%s\n", technology_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_technology_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
return sprintf(buf, "%s\n", capacity_level_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_capacity_level_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_TYPE) else if (off == POWER_SUPPLY_PROP_TYPE)
return sprintf(buf, "%s\n", type_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_type_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_SCOPE) else if (off == POWER_SUPPLY_PROP_SCOPE)
return sprintf(buf, "%s\n", scope_text[value.intval]); return sprintf(buf, "%s\n",
power_supply_scope_text[value.intval]);
else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
return sprintf(buf, "%s\n", value.strval); return sprintf(buf, "%s\n", value.strval);
...@@ -117,14 +131,46 @@ static ssize_t power_supply_store_property(struct device *dev, ...@@ -117,14 +131,46 @@ static ssize_t power_supply_store_property(struct device *dev,
struct power_supply *psy = dev_get_drvdata(dev); struct power_supply *psy = dev_get_drvdata(dev);
const ptrdiff_t off = attr - power_supply_attrs; const ptrdiff_t off = attr - power_supply_attrs;
union power_supply_propval value; union power_supply_propval value;
/* maybe it is a enum property? */
switch (off) {
case POWER_SUPPLY_PROP_STATUS:
ret = sysfs_match_string(power_supply_status_text, buf);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = sysfs_match_string(power_supply_charge_type_text, buf);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = sysfs_match_string(power_supply_health_text, buf);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
ret = sysfs_match_string(power_supply_technology_text, buf);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
ret = sysfs_match_string(power_supply_capacity_level_text, buf);
break;
case POWER_SUPPLY_PROP_SCOPE:
ret = sysfs_match_string(power_supply_scope_text, buf);
break;
default:
ret = -EINVAL;
}
/*
* If no match was found, then check to see if it is an integer.
* Integer values are valid for enums in addition to the text value.
*/
if (ret < 0) {
long long_val; long long_val;
/* TODO: support other types than int */
ret = kstrtol(buf, 10, &long_val); ret = kstrtol(buf, 10, &long_val);
if (ret < 0) if (ret < 0)
return ret; return ret;
value.intval = long_val; ret = long_val;
}
value.intval = ret;
ret = power_supply_set_property(psy, off, &value); ret = power_supply_set_property(psy, off, &value);
if (ret < 0) if (ret < 0)
...@@ -196,6 +242,7 @@ static struct device_attribute power_supply_attrs[] = { ...@@ -196,6 +242,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(time_to_full_avg),
POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope), POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(precharge_current),
POWER_SUPPLY_ATTR(charge_term_current), POWER_SUPPLY_ATTR(charge_term_current),
POWER_SUPPLY_ATTR(calibrate), POWER_SUPPLY_ATTR(calibrate),
/* Properties of type `const char *' */ /* Properties of type `const char *' */
......
...@@ -171,6 +171,7 @@ struct sbs_info { ...@@ -171,6 +171,7 @@ struct sbs_info {
u32 i2c_retry_count; u32 i2c_retry_count;
u32 poll_retry_count; u32 poll_retry_count;
struct delayed_work work; struct delayed_work work;
struct mutex mode_lock;
}; };
static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
...@@ -199,7 +200,7 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address) ...@@ -199,7 +200,7 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address)
return ret; return ret;
} }
return le16_to_cpu(ret); return ret;
} }
static int sbs_read_string_data(struct i2c_client *client, u8 address, static int sbs_read_string_data(struct i2c_client *client, u8 address,
...@@ -265,7 +266,7 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, ...@@ -265,7 +266,7 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address,
memcpy(values, block_buffer + 1, block_length); memcpy(values, block_buffer + 1, block_length);
values[block_length] = '\0'; values[block_length] = '\0';
return le16_to_cpu(ret); return ret;
} }
static int sbs_write_word_data(struct i2c_client *client, u8 address, static int sbs_write_word_data(struct i2c_client *client, u8 address,
...@@ -278,8 +279,7 @@ static int sbs_write_word_data(struct i2c_client *client, u8 address, ...@@ -278,8 +279,7 @@ static int sbs_write_word_data(struct i2c_client *client, u8 address,
retries = chip->i2c_retry_count; retries = chip->i2c_retry_count;
while (retries > 0) { while (retries > 0) {
ret = i2c_smbus_write_word_data(client, address, ret = i2c_smbus_write_word_data(client, address, value);
le16_to_cpu(value));
if (ret >= 0) if (ret >= 0)
break; break;
retries--; retries--;
...@@ -438,6 +438,11 @@ static int sbs_get_battery_property(struct i2c_client *client, ...@@ -438,6 +438,11 @@ static int sbs_get_battery_property(struct i2c_client *client,
} else { } else {
if (psp == POWER_SUPPLY_PROP_STATUS) if (psp == POWER_SUPPLY_PROP_STATUS)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN; val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
else if (psp == POWER_SUPPLY_PROP_CAPACITY)
/* sbs spec says that this can be >100 %
* even if max value is 100 %
*/
val->intval = min(ret, 100);
else else
val->intval = 0; val->intval = 0;
} }
...@@ -548,11 +553,6 @@ static int sbs_get_battery_capacity(struct i2c_client *client, ...@@ -548,11 +553,6 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
if (ret < 0) if (ret < 0)
return ret; return ret;
if (psp == POWER_SUPPLY_PROP_CAPACITY) {
/* sbs spec says that this can be >100 %
* even if max value is 100 % */
val->intval = min(ret, 100);
} else
val->intval = ret; val->intval = ret;
ret = sbs_set_battery_mode(client, mode); ret = sbs_set_battery_mode(client, mode);
...@@ -618,12 +618,17 @@ static int sbs_get_property(struct power_supply *psy, ...@@ -618,12 +618,17 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW:
case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
case POWER_SUPPLY_PROP_CAPACITY:
ret = sbs_get_property_index(client, psp); ret = sbs_get_property_index(client, psp);
if (ret < 0) if (ret < 0)
break; break;
/* sbs_get_battery_capacity() will change the battery mode
* temporarily to read the requested attribute. Ensure we stay
* in the desired mode for the duration of the attribute read.
*/
mutex_lock(&chip->mode_lock);
ret = sbs_get_battery_capacity(client, ret, psp, val); ret = sbs_get_battery_capacity(client, ret, psp, val);
mutex_unlock(&chip->mode_lock);
break; break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER: case POWER_SUPPLY_PROP_SERIAL_NUMBER:
...@@ -640,6 +645,7 @@ static int sbs_get_property(struct power_supply *psy, ...@@ -640,6 +645,7 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
case POWER_SUPPLY_PROP_CAPACITY:
ret = sbs_get_property_index(client, psp); ret = sbs_get_property_index(client, psp);
if (ret < 0) if (ret < 0)
break; break;
...@@ -808,6 +814,7 @@ static int sbs_probe(struct i2c_client *client, ...@@ -808,6 +814,7 @@ static int sbs_probe(struct i2c_client *client,
psy_cfg.of_node = client->dev.of_node; psy_cfg.of_node = client->dev.of_node;
psy_cfg.drv_data = chip; psy_cfg.drv_data = chip;
chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
mutex_init(&chip->mode_lock);
/* use pdata if available, fall back to DT properties, /* use pdata if available, fall back to DT properties,
* or hardcoded defaults if not * or hardcoded defaults if not
......
...@@ -153,7 +153,7 @@ struct twl4030_bci { ...@@ -153,7 +153,7 @@ struct twl4030_bci {
}; };
/* strings for 'usb_mode' values */ /* strings for 'usb_mode' values */
static char *modes[] = { "off", "auto", "continuous" }; static const char *modes[] = { "off", "auto", "continuous" };
/* /*
* clear and set bits on an given register on a given module * clear and set bits on an given register on a given module
...@@ -624,63 +624,6 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) ...@@ -624,63 +624,6 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*
* Provide "max_current" attribute in sysfs.
*/
static ssize_t
twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t n)
{
struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
int cur = 0;
int status = 0;
status = kstrtoint(buf, 10, &cur);
if (status)
return status;
if (cur < 0)
return -EINVAL;
if (dev == &bci->ac->dev)
bci->ac_cur = cur;
else
bci->usb_cur_target = cur;
twl4030_charger_update_current(bci);
return n;
}
/*
* sysfs max_current show
*/
static ssize_t twl4030_bci_max_current_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int status = 0;
int cur = -1;
u8 bcictl1;
struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
if (dev == &bci->ac->dev) {
if (!bci->ac_is_active)
cur = bci->ac_cur;
} else {
if (bci->ac_is_active)
cur = bci->usb_cur_target;
}
if (cur < 0) {
cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
if (cur < 0)
return cur;
status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
if (status < 0)
return status;
cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
}
return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
}
static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
twl4030_bci_max_current_store);
static void twl4030_bci_usb_work(struct work_struct *data) static void twl4030_bci_usb_work(struct work_struct *data)
{ {
struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
...@@ -726,14 +669,10 @@ twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, ...@@ -726,14 +669,10 @@ twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
int mode; int mode;
int status; int status;
if (sysfs_streq(buf, modes[0])) mode = sysfs_match_string(modes, buf);
mode = 0; if (mode < 0)
else if (sysfs_streq(buf, modes[1])) return mode;
mode = 1;
else if (sysfs_streq(buf, modes[2]))
mode = 2;
else
return -EINVAL;
if (dev == &bci->ac->dev) { if (dev == &bci->ac->dev) {
if (mode == 2) if (mode == 2)
return -EINVAL; return -EINVAL;
...@@ -1041,6 +980,12 @@ static int twl4030_bci_probe(struct platform_device *pdev) ...@@ -1041,6 +980,12 @@ static int twl4030_bci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bci); platform_set_drvdata(pdev, bci);
bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
if (IS_ERR(bci->channel_vac)) {
bci->channel_vac = NULL;
dev_warn(&pdev->dev, "could not request vac iio channel");
}
bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
NULL); NULL);
if (IS_ERR(bci->ac)) { if (IS_ERR(bci->ac)) {
...@@ -1074,12 +1019,6 @@ static int twl4030_bci_probe(struct platform_device *pdev) ...@@ -1074,12 +1019,6 @@ static int twl4030_bci_probe(struct platform_device *pdev)
return ret; return ret;
} }
bci->channel_vac = iio_channel_get(&pdev->dev, "vac");
if (IS_ERR(bci->channel_vac)) {
bci->channel_vac = NULL;
dev_warn(&pdev->dev, "could not request vac iio channel");
}
INIT_WORK(&bci->work, twl4030_bci_usb_work); INIT_WORK(&bci->work, twl4030_bci_usb_work);
INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
...@@ -1101,7 +1040,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) ...@@ -1101,7 +1040,7 @@ static int twl4030_bci_probe(struct platform_device *pdev)
TWL4030_INTERRUPTS_BCIIMR1A); TWL4030_INTERRUPTS_BCIIMR1A);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
goto fail; return ret;
} }
reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
...@@ -1111,14 +1050,10 @@ static int twl4030_bci_probe(struct platform_device *pdev) ...@@ -1111,14 +1050,10 @@ static int twl4030_bci_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
twl4030_charger_update_current(bci); twl4030_charger_update_current(bci);
if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
dev_warn(&pdev->dev, "could not create sysfs file\n");
if (device_create_file(&bci->usb->dev, &dev_attr_mode)) if (device_create_file(&bci->usb->dev, &dev_attr_mode))
dev_warn(&pdev->dev, "could not create sysfs file\n"); dev_warn(&pdev->dev, "could not create sysfs file\n");
if (device_create_file(&bci->ac->dev, &dev_attr_mode)) if (device_create_file(&bci->ac->dev, &dev_attr_mode))
dev_warn(&pdev->dev, "could not create sysfs file\n"); dev_warn(&pdev->dev, "could not create sysfs file\n");
if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
dev_warn(&pdev->dev, "could not create sysfs file\n");
twl4030_charger_enable_ac(bci, true); twl4030_charger_enable_ac(bci, true);
if (!IS_ERR_OR_NULL(bci->transceiver)) if (!IS_ERR_OR_NULL(bci->transceiver))
...@@ -1134,10 +1069,6 @@ static int twl4030_bci_probe(struct platform_device *pdev) ...@@ -1134,10 +1069,6 @@ static int twl4030_bci_probe(struct platform_device *pdev)
twl4030_charger_enable_backup(0, 0); twl4030_charger_enable_backup(0, 0);
return 0; return 0;
fail:
iio_channel_release(bci->channel_vac);
return ret;
} }
static int twl4030_bci_remove(struct platform_device *pdev) static int twl4030_bci_remove(struct platform_device *pdev)
...@@ -1148,11 +1079,7 @@ static int twl4030_bci_remove(struct platform_device *pdev) ...@@ -1148,11 +1079,7 @@ static int twl4030_bci_remove(struct platform_device *pdev)
twl4030_charger_enable_usb(bci, false); twl4030_charger_enable_usb(bci, false);
twl4030_charger_enable_backup(0, 0); twl4030_charger_enable_backup(0, 0);
iio_channel_release(bci->channel_vac);
device_remove_file(&bci->usb->dev, &dev_attr_max_current);
device_remove_file(&bci->usb->dev, &dev_attr_mode); device_remove_file(&bci->usb->dev, &dev_attr_mode);
device_remove_file(&bci->ac->dev, &dev_attr_max_current);
device_remove_file(&bci->ac->dev, &dev_attr_mode); device_remove_file(&bci->ac->dev, &dev_attr_mode);
/* mask interrupts */ /* mask interrupts */
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
......
...@@ -40,6 +40,9 @@ struct bq27xxx_platform_data { ...@@ -40,6 +40,9 @@ struct bq27xxx_platform_data {
struct bq27xxx_device_info; struct bq27xxx_device_info;
struct bq27xxx_access_methods { struct bq27xxx_access_methods {
int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single); int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
int (*write)(struct bq27xxx_device_info *di, u8 reg, int value, bool single);
int (*read_bulk)(struct bq27xxx_device_info *di, u8 reg, u8 *data, int len);
int (*write_bulk)(struct bq27xxx_device_info *di, u8 reg, u8 *data, int len);
}; };
struct bq27xxx_reg_cache { struct bq27xxx_reg_cache {
...@@ -60,7 +63,10 @@ struct bq27xxx_device_info { ...@@ -60,7 +63,10 @@ struct bq27xxx_device_info {
struct device *dev; struct device *dev;
int id; int id;
enum bq27xxx_chip chip; enum bq27xxx_chip chip;
bool ram_chip;
const char *name; const char *name;
struct bq27xxx_dm_reg *dm_regs;
u32 unseal_key;
struct bq27xxx_access_methods bus; struct bq27xxx_access_methods bus;
struct bq27xxx_reg_cache cache; struct bq27xxx_reg_cache cache;
int charge_design_full; int charge_design_full;
......
...@@ -146,6 +146,7 @@ enum power_supply_property { ...@@ -146,6 +146,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CALIBRATE, POWER_SUPPLY_PROP_CALIBRATE,
/* Properties of type `const char *' */ /* Properties of type `const char *' */
...@@ -166,6 +167,7 @@ enum power_supply_type { ...@@ -166,6 +167,7 @@ enum power_supply_type {
POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */ POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */
POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */ POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */
POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */ POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */
POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
}; };
enum power_supply_notifier_events { enum power_supply_notifier_events {
...@@ -288,6 +290,25 @@ struct power_supply_info { ...@@ -288,6 +290,25 @@ struct power_supply_info {
int use_for_apm; int use_for_apm;
}; };
/*
* This is the recommended struct to manage static battery parameters,
* populated by power_supply_get_battery_info(). Most platform drivers should
* use these for consistency.
* Its field names must correspond to elements in enum power_supply_property.
* The default field value is -EINVAL.
* Power supply class itself doesn't use this.
*/
struct power_supply_battery_info {
int energy_full_design_uwh; /* microWatt-hours */
int charge_full_design_uah; /* microAmp-hours */
int voltage_min_design_uv; /* microVolts */
int precharge_current_ua; /* microAmps */
int charge_term_current_ua; /* microAmps */
int constant_charge_current_max_ua; /* microAmps */
int constant_charge_voltage_max_uv; /* microVolts */
};
extern struct atomic_notifier_head power_supply_notifier; extern struct atomic_notifier_head power_supply_notifier;
extern int power_supply_reg_notifier(struct notifier_block *nb); extern int power_supply_reg_notifier(struct notifier_block *nb);
extern void power_supply_unreg_notifier(struct notifier_block *nb); extern void power_supply_unreg_notifier(struct notifier_block *nb);
...@@ -306,6 +327,9 @@ static inline struct power_supply * ...@@ -306,6 +327,9 @@ static inline struct power_supply *
devm_power_supply_get_by_phandle(struct device *dev, const char *property) devm_power_supply_get_by_phandle(struct device *dev, const char *property)
{ return NULL; } { return NULL; }
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
extern int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info);
extern void power_supply_changed(struct power_supply *psy); extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy); extern int power_supply_set_battery_charged(struct power_supply *psy);
...@@ -359,6 +383,8 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp) ...@@ -359,6 +383,8 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp)
case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW:
case POWER_SUPPLY_PROP_CHARGE_AVG: case POWER_SUPPLY_PROP_CHARGE_AVG:
case POWER_SUPPLY_PROP_CHARGE_COUNTER: case POWER_SUPPLY_PROP_CHARGE_COUNTER:
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CURRENT_MAX:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment