mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 06:31:52 +00:00
power supply and reset changes for the v3.18 series
- Initial support for the following chips * max77836 (charger) * max14577 (charger) * bq27742 (battery gauge) * ltc2952 (poweroff) * stih416 (restart) * syscon-reboot (restart) * gpio-restart (restart) - cleanup of power supply core - misc. fixes in power supply and reset drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJUOWD7AAoJENju1/PIO/qaj5QP/AtbG96NRpX6Ou8W+qulhSbe npTKItRMERZhV+lJUAdmLxQq8Cy6I5Cobdj140oIM6HIKZ7Dh6jChxd2t+RCLsO0 Y16VMq45L9E/mQuVCLqSSNCPKrRjbF7hW+8gpym/Yc846o3Fv8nhTQzkWxCsVrDR rNerRnrUyvKpqRpCN88u4ZuOHjm/146nZtTsAzvL8nGnQBZFJHv4eu7D5nwSN0QS FrLiI/mE7h1KFcdK5LrZf51IfEP9w4RVV3vs1wEuP0iPS5dguv2dNzXXVZa5eD/2 AYSSouVKkgdwryLAvIIaDLD405UYl5Xgt5J6d+QKXxm2+eeZMEY36+PEK5WtRtBX CykzO2BgvxyOdz2HU9D8irR1Li71jVSWwQITMJ6bQsbKEQAhltmkmsTOxtqB+ekd 254rKcKWTqllmEhg1LR8PCidf8OZNEcKzXi4XmQdQIcKNF9tvdTXP5c0/FzuIAlQ tqWZW1kQK6/TTfx+XxeeszJWY5VknIaka2Bi83pOZYtu94CtUFBdEVkpaYmC433+ qLOto7VMy+AKYViTJJDDEnSEFiLNZz/zF+SUDf+YB1RelULSF3zC8CbDcZQa5fNm 4qTU1fl2gGrIZ1jDPPihm6xP8r9WkeuQWEytG2UJZoa+l4XmzfLHBUUDVo0zqTd3 txfmKca8Y3GhXGwINi1m =sj9S -----END PGP SIGNATURE----- Merge tag 'for-v3.18' of git://git.infradead.org/battery-2.6 Pull power supply and reset updates from Sebastian Reichel: - Initial support for the following chips * max77836 (charger) * max14577 (charger) * bq27742 (battery gauge) * ltc2952 (poweroff) * stih416 (restart) * syscon-reboot (restart) * gpio-restart (restart) - cleanup of power supply core - misc fixes in power supply and reset drivers * tag 'for-v3.18' of git://git.infradead.org/battery-2.6: (48 commits) power: ab8500_fg: Fix build warning Documentation: charger: max14577: Update the date of introducing ABI power: reset: corrections for simple syscon reboot driver Documentation: power: reset: Add documentation for generic SYSCON reboot driver power: reset: Add generic SYSCON register mapped reset bq27x00_battery: Fix flag reading for bq27742 power: reset: use restart_notifier mechanism for msm-poweroff power: Add simple gpio-restart driver power: reset: st: Provide DT bindings for ST's Power Reset driver power: reset: Add restart functionality for STiH41x platforms power: charger-manager: Fix NULL pointer exception with missing cm-fuel-gauge power: max14577: Fix circular config SYSFS dependency power: gpio-charger: do not use gpio value directly power: max8925: Use of_get_child_by_name power: max8925: Fix NULL ptr dereference on memory allocation failure bq27x00_battery: Add support to bq27742 Documentation: charger: max14577: Document exported sysfs entry devicetree: mfd: max14577: Add device tree bindings document power: max17040: Add ID for MAX77836 Fuel Gauge block charger: max14577: Configure battery-dependent settings from DTS and sysfs ... Conflicts: drivers/power/reset/Kconfig drivers/power/reset/Makefile
This commit is contained in:
commit
50fa86172b
@ -18,3 +18,17 @@ Description:
|
||||
This file is writeable and can be used to set the assumed
|
||||
battery 'full level'. As batteries age, this value has to be
|
||||
amended over time.
|
||||
|
||||
What: /sys/class/power_supply/max14577-charger/device/fast_charge_timer
|
||||
Date: October 2014
|
||||
KernelVersion: 3.18.0
|
||||
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
Description:
|
||||
This entry shows and sets the maximum time the max14577
|
||||
charger operates in fast-charge mode. When the timer expires
|
||||
the device will terminate fast-charge mode (charging current
|
||||
will drop to 0 A) and will trigger interrupt.
|
||||
|
||||
Valid values:
|
||||
- 5, 6 or 7 (hours),
|
||||
- 0: disabled.
|
||||
|
54
Documentation/devicetree/bindings/gpio/gpio-restart.txt
Normal file
54
Documentation/devicetree/bindings/gpio/gpio-restart.txt
Normal file
@ -0,0 +1,54 @@
|
||||
Drive a GPIO line that can be used to restart the system from a restart
|
||||
handler.
|
||||
|
||||
This binding supports level and edge triggered reset. At driver load
|
||||
time, the driver will request the given gpio line and install a restart
|
||||
handler. If the optional properties 'open-source' is not found, the GPIO line
|
||||
will be driven in the inactive state. Otherwise its not driven until
|
||||
the restart is initiated.
|
||||
|
||||
When the system is restarted, the restart handler will be invoked in
|
||||
priority order. The gpio is configured as an output, and driven active,
|
||||
triggering a level triggered reset condition. This will also cause an
|
||||
inactive->active edge condition, triggering positive edge triggered
|
||||
reset. After a delay specified by active-delay, the GPIO is set to
|
||||
inactive, thus causing an active->inactive edge, triggering negative edge
|
||||
triggered reset. After a delay specified by inactive-delay, the GPIO
|
||||
is driven active again. After a delay specified by wait-delay, the
|
||||
restart handler completes allowing other restart handlers to be attempted.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "gpio-restart".
|
||||
- gpios : The GPIO to set high/low, see "gpios property" in
|
||||
Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be
|
||||
low to reset the board set it to "Active Low", otherwise set
|
||||
gpio to "Active High".
|
||||
|
||||
Optional properties:
|
||||
- open-source : Treat the GPIO as being open source and defer driving
|
||||
it to when the restart is initiated. If this optional property is not
|
||||
specified, the GPIO is initialized as an output in its inactive state.
|
||||
- priority : A priority ranging from 0 to 255 (default 128) according to
|
||||
the following guidelines:
|
||||
0: Restart handler of last resort, with limited restart
|
||||
capabilities
|
||||
128: Default restart handler; use if no other restart handler is
|
||||
expected to be available, and/or if restart functionality is
|
||||
sufficient to restart the entire system
|
||||
255: Highest priority restart handler, will preempt all other
|
||||
restart handlers
|
||||
- active-delay: Delay (default 100) to wait after driving gpio active [ms]
|
||||
- inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms]
|
||||
- wait-delay: Delay (default 3000) to wait after completing restart
|
||||
sequence [ms]
|
||||
|
||||
Examples:
|
||||
|
||||
gpio-restart {
|
||||
compatible = "gpio-restart";
|
||||
gpios = <&gpio 4 0>;
|
||||
priority = <128>;
|
||||
active-delay = <100>;
|
||||
inactive-delay = <100>;
|
||||
wait-delay = <3000>;
|
||||
};
|
146
Documentation/devicetree/bindings/mfd/max14577.txt
Normal file
146
Documentation/devicetree/bindings/mfd/max14577.txt
Normal file
@ -0,0 +1,146 @@
|
||||
Maxim MAX14577/77836 Multi-Function Device
|
||||
|
||||
MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+
|
||||
Battery Charger and SFOUT LDO output for powering USB devices. It is
|
||||
interfaced to host controller using I2C.
|
||||
|
||||
MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge.
|
||||
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "maxim,max14577" or "maxim,max77836".
|
||||
- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836)
|
||||
- interrupts : IRQ line for the chip.
|
||||
- interrupt-parent : The parent interrupt controller.
|
||||
|
||||
|
||||
Required nodes:
|
||||
- charger :
|
||||
Node for configuring the charger driver.
|
||||
Required properties:
|
||||
- compatible : "maxim,max14577-charger"
|
||||
or "maxim,max77836-charger"
|
||||
- maxim,fast-charge-uamp : Current in uA for Fast Charge;
|
||||
Valid values:
|
||||
- for max14577: 90000 - 950000;
|
||||
- for max77836: 45000 - 475000;
|
||||
- maxim,eoc-uamp : Current in uA for End-Of-Charge mode;
|
||||
Valid values:
|
||||
- for max14577: 50000 - 200000;
|
||||
- for max77836: 5000 - 100000;
|
||||
- maxim,ovp-uvolt : OverVoltage Protection Threshold in uV;
|
||||
In an overvoltage condition, INT asserts and charging
|
||||
stops. Valid values:
|
||||
- 6000000, 6500000, 7000000, 7500000;
|
||||
- maxim,constant-uvolt : Battery Constant Voltage in uV;
|
||||
Valid values:
|
||||
- 4000000 - 4280000 (step by 20000);
|
||||
- 4350000;
|
||||
|
||||
|
||||
Optional nodes:
|
||||
- max14577-muic/max77836-muic :
|
||||
Node used only by extcon consumers.
|
||||
Required properties:
|
||||
- compatible : "maxim,max14577-muic" or "maxim,max77836-muic"
|
||||
|
||||
- regulators :
|
||||
Required properties:
|
||||
- compatible : "maxim,max14577-regulator"
|
||||
or "maxim,max77836-regulator"
|
||||
|
||||
May contain a sub-node per regulator from the list below. Each
|
||||
sub-node should contain the constraints and initialization information
|
||||
for that regulator. See regulator.txt for a description of standard
|
||||
properties for these sub-nodes.
|
||||
|
||||
List of valid regulator names:
|
||||
- for max14577: CHARGER, SAFEOUT.
|
||||
- for max77836: CHARGER, SAFEOUT, LDO1, LDO2.
|
||||
|
||||
The SAFEOUT is a fixed voltage regulator so there is no need to specify
|
||||
voltages for it.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
max14577@25 {
|
||||
compatible = "maxim,max14577";
|
||||
reg = <0x25>;
|
||||
interrupt-parent = <&gpx1>;
|
||||
interrupts = <5 IRQ_TYPE_NONE>;
|
||||
|
||||
muic: max14577-muic {
|
||||
compatible = "maxim,max14577-muic";
|
||||
};
|
||||
|
||||
regulators {
|
||||
compatible = "maxim,max14577-regulator";
|
||||
|
||||
SAFEOUT {
|
||||
regulator-name = "SAFEOUT";
|
||||
};
|
||||
CHARGER {
|
||||
regulator-name = "CHARGER";
|
||||
regulator-min-microamp = <90000>;
|
||||
regulator-max-microamp = <950000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
|
||||
charger {
|
||||
compatible = "maxim,max14577-charger";
|
||||
|
||||
maxim,constant-uvolt = <4350000>;
|
||||
maxim,fast-charge-uamp = <450000>;
|
||||
maxim,eoc-uamp = <50000>;
|
||||
maxim,ovp-uvolt = <6500000>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
max77836@25 {
|
||||
compatible = "maxim,max77836";
|
||||
reg = <0x25>;
|
||||
interrupt-parent = <&gpx1>;
|
||||
interrupts = <5 IRQ_TYPE_NONE>;
|
||||
|
||||
muic: max77836-muic {
|
||||
compatible = "maxim,max77836-muic";
|
||||
};
|
||||
|
||||
regulators {
|
||||
compatible = "maxim,max77836-regulator";
|
||||
|
||||
SAFEOUT {
|
||||
regulator-name = "SAFEOUT";
|
||||
};
|
||||
CHARGER {
|
||||
regulator-name = "CHARGER";
|
||||
regulator-min-microamp = <90000>;
|
||||
regulator-max-microamp = <950000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
LDO1 {
|
||||
regulator-name = "LDO1";
|
||||
regulator-min-microvolt = <2700000>;
|
||||
regulator-max-microvolt = <2700000>;
|
||||
};
|
||||
LDO2 {
|
||||
regulator-name = "LDO2";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3950000>;
|
||||
};
|
||||
};
|
||||
|
||||
charger {
|
||||
compatible = "maxim,max77836-charger";
|
||||
|
||||
maxim,constant-uvolt = <4350000>;
|
||||
maxim,fast-charge-uamp = <225000>;
|
||||
maxim,eoc-uamp = <7500>;
|
||||
maxim,ovp-uvolt = <6500000>;
|
||||
};
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
Binding for the LTC2952 PowerPath controller
|
||||
|
||||
This chip is used to externally trigger a system shut down. Once the trigger has
|
||||
been sent, the chips' watchdog has to be reset to gracefully shut down.
|
||||
If the Linux systems decides to shut down it powers off the platform via the
|
||||
kill signal.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must contain: "lltc,ltc2952"
|
||||
- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the
|
||||
chip's trigger line
|
||||
- watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the
|
||||
chip's watchdog line
|
||||
- kill-gpios: phandle + gpio-specifier for the GPIO connected to the
|
||||
chip's kill line
|
||||
|
||||
Example:
|
||||
|
||||
ltc2952 {
|
||||
compatible = "lltc,ltc2952";
|
||||
|
||||
trigger-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
watchdog-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
kill-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
|
||||
};
|
11
Documentation/devicetree/bindings/power/reset/st-reset.txt
Normal file
11
Documentation/devicetree/bindings/power/reset/st-reset.txt
Normal file
@ -0,0 +1,11 @@
|
||||
*Device-Tree bindings for ST SW reset functionality
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "st,<chip>-restart".
|
||||
- st,syscfg: should be a phandle of the syscfg node.
|
||||
|
||||
Example node:
|
||||
restart {
|
||||
compatible = "st,stih416-restart";
|
||||
st,syscfg = <&syscfg_sbc>;
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
Generic SYSCON mapped register reset driver
|
||||
|
||||
This is a generic reset driver using syscon to map the reset register.
|
||||
The reset is generally performed with a write to the reset register
|
||||
defined by the register map pointed by syscon reference plus the offset
|
||||
with the mask defined in the reboot node.
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain "syscon-reboot"
|
||||
- regmap: this is phandle to the register map node
|
||||
- offset: offset in the register map for the reboot register (in bytes)
|
||||
- mask: the reset value written to the reboot register (32 bit access)
|
||||
|
||||
Default will be little endian mode, 32 bit access only.
|
||||
|
||||
Examples:
|
||||
|
||||
reboot {
|
||||
compatible = "syscon-reboot";
|
||||
regmap = <®mapnode>;
|
||||
offset = <0x0>;
|
||||
mask = <0x1>;
|
||||
};
|
@ -29,7 +29,7 @@ Charger Manager supports the following:
|
||||
While the battery is being charged and the system is in suspend-to-RAM,
|
||||
we may need to monitor the battery health by looking at the ambient or
|
||||
battery temperature. We can accomplish this by waking up the system
|
||||
periodically. However, such a method wakes up devices unncessary for
|
||||
periodically. However, such a method wakes up devices unnecessarily for
|
||||
monitoring the battery health and tasks, and user processes that are
|
||||
supposed to be kept suspended. That, in turn, incurs unnecessary power
|
||||
consumption and slow down charging process. Or even, such peak power
|
||||
|
@ -101,6 +101,10 @@ VOLTAGE_MAX, VOLTAGE_MIN - same as _DESIGN voltage values except that
|
||||
these ones should be used if hardware could only guess (measure and
|
||||
retain) the thresholds of a given power supply.
|
||||
|
||||
VOLTAGE_BOOT - Reports the voltage measured during boot
|
||||
|
||||
CURRENT_BOOT - Reports the current measured during boot
|
||||
|
||||
CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
|
||||
battery considered full/empty.
|
||||
|
||||
@ -123,6 +127,8 @@ the current drawn from a charging source.
|
||||
CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge
|
||||
condition.
|
||||
|
||||
CALIBRATE - battery or coulomb counter calibration status
|
||||
|
||||
CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
|
||||
CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
|
||||
power supply object.
|
||||
|
@ -26,6 +26,87 @@
|
||||
#include <linux/mfd/max14577.h>
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
|
||||
/*
|
||||
* Table of valid charger currents for different Maxim chipsets.
|
||||
* It is placed here because it is used by both charger and regulator driver.
|
||||
*/
|
||||
const struct maxim_charger_current maxim_charger_currents[] = {
|
||||
[MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
|
||||
[MAXIM_DEVICE_TYPE_MAX14577] = {
|
||||
.min = MAX14577_CHARGER_CURRENT_LIMIT_MIN,
|
||||
.high_start = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START,
|
||||
.high_step = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP,
|
||||
.max = MAX14577_CHARGER_CURRENT_LIMIT_MAX,
|
||||
},
|
||||
[MAXIM_DEVICE_TYPE_MAX77836] = {
|
||||
.min = MAX77836_CHARGER_CURRENT_LIMIT_MIN,
|
||||
.high_start = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START,
|
||||
.high_step = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP,
|
||||
.max = MAX77836_CHARGER_CURRENT_LIMIT_MAX,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(maxim_charger_currents);
|
||||
|
||||
/*
|
||||
* maxim_charger_calc_reg_current - Calculate register value for current
|
||||
* @limits: constraints for charger, matching the MBCICHWRC register
|
||||
* @min_ua: minimal requested current, micro Amps
|
||||
* @max_ua: maximum requested current, micro Amps
|
||||
* @dst: destination to store calculated register value
|
||||
*
|
||||
* Calculates the value of MBCICHWRC (Fast Battery Charge Current) register
|
||||
* for given current and stores it under pointed 'dst'. The stored value
|
||||
* combines low bit (MBCICHWRCL) and high bits (MBCICHWRCH). It is also
|
||||
* properly shifted.
|
||||
*
|
||||
* The calculated register value matches the current which:
|
||||
* - is always between <limits.min, limits.max>;
|
||||
* - is always less or equal to max_ua;
|
||||
* - is the highest possible value;
|
||||
* - may be lower than min_ua.
|
||||
*
|
||||
* On success returns 0. On error returns -EINVAL (requested min/max current
|
||||
* is outside of given charger limits) and 'dst' is not set.
|
||||
*/
|
||||
int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
|
||||
unsigned int min_ua, unsigned int max_ua, u8 *dst)
|
||||
{
|
||||
unsigned int current_bits = 0xf;
|
||||
|
||||
if (min_ua > max_ua)
|
||||
return -EINVAL;
|
||||
|
||||
if (min_ua > limits->max || max_ua < limits->min)
|
||||
return -EINVAL;
|
||||
|
||||
if (max_ua < limits->high_start) {
|
||||
/*
|
||||
* Less than high_start, so set the minimal current
|
||||
* (turn Low Bit off, 0 as high bits).
|
||||
*/
|
||||
*dst = 0x0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* max_ua is in range: <high_start, infinite>, cut it to limits.max */
|
||||
max_ua = min(limits->max, max_ua);
|
||||
max_ua -= limits->high_start;
|
||||
/*
|
||||
* There is no risk of overflow 'max_ua' here because:
|
||||
* - max_ua >= limits.high_start
|
||||
* - BUILD_BUG checks that 'limits' are: max >= high_start + high_step
|
||||
*/
|
||||
current_bits = max_ua / limits->high_step;
|
||||
|
||||
/* Turn Low Bit on (use range <limits.high_start, limits.max>) ... */
|
||||
*dst = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||
/* and set proper High Bits */
|
||||
*dst |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(maxim_charger_calc_reg_current);
|
||||
|
||||
static const struct mfd_cell max14577_devs[] = {
|
||||
{
|
||||
.name = "max14577-muic",
|
||||
@ -35,7 +116,10 @@ static const struct mfd_cell max14577_devs[] = {
|
||||
.name = "max14577-regulator",
|
||||
.of_compatible = "maxim,max14577-regulator",
|
||||
},
|
||||
{ .name = "max14577-charger", },
|
||||
{
|
||||
.name = "max14577-charger",
|
||||
.of_compatible = "maxim,max14577-charger",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mfd_cell max77836_devs[] = {
|
||||
@ -463,6 +547,20 @@ static int __init max14577_i2c_init(void)
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
|
||||
|
||||
/* Valid charger current values must be provided for each chipset */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
|
||||
|
||||
/* Check for valid values for charger */
|
||||
BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START +
|
||||
MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||
MAX14577_CHARGER_CURRENT_LIMIT_MAX);
|
||||
BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
|
||||
|
||||
BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START +
|
||||
MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||
MAX77836_CHARGER_CURRENT_LIMIT_MAX);
|
||||
BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
|
||||
|
||||
return i2c_add_driver(&max14577_i2c_driver);
|
||||
}
|
||||
subsys_initcall(max14577_i2c_init);
|
||||
|
@ -325,11 +325,12 @@ config CHARGER_MANAGER
|
||||
with help of suspend_again support.
|
||||
|
||||
config CHARGER_MAX14577
|
||||
tristate "Maxim MAX14577 MUIC battery charger driver"
|
||||
tristate "Maxim MAX14577/77836 battery charger driver"
|
||||
depends on MFD_MAX14577
|
||||
depends on SYSFS
|
||||
help
|
||||
Say Y to enable support for the battery charger control sysfs and
|
||||
platform data of MAX14577 MUICs.
|
||||
platform data of MAX14577/77836 MUICs.
|
||||
|
||||
config CHARGER_MAX8997
|
||||
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
|
||||
|
@ -2969,7 +2969,7 @@ static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
|
||||
|
||||
static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
|
||||
{
|
||||
unsigned int i, j;
|
||||
unsigned int i;
|
||||
struct power_supply *psy = dev_get_drvdata(dev);
|
||||
struct ab8500_fg *di;
|
||||
|
||||
@ -2978,14 +2978,15 @@ static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev)
|
||||
if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
|
||||
abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
|
||||
|| is_ab8540(di->parent)) {
|
||||
for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
|
||||
if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j]))
|
||||
for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
|
||||
if (device_create_file(dev,
|
||||
&ab8505_fg_sysfs_psy_attrs[i]))
|
||||
goto sysfs_psy_create_attrs_failed_ab8505;
|
||||
}
|
||||
return 0;
|
||||
sysfs_psy_create_attrs_failed_ab8505:
|
||||
dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
|
||||
while (j--)
|
||||
while (i--)
|
||||
device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
|
||||
|
||||
return -EIO;
|
||||
|
@ -23,6 +23,7 @@
|
||||
* http://focus.ti.com/docs/prod/folders/print/bq27000.html
|
||||
* http://focus.ti.com/docs/prod/folders/print/bq27500.html
|
||||
* http://www.ti.com/product/bq27425-g1
|
||||
* http://www.ti.com/product/BQ27742-G1
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
@ -71,6 +72,8 @@
|
||||
#define BQ27500_FLAG_FC BIT(9)
|
||||
#define BQ27500_FLAG_OTC BIT(15)
|
||||
|
||||
#define BQ27742_POWER_AVG 0x76
|
||||
|
||||
/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
|
||||
#define BQ27425_REG_OFFSET 0x04
|
||||
#define BQ27425_REG_SOC 0x18 /* Register address plus offset */
|
||||
@ -83,7 +86,7 @@ struct bq27x00_access_methods {
|
||||
int (*read)(struct bq27x00_device_info *di, u8 reg, bool single);
|
||||
};
|
||||
|
||||
enum bq27x00_chip { BQ27000, BQ27500, BQ27425};
|
||||
enum bq27x00_chip { BQ27000, BQ27500, BQ27425, BQ27742};
|
||||
|
||||
struct bq27x00_reg_cache {
|
||||
int temperature;
|
||||
@ -152,6 +155,24 @@ static enum power_supply_property bq27425_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27742_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
};
|
||||
|
||||
static unsigned int poll_interval = 360;
|
||||
module_param(poll_interval, uint, 0644);
|
||||
MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \
|
||||
@ -176,7 +197,7 @@ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
|
||||
*/
|
||||
static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di)
|
||||
{
|
||||
if (di->chip == BQ27425 || di->chip == BQ27500)
|
||||
if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -189,7 +210,7 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
|
||||
{
|
||||
int rsoc;
|
||||
|
||||
if (di->chip == BQ27500)
|
||||
if (di->chip == BQ27500 || di->chip == BQ27742)
|
||||
rsoc = bq27x00_read(di, BQ27500_REG_SOC, false);
|
||||
else if (di->chip == BQ27425)
|
||||
rsoc = bq27x00_read(di, BQ27425_REG_SOC, false);
|
||||
@ -233,9 +254,11 @@ static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
|
||||
{
|
||||
int flags;
|
||||
bool is_bq27500 = di->chip == BQ27500;
|
||||
bool is_bq27742 = di->chip == BQ27742;
|
||||
bool is_higher = bq27xxx_is_chip_version_higher(di);
|
||||
bool flags_1b = !(is_bq27500 || is_bq27742);
|
||||
|
||||
flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
|
||||
flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b);
|
||||
if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
|
||||
return -ENODATA;
|
||||
|
||||
@ -414,13 +437,15 @@ static void bq27x00_update(struct bq27x00_device_info *di)
|
||||
struct bq27x00_reg_cache cache = {0, };
|
||||
bool is_bq27500 = di->chip == BQ27500;
|
||||
bool is_bq27425 = di->chip == BQ27425;
|
||||
bool is_bq27742 = di->chip == BQ27742;
|
||||
bool flags_1b = !(is_bq27500 || is_bq27742);
|
||||
|
||||
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
|
||||
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b);
|
||||
if ((cache.flags & 0xff) == 0xff)
|
||||
/* read error */
|
||||
cache.flags = -1;
|
||||
if (cache.flags >= 0) {
|
||||
if (!is_bq27500 && !is_bq27425
|
||||
if (!is_bq27500 && !is_bq27425 && !is_bq27742
|
||||
&& (cache.flags & BQ27000_FLAG_CI)) {
|
||||
dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
|
||||
cache.capacity = -ENODATA;
|
||||
@ -432,7 +457,11 @@ static void bq27x00_update(struct bq27x00_device_info *di)
|
||||
cache.health = -ENODATA;
|
||||
} else {
|
||||
cache.capacity = bq27x00_battery_read_rsoc(di);
|
||||
if (!is_bq27425) {
|
||||
if (is_bq27742)
|
||||
cache.time_to_empty =
|
||||
bq27x00_battery_read_time(di,
|
||||
BQ27x00_REG_TTE);
|
||||
else if (!is_bq27425) {
|
||||
cache.energy = bq27x00_battery_read_energy(di);
|
||||
cache.time_to_empty =
|
||||
bq27x00_battery_read_time(di,
|
||||
@ -450,8 +479,14 @@ static void bq27x00_update(struct bq27x00_device_info *di)
|
||||
cache.temperature = bq27x00_battery_read_temperature(di);
|
||||
if (!is_bq27425)
|
||||
cache.cycle_count = bq27x00_battery_read_cyct(di);
|
||||
cache.power_avg =
|
||||
bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
|
||||
if (is_bq27742)
|
||||
cache.power_avg =
|
||||
bq27x00_battery_read_pwr_avg(di,
|
||||
BQ27742_POWER_AVG);
|
||||
else
|
||||
cache.power_avg =
|
||||
bq27x00_battery_read_pwr_avg(di,
|
||||
BQ27x00_POWER_AVG);
|
||||
|
||||
/* We only have to read charge design full once */
|
||||
if (di->charge_design_full <= 0)
|
||||
@ -702,6 +737,9 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
|
||||
if (di->chip == BQ27425) {
|
||||
di->bat.properties = bq27425_battery_props;
|
||||
di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
|
||||
} else if (di->chip == BQ27742) {
|
||||
di->bat.properties = bq27742_battery_props;
|
||||
di->bat.num_properties = ARRAY_SIZE(bq27742_battery_props);
|
||||
} else {
|
||||
di->bat.properties = bq27x00_battery_props;
|
||||
di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);
|
||||
@ -858,6 +896,7 @@ static const struct i2c_device_id bq27x00_id[] = {
|
||||
{ "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
|
||||
{ "bq27500", BQ27500 },
|
||||
{ "bq27425", BQ27425 },
|
||||
{ "bq27742", BQ27742 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq27x00_id);
|
||||
|
@ -1656,7 +1656,7 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
|
||||
{
|
||||
if (pdev->dev.of_node)
|
||||
return of_cm_parse_desc(&pdev->dev);
|
||||
return (struct charger_desc *)dev_get_platdata(&pdev->dev);
|
||||
return dev_get_platdata(&pdev->dev);
|
||||
}
|
||||
|
||||
static int charger_manager_probe(struct platform_device *pdev)
|
||||
@ -1677,7 +1677,7 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!desc) {
|
||||
if (IS_ERR(desc)) {
|
||||
dev_err(&pdev->dev, "No platform data (desc) found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -1720,6 +1720,11 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!desc->psy_fuel_gauge) {
|
||||
dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Counting index only */
|
||||
while (desc->psy_charger_stat[i])
|
||||
i++;
|
||||
@ -1839,6 +1844,13 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
device_set_wakeup_capable(&pdev->dev, false);
|
||||
|
||||
/*
|
||||
* Charger-manager have to check the charging state right after
|
||||
* tialization of charger-manager and then update current charging
|
||||
* state.
|
||||
*/
|
||||
cm_monitor();
|
||||
|
||||
schedule_work(&setup_polling);
|
||||
|
||||
return 0;
|
||||
|
@ -55,7 +55,7 @@ static int gpio_charger_get_property(struct power_supply *psy,
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = gpio_get_value_cansleep(pdata->gpio);
|
||||
val->intval = !!gpio_get_value_cansleep(pdata->gpio);
|
||||
val->intval ^= pdata->gpio_active_low;
|
||||
break;
|
||||
default:
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Battery charger driver for the Maxim 14577
|
||||
* max14577_charger.c - Battery charger driver for the Maxim 14577/77836
|
||||
*
|
||||
* Copyright (C) 2013 Samsung Electronics
|
||||
* Copyright (C) 2013,2014 Samsung Electronics
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -19,16 +19,44 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
#include <linux/mfd/max14577.h>
|
||||
|
||||
struct max14577_charger {
|
||||
struct device *dev;
|
||||
struct max14577 *max14577;
|
||||
struct power_supply charger;
|
||||
|
||||
unsigned int charging_state;
|
||||
unsigned int battery_state;
|
||||
unsigned int charging_state;
|
||||
unsigned int battery_state;
|
||||
|
||||
struct max14577_charger_platform_data *pdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function for mapping values of STATUS2/CHGTYP register on max14577
|
||||
* and max77836 chipsets to enum maxim_muic_charger_type.
|
||||
*/
|
||||
static enum max14577_muic_charger_type maxim_get_charger_type(
|
||||
enum maxim_device_type dev_type, u8 val) {
|
||||
switch (val) {
|
||||
case MAX14577_CHARGER_TYPE_NONE:
|
||||
case MAX14577_CHARGER_TYPE_USB:
|
||||
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
|
||||
return val;
|
||||
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
|
||||
case MAX14577_CHARGER_TYPE_RESERVED:
|
||||
if (dev_type == MAXIM_DEVICE_TYPE_MAX77836)
|
||||
val |= 0x8;
|
||||
return val;
|
||||
default:
|
||||
WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
static int max14577_get_charger_state(struct max14577_charger *chg)
|
||||
{
|
||||
struct regmap *rmap = chg->max14577->regmap;
|
||||
@ -89,19 +117,23 @@ static int max14577_get_online(struct max14577_charger *chg)
|
||||
{
|
||||
struct regmap *rmap = chg->max14577->regmap;
|
||||
u8 reg_data;
|
||||
enum max14577_muic_charger_type chg_type;
|
||||
|
||||
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data);
|
||||
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
|
||||
switch (reg_data) {
|
||||
chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
|
||||
switch (chg_type) {
|
||||
case MAX14577_CHARGER_TYPE_USB:
|
||||
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
|
||||
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
|
||||
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
|
||||
case MAX77836_CHARGER_TYPE_SPECIAL_BIAS:
|
||||
return 1;
|
||||
case MAX14577_CHARGER_TYPE_NONE:
|
||||
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
case MAX14577_CHARGER_TYPE_RESERVED:
|
||||
case MAX77836_CHARGER_TYPE_RESERVED:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -118,10 +150,12 @@ static int max14577_get_battery_health(struct max14577_charger *chg)
|
||||
struct regmap *rmap = chg->max14577->regmap;
|
||||
int state = POWER_SUPPLY_HEALTH_GOOD;
|
||||
u8 reg_data;
|
||||
enum max14577_muic_charger_type chg_type;
|
||||
|
||||
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data);
|
||||
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
|
||||
if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
|
||||
chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
|
||||
if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
|
||||
state = POWER_SUPPLY_HEALTH_DEAD;
|
||||
goto state_set;
|
||||
}
|
||||
@ -147,15 +181,131 @@ static int max14577_get_present(struct max14577_charger *chg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int max14577_set_fast_charge_timer(struct max14577_charger *chg,
|
||||
unsigned long hours)
|
||||
{
|
||||
u8 reg_data;
|
||||
|
||||
switch (hours) {
|
||||
case 5 ... 7:
|
||||
reg_data = hours - 3;
|
||||
break;
|
||||
case 0:
|
||||
/* Disable */
|
||||
reg_data = 0x7;
|
||||
break;
|
||||
default:
|
||||
dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n",
|
||||
hours);
|
||||
return -EINVAL;
|
||||
}
|
||||
reg_data <<= CHGCTRL1_TCHW_SHIFT;
|
||||
|
||||
return max14577_update_reg(chg->max14577->regmap,
|
||||
MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data);
|
||||
}
|
||||
|
||||
static int max14577_init_constant_voltage(struct max14577_charger *chg,
|
||||
unsigned int uvolt)
|
||||
{
|
||||
u8 reg_data;
|
||||
|
||||
if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN ||
|
||||
uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (uvolt == 4200000)
|
||||
reg_data = 0x0;
|
||||
else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
|
||||
reg_data = 0x1f;
|
||||
else if (uvolt <= 4280000) {
|
||||
unsigned int val = uvolt;
|
||||
|
||||
val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN;
|
||||
val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP;
|
||||
if (uvolt <= 4180000)
|
||||
reg_data = 0x1 + val;
|
||||
else
|
||||
reg_data = val; /* Fix for gap between 4.18V and 4.22V */
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT;
|
||||
|
||||
return max14577_write_reg(chg->max14577->regmap,
|
||||
MAX14577_CHG_REG_CHG_CTRL3, reg_data);
|
||||
}
|
||||
|
||||
static int max14577_init_eoc(struct max14577_charger *chg,
|
||||
unsigned int uamp)
|
||||
{
|
||||
unsigned int current_bits = 0xf;
|
||||
u8 reg_data;
|
||||
|
||||
switch (chg->max14577->dev_type) {
|
||||
case MAXIM_DEVICE_TYPE_MAX77836:
|
||||
if (uamp < 5000)
|
||||
return -EINVAL; /* Requested current is too low */
|
||||
|
||||
if (uamp >= 7500 && uamp < 10000)
|
||||
current_bits = 0x0;
|
||||
else if (uamp <= 50000) {
|
||||
/* <5000, 7499> and <10000, 50000> */
|
||||
current_bits = uamp / 5000;
|
||||
} else {
|
||||
uamp = min(uamp, 100000U) - 50000U;
|
||||
current_bits = 0xa + uamp / 10000;
|
||||
}
|
||||
break;
|
||||
|
||||
case MAXIM_DEVICE_TYPE_MAX14577:
|
||||
default:
|
||||
if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN)
|
||||
return -EINVAL; /* Requested current is too low */
|
||||
|
||||
uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
|
||||
uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN;
|
||||
current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP;
|
||||
break;
|
||||
}
|
||||
|
||||
reg_data = current_bits << CHGCTRL5_EOCS_SHIFT;
|
||||
|
||||
return max14577_update_reg(chg->max14577->regmap,
|
||||
MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK,
|
||||
reg_data);
|
||||
}
|
||||
|
||||
static int max14577_init_fast_charge(struct max14577_charger *chg,
|
||||
unsigned int uamp)
|
||||
{
|
||||
u8 reg_data;
|
||||
int ret;
|
||||
const struct maxim_charger_current *limits =
|
||||
&maxim_charger_currents[chg->max14577->dev_type];
|
||||
|
||||
ret = maxim_charger_calc_reg_current(limits, uamp, uamp, ®_data);
|
||||
if (ret) {
|
||||
dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return max14577_update_reg(chg->max14577->regmap,
|
||||
MAX14577_CHG_REG_CHG_CTRL4,
|
||||
CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
|
||||
reg_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets charger registers to proper and safe default values.
|
||||
* Some of these values are equal to defaults in MAX14577E
|
||||
* data sheet but there are minor differences.
|
||||
*/
|
||||
static void max14577_charger_reg_init(struct max14577_charger *chg)
|
||||
static int max14577_charger_reg_init(struct max14577_charger *chg)
|
||||
{
|
||||
struct regmap *rmap = chg->max14577->regmap;
|
||||
u8 reg_data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
|
||||
@ -167,10 +317,6 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
|
||||
CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
|
||||
reg_data);
|
||||
|
||||
/* Battery Fast-Charge Timer, from SM-V700: 6hrs */
|
||||
reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data);
|
||||
|
||||
/*
|
||||
* Wall-Adapter Rapid Charge, default on
|
||||
* Battery-Charger, default on
|
||||
@ -179,29 +325,46 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
|
||||
reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
|
||||
|
||||
/* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */
|
||||
reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data);
|
||||
|
||||
/*
|
||||
* Fast Battery-Charge Current Low, default 200-950mA
|
||||
* Fast Battery-Charge Current High, from SM-V700: 450mA
|
||||
*/
|
||||
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||
reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data);
|
||||
|
||||
/* End-of-Charge Current, from SM-V700: 50mA */
|
||||
reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data);
|
||||
|
||||
/* Auto Charging Stop, default off */
|
||||
reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
|
||||
|
||||
/* Overvoltage-Protection Threshold, from SM-V700: 6.5V */
|
||||
reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT;
|
||||
ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max14577_set_fast_charge_timer(chg,
|
||||
MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize Overvoltage-Protection Threshold */
|
||||
switch (chg->pdata->ovp_uvolt) {
|
||||
case 7500000:
|
||||
reg_data = 0x0;
|
||||
break;
|
||||
case 6000000:
|
||||
case 6500000:
|
||||
case 7000000:
|
||||
reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000;
|
||||
break;
|
||||
default:
|
||||
dev_err(chg->dev, "Wrong value for OVP: %u\n",
|
||||
chg->pdata->ovp_uvolt);
|
||||
return -EINVAL;
|
||||
}
|
||||
reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT;
|
||||
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Support property from charger */
|
||||
@ -215,7 +378,11 @@ static enum power_supply_property max14577_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static const char *model_name = "MAX14577";
|
||||
static const char * const model_names[] = {
|
||||
[MAXIM_DEVICE_TYPE_UNKNOWN] = "MAX14577-like",
|
||||
[MAXIM_DEVICE_TYPE_MAX14577] = "MAX14577",
|
||||
[MAXIM_DEVICE_TYPE_MAX77836] = "MAX77836",
|
||||
};
|
||||
static const char *manufacturer = "Maxim Integrated";
|
||||
|
||||
static int max14577_charger_get_property(struct power_supply *psy,
|
||||
@ -244,7 +411,8 @@ static int max14577_charger_get_property(struct power_supply *psy,
|
||||
val->intval = max14577_get_online(chg);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = model_name;
|
||||
BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM);
|
||||
val->strval = model_names[chg->max14577->dev_type];
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = manufacturer;
|
||||
@ -256,6 +424,110 @@ static int max14577_charger_get_property(struct power_supply *psy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct max14577_charger_platform_data *max14577_charger_dt_init(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct max14577_charger_platform_data *pdata;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int ret;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "No charger OF node\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,constant-uvolt",
|
||||
&pdata->constant_uvolt);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,fast-charge-uamp",
|
||||
&pdata->fast_charge_uamp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
static struct max14577_charger_platform_data *max14577_charger_dt_init(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static ssize_t show_fast_charge_timer(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct max14577_charger *chg = dev_get_drvdata(dev);
|
||||
u8 reg_data;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1,
|
||||
®_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg_data &= CHGCTRL1_TCHW_MASK;
|
||||
reg_data >>= CHGCTRL1_TCHW_SHIFT;
|
||||
switch (reg_data) {
|
||||
case 0x2 ... 0x4:
|
||||
val = reg_data + 3;
|
||||
break;
|
||||
case 0x7:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
val = 5;
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t store_fast_charge_timer(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct max14577_charger *chg = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max14577_set_fast_charge_timer(chg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR,
|
||||
show_fast_charge_timer, store_fast_charge_timer);
|
||||
|
||||
static int max14577_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max14577_charger *chg;
|
||||
@ -270,7 +542,13 @@ static int max14577_charger_probe(struct platform_device *pdev)
|
||||
chg->dev = &pdev->dev;
|
||||
chg->max14577 = max14577;
|
||||
|
||||
max14577_charger_reg_init(chg);
|
||||
chg->pdata = max14577_charger_dt_init(pdev);
|
||||
if (IS_ERR_OR_NULL(chg->pdata))
|
||||
return PTR_ERR(chg->pdata);
|
||||
|
||||
ret = max14577_charger_reg_init(chg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chg->charger.name = "max14577-charger",
|
||||
chg->charger.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
@ -278,24 +556,47 @@ static int max14577_charger_probe(struct platform_device *pdev)
|
||||
chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props),
|
||||
chg->charger.get_property = max14577_charger_get_property,
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &chg->charger);
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
||||
dev_err(&pdev->dev, "failed: create sysfs entry\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &chg->charger);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check for valid values for charger */
|
||||
BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN +
|
||||
MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf !=
|
||||
MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max14577_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max14577_charger *chg = platform_get_drvdata(pdev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
power_supply_unregister(&chg->charger);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id max14577_charger_id[] = {
|
||||
{ "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
|
||||
{ "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max14577_charger_id);
|
||||
|
||||
static struct platform_driver max14577_charger_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
@ -303,9 +604,10 @@ static struct platform_driver max14577_charger_driver = {
|
||||
},
|
||||
.probe = max14577_charger_probe,
|
||||
.remove = max14577_charger_remove,
|
||||
.id_table = max14577_charger_id,
|
||||
};
|
||||
module_platform_driver(max14577_charger_driver);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||
MODULE_DESCRIPTION("MAXIM 14577 charger driver");
|
||||
MODULE_DESCRIPTION("Maxim 14577/77836 charger driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -277,7 +277,8 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct i2c_device_id max17040_id[] = {
|
||||
{ "max17040", 0 },
|
||||
{ "max17040" },
|
||||
{ "max77836-battery" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max17040_id);
|
||||
|
@ -443,7 +443,7 @@ max8925_power_dt_init(struct platform_device *pdev)
|
||||
if (!nproot)
|
||||
return pdev->dev.platform_data;
|
||||
|
||||
np = of_find_node_by_name(nproot, "charger");
|
||||
np = of_get_child_by_name(nproot, "charger");
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "failed to find charger node\n");
|
||||
return NULL;
|
||||
@ -452,13 +452,14 @@ max8925_power_dt_init(struct platform_device *pdev)
|
||||
pdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct max8925_power_pdata),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
goto ret;
|
||||
|
||||
of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
|
||||
of_property_read_u32(np, "batt-detect", &batt_detect);
|
||||
of_property_read_u32(np, "fast-charge", &fast_charge);
|
||||
of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
|
||||
of_property_read_u32(np, "no-temp-support", &no_temp_support);
|
||||
of_node_put(np);
|
||||
|
||||
pdata->batt_detect = batt_detect;
|
||||
pdata->fast_charge = fast_charge;
|
||||
@ -466,6 +467,8 @@ max8925_power_dt_init(struct platform_device *pdev)
|
||||
pdata->no_insert_detect = no_insert_detect;
|
||||
pdata->no_temp_support = no_temp_support;
|
||||
|
||||
ret:
|
||||
of_node_put(np);
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
|
@ -58,7 +58,7 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier,
|
||||
|
||||
static int __power_supply_changed_work(struct device *dev, void *data)
|
||||
{
|
||||
struct power_supply *psy = (struct power_supply *)data;
|
||||
struct power_supply *psy = data;
|
||||
struct power_supply *pst = dev_get_drvdata(dev);
|
||||
|
||||
if (__power_supply_is_supplied_by(psy, pst)) {
|
||||
@ -78,7 +78,14 @@ static void power_supply_changed_work(struct work_struct *work)
|
||||
dev_dbg(psy->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&psy->changed_lock, flags);
|
||||
if (psy->changed) {
|
||||
/*
|
||||
* Check 'changed' here to avoid issues due to race between
|
||||
* power_supply_changed() and this routine. In worst case
|
||||
* power_supply_changed() can be called again just before we take above
|
||||
* lock. During the first call of this routine we will mark 'changed' as
|
||||
* false and it will stay false for the next call as well.
|
||||
*/
|
||||
if (likely(psy->changed)) {
|
||||
psy->changed = false;
|
||||
spin_unlock_irqrestore(&psy->changed_lock, flags);
|
||||
class_for_each_device(power_supply_class, NULL, psy,
|
||||
@ -89,12 +96,13 @@ static void power_supply_changed_work(struct work_struct *work)
|
||||
kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
|
||||
spin_lock_irqsave(&psy->changed_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dependent power supplies (e.g. battery) may have changed state
|
||||
* as a result of this event, so poll again and hold the
|
||||
* wakeup_source until all events are processed.
|
||||
* Hold the wakeup_source until all events are processed.
|
||||
* power_supply_changed() might have called again and have set 'changed'
|
||||
* to true.
|
||||
*/
|
||||
if (!psy->changed)
|
||||
if (likely(!psy->changed))
|
||||
pm_relax(psy->dev);
|
||||
spin_unlock_irqrestore(&psy->changed_lock, flags);
|
||||
}
|
||||
@ -119,7 +127,7 @@ EXPORT_SYMBOL_GPL(power_supply_changed);
|
||||
static int __power_supply_populate_supplied_from(struct device *dev,
|
||||
void *data)
|
||||
{
|
||||
struct power_supply *psy = (struct power_supply *)data;
|
||||
struct power_supply *psy = data;
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
struct device_node *np;
|
||||
int i = 0;
|
||||
@ -127,7 +135,7 @@ static int __power_supply_populate_supplied_from(struct device *dev,
|
||||
do {
|
||||
np = of_parse_phandle(psy->of_node, "power-supplies", i++);
|
||||
if (!np)
|
||||
continue;
|
||||
break;
|
||||
|
||||
if (np == epsy->of_node) {
|
||||
dev_info(psy->dev, "%s: Found supply : %s\n",
|
||||
@ -158,12 +166,12 @@ static int power_supply_populate_supplied_from(struct power_supply *psy)
|
||||
static int __power_supply_find_supply_from_node(struct device *dev,
|
||||
void *data)
|
||||
{
|
||||
struct device_node *np = (struct device_node *)data;
|
||||
struct device_node *np = data;
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
|
||||
/* return error breaks out of class_for_each_device loop */
|
||||
/* returning non-zero breaks out of class_for_each_device loop */
|
||||
if (epsy->of_node == np)
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -171,30 +179,21 @@ static int __power_supply_find_supply_from_node(struct device *dev,
|
||||
static int power_supply_find_supply_from_node(struct device_node *supply_node)
|
||||
{
|
||||
int error;
|
||||
struct device *dev;
|
||||
struct class_dev_iter iter;
|
||||
|
||||
/*
|
||||
* Use iterator to see if any other device is registered.
|
||||
* This is required since class_for_each_device returns 0
|
||||
* if there are no devices registered.
|
||||
*/
|
||||
class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
|
||||
dev = class_dev_iter_next(&iter);
|
||||
|
||||
if (!dev)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* We have to treat the return value as inverted, because if
|
||||
* we return error on not found, then it won't continue looking.
|
||||
* So we trick it by returning error on success to stop looking
|
||||
* once the matching device is found.
|
||||
* class_for_each_device() either returns its own errors or values
|
||||
* returned by __power_supply_find_supply_from_node().
|
||||
*
|
||||
* __power_supply_find_supply_from_node() will return 0 (no match)
|
||||
* or 1 (match).
|
||||
*
|
||||
* We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if
|
||||
* it returned 0, or error as returned by it.
|
||||
*/
|
||||
error = class_for_each_device(power_supply_class, NULL, supply_node,
|
||||
__power_supply_find_supply_from_node);
|
||||
|
||||
return error ? 0 : -EPROBE_DEFER;
|
||||
return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
static int power_supply_check_supplies(struct power_supply *psy)
|
||||
@ -215,17 +214,21 @@ static int power_supply_check_supplies(struct power_supply *psy)
|
||||
|
||||
np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
|
||||
if (!np)
|
||||
continue;
|
||||
break;
|
||||
|
||||
ret = power_supply_find_supply_from_node(np);
|
||||
if (ret) {
|
||||
dev_dbg(psy->dev, "Failed to find supply, defer!\n");
|
||||
of_node_put(np);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
if (ret) {
|
||||
dev_dbg(psy->dev, "Failed to find supply!\n");
|
||||
return ret;
|
||||
}
|
||||
} while (np);
|
||||
|
||||
/* Missing valid "power-supplies" entries */
|
||||
if (cnt == 1)
|
||||
return 0;
|
||||
|
||||
/* All supplies found, allocate char ** array for filling */
|
||||
psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from),
|
||||
GFP_KERNEL);
|
||||
@ -234,7 +237,7 @@ static int power_supply_check_supplies(struct power_supply *psy)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt,
|
||||
*psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * (cnt - 1),
|
||||
GFP_KERNEL);
|
||||
if (!*psy->supplied_from) {
|
||||
dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
|
||||
@ -253,14 +256,12 @@ static inline int power_supply_check_supplies(struct power_supply *psy)
|
||||
static int __power_supply_am_i_supplied(struct device *dev, void *data)
|
||||
{
|
||||
union power_supply_propval ret = {0,};
|
||||
struct power_supply *psy = (struct power_supply *)data;
|
||||
struct power_supply *psy = data;
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
|
||||
if (__power_supply_is_supplied_by(epsy, psy))
|
||||
if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
|
||||
if (ret.intval)
|
||||
return ret.intval;
|
||||
}
|
||||
if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret))
|
||||
return ret.intval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -285,12 +286,10 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data)
|
||||
unsigned int *count = data;
|
||||
|
||||
(*count)++;
|
||||
if (psy->type != POWER_SUPPLY_TYPE_BATTERY) {
|
||||
if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
|
||||
return 0;
|
||||
if (ret.intval)
|
||||
if (psy->type != POWER_SUPPLY_TYPE_BATTERY)
|
||||
if (!psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
|
||||
return ret.intval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -423,9 +422,7 @@ static int psy_register_thermal(struct power_supply *psy)
|
||||
if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
|
||||
psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
|
||||
psy, &psy_tzd_ops, NULL, 0, 0);
|
||||
if (IS_ERR(psy->tzd))
|
||||
return PTR_ERR(psy->tzd);
|
||||
break;
|
||||
return PTR_ERR_OR_ZERO(psy->tzd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -503,9 +500,7 @@ static int psy_register_cooler(struct power_supply *psy)
|
||||
psy->tcd = thermal_cooling_device_register(
|
||||
(char *)psy->name,
|
||||
psy, &psy_tcd_ops);
|
||||
if (IS_ERR(psy->tcd))
|
||||
return PTR_ERR(psy->tcd);
|
||||
break;
|
||||
return PTR_ERR_OR_ZERO(psy->tcd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -591,7 +586,7 @@ static int __power_supply_register(struct device *parent,
|
||||
|
||||
power_supply_changed(psy);
|
||||
|
||||
goto success;
|
||||
return 0;
|
||||
|
||||
create_triggers_failed:
|
||||
psy_unregister_cooler(psy);
|
||||
@ -604,7 +599,6 @@ wakeup_init_failed:
|
||||
check_supplies_failed:
|
||||
dev_set_name_failed:
|
||||
put_device(dev);
|
||||
success:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -57,8 +57,6 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
|
||||
|
||||
static int power_supply_create_bat_triggers(struct power_supply *psy)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
psy->charging_full_trig_name = kasprintf(GFP_KERNEL,
|
||||
"%s-charging-or-full", psy->name);
|
||||
if (!psy->charging_full_trig_name)
|
||||
@ -87,7 +85,7 @@ static int power_supply_create_bat_triggers(struct power_supply *psy)
|
||||
led_trigger_register_simple(psy->charging_blink_full_solid_trig_name,
|
||||
&psy->charging_blink_full_solid_trig);
|
||||
|
||||
goto success;
|
||||
return 0;
|
||||
|
||||
charging_blink_full_solid_failed:
|
||||
kfree(psy->full_trig_name);
|
||||
@ -96,9 +94,7 @@ full_failed:
|
||||
charging_failed:
|
||||
kfree(psy->charging_full_trig_name);
|
||||
charging_full_failed:
|
||||
rc = -ENOMEM;
|
||||
success:
|
||||
return rc;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void power_supply_remove_bat_triggers(struct power_supply *psy)
|
||||
@ -132,20 +128,13 @@ static void power_supply_update_gen_leds(struct power_supply *psy)
|
||||
|
||||
static int power_supply_create_gen_triggers(struct power_supply *psy)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", psy->name);
|
||||
if (!psy->online_trig_name)
|
||||
goto online_failed;
|
||||
return -ENOMEM;
|
||||
|
||||
led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
|
||||
|
||||
goto success;
|
||||
|
||||
online_failed:
|
||||
rc = -ENOMEM;
|
||||
success:
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void power_supply_remove_gen_triggers(struct power_supply *psy)
|
||||
|
@ -73,19 +73,20 @@ static ssize_t power_supply_show_property(struct device *dev,
|
||||
const ptrdiff_t off = attr - power_supply_attrs;
|
||||
union power_supply_propval value;
|
||||
|
||||
if (off == POWER_SUPPLY_PROP_TYPE)
|
||||
if (off == POWER_SUPPLY_PROP_TYPE) {
|
||||
value.intval = psy->type;
|
||||
else
|
||||
} else {
|
||||
ret = psy->get_property(psy, off, &value);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENODATA)
|
||||
dev_dbg(dev, "driver has no data for `%s' property\n",
|
||||
attr->attr.name);
|
||||
else if (ret != -ENODEV)
|
||||
dev_err(dev, "driver failed to report `%s' property: %zd\n",
|
||||
attr->attr.name, ret);
|
||||
return ret;
|
||||
if (ret < 0) {
|
||||
if (ret == -ENODATA)
|
||||
dev_dbg(dev, "driver has no data for `%s' property\n",
|
||||
attr->attr.name);
|
||||
else if (ret != -ENODEV)
|
||||
dev_err(dev, "driver failed to report `%s' property: %zd\n",
|
||||
attr->attr.name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (off == POWER_SUPPLY_PROP_STATUS)
|
||||
@ -149,9 +150,11 @@ static struct device_attribute power_supply_attrs[] = {
|
||||
POWER_SUPPLY_ATTR(voltage_now),
|
||||
POWER_SUPPLY_ATTR(voltage_avg),
|
||||
POWER_SUPPLY_ATTR(voltage_ocv),
|
||||
POWER_SUPPLY_ATTR(voltage_boot),
|
||||
POWER_SUPPLY_ATTR(current_max),
|
||||
POWER_SUPPLY_ATTR(current_now),
|
||||
POWER_SUPPLY_ATTR(current_avg),
|
||||
POWER_SUPPLY_ATTR(current_boot),
|
||||
POWER_SUPPLY_ATTR(power_now),
|
||||
POWER_SUPPLY_ATTR(power_avg),
|
||||
POWER_SUPPLY_ATTR(charge_full_design),
|
||||
@ -193,6 +196,7 @@ static struct device_attribute power_supply_attrs[] = {
|
||||
POWER_SUPPLY_ATTR(type),
|
||||
POWER_SUPPLY_ATTR(scope),
|
||||
POWER_SUPPLY_ATTR(charge_term_current),
|
||||
POWER_SUPPLY_ATTR(calibrate),
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_ATTR(model_name),
|
||||
POWER_SUPPLY_ATTR(manufacturer),
|
||||
|
@ -40,7 +40,7 @@ config POWER_RESET_AXXIA
|
||||
|
||||
config POWER_RESET_BRCMSTB
|
||||
bool "Broadcom STB reset driver" if COMPILE_TEST
|
||||
depends on POWER_RESET && ARM
|
||||
depends on ARM
|
||||
default ARCH_BRCMSTB
|
||||
help
|
||||
This driver provides restart support for ARM-based Broadcom STB
|
||||
@ -57,9 +57,17 @@ config POWER_RESET_GPIO
|
||||
If your board needs a GPIO high/low to power down, say Y and
|
||||
create a binding in your devicetree.
|
||||
|
||||
config POWER_RESET_GPIO_RESTART
|
||||
bool "GPIO restart driver"
|
||||
depends on OF_GPIO
|
||||
help
|
||||
This driver supports restarting your board via a GPIO line.
|
||||
If your board needs a GPIO high/low to restart, say Y and
|
||||
create a binding in your devicetree.
|
||||
|
||||
config POWER_RESET_HISI
|
||||
bool "Hisilicon power-off driver"
|
||||
depends on POWER_RESET && ARCH_HISI
|
||||
depends on ARCH_HISI
|
||||
help
|
||||
Reboot support for Hisilicon boards.
|
||||
|
||||
@ -69,6 +77,13 @@ config POWER_RESET_MSM
|
||||
help
|
||||
Power off and restart support for Qualcomm boards.
|
||||
|
||||
config POWER_RESET_LTC2952
|
||||
bool "LTC2952 PowerPath power-off driver"
|
||||
depends on OF_GPIO
|
||||
help
|
||||
This driver supports an external powerdown trigger and board power
|
||||
down via the LTC2952. Bindings are made in the device tree.
|
||||
|
||||
config POWER_RESET_QNAP
|
||||
bool "QNAP power-off driver"
|
||||
depends on OF_GPIO && PLAT_ORION
|
||||
@ -92,6 +107,12 @@ config POWER_RESET_SUN6I
|
||||
help
|
||||
Reboot support for the Allwinner A31 SoCs.
|
||||
|
||||
config POWER_RESET_ST
|
||||
bool "ST restart power-off driver"
|
||||
depends on ARCH_STI
|
||||
help
|
||||
Power off and reset support for STMicroelectronics boards.
|
||||
|
||||
config POWER_RESET_VERSATILE
|
||||
bool "ARM Versatile family reboot driver"
|
||||
depends on ARM
|
||||
@ -122,4 +143,12 @@ config POWER_RESET_KEYSTONE
|
||||
help
|
||||
Reboot support for the KEYSTONE SoCs.
|
||||
|
||||
config POWER_RESET_SYSCON
|
||||
bool "Generic SYSCON regmap reset driver"
|
||||
depends on OF
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Reboot support for generic SYSCON mapped register reset.
|
||||
|
||||
endif
|
||||
|
||||
|
@ -4,12 +4,16 @@ obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
|
||||
|
149
drivers/power/reset/gpio-restart.c
Normal file
149
drivers/power/reset/gpio-restart.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Toggles a GPIO pin to restart a device
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Based on the gpio-poweroff driver.
|
||||
*/
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct gpio_restart {
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct notifier_block restart_handler;
|
||||
u32 active_delay_ms;
|
||||
u32 inactive_delay_ms;
|
||||
u32 wait_delay_ms;
|
||||
};
|
||||
|
||||
static int gpio_restart_notify(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
struct gpio_restart *gpio_restart =
|
||||
container_of(this, struct gpio_restart, restart_handler);
|
||||
|
||||
/* drive it active, also inactive->active edge */
|
||||
gpiod_direction_output(gpio_restart->reset_gpio, 1);
|
||||
mdelay(gpio_restart->active_delay_ms);
|
||||
|
||||
/* drive inactive, also active->inactive edge */
|
||||
gpiod_set_value(gpio_restart->reset_gpio, 0);
|
||||
mdelay(gpio_restart->inactive_delay_ms);
|
||||
|
||||
/* drive it active, also inactive->active edge */
|
||||
gpiod_set_value(gpio_restart->reset_gpio, 1);
|
||||
|
||||
/* give it some time */
|
||||
mdelay(gpio_restart->wait_delay_ms);
|
||||
|
||||
WARN_ON(1);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int gpio_restart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_restart *gpio_restart;
|
||||
bool open_source = false;
|
||||
u32 property;
|
||||
int ret;
|
||||
|
||||
gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart),
|
||||
GFP_KERNEL);
|
||||
if (!gpio_restart)
|
||||
return -ENOMEM;
|
||||
|
||||
open_source = of_property_read_bool(pdev->dev.of_node, "open-source");
|
||||
|
||||
gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL,
|
||||
open_source ? GPIOD_IN : GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio_restart->reset_gpio)) {
|
||||
dev_err(&pdev->dev, "Could net get reset GPIO\n");
|
||||
return PTR_ERR(gpio_restart->reset_gpio);
|
||||
}
|
||||
|
||||
gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
|
||||
gpio_restart->restart_handler.priority = 128;
|
||||
gpio_restart->active_delay_ms = 100;
|
||||
gpio_restart->inactive_delay_ms = 100;
|
||||
gpio_restart->wait_delay_ms = 3000;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "priority", &property);
|
||||
if (!ret) {
|
||||
if (property > 255)
|
||||
dev_err(&pdev->dev, "Invalid priority property: %u\n",
|
||||
property);
|
||||
else
|
||||
gpio_restart->restart_handler.priority = property;
|
||||
}
|
||||
|
||||
of_property_read_u32(pdev->dev.of_node, "active-delay",
|
||||
&gpio_restart->active_delay_ms);
|
||||
of_property_read_u32(pdev->dev.of_node, "inactive-delay",
|
||||
&gpio_restart->inactive_delay_ms);
|
||||
of_property_read_u32(pdev->dev.of_node, "wait-delay",
|
||||
&gpio_restart->wait_delay_ms);
|
||||
|
||||
platform_set_drvdata(pdev, gpio_restart);
|
||||
|
||||
ret = register_restart_handler(&gpio_restart->restart_handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n",
|
||||
__func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_restart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_restart *gpio_restart = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = unregister_restart_handler(&gpio_restart->restart_handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: cannot unregister restart handler, %d\n",
|
||||
__func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_gpio_restart_match[] = {
|
||||
{ .compatible = "gpio-restart", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver gpio_restart_driver = {
|
||||
.probe = gpio_restart_probe,
|
||||
.remove = gpio_restart_remove,
|
||||
.driver = {
|
||||
.name = "restart-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_gpio_restart_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(gpio_restart_driver);
|
||||
|
||||
MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
|
||||
MODULE_DESCRIPTION("GPIO restart driver");
|
||||
MODULE_LICENSE("GPL");
|
386
drivers/power/reset/ltc2952-poweroff.c
Normal file
386
drivers/power/reset/ltc2952-poweroff.c
Normal file
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* LTC2952 (PowerPath) driver
|
||||
*
|
||||
* Copyright (C) 2014, Xsens Technologies BV <info@xsens.com>
|
||||
* Maintainer: René Moll <linux@r-moll.nl>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ----------------------------------------
|
||||
* - Description
|
||||
* ----------------------------------------
|
||||
*
|
||||
* This driver is to be used with an external PowerPath Controller (LTC2952).
|
||||
* Its function is to determine when a external shut down is triggered
|
||||
* and react by properly shutting down the system.
|
||||
*
|
||||
* This driver expects a device tree with a ltc2952 entry for pin mapping.
|
||||
*
|
||||
* ----------------------------------------
|
||||
* - GPIO
|
||||
* ----------------------------------------
|
||||
*
|
||||
* The following GPIOs are used:
|
||||
* - trigger (input)
|
||||
* A level change indicates the shut-down trigger. If it's state reverts
|
||||
* within the time-out defined by trigger_delay, the shut down is not
|
||||
* executed.
|
||||
*
|
||||
* - watchdog (output)
|
||||
* Once a shut down is triggered, the driver will toggle this signal,
|
||||
* with an internal (wde_interval) to stall the hardware shut down.
|
||||
*
|
||||
* - kill (output)
|
||||
* The last action during shut down is triggering this signalling, such
|
||||
* that the PowerPath Control will power down the hardware.
|
||||
*
|
||||
* ----------------------------------------
|
||||
* - Interrupts
|
||||
* ----------------------------------------
|
||||
*
|
||||
* The driver requires a non-shared, edge-triggered interrupt on the trigger
|
||||
* GPIO.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
struct ltc2952_poweroff_data {
|
||||
struct hrtimer timer_trigger;
|
||||
struct hrtimer timer_wde;
|
||||
|
||||
ktime_t trigger_delay;
|
||||
ktime_t wde_interval;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
unsigned int virq;
|
||||
|
||||
/**
|
||||
* 0: trigger
|
||||
* 1: watchdog
|
||||
* 2: kill
|
||||
*/
|
||||
struct gpio_desc *gpio[3];
|
||||
};
|
||||
|
||||
static int ltc2952_poweroff_panic;
|
||||
static struct ltc2952_poweroff_data *ltc2952_data;
|
||||
|
||||
#define POWERPATH_IO_TRIGGER 0
|
||||
#define POWERPATH_IO_WATCHDOG 1
|
||||
#define POWERPATH_IO_KILL 2
|
||||
|
||||
/**
|
||||
* ltc2952_poweroff_timer_wde - Timer callback
|
||||
* Toggles the watchdog reset signal each wde_interval
|
||||
*
|
||||
* @timer: corresponding timer
|
||||
*
|
||||
* Returns HRTIMER_RESTART for an infinite loop which will only stop when the
|
||||
* machine actually shuts down
|
||||
*/
|
||||
static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
|
||||
{
|
||||
ktime_t now;
|
||||
int state;
|
||||
unsigned long overruns;
|
||||
|
||||
if (ltc2952_poweroff_panic)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]);
|
||||
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state);
|
||||
|
||||
now = hrtimer_cb_get_time(timer);
|
||||
overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
|
||||
struct hrtimer *timer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hrtimer_start(<c2952_data->timer_wde,
|
||||
ltc2952_data->wde_interval, HRTIMER_MODE_REL);
|
||||
|
||||
if (ret) {
|
||||
dev_err(ltc2952_data->dev, "unable to start the timer\n");
|
||||
/*
|
||||
* The device will not toggle the watchdog reset,
|
||||
* thus shut down is only safe if the PowerPath controller
|
||||
* has a long enough time-off before triggering a hardware
|
||||
* power-off.
|
||||
*
|
||||
* Only sending a warning as the system will power-off anyway
|
||||
*/
|
||||
}
|
||||
|
||||
dev_info(ltc2952_data->dev, "executing shutdown\n");
|
||||
|
||||
orderly_poweroff(true);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* ltc2952_poweroff_handler - Interrupt handler
|
||||
* Triggered each time the trigger signal changes state and (de)activates a
|
||||
* time-out (timer_trigger). Once the time-out is actually reached the shut
|
||||
* down is executed.
|
||||
*
|
||||
* @irq: IRQ number
|
||||
* @dev_id: pointer to the main data structure
|
||||
*/
|
||||
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
|
||||
{
|
||||
int ret;
|
||||
struct ltc2952_poweroff_data *data = dev_id;
|
||||
|
||||
if (ltc2952_poweroff_panic)
|
||||
goto irq_ok;
|
||||
|
||||
if (hrtimer_active(&data->timer_wde)) {
|
||||
/* shutdown is already triggered, nothing to do any more */
|
||||
goto irq_ok;
|
||||
}
|
||||
|
||||
if (!hrtimer_active(&data->timer_trigger)) {
|
||||
ret = hrtimer_start(&data->timer_trigger, data->trigger_delay,
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
if (ret)
|
||||
dev_err(data->dev, "unable to start the wait timer\n");
|
||||
} else {
|
||||
ret = hrtimer_cancel(&data->timer_trigger);
|
||||
/* omitting return value check, timer should have been valid */
|
||||
}
|
||||
|
||||
irq_ok:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ltc2952_poweroff_kill(void)
|
||||
{
|
||||
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1);
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_resume(struct platform_device *pdev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->gpio); i++)
|
||||
data->gpio[i] = NULL;
|
||||
|
||||
data->wde_interval = ktime_set(0, 300L*1E6L);
|
||||
data->trigger_delay = ktime_set(2, 500L*1E6L);
|
||||
|
||||
hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
data->timer_trigger.function = <c2952_poweroff_timer_trigger;
|
||||
|
||||
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
data->timer_wde.function = <c2952_poweroff_timer_wde;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_init(struct platform_device *pdev)
|
||||
{
|
||||
int ret, virq;
|
||||
unsigned int i;
|
||||
struct ltc2952_poweroff_data *data;
|
||||
|
||||
static char *name[] = {
|
||||
"trigger",
|
||||
"watchdog",
|
||||
"kill",
|
||||
NULL
|
||||
};
|
||||
|
||||
data = ltc2952_data;
|
||||
ltc2952_poweroff_default(ltc2952_data);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) {
|
||||
ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]);
|
||||
|
||||
if (IS_ERR(ltc2952_data->gpio[i])) {
|
||||
ret = PTR_ERR(ltc2952_data->gpio[i]);
|
||||
dev_err(&pdev->dev,
|
||||
"unable to claim the following gpio: %s\n",
|
||||
name[i]);
|
||||
goto err_io;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gpiod_direction_output(
|
||||
ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to use kill-gpio as output\n");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]);
|
||||
if (virq < 0) {
|
||||
dev_err(&pdev->dev, "cannot map GPIO as interrupt");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
ltc2952_data->virq = virq;
|
||||
ret = request_irq(virq,
|
||||
ltc2952_poweroff_handler,
|
||||
(IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
|
||||
"ltc2952-poweroff",
|
||||
ltc2952_data
|
||||
);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot configure an interrupt handler\n");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_io:
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
|
||||
if (ltc2952_data->gpio[i])
|
||||
gpiod_put(ltc2952_data->gpio[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pm_power_off) {
|
||||
dev_err(&pdev->dev, "pm_power_off already registered");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL);
|
||||
if (!ltc2952_data)
|
||||
return -ENOMEM;
|
||||
|
||||
ltc2952_data->dev = &pdev->dev;
|
||||
|
||||
ret = ltc2952_poweroff_init(pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
pm_power_off = <c2952_poweroff_kill;
|
||||
|
||||
dev_info(&pdev->dev, "probe successful\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(ltc2952_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_remove(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
pm_power_off = NULL;
|
||||
|
||||
if (ltc2952_data) {
|
||||
free_irq(ltc2952_data->virq, ltc2952_data);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
|
||||
gpiod_put(ltc2952_data->gpio[i]);
|
||||
|
||||
kfree(ltc2952_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_ltc2952_poweroff_match[] = {
|
||||
{ .compatible = "lltc,ltc2952"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match);
|
||||
|
||||
static struct platform_driver ltc2952_poweroff_driver = {
|
||||
.probe = ltc2952_poweroff_probe,
|
||||
.remove = ltc2952_poweroff_remove,
|
||||
.driver = {
|
||||
.name = "ltc2952-poweroff",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_ltc2952_poweroff_match,
|
||||
},
|
||||
.suspend = ltc2952_poweroff_suspend,
|
||||
.resume = ltc2952_poweroff_resume,
|
||||
};
|
||||
|
||||
static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
|
||||
unsigned long code, void *unused)
|
||||
{
|
||||
ltc2952_poweroff_panic = 1;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block ltc2952_poweroff_panic_nb = {
|
||||
.notifier_call = ltc2952_poweroff_notify_panic,
|
||||
};
|
||||
|
||||
static int __init ltc2952_poweroff_platform_init(void)
|
||||
{
|
||||
ltc2952_poweroff_panic = 0;
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
<c2952_poweroff_panic_nb);
|
||||
|
||||
return platform_driver_register(<c2952_poweroff_driver);
|
||||
}
|
||||
|
||||
static void __exit ltc2952_poweroff_platform_exit(void)
|
||||
{
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
<c2952_poweroff_panic_nb);
|
||||
|
||||
platform_driver_unregister(<c2952_poweroff_driver);
|
||||
}
|
||||
|
||||
module_init(ltc2952_poweroff_platform_init);
|
||||
module_exit(ltc2952_poweroff_platform_exit);
|
||||
|
||||
MODULE_AUTHOR("René Moll <rene.moll@xsens.com>");
|
||||
MODULE_DESCRIPTION("LTC PowerPath power-off driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -20,21 +20,27 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
static void __iomem *msm_ps_hold;
|
||||
|
||||
static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
|
||||
static int do_msm_restart(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
writel(0, msm_ps_hold);
|
||||
mdelay(10000);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block restart_nb = {
|
||||
.notifier_call = do_msm_restart,
|
||||
.priority = 128,
|
||||
};
|
||||
|
||||
static void do_msm_poweroff(void)
|
||||
{
|
||||
/* TODO: Add poweroff capability */
|
||||
do_msm_restart(REBOOT_HARD, NULL);
|
||||
do_msm_restart(&restart_nb, 0, NULL);
|
||||
}
|
||||
|
||||
static int msm_restart_probe(struct platform_device *pdev)
|
||||
@ -47,8 +53,10 @@ static int msm_restart_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(msm_ps_hold))
|
||||
return PTR_ERR(msm_ps_hold);
|
||||
|
||||
register_restart_handler(&restart_nb);
|
||||
|
||||
pm_power_off = do_msm_poweroff;
|
||||
arm_pm_restart = do_msm_restart;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
151
drivers/power/reset/st-poweroff.c
Normal file
151
drivers/power/reset/st-poweroff.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics
|
||||
*
|
||||
* Power off Restart driver, used in STMicroelectronics devices.
|
||||
*
|
||||
* Author: Christophe Kerello <christophe.kerello@st.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
struct reset_syscfg {
|
||||
struct regmap *regmap;
|
||||
/* syscfg used for reset */
|
||||
unsigned int offset_rst;
|
||||
unsigned int mask_rst;
|
||||
/* syscfg used for unmask the reset */
|
||||
unsigned int offset_rst_msk;
|
||||
unsigned int mask_rst_msk;
|
||||
};
|
||||
|
||||
/* STiH415 */
|
||||
#define STIH415_SYSCFG_11 0x2c
|
||||
#define STIH415_SYSCFG_15 0x3c
|
||||
|
||||
static struct reset_syscfg stih415_reset = {
|
||||
.offset_rst = STIH415_SYSCFG_11,
|
||||
.mask_rst = BIT(0),
|
||||
.offset_rst_msk = STIH415_SYSCFG_15,
|
||||
.mask_rst_msk = BIT(0)
|
||||
};
|
||||
|
||||
/* STiH416 */
|
||||
#define STIH416_SYSCFG_500 0x7d0
|
||||
#define STIH416_SYSCFG_504 0x7e0
|
||||
|
||||
static struct reset_syscfg stih416_reset = {
|
||||
.offset_rst = STIH416_SYSCFG_500,
|
||||
.mask_rst = BIT(0),
|
||||
.offset_rst_msk = STIH416_SYSCFG_504,
|
||||
.mask_rst_msk = BIT(0)
|
||||
};
|
||||
|
||||
/* STiH407 */
|
||||
#define STIH407_SYSCFG_4000 0x0
|
||||
#define STIH407_SYSCFG_4008 0x20
|
||||
|
||||
static struct reset_syscfg stih407_reset = {
|
||||
.offset_rst = STIH407_SYSCFG_4000,
|
||||
.mask_rst = BIT(0),
|
||||
.offset_rst_msk = STIH407_SYSCFG_4008,
|
||||
.mask_rst_msk = BIT(0)
|
||||
};
|
||||
|
||||
/* STiD127 */
|
||||
#define STID127_SYSCFG_700 0x0
|
||||
#define STID127_SYSCFG_773 0x124
|
||||
|
||||
static struct reset_syscfg stid127_reset = {
|
||||
.offset_rst = STID127_SYSCFG_773,
|
||||
.mask_rst = BIT(0),
|
||||
.offset_rst_msk = STID127_SYSCFG_700,
|
||||
.mask_rst_msk = BIT(8)
|
||||
};
|
||||
|
||||
static struct reset_syscfg *st_restart_syscfg;
|
||||
|
||||
static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
|
||||
{
|
||||
/* reset syscfg updated */
|
||||
regmap_update_bits(st_restart_syscfg->regmap,
|
||||
st_restart_syscfg->offset_rst,
|
||||
st_restart_syscfg->mask_rst,
|
||||
0);
|
||||
|
||||
/* unmask the reset */
|
||||
regmap_update_bits(st_restart_syscfg->regmap,
|
||||
st_restart_syscfg->offset_rst_msk,
|
||||
st_restart_syscfg->mask_rst_msk,
|
||||
0);
|
||||
}
|
||||
|
||||
static struct of_device_id st_reset_of_match[] = {
|
||||
{
|
||||
.compatible = "st,stih415-restart",
|
||||
.data = (void *)&stih415_reset,
|
||||
}, {
|
||||
.compatible = "st,stih416-restart",
|
||||
.data = (void *)&stih416_reset,
|
||||
}, {
|
||||
.compatible = "st,stih407-restart",
|
||||
.data = (void *)&stih407_reset,
|
||||
}, {
|
||||
.compatible = "st,stid127-restart",
|
||||
.data = (void *)&stid127_reset,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int st_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
match = of_match_device(st_reset_of_match, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
st_restart_syscfg = (struct reset_syscfg *)match->data;
|
||||
|
||||
st_restart_syscfg->regmap =
|
||||
syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(st_restart_syscfg->regmap)) {
|
||||
dev_err(dev, "No syscfg phandle specified\n");
|
||||
return PTR_ERR(st_restart_syscfg->regmap);
|
||||
}
|
||||
|
||||
arm_pm_restart = st_restart;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver st_reset_driver = {
|
||||
.probe = st_reset_probe,
|
||||
.driver = {
|
||||
.name = "st_reset",
|
||||
.of_match_table = st_reset_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init st_reset_init(void)
|
||||
{
|
||||
return platform_driver_register(&st_reset_driver);
|
||||
}
|
||||
|
||||
device_initcall(st_reset_init);
|
||||
|
||||
MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver");
|
||||
MODULE_LICENSE("GPL v2");
|
91
drivers/power/reset/syscon-reboot.c
Normal file
91
drivers/power/reset/syscon-reboot.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Generic Syscon Reboot Driver
|
||||
*
|
||||
* Copyright (c) 2013, Applied Micro Circuits Corporation
|
||||
* Author: Feng Kan <fkan@apm.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct syscon_reboot_context {
|
||||
struct regmap *map;
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
struct notifier_block restart_handler;
|
||||
};
|
||||
|
||||
static int syscon_restart_handle(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
struct syscon_reboot_context *ctx =
|
||||
container_of(this, struct syscon_reboot_context,
|
||||
restart_handler);
|
||||
|
||||
/* Issue the reboot */
|
||||
regmap_write(ctx->map, ctx->offset, ctx->mask);
|
||||
|
||||
mdelay(1000);
|
||||
|
||||
pr_emerg("Unable to restart system\n");
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int syscon_reboot_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct syscon_reboot_context *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
int err;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
|
||||
if (IS_ERR(ctx->map))
|
||||
return PTR_ERR(ctx->map);
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask))
|
||||
return -EINVAL;
|
||||
|
||||
ctx->restart_handler.notifier_call = syscon_restart_handle;
|
||||
ctx->restart_handler.priority = 128;
|
||||
err = register_restart_handler(&ctx->restart_handler);
|
||||
if (err)
|
||||
dev_err(dev, "can't register restart notifier (err=%d)\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct of_device_id syscon_reboot_of_match[] = {
|
||||
{ .compatible = "syscon-reboot" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver syscon_reboot_driver = {
|
||||
.probe = syscon_reboot_probe,
|
||||
.driver = {
|
||||
.name = "syscon-reboot",
|
||||
.of_match_table = syscon_reboot_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(syscon_reboot_driver);
|
@ -40,7 +40,7 @@ struct xgene_reboot_context {
|
||||
|
||||
static struct xgene_reboot_context *xgene_restart_ctx;
|
||||
|
||||
static void xgene_restart(char str, const char *cmd)
|
||||
static void xgene_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
struct xgene_reboot_context *ctx = xgene_restart_ctx;
|
||||
unsigned long timeout;
|
||||
|
@ -48,7 +48,10 @@ enum {
|
||||
REG_FULL_CHARGE_CAPACITY_CHARGE,
|
||||
REG_DESIGN_CAPACITY,
|
||||
REG_DESIGN_CAPACITY_CHARGE,
|
||||
REG_DESIGN_VOLTAGE,
|
||||
REG_DESIGN_VOLTAGE_MIN,
|
||||
REG_DESIGN_VOLTAGE_MAX,
|
||||
REG_MANUFACTURER,
|
||||
REG_MODEL_NAME,
|
||||
};
|
||||
|
||||
/* Battery Mode defines */
|
||||
@ -68,6 +71,7 @@ enum sbs_battery_mode {
|
||||
#define BATTERY_FULL_CHARGED 0x20
|
||||
#define BATTERY_FULL_DISCHARGED 0x10
|
||||
|
||||
/* min_value and max_value are only valid for numerical data */
|
||||
#define SBS_DATA(_psp, _addr, _min_value, _max_value) { \
|
||||
.psp = _psp, \
|
||||
.addr = _addr, \
|
||||
@ -111,10 +115,17 @@ static const struct chip_data {
|
||||
SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535),
|
||||
[REG_DESIGN_CAPACITY_CHARGE] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535),
|
||||
[REG_DESIGN_VOLTAGE] =
|
||||
[REG_DESIGN_VOLTAGE_MIN] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 0x19, 0, 65535),
|
||||
[REG_DESIGN_VOLTAGE_MAX] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535),
|
||||
[REG_SERIAL_NUMBER] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
|
||||
/* Properties of type `const char *' */
|
||||
[REG_MANUFACTURER] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535),
|
||||
[REG_MODEL_NAME] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535)
|
||||
};
|
||||
|
||||
static enum power_supply_property sbs_properties[] = {
|
||||
@ -130,6 +141,7 @@ static enum power_supply_property sbs_properties[] = {
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_ENERGY_NOW,
|
||||
POWER_SUPPLY_PROP_ENERGY_FULL,
|
||||
@ -137,6 +149,9 @@ static enum power_supply_property sbs_properties[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME
|
||||
};
|
||||
|
||||
struct sbs_info {
|
||||
@ -153,6 +168,9 @@ struct sbs_info {
|
||||
int ignore_changes;
|
||||
};
|
||||
|
||||
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
|
||||
static int sbs_read_word_data(struct i2c_client *client, u8 address)
|
||||
{
|
||||
struct sbs_info *chip = i2c_get_clientdata(client);
|
||||
@ -179,6 +197,74 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address)
|
||||
return le16_to_cpu(ret);
|
||||
}
|
||||
|
||||
static int sbs_read_string_data(struct i2c_client *client, u8 address,
|
||||
char *values)
|
||||
{
|
||||
struct sbs_info *chip = i2c_get_clientdata(client);
|
||||
s32 ret = 0, block_length = 0;
|
||||
int retries_length = 1, retries_block = 1;
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
|
||||
if (chip->pdata) {
|
||||
retries_length = max(chip->pdata->i2c_retry_count + 1, 1);
|
||||
retries_block = max(chip->pdata->i2c_retry_count + 1, 1);
|
||||
}
|
||||
|
||||
/* Adapter needs to support these two functions */
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)){
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Get the length of block data */
|
||||
while (retries_length > 0) {
|
||||
ret = i2c_smbus_read_byte_data(client, address);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
retries_length--;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_dbg(&client->dev,
|
||||
"%s: i2c read at address 0x%x failed\n",
|
||||
__func__, address);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* block_length does not include NULL terminator */
|
||||
block_length = ret;
|
||||
if (block_length > I2C_SMBUS_BLOCK_MAX) {
|
||||
dev_err(&client->dev,
|
||||
"%s: Returned block_length is longer than 0x%x\n",
|
||||
__func__, I2C_SMBUS_BLOCK_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get the block data */
|
||||
while (retries_block > 0) {
|
||||
ret = i2c_smbus_read_i2c_block_data(
|
||||
client, address,
|
||||
block_length + 1, block_buffer);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
retries_block--;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_dbg(&client->dev,
|
||||
"%s: i2c read at address 0x%x failed\n",
|
||||
__func__, address);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* block_buffer[0] == block_length */
|
||||
memcpy(values, block_buffer + 1, block_length);
|
||||
values[block_length] = '\0';
|
||||
|
||||
return le16_to_cpu(ret);
|
||||
}
|
||||
|
||||
static int sbs_write_word_data(struct i2c_client *client, u8 address,
|
||||
u16 value)
|
||||
{
|
||||
@ -318,6 +404,19 @@ static int sbs_get_battery_property(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_get_battery_string_property(struct i2c_client *client,
|
||||
int reg_offset, enum power_supply_property psp, char *val)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sbs_unit_adjustment(struct i2c_client *client,
|
||||
enum power_supply_property psp, union power_supply_propval *val)
|
||||
{
|
||||
@ -336,6 +435,7 @@ static void sbs_unit_adjustment(struct i2c_client *client,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
@ -497,6 +597,7 @@ static int sbs_get_property(struct power_supply *psy,
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
@ -505,6 +606,26 @@ static int sbs_get_property(struct power_supply *psy,
|
||||
ret = sbs_get_battery_property(client, ret, psp, val);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = sbs_get_battery_string_property(client, ret, psp,
|
||||
model_name);
|
||||
val->strval = model_name;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
ret = sbs_get_property_index(client, psp);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = sbs_get_battery_string_property(client, ret, psp,
|
||||
manufacturer);
|
||||
val->strval = manufacturer;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&client->dev,
|
||||
"%s: INVALID property\n", __func__);
|
||||
|
@ -22,42 +22,6 @@
|
||||
#include <linux/mfd/max14577-private.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
/*
|
||||
* Valid limits of current for max14577 and max77836 chargers.
|
||||
* They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
|
||||
* register for given chipset.
|
||||
*/
|
||||
struct maxim_charger_current {
|
||||
/* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
|
||||
unsigned int min;
|
||||
/*
|
||||
* Minimal current when high setting is active,
|
||||
* set in CHGCTRL4/MBCICHWRCH, uA
|
||||
*/
|
||||
unsigned int high_start;
|
||||
/* Value of one step in high setting, uA */
|
||||
unsigned int high_step;
|
||||
/* Maximum current of high setting, uA */
|
||||
unsigned int max;
|
||||
};
|
||||
|
||||
/* Table of valid charger currents for different Maxim chipsets */
|
||||
static const struct maxim_charger_current maxim_charger_currents[] = {
|
||||
[MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
|
||||
[MAXIM_DEVICE_TYPE_MAX14577] = {
|
||||
.min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
|
||||
.high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
|
||||
.high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
|
||||
.max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
|
||||
},
|
||||
[MAXIM_DEVICE_TYPE_MAX77836] = {
|
||||
.min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
|
||||
.high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
|
||||
.high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
|
||||
.max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
|
||||
},
|
||||
};
|
||||
|
||||
static int max14577_reg_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
int rid = rdev_get_id(rdev);
|
||||
@ -103,8 +67,8 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
|
||||
static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA)
|
||||
{
|
||||
int i, current_bits = 0xf;
|
||||
u8 reg_data;
|
||||
int ret;
|
||||
struct max14577 *max14577 = rdev_get_drvdata(rdev);
|
||||
const struct maxim_charger_current *limits =
|
||||
&maxim_charger_currents[max14577->dev_type];
|
||||
@ -112,35 +76,9 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
|
||||
if (rdev_get_id(rdev) != MAX14577_CHARGER)
|
||||
return -EINVAL;
|
||||
|
||||
if (min_uA > limits->max || max_uA < limits->min)
|
||||
return -EINVAL;
|
||||
|
||||
if (max_uA < limits->high_start) {
|
||||
/*
|
||||
* Less than high_start,
|
||||
* so set the minimal current (turn only Low Bit off)
|
||||
*/
|
||||
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||
return max14577_update_reg(rdev->regmap,
|
||||
MAX14577_CHG_REG_CHG_CTRL4,
|
||||
CHGCTRL4_MBCICHWRCL_MASK, reg_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* max_uA is in range: <high_start, inifinite>, so search for
|
||||
* valid current starting from maximum current.
|
||||
*/
|
||||
for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
|
||||
if (i <= max_uA)
|
||||
break;
|
||||
current_bits--;
|
||||
}
|
||||
BUG_ON(current_bits < 0); /* Cannot happen */
|
||||
|
||||
/* Turn Low Bit on (use range high_start-max)... */
|
||||
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
|
||||
/* and set proper High Bits */
|
||||
reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
|
||||
ret = maxim_charger_calc_reg_current(limits, min_uA, max_uA, ®_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4,
|
||||
CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
|
||||
@ -442,16 +380,6 @@ static struct platform_driver max14577_regulator_driver = {
|
||||
|
||||
static int __init max14577_regulator_init(void)
|
||||
{
|
||||
/* Check for valid values for charger */
|
||||
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||
MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
|
||||
BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
|
||||
MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
|
||||
MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
|
||||
/* Valid charger current values must be provided for each chipset */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
|
||||
|
||||
|
@ -72,15 +72,33 @@ enum max14577_muic_reg {
|
||||
MAX14577_MUIC_REG_END,
|
||||
};
|
||||
|
||||
/*
|
||||
* Combined charger types for max14577 and max77836.
|
||||
*
|
||||
* On max14577 three lower bits map to STATUS2/CHGTYP field.
|
||||
* However the max77836 has different two last values of STATUS2/CHGTYP.
|
||||
* To indicate the difference enum has two additional values for max77836.
|
||||
* These values are just a register value bitwise OR with 0x8.
|
||||
*/
|
||||
enum max14577_muic_charger_type {
|
||||
MAX14577_CHARGER_TYPE_NONE = 0,
|
||||
MAX14577_CHARGER_TYPE_USB,
|
||||
MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT,
|
||||
MAX14577_CHARGER_TYPE_DEDICATED_CHG,
|
||||
MAX14577_CHARGER_TYPE_SPECIAL_500MA,
|
||||
MAX14577_CHARGER_TYPE_SPECIAL_1A,
|
||||
MAX14577_CHARGER_TYPE_RESERVED,
|
||||
MAX14577_CHARGER_TYPE_DEAD_BATTERY = 7,
|
||||
MAX14577_CHARGER_TYPE_NONE = 0x0,
|
||||
MAX14577_CHARGER_TYPE_USB = 0x1,
|
||||
MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT = 0x2,
|
||||
MAX14577_CHARGER_TYPE_DEDICATED_CHG = 0x3,
|
||||
MAX14577_CHARGER_TYPE_SPECIAL_500MA = 0x4,
|
||||
/* Special 1A or 2A charger */
|
||||
MAX14577_CHARGER_TYPE_SPECIAL_1A = 0x5,
|
||||
/* max14577: reserved, used on max77836 */
|
||||
MAX14577_CHARGER_TYPE_RESERVED = 0x6,
|
||||
/* max14577: dead-battery charing with maximum current 100mA */
|
||||
MAX14577_CHARGER_TYPE_DEAD_BATTERY = 0x7,
|
||||
/*
|
||||
* max77836: special charger (bias on D+/D-),
|
||||
* matches register value of 0x6
|
||||
*/
|
||||
MAX77836_CHARGER_TYPE_SPECIAL_BIAS = 0xe,
|
||||
/* max77836: reserved, register value 0x7 */
|
||||
MAX77836_CHARGER_TYPE_RESERVED = 0xf,
|
||||
};
|
||||
|
||||
/* MAX14577 interrupts */
|
||||
@ -121,13 +139,15 @@ enum max14577_muic_charger_type {
|
||||
#define STATUS2_CHGTYP_SHIFT 0
|
||||
#define STATUS2_CHGDETRUN_SHIFT 3
|
||||
#define STATUS2_DCDTMR_SHIFT 4
|
||||
#define STATUS2_DBCHG_SHIFT 5
|
||||
#define MAX14577_STATUS2_DBCHG_SHIFT 5
|
||||
#define MAX77836_STATUS2_DXOVP_SHIFT 5
|
||||
#define STATUS2_VBVOLT_SHIFT 6
|
||||
#define MAX77836_STATUS2_VIDRM_SHIFT 7
|
||||
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
|
||||
#define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT)
|
||||
#define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT)
|
||||
#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT)
|
||||
#define MAX14577_STATUS2_DBCHG_MASK BIT(MAX14577_STATUS2_DBCHG_SHIFT)
|
||||
#define MAX77836_STATUS2_DXOVP_MASK BIT(MAX77836_STATUS2_DXOVP_SHIFT)
|
||||
#define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT)
|
||||
#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT)
|
||||
|
||||
@ -177,9 +197,11 @@ enum max14577_muic_charger_type {
|
||||
#define CTRL3_JIGSET_SHIFT 0
|
||||
#define CTRL3_BOOTSET_SHIFT 2
|
||||
#define CTRL3_ADCDBSET_SHIFT 4
|
||||
#define CTRL3_WBTH_SHIFT 6
|
||||
#define CTRL3_JIGSET_MASK (0x3 << CTRL3_JIGSET_SHIFT)
|
||||
#define CTRL3_BOOTSET_MASK (0x3 << CTRL3_BOOTSET_SHIFT)
|
||||
#define CTRL3_ADCDBSET_MASK (0x3 << CTRL3_ADCDBSET_SHIFT)
|
||||
#define CTRL3_WBTH_MASK (0x3 << CTRL3_WBTH_SHIFT)
|
||||
|
||||
/* Slave addr = 0x4A: Charger */
|
||||
enum max14577_charger_reg {
|
||||
@ -210,16 +232,20 @@ enum max14577_charger_reg {
|
||||
#define CDETCTRL1_CHGTYPMAN_SHIFT 1
|
||||
#define CDETCTRL1_DCDEN_SHIFT 2
|
||||
#define CDETCTRL1_DCD2SCT_SHIFT 3
|
||||
#define CDETCTRL1_DCHKTM_SHIFT 4
|
||||
#define CDETCTRL1_DBEXIT_SHIFT 5
|
||||
#define MAX14577_CDETCTRL1_DCHKTM_SHIFT 4
|
||||
#define MAX77836_CDETCTRL1_CDLY_SHIFT 4
|
||||
#define MAX14577_CDETCTRL1_DBEXIT_SHIFT 5
|
||||
#define MAX77836_CDETCTRL1_DCDCPL_SHIFT 5
|
||||
#define CDETCTRL1_DBIDLE_SHIFT 6
|
||||
#define CDETCTRL1_CDPDET_SHIFT 7
|
||||
#define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT)
|
||||
#define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
|
||||
#define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT)
|
||||
#define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT)
|
||||
#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT)
|
||||
#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT)
|
||||
#define MAX14577_CDETCTRL1_DCHKTM_MASK BIT(MAX14577_CDETCTRL1_DCHKTM_SHIFT)
|
||||
#define MAX77836_CDETCTRL1_CDDLY_MASK BIT(MAX77836_CDETCTRL1_CDDLY_SHIFT)
|
||||
#define MAX14577_CDETCTRL1_DBEXIT_MASK BIT(MAX14577_CDETCTRL1_DBEXIT_SHIFT)
|
||||
#define MAX77836_CDETCTRL1_DCDCPL_MASK BIT(MAX77836_CDETCTRL1_DCDCPL_SHIFT)
|
||||
#define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT)
|
||||
#define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT)
|
||||
|
||||
@ -255,17 +281,36 @@ enum max14577_charger_reg {
|
||||
#define CHGCTRL7_OTPCGHCVS_SHIFT 0
|
||||
#define CHGCTRL7_OTPCGHCVS_MASK (0x3 << CHGCTRL7_OTPCGHCVS_SHIFT)
|
||||
|
||||
/* MAX14577 regulator current limits (as in CHGCTRL4 register), uA */
|
||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_MIN 90000
|
||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START 200000
|
||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000
|
||||
#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000
|
||||
/* MAX14577 charger current limits (as in CHGCTRL4 register), uA */
|
||||
#define MAX14577_CHARGER_CURRENT_LIMIT_MIN 90000U
|
||||
#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START 200000U
|
||||
#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP 50000U
|
||||
#define MAX14577_CHARGER_CURRENT_LIMIT_MAX 950000U
|
||||
|
||||
/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
|
||||
#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000
|
||||
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000
|
||||
#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000
|
||||
#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000
|
||||
/* MAX77836 charger current limits (as in CHGCTRL4 register), uA */
|
||||
#define MAX77836_CHARGER_CURRENT_LIMIT_MIN 45000U
|
||||
#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START 100000U
|
||||
#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP 25000U
|
||||
#define MAX77836_CHARGER_CURRENT_LIMIT_MAX 475000U
|
||||
|
||||
/*
|
||||
* MAX14577 charger End-Of-Charge current limits
|
||||
* (as in CHGCTRL5 register), uA
|
||||
*/
|
||||
#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN 50000U
|
||||
#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP 10000U
|
||||
#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX 200000U
|
||||
|
||||
/*
|
||||
* MAX14577/MAX77836 Battery Constant Voltage
|
||||
* (as in CHGCTRL3 register), uV
|
||||
*/
|
||||
#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN 4000000U
|
||||
#define MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP 20000U
|
||||
#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX 4350000U
|
||||
|
||||
/* Default value for fast charge timer, in hours */
|
||||
#define MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT 5
|
||||
|
||||
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
|
||||
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
|
||||
|
@ -54,6 +54,13 @@ struct max14577_regulator_platform_data {
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
||||
struct max14577_charger_platform_data {
|
||||
u32 constant_uvolt;
|
||||
u32 fast_charge_uamp;
|
||||
u32 eoc_uamp;
|
||||
u32 ovp_uvolt;
|
||||
};
|
||||
|
||||
/*
|
||||
* MAX14577 MFD platform data
|
||||
*/
|
||||
@ -74,4 +81,27 @@ struct max14577_platform_data {
|
||||
struct max14577_regulator_platform_data *regulators;
|
||||
};
|
||||
|
||||
/*
|
||||
* Valid limits of current for max14577 and max77836 chargers.
|
||||
* They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
|
||||
* register for given chipset.
|
||||
*/
|
||||
struct maxim_charger_current {
|
||||
/* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
|
||||
unsigned int min;
|
||||
/*
|
||||
* Minimal current when high setting is active,
|
||||
* set in CHGCTRL4/MBCICHWRCH, uA
|
||||
*/
|
||||
unsigned int high_start;
|
||||
/* Value of one step in high setting, uA */
|
||||
unsigned int high_step;
|
||||
/* Maximum current of high setting, uA */
|
||||
unsigned int max;
|
||||
};
|
||||
|
||||
extern const struct maxim_charger_current maxim_charger_currents[];
|
||||
extern int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
|
||||
unsigned int min_ua, unsigned int max_ua, u8 *dst);
|
||||
|
||||
#endif /* __MAX14577_H__ */
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
struct device;
|
||||
|
||||
/*
|
||||
* All voltages, currents, charges, energies, time and temperatures in uV,
|
||||
* µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
|
||||
@ -102,9 +100,11 @@ enum power_supply_property {
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
|
||||
POWER_SUPPLY_PROP_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
POWER_SUPPLY_PROP_CURRENT_BOOT,
|
||||
POWER_SUPPLY_PROP_POWER_NOW,
|
||||
POWER_SUPPLY_PROP_POWER_AVG,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
@ -146,6 +146,7 @@ enum power_supply_property {
|
||||
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_CALIBRATE,
|
||||
/* Properties of type `const char *' */
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
@ -172,6 +173,7 @@ union power_supply_propval {
|
||||
const char *strval;
|
||||
};
|
||||
|
||||
struct device;
|
||||
struct device_node;
|
||||
|
||||
struct power_supply {
|
||||
@ -291,6 +293,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp)
|
||||
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
case POWER_SUPPLY_PROP_CURRENT_BOOT:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
@ -315,6 +318,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
|
||||
case POWER_SUPPLY_PROP_POWER_NOW:
|
||||
|
Loading…
Reference in New Issue
Block a user