mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 08:02:07 +00:00
power supply and reset changes for the v3.20 series
* new drivers - charger driver for Maxim 77693 - battery gauge driver for LTC 2941/2943 - battery gauge driver for RT5033 - reset driver for R-Mobile platforms * convert drivers to restart handler framework - arm-versatile - at91 - st-poweroff * remove deprecated sun6i reboot driver * use alarmtimer instead of rtc in charger-manager * misc. fixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJU2MKtAAoJENju1/PIO/qaWAoP/Rr7rr82WidViY7R8SlSjaIM Iombb0I4/M1d1QLnWEXcn6g59ujq9Qt7OggFQbyB3SiHk3pn9FgYNelyMO5LlMgz /Or2WshMaweef9jDn3TQRCvMty9VStjZw9rVrUn8sEHDU9lSH97Em4wlmLaeE8LI pPvMillZF1F9HYpgkRw7i59XOpC+fC+RuwE394l3JqvfCvhZIYlEDEhdYJAi+Pro xYnx6sf2MQU1dqyuTCvxespNf1lvzFBXEtpn3iXcRu6jCc664coIcIr9cfUP9xTA 5qyiqzHPzT0LeZF5gZDhctegkdGJwqoNw7s1Z5LQyo43noDeTf4LgkdssrU7j7w0 In7JUN8CassjhDZaKPN82B8jYoY19X/x7hDE53kP8BBUcU78QAWY4PtI6/IN4iOe u9+mbOw5/8UkwF2V2qblkHOA51E+4Q6qsiLE9zJKoh69AIeefErFfpyL/FnVD2VQ MUbUtNKPvfTwqJfP7YnYstmg5rYUuIwEOda7yf5VQuUybtagKScQWte8edPDqkLM Y3GNUgkr/vSS2Xvil6yYuv+VfblFdtFci+Cq4cj/CtiCy7HZfwdcTbKbpKvmqRIC RKpSpq+njTdeDHczY4tKKkx7lb6XfsSc1njcn/2dVNd/AyNUnc4zorY3VxNRu3Ra 8bFYXOhh0pEUyOQgJ5Mn =M8sG -----END PGP SIGNATURE----- Merge tag 'for-v3.20' of git://git.infradead.org/battery-2.6 Pull power supply and reset changes from Sebastian Reichel: "New drivers: - charger driver for Maxim 77693 - battery gauge driver for LTC 2941/2943 - battery gauge driver for RT5033 - reset driver for R-Mobile platforms Convert drivers to restart handler framework: - arm-versatile - at91 - st-poweroff Misc: - remove deprecated sun6i reboot driver - use alarmtimer instead of rtc in charger-manager - misc fixes" * tag 'for-v3.20' of git://git.infradead.org/battery-2.6: (48 commits) power_supply: 88pm860x: Fix leaked power supply on probe fail power/reset: restart-poweroff: Remove arm dependencies power/reset: st-poweroff: Fix misleading Kconfig description power/reset: st-poweroff: Register with kernel restart handler power/reset: Remove sun6i reboot driver power/reset: at91: Register with kernel restart handler power/reset: arm-versatile: Register with kernel restart handler power: test_power: Use enum as index for array of supplies Add devicetree binding documentation for the LTC2941/LTC2943 driver Add LTC2941/LTC2943 Battery Gauge Driver power/reset: brcmstb: Add support for old 65nm chips power/reset: brcmstb: Use the DT "compatible" string to indicate bit positions power/reset: brcmstb: Make the driver buildable on MIPS power: charger-manager: Use alarmtimer for battery monitoring in suspend. power/reset: at91-poweroff: Fix error handling and other compiler warnings bq27x00_battery: Call power_supply_changed only when capacity changed bq27x00_battery: fix register offset for bq27425 power: max14577: Remove SYSFS dependency from Kconfig power: bq24190_charger: suppress build warning power: reset: Add reset driver for R-Mobile platforms ...
This commit is contained in:
commit
13c071907b
@ -32,3 +32,45 @@ Description:
|
||||
Valid values:
|
||||
- 5, 6 or 7 (hours),
|
||||
- 0: disabled.
|
||||
|
||||
What: /sys/class/power_supply/max77693-charger/device/fast_charge_timer
|
||||
Date: January 2015
|
||||
KernelVersion: 3.19.0
|
||||
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
Description:
|
||||
This entry shows and sets the maximum time the max77693
|
||||
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:
|
||||
- 4 - 16 (hours), step by 2 (rounded down)
|
||||
- 0: disabled.
|
||||
|
||||
What: /sys/class/power_supply/max77693-charger/device/top_off_threshold_current
|
||||
Date: January 2015
|
||||
KernelVersion: 3.19.0
|
||||
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
Description:
|
||||
This entry shows and sets the charging current threshold for
|
||||
entering top-off charging mode. When charging current in fast
|
||||
charge mode drops below this value, the charger will trigger
|
||||
interrupt and start top-off charging mode.
|
||||
|
||||
Valid values:
|
||||
- 100000 - 200000 (microamps), step by 25000 (rounded down)
|
||||
- 200000 - 350000 (microamps), step by 50000 (rounded down)
|
||||
- 0: disabled.
|
||||
|
||||
What: /sys/class/power_supply/max77693-charger/device/top_off_timer
|
||||
Date: January 2015
|
||||
KernelVersion: 3.19.0
|
||||
Contact: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
Description:
|
||||
This entry shows and sets the maximum time the max77693
|
||||
charger operates in top-off charge mode. When the timer expires
|
||||
the device will terminate top-off charge mode (charging current
|
||||
will drop to 0 A) and will trigger interrupt.
|
||||
|
||||
Valid values:
|
||||
- 0 - 70 (minutes), step by 10 (rounded down)
|
||||
|
@ -79,7 +79,9 @@ reboot
|
||||
Required properties
|
||||
|
||||
- compatible
|
||||
The string property "brcm,brcmstb-reboot".
|
||||
The string property "brcm,brcmstb-reboot" for 40nm/28nm chips with
|
||||
the new SYS_CTRL interface, or "brcm,bcm7038-reboot" for 65nm
|
||||
chips with the old SUN_TOP_CTRL interface.
|
||||
|
||||
- syscon
|
||||
A phandle / integer array that points to the syscon node which describes
|
||||
|
@ -41,6 +41,41 @@ Optional properties:
|
||||
To get more informations, please refer to documentaion.
|
||||
[*] refer Documentation/devicetree/bindings/pwm/pwm.txt
|
||||
|
||||
- charger : Node configuring the charger driver.
|
||||
If present, required properties:
|
||||
- compatible : Must be "maxim,max77693-charger".
|
||||
|
||||
Optional properties (if not set, defaults will be used):
|
||||
- maxim,constant-microvolt : Battery constant voltage in uV. The charger
|
||||
will operate in fast charge constant current mode till battery voltage
|
||||
reaches this level. Then the charger will switch to fast charge constant
|
||||
voltage mode. Also vsys (system voltage) will be set to this value when
|
||||
DC power is supplied but charger is not enabled.
|
||||
Valid values: 3650000 - 4400000, step by 25000 (rounded down)
|
||||
Default: 4200000
|
||||
|
||||
- maxim,min-system-microvolt : Minimal system voltage in uV.
|
||||
Valid values: 3000000 - 3700000, step by 100000 (rounded down)
|
||||
Default: 3600000
|
||||
|
||||
- maxim,thermal-regulation-celsius : Temperature in Celsius for entering
|
||||
high temperature charging mode. If die temperature exceeds this value
|
||||
the charging current will be reduced by 105 mA/Celsius.
|
||||
Valid values: 70, 85, 100, 115
|
||||
Default: 100
|
||||
|
||||
- maxim,battery-overcurrent-microamp : Overcurrent protection threshold
|
||||
in uA (current from battery to system).
|
||||
Valid values: 2000000 - 3500000, step by 250000 (rounded down)
|
||||
Default: 3500000
|
||||
|
||||
- maxim,charge-input-threshold-microvolt : Threshold voltage in uV for
|
||||
triggering input voltage regulation loop. If input voltage decreases
|
||||
below this value, the input current will be reduced to reach the
|
||||
threshold voltage.
|
||||
Valid values: 4300000, 4700000, 4800000, 4900000
|
||||
Default: 4300000
|
||||
|
||||
Example:
|
||||
max77693@66 {
|
||||
compatible = "maxim,max77693";
|
||||
@ -73,4 +108,14 @@ Example:
|
||||
pwms = <&pwm 0 40000 0>;
|
||||
pwm-names = "haptic";
|
||||
};
|
||||
|
||||
charger {
|
||||
compatible = "maxim,max77693-charger";
|
||||
|
||||
maxim,constant-microvolt = <4200000>;
|
||||
maxim,min-system-microvolt = <3600000>;
|
||||
maxim,thermal-regulation-celsius = <75>;
|
||||
maxim,battery-overcurrent-microamp = <3000000>;
|
||||
maxim,charge-input-threshold-microvolt = <4300000>;
|
||||
};
|
||||
};
|
||||
|
27
Documentation/devicetree/bindings/power/ltc2941.txt
Normal file
27
Documentation/devicetree/bindings/power/ltc2941.txt
Normal file
@ -0,0 +1,27 @@
|
||||
binding for LTC2941 and LTC2943 battery gauges
|
||||
|
||||
Both the LTC2941 and LTC2943 measure battery capacity.
|
||||
The LTC2943 is compatible with the LTC2941, it adds voltage and
|
||||
temperature monitoring, and uses a slightly different conversion
|
||||
formula for the charge counter.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "ltc2941" or "ltc2943" which also indicates the
|
||||
type of I2C chip attached.
|
||||
- reg: The 7-bit I2C address.
|
||||
- lltc,resistor-sense: The sense resistor value in milli-ohms. Can be a 32-bit
|
||||
negative value when the battery has been connected to the wrong end of the
|
||||
resistor.
|
||||
- lltc,prescaler-exponent: The prescaler exponent as explained in the datasheet.
|
||||
This determines the range and accuracy of the gauge. The value is programmed
|
||||
into the chip only if it differs from the current setting. The setting is
|
||||
lost when the battery is disconnected.
|
||||
|
||||
Example from the Topic Miami Florida board:
|
||||
|
||||
fuelgauge: ltc2943@64 {
|
||||
compatible = "ltc2943";
|
||||
reg = <0x64>;
|
||||
lltc,resistor-sense = <15>;
|
||||
lltc,prescaler-exponent = <5>; /* 2^(2*5) = 1024 */
|
||||
};
|
@ -1,20 +1,23 @@
|
||||
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.
|
||||
been sent, the chip's watchdog has to be reset to gracefully shut down.
|
||||
A full powerdown can be triggered 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
|
||||
|
||||
Optional properties:
|
||||
- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the
|
||||
chip's trigger line. If this property is not set, the
|
||||
trigger function is ignored and the chip is kept alive
|
||||
until an explicit kill signal is received
|
||||
|
||||
Example:
|
||||
|
||||
ltc2952 {
|
||||
|
@ -6165,6 +6165,13 @@ F: Documentation/devicetree/bindings/i2c/max6697.txt
|
||||
F: drivers/hwmon/max6697.c
|
||||
F: include/linux/platform_data/max6697.h
|
||||
|
||||
MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
|
||||
M: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/power/max14577_charger.c
|
||||
F: drivers/power/max77693_charger.c
|
||||
|
||||
MAXIRADIO FM RADIO RECEIVER DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_irq:
|
||||
power_supply_unregister(&info->usb);
|
||||
while (--i >= 0)
|
||||
free_irq(info->irq[i], info);
|
||||
out:
|
||||
|
@ -315,7 +315,7 @@ config CHARGER_GPIO
|
||||
|
||||
config CHARGER_MANAGER
|
||||
bool "Battery charger manager for multiple chargers"
|
||||
depends on REGULATOR && RTC_CLASS
|
||||
depends on REGULATOR
|
||||
select EXTCON
|
||||
help
|
||||
Say Y to enable charger-manager support, which allows multiple
|
||||
@ -327,11 +327,16 @@ config CHARGER_MANAGER
|
||||
config CHARGER_MAX14577
|
||||
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/77836 MUICs.
|
||||
|
||||
config CHARGER_MAX77693
|
||||
tristate "Maxim MAX77693 battery charger driver"
|
||||
depends on MFD_MAX77693
|
||||
help
|
||||
Say Y to enable support for the Maxim MAX77693 battery charger.
|
||||
|
||||
config CHARGER_MAX8997
|
||||
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
|
||||
depends on MFD_MAX8997 && REGULATOR_MAX8997
|
||||
@ -383,6 +388,14 @@ config CHARGER_TPS65090
|
||||
Say Y here to enable support for battery charging with TPS65090
|
||||
PMIC chips.
|
||||
|
||||
config BATTERY_GAUGE_LTC2941
|
||||
tristate "LTC2941/LTC2943 Battery Gauge Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to include support for LTC2941 and LTC2943 Battery
|
||||
Gauge IC. The driver reports the charge count continuously, and
|
||||
measures the voltage and temperature every 10 seconds.
|
||||
|
||||
config AB8500_BM
|
||||
bool "AB8500 Battery Management Driver"
|
||||
depends on AB8500_CORE && AB8500_GPADC
|
||||
@ -397,6 +410,14 @@ config BATTERY_GOLDFISH
|
||||
Say Y to enable support for the battery and AC power in the
|
||||
Goldfish emulator.
|
||||
|
||||
config BATTERY_RT5033
|
||||
tristate "RT5033 fuel gauge support"
|
||||
depends on MFD_RT5033
|
||||
help
|
||||
This adds support for battery fuel gauge in Richtek RT5033 PMIC.
|
||||
The fuelgauge calculates and determines the battery state of charge
|
||||
according to battery open circuit voltage.
|
||||
|
||||
source "drivers/power/reset/Kconfig"
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
||||
obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o
|
||||
obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
||||
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
||||
@ -34,6 +35,7 @@ obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
|
||||
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
|
||||
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
|
||||
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
|
||||
obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
|
||||
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
|
||||
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
|
||||
@ -50,6 +52,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
|
||||
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
|
||||
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
||||
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
|
@ -2435,20 +2435,6 @@ static void ab8500_fg_reinit_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values
|
||||
*
|
||||
* This function can be used to force the FG algorithm to recalculate a new
|
||||
* voltage based battery capacity.
|
||||
*/
|
||||
void ab8500_fg_reinit(void)
|
||||
{
|
||||
struct ab8500_fg *di = ab8500_fg_get();
|
||||
/* User won't be notified if a null pointer returned. */
|
||||
if (di != NULL)
|
||||
queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0);
|
||||
}
|
||||
|
||||
/* Exposure to the sysfs interface */
|
||||
|
||||
struct ab8500_fg_sysfs_entry {
|
||||
|
@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger)
|
||||
charger->properties = bq24190_charger_properties;
|
||||
charger->num_properties = ARRAY_SIZE(bq24190_charger_properties);
|
||||
charger->supplied_to = bq24190_charger_supplied_to;
|
||||
charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to);
|
||||
charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to);
|
||||
charger->get_property = bq24190_charger_get_property;
|
||||
charger->set_property = bq24190_charger_set_property;
|
||||
charger->property_is_writeable = bq24190_charger_property_is_writeable;
|
||||
@ -1208,7 +1208,7 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
||||
{
|
||||
struct bq24190_dev_info *bdi = data;
|
||||
bool alert_userspace = false;
|
||||
u8 ss_reg, f_reg;
|
||||
u8 ss_reg = 0, f_reg = 0;
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(bdi->dev);
|
||||
|
@ -76,7 +76,8 @@
|
||||
|
||||
/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
|
||||
#define BQ27425_REG_OFFSET 0x04
|
||||
#define BQ27425_REG_SOC 0x18 /* Register address plus offset */
|
||||
#define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET)
|
||||
#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET)
|
||||
|
||||
#define BQ27000_RS 20 /* Resistor sense */
|
||||
#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000)
|
||||
@ -282,9 +283,12 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
|
||||
{
|
||||
int ilmd;
|
||||
|
||||
if (bq27xxx_is_chip_version_higher(di))
|
||||
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
|
||||
else
|
||||
if (bq27xxx_is_chip_version_higher(di)) {
|
||||
if (di->chip == BQ27425)
|
||||
ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false);
|
||||
else
|
||||
ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
|
||||
} else
|
||||
ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
|
||||
|
||||
if (ilmd < 0) {
|
||||
@ -493,10 +497,11 @@ static void bq27x00_update(struct bq27x00_device_info *di)
|
||||
di->charge_design_full = bq27x00_battery_read_ilmd(di);
|
||||
}
|
||||
|
||||
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {
|
||||
di->cache = cache;
|
||||
if (di->cache.capacity != cache.capacity)
|
||||
power_supply_changed(&di->bat);
|
||||
}
|
||||
|
||||
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
|
||||
di->cache = cache;
|
||||
|
||||
di->last_update = jiffies;
|
||||
}
|
||||
|
@ -69,16 +69,10 @@ static LIST_HEAD(cm_list);
|
||||
static DEFINE_MUTEX(cm_list_mtx);
|
||||
|
||||
/* About in-suspend (suspend-again) monitoring */
|
||||
static struct rtc_device *rtc_dev;
|
||||
/*
|
||||
* Backup RTC alarm
|
||||
* Save the wakeup alarm before entering suspend-to-RAM
|
||||
*/
|
||||
static struct rtc_wkalrm rtc_wkalarm_save;
|
||||
/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
|
||||
static unsigned long rtc_wkalarm_save_time;
|
||||
static struct alarm *cm_timer;
|
||||
|
||||
static bool cm_suspended;
|
||||
static bool cm_rtc_set;
|
||||
static bool cm_timer_set;
|
||||
static unsigned long cm_suspend_duration_ms;
|
||||
|
||||
/* About normal (not suspended) monitoring */
|
||||
@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */
|
||||
static struct workqueue_struct *cm_wq; /* init at driver add */
|
||||
static struct delayed_work cm_monitor_work; /* init at driver add */
|
||||
|
||||
/* Global charger-manager description */
|
||||
static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
|
||||
|
||||
/**
|
||||
* is_batt_present - See if the battery presents in place.
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void)
|
||||
{
|
||||
struct charger_manager *cm;
|
||||
unsigned int wakeup_ms = UINT_MAX;
|
||||
bool ret = false;
|
||||
int timer_req = 0;
|
||||
|
||||
if (time_after(next_polling, jiffies))
|
||||
CM_MIN_VALID(wakeup_ms,
|
||||
jiffies_to_msecs(next_polling - jiffies));
|
||||
|
||||
mutex_lock(&cm_list_mtx);
|
||||
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
unsigned int fbchk_ms = 0;
|
||||
|
||||
@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void)
|
||||
/* Skip if polling is not required for this CM */
|
||||
if (!is_polling_required(cm) && !cm->emergency_stop)
|
||||
continue;
|
||||
timer_req++;
|
||||
if (cm->desc->polling_interval_ms == 0)
|
||||
continue;
|
||||
CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
|
||||
}
|
||||
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
if (wakeup_ms < UINT_MAX && wakeup_ms > 0) {
|
||||
if (timer_req && cm_timer) {
|
||||
ktime_t now, add;
|
||||
|
||||
/*
|
||||
* Set alarm with the polling interval (wakeup_ms)
|
||||
* The alarm time should be NOW + CM_RTC_SMALL or later.
|
||||
*/
|
||||
if (wakeup_ms == UINT_MAX ||
|
||||
wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
|
||||
wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
|
||||
|
||||
pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
|
||||
if (rtc_dev) {
|
||||
struct rtc_wkalrm tmp;
|
||||
unsigned long time, now;
|
||||
unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
|
||||
|
||||
/*
|
||||
* Set alarm with the polling interval (wakeup_ms)
|
||||
* except when rtc_wkalarm_save comes first.
|
||||
* However, the alarm time should be NOW +
|
||||
* CM_RTC_SMALL or later.
|
||||
*/
|
||||
tmp.enabled = 1;
|
||||
rtc_read_time(rtc_dev, &tmp.time);
|
||||
rtc_tm_to_time(&tmp.time, &now);
|
||||
if (add < CM_RTC_SMALL)
|
||||
add = CM_RTC_SMALL;
|
||||
time = now + add;
|
||||
now = ktime_get_boottime();
|
||||
add = ktime_set(wakeup_ms / MSEC_PER_SEC,
|
||||
(wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
|
||||
alarm_start(cm_timer, ktime_add(now, add));
|
||||
|
||||
ret = true;
|
||||
cm_suspend_duration_ms = wakeup_ms;
|
||||
|
||||
if (rtc_wkalarm_save.enabled &&
|
||||
rtc_wkalarm_save_time &&
|
||||
rtc_wkalarm_save_time < time) {
|
||||
if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
|
||||
time = now + CM_RTC_SMALL;
|
||||
else
|
||||
time = rtc_wkalarm_save_time;
|
||||
|
||||
/* The timer is not appointed by CM */
|
||||
ret = false;
|
||||
}
|
||||
|
||||
pr_info("Waking up after %lu secs\n", time - now);
|
||||
|
||||
rtc_time_to_tm(time, &tmp.time);
|
||||
rtc_set_alarm(rtc_dev, &tmp);
|
||||
cm_suspend_duration_ms += wakeup_ms;
|
||||
return ret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rtc_dev)
|
||||
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _cm_fbchk_in_suspend(struct charger_manager *cm)
|
||||
{
|
||||
unsigned long jiffy_now = jiffies;
|
||||
|
||||
if (!cm->fullbatt_vchk_jiffies_at)
|
||||
return;
|
||||
|
||||
if (g_desc && g_desc->assume_timer_stops_in_suspend)
|
||||
jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
|
||||
|
||||
/* Execute now if it's going to be executed not too long after */
|
||||
jiffy_now += CM_JIFFIES_SMALL;
|
||||
|
||||
if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
|
||||
fullbatt_vchk(&cm->fullbatt_vchk_work.work);
|
||||
}
|
||||
|
||||
/**
|
||||
* cm_suspend_again - Determine whether suspend again or not
|
||||
*
|
||||
* Returns true if the system should be suspended again
|
||||
* Returns false if the system should be woken up
|
||||
*/
|
||||
bool cm_suspend_again(void)
|
||||
{
|
||||
struct charger_manager *cm;
|
||||
bool ret = false;
|
||||
|
||||
if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
|
||||
!cm_rtc_set)
|
||||
return false;
|
||||
|
||||
if (cm_monitor())
|
||||
goto out;
|
||||
|
||||
ret = true;
|
||||
mutex_lock(&cm_list_mtx);
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
_cm_fbchk_in_suspend(cm);
|
||||
|
||||
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
|
||||
cm->status_save_batt != is_batt_present(cm)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
cm_rtc_set = cm_setup_timer();
|
||||
out:
|
||||
/* It's about the time when the non-CM appointed timer goes off */
|
||||
if (rtc_wkalarm_save.enabled) {
|
||||
unsigned long now;
|
||||
struct rtc_time tmp;
|
||||
|
||||
rtc_read_time(rtc_dev, &tmp);
|
||||
rtc_tm_to_time(&tmp, &now);
|
||||
|
||||
if (rtc_wkalarm_save_time &&
|
||||
now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
|
||||
return false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cm_suspend_again);
|
||||
|
||||
/**
|
||||
* setup_charger_manager - initialize charger_global_desc data
|
||||
* @gd: pointer to instance of charger_global_desc
|
||||
*/
|
||||
int setup_charger_manager(struct charger_global_desc *gd)
|
||||
{
|
||||
if (!gd)
|
||||
return -EINVAL;
|
||||
|
||||
if (rtc_dev)
|
||||
rtc_class_close(rtc_dev);
|
||||
rtc_dev = NULL;
|
||||
g_desc = NULL;
|
||||
|
||||
if (!gd->rtc_only_wakeup) {
|
||||
pr_err("The callback rtc_only_wakeup is not given\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gd->rtc_name) {
|
||||
rtc_dev = rtc_class_open(gd->rtc_name);
|
||||
if (IS_ERR_OR_NULL(rtc_dev)) {
|
||||
rtc_dev = NULL;
|
||||
/* Retry at probe. RTC may be not registered yet */
|
||||
}
|
||||
} else {
|
||||
pr_warn("No wakeup timer is given for charger manager. "
|
||||
"In-suspend monitoring won't work.\n");
|
||||
}
|
||||
|
||||
g_desc = gd;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(setup_charger_manager);
|
||||
|
||||
/**
|
||||
* charger_extcon_work - enable/diable charger according to the state
|
||||
* of charger cable
|
||||
@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
|
||||
return dev_get_platdata(&pdev->dev);
|
||||
}
|
||||
|
||||
static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
|
||||
{
|
||||
cm_timer_set = false;
|
||||
return ALARMTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int charger_manager_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct charger_desc *desc = cm_get_drv_data(pdev);
|
||||
@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
union power_supply_propval val;
|
||||
struct power_supply *fuel_gauge;
|
||||
|
||||
if (g_desc && !rtc_dev && g_desc->rtc_name) {
|
||||
rtc_dev = rtc_class_open(g_desc->rtc_name);
|
||||
if (IS_ERR_OR_NULL(rtc_dev)) {
|
||||
rtc_dev = NULL;
|
||||
dev_err(&pdev->dev, "Cannot get RTC %s\n",
|
||||
g_desc->rtc_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR(desc)) {
|
||||
dev_err(&pdev->dev, "No platform data (desc) found\n");
|
||||
return -ENODEV;
|
||||
@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
cm->dev = &pdev->dev;
|
||||
cm->desc = desc;
|
||||
|
||||
/* Initialize alarm timer */
|
||||
if (alarmtimer_get_rtcdev()) {
|
||||
cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
|
||||
alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following two do not need to be errors.
|
||||
* Users may intentionally ignore those two features.
|
||||
@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool cm_need_to_awake(void)
|
||||
{
|
||||
struct charger_manager *cm;
|
||||
|
||||
if (cm_timer)
|
||||
return false;
|
||||
|
||||
mutex_lock(&cm_list_mtx);
|
||||
list_for_each_entry(cm, &cm_list, entry) {
|
||||
if (is_charging(cm)) {
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cm_suspend_prepare(struct device *dev)
|
||||
{
|
||||
struct charger_manager *cm = dev_get_drvdata(dev);
|
||||
|
||||
if (!cm_suspended) {
|
||||
if (rtc_dev) {
|
||||
struct rtc_time tmp;
|
||||
unsigned long now;
|
||||
if (cm_need_to_awake())
|
||||
return -EBUSY;
|
||||
|
||||
rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
|
||||
rtc_read_time(rtc_dev, &tmp);
|
||||
|
||||
if (rtc_wkalarm_save.enabled) {
|
||||
rtc_tm_to_time(&rtc_wkalarm_save.time,
|
||||
&rtc_wkalarm_save_time);
|
||||
rtc_tm_to_time(&tmp, &now);
|
||||
if (now > rtc_wkalarm_save_time)
|
||||
rtc_wkalarm_save_time = 0;
|
||||
} else {
|
||||
rtc_wkalarm_save_time = 0;
|
||||
}
|
||||
}
|
||||
if (!cm_suspended)
|
||||
cm_suspended = true;
|
||||
}
|
||||
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
|
||||
cm->status_save_batt = is_batt_present(cm);
|
||||
cm_timer_set = cm_setup_timer();
|
||||
|
||||
if (!cm_rtc_set) {
|
||||
cm_suspend_duration_ms = 0;
|
||||
cm_rtc_set = cm_setup_timer();
|
||||
if (cm_timer_set) {
|
||||
cancel_work_sync(&setup_polling);
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev)
|
||||
{
|
||||
struct charger_manager *cm = dev_get_drvdata(dev);
|
||||
|
||||
if (cm_suspended) {
|
||||
if (rtc_dev) {
|
||||
struct rtc_wkalrm tmp;
|
||||
|
||||
rtc_read_alarm(rtc_dev, &tmp);
|
||||
rtc_wkalarm_save.pending = tmp.pending;
|
||||
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
|
||||
}
|
||||
if (cm_suspended)
|
||||
cm_suspended = false;
|
||||
cm_rtc_set = false;
|
||||
|
||||
if (cm_timer_set) {
|
||||
ktime_t remain;
|
||||
|
||||
alarm_cancel(cm_timer);
|
||||
cm_timer_set = false;
|
||||
remain = alarm_expires_remaining(cm_timer);
|
||||
cm_suspend_duration_ms -= ktime_to_ms(remain);
|
||||
schedule_work(&setup_polling);
|
||||
}
|
||||
|
||||
_cm_monitor(cm);
|
||||
|
||||
/* Re-enqueue delayed work (fullbatt_vchk_work) */
|
||||
if (cm->fullbatt_vchk_jiffies_at) {
|
||||
unsigned long delay = 0;
|
||||
@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* Account for cm_suspend_duration_ms if
|
||||
* assume_timer_stops_in_suspend is active
|
||||
* Account for cm_suspend_duration_ms with assuming that
|
||||
* timer stops in suspend.
|
||||
*/
|
||||
if (g_desc && g_desc->assume_timer_stops_in_suspend) {
|
||||
if (delay > cm_suspend_duration_ms)
|
||||
delay -= cm_suspend_duration_ms;
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
if (delay > cm_suspend_duration_ms)
|
||||
delay -= cm_suspend_duration_ms;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
|
||||
msecs_to_jiffies(delay));
|
||||
}
|
||||
device_set_wakeup_capable(cm->dev, false);
|
||||
uevent_notify(cm, NULL);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops charger_manager_pm = {
|
||||
|
@ -26,6 +26,7 @@
|
||||
static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
|
||||
static struct work_struct bat_work;
|
||||
static struct ucb1x00 *ucb;
|
||||
static int wakeup_enabled;
|
||||
|
||||
struct collie_bat {
|
||||
int status;
|
||||
@ -291,11 +292,21 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev)
|
||||
{
|
||||
/* flush all pending status updates */
|
||||
flush_work(&bat_work);
|
||||
|
||||
if (device_may_wakeup(&dev->ucb->dev) &&
|
||||
collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
|
||||
wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
|
||||
else
|
||||
wakeup_enabled = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int collie_bat_resume(struct ucb1x00_dev *dev)
|
||||
{
|
||||
if (wakeup_enabled)
|
||||
disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
|
||||
|
||||
/* things may have changed while we were away */
|
||||
schedule_work(&bat_work);
|
||||
return 0;
|
||||
@ -334,10 +345,15 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
|
||||
collie_bat_gpio_isr,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"main full", &collie_bat_main);
|
||||
if (!ret) {
|
||||
schedule_work(&bat_work);
|
||||
return 0;
|
||||
}
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
device_init_wakeup(&ucb->dev, 1);
|
||||
schedule_work(&bat_work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
power_supply_unregister(&collie_bat_bu.psy);
|
||||
err_psy_reg_bu:
|
||||
power_supply_unregister(&collie_bat_main.psy);
|
||||
|
@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev)
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
gpio_charger->wakeup_enabled =
|
||||
enable_irq_wake(gpio_charger->irq);
|
||||
!enable_irq_wake(gpio_charger->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
|
||||
|
||||
if (gpio_charger->wakeup_enabled)
|
||||
if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
|
||||
disable_irq_wake(gpio_charger->irq);
|
||||
power_supply_changed(&gpio_charger->charger);
|
||||
|
||||
|
547
drivers/power/ltc2941-battery-gauge.c
Normal file
547
drivers/power/ltc2941-battery-gauge.c
Normal file
@ -0,0 +1,547 @@
|
||||
/*
|
||||
* I2C client/driver for the Linear Technology LTC2941 and LTC2943
|
||||
* Battery Gas Gauge IC
|
||||
*
|
||||
* Copyright (C) 2014 Topic Embedded Systems
|
||||
*
|
||||
* Author: Auryn Verwegen
|
||||
* Author: Mike Looijmans
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/swab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define I16_MSB(x) ((x >> 8) & 0xFF)
|
||||
#define I16_LSB(x) (x & 0xFF)
|
||||
|
||||
#define LTC294X_WORK_DELAY 10 /* Update delay in seconds */
|
||||
|
||||
#define LTC294X_MAX_VALUE 0xFFFF
|
||||
#define LTC294X_MID_SUPPLY 0x7FFF
|
||||
|
||||
#define LTC2941_MAX_PRESCALER_EXP 7
|
||||
#define LTC2943_MAX_PRESCALER_EXP 6
|
||||
|
||||
enum ltc294x_reg {
|
||||
LTC294X_REG_STATUS = 0x00,
|
||||
LTC294X_REG_CONTROL = 0x01,
|
||||
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
|
||||
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
|
||||
LTC294X_REG_THRESH_HIGH_MSB = 0x04,
|
||||
LTC294X_REG_THRESH_HIGH_LSB = 0x05,
|
||||
LTC294X_REG_THRESH_LOW_MSB = 0x06,
|
||||
LTC294X_REG_THRESH_LOW_LSB = 0x07,
|
||||
LTC294X_REG_VOLTAGE_MSB = 0x08,
|
||||
LTC294X_REG_VOLTAGE_LSB = 0x09,
|
||||
LTC294X_REG_CURRENT_MSB = 0x0E,
|
||||
LTC294X_REG_CURRENT_LSB = 0x0F,
|
||||
LTC294X_REG_TEMPERATURE_MSB = 0x14,
|
||||
LTC294X_REG_TEMPERATURE_LSB = 0x15,
|
||||
};
|
||||
|
||||
#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
|
||||
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
|
||||
#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3))
|
||||
#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0))
|
||||
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
|
||||
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
|
||||
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
|
||||
|
||||
#define LTC2941_NUM_REGS 0x08
|
||||
#define LTC2943_NUM_REGS 0x18
|
||||
|
||||
struct ltc294x_info {
|
||||
struct i2c_client *client; /* I2C Client pointer */
|
||||
struct power_supply supply; /* Supply pointer */
|
||||
struct delayed_work work; /* Work scheduler */
|
||||
int num_regs; /* Number of registers (chip type) */
|
||||
int id; /* Identifier of ltc294x chip */
|
||||
int charge; /* Last charge register content */
|
||||
int r_sense; /* mOhm */
|
||||
int Qlsb; /* nAh */
|
||||
};
|
||||
|
||||
static DEFINE_IDR(ltc294x_id);
|
||||
static DEFINE_MUTEX(ltc294x_lock);
|
||||
|
||||
static inline int convert_bin_to_uAh(
|
||||
const struct ltc294x_info *info, int Q)
|
||||
{
|
||||
return ((Q * (info->Qlsb / 10))) / 100;
|
||||
}
|
||||
|
||||
static inline int convert_uAh_to_bin(
|
||||
const struct ltc294x_info *info, int uAh)
|
||||
{
|
||||
int Q;
|
||||
|
||||
Q = (uAh * 100) / (info->Qlsb/10);
|
||||
return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE;
|
||||
}
|
||||
|
||||
static int ltc294x_read_regs(struct i2c_client *client,
|
||||
enum ltc294x_reg reg, u8 *buf, int num_regs)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msgs[2] = { };
|
||||
u8 reg_start = reg;
|
||||
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = ®_start;
|
||||
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].len = num_regs;
|
||||
msgs[1].buf = buf;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msgs[0], 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "ltc2941 read_reg failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
|
||||
__func__, reg, num_regs, *buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_write_regs(struct i2c_client *client,
|
||||
enum ltc294x_reg reg, const u8 *buf, int num_regs)
|
||||
{
|
||||
int ret;
|
||||
u8 reg_start = reg;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "ltc2941 write_reg failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
|
||||
__func__, reg, num_regs, *buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
|
||||
{
|
||||
int ret;
|
||||
u8 value;
|
||||
u8 control;
|
||||
|
||||
/* Read status and control registers */
|
||||
ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->client->dev,
|
||||
"Could not read registers from device\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
|
||||
LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
|
||||
/* Put the 2943 into "monitor" mode, so it measures every 10 sec */
|
||||
if (info->num_regs == LTC2943_NUM_REGS)
|
||||
control |= LTC2943_REG_CONTROL_MODE_SCAN;
|
||||
|
||||
if (value != control) {
|
||||
ret = ltc294x_write_regs(info->client,
|
||||
LTC294X_REG_CONTROL, &control, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->client->dev,
|
||||
"Could not write register\n");
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc294x_read_charge_register(const struct ltc294x_info *info)
|
||||
{
|
||||
int ret;
|
||||
u8 datar[2];
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return (datar[0] << 8) + datar[1];
|
||||
}
|
||||
|
||||
static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
int value = ltc294x_read_charge_register(info);
|
||||
|
||||
if (value < 0)
|
||||
return value;
|
||||
/* When r_sense < 0, this counts up when the battery discharges */
|
||||
if (info->Qlsb < 0)
|
||||
value -= 0xFFFF;
|
||||
*val = convert_bin_to_uAh(info, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
|
||||
{
|
||||
int ret;
|
||||
u8 dataw[2];
|
||||
u8 ctrl_reg;
|
||||
s32 value;
|
||||
|
||||
value = convert_uAh_to_bin(info, val);
|
||||
/* Direction depends on how sense+/- were connected */
|
||||
if (info->Qlsb < 0)
|
||||
value += 0xFFFF;
|
||||
if ((value < 0) || (value > 0xFFFF)) /* input validation */
|
||||
return -EINVAL;
|
||||
|
||||
/* Read control register */
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_CONTROL, &ctrl_reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Disable analog section */
|
||||
ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK;
|
||||
ret = ltc294x_write_regs(info->client,
|
||||
LTC294X_REG_CONTROL, &ctrl_reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Set new charge value */
|
||||
dataw[0] = I16_MSB(value);
|
||||
dataw[1] = I16_LSB(value);
|
||||
ret = ltc294x_write_regs(info->client,
|
||||
LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2);
|
||||
if (ret < 0)
|
||||
goto error_exit;
|
||||
/* Enable analog section */
|
||||
error_exit:
|
||||
ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK;
|
||||
ret = ltc294x_write_regs(info->client,
|
||||
LTC294X_REG_CONTROL, &ctrl_reg, 1);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int ltc294x_get_charge_counter(
|
||||
const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
int value = ltc294x_read_charge_register(info);
|
||||
|
||||
if (value < 0)
|
||||
return value;
|
||||
value -= LTC294X_MID_SUPPLY;
|
||||
*val = convert_bin_to_uAh(info, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
int ret;
|
||||
u8 datar[2];
|
||||
u32 value;
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
*val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
int ret;
|
||||
u8 datar[2];
|
||||
s32 value;
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_CURRENT_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
value -= 0x7FFF;
|
||||
/* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
|
||||
* the formula below keeps everything in s32 range while preserving
|
||||
* enough digits */
|
||||
*val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
|
||||
{
|
||||
int ret;
|
||||
u8 datar[2];
|
||||
u32 value;
|
||||
|
||||
ret = ltc294x_read_regs(info->client,
|
||||
LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
|
||||
value = (datar[0] << 8) | datar[1];
|
||||
/* Full-scale is 510 Kelvin, convert to centidegrees */
|
||||
*val = (((51000 * value) / 0xFFFF) - 27215);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc294x_get_property(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct ltc294x_info *info =
|
||||
container_of(psy, struct ltc294x_info, supply);
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
return ltc294x_get_charge_now(info, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||
return ltc294x_get_charge_counter(info, &val->intval);
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
return ltc294x_get_voltage(info, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
return ltc294x_get_current(info, &val->intval);
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
return ltc294x_get_temperature(info, &val->intval);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ltc294x_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct ltc294x_info *info =
|
||||
container_of(psy, struct ltc294x_info, supply);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
return ltc294x_set_charge_now(info, val->intval);
|
||||
default:
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
static int ltc294x_property_is_writeable(
|
||||
struct power_supply *psy, enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ltc294x_update(struct ltc294x_info *info)
|
||||
{
|
||||
int charge = ltc294x_read_charge_register(info);
|
||||
|
||||
if (charge != info->charge) {
|
||||
info->charge = charge;
|
||||
power_supply_changed(&info->supply);
|
||||
}
|
||||
}
|
||||
|
||||
static void ltc294x_work(struct work_struct *work)
|
||||
{
|
||||
struct ltc294x_info *info;
|
||||
|
||||
info = container_of(work, struct ltc294x_info, work.work);
|
||||
ltc294x_update(info);
|
||||
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
|
||||
}
|
||||
|
||||
static enum power_supply_property ltc294x_properties[] = {
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
};
|
||||
|
||||
static int ltc294x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ltc294x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work(&info->work);
|
||||
power_supply_unregister(&info->supply);
|
||||
kfree(info->supply.name);
|
||||
mutex_lock(<c294x_lock);
|
||||
idr_remove(<c294x_id, info->id);
|
||||
mutex_unlock(<c294x_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ltc294x_info *info;
|
||||
int ret;
|
||||
int num;
|
||||
u32 prescaler_exp;
|
||||
s32 r_sense;
|
||||
struct device_node *np;
|
||||
|
||||
mutex_lock(<c294x_lock);
|
||||
ret = idr_alloc(<c294x_id, client, 0, 0, GFP_KERNEL);
|
||||
mutex_unlock(<c294x_lock);
|
||||
if (ret < 0)
|
||||
goto fail_id;
|
||||
|
||||
num = ret;
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_info;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, info);
|
||||
|
||||
info->num_regs = id->driver_data;
|
||||
info->supply.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
|
||||
if (!info->supply.name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_name;
|
||||
}
|
||||
|
||||
np = of_node_get(client->dev.of_node);
|
||||
|
||||
/* r_sense can be negative, when sense+ is connected to the battery
|
||||
* instead of the sense-. This results in reversed measurements. */
|
||||
ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Could not find lltc,resistor-sense in devicetree\n");
|
||||
goto fail_name;
|
||||
}
|
||||
info->r_sense = r_sense;
|
||||
|
||||
ret = of_property_read_u32(np, "lltc,prescaler-exponent",
|
||||
&prescaler_exp);
|
||||
if (ret < 0) {
|
||||
dev_warn(&client->dev,
|
||||
"lltc,prescaler-exponent not in devicetree\n");
|
||||
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
|
||||
}
|
||||
|
||||
if (info->num_regs == LTC2943_NUM_REGS) {
|
||||
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
|
||||
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
|
||||
info->Qlsb = ((340 * 50000) / r_sense) /
|
||||
(4096 / (1 << (2*prescaler_exp)));
|
||||
} else {
|
||||
if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP)
|
||||
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
|
||||
info->Qlsb = ((58 * 50000) / r_sense) /
|
||||
(128 / (1 << prescaler_exp));
|
||||
}
|
||||
|
||||
info->client = client;
|
||||
info->id = num;
|
||||
info->supply.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
info->supply.properties = ltc294x_properties;
|
||||
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
|
||||
info->supply.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties);
|
||||
else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
|
||||
info->supply.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 1;
|
||||
else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
|
||||
info->supply.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 2;
|
||||
else
|
||||
info->supply.num_properties =
|
||||
ARRAY_SIZE(ltc294x_properties) - 3;
|
||||
info->supply.get_property = ltc294x_get_property;
|
||||
info->supply.set_property = ltc294x_set_property;
|
||||
info->supply.property_is_writeable = ltc294x_property_is_writeable;
|
||||
info->supply.external_power_changed = NULL;
|
||||
|
||||
INIT_DELAYED_WORK(&info->work, ltc294x_work);
|
||||
|
||||
ret = ltc294x_reset(info, prescaler_exp);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Communication with chip failed\n");
|
||||
goto fail_comm;
|
||||
}
|
||||
|
||||
ret = power_supply_register(&client->dev, &info->supply);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register ltc2941\n");
|
||||
goto fail_register;
|
||||
} else {
|
||||
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register:
|
||||
kfree(info->supply.name);
|
||||
fail_comm:
|
||||
fail_name:
|
||||
fail_info:
|
||||
mutex_lock(<c294x_lock);
|
||||
idr_remove(<c294x_id, num);
|
||||
mutex_unlock(<c294x_lock);
|
||||
fail_id:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int ltc294x_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ltc294x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work(&info->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc294x_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ltc294x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
|
||||
#define LTC294X_PM_OPS (<c294x_pm_ops)
|
||||
|
||||
#else
|
||||
#define LTC294X_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
|
||||
static const struct i2c_device_id ltc294x_i2c_id[] = {
|
||||
{"ltc2941", LTC2941_NUM_REGS},
|
||||
{"ltc2943", LTC2943_NUM_REGS},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
|
||||
|
||||
static struct i2c_driver ltc294x_driver = {
|
||||
.driver = {
|
||||
.name = "LTC2941",
|
||||
.pm = LTC294X_PM_OPS,
|
||||
},
|
||||
.probe = ltc294x_i2c_probe,
|
||||
.remove = ltc294x_i2c_remove,
|
||||
.id_table = ltc294x_i2c_id,
|
||||
};
|
||||
module_i2c_driver(ltc294x_driver);
|
||||
|
||||
MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
|
||||
MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
|
||||
MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -658,7 +658,7 @@ max17042_get_pdata(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct regmap_config max17042_regmap_config = {
|
||||
static const struct regmap_config max17042_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.val_format_endian = REGMAP_ENDIAN_NATIVE,
|
||||
|
753
drivers/power/max77693_charger.c
Normal file
753
drivers/power/max77693_charger.c
Normal file
@ -0,0 +1,753 @@
|
||||
/*
|
||||
* max77693_charger.c - Battery charger driver for the Maxim 77693
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.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/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/max77693.h>
|
||||
#include <linux/mfd/max77693-private.h>
|
||||
|
||||
static const char *max77693_charger_name = "max77693-charger";
|
||||
static const char *max77693_charger_model = "MAX77693";
|
||||
static const char *max77693_charger_manufacturer = "Maxim Integrated";
|
||||
|
||||
struct max77693_charger {
|
||||
struct device *dev;
|
||||
struct max77693_dev *max77693;
|
||||
struct power_supply charger;
|
||||
|
||||
u32 constant_volt;
|
||||
u32 min_system_volt;
|
||||
u32 thermal_regulation_temp;
|
||||
u32 batttery_overcurrent;
|
||||
u32 charge_input_threshold_volt;
|
||||
};
|
||||
|
||||
static int max77693_get_charger_state(struct regmap *regmap)
|
||||
{
|
||||
int state;
|
||||
unsigned int data;
|
||||
|
||||
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
|
||||
return POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
|
||||
data &= CHG_DETAILS_01_CHG_MASK;
|
||||
data >>= CHG_DETAILS_01_CHG_SHIFT;
|
||||
|
||||
switch (data) {
|
||||
case MAX77693_CHARGING_PREQUALIFICATION:
|
||||
case MAX77693_CHARGING_FAST_CONST_CURRENT:
|
||||
case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
|
||||
case MAX77693_CHARGING_TOP_OFF:
|
||||
/* In high temp the charging current is reduced, but still charging */
|
||||
case MAX77693_CHARGING_HIGH_TEMP:
|
||||
state = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case MAX77693_CHARGING_DONE:
|
||||
state = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
case MAX77693_CHARGING_TIMER_EXPIRED:
|
||||
case MAX77693_CHARGING_THERMISTOR_SUSPEND:
|
||||
state = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
case MAX77693_CHARGING_OFF:
|
||||
case MAX77693_CHARGING_OVER_TEMP:
|
||||
case MAX77693_CHARGING_WATCHDOG_EXPIRED:
|
||||
state = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case MAX77693_CHARGING_RESERVED:
|
||||
default:
|
||||
state = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int max77693_get_charge_type(struct regmap *regmap)
|
||||
{
|
||||
int state;
|
||||
unsigned int data;
|
||||
|
||||
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
|
||||
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
||||
|
||||
data &= CHG_DETAILS_01_CHG_MASK;
|
||||
data >>= CHG_DETAILS_01_CHG_SHIFT;
|
||||
|
||||
switch (data) {
|
||||
case MAX77693_CHARGING_PREQUALIFICATION:
|
||||
/*
|
||||
* Top-off: trickle or fast? In top-off the current varies between
|
||||
* 100 and 250 mA. It is higher than prequalification current.
|
||||
*/
|
||||
case MAX77693_CHARGING_TOP_OFF:
|
||||
state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
break;
|
||||
case MAX77693_CHARGING_FAST_CONST_CURRENT:
|
||||
case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
|
||||
/* In high temp the charging current is reduced, but still charging */
|
||||
case MAX77693_CHARGING_HIGH_TEMP:
|
||||
state = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
break;
|
||||
case MAX77693_CHARGING_DONE:
|
||||
case MAX77693_CHARGING_TIMER_EXPIRED:
|
||||
case MAX77693_CHARGING_THERMISTOR_SUSPEND:
|
||||
case MAX77693_CHARGING_OFF:
|
||||
case MAX77693_CHARGING_OVER_TEMP:
|
||||
case MAX77693_CHARGING_WATCHDOG_EXPIRED:
|
||||
state = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
case MAX77693_CHARGING_RESERVED:
|
||||
default:
|
||||
state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Supported health statuses:
|
||||
* - POWER_SUPPLY_HEALTH_DEAD
|
||||
* - POWER_SUPPLY_HEALTH_GOOD
|
||||
* - POWER_SUPPLY_HEALTH_OVERVOLTAGE
|
||||
* - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
|
||||
* - POWER_SUPPLY_HEALTH_UNKNOWN
|
||||
* - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
|
||||
*/
|
||||
static int max77693_get_battery_health(struct regmap *regmap)
|
||||
{
|
||||
int state;
|
||||
unsigned int data;
|
||||
|
||||
if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0)
|
||||
return POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
|
||||
data &= CHG_DETAILS_01_BAT_MASK;
|
||||
data >>= CHG_DETAILS_01_BAT_SHIFT;
|
||||
|
||||
switch (data) {
|
||||
case MAX77693_BATTERY_NOBAT:
|
||||
state = POWER_SUPPLY_HEALTH_DEAD;
|
||||
break;
|
||||
case MAX77693_BATTERY_PREQUALIFICATION:
|
||||
case MAX77693_BATTERY_GOOD:
|
||||
case MAX77693_BATTERY_LOWVOLTAGE:
|
||||
state = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case MAX77693_BATTERY_TIMER_EXPIRED:
|
||||
/*
|
||||
* Took longer to charge than expected, charging suspended.
|
||||
* Damaged battery?
|
||||
*/
|
||||
state = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
|
||||
break;
|
||||
case MAX77693_BATTERY_OVERVOLTAGE:
|
||||
state = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
break;
|
||||
case MAX77693_BATTERY_OVERCURRENT:
|
||||
state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
break;
|
||||
case MAX77693_BATTERY_RESERVED:
|
||||
default:
|
||||
state = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int max77693_get_present(struct regmap *regmap)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
/*
|
||||
* Read CHG_INT_OK register. High DETBAT bit here should be
|
||||
* equal to value 0x0 in CHG_DETAILS_01/BAT field.
|
||||
*/
|
||||
regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
|
||||
if (data & CHG_INT_OK_DETBAT_MASK)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int max77693_get_online(struct regmap *regmap)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
|
||||
if (data & CHG_INT_OK_CHGIN_MASK)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property max77693_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static int max77693_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max77693_charger *chg = container_of(psy,
|
||||
struct max77693_charger,
|
||||
charger);
|
||||
struct regmap *regmap = chg->max77693->regmap;
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = max77693_get_charger_state(regmap);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
val->intval = max77693_get_charge_type(regmap);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = max77693_get_battery_health(regmap);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = max77693_get_present(regmap);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = max77693_get_online(regmap);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = max77693_charger_model;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = max77693_charger_manufacturer;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t device_attr_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count,
|
||||
int (*fn)(struct max77693_charger *, unsigned long))
|
||||
{
|
||||
struct max77693_charger *chg = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fn(chg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t fast_charge_timer_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct max77693_charger *chg = dev_get_drvdata(dev);
|
||||
unsigned int data, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01,
|
||||
&data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data &= CHG_CNFG_01_FCHGTIME_MASK;
|
||||
data >>= CHG_CNFG_01_FCHGTIME_SHIFT;
|
||||
switch (data) {
|
||||
case 0x1 ... 0x7:
|
||||
/* Starting from 4 hours, step by 2 hours */
|
||||
val = 4 + (data - 1) * 2;
|
||||
break;
|
||||
case 0x0:
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
|
||||
}
|
||||
|
||||
static int max77693_set_fast_charge_timer(struct max77693_charger *chg,
|
||||
unsigned long hours)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
/*
|
||||
* 0x00 - disable
|
||||
* 0x01 - 4h
|
||||
* 0x02 - 6h
|
||||
* ...
|
||||
* 0x07 - 16h
|
||||
* Round down odd values.
|
||||
*/
|
||||
switch (hours) {
|
||||
case 4 ... 16:
|
||||
data = (hours - 4) / 2 + 1;
|
||||
break;
|
||||
case 0:
|
||||
/* Disable */
|
||||
data = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
data <<= CHG_CNFG_01_FCHGTIME_SHIFT;
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_01,
|
||||
CHG_CNFG_01_FCHGTIME_MASK, data);
|
||||
}
|
||||
|
||||
static ssize_t fast_charge_timer_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return device_attr_store(dev, attr, buf, count,
|
||||
max77693_set_fast_charge_timer);
|
||||
}
|
||||
|
||||
static ssize_t top_off_threshold_current_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct max77693_charger *chg = dev_get_drvdata(dev);
|
||||
unsigned int data, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
|
||||
&data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data &= CHG_CNFG_03_TOITH_MASK;
|
||||
data >>= CHG_CNFG_03_TOITH_SHIFT;
|
||||
|
||||
if (data <= 0x04)
|
||||
val = 100000 + data * 25000;
|
||||
else
|
||||
val = data * 50000;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
|
||||
}
|
||||
|
||||
static int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
|
||||
unsigned long uamp)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
if (uamp < 100000 || uamp > 350000)
|
||||
return -EINVAL;
|
||||
|
||||
if (uamp <= 200000)
|
||||
data = (uamp - 100000) / 25000;
|
||||
else
|
||||
/* (200000, 350000> */
|
||||
data = uamp / 50000;
|
||||
|
||||
data <<= CHG_CNFG_03_TOITH_SHIFT;
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_03,
|
||||
CHG_CNFG_03_TOITH_MASK, data);
|
||||
}
|
||||
|
||||
static ssize_t top_off_threshold_current_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return device_attr_store(dev, attr, buf, count,
|
||||
max77693_set_top_off_threshold_current);
|
||||
}
|
||||
|
||||
static ssize_t top_off_timer_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct max77693_charger *chg = dev_get_drvdata(dev);
|
||||
unsigned int data, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
|
||||
&data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data &= CHG_CNFG_03_TOTIME_MASK;
|
||||
data >>= CHG_CNFG_03_TOTIME_SHIFT;
|
||||
|
||||
val = data * 10;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
|
||||
}
|
||||
|
||||
static int max77693_set_top_off_timer(struct max77693_charger *chg,
|
||||
unsigned long minutes)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
if (minutes > 70)
|
||||
return -EINVAL;
|
||||
|
||||
data = minutes / 10;
|
||||
data <<= CHG_CNFG_03_TOTIME_SHIFT;
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_03,
|
||||
CHG_CNFG_03_TOTIME_MASK, data);
|
||||
}
|
||||
|
||||
static ssize_t top_off_timer_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
return device_attr_store(dev, attr, buf, count,
|
||||
max77693_set_top_off_timer);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(fast_charge_timer);
|
||||
static DEVICE_ATTR_RW(top_off_threshold_current);
|
||||
static DEVICE_ATTR_RW(top_off_timer);
|
||||
|
||||
static int max77693_set_constant_volt(struct max77693_charger *chg,
|
||||
unsigned int uvolt)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
/*
|
||||
* 0x00 - 3.650 V
|
||||
* 0x01 - 3.675 V
|
||||
* ...
|
||||
* 0x1b - 4.325 V
|
||||
* 0x1c - 4.340 V
|
||||
* 0x1d - 4.350 V
|
||||
* 0x1e - 4.375 V
|
||||
* 0x1f - 4.400 V
|
||||
*/
|
||||
if (uvolt >= 3650000 && uvolt < 4340000)
|
||||
data = (uvolt - 3650000) / 25000;
|
||||
else if (uvolt >= 4340000 && uvolt < 4350000)
|
||||
data = 0x1c;
|
||||
else if (uvolt >= 4350000 && uvolt <= 4400000)
|
||||
data = 0x1d + (uvolt - 4350000) / 25000;
|
||||
else {
|
||||
dev_err(chg->dev, "Wrong value for charging constant voltage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data <<= CHG_CNFG_04_CHGCVPRM_SHIFT;
|
||||
|
||||
dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
|
||||
data);
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_04,
|
||||
CHG_CNFG_04_CHGCVPRM_MASK, data);
|
||||
}
|
||||
|
||||
static int max77693_set_min_system_volt(struct max77693_charger *chg,
|
||||
unsigned int uvolt)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
if (uvolt < 3000000 || uvolt > 3700000) {
|
||||
dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = (uvolt - 3000000) / 100000;
|
||||
|
||||
data <<= CHG_CNFG_04_MINVSYS_SHIFT;
|
||||
|
||||
dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
|
||||
uvolt, data);
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_04,
|
||||
CHG_CNFG_04_MINVSYS_MASK, data);
|
||||
}
|
||||
|
||||
static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg,
|
||||
unsigned int cels)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
switch (cels) {
|
||||
case 70:
|
||||
case 85:
|
||||
case 100:
|
||||
case 115:
|
||||
data = (cels - 70) / 15;
|
||||
break;
|
||||
default:
|
||||
dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data <<= CHG_CNFG_07_REGTEMP_SHIFT;
|
||||
|
||||
dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
|
||||
cels, data);
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_07,
|
||||
CHG_CNFG_07_REGTEMP_MASK, data);
|
||||
}
|
||||
|
||||
static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
|
||||
unsigned int uamp)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
if (uamp && (uamp < 2000000 || uamp > 3500000)) {
|
||||
dev_err(chg->dev, "Wrong value for battery overcurrent\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (uamp)
|
||||
data = ((uamp - 2000000) / 250000) + 1;
|
||||
else
|
||||
data = 0; /* disable */
|
||||
|
||||
data <<= CHG_CNFG_12_B2SOVRC_SHIFT;
|
||||
|
||||
dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data);
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_12,
|
||||
CHG_CNFG_12_B2SOVRC_MASK, data);
|
||||
}
|
||||
|
||||
static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
|
||||
unsigned int uvolt)
|
||||
{
|
||||
unsigned int data;
|
||||
|
||||
switch (uvolt) {
|
||||
case 4300000:
|
||||
data = 0x0;
|
||||
break;
|
||||
case 4700000:
|
||||
case 4800000:
|
||||
case 4900000:
|
||||
data = (uvolt - 4700000) / 100000;
|
||||
default:
|
||||
dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data <<= CHG_CNFG_12_VCHGINREG_SHIFT;
|
||||
|
||||
dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n",
|
||||
uvolt, data);
|
||||
|
||||
return regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_12,
|
||||
CHG_CNFG_12_VCHGINREG_MASK, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets charger registers to proper and safe default values.
|
||||
*/
|
||||
static int max77693_reg_init(struct max77693_charger *chg)
|
||||
{
|
||||
int ret;
|
||||
unsigned int data;
|
||||
|
||||
/* Unlock charger register protection */
|
||||
data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT);
|
||||
ret = regmap_update_bits(chg->max77693->regmap,
|
||||
MAX77693_CHG_REG_CHG_CNFG_06,
|
||||
CHG_CNFG_06_CHGPROT_MASK, data);
|
||||
if (ret) {
|
||||
dev_err(chg->dev, "Error unlocking registers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_top_off_threshold_current(chg,
|
||||
DEFAULT_TOP_OFF_THRESHOLD_CURRENT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_constant_volt(chg, chg->constant_volt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_min_system_volt(chg, chg->min_system_volt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_thermal_regulation_temp(chg,
|
||||
chg->thermal_regulation_temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return max77693_set_charge_input_threshold_volt(chg,
|
||||
chg->charge_input_threshold_volt);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "no charger OF node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "maxim,constant-microvolt",
|
||||
&chg->constant_volt))
|
||||
chg->constant_volt = DEFAULT_CONSTANT_VOLT;
|
||||
|
||||
if (of_property_read_u32(np, "maxim,min-system-microvolt",
|
||||
&chg->min_system_volt))
|
||||
chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT;
|
||||
|
||||
if (of_property_read_u32(np, "maxim,thermal-regulation-celsius",
|
||||
&chg->thermal_regulation_temp))
|
||||
chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP;
|
||||
|
||||
if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp",
|
||||
&chg->batttery_overcurrent))
|
||||
chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
|
||||
|
||||
if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
|
||||
&chg->charge_input_threshold_volt))
|
||||
chg->charge_input_threshold_volt =
|
||||
DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int max77693_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77693_charger *chg;
|
||||
struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
|
||||
if (!chg)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, chg);
|
||||
chg->dev = &pdev->dev;
|
||||
chg->max77693 = max77693;
|
||||
|
||||
ret = max77693_dt_init(&pdev->dev, chg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max77693_reg_init(chg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chg->charger.name = max77693_charger_name;
|
||||
chg->charger.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
chg->charger.properties = max77693_charger_props;
|
||||
chg->charger.num_properties = ARRAY_SIZE(max77693_charger_props);
|
||||
chg->charger.get_property = max77693_charger_get_property;
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = device_create_file(&pdev->dev,
|
||||
&dev_attr_top_off_threshold_current);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: create top off current sysfs entry\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &chg->charger);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed: power supply register\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
|
||||
device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
|
||||
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77693_charger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max77693_charger *chg = platform_get_drvdata(pdev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
|
||||
device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
|
||||
device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
|
||||
|
||||
power_supply_unregister(&chg->charger);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id max77693_charger_id[] = {
|
||||
{ "max77693-charger", 0, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77693_charger_id);
|
||||
|
||||
static struct platform_driver max77693_charger_driver = {
|
||||
.driver = {
|
||||
.name = "max77693-charger",
|
||||
},
|
||||
.probe = max77693_charger_probe,
|
||||
.remove = max77693_charger_remove,
|
||||
.id_table = max77693_charger_id,
|
||||
};
|
||||
module_platform_driver(max77693_charger_driver);
|
||||
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||
MODULE_DESCRIPTION("Maxim 77693 charger driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -39,14 +39,13 @@ config POWER_RESET_AXXIA
|
||||
Say Y if you have an Axxia family SoC.
|
||||
|
||||
config POWER_RESET_BRCMSTB
|
||||
bool "Broadcom STB reset driver" if COMPILE_TEST
|
||||
depends on ARM
|
||||
bool "Broadcom STB reset driver"
|
||||
depends on ARM || MIPS || COMPILE_TEST
|
||||
default ARCH_BRCMSTB
|
||||
help
|
||||
This driver provides restart support for ARM-based Broadcom STB
|
||||
boards.
|
||||
This driver provides restart support for Broadcom STB boards.
|
||||
|
||||
Say Y here if you have an ARM-based Broadcom STB board and you wish
|
||||
Say Y here if you have a Broadcom STB board and you wish
|
||||
to have restart support.
|
||||
|
||||
config POWER_RESET_GPIO
|
||||
@ -104,23 +103,16 @@ config POWER_RESET_QNAP
|
||||
|
||||
config POWER_RESET_RESTART
|
||||
bool "Restart power-off driver"
|
||||
depends on ARM
|
||||
help
|
||||
Some boards don't actually have the ability to power off.
|
||||
Instead they restart, and u-boot holds the SoC until the
|
||||
user presses a key. u-boot then boots into Linux.
|
||||
|
||||
config POWER_RESET_SUN6I
|
||||
bool "Allwinner A31 SoC reset driver"
|
||||
depends on ARCH_SUNXI
|
||||
help
|
||||
Reboot support for the Allwinner A31 SoCs.
|
||||
|
||||
config POWER_RESET_ST
|
||||
bool "ST restart power-off driver"
|
||||
bool "ST restart driver"
|
||||
depends on ARCH_STI
|
||||
help
|
||||
Power off and reset support for STMicroelectronics boards.
|
||||
Reset support for STMicroelectronics boards.
|
||||
|
||||
config POWER_RESET_VERSATILE
|
||||
bool "ARM Versatile family reboot driver"
|
||||
@ -159,5 +151,11 @@ config POWER_RESET_SYSCON
|
||||
help
|
||||
Reboot support for generic SYSCON mapped register reset.
|
||||
|
||||
config POWER_RESET_RMOBILE
|
||||
tristate "Renesas R-Mobile reset driver"
|
||||
depends on ARCH_RMOBILE || COMPILE_TEST
|
||||
help
|
||||
Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
|
||||
|
||||
endif
|
||||
|
||||
|
@ -11,10 +11,10 @@ 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
|
||||
obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
|
||||
|
@ -13,16 +13,22 @@
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
|
||||
#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
|
||||
#define INTEGRATOR_CM_CTRL_RESET (1 << 3)
|
||||
|
||||
#define REALVIEW_SYS_LOCK_OFFSET 0x20
|
||||
#define REALVIEW_SYS_LOCK_VAL 0xA05F
|
||||
#define REALVIEW_SYS_RESETCTL_OFFSET 0x40
|
||||
|
||||
/* Magic unlocking token used on all Versatile boards */
|
||||
#define VERSATILE_LOCK_VAL 0xA05F
|
||||
|
||||
/*
|
||||
* We detect the different syscon types from the compatible strings.
|
||||
*/
|
||||
enum versatile_reboot {
|
||||
INTEGRATOR_REBOOT_CM,
|
||||
REALVIEW_REBOOT_EB,
|
||||
REALVIEW_REBOOT_PB1176,
|
||||
REALVIEW_REBOOT_PB11MP,
|
||||
@ -35,6 +41,10 @@ static struct regmap *syscon_regmap;
|
||||
static enum versatile_reboot versatile_reboot_type;
|
||||
|
||||
static const struct of_device_id versatile_reboot_of_match[] = {
|
||||
{
|
||||
.compatible = "arm,core-module-integrator",
|
||||
.data = (void *)INTEGRATOR_REBOOT_CM
|
||||
},
|
||||
{
|
||||
.compatible = "arm,realview-eb-syscon",
|
||||
.data = (void *)REALVIEW_REBOOT_EB,
|
||||
@ -55,31 +65,47 @@ static const struct of_device_id versatile_reboot_of_match[] = {
|
||||
.compatible = "arm,realview-pbx-syscon",
|
||||
.data = (void *)REALVIEW_REBOOT_PBX,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static void versatile_reboot(enum reboot_mode mode, const char *cmd)
|
||||
static int versatile_reboot(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
/* Unlock the reset register */
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
|
||||
REALVIEW_SYS_LOCK_VAL);
|
||||
/* Then hit reset on the different machines */
|
||||
switch (versatile_reboot_type) {
|
||||
case INTEGRATOR_REBOOT_CM:
|
||||
regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET,
|
||||
VERSATILE_LOCK_VAL);
|
||||
regmap_update_bits(syscon_regmap,
|
||||
INTEGRATOR_HDR_CTRL_OFFSET,
|
||||
INTEGRATOR_CM_CTRL_RESET,
|
||||
INTEGRATOR_CM_CTRL_RESET);
|
||||
break;
|
||||
case REALVIEW_REBOOT_EB:
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
|
||||
VERSATILE_LOCK_VAL);
|
||||
regmap_write(syscon_regmap,
|
||||
REALVIEW_SYS_RESETCTL_OFFSET, 0x0008);
|
||||
break;
|
||||
case REALVIEW_REBOOT_PB1176:
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
|
||||
VERSATILE_LOCK_VAL);
|
||||
regmap_write(syscon_regmap,
|
||||
REALVIEW_SYS_RESETCTL_OFFSET, 0x0100);
|
||||
break;
|
||||
case REALVIEW_REBOOT_PB11MP:
|
||||
case REALVIEW_REBOOT_PBA8:
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
|
||||
VERSATILE_LOCK_VAL);
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
|
||||
0x0000);
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
|
||||
0x0004);
|
||||
break;
|
||||
case REALVIEW_REBOOT_PBX:
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
|
||||
VERSATILE_LOCK_VAL);
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
|
||||
0x00f0);
|
||||
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
|
||||
@ -87,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd)
|
||||
break;
|
||||
}
|
||||
dsb();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block versatile_reboot_nb = {
|
||||
.notifier_call = versatile_reboot,
|
||||
.priority = 192,
|
||||
};
|
||||
|
||||
static int __init versatile_reboot_probe(void)
|
||||
{
|
||||
const struct of_device_id *reboot_id;
|
||||
struct device_node *np;
|
||||
int err;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
|
||||
&reboot_id);
|
||||
@ -104,7 +138,10 @@ static int __init versatile_reboot_probe(void)
|
||||
if (IS_ERR(syscon_regmap))
|
||||
return PTR_ERR(syscon_regmap);
|
||||
|
||||
arm_pm_restart = versatile_reboot;
|
||||
err = register_restart_handler(&versatile_reboot_nb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_info("versatile reboot driver registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -71,10 +71,11 @@ static void at91_poweroff(void)
|
||||
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
|
||||
}
|
||||
|
||||
const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
|
||||
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
|
||||
{
|
||||
const char *pm;
|
||||
int err, i;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
|
||||
if (err < 0)
|
||||
@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
|
||||
static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
enum wakeup_type wakeup_mode;
|
||||
int wakeup_mode;
|
||||
u32 mode = 0, tmp;
|
||||
|
||||
wakeup_mode = at91_poweroff_get_wakeup_mode(np);
|
||||
|
@ -17,8 +17,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
#include <soc/at91/at91sam9_ddrsdr.h>
|
||||
#include <soc/at91/at91sam9_sdramc.h>
|
||||
|
||||
@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base;
|
||||
* reset register it can be left driving the data bus and
|
||||
* killing the chance of a subsequent boot from NAND
|
||||
*/
|
||||
static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
|
||||
static int at91sam9260_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
asm volatile(
|
||||
/* Align to cache lines */
|
||||
@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
|
||||
"r" (1),
|
||||
"r" (AT91_SDRAMC_LPCB_POWER_DOWN),
|
||||
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
|
||||
static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
asm volatile(
|
||||
/*
|
||||
@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
|
||||
"r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
|
||||
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
|
||||
: "r0");
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void __init at91_reset_status(struct platform_device *pdev)
|
||||
@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = {
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct notifier_block at91_restart_nb = {
|
||||
.priority = 192,
|
||||
};
|
||||
|
||||
static int at91_reset_of_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
|
||||
arm_pm_restart = match->data;
|
||||
|
||||
return 0;
|
||||
at91_restart_nb.notifier_call = match->data;
|
||||
return register_restart_handler(&at91_restart_nb);
|
||||
}
|
||||
|
||||
static int at91_reset_platform_probe(struct platform_device *pdev)
|
||||
@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
match = platform_get_device_id(pdev);
|
||||
arm_pm_restart = (void (*)(enum reboot_mode, const char*))
|
||||
match->driver_data;
|
||||
at91_restart_nb.notifier_call =
|
||||
(int (*)(struct notifier_block *,
|
||||
unsigned long, void *)) match->driver_data;
|
||||
|
||||
return 0;
|
||||
return register_restart_handler(&at91_restart_nb);
|
||||
}
|
||||
|
||||
static int at91_reset_probe(struct platform_device *pdev)
|
||||
|
@ -11,6 +11,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
@ -34,13 +35,20 @@ static struct regmap *regmap;
|
||||
static u32 rst_src_en;
|
||||
static u32 sw_mstr_rst;
|
||||
|
||||
struct reset_reg_mask {
|
||||
u32 rst_src_en_mask;
|
||||
u32 sw_mstr_rst_mask;
|
||||
};
|
||||
|
||||
static const struct reset_reg_mask *reset_masks;
|
||||
|
||||
static int brcmstb_restart_handler(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
int rc;
|
||||
u32 tmp;
|
||||
|
||||
rc = regmap_write(regmap, rst_src_en, 1);
|
||||
rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask);
|
||||
if (rc) {
|
||||
pr_err("failed to write rst_src_en (%d)\n", rc);
|
||||
return NOTIFY_DONE;
|
||||
@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
rc = regmap_write(regmap, sw_mstr_rst, 1);
|
||||
rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask);
|
||||
if (rc) {
|
||||
pr_err("failed to write sw_mstr_rst (%d)\n", rc);
|
||||
return NOTIFY_DONE;
|
||||
@ -75,10 +83,34 @@ static struct notifier_block brcmstb_restart_nb = {
|
||||
.priority = 128,
|
||||
};
|
||||
|
||||
static const struct reset_reg_mask reset_bits_40nm = {
|
||||
.rst_src_en_mask = BIT(0),
|
||||
.sw_mstr_rst_mask = BIT(0),
|
||||
};
|
||||
|
||||
static const struct reset_reg_mask reset_bits_65nm = {
|
||||
.rst_src_en_mask = BIT(3),
|
||||
.sw_mstr_rst_mask = BIT(31),
|
||||
};
|
||||
|
||||
static const struct of_device_id of_match[] = {
|
||||
{ .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm },
|
||||
{ .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm },
|
||||
{},
|
||||
};
|
||||
|
||||
static int brcmstb_reboot_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_node(of_match, np);
|
||||
if (!of_id) {
|
||||
pr_err("failed to look up compatible string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
reset_masks = of_id->data;
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
|
||||
if (IS_ERR(regmap)) {
|
||||
@ -108,11 +140,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match[] = {
|
||||
{ .compatible = "brcm,brcmstb-reboot", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver brcmstb_reboot_driver = {
|
||||
.probe = brcmstb_reboot_probe,
|
||||
.driver = {
|
||||
|
@ -32,7 +32,9 @@
|
||||
* - 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.
|
||||
* executed. If no pin is assigned to this input, the driver will start the
|
||||
* watchdog toggle immediately. The chip will only power off the system if
|
||||
* it is requested to do so through the kill line.
|
||||
*
|
||||
* - watchdog (output)
|
||||
* Once a shut down is triggered, the driver will toggle this signal,
|
||||
@ -63,7 +65,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
struct ltc2952_poweroff_data {
|
||||
struct ltc2952_poweroff {
|
||||
struct hrtimer timer_trigger;
|
||||
struct hrtimer timer_wde;
|
||||
|
||||
@ -72,22 +74,21 @@ struct ltc2952_poweroff_data {
|
||||
|
||||
struct device *dev;
|
||||
|
||||
unsigned int virq;
|
||||
struct gpio_desc *gpio_trigger;
|
||||
struct gpio_desc *gpio_watchdog;
|
||||
struct gpio_desc *gpio_kill;
|
||||
|
||||
/**
|
||||
* 0: trigger
|
||||
* 1: watchdog
|
||||
* 2: kill
|
||||
*/
|
||||
struct gpio_desc *gpio[3];
|
||||
bool kernel_panic;
|
||||
struct notifier_block panic_notifier;
|
||||
};
|
||||
|
||||
static int ltc2952_poweroff_panic;
|
||||
static struct ltc2952_poweroff_data *ltc2952_data;
|
||||
#define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m)
|
||||
|
||||
#define POWERPATH_IO_TRIGGER 0
|
||||
#define POWERPATH_IO_WATCHDOG 1
|
||||
#define POWERPATH_IO_KILL 2
|
||||
/*
|
||||
* This global variable is only needed for pm_power_off. We should
|
||||
* remove it entirely once we don't need the global state anymore.
|
||||
*/
|
||||
static struct ltc2952_poweroff *ltc2952_data;
|
||||
|
||||
/**
|
||||
* ltc2952_poweroff_timer_wde - Timer callback
|
||||
@ -103,29 +104,24 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
|
||||
ktime_t now;
|
||||
int state;
|
||||
unsigned long overruns;
|
||||
struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde);
|
||||
|
||||
if (ltc2952_poweroff_panic)
|
||||
if (data->kernel_panic)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]);
|
||||
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state);
|
||||
state = gpiod_get_value(data->gpio_watchdog);
|
||||
gpiod_set_value(data->gpio_watchdog, !state);
|
||||
|
||||
now = hrtimer_cb_get_time(timer);
|
||||
overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval);
|
||||
overruns = hrtimer_forward(timer, now, data->wde_interval);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
|
||||
struct hrtimer *timer)
|
||||
static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data)
|
||||
{
|
||||
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");
|
||||
if (hrtimer_start(&data->timer_wde, data->wde_interval,
|
||||
HRTIMER_MODE_REL)) {
|
||||
/*
|
||||
* The device will not toggle the watchdog reset,
|
||||
* thus shut down is only safe if the PowerPath controller
|
||||
@ -134,10 +130,17 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
|
||||
*
|
||||
* Only sending a warning as the system will power-off anyway
|
||||
*/
|
||||
dev_err(data->dev, "unable to start the timer\n");
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(ltc2952_data->dev, "executing shutdown\n");
|
||||
static enum hrtimer_restart
|
||||
ltc2952_poweroff_timer_trigger(struct hrtimer *timer)
|
||||
{
|
||||
struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger);
|
||||
|
||||
ltc2952_poweroff_start_wde(data);
|
||||
dev_info(data->dev, "executing shutdown\n");
|
||||
orderly_poweroff(true);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
@ -154,180 +157,161 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
|
||||
*/
|
||||
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
|
||||
{
|
||||
int ret;
|
||||
struct ltc2952_poweroff_data *data = dev_id;
|
||||
struct ltc2952_poweroff *data = dev_id;
|
||||
|
||||
if (ltc2952_poweroff_panic)
|
||||
goto irq_ok;
|
||||
|
||||
if (hrtimer_active(&data->timer_wde)) {
|
||||
if (data->kernel_panic || hrtimer_active(&data->timer_wde)) {
|
||||
/* shutdown is already triggered, nothing to do any more */
|
||||
goto irq_ok;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (!hrtimer_active(&data->timer_trigger)) {
|
||||
ret = hrtimer_start(&data->timer_trigger, data->trigger_delay,
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
if (ret)
|
||||
if (gpiod_get_value(data->gpio_trigger)) {
|
||||
if (hrtimer_start(&data->timer_trigger, data->trigger_delay,
|
||||
HRTIMER_MODE_REL))
|
||||
dev_err(data->dev, "unable to start the wait timer\n");
|
||||
} else {
|
||||
ret = hrtimer_cancel(&data->timer_trigger);
|
||||
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);
|
||||
gpiod_set_value(ltc2952_data->gpio_kill, 1);
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
static void ltc2952_poweroff_default(struct ltc2952_poweroff *data)
|
||||
{
|
||||
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;
|
||||
data->timer_trigger.function = ltc2952_poweroff_timer_trigger;
|
||||
|
||||
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
data->timer_wde.function = <c2952_poweroff_timer_wde;
|
||||
data->timer_wde.function = ltc2952_poweroff_timer_wde;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_init(struct platform_device *pdev)
|
||||
{
|
||||
int ret, virq;
|
||||
unsigned int i;
|
||||
struct ltc2952_poweroff_data *data;
|
||||
int ret;
|
||||
struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
|
||||
|
||||
static char *name[] = {
|
||||
"trigger",
|
||||
"watchdog",
|
||||
"kill",
|
||||
NULL
|
||||
};
|
||||
ltc2952_poweroff_default(data);
|
||||
|
||||
data = ltc2952_data;
|
||||
ltc2952_poweroff_default(ltc2952_data);
|
||||
data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->gpio_watchdog)) {
|
||||
ret = PTR_ERR(data->gpio_watchdog);
|
||||
dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) {
|
||||
ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]);
|
||||
data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->gpio_kill)) {
|
||||
ret = PTR_ERR(data->gpio_kill);
|
||||
dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (IS_ERR(ltc2952_data->gpio[i])) {
|
||||
ret = PTR_ERR(ltc2952_data->gpio[i]);
|
||||
data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN);
|
||||
if (IS_ERR(data->gpio_trigger)) {
|
||||
/*
|
||||
* It's not a problem if the trigger gpio isn't available, but
|
||||
* it is worth a warning if its use was defined in the device
|
||||
* tree.
|
||||
*/
|
||||
if (PTR_ERR(data->gpio_trigger) != -ENOENT)
|
||||
dev_err(&pdev->dev,
|
||||
"unable to claim the following gpio: %s\n",
|
||||
name[i]);
|
||||
goto err_io;
|
||||
"unable to claim gpio \"trigger\"\n");
|
||||
data->gpio_trigger = NULL;
|
||||
}
|
||||
|
||||
if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger),
|
||||
ltc2952_poweroff_handler,
|
||||
(IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
|
||||
"ltc2952-poweroff",
|
||||
data)) {
|
||||
/*
|
||||
* Some things may have happened:
|
||||
* - No trigger input was defined
|
||||
* - Claiming the GPIO failed
|
||||
* - We could not map to an IRQ
|
||||
* - We couldn't register an interrupt handler
|
||||
*
|
||||
* None of these really are problems, but all of them
|
||||
* disqualify the push button from controlling the power.
|
||||
*
|
||||
* It is therefore important to note that if the ltc2952
|
||||
* detects a button press for long enough, it will still start
|
||||
* its own powerdown window and cut the power on us if we don't
|
||||
* start the watchdog trigger.
|
||||
*/
|
||||
if (data->gpio_trigger) {
|
||||
dev_warn(&pdev->dev,
|
||||
"unable to configure the trigger interrupt\n");
|
||||
devm_gpiod_put(&pdev->dev, data->gpio_trigger);
|
||||
data->gpio_trigger = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
dev_info(&pdev->dev,
|
||||
"power down trigger input will not be used\n");
|
||||
ltc2952_poweroff_start_wde(data);
|
||||
}
|
||||
|
||||
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]);
|
||||
static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
|
||||
unsigned long code, void *unused)
|
||||
{
|
||||
struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier);
|
||||
|
||||
return ret;
|
||||
data->kernel_panic = true;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int ltc2952_poweroff_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct ltc2952_poweroff *data;
|
||||
|
||||
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)
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
ltc2952_data->dev = &pdev->dev;
|
||||
data->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
ret = ltc2952_poweroff_init(pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
pm_power_off = <c2952_poweroff_kill;
|
||||
/* TODO: remove ltc2952_data */
|
||||
ltc2952_data = data;
|
||||
pm_power_off = ltc2952_poweroff_kill;
|
||||
|
||||
data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic;
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&data->panic_notifier);
|
||||
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;
|
||||
struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
hrtimer_cancel(&data->timer_trigger);
|
||||
hrtimer_cancel(&data->timer_wde);
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&data->panic_notifier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -344,41 +328,9 @@ static struct platform_driver ltc2952_poweroff_driver = {
|
||||
.name = "ltc2952-poweroff",
|
||||
.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_platform_driver(ltc2952_poweroff_driver);
|
||||
|
||||
MODULE_AUTHOR("René Moll <rene.moll@xsens.com>");
|
||||
MODULE_DESCRIPTION("LTC PowerPath power-off driver");
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
static void restart_poweroff_do_poweroff(void)
|
||||
{
|
||||
|
91
drivers/power/reset/rmobile-reset.c
Normal file
91
drivers/power/reset/rmobile-reset.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Renesas R-Mobile Reset Driver
|
||||
*
|
||||
* Copyright (C) 2014 Glider bvba
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
/* SYSC Register Bank 2 */
|
||||
#define RESCNT2 0x20 /* Reset Control Register 2 */
|
||||
|
||||
/* Reset Control Register 2 */
|
||||
#define RESCNT2_PRES 0x80000000 /* Soft power-on reset */
|
||||
|
||||
static void __iomem *sysc_base2;
|
||||
|
||||
static int rmobile_reset_handler(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
pr_debug("%s %lu\n", __func__, mode);
|
||||
|
||||
/* Let's assume we have acquired the HPB semaphore */
|
||||
writel(RESCNT2_PRES, sysc_base2 + RESCNT2);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block rmobile_reset_nb = {
|
||||
.notifier_call = rmobile_reset_handler,
|
||||
.priority = 192,
|
||||
};
|
||||
|
||||
static int rmobile_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error;
|
||||
|
||||
sysc_base2 = of_iomap(pdev->dev.of_node, 1);
|
||||
if (!sysc_base2)
|
||||
return -ENODEV;
|
||||
|
||||
error = register_restart_handler(&rmobile_reset_nb);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"cannot register restart handler (err=%d)\n", error);
|
||||
goto fail_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_unmap:
|
||||
iounmap(sysc_base2);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmobile_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
unregister_restart_handler(&rmobile_reset_nb);
|
||||
iounmap(sysc_base2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rmobile_reset_of_match[] = {
|
||||
{ .compatible = "renesas,sysc-rmobile", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rmobile_reset_of_match);
|
||||
|
||||
static struct platform_driver rmobile_reset_driver = {
|
||||
.probe = rmobile_reset_probe,
|
||||
.remove = rmobile_reset_remove,
|
||||
.driver = {
|
||||
.name = "rmobile_reset",
|
||||
.of_match_table = rmobile_reset_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rmobile_reset_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas R-Mobile Reset Driver");
|
||||
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,10 +15,9 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
struct reset_syscfg {
|
||||
struct regmap *regmap;
|
||||
/* syscfg used for reset */
|
||||
@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = {
|
||||
|
||||
static struct reset_syscfg *st_restart_syscfg;
|
||||
|
||||
static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
|
||||
static int st_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
/* reset syscfg updated */
|
||||
regmap_update_bits(st_restart_syscfg->regmap,
|
||||
@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
|
||||
st_restart_syscfg->offset_rst_msk,
|
||||
st_restart_syscfg->mask_rst_msk,
|
||||
0);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block st_restart_nb = {
|
||||
.notifier_call = st_restart,
|
||||
.priority = 192,
|
||||
};
|
||||
|
||||
static struct of_device_id st_reset_of_match[] = {
|
||||
{
|
||||
.compatible = "st,stih415-restart",
|
||||
@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(st_restart_syscfg->regmap);
|
||||
}
|
||||
|
||||
arm_pm_restart = st_restart;
|
||||
|
||||
return 0;
|
||||
return register_restart_handler(&st_restart_nb);
|
||||
}
|
||||
|
||||
static struct platform_driver st_reset_driver = {
|
||||
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Allwinner A31 SoCs reset code
|
||||
*
|
||||
* Copyright (C) 2012-2014 Maxime Ripard
|
||||
*
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
#define SUN6I_WATCHDOG1_IRQ_REG 0x00
|
||||
#define SUN6I_WATCHDOG1_CTRL_REG 0x10
|
||||
#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0)
|
||||
#define SUN6I_WATCHDOG1_CONFIG_REG 0x14
|
||||
#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0)
|
||||
#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1)
|
||||
#define SUN6I_WATCHDOG1_MODE_REG 0x18
|
||||
#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0)
|
||||
|
||||
static void __iomem *wdt_base;
|
||||
|
||||
static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
if (!wdt_base)
|
||||
return;
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
|
||||
|
||||
/* We want to disable the IRQ and just reset the whole system */
|
||||
writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
|
||||
wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
|
||||
|
||||
/* Enable timer. The default and lowest interval value is 0.5s */
|
||||
writel(SUN6I_WATCHDOG1_MODE_ENABLE,
|
||||
wdt_base + SUN6I_WATCHDOG1_MODE_REG);
|
||||
|
||||
/* Restart the watchdog. */
|
||||
writel(SUN6I_WATCHDOG1_CTRL_RESTART,
|
||||
wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
|
||||
|
||||
while (1) {
|
||||
mdelay(5);
|
||||
writel(SUN6I_WATCHDOG1_MODE_ENABLE,
|
||||
wdt_base + SUN6I_WATCHDOG1_MODE_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static int sun6i_reboot_probe(struct platform_device *pdev)
|
||||
{
|
||||
wdt_base = of_iomap(pdev->dev.of_node, 0);
|
||||
if (!wdt_base) {
|
||||
WARN(1, "failed to map watchdog base address");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
arm_pm_restart = sun6i_wdt_restart;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id sun6i_reboot_of_match[] = {
|
||||
{ .compatible = "allwinner,sun6i-a31-wdt" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver sun6i_reboot_driver = {
|
||||
.probe = sun6i_reboot_probe,
|
||||
.driver = {
|
||||
.name = "sun6i-reboot",
|
||||
.of_match_table = sun6i_reboot_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sun6i_reboot_driver);
|
@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev)
|
||||
|
||||
static int vexpress_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
enum vexpress_reset_func func;
|
||||
const struct of_device_id *match =
|
||||
of_match_device(vexpress_reset_of_match, &pdev->dev);
|
||||
struct regmap *regmap;
|
||||
int ret = 0;
|
||||
|
||||
if (match)
|
||||
func = (enum vexpress_reset_func)match->data;
|
||||
else
|
||||
func = pdev->id_entry->driver_data;
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init_vexpress_config(&pdev->dev);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
dev_set_drvdata(&pdev->dev, regmap);
|
||||
|
||||
switch (func) {
|
||||
switch ((enum vexpress_reset_func)match->data) {
|
||||
case FUNC_SHUTDOWN:
|
||||
vexpress_power_off_device = &pdev->dev;
|
||||
pm_power_off = vexpress_power_off;
|
||||
@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct platform_device_id vexpress_reset_id_table[] = {
|
||||
{ .name = "vexpress-reset", .driver_data = FUNC_RESET, },
|
||||
{ .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, },
|
||||
{ .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver vexpress_reset_driver = {
|
||||
.probe = vexpress_reset_probe,
|
||||
.driver = {
|
||||
.name = "vexpress-reset",
|
||||
.of_match_table = vexpress_reset_of_match,
|
||||
},
|
||||
.id_table = vexpress_reset_id_table,
|
||||
};
|
||||
|
||||
static int __init vexpress_reset_init(void)
|
||||
|
177
drivers/power/rt5033_battery.c
Normal file
177
drivers/power/rt5033_battery.c
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Fuel gauge driver for Richtek RT5033
|
||||
*
|
||||
* Copyright (C) 2014 Samsung Electronics, Co., Ltd.
|
||||
* Author: Beomho Seo <beomho.seo@samsung.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 bythe Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mfd/rt5033-private.h>
|
||||
#include <linux/mfd/rt5033.h>
|
||||
|
||||
static int rt5033_battery_get_capacity(struct i2c_client *client)
|
||||
{
|
||||
struct rt5033_battery *battery = i2c_get_clientdata(client);
|
||||
u32 msb;
|
||||
|
||||
regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb);
|
||||
|
||||
return msb;
|
||||
}
|
||||
|
||||
static int rt5033_battery_get_present(struct i2c_client *client)
|
||||
{
|
||||
struct rt5033_battery *battery = i2c_get_clientdata(client);
|
||||
u32 val;
|
||||
|
||||
regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val);
|
||||
|
||||
return (val & RT5033_FUEL_BAT_PRESENT) ? true : false;
|
||||
}
|
||||
|
||||
static int rt5033_battery_get_watt_prop(struct i2c_client *client,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
struct rt5033_battery *battery = i2c_get_clientdata(client);
|
||||
unsigned int regh, regl;
|
||||
int ret;
|
||||
u32 msb, lsb;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
regh = RT5033_FUEL_REG_VBAT_H;
|
||||
regl = RT5033_FUEL_REG_VBAT_L;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
regh = RT5033_FUEL_REG_AVG_VOLT_H;
|
||||
regl = RT5033_FUEL_REG_AVG_VOLT_L;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
regh = RT5033_FUEL_REG_OCV_H;
|
||||
regl = RT5033_FUEL_REG_OCV_L;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_read(battery->regmap, regh, &msb);
|
||||
regmap_read(battery->regmap, regl, &lsb);
|
||||
|
||||
ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt5033_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct rt5033_battery *battery = container_of(psy,
|
||||
struct rt5033_battery, psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
val->intval = rt5033_battery_get_watt_prop(battery->client,
|
||||
psp);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = rt5033_battery_get_present(battery->client);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = rt5033_battery_get_capacity(battery->client);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property rt5033_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
};
|
||||
|
||||
static struct regmap_config rt5033_battery_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = RT5033_FUEL_REG_END,
|
||||
};
|
||||
|
||||
static int rt5033_battery_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct rt5033_battery *battery;
|
||||
u32 ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
return -EIO;
|
||||
|
||||
battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
|
||||
if (!battery)
|
||||
return -EINVAL;
|
||||
|
||||
battery->client = client;
|
||||
battery->regmap = devm_regmap_init_i2c(client,
|
||||
&rt5033_battery_regmap_config);
|
||||
if (IS_ERR(battery->regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, battery);
|
||||
|
||||
battery->psy.name = "rt5033-battery";
|
||||
battery->psy.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
battery->psy.get_property = rt5033_battery_get_property;
|
||||
battery->psy.properties = rt5033_battery_props;
|
||||
battery->psy.num_properties = ARRAY_SIZE(rt5033_battery_props);
|
||||
|
||||
ret = power_supply_register(&client->dev, &battery->psy);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to register power supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5033_battery_remove(struct i2c_client *client)
|
||||
{
|
||||
struct rt5033_battery *battery = i2c_get_clientdata(client);
|
||||
|
||||
power_supply_unregister(&battery->psy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id rt5033_battery_id[] = {
|
||||
{ "rt5033-battery", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, rt5033_battery_id);
|
||||
|
||||
static struct i2c_driver rt5033_battery_driver = {
|
||||
.driver = {
|
||||
.name = "rt5033-battery",
|
||||
},
|
||||
.probe = rt5033_battery_probe,
|
||||
.remove = rt5033_battery_remove,
|
||||
.id_table = rt5033_battery_id,
|
||||
};
|
||||
module_i2c_driver(rt5033_battery_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
|
||||
MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -21,6 +21,13 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/vermagic.h>
|
||||
|
||||
enum test_power_id {
|
||||
TEST_AC,
|
||||
TEST_BATTERY,
|
||||
TEST_USB,
|
||||
TEST_POWER_NUM,
|
||||
};
|
||||
|
||||
static int ac_online = 1;
|
||||
static int usb_online = 1;
|
||||
static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
@ -147,7 +154,7 @@ static char *test_power_ac_supplied_to[] = {
|
||||
};
|
||||
|
||||
static struct power_supply test_power_supplies[] = {
|
||||
{
|
||||
[TEST_AC] = {
|
||||
.name = "test_ac",
|
||||
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||
.supplied_to = test_power_ac_supplied_to,
|
||||
@ -155,13 +162,15 @@ static struct power_supply test_power_supplies[] = {
|
||||
.properties = test_power_ac_props,
|
||||
.num_properties = ARRAY_SIZE(test_power_ac_props),
|
||||
.get_property = test_power_get_ac_property,
|
||||
}, {
|
||||
},
|
||||
[TEST_BATTERY] = {
|
||||
.name = "test_battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = test_power_battery_props,
|
||||
.num_properties = ARRAY_SIZE(test_power_battery_props),
|
||||
.get_property = test_power_get_battery_property,
|
||||
}, {
|
||||
},
|
||||
[TEST_USB] = {
|
||||
.name = "test_usb",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.supplied_to = test_power_ac_supplied_to,
|
||||
@ -178,6 +187,8 @@ static int __init test_power_init(void)
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
|
||||
ret = power_supply_register(NULL, &test_power_supplies[i]);
|
||||
if (ret) {
|
||||
@ -309,7 +320,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy)
|
||||
static int param_set_ac_online(const char *key, const struct kernel_param *kp)
|
||||
{
|
||||
ac_online = map_get_value(map_ac_online, key, ac_online);
|
||||
signal_power_supply_changed(&test_power_supplies[0]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_AC]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -322,7 +333,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
|
||||
static int param_set_usb_online(const char *key, const struct kernel_param *kp)
|
||||
{
|
||||
usb_online = map_get_value(map_ac_online, key, usb_online);
|
||||
signal_power_supply_changed(&test_power_supplies[2]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_USB]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -336,7 +347,7 @@ static int param_set_battery_status(const char *key,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
battery_status = map_get_value(map_status, key, battery_status);
|
||||
signal_power_supply_changed(&test_power_supplies[1]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -350,7 +361,7 @@ static int param_set_battery_health(const char *key,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
battery_health = map_get_value(map_health, key, battery_health);
|
||||
signal_power_supply_changed(&test_power_supplies[1]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -364,7 +375,7 @@ static int param_set_battery_present(const char *key,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
battery_present = map_get_value(map_present, key, battery_present);
|
||||
signal_power_supply_changed(&test_power_supplies[0]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_AC]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -380,7 +391,7 @@ static int param_set_battery_technology(const char *key,
|
||||
{
|
||||
battery_technology = map_get_value(map_technology, key,
|
||||
battery_technology);
|
||||
signal_power_supply_changed(&test_power_supplies[1]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -401,7 +412,7 @@ static int param_set_battery_capacity(const char *key,
|
||||
return -EINVAL;
|
||||
|
||||
battery_capacity = tmp;
|
||||
signal_power_supply_changed(&test_power_supplies[1]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -416,7 +427,7 @@ static int param_set_battery_voltage(const char *key,
|
||||
return -EINVAL;
|
||||
|
||||
battery_voltage = tmp;
|
||||
signal_power_supply_changed(&test_power_supplies[1]);
|
||||
signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -461,7 +461,6 @@ struct ab8500_fg;
|
||||
#ifdef CONFIG_AB8500_BM
|
||||
extern struct abx500_bm_data ab8500_bm_data;
|
||||
|
||||
void ab8500_fg_reinit(void);
|
||||
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
|
||||
struct ab8500_btemp *ab8500_btemp_get(void);
|
||||
int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
|
||||
|
@ -143,10 +143,118 @@ enum max77693_pmic_reg {
|
||||
#define FLASH_INT_FLED1_SHORT BIT(3)
|
||||
#define FLASH_INT_OVER_CURRENT BIT(4)
|
||||
|
||||
/* Fast charge timer in in hours */
|
||||
#define DEFAULT_FAST_CHARGE_TIMER 4
|
||||
/* microamps */
|
||||
#define DEFAULT_TOP_OFF_THRESHOLD_CURRENT 150000
|
||||
/* minutes */
|
||||
#define DEFAULT_TOP_OFF_TIMER 30
|
||||
/* microvolts */
|
||||
#define DEFAULT_CONSTANT_VOLT 4200000
|
||||
/* microvolts */
|
||||
#define DEFAULT_MIN_SYSTEM_VOLT 3600000
|
||||
/* celsius */
|
||||
#define DEFAULT_THERMAL_REGULATION_TEMP 100
|
||||
/* microamps */
|
||||
#define DEFAULT_BATTERY_OVERCURRENT 3500000
|
||||
/* microvolts */
|
||||
#define DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT 4300000
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_INT_OK register */
|
||||
#define CHG_INT_OK_BYP_SHIFT 0
|
||||
#define CHG_INT_OK_BAT_SHIFT 3
|
||||
#define CHG_INT_OK_CHG_SHIFT 4
|
||||
#define CHG_INT_OK_CHGIN_SHIFT 6
|
||||
#define CHG_INT_OK_DETBAT_SHIFT 7
|
||||
#define CHG_INT_OK_BYP_MASK BIT(CHG_INT_OK_BYP_SHIFT)
|
||||
#define CHG_INT_OK_BAT_MASK BIT(CHG_INT_OK_BAT_SHIFT)
|
||||
#define CHG_INT_OK_CHG_MASK BIT(CHG_INT_OK_CHG_SHIFT)
|
||||
#define CHG_INT_OK_CHGIN_MASK BIT(CHG_INT_OK_CHGIN_SHIFT)
|
||||
#define CHG_INT_OK_DETBAT_MASK BIT(CHG_INT_OK_DETBAT_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_DETAILS_00 register */
|
||||
#define CHG_DETAILS_00_CHGIN_SHIFT 5
|
||||
#define CHG_DETAILS_00_CHGIN_MASK (0x3 << CHG_DETAILS_00_CHGIN_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_DETAILS_01 register */
|
||||
#define CHG_DETAILS_01_CHG_SHIFT 0
|
||||
#define CHG_DETAILS_01_BAT_SHIFT 4
|
||||
#define CHG_DETAILS_01_TREG_SHIFT 7
|
||||
#define CHG_DETAILS_01_CHG_MASK (0xf << CHG_DETAILS_01_CHG_SHIFT)
|
||||
#define CHG_DETAILS_01_BAT_MASK (0x7 << CHG_DETAILS_01_BAT_SHIFT)
|
||||
#define CHG_DETAILS_01_TREG_MASK BIT(7)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_DETAILS_01/CHG field */
|
||||
enum max77693_charger_charging_state {
|
||||
MAX77693_CHARGING_PREQUALIFICATION = 0x0,
|
||||
MAX77693_CHARGING_FAST_CONST_CURRENT,
|
||||
MAX77693_CHARGING_FAST_CONST_VOLTAGE,
|
||||
MAX77693_CHARGING_TOP_OFF,
|
||||
MAX77693_CHARGING_DONE,
|
||||
MAX77693_CHARGING_HIGH_TEMP,
|
||||
MAX77693_CHARGING_TIMER_EXPIRED,
|
||||
MAX77693_CHARGING_THERMISTOR_SUSPEND,
|
||||
MAX77693_CHARGING_OFF,
|
||||
MAX77693_CHARGING_RESERVED,
|
||||
MAX77693_CHARGING_OVER_TEMP,
|
||||
MAX77693_CHARGING_WATCHDOG_EXPIRED,
|
||||
};
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_DETAILS_01/BAT field */
|
||||
enum max77693_charger_battery_state {
|
||||
MAX77693_BATTERY_NOBAT = 0x0,
|
||||
/* Dead-battery or low-battery prequalification */
|
||||
MAX77693_BATTERY_PREQUALIFICATION,
|
||||
MAX77693_BATTERY_TIMER_EXPIRED,
|
||||
MAX77693_BATTERY_GOOD,
|
||||
MAX77693_BATTERY_LOWVOLTAGE,
|
||||
MAX77693_BATTERY_OVERVOLTAGE,
|
||||
MAX77693_BATTERY_OVERCURRENT,
|
||||
MAX77693_BATTERY_RESERVED,
|
||||
};
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_DETAILS_02 register */
|
||||
#define CHG_DETAILS_02_BYP_SHIFT 0
|
||||
#define CHG_DETAILS_02_BYP_MASK (0xf << CHG_DETAILS_02_BYP_SHIFT)
|
||||
|
||||
/* MAX77693 CHG_CNFG_00 register */
|
||||
#define CHG_CNFG_00_CHG_MASK 0x1
|
||||
#define CHG_CNFG_00_BUCK_MASK 0x4
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_01 register */
|
||||
#define CHG_CNFG_01_FCHGTIME_SHIFT 0
|
||||
#define CHG_CNFG_01_CHGRSTRT_SHIFT 4
|
||||
#define CHG_CNFG_01_PQEN_SHIFT 7
|
||||
#define CHG_CNFG_01_FCHGTIME_MASK (0x7 << CHG_CNFG_01_FCHGTIME_SHIFT)
|
||||
#define CHG_CNFG_01_CHGRSTRT_MASK (0x3 << CHG_CNFG_01_CHGRSTRT_SHIFT)
|
||||
#define CHG_CNFG_01_PQEN_MAKS BIT(CHG_CNFG_01_PQEN_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_03 register */
|
||||
#define CHG_CNFG_03_TOITH_SHIFT 0
|
||||
#define CHG_CNFG_03_TOTIME_SHIFT 3
|
||||
#define CHG_CNFG_03_TOITH_MASK (0x7 << CHG_CNFG_03_TOITH_SHIFT)
|
||||
#define CHG_CNFG_03_TOTIME_MASK (0x7 << CHG_CNFG_03_TOTIME_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_04 register */
|
||||
#define CHG_CNFG_04_CHGCVPRM_SHIFT 0
|
||||
#define CHG_CNFG_04_MINVSYS_SHIFT 5
|
||||
#define CHG_CNFG_04_CHGCVPRM_MASK (0x1f << CHG_CNFG_04_CHGCVPRM_SHIFT)
|
||||
#define CHG_CNFG_04_MINVSYS_MASK (0x7 << CHG_CNFG_04_MINVSYS_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_06 register */
|
||||
#define CHG_CNFG_06_CHGPROT_SHIFT 2
|
||||
#define CHG_CNFG_06_CHGPROT_MASK (0x3 << CHG_CNFG_06_CHGPROT_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_07 register */
|
||||
#define CHG_CNFG_07_REGTEMP_SHIFT 5
|
||||
#define CHG_CNFG_07_REGTEMP_MASK (0x3 << CHG_CNFG_07_REGTEMP_SHIFT)
|
||||
|
||||
/* MAX77693_CHG_REG_CHG_CNFG_12 register */
|
||||
#define CHG_CNFG_12_B2SOVRC_SHIFT 0
|
||||
#define CHG_CNFG_12_VCHGINREG_SHIFT 3
|
||||
#define CHG_CNFG_12_B2SOVRC_MASK (0x7 << CHG_CNFG_12_B2SOVRC_SHIFT)
|
||||
#define CHG_CNFG_12_VCHGINREG_MASK (0x3 << CHG_CNFG_12_VCHGINREG_SHIFT)
|
||||
|
||||
/* MAX77693 CHG_CNFG_09 Register */
|
||||
#define CHG_CNFG_09_CHGIN_ILIM_MASK 0x7F
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/alarmtimer.h>
|
||||
|
||||
enum data_source {
|
||||
CM_BATTERY_PRESENT,
|
||||
@ -44,29 +45,6 @@ enum cm_event_types {
|
||||
CM_EVENT_OTHERS,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct charger_global_desc
|
||||
* @rtc_name: the name of RTC used to wake up the system from suspend.
|
||||
* @rtc_only_wakeup:
|
||||
* If the system is woken up by waekup-sources other than the RTC or
|
||||
* callbacks, Charger Manager should recognize with
|
||||
* rtc_only_wakeup() returning false.
|
||||
* If the RTC given to CM is the only wakeup reason,
|
||||
* rtc_only_wakeup should return true.
|
||||
* @assume_timer_stops_in_suspend:
|
||||
* Assume that the jiffy timer stops in suspend-to-RAM.
|
||||
* When enabled, CM does not rely on jiffies value in
|
||||
* suspend_again and assumes that jiffies value does not
|
||||
* change during suspend.
|
||||
*/
|
||||
struct charger_global_desc {
|
||||
char *rtc_name;
|
||||
|
||||
bool (*rtc_only_wakeup)(void);
|
||||
|
||||
bool assume_timer_stops_in_suspend;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct charger_cable
|
||||
* @extcon_name: the name of extcon device.
|
||||
@ -266,22 +244,14 @@ struct charger_manager {
|
||||
char psy_name_buf[PSY_NAME_MAX + 1];
|
||||
struct power_supply charger_psy;
|
||||
|
||||
bool status_save_ext_pwr_inserted;
|
||||
bool status_save_batt;
|
||||
|
||||
u64 charging_start_time;
|
||||
u64 charging_end_time;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CHARGER_MANAGER
|
||||
extern int setup_charger_manager(struct charger_global_desc *gd);
|
||||
extern bool cm_suspend_again(void);
|
||||
extern void cm_notify_event(struct power_supply *psy,
|
||||
enum cm_event_types type, char *msg);
|
||||
#else
|
||||
static inline int setup_charger_manager(struct charger_global_desc *gd)
|
||||
{ return 0; }
|
||||
static inline bool cm_suspend_again(void) { return false; }
|
||||
static inline void cm_notify_event(struct power_supply *psy,
|
||||
enum cm_event_types type, char *msg) { }
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user