power supply and reset changes for the v4.4 series

* new AXP20X USB Power driver
  * new Qualcomm SMBB driver
  * new TPS65217 Charger driver
  * BQ24257: add BQ24250/BQ24251 support
  * overhaul bq27x00 battery driver, rename to bq27xxx
  * misc. fixes and cleanups
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCgAGBQJWOn8oAAoJENju1/PIO/qahrEP/RPHXvTxtYv8YvhaFbqcr07x
 11gRB8RRthKK5woC3s70QKlMtcHijFeCwKuacut+7tfgK2oZDIm+Bd43Pl93aypU
 Yk4oTwop5OyrLsGIGiTIPPV9HTwr4q69Or2Q91mqUAUe2wGB5uCuJiJmCowUwfZs
 piv+ISLSOcH+i1bTx3hQ7h7axsdAlaSz2o1JprZaxm3rM67M8zF1ZHSK7wlpAeBa
 DiDrtUvEDCeLL8dmCA9xkFV4yFB9T+KNh0730N2RY+zlZS6WjSpzryqwsUWWyQ6h
 U9gFgcs+DjVaGHuMsIX2zm7xXfXHgUBtXdSQoCBhpSrZsdufMsT/8VQqVZ0yqpXL
 pQ2EXy63k6tXnXEyAKTA0tAOX/WZNfmmNblkea4JiyZULn7b/WH09waMZUBJqe84
 Yn3SatVdGDN4nawsivzdiM3zR9/WTE9R3DmEeAE+g1RRsSraNwWlkzpDXcmnWmbc
 DoprnjLsDJfqL3/1ysF2V2I4B26Bws73qq6UUjJ4KEGWf8Y1fVawxrbcYmnrvg1K
 zW/saEgtl70FYqtee/0lIqTpjOZRX/O6QH5i8p7VC8cUzftX0Twc8MvfvpamDywX
 Cr1Y7pIMEhc6TFUP5lOzx9xJelA5PHJMpAT+02fh6YXMAzCcBUzL113rY5zBrVVl
 gce0iHDKAybmvww77z1r
 =0be4
 -----END PGP SIGNATURE-----

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

Pull power supply and reset updates from Sebastian Reichel:
 - new AXP20X USB Power driver
 - new Qualcomm SMBB driver
 - new TPS65217 Charger driver
 - BQ24257: add BQ24250/BQ24251 support
 - overhaul bq27x00 battery driver, rename to bq27xxx
 - misc fixes and cleanups

* tag 'for-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (54 commits)
  power: bq27xxx_battery: Remove unneeded dependency in Kconfig
  power: bq27xxx_battery: move irq handler to i2c section
  power: bq27xxx_battery: fix platform probe
  twl4030_charger: add missing iio dependency
  power_supply: charger-manager: add missing of_node_put
  Documentation: power: bq24257: Document exported sysfs entries
  power: bq24257: Add various device-specific sysfs properties
  power: bq24257: Allow input current limit sysfs access
  power: bq24257: Add input DPM voltage threshold setting support
  power: bq24257: Add over voltage protection setting support
  power: bq24257: Add SW-based approach for Power Good determination
  power: bq24257: Allow manual setting of input current limit
  power: bq24257: Add bit definition for temp sense enable
  power: bq24257: Add basic support for bq24250/bq24251
  dt: power: bq24257-charger: Cover additional devices
  power: bq24257: Simplify bq24257_power_supply_init()
  power: bq24257: Use managed power supply register
  power: bq24257: Streamline input current limit setup
  power: bq24257: Remove IRQ config through stat-gpios
  power: bq27xxx_battery: fix signedness bug in bq27xxx_battery_read_health()
  ...
This commit is contained in:
Linus Torvalds 2015-11-05 12:28:15 -08:00
commit 400c5bd5a5
39 changed files with 3822 additions and 1478 deletions

View file

@ -74,3 +74,61 @@ Description:
Valid values:
- 0 - 70 (minutes), step by 10 (rounded down)
What: /sys/class/power_supply/bq24257-charger/ovp_voltage
Date: October 2015
KernelVersion: 4.4.0
Contact: Andreas Dannenberg <dannenberg@ti.com>
Description:
This entry configures the overvoltage protection feature of bq24257-
type charger devices. This feature protects the device and other
components against damage from overvoltage on the input supply. See
device datasheet for details.
Valid values:
- 6000000, 6500000, 7000000, 8000000, 9000000, 9500000, 10000000,
10500000 (all uV)
What: /sys/class/power_supply/bq24257-charger/in_dpm_voltage
Date: October 2015
KernelVersion: 4.4.0
Contact: Andreas Dannenberg <dannenberg@ti.com>
Description:
This entry configures the input dynamic power path management voltage of
bq24257-type charger devices. Once the supply drops to the configured
voltage, the input current limit is reduced down to prevent the further
drop of the supply. When the IC enters this mode, the charge current is
lower than the set value. See device datasheet for details.
Valid values:
- 4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
4760000 (all uV)
What: /sys/class/power_supply/bq24257-charger/high_impedance_enable
Date: October 2015
KernelVersion: 4.4.0
Contact: Andreas Dannenberg <dannenberg@ti.com>
Description:
This entry allows enabling the high-impedance mode of bq24257-type
charger devices. If enabled, it places the charger IC into low power
standby mode with the switch mode controller disabled. When disabled,
the charger operates normally. See device datasheet for details.
Valid values:
- 1: enabled
- 0: disabled
What: /sys/class/power_supply/bq24257-charger/sysoff_enable
Date: October 2015
KernelVersion: 4.4.0
Contact: Andreas Dannenberg <dannenberg@ti.com>
Description:
This entry allows enabling the sysoff mode of bq24257-type charger
devices. If enabled and the input is removed, the internal battery FET
is turned off in order to reduce the leakage from the BAT pin to less
than 1uA. Note that on some devices/systems this disconnects the battery
from the system. See device datasheet for details.
Valid values:
- 1: enabled
- 0: disabled

View file

@ -1,21 +1,64 @@
Binding for TI bq24257 Li-Ion Charger
Binding for TI bq24250/bq24251/bq24257 Li-Ion Charger
Required properties:
- compatible: Should contain one of the following:
* "ti,bq24250"
* "ti,bq24251"
* "ti,bq24257"
- reg: integer, i2c address of the device.
- reg: integer, i2c address of the device.
- interrupt-parent: Should be the phandle for the interrupt controller. Use in
conjunction with "interrupts".
- interrupts: Interrupt mapping for GPIO IRQ (configure for both edges). Use in
conjunction with "interrupt-parent".
- ti,battery-regulation-voltage: integer, maximum charging voltage in uV.
- ti,charge-current: integer, maximum charging current in uA.
- ti,termination-current: integer, charge will be terminated when current in
constant-voltage phase drops below this value (in uA).
- ti,charge-current: integer, maximum charging current in uA.
- ti,termination-current: integer, charge will be terminated when current in
constant-voltage phase drops below this value (in uA).
Optional properties:
- pg-gpios: GPIO used for connecting the bq2425x device PG (Power Good) pin.
This pin is not available on all devices however it should be used if
possible as this is the recommended way to obtain the charger's input PG
state. If this pin is not specified a software-based approach for PG
detection is used.
- ti,current-limit: The maximum current to be drawn from the charger's input
(in uA). If this property is not specified, the input limit current is
set automatically using USB D+/D- signal based charger type detection.
If the hardware does not support the D+/D- based detection, a default
of 500,000 is used (=500mA) instead.
- ti,ovp-voltage: Configures the over voltage protection voltage (in uV). If
not specified a default of 6,5000,000 (=6.5V) is used.
- ti,in-dpm-voltage: Configures the threshold input voltage for the dynamic
power path management (in uV). If not specified a default of 4,360,000
(=4.36V) is used.
Example:
bq24257 {
compatible = "ti,bq24257";
reg = <0x6a>;
interrupt-parent = <&gpio1>;
interrupts = <16 IRQ_TYPE_EDGE_BOTH>;
pg-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
ti,battery-regulation-voltage = <4200000>;
ti,charge-current = <1000000>;
ti,termination-current = <50000>;
};
Example:
bq24250 {
compatible = "ti,bq24250";
reg = <0x6a>;
interrupt-parent = <&gpio1>;
interrupts = <16 IRQ_TYPE_EDGE_BOTH>;
ti,battery-regulation-voltage = <4200000>;
ti,charge-current = <500000>;
ti,termination-current = <50000>;
ti,current-limit = <900000>;
ti,ovp-voltage = <9500000>;
ti,in-dpm-voltage = <4440000>;
};

View file

@ -0,0 +1,34 @@
AXP20x USB power supply
Required Properties:
-compatible: "x-powers,axp202-usb-power-supply"
This node is a subnode of the axp20x PMIC.
Example:
axp209: pmic@34 {
compatible = "x-powers,axp209";
reg = <0x34>;
interrupt-parent = <&nmi_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
interrupt-controller;
#interrupt-cells = <1>;
regulators {
x-powers,dcdc-freq = <1500>;
vdd_cpu: dcdc2 {
regulator-always-on;
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1450000>;
regulator-name = "vdd-cpu";
};
...
};
usb-power-supply: usb-power-supply {
compatible = "x-powers,axp202-usb-power-supply";
};
};

View file

@ -0,0 +1,131 @@
Qualcomm Switch-Mode Battery Charger and Boost
PROPERTIES
- compatible:
Usage: required
Value type: <stringlist>
Description: Must be one of:
- "qcom,pm8941-charger"
- reg:
Usage: required
Value type: <prop-encoded-array>
Description: Base address of registers for SMBB block
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Description: The format of the specifier is defined by the binding document
describing the node's interrupt parent. Must contain one
specifier for each of the following interrupts, in order:
- charge done
- charge fast mode
- charge trickle mode
- battery temperature ok
- battery present
- charger disconnected
- USB-in valid
- DC-in valid
- interrupt-names:
Usage: required
Value type: <stringlist>
Description: Must contain the following list, strictly ordered:
"chg-done",
"chg-fast",
"chg-trkl",
"bat-temp-ok",
"bat-present",
"chg-gone",
"usb-valid",
"dc-valid"
- qcom,fast-charge-current-limit:
Usage: optional (default: 1A, or pre-configured value)
Value type: <u32>; uA; range [100mA : 3A]
Description: Maximum charge current; May be clamped to safety limits.
- qcom,fast-charge-low-threshold-voltage:
Usage: optional (default: 3.2V, or pre-configured value)
Value type: <u32>; uV; range [2.1V : 3.6V]
Description: Battery voltage limit above which fast charging may operate;
Below this value linear or switch-mode auto-trickle-charging
will operate.
- qcom,fast-charge-high-threshold-voltage:
Usage: optional (default: 4.2V, or pre-configured value)
Value type: <u32>; uV; range [3.24V : 5V]
Description: Battery voltage limit below which fast charging may operate;
The fast charger will attempt to charge the battery to this
voltage. May be clamped to safety limits.
- qcom,fast-charge-safe-voltage:
Usage: optional (default: 4.2V, or pre-configured value)
Value type: <u32>; uV; range [3.24V : 5V]
Description: Maximum safe battery voltage; May be pre-set by bootloader, in
which case, setting this will harmlessly fail. The property
'fast-charge-high-watermark' will be clamped by this value.
- qcom,fast-charge-safe-current:
Usage: optional (default: 1A, or pre-configured value)
Value type: <u32>; uA; range [100mA : 3A]
Description: Maximum safe battery charge current; May pre-set by bootloader,
in which case, setting this will harmlessly fail. The property
'qcom,fast-charge-current-limit' will be clamped by this value.
- qcom,auto-recharge-threshold-voltage:
Usage: optional (default: 4.1V, or pre-configured value)
Value type: <u32>; uV; range [3.24V : 5V]
Description: Battery voltage limit below which auto-recharge functionality
will restart charging after end-of-charge; The high cutoff
limit for auto-recharge is 5% above this value.
- qcom,minimum-input-voltage:
Usage: optional (default: 4.3V, or pre-configured value)
Value type: <u32>; uV; range [4.2V : 9.6V]
Description: Input voltage level above which charging may operate
- qcom,dc-current-limit:
Usage: optional (default: 100mA, or pre-configured value)
Value type: <u32>; uA; range [100mA : 2.5A]
Description: Default DC charge current limit
- qcom,disable-dc:
Usage: optional (default: false)
Value type: boolean: <u32> or <empty>
Description: Disable DC charger
- qcom,jeita-extended-temp-range:
Usage: optional (default: false)
Value type: boolean: <u32> or <empty>
Description: Enable JEITA extended temperature range; This does *not*
adjust the maximum charge voltage or current in the extended
temperature range. It only allows charging when the battery
is in the extended temperature range. Voltage/current
regulation must be done externally to fully comply with
the JEITA safety guidelines if this flag is set.
EXAMPLE
charger@1000 {
compatible = "qcom,pm8941-charger";
reg = <0x1000 0x700>;
interrupts = <0x0 0x10 7 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x10 5 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x10 4 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x12 1 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x12 0 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x13 2 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x13 1 IRQ_TYPE_EDGE_BOTH>,
<0x0 0x14 1 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "chg-done",
"chg-fast",
"chg-trkl",
"bat-temp-ok",
"bat-present",
"chg-gone",
"usb-valid",
"dc-valid";
qcom,fast-charge-current-limit = <1000000>;
qcom,dc-charge-current-limit = <1000000>;
};

View file

@ -0,0 +1,12 @@
TPS65217 Charger
Required Properties:
-compatible: "ti,tps65217-charger"
This node is a subnode of the tps65217 PMIC.
Example:
tps65217-charger {
compatible = "ti,tps65090-charger";
};

View file

@ -7505,10 +7505,10 @@ NOKIA N900 POWER SUPPLY DRIVERS
M: Pali Rohár <pali.rohar@gmail.com>
S: Maintained
F: include/linux/power/bq2415x_charger.h
F: include/linux/power/bq27x00_battery.h
F: include/linux/power/bq27xxx_battery.h
F: include/linux/power/isp1704_charger.h
F: drivers/power/bq2415x_charger.c
F: drivers/power/bq27x00_battery.c
F: drivers/power/bq27xxx_battery.c
F: drivers/power/isp1704_charger.c
F: drivers/power/rx51_battery.c

View file

@ -22,6 +22,8 @@
charger: bci {
compatible = "ti,twl4030-bci";
interrupts = <9>, <2>;
io-channels = <&twl4030_madc 11>;
io-channel-name = "vac";
bci3v1-supply = <&vusb3v1>;
};

View file

@ -246,7 +246,7 @@ CONFIG_GPIO_TWL4030=y
CONFIG_GPIO_PALMAS=y
CONFIG_W1=m
CONFIG_HDQ_MASTER_OMAP=m
CONFIG_BATTERY_BQ27x00=m
CONFIG_BATTERY_BQ27XXX=m
CONFIG_CHARGER_ISP1704=m
CONFIG_CHARGER_TWL4030=m
CONFIG_CHARGER_BQ2415X=m

View file

@ -222,7 +222,7 @@ config I2C_BATTERY_BQ27200
tristate "I2C Battery BQ27200 Support"
select I2C_PUV3
select POWER_SUPPLY
select BATTERY_BQ27x00
select BATTERY_BQ27XXX
config I2C_EEPROM_AT24
tristate "I2C EEPROMs AT24 support"

View file

@ -954,46 +954,32 @@ static int pm860x_battery_probe(struct platform_device *pdev)
else
info->resistor = 300; /* set default internal resistor */
info->battery = power_supply_register(&pdev->dev, &pm860x_battery_desc,
NULL);
info->battery = devm_power_supply_register(&pdev->dev,
&pm860x_battery_desc,
NULL);
if (IS_ERR(info->battery))
return PTR_ERR(info->battery);
info->battery->dev.parent = &pdev->dev;
ret = request_threaded_irq(info->irq_cc, NULL,
pm860x_coulomb_handler, IRQF_ONESHOT,
"coulomb", info);
ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
pm860x_coulomb_handler, IRQF_ONESHOT,
"coulomb", info);
if (ret < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
info->irq_cc, ret);
goto out_reg;
return ret;
}
ret = request_threaded_irq(info->irq_batt, NULL, pm860x_batt_handler,
IRQF_ONESHOT, "battery", info);
ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
pm860x_batt_handler,
IRQF_ONESHOT, "battery", info);
if (ret < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
info->irq_batt, ret);
goto out_coulomb;
return ret;
}
return 0;
out_coulomb:
free_irq(info->irq_cc, info);
out_reg:
power_supply_unregister(info->battery);
return ret;
}
static int pm860x_battery_remove(struct platform_device *pdev)
{
struct pm860x_battery_info *info = platform_get_drvdata(pdev);
free_irq(info->irq_batt, info);
free_irq(info->irq_cc, info);
power_supply_unregister(info->battery);
return 0;
}
@ -1028,7 +1014,6 @@ static struct platform_driver pm860x_battery_driver = {
.pm = &pm860x_battery_pm_ops,
},
.probe = pm860x_battery_probe,
.remove = pm860x_battery_remove,
};
module_platform_driver(pm860x_battery_driver);

View file

@ -157,26 +157,25 @@ config BATTERY_SBS
Say Y to include support for SBS battery driver for SBS-compliant
gas gauges.
config BATTERY_BQ27x00
tristate "BQ27x00 battery driver"
depends on I2C || I2C=n
config BATTERY_BQ27XXX
tristate "BQ27xxx battery driver"
help
Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips.
Say Y here to enable support for batteries with BQ27xxx (I2C/HDQ) chips.
config BATTERY_BQ27X00_I2C
bool "BQ27200/BQ27500 support"
depends on BATTERY_BQ27x00
config BATTERY_BQ27XXX_I2C
bool "BQ27xxx I2C support"
depends on BATTERY_BQ27XXX
depends on I2C
default y
help
Say Y here to enable support for batteries with BQ27x00 (I2C) chips.
Say Y here to enable support for batteries with BQ27xxx (I2C) chips.
config BATTERY_BQ27X00_PLATFORM
bool "BQ27000 support"
depends on BATTERY_BQ27x00
config BATTERY_BQ27XXX_PLATFORM
bool "BQ27xxx HDQ support"
depends on BATTERY_BQ27XXX
default y
help
Say Y here to enable support for batteries with BQ27000 (HDQ) chips.
Say Y here to enable support for batteries with BQ27xxx (HDQ) chips.
config BATTERY_DA9030
tristate "DA9030 battery driver"
@ -313,7 +312,7 @@ config CHARGER_MAX8903
config CHARGER_TWL4030
tristate "OMAP TWL4030 BCI charger driver"
depends on TWL4030_CORE
depends on IIO && TWL4030_CORE
help
Say Y here to enable support for TWL4030 Battery Charge Interface.
@ -379,6 +378,18 @@ config CHARGER_MAX8998
Say Y to enable support for the battery charger control sysfs and
platform data of MAX8998/LP3974 PMICs.
config CHARGER_QCOM_SMBB
tristate "Qualcomm Switch-Mode Battery Charger and Boost"
depends on MFD_SPMI_PMIC || COMPILE_TEST
depends on OF
help
Say Y to include support for the Switch-Mode Battery Charger and
Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger
is an integrated, single-cell lithium-ion battery charger. DT
configuration is required for loading, see the devicetree
documentation for more detail. The base name for this driver is
'pm8941_charger'.
config CHARGER_BQ2415X
tristate "TI BQ2415x battery charger driver"
depends on I2C
@ -397,12 +408,13 @@ config CHARGER_BQ24190
Say Y to enable support for the TI BQ24190 battery charger.
config CHARGER_BQ24257
tristate "TI BQ24257 battery charger driver"
tristate "TI BQ24250/24251/24257 battery charger driver"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
depends on REGMAP_I2C
help
Say Y to enable support for the TI BQ24257 battery charger.
Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery
chargers.
config CHARGER_BQ24735
tristate "TI BQ24735 battery charger support"
@ -434,6 +446,13 @@ config CHARGER_TPS65090
Say Y here to enable support for battery charging with TPS65090
PMIC chips.
config CHARGER_TPS65217
tristate "TPS65217 battery charger driver"
depends on MFD_TPS65217
help
Say Y here to enable support for battery charging with TPS65217
PMIC chips.
config BATTERY_GAUGE_LTC2941
tristate "LTC2941/LTC2943 Battery Gauge Driver"
depends on I2C
@ -472,6 +491,13 @@ config CHARGER_RT9455
help
Say Y to enable support for Richtek RT9455 battery charger.
config AXP20X_POWER
tristate "AXP20x power supply driver"
depends on MFD_AXP20X
help
This driver provides support for the power supply features of
AXP20x PMIC.
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY

View file

@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o
obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o
obj-$(CONFIG_MAX8925_POWER) += max8925_power.o
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
@ -29,7 +30,7 @@ obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
@ -57,6 +58,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o
@ -65,6 +67,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o

View file

@ -0,0 +1,248 @@
/*
* AXP20x PMIC USB power supply status driver
*
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org>
*
* 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/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define DRVNAME "axp20x-usb-power-supply"
#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
#define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
#define AXP20X_VBUS_CLIMIT_MASK 3
#define AXP20X_VBUC_CLIMIT_900mA 0
#define AXP20X_VBUC_CLIMIT_500mA 1
#define AXP20X_VBUC_CLIMIT_100mA 2
#define AXP20X_VBUC_CLIMIT_NONE 3
#define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
#define AXP20X_VBUS_MON_VBUS_VALID BIT(3)
struct axp20x_usb_power {
struct regmap *regmap;
struct power_supply *supply;
};
static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
{
struct axp20x_usb_power *power = devid;
power_supply_changed(power->supply);
return IRQ_HANDLED;
}
static int axp20x_usb_power_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
unsigned int input, v;
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
if (ret)
return ret;
val->intval = AXP20X_VBUS_VHOLD_uV(v);
return 0;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = axp20x_read_variable_width(power->regmap,
AXP20X_VBUS_V_ADC_H, 12);
if (ret < 0)
return ret;
val->intval = ret * 1700; /* 1 step = 1.7 mV */
return 0;
case POWER_SUPPLY_PROP_CURRENT_MAX:
ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
if (ret)
return ret;
switch (v & AXP20X_VBUS_CLIMIT_MASK) {
case AXP20X_VBUC_CLIMIT_100mA:
val->intval = 100000;
break;
case AXP20X_VBUC_CLIMIT_500mA:
val->intval = 500000;
break;
case AXP20X_VBUC_CLIMIT_900mA:
val->intval = 900000;
break;
case AXP20X_VBUC_CLIMIT_NONE:
val->intval = -1;
break;
}
return 0;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = axp20x_read_variable_width(power->regmap,
AXP20X_VBUS_I_ADC_H, 12);
if (ret < 0)
return ret;
val->intval = ret * 375; /* 1 step = 0.375 mA */
return 0;
default:
break;
}
/* All the properties below need the input-status reg value */
ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
if (ret)
return ret;
switch (psp) {
case POWER_SUPPLY_PROP_HEALTH:
if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
break;
}
ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v);
if (ret)
return ret;
if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) {
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
break;
}
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property axp20x_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static const struct power_supply_desc axp20x_usb_power_desc = {
.name = "axp20x-usb",
.type = POWER_SUPPLY_TYPE_USB,
.properties = axp20x_usb_power_properties,
.num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
.get_property = axp20x_usb_power_get_property,
};
static int axp20x_usb_power_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {};
struct axp20x_usb_power *power;
static const char * const irq_names[] = { "VBUS_PLUGIN",
"VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" };
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;
}
power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
if (!power)
return -ENOMEM;
power->regmap = axp20x->regmap;
/* Enable vbus valid checking */
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID);
if (ret)
return ret;
/* Enable vbus voltage and current measurement */
ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
if (ret)
return ret;
psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = power;
power->supply = devm_power_supply_register(&pdev->dev,
&axp20x_usb_power_desc, &psy_cfg);
if (IS_ERR(power->supply))
return PTR_ERR(power->supply);
/* Request irqs after registering, as irqs may trigger immediately */
for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
irq = platform_get_irq_byname(pdev, irq_names[i]);
if (irq < 0) {
dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
irq_names[i], irq);
continue;
}
irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
ret = devm_request_any_context_irq(&pdev->dev, irq,
axp20x_usb_power_irq, 0, DRVNAME, power);
if (ret < 0)
dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
irq_names[i], ret);
}
return 0;
}
static const struct of_device_id axp20x_usb_power_match[] = {
{ .compatible = "x-powers,axp202-usb-power-supply" },
{ }
};
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
static struct platform_driver axp20x_usb_power_driver = {
.probe = axp20x_usb_power_probe,
.driver = {
.name = DRVNAME,
.of_match_table = axp20x_usb_power_match,
},
};
module_platform_driver(axp20x_usb_power_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
MODULE_LICENSE("GPL");

View file

@ -1704,7 +1704,7 @@ static int bq2415x_probe(struct i2c_client *client,
error_3:
bq2415x_power_supply_exit(bq);
error_2:
if (bq->notify_node)
if (bq && bq->notify_node)
of_node_put(bq->notify_node);
kfree(name);
error_1:

View file

@ -1543,5 +1543,4 @@ module_i2c_driver(bq24190_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
MODULE_ALIAS("i2c:bq24190-charger");
MODULE_DESCRIPTION("TI BQ24190 Charger Driver");

View file

@ -13,6 +13,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Datasheets:
* http://www.ti.com/product/bq24250
* http://www.ti.com/product/bq24251
* http://www.ti.com/product/bq24257
*/
#include <linux/module.h>
@ -36,18 +40,33 @@
#define BQ24257_REG_7 0x06
#define BQ24257_MANUFACTURER "Texas Instruments"
#define BQ24257_STAT_IRQ "stat"
#define BQ24257_PG_GPIO "pg"
#define BQ24257_ILIM_SET_DELAY 1000 /* msec */
/*
* When adding support for new devices make sure that enum bq2425x_chip and
* bq2425x_chip_name[] always stay in sync!
*/
enum bq2425x_chip {
BQ24250,
BQ24251,
BQ24257,
};
static const char *const bq2425x_chip_name[] = {
"bq24250",
"bq24251",
"bq24257",
};
enum bq24257_fields {
F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT, /* REG 1 */
F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE, /* REG 2 */
F_VBAT, F_USB_DET, /* REG 3 */
F_ICHG, F_ITERM, /* REG 4 */
F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */
F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_STAT, /* REG 6 */
F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_EN, F_TS_STAT, /* REG 6 */
F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM, /* REG 7 */
F_MAX_FIELDS
@ -58,6 +77,9 @@ struct bq24257_init_data {
u8 ichg; /* charge current */
u8 vbat; /* regulation voltage */
u8 iterm; /* termination current */
u8 iilimit; /* input current limit */
u8 vovp; /* over voltage protection voltage */
u8 vindpm; /* VDMP input threshold voltage */
};
struct bq24257_state {
@ -71,6 +93,8 @@ struct bq24257_device {
struct device *dev;
struct power_supply *charger;
enum bq2425x_chip chip;
struct regmap *rmap;
struct regmap_field *rmap_fields[F_MAX_FIELDS];
@ -82,6 +106,8 @@ struct bq24257_device {
struct bq24257_state state;
struct mutex lock; /* protect state data */
bool iilimit_autoset_enable;
};
static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg)
@ -135,6 +161,7 @@ static const struct reg_field bq24257_reg_fields[] = {
[F_X2_TMR_EN] = REG_FIELD(BQ24257_REG_6, 7, 7),
[F_TMR] = REG_FIELD(BQ24257_REG_6, 5, 6),
[F_SYSOFF] = REG_FIELD(BQ24257_REG_6, 4, 4),
[F_TS_EN] = REG_FIELD(BQ24257_REG_6, 3, 3),
[F_TS_STAT] = REG_FIELD(BQ24257_REG_6, 0, 2),
/* REG 7 */
[F_VOVP] = REG_FIELD(BQ24257_REG_7, 5, 7),
@ -169,6 +196,26 @@ static const u32 bq24257_iterm_map[] = {
#define BQ24257_ITERM_MAP_SIZE ARRAY_SIZE(bq24257_iterm_map)
static const u32 bq24257_iilimit_map[] = {
100000, 150000, 500000, 900000, 1500000, 2000000
};
#define BQ24257_IILIMIT_MAP_SIZE ARRAY_SIZE(bq24257_iilimit_map)
static const u32 bq24257_vovp_map[] = {
6000000, 6500000, 7000000, 8000000, 9000000, 9500000, 10000000,
10500000
};
#define BQ24257_VOVP_MAP_SIZE ARRAY_SIZE(bq24257_vovp_map)
static const u32 bq24257_vindpm_map[] = {
4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
4760000
};
#define BQ24257_VINDPM_MAP_SIZE ARRAY_SIZE(bq24257_vindpm_map)
static int bq24257_field_read(struct bq24257_device *bq,
enum bq24257_fields field_id)
{
@ -220,6 +267,47 @@ enum bq24257_fault {
FAULT_INPUT_LDO_LOW,
};
static int bq24257_get_input_current_limit(struct bq24257_device *bq,
union power_supply_propval *val)
{
int ret;
ret = bq24257_field_read(bq, F_IILIMIT);
if (ret < 0)
return ret;
/*
* The "External ILIM" and "Production & Test" modes are not exposed
* through this driver and not being covered by the lookup table.
* Should such a mode have become active let's return an error rather
* than exceeding the bounds of the lookup table and returning
* garbage.
*/
if (ret >= BQ24257_IILIMIT_MAP_SIZE)
return -ENODATA;
val->intval = bq24257_iilimit_map[ret];
return 0;
}
static int bq24257_set_input_current_limit(struct bq24257_device *bq,
const union power_supply_propval *val)
{
/*
* Address the case where the user manually sets an input current limit
* while the charger auto-detection mechanism is is active. In this
* case we want to abort and go straight to the user-specified value.
*/
if (bq->iilimit_autoset_enable)
cancel_delayed_work_sync(&bq->iilimit_setup_work);
return bq24257_field_write(bq, F_IILIMIT,
bq24257_find_idx(val->intval,
bq24257_iilimit_map,
BQ24257_IILIMIT_MAP_SIZE));
}
static int bq24257_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@ -249,6 +337,10 @@ static int bq24257_power_supply_get_property(struct power_supply *psy,
val->strval = BQ24257_MANUFACTURER;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = bq2425x_chip_name[bq->chip];
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = state.power_good;
break;
@ -300,6 +392,9 @@ static int bq24257_power_supply_get_property(struct power_supply *psy,
val->intval = bq24257_iterm_map[bq->init_data.iterm];
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return bq24257_get_input_current_limit(bq, val);
default:
return -EINVAL;
}
@ -307,6 +402,31 @@ static int bq24257_power_supply_get_property(struct power_supply *psy,
return 0;
}
static int bq24257_power_supply_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
struct bq24257_device *bq = power_supply_get_drvdata(psy);
switch (prop) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return bq24257_set_input_current_limit(bq, val);
default:
return -EINVAL;
}
}
static int bq24257_power_supply_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
return true;
default:
return false;
}
}
static int bq24257_get_chip_state(struct bq24257_device *bq,
struct bq24257_state *state)
{
@ -324,7 +444,26 @@ static int bq24257_get_chip_state(struct bq24257_device *bq,
state->fault = ret;
state->power_good = !gpiod_get_value_cansleep(bq->pg);
if (bq->pg)
state->power_good = !gpiod_get_value_cansleep(bq->pg);
else
/*
* If we have a chip without a dedicated power-good GPIO or
* some other explicit bit that would provide this information
* assume the power is good if there is no supply related
* fault - and not good otherwise. There is a possibility for
* other errors to mask that power in fact is not good but this
* is probably the best we can do here.
*/
switch (state->fault) {
case FAULT_INPUT_OVP:
case FAULT_INPUT_UVLO:
case FAULT_INPUT_LDO_LOW:
state->power_good = false;
break;
default:
state->power_good = true;
}
return 0;
}
@ -361,6 +500,28 @@ enum bq24257_in_ilimit {
IILIMIT_NONE,
};
enum bq24257_vovp {
VOVP_6000,
VOVP_6500,
VOVP_7000,
VOVP_8000,
VOVP_9000,
VOVP_9500,
VOVP_10000,
VOVP_10500
};
enum bq24257_vindpm {
VINDPM_4200,
VINDPM_4280,
VINDPM_4360,
VINDPM_4440,
VINDPM_4520,
VINDPM_4600,
VINDPM_4680,
VINDPM_4760
};
enum bq24257_port_type {
PORT_TYPE_DCP, /* Dedicated Charging Port */
PORT_TYPE_CDP, /* Charging Downstream Port */
@ -449,41 +610,43 @@ static void bq24257_handle_state_change(struct bq24257_device *bq,
{
int ret;
struct bq24257_state old_state;
bool reset_iilimit = false;
bool config_iilimit = false;
mutex_lock(&bq->lock);
old_state = bq->state;
mutex_unlock(&bq->lock);
if (!new_state->power_good) { /* power removed */
cancel_delayed_work_sync(&bq->iilimit_setup_work);
/*
* Handle BQ2425x state changes observing whether the D+/D- based input
* current limit autoset functionality is enabled.
*/
if (!new_state->power_good) {
dev_dbg(bq->dev, "Power removed\n");
if (bq->iilimit_autoset_enable) {
cancel_delayed_work_sync(&bq->iilimit_setup_work);
/* activate D+/D- port detection algorithm */
ret = bq24257_field_write(bq, F_DPDM_EN, 1);
/* activate D+/D- port detection algorithm */
ret = bq24257_field_write(bq, F_DPDM_EN, 1);
if (ret < 0)
goto error;
}
/*
* When power is removed always return to the default input
* current limit as configured during probe.
*/
ret = bq24257_field_write(bq, F_IILIMIT, bq->init_data.iilimit);
if (ret < 0)
goto error;
} else if (!old_state.power_good) {
dev_dbg(bq->dev, "Power inserted\n");
reset_iilimit = true;
} else if (!old_state.power_good) { /* power inserted */
config_iilimit = true;
} else if (new_state->fault == FAULT_NO_BAT) { /* battery removed */
cancel_delayed_work_sync(&bq->iilimit_setup_work);
reset_iilimit = true;
} else if (old_state.fault == FAULT_NO_BAT) { /* battery connected */
config_iilimit = true;
} else if (new_state->fault == FAULT_TIMER) { /* safety timer expired */
dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
}
if (reset_iilimit) {
ret = bq24257_field_write(bq, F_IILIMIT, IILIMIT_500);
if (ret < 0)
goto error;
} else if (config_iilimit) {
schedule_delayed_work(&bq->iilimit_setup_work,
if (bq->iilimit_autoset_enable)
/* configure input current limit */
schedule_delayed_work(&bq->iilimit_setup_work,
msecs_to_jiffies(BQ24257_ILIM_SET_DELAY));
} else if (new_state->fault == FAULT_NO_BAT) {
dev_warn(bq->dev, "Battery removed\n");
} else if (new_state->fault == FAULT_TIMER) {
dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
}
return;
@ -531,7 +694,9 @@ static int bq24257_hw_init(struct bq24257_device *bq)
} init_data[] = {
{F_ICHG, bq->init_data.ichg},
{F_VBAT, bq->init_data.vbat},
{F_ITERM, bq->init_data.iterm}
{F_ITERM, bq->init_data.iterm},
{F_VOVP, bq->init_data.vovp},
{F_VINDPM, bq->init_data.vindpm},
};
/*
@ -558,7 +723,16 @@ static int bq24257_hw_init(struct bq24257_device *bq)
bq->state = state;
mutex_unlock(&bq->lock);
if (!state.power_good)
if (!bq->iilimit_autoset_enable) {
dev_dbg(bq->dev, "manually setting iilimit = %u\n",
bq->init_data.iilimit);
/* program fixed input current limit */
ret = bq24257_field_write(bq, F_IILIMIT,
bq->init_data.iilimit);
if (ret < 0)
return ret;
} else if (!state.power_good)
/* activate D+/D- detection algorithm */
ret = bq24257_field_write(bq, F_DPDM_EN, 1);
else if (state.fault != FAULT_NO_BAT)
@ -569,6 +743,7 @@ static int bq24257_hw_init(struct bq24257_device *bq)
static enum power_supply_property bq24257_power_supply_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_HEALTH,
@ -577,6 +752,7 @@ static enum power_supply_property bq24257_power_supply_props[] = {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
static char *bq24257_charger_supplied_to[] = {
@ -589,6 +765,96 @@ static const struct power_supply_desc bq24257_power_supply_desc = {
.properties = bq24257_power_supply_props,
.num_properties = ARRAY_SIZE(bq24257_power_supply_props),
.get_property = bq24257_power_supply_get_property,
.set_property = bq24257_power_supply_set_property,
.property_is_writeable = bq24257_power_supply_property_is_writeable,
};
static ssize_t bq24257_show_ovp_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return scnprintf(buf, PAGE_SIZE, "%u\n",
bq24257_vovp_map[bq->init_data.vovp]);
}
static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
return scnprintf(buf, PAGE_SIZE, "%u\n",
bq24257_vindpm_map[bq->init_data.vindpm]);
}
static ssize_t bq24257_sysfs_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
int ret;
if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
ret = bq24257_field_read(bq, F_HZ_MODE);
else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
ret = bq24257_field_read(bq, F_SYSOFF);
else
return -EINVAL;
if (ret < 0)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
}
static ssize_t bq24257_sysfs_set_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq24257_device *bq = power_supply_get_drvdata(psy);
long val;
int ret;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
ret = bq24257_field_write(bq, F_HZ_MODE, (bool)val);
else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
ret = bq24257_field_write(bq, F_SYSOFF, (bool)val);
else
return -EINVAL;
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(ovp_voltage, S_IRUGO, bq24257_show_ovp_voltage, NULL);
static DEVICE_ATTR(in_dpm_voltage, S_IRUGO, bq24257_show_in_dpm_voltage, NULL);
static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
static DEVICE_ATTR(sysoff_enable, S_IWUSR | S_IRUGO,
bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
static struct attribute *bq24257_charger_attr[] = {
&dev_attr_ovp_voltage.attr,
&dev_attr_in_dpm_voltage.attr,
&dev_attr_high_impedance_enable.attr,
&dev_attr_sysoff_enable.attr,
NULL,
};
static const struct attribute_group bq24257_attr_group = {
.attrs = bq24257_charger_attr,
};
static int bq24257_power_supply_init(struct bq24257_device *bq)
@ -598,36 +864,28 @@ static int bq24257_power_supply_init(struct bq24257_device *bq)
psy_cfg.supplied_to = bq24257_charger_supplied_to;
psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
bq->charger = power_supply_register(bq->dev, &bq24257_power_supply_desc,
&psy_cfg);
if (IS_ERR(bq->charger))
return PTR_ERR(bq->charger);
bq->charger = devm_power_supply_register(bq->dev,
&bq24257_power_supply_desc,
&psy_cfg);
return 0;
return PTR_ERR_OR_ZERO(bq->charger);
}
static int bq24257_irq_probe(struct bq24257_device *bq)
static void bq24257_pg_gpio_probe(struct bq24257_device *bq)
{
struct gpio_desc *stat_irq;
bq->pg = devm_gpiod_get_optional(bq->dev, BQ24257_PG_GPIO, GPIOD_IN);
stat_irq = devm_gpiod_get_index(bq->dev, BQ24257_STAT_IRQ, 0, GPIOD_IN);
if (IS_ERR(stat_irq)) {
dev_err(bq->dev, "could not probe stat_irq pin\n");
return PTR_ERR(stat_irq);
if (PTR_ERR(bq->pg) == -EPROBE_DEFER) {
dev_info(bq->dev, "probe retry requested for PG pin\n");
return;
} else if (IS_ERR(bq->pg)) {
dev_err(bq->dev, "error probing PG pin\n");
bq->pg = NULL;
return;
}
return gpiod_to_irq(stat_irq);
}
static int bq24257_pg_gpio_probe(struct bq24257_device *bq)
{
bq->pg = devm_gpiod_get_index(bq->dev, BQ24257_PG_GPIO, 0, GPIOD_IN);
if (IS_ERR(bq->pg)) {
dev_err(bq->dev, "could not probe PG pin\n");
return PTR_ERR(bq->pg);
}
return 0;
if (bq->pg)
dev_dbg(bq->dev, "probed PG pin = %d\n", desc_to_gpio(bq->pg));
}
static int bq24257_fw_probe(struct bq24257_device *bq)
@ -635,6 +893,7 @@ static int bq24257_fw_probe(struct bq24257_device *bq)
int ret;
u32 property;
/* Required properties */
ret = device_property_read_u32(bq->dev, "ti,charge-current", &property);
if (ret < 0)
return ret;
@ -658,6 +917,43 @@ static int bq24257_fw_probe(struct bq24257_device *bq)
bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map,
BQ24257_ITERM_MAP_SIZE);
/* Optional properties. If not provided use reasonable default. */
ret = device_property_read_u32(bq->dev, "ti,current-limit",
&property);
if (ret < 0) {
bq->iilimit_autoset_enable = true;
/*
* Explicitly set a default value which will be needed for
* devices that don't support the automatic setting of the input
* current limit through the charger type detection mechanism.
*/
bq->init_data.iilimit = IILIMIT_500;
} else
bq->init_data.iilimit =
bq24257_find_idx(property,
bq24257_iilimit_map,
BQ24257_IILIMIT_MAP_SIZE);
ret = device_property_read_u32(bq->dev, "ti,ovp-voltage",
&property);
if (ret < 0)
bq->init_data.vovp = VOVP_6500;
else
bq->init_data.vovp = bq24257_find_idx(property,
bq24257_vovp_map,
BQ24257_VOVP_MAP_SIZE);
ret = device_property_read_u32(bq->dev, "ti,in-dpm-voltage",
&property);
if (ret < 0)
bq->init_data.vindpm = VINDPM_4360;
else
bq->init_data.vindpm =
bq24257_find_idx(property,
bq24257_vindpm_map,
BQ24257_VINDPM_MAP_SIZE);
return 0;
}
@ -666,6 +962,7 @@ static int bq24257_probe(struct i2c_client *client,
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct device *dev = &client->dev;
const struct acpi_device_id *acpi_id;
struct bq24257_device *bq;
int ret;
int i;
@ -682,6 +979,18 @@ static int bq24257_probe(struct i2c_client *client,
bq->client = client;
bq->dev = dev;
if (ACPI_HANDLE(dev)) {
acpi_id = acpi_match_device(dev->driver->acpi_match_table,
&client->dev);
if (!acpi_id) {
dev_err(dev, "Failed to match ACPI device\n");
return -ENODEV;
}
bq->chip = (enum bq2425x_chip)acpi_id->driver_data;
} else {
bq->chip = (enum bq2425x_chip)id->driver_data;
}
mutex_init(&bq->lock);
bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config);
@ -703,8 +1012,6 @@ static int bq24257_probe(struct i2c_client *client,
i2c_set_clientdata(client, bq);
INIT_DELAYED_WORK(&bq->iilimit_setup_work, bq24257_iilimit_setup_work);
if (!dev->platform_data) {
ret = bq24257_fw_probe(bq);
if (ret < 0) {
@ -715,10 +1022,31 @@ static int bq24257_probe(struct i2c_client *client,
return -ENODEV;
}
/* we can only check Power Good status by probing the PG pin */
ret = bq24257_pg_gpio_probe(bq);
if (ret < 0)
return ret;
/*
* The BQ24250 doesn't support the D+/D- based charger type detection
* used for the automatic setting of the input current limit setting so
* explicitly disable that feature.
*/
if (bq->chip == BQ24250)
bq->iilimit_autoset_enable = false;
if (bq->iilimit_autoset_enable)
INIT_DELAYED_WORK(&bq->iilimit_setup_work,
bq24257_iilimit_setup_work);
/*
* The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's
* not probe for it and instead use a SW-based approach to determine
* the PG state. We also use a SW-based approach for all other devices
* if the PG pin is either not defined or can't be probed.
*/
if (bq->chip != BQ24250)
bq24257_pg_gpio_probe(bq);
if (PTR_ERR(bq->pg) == -EPROBE_DEFER)
return PTR_ERR(bq->pg);
else if (!bq->pg)
dev_info(bq->dev, "using SW-based power-good detection\n");
/* reset all registers to defaults */
ret = bq24257_field_write(bq, F_RESET, 1);
@ -740,36 +1068,39 @@ static int bq24257_probe(struct i2c_client *client,
return ret;
}
if (client->irq <= 0)
client->irq = bq24257_irq_probe(bq);
if (client->irq < 0) {
dev_err(dev, "no irq resource found\n");
return client->irq;
}
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq24257_irq_handler_thread,
IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
BQ24257_STAT_IRQ, bq);
if (ret)
bq2425x_chip_name[bq->chip], bq);
if (ret) {
dev_err(dev, "Failed to request IRQ #%d\n", client->irq);
return ret;
}
ret = bq24257_power_supply_init(bq);
if (ret < 0)
if (ret < 0) {
dev_err(dev, "Failed to register power supply\n");
return ret;
}
return ret;
ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
if (ret < 0) {
dev_err(dev, "Can't create sysfs entries\n");
return ret;
}
return 0;
}
static int bq24257_remove(struct i2c_client *client)
{
struct bq24257_device *bq = i2c_get_clientdata(client);
cancel_delayed_work_sync(&bq->iilimit_setup_work);
if (bq->iilimit_autoset_enable)
cancel_delayed_work_sync(&bq->iilimit_setup_work);
power_supply_unregister(bq->charger);
sysfs_remove_group(&bq->charger->dev.kobj, &bq24257_attr_group);
bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
@ -782,7 +1113,8 @@ static int bq24257_suspend(struct device *dev)
struct bq24257_device *bq = dev_get_drvdata(dev);
int ret = 0;
cancel_delayed_work_sync(&bq->iilimit_setup_work);
if (bq->iilimit_autoset_enable)
cancel_delayed_work_sync(&bq->iilimit_setup_work);
/* reset all registers to default (and activate standalone mode) */
ret = bq24257_field_write(bq, F_RESET, 1);
@ -823,19 +1155,25 @@ static const struct dev_pm_ops bq24257_pm = {
};
static const struct i2c_device_id bq24257_i2c_ids[] = {
{ "bq24257", 0 },
{ "bq24250", BQ24250 },
{ "bq24251", BQ24251 },
{ "bq24257", BQ24257 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);
static const struct of_device_id bq24257_of_match[] = {
{ .compatible = "ti,bq24250", },
{ .compatible = "ti,bq24251", },
{ .compatible = "ti,bq24257", },
{ },
};
MODULE_DEVICE_TABLE(of, bq24257_of_match);
static const struct acpi_device_id bq24257_acpi_match[] = {
{"BQ242570", 0},
{ "BQ242500", BQ24250 },
{ "BQ242510", BQ24251 },
{ "BQ242570", BQ24257 },
{},
};
MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1581,8 +1581,10 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev)
cables = devm_kzalloc(dev, sizeof(*cables)
* chg_regs->num_cables,
GFP_KERNEL);
if (!cables)
if (!cables) {
of_node_put(child);
return ERR_PTR(-ENOMEM);
}
chg_regs->cables = cables;

View file

@ -508,23 +508,23 @@ static struct lp8727_chg_param
return param;
}
static int lp8727_parse_dt(struct device *dev)
static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
{
struct device_node *np = dev->of_node;
struct device_node *child;
struct lp8727_platform_data *pdata;
const char *type;
/* If charging parameter is not defined, just skip parsing the dt */
if (of_get_child_count(np) == 0)
goto out;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec);
/* If charging parameter is not defined, just skip parsing the dt */
if (of_get_child_count(np) == 0)
return pdata;
for_each_child_of_node(np, child) {
of_property_read_string(child, "charger-type", &type);
@ -535,29 +535,30 @@ static int lp8727_parse_dt(struct device *dev)
pdata->usb = lp8727_parse_charge_pdata(dev, child);
}
dev->platform_data = pdata;
out:
return 0;
return pdata;
}
#else
static int lp8727_parse_dt(struct device *dev)
static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
{
return 0;
return NULL;
}
#endif
static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp8727_chg *pchg;
struct lp8727_platform_data *pdata;
int ret;
if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return -EIO;
if (cl->dev.of_node) {
ret = lp8727_parse_dt(&cl->dev);
if (ret)
return ret;
pdata = lp8727_parse_dt(&cl->dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
} else {
pdata = dev_get_platdata(&cl->dev);
}
pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
@ -566,7 +567,7 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
pchg->client = cl;
pchg->dev = &cl->dev;
pchg->pdata = cl->dev.platform_data;
pchg->pdata = pdata;
i2c_set_clientdata(cl, pchg);
mutex_init(&pchg->xfer_lock);

View file

@ -909,18 +909,21 @@ static int max17042_probe(struct i2c_client *client,
regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
}
chip->battery = power_supply_register(&client->dev, max17042_desc,
&psy_cfg);
chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
&psy_cfg);
if (IS_ERR(chip->battery)) {
dev_err(&client->dev, "failed: power supply register\n");
return PTR_ERR(chip->battery);
}
if (client->irq) {
ret = request_threaded_irq(client->irq, NULL,
max17042_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
chip->battery->desc->name, chip);
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL,
max17042_thread_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
chip->battery->desc->name,
chip);
if (!ret) {
regmap_update_bits(chip->regmap, MAX17042_CONFIG,
CONFIG_ALRT_BIT_ENBL,
@ -944,16 +947,6 @@ static int max17042_probe(struct i2c_client *client,
return 0;
}
static int max17042_remove(struct i2c_client *client)
{
struct max17042_chip *chip = i2c_get_clientdata(client);
if (client->irq)
free_irq(client->irq, chip);
power_supply_unregister(chip->battery);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int max17042_suspend(struct device *dev)
{
@ -1014,7 +1007,6 @@ static struct i2c_driver max17042_i2c_driver = {
.pm = &max17042_pm_ops,
},
.probe = max17042_probe,
.remove = max17042_remove,
.id_table = max17042_id,
};
module_i2c_driver(max17042_i2c_driver);

View file

@ -201,8 +201,7 @@ static int max8903_probe(struct platform_device *pdev)
if (pdata->dc_valid == false && pdata->usb_valid == false) {
dev_err(dev, "No valid power sources.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
if (pdata->dc_valid) {
@ -216,8 +215,7 @@ static int max8903_probe(struct platform_device *pdev)
} else {
dev_err(dev, "When DC is wired, DOK and DCM should"
" be wired as well.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
} else {
if (pdata->dcm) {
@ -225,8 +223,7 @@ static int max8903_probe(struct platform_device *pdev)
gpio_set_value(pdata->dcm, 0);
else {
dev_err(dev, "Invalid pin: dcm.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
}
@ -238,8 +235,7 @@ static int max8903_probe(struct platform_device *pdev)
} else {
dev_err(dev, "When USB is wired, UOK should be wired."
"as well.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
@ -248,32 +244,28 @@ static int max8903_probe(struct platform_device *pdev)
gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
} else {
dev_err(dev, "Invalid pin: cen.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
if (pdata->chg) {
if (!gpio_is_valid(pdata->chg)) {
dev_err(dev, "Invalid pin: chg.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
if (pdata->flt) {
if (!gpio_is_valid(pdata->flt)) {
dev_err(dev, "Invalid pin: flt.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
if (pdata->usus) {
if (!gpio_is_valid(pdata->usus)) {
dev_err(dev, "Invalid pin: usus.\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
}
@ -291,85 +283,56 @@ static int max8903_probe(struct platform_device *pdev)
psy_cfg.drv_data = data;
data->psy = power_supply_register(dev, &data->psy_desc, &psy_cfg);
data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
if (IS_ERR(data->psy)) {
dev_err(dev, "failed: power supply register.\n");
ret = PTR_ERR(data->psy);
goto err;
return PTR_ERR(data->psy);
}
if (pdata->dc_valid) {
ret = request_threaded_irq(gpio_to_irq(pdata->dok),
NULL, max8903_dcin,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"MAX8903 DC IN", data);
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
NULL, max8903_dcin,
IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING,
"MAX8903 DC IN", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for DC (%d)\n",
gpio_to_irq(pdata->dok), ret);
goto err_psy;
return ret;
}
}
if (pdata->usb_valid) {
ret = request_threaded_irq(gpio_to_irq(pdata->uok),
NULL, max8903_usbin,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"MAX8903 USB IN", data);
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
NULL, max8903_usbin,
IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING,
"MAX8903 USB IN", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for USB (%d)\n",
gpio_to_irq(pdata->uok), ret);
goto err_dc_irq;
return ret;
}
}
if (pdata->flt) {
ret = request_threaded_irq(gpio_to_irq(pdata->flt),
NULL, max8903_fault,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"MAX8903 Fault", data);
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
NULL, max8903_fault,
IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING,
"MAX8903 Fault", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
gpio_to_irq(pdata->flt), ret);
goto err_usb_irq;
return ret;
}
}
return 0;
err_usb_irq:
if (pdata->usb_valid)
free_irq(gpio_to_irq(pdata->uok), data);
err_dc_irq:
if (pdata->dc_valid)
free_irq(gpio_to_irq(pdata->dok), data);
err_psy:
power_supply_unregister(data->psy);
err:
return ret;
}
static int max8903_remove(struct platform_device *pdev)
{
struct max8903_data *data = platform_get_drvdata(pdev);
if (data) {
struct max8903_pdata *pdata = &data->pdata;
if (pdata->flt)
free_irq(gpio_to_irq(pdata->flt), data);
if (pdata->usb_valid)
free_irq(gpio_to_irq(pdata->uok), data);
if (pdata->dc_valid)
free_irq(gpio_to_irq(pdata->dok), data);
power_supply_unregister(data->psy);
}
return 0;
}
static struct platform_driver max8903_driver = {
.probe = max8903_probe,
.remove = max8903_remove,
.driver = {
.name = "max8903-charger",
},

View file

@ -117,8 +117,7 @@ static int max8998_battery_probe(struct platform_device *pdev)
"EOC value not set: leave it unchanged.\n");
} else {
dev_err(max8998->dev, "Invalid EOC value\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
/* Setup Charge Restart Level */
@ -141,8 +140,7 @@ static int max8998_battery_probe(struct platform_device *pdev)
break;
default:
dev_err(max8998->dev, "Invalid Restart Level\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
/* Setup Charge Full Timeout */
@ -165,33 +163,21 @@ static int max8998_battery_probe(struct platform_device *pdev)
break;
default:
dev_err(max8998->dev, "Invalid Full Timeout value\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
psy_cfg.drv_data = max8998;
max8998->battery = power_supply_register(max8998->dev,
&max8998_battery_desc,
&psy_cfg);
max8998->battery = devm_power_supply_register(max8998->dev,
&max8998_battery_desc,
&psy_cfg);
if (IS_ERR(max8998->battery)) {
ret = PTR_ERR(max8998->battery);
dev_err(max8998->dev, "failed: power supply register: %d\n",
ret);
goto err;
return ret;
}
return 0;
err:
return ret;
}
static int max8998_battery_remove(struct platform_device *pdev)
{
struct max8998_battery_data *max8998 = platform_get_drvdata(pdev);
power_supply_unregister(max8998->battery);
return 0;
}
@ -205,7 +191,6 @@ static struct platform_driver max8998_battery_driver = {
.name = "max8998-battery",
},
.probe = max8998_battery_probe,
.remove = max8998_battery_remove,
.id_table = max8998_battery_id,
};

View file

@ -1264,5 +1264,4 @@ module_exit(pm2xxx_charger_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
MODULE_ALIAS("i2c:pm2xxx-charger");
MODULE_DESCRIPTION("PM2xxx charger management driver");

951
drivers/power/qcom_smbb.c Normal file
View file

@ -0,0 +1,951 @@
/* Copyright (c) 2014, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This driver is for the multi-block Switch-Mode Battery Charger and Boost
* (SMBB) hardware, found in Qualcomm PM8941 PMICs. The charger is an
* integrated, single-cell lithium-ion battery charger.
*
* Sub-components:
* - Charger core
* - Buck
* - DC charge-path
* - USB charge-path
* - Battery interface
* - Boost (not implemented)
* - Misc
* - HF-Buck
*/
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define SMBB_CHG_VMAX 0x040
#define SMBB_CHG_VSAFE 0x041
#define SMBB_CHG_CFG 0x043
#define SMBB_CHG_IMAX 0x044
#define SMBB_CHG_ISAFE 0x045
#define SMBB_CHG_VIN_MIN 0x047
#define SMBB_CHG_CTRL 0x049
#define CTRL_EN BIT(7)
#define SMBB_CHG_VBAT_WEAK 0x052
#define SMBB_CHG_IBAT_TERM_CHG 0x05b
#define IBAT_TERM_CHG_IEOC BIT(7)
#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
#define IBAT_TERM_CHG_IEOC_CHG 0
#define SMBB_CHG_VBAT_DET 0x05d
#define SMBB_CHG_TCHG_MAX_EN 0x060
#define TCHG_MAX_EN BIT(7)
#define SMBB_CHG_WDOG_TIME 0x062
#define SMBB_CHG_WDOG_EN 0x065
#define WDOG_EN BIT(7)
#define SMBB_BUCK_REG_MODE 0x174
#define BUCK_REG_MODE BIT(0)
#define BUCK_REG_MODE_VBAT BIT(0)
#define BUCK_REG_MODE_VSYS 0
#define SMBB_BAT_PRES_STATUS 0x208
#define PRES_STATUS_BAT_PRES BIT(7)
#define SMBB_BAT_TEMP_STATUS 0x209
#define TEMP_STATUS_OK BIT(7)
#define TEMP_STATUS_HOT BIT(6)
#define SMBB_BAT_BTC_CTRL 0x249
#define BTC_CTRL_COMP_EN BIT(7)
#define BTC_CTRL_COLD_EXT BIT(1)
#define BTC_CTRL_HOT_EXT_N BIT(0)
#define SMBB_USB_IMAX 0x344
#define SMBB_USB_ENUM_TIMER_STOP 0x34e
#define ENUM_TIMER_STOP BIT(0)
#define SMBB_USB_SEC_ACCESS 0x3d0
#define SEC_ACCESS_MAGIC 0xa5
#define SMBB_USB_REV_BST 0x3ed
#define REV_BST_CHG_GONE BIT(7)
#define SMBB_DC_IMAX 0x444
#define SMBB_MISC_REV2 0x601
#define SMBB_MISC_BOOT_DONE 0x642
#define BOOT_DONE BIT(7)
#define STATUS_USBIN_VALID BIT(0) /* USB connection is valid */
#define STATUS_DCIN_VALID BIT(1) /* DC connection is valid */
#define STATUS_BAT_HOT BIT(2) /* Battery temp 1=Hot, 0=Cold */
#define STATUS_BAT_OK BIT(3) /* Battery temp OK */
#define STATUS_BAT_PRESENT BIT(4) /* Battery is present */
#define STATUS_CHG_DONE BIT(5) /* Charge cycle is complete */
#define STATUS_CHG_TRKL BIT(6) /* Trickle charging */
#define STATUS_CHG_FAST BIT(7) /* Fast charging */
#define STATUS_CHG_GONE BIT(8) /* No charger is connected */
enum smbb_attr {
ATTR_BAT_ISAFE,
ATTR_BAT_IMAX,
ATTR_USBIN_IMAX,
ATTR_DCIN_IMAX,
ATTR_BAT_VSAFE,
ATTR_BAT_VMAX,
ATTR_BAT_VMIN,
ATTR_CHG_VDET,
ATTR_VIN_MIN,
_ATTR_CNT,
};
struct smbb_charger {
unsigned int revision;
unsigned int addr;
struct device *dev;
bool dc_disabled;
bool jeita_ext_temp;
unsigned long status;
struct mutex statlock;
unsigned int attr[_ATTR_CNT];
struct power_supply *usb_psy;
struct power_supply *dc_psy;
struct power_supply *bat_psy;
struct regmap *regmap;
};
static int smbb_vbat_weak_fn(unsigned int index)
{
return 2100000 + index * 100000;
}
static int smbb_vin_fn(unsigned int index)
{
if (index > 42)
return 5600000 + (index - 43) * 200000;
return 3400000 + index * 50000;
}
static int smbb_vmax_fn(unsigned int index)
{
return 3240000 + index * 10000;
}
static int smbb_vbat_det_fn(unsigned int index)
{
return 3240000 + index * 20000;
}
static int smbb_imax_fn(unsigned int index)
{
if (index < 2)
return 100000 + index * 50000;
return index * 100000;
}
static int smbb_bat_imax_fn(unsigned int index)
{
return index * 50000;
}
static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
{
unsigned int widx;
unsigned int sel;
for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
sel = widx;
return sel;
}
static const struct smbb_charger_attr {
const char *name;
unsigned int reg;
unsigned int safe_reg;
unsigned int max;
unsigned int min;
unsigned int fail_ok;
int (*hw_fn)(unsigned int);
} smbb_charger_attrs[] = {
[ATTR_BAT_ISAFE] = {
.name = "qcom,fast-charge-safe-current",
.reg = SMBB_CHG_ISAFE,
.max = 3000000,
.min = 200000,
.hw_fn = smbb_bat_imax_fn,
.fail_ok = 1,
},
[ATTR_BAT_IMAX] = {
.name = "qcom,fast-charge-current-limit",
.reg = SMBB_CHG_IMAX,
.safe_reg = SMBB_CHG_ISAFE,
.max = 3000000,
.min = 200000,
.hw_fn = smbb_bat_imax_fn,
},
[ATTR_DCIN_IMAX] = {
.name = "qcom,dc-current-limit",
.reg = SMBB_DC_IMAX,
.max = 2500000,
.min = 100000,
.hw_fn = smbb_imax_fn,
},
[ATTR_BAT_VSAFE] = {
.name = "qcom,fast-charge-safe-voltage",
.reg = SMBB_CHG_VSAFE,
.max = 5000000,
.min = 3240000,
.hw_fn = smbb_vmax_fn,
.fail_ok = 1,
},
[ATTR_BAT_VMAX] = {
.name = "qcom,fast-charge-high-threshold-voltage",
.reg = SMBB_CHG_VMAX,
.safe_reg = SMBB_CHG_VSAFE,
.max = 5000000,
.min = 3240000,
.hw_fn = smbb_vmax_fn,
},
[ATTR_BAT_VMIN] = {
.name = "qcom,fast-charge-low-threshold-voltage",
.reg = SMBB_CHG_VBAT_WEAK,
.max = 3600000,
.min = 2100000,
.hw_fn = smbb_vbat_weak_fn,
},
[ATTR_CHG_VDET] = {
.name = "qcom,auto-recharge-threshold-voltage",
.reg = SMBB_CHG_VBAT_DET,
.max = 5000000,
.min = 3240000,
.hw_fn = smbb_vbat_det_fn,
},
[ATTR_VIN_MIN] = {
.name = "qcom,minimum-input-voltage",
.reg = SMBB_CHG_VIN_MIN,
.max = 9600000,
.min = 4200000,
.hw_fn = smbb_vin_fn,
},
[ATTR_USBIN_IMAX] = {
.name = "usb-charge-current-limit",
.reg = SMBB_USB_IMAX,
.max = 2500000,
.min = 100000,
.hw_fn = smbb_imax_fn,
},
};
static int smbb_charger_attr_write(struct smbb_charger *chg,
enum smbb_attr which, unsigned int val)
{
const struct smbb_charger_attr *prop;
unsigned int wval;
unsigned int out;
int rc;
prop = &smbb_charger_attrs[which];
if (val > prop->max || val < prop->min) {
dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
prop->name, prop->min, prop->max);
return -EINVAL;
}
if (prop->safe_reg) {
rc = regmap_read(chg->regmap,
chg->addr + prop->safe_reg, &wval);
if (rc) {
dev_err(chg->dev,
"unable to read safe value for '%s'\n",
prop->name);
return rc;
}
wval = prop->hw_fn(wval);
if (val > wval) {
dev_warn(chg->dev,
"%s above safe value, clamping at %u\n",
prop->name, wval);
val = wval;
}
}
wval = smbb_hw_lookup(val, prop->hw_fn);
rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
if (rc) {
dev_err(chg->dev, "unable to update %s", prop->name);
return rc;
}
out = prop->hw_fn(wval);
if (out != val) {
dev_warn(chg->dev,
"%s inaccurate, rounded to %u\n",
prop->name, out);
}
dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
chg->attr[which] = out;
return 0;
}
static int smbb_charger_attr_read(struct smbb_charger *chg,
enum smbb_attr which)
{
const struct smbb_charger_attr *prop;
unsigned int val;
int rc;
prop = &smbb_charger_attrs[which];
rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
if (rc) {
dev_err(chg->dev, "failed to read %s\n", prop->name);
return rc;
}
val = prop->hw_fn(val);
dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
chg->attr[which] = val;
return 0;
}
static int smbb_charger_attr_parse(struct smbb_charger *chg,
enum smbb_attr which)
{
const struct smbb_charger_attr *prop;
unsigned int val;
int rc;
prop = &smbb_charger_attrs[which];
rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
if (rc == 0) {
rc = smbb_charger_attr_write(chg, which, val);
if (!rc || !prop->fail_ok)
return rc;
}
return smbb_charger_attr_read(chg, which);
}
static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
{
bool state;
int ret;
ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
if (ret < 0) {
dev_err(chg->dev, "failed to read irq line\n");
return;
}
mutex_lock(&chg->statlock);
if (state)
chg->status |= flag;
else
chg->status &= ~flag;
mutex_unlock(&chg->statlock);
dev_dbg(chg->dev, "status = %03lx\n", chg->status);
}
static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
power_supply_changed(chg->usb_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
if (!chg->dc_disabled)
power_supply_changed(chg->dc_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
unsigned int val;
int rc;
rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
if (rc)
return IRQ_HANDLED;
mutex_lock(&chg->statlock);
if (val & TEMP_STATUS_OK) {
chg->status |= STATUS_BAT_OK;
} else {
chg->status &= ~STATUS_BAT_OK;
if (val & TEMP_STATUS_HOT)
chg->status |= STATUS_BAT_HOT;
}
mutex_unlock(&chg->statlock);
power_supply_changed(chg->bat_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
power_supply_changed(chg->bat_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
power_supply_changed(chg->bat_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
power_supply_changed(chg->bat_psy);
power_supply_changed(chg->usb_psy);
if (!chg->dc_disabled)
power_supply_changed(chg->dc_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
power_supply_changed(chg->bat_psy);
return IRQ_HANDLED;
}
static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
{
struct smbb_charger *chg = _data;
smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
power_supply_changed(chg->bat_psy);
return IRQ_HANDLED;
}
static const struct smbb_irq {
const char *name;
irqreturn_t (*handler)(int, void *);
} smbb_charger_irqs[] = {
{ "chg-done", smbb_chg_done_handler },
{ "chg-fast", smbb_chg_fast_handler },
{ "chg-trkl", smbb_chg_trkl_handler },
{ "bat-temp-ok", smbb_bat_temp_handler },
{ "bat-present", smbb_bat_present_handler },
{ "chg-gone", smbb_chg_gone_handler },
{ "usb-valid", smbb_usb_valid_handler },
{ "dc-valid", smbb_dc_valid_handler },
};
static int smbb_usbin_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
int rc = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
mutex_lock(&chg->statlock);
val->intval = !(chg->status & STATUS_CHG_GONE) &&
(chg->status & STATUS_USBIN_VALID);
mutex_unlock(&chg->statlock);
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
val->intval = chg->attr[ATTR_USBIN_IMAX];
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
val->intval = 2500000;
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_usbin_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
int rc;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
val->intval);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_dcin_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
int rc = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
mutex_lock(&chg->statlock);
val->intval = !(chg->status & STATUS_CHG_GONE) &&
(chg->status & STATUS_DCIN_VALID);
mutex_unlock(&chg->statlock);
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
val->intval = chg->attr[ATTR_DCIN_IMAX];
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
val->intval = 2500000;
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_dcin_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
int rc;
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
val->intval);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_charger_writable_property(struct power_supply *psy,
enum power_supply_property psp)
{
return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
}
static int smbb_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
unsigned long status;
int rc = 0;
mutex_lock(&chg->statlock);
status = chg->status;
mutex_unlock(&chg->statlock);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (status & STATUS_CHG_GONE)
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (status & STATUS_CHG_DONE)
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (!(status & STATUS_BAT_OK))
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else /* everything is ok for charging, but we are not... */
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case POWER_SUPPLY_PROP_HEALTH:
if (status & STATUS_BAT_OK)
val->intval = POWER_SUPPLY_HEALTH_GOOD;
else if (status & STATUS_BAT_HOT)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
else
val->intval = POWER_SUPPLY_HEALTH_COLD;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
if (status & STATUS_CHG_FAST)
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
else if (status & STATUS_CHG_TRKL)
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
else
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = !!(status & STATUS_BAT_PRESENT);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
val->intval = chg->attr[ATTR_BAT_IMAX];
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = chg->attr[ATTR_BAT_VMAX];
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
/* this charger is a single-cell lithium-ion battery charger
* only. If you hook up some other technology, there will be
* fireworks.
*/
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = 3000000; /* single-cell li-ion low end */
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_battery_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct smbb_charger *chg = power_supply_get_drvdata(psy);
int rc;
switch (psp) {
case POWER_SUPPLY_PROP_CURRENT_MAX:
rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static int smbb_battery_writable_property(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
return 1;
default:
return 0;
}
}
static enum power_supply_property smbb_charger_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
};
static enum power_supply_property smbb_battery_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_TECHNOLOGY,
};
static const struct reg_off_mask_default {
unsigned int offset;
unsigned int mask;
unsigned int value;
unsigned int rev_mask;
} smbb_charger_setup[] = {
/* The bootloader is supposed to set this... make sure anyway. */
{ SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
/* Disable software timer */
{ SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
/* Clear and disable watchdog */
{ SMBB_CHG_WDOG_TIME, 0xff, 160 },
{ SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
/* Use charger based EoC detection */
{ SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
/* Disable GSM PA load adjustment.
* The PA signal is incorrectly connected on v2.
*/
{ SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
/* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
{ SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
/* Enable battery temperature comparators */
{ SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
/* Stop USB enumeration timer */
{ SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
#if 0 /* FIXME supposedly only to disable hardware ARB termination */
{ SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
{ SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
#endif
/* Stop USB enumeration timer, again */
{ SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
/* Enable charging */
{ SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
};
static char *smbb_bif[] = { "smbb-bif" };
static const struct power_supply_desc bat_psy_desc = {
.name = "smbb-bif",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = smbb_battery_properties,
.num_properties = ARRAY_SIZE(smbb_battery_properties),
.get_property = smbb_battery_get_property,
.set_property = smbb_battery_set_property,
.property_is_writeable = smbb_battery_writable_property,
};
static const struct power_supply_desc usb_psy_desc = {
.name = "smbb-usbin",
.type = POWER_SUPPLY_TYPE_USB,
.properties = smbb_charger_properties,
.num_properties = ARRAY_SIZE(smbb_charger_properties),
.get_property = smbb_usbin_get_property,
.set_property = smbb_usbin_set_property,
.property_is_writeable = smbb_charger_writable_property,
};
static const struct power_supply_desc dc_psy_desc = {
.name = "smbb-dcin",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = smbb_charger_properties,
.num_properties = ARRAY_SIZE(smbb_charger_properties),
.get_property = smbb_dcin_get_property,
.set_property = smbb_dcin_set_property,
.property_is_writeable = smbb_charger_writable_property,
};
static int smbb_charger_probe(struct platform_device *pdev)
{
struct power_supply_config bat_cfg = {};
struct power_supply_config usb_cfg = {};
struct power_supply_config dc_cfg = {};
struct smbb_charger *chg;
int rc, i;
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
if (!chg)
return -ENOMEM;
chg->dev = &pdev->dev;
mutex_init(&chg->statlock);
chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!chg->regmap) {
dev_err(&pdev->dev, "failed to locate regmap\n");
return -ENODEV;
}
rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
if (rc) {
dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
return rc;
}
rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
if (rc) {
dev_err(&pdev->dev, "unable to read revision\n");
return rc;
}
chg->revision += 1;
if (chg->revision != 2 && chg->revision != 3) {
dev_err(&pdev->dev, "v1 hardware not supported\n");
return -ENODEV;
}
dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
for (i = 0; i < _ATTR_CNT; ++i) {
rc = smbb_charger_attr_parse(chg, i);
if (rc) {
dev_err(&pdev->dev, "failed to parse/apply settings\n");
return rc;
}
}
bat_cfg.drv_data = chg;
bat_cfg.of_node = pdev->dev.of_node;
chg->bat_psy = devm_power_supply_register(&pdev->dev,
&bat_psy_desc,
&bat_cfg);
if (IS_ERR(chg->bat_psy)) {
dev_err(&pdev->dev, "failed to register battery\n");
return PTR_ERR(chg->bat_psy);
}
usb_cfg.drv_data = chg;
usb_cfg.supplied_to = smbb_bif;
usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
chg->usb_psy = devm_power_supply_register(&pdev->dev,
&usb_psy_desc,
&usb_cfg);
if (IS_ERR(chg->usb_psy)) {
dev_err(&pdev->dev, "failed to register USB power supply\n");
return PTR_ERR(chg->usb_psy);
}
if (!chg->dc_disabled) {
dc_cfg.drv_data = chg;
dc_cfg.supplied_to = smbb_bif;
dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
chg->dc_psy = devm_power_supply_register(&pdev->dev,
&dc_psy_desc,
&dc_cfg);
if (IS_ERR(chg->dc_psy)) {
dev_err(&pdev->dev, "failed to register DC power supply\n");
return PTR_ERR(chg->dc_psy);
}
}
for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
int irq;
irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq '%s'\n",
smbb_charger_irqs[i].name);
return irq;
}
smbb_charger_irqs[i].handler(irq, chg);
rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
smbb_charger_irqs[i].handler, IRQF_ONESHOT,
smbb_charger_irqs[i].name, chg);
if (rc) {
dev_err(&pdev->dev, "failed to request irq '%s'\n",
smbb_charger_irqs[i].name);
return rc;
}
}
chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
"qcom,jeita-extended-temp-range");
/* Set temperature range to [35%:70%] or [25%:80%] accordingly */
rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
chg->jeita_ext_temp ?
BTC_CTRL_COLD_EXT :
BTC_CTRL_HOT_EXT_N);
if (rc) {
dev_err(&pdev->dev,
"unable to set %s temperature range\n",
chg->jeita_ext_temp ? "JEITA extended" : "normal");
return rc;
}
for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
const struct reg_off_mask_default *r = &smbb_charger_setup[i];
if (r->rev_mask & BIT(chg->revision))
continue;
rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
r->mask, r->value);
if (rc) {
dev_err(&pdev->dev,
"unable to initializing charging, bailing\n");
return rc;
}
}
platform_set_drvdata(pdev, chg);
return 0;
}
static int smbb_charger_remove(struct platform_device *pdev)
{
struct smbb_charger *chg;
chg = platform_get_drvdata(pdev);
regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
return 0;
}
static const struct of_device_id smbb_charger_id_table[] = {
{ .compatible = "qcom,pm8941-charger" },
{ }
};
MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
static struct platform_driver smbb_charger_driver = {
.probe = smbb_charger_probe,
.remove = smbb_charger_remove,
.driver = {
.name = "qcom-smbb",
.of_match_table = smbb_charger_id_table,
},
};
module_platform_driver(smbb_charger_driver);
MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
MODULE_LICENSE("GPL v2");

View file

@ -15,7 +15,7 @@ config POWER_RESET_AS3722
This driver supports turning off board via a ams AS3722 power-off.
config POWER_RESET_AT91_POWEROFF
bool "Atmel AT91 poweroff driver"
tristate "Atmel AT91 poweroff driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help
@ -23,7 +23,7 @@ config POWER_RESET_AT91_POWEROFF
SoCs
config POWER_RESET_AT91_RESET
bool "Atmel AT91 reset driver"
tristate "Atmel AT91 reset driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help

View file

@ -10,6 +10,7 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
@ -48,6 +49,7 @@ static const char *shdwc_wakeup_modes[] = {
};
static void __iomem *at91_shdwc_base;
static struct clk *sclk;
static void __init at91_wakeup_status(void)
{
@ -119,9 +121,10 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
}
static int at91_poweroff_probe(struct platform_device *pdev)
static int __init at91_poweroff_probe(struct platform_device *pdev)
{
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
@ -130,6 +133,16 @@ static int at91_poweroff_probe(struct platform_device *pdev)
return PTR_ERR(at91_shdwc_base);
}
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
return PTR_ERR(sclk);
ret = clk_prepare_enable(sclk);
if (ret) {
dev_err(&pdev->dev, "Could not enable slow clock\n");
return ret;
}
at91_wakeup_status();
if (pdev->dev.of_node)
@ -140,6 +153,16 @@ static int at91_poweroff_probe(struct platform_device *pdev)
return 0;
}
static int __exit at91_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == at91_poweroff)
pm_power_off = NULL;
clk_disable_unprepare(sclk);
return 0;
}
static const struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
@ -148,10 +171,14 @@ static const struct of_device_id at91_poweroff_of_match[] = {
};
static struct platform_driver at91_poweroff_driver = {
.probe = at91_poweroff_probe,
.remove = __exit_p(at91_poweroff_remove),
.driver = {
.name = "at91-poweroff",
.of_match_table = at91_poweroff_of_match,
},
};
module_platform_driver(at91_poweroff_driver);
module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe);
MODULE_AUTHOR("Atmel Corporation");
MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs");
MODULE_LICENSE("GPL v2");

View file

@ -1,5 +1,5 @@
/*
* Atmel AT91 SAM9 SoCs reset code
* Atmel AT91 SAM9 & SAMA5 SoCs reset code
*
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) BitBox Ltd 2010
@ -11,6 +11,7 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
@ -46,6 +47,7 @@ enum reset_type {
};
static void __iomem *at91_ramc_base[2], *at91_rstc_base;
static struct clk *sclk;
/*
* unless the SDRAM is cleanly shutdown before we hit the
@ -178,11 +180,11 @@ static struct notifier_block at91_restart_nb = {
.priority = 192,
};
static int at91_reset_of_probe(struct platform_device *pdev)
static int __init at91_reset_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *np;
int idx = 0;
int ret, idx = 0;
at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
if (!at91_rstc_base) {
@ -204,57 +206,36 @@ static int at91_reset_of_probe(struct platform_device *pdev)
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
at91_restart_nb.notifier_call = match->data;
return register_restart_handler(&at91_restart_nb);
}
static int at91_reset_platform_probe(struct platform_device *pdev)
{
const struct platform_device_id *match;
struct resource *res;
int idx = 0;
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
return PTR_ERR(sclk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_rstc_base)) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_rstc_base);
}
for (idx = 0; idx < 2; idx++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!at91_ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
return -ENOMEM;
}
}
match = platform_get_device_id(pdev);
at91_restart_nb.notifier_call =
(int (*)(struct notifier_block *,
unsigned long, void *)) match->driver_data;
return register_restart_handler(&at91_restart_nb);
}
static int at91_reset_probe(struct platform_device *pdev)
{
int ret;
if (pdev->dev.of_node)
ret = at91_reset_of_probe(pdev);
else
ret = at91_reset_platform_probe(pdev);
if (ret)
ret = clk_prepare_enable(sclk);
if (ret) {
dev_err(&pdev->dev, "Could not enable slow clock\n");
return ret;
}
ret = register_restart_handler(&at91_restart_nb);
if (ret) {
clk_disable_unprepare(sclk);
return ret;
}
at91_reset_status(pdev);
return 0;
}
static int __exit at91_reset_remove(struct platform_device *pdev)
{
unregister_restart_handler(&at91_restart_nb);
clk_disable_unprepare(sclk);
return 0;
}
static const struct platform_device_id at91_reset_plat_match[] = {
{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
@ -262,11 +243,15 @@ static const struct platform_device_id at91_reset_plat_match[] = {
};
static struct platform_driver at91_reset_driver = {
.probe = at91_reset_probe,
.remove = __exit_p(at91_reset_remove),
.driver = {
.name = "at91-reset",
.of_match_table = at91_reset_of_match,
},
.id_table = at91_reset_plat_match,
};
module_platform_driver(at91_reset_driver);
module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
MODULE_AUTHOR("Atmel Corporation");
MODULE_DESCRIPTION("Reset driver for Atmel SoCs");
MODULE_LICENSE("GPL v2");

View file

@ -1760,5 +1760,4 @@ module_i2c_driver(rt9455_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
MODULE_ALIAS("i2c:rt9455-charger");
MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");

View file

@ -1332,4 +1332,3 @@ MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_DESCRIPTION("SMB347 battery charger driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:smb347");

View file

@ -353,6 +353,7 @@ static const struct of_device_id of_tps65090_charger_match[] = {
{ .compatible = "ti,tps65090-charger", },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, of_tps65090_charger_match);
static struct platform_driver tps65090_charger_driver = {
.driver = {

View file

@ -0,0 +1,264 @@
/*
* Battery charger driver for TI's tps65217
*
* Copyright (c) 2015, Collabora Ltd.
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Battery charger driver for TI's tps65217
*/
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65217.h>
#define POLL_INTERVAL (HZ * 2)
struct tps65217_charger {
struct tps65217 *tps;
struct device *dev;
struct power_supply *ac;
int ac_online;
int prev_ac_online;
struct task_struct *poll_task;
};
static enum power_supply_property tps65217_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static int tps65217_config_charger(struct tps65217_charger *charger)
{
int ret;
dev_dbg(charger->dev, "%s\n", __func__);
/*
* tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
*
* The device can be configured to support a 100k NTC (B = 3960) by
* setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
* is not recommended to do so. In sleep mode, the charger continues
* charging the battery, but all register values are reset to default
* values. Therefore, the charger would get the wrong temperature
* information. If 100k NTC setting is required, please contact the
* factory.
*
* ATTENTION, conflicting information, from p. 46
*
* NTC TYPE (for battery temperature measurement)
* 0 100k (curve 1, B = 3960)
* 1 10k (curve 2, B = 3480) (default on reset)
*
*/
ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
TPS65217_CHGCONFIG1_NTC_TYPE,
TPS65217_PROTECT_NONE);
if (ret) {
dev_err(charger->dev,
"failed to set 100k NTC setting: %d\n", ret);
return ret;
}
return 0;
}
static int tps65217_enable_charging(struct tps65217_charger *charger)
{
int ret;
/* charger already enabled */
if (charger->ac_online)
return 0;
dev_dbg(charger->dev, "%s: enable charging\n", __func__);
ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
TPS65217_CHGCONFIG1_CHG_EN,
TPS65217_CHGCONFIG1_CHG_EN,
TPS65217_PROTECT_NONE);
if (ret) {
dev_err(charger->dev,
"%s: Error in writing CHG_EN in reg 0x%x: %d\n",
__func__, TPS65217_REG_CHGCONFIG1, ret);
return ret;
}
charger->ac_online = 1;
return 0;
}
static int tps65217_ac_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct tps65217_charger *charger = power_supply_get_drvdata(psy);
if (psp == POWER_SUPPLY_PROP_ONLINE) {
val->intval = charger->ac_online;
return 0;
}
return -EINVAL;
}
static irqreturn_t tps65217_charger_irq(int irq, void *dev)
{
int ret, val;
struct tps65217_charger *charger = dev;
charger->prev_ac_online = charger->ac_online;
ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
if (ret < 0) {
dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
__func__, TPS65217_REG_STATUS);
return IRQ_HANDLED;
}
dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
/* check for AC status bit */
if (val & TPS65217_STATUS_ACPWR) {
ret = tps65217_enable_charging(charger);
if (ret) {
dev_err(charger->dev,
"failed to enable charger: %d\n", ret);
return IRQ_HANDLED;
}
} else {
charger->ac_online = 0;
}
if (charger->prev_ac_online != charger->ac_online)
power_supply_changed(charger->ac);
ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
if (ret < 0) {
dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
__func__, TPS65217_REG_CHGCONFIG0);
return IRQ_HANDLED;
}
if (val & TPS65217_CHGCONFIG0_ACTIVE)
dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
else
dev_dbg(charger->dev,
"%s: charger is NOT charging\n", __func__);
return IRQ_HANDLED;
}
static int tps65217_charger_poll_task(void *data)
{
set_freezable();
while (!kthread_should_stop()) {
schedule_timeout_interruptible(POLL_INTERVAL);
try_to_freeze();
tps65217_charger_irq(-1, data);
}
return 0;
}
static const struct power_supply_desc tps65217_charger_desc = {
.name = "tps65217-ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.get_property = tps65217_ac_get_property,
.properties = tps65217_ac_props,
.num_properties = ARRAY_SIZE(tps65217_ac_props),
};
static int tps65217_charger_probe(struct platform_device *pdev)
{
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
struct tps65217_charger *charger;
int ret;
dev_dbg(&pdev->dev, "%s\n", __func__);
charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
if (!charger)
return -ENOMEM;
charger->tps = tps;
charger->dev = &pdev->dev;
charger->ac = devm_power_supply_register(&pdev->dev,
&tps65217_charger_desc,
NULL);
if (IS_ERR(charger->ac)) {
dev_err(&pdev->dev, "failed: power supply register\n");
return PTR_ERR(charger->ac);
}
ret = tps65217_config_charger(charger);
if (ret < 0) {
dev_err(charger->dev, "charger config failed, err %d\n", ret);
return ret;
}
charger->poll_task = kthread_run(tps65217_charger_poll_task,
charger, "ktps65217charger");
if (IS_ERR(charger->poll_task)) {
ret = PTR_ERR(charger->poll_task);
dev_err(charger->dev, "Unable to run kthread err %d\n", ret);
return ret;
}
return 0;
}
static int tps65217_charger_remove(struct platform_device *pdev)
{
struct tps65217_charger *charger = platform_get_drvdata(pdev);
kthread_stop(charger->poll_task);
return 0;
}
static const struct of_device_id tps65217_charger_match_table[] = {
{ .compatible = "ti,tps65217-charger", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
static struct platform_driver tps65217_charger_driver = {
.probe = tps65217_charger_probe,
.remove = tps65217_charger_remove,
.driver = {
.name = "tps65217-charger",
.of_match_table = of_match_ptr(tps65217_charger_match_table),
},
};
module_platform_driver(tps65217_charger_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
MODULE_DESCRIPTION("TPS65217 battery charger driver");

View file

@ -22,7 +22,7 @@
#include <linux/power_supply.h>
#include <linux/notifier.h>
#include <linux/usb/otg.h>
#include <linux/i2c/twl4030-madc.h>
#include <linux/iio/consumer.h>
#define TWL4030_BCIMDEN 0x00
#define TWL4030_BCIMDKEY 0x01
@ -91,21 +91,23 @@
#define TWL4030_MSTATEC_COMPLETE1 0x0b
#define TWL4030_MSTATEC_COMPLETE4 0x0e
#if IS_REACHABLE(CONFIG_TWL4030_MADC)
/*
* If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
* then AC is available.
*/
static inline int ac_available(void)
static inline int ac_available(struct iio_channel *channel_vac)
{
return twl4030_get_madc_conversion(11) > 4500;
int val, err;
if (!channel_vac)
return 0;
err = iio_read_channel_processed(channel_vac, &val);
if (err < 0)
return 0;
return val > 4500;
}
#else
static inline int ac_available(void)
{
return 0;
}
#endif
static bool allow_usb;
module_param(allow_usb, bool, 0644);
MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
@ -128,6 +130,7 @@ struct twl4030_bci {
*/
unsigned int ichg_eoc, ichg_lo, ichg_hi;
unsigned int usb_cur, ac_cur;
struct iio_channel *channel_vac;
bool ac_is_active;
int usb_mode, ac_mode; /* charging mode requested */
#define CHARGE_OFF 0
@ -278,7 +281,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci)
* If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
* and AC is enabled, set current for 'ac'
*/
if (ac_available()) {
if (ac_available(bci->channel_vac)) {
cur = bci->ac_cur;
bci->ac_is_active = true;
} else {
@ -1048,6 +1051,12 @@ static int twl4030_bci_probe(struct platform_device *pdev)
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_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
@ -1069,7 +1078,7 @@ static int twl4030_bci_probe(struct platform_device *pdev)
TWL4030_INTERRUPTS_BCIIMR1A);
if (ret < 0) {
dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
return ret;
goto fail;
}
reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
@ -1102,6 +1111,10 @@ static int twl4030_bci_probe(struct platform_device *pdev)
twl4030_charger_enable_backup(0, 0);
return 0;
fail:
iio_channel_release(bci->channel_vac);
return ret;
}
static int __exit twl4030_bci_remove(struct platform_device *pdev)
@ -1112,6 +1125,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
twl4030_charger_enable_usb(bci, false);
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->ac->dev, &dev_attr_max_current);

View file

@ -499,7 +499,8 @@ static int wm831x_power_probe(struct platform_device *pdev)
struct wm831x_power *power;
int ret, irq, i;
power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
GFP_KERNEL);
if (power == NULL)
return -ENOMEM;
@ -536,7 +537,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
NULL);
if (IS_ERR(power->wall)) {
ret = PTR_ERR(power->wall);
goto err_kmalloc;
goto err;
}
power->usb_desc.name = power->usb_name,
@ -572,7 +573,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
IRQF_TRIGGER_RISING, "System power low",
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
power);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
@ -582,7 +583,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
IRQF_TRIGGER_RISING, "Power source",
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
power);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
@ -595,7 +596,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
platform_get_irq_byname(pdev,
wm831x_bat_irqs[i]));
ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
IRQF_TRIGGER_RISING,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
wm831x_bat_irqs[i],
power);
if (ret != 0) {
@ -626,8 +627,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
power_supply_unregister(power->usb);
err_wall:
power_supply_unregister(power->wall);
err_kmalloc:
kfree(power);
err:
return ret;
}
@ -654,7 +654,6 @@ static int wm831x_power_remove(struct platform_device *pdev)
power_supply_unregister(wm831x_power->battery);
power_supply_unregister(wm831x_power->wall);
power_supply_unregister(wm831x_power->usb);
kfree(wm831x_power);
return 0;
}

View file

@ -15,7 +15,7 @@
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/power/bq27x00_battery.h>
#include <linux/power/bq27xxx_battery.h>
#include "../w1.h"
#include "../w1_int.h"
@ -39,9 +39,10 @@ static int w1_bq27000_read(struct device *dev, unsigned int reg)
return val;
}
static struct bq27000_platform_data bq27000_battery_info = {
static struct bq27xxx_platform_data bq27000_battery_info = {
.read = w1_bq27000_read,
.name = "bq27000-battery",
.chip = BQ27000,
};
static int w1_bq27000_add_slave(struct w1_slave *sl)

View file

@ -11,6 +11,8 @@
#ifndef __LINUX_MFD_AXP20X_H
#define __LINUX_MFD_AXP20X_H
#include <linux/regmap.h>
enum {
AXP152_ID = 0,
AXP202_ID,
@ -438,4 +440,26 @@ struct axp288_extcon_pdata {
struct gpio_desc *gpio_mux_cntl;
};
/* generic helper function for reading 9-16 bit wide regs */
static inline int axp20x_read_variable_width(struct regmap *regmap,
unsigned int reg, unsigned int width)
{
unsigned int reg_val, result;
int err;
err = regmap_read(regmap, reg, &reg_val);
if (err)
return err;
result = reg_val << (width - 8);
err = regmap_read(regmap, reg + 1, &reg_val);
if (err)
return err;
result |= reg_val;
return result;
}
#endif /* __LINUX_MFD_AXP20X_H */

View file

@ -1,19 +0,0 @@
#ifndef __LINUX_BQ27X00_BATTERY_H__
#define __LINUX_BQ27X00_BATTERY_H__
/**
* struct bq27000_plaform_data - Platform data for bq27000 devices
* @name: Name of the battery. If NULL the driver will fallback to "bq27000".
* @read: HDQ read callback.
* This function should provide access to the HDQ bus the battery is
* connected to.
* The first parameter is a pointer to the battery device, the second the
* register to be read. The return value should either be the content of
* the passed register or an error value.
*/
struct bq27000_platform_data {
const char *name;
int (*read)(struct device *dev, unsigned int);
};
#endif

View file

@ -0,0 +1,31 @@
#ifndef __LINUX_BQ27X00_BATTERY_H__
#define __LINUX_BQ27X00_BATTERY_H__
/**
* struct bq27xxx_plaform_data - Platform data for bq27xxx devices
* @name: Name of the battery.
* @chip: Chip class number of this device.
* @read: HDQ read callback.
* This function should provide access to the HDQ bus the battery is
* connected to.
* The first parameter is a pointer to the battery device, the second the
* register to be read. The return value should either be the content of
* the passed register or an error value.
*/
enum bq27xxx_chip {
BQ27000 = 1, /* bq27000, bq27200 */
BQ27010, /* bq27010, bq27210 */
BQ27500, /* bq27500, bq27510, bq27520 */
BQ27530, /* bq27530, bq27531 */
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
BQ27545, /* bq27545 */
BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
};
struct bq27xxx_platform_data {
const char *name;
enum bq27xxx_chip chip;
int (*read)(struct device *dev, unsigned int);
};
#endif

View file

@ -65,7 +65,7 @@ struct charger_cable {
const char *extcon_name;
const char *name;
/* The charger-manager use Exton framework*/
/* The charger-manager use Extcon framework */
struct extcon_specific_cable_nb extcon_dev;
struct work_struct wq;
struct notifier_block nb;
@ -94,7 +94,7 @@ struct charger_cable {
* the charger will be maintained with disabled state.
* @cables:
* the array of charger cables to enable/disable charger
* and set current limit according to constratint data of
* and set current limit according to constraint data of
* struct charger_cable if only charger cable included
* in the array of charger cables is attached/detached.
* @num_cables: the number of charger cables.
@ -148,7 +148,7 @@ struct charger_regulator {
* @polling_interval_ms: interval in millisecond at which
* charger manager will monitor battery health
* @battery_present:
* Specify where information for existance of battery can be obtained
* Specify where information for existence of battery can be obtained
* @psy_charger_stat: the names of power-supply for chargers
* @num_charger_regulator: the number of entries in charger_regulators
* @charger_regulators: array of charger regulators
@ -156,7 +156,7 @@ struct charger_regulator {
* @thermal_zone : the name of thermal zone for battery
* @temp_min : Minimum battery temperature for charging.
* @temp_max : Maximum battery temperature for charging.
* @temp_diff : Temperature diffential to restart charging.
* @temp_diff : Temperature difference to restart charging.
* @measure_battery_temp:
* true: measure battery temperature
* false: measure ambient temperature