- Initial support for the following chips * max77836 (charger) * max14577 (charger) * bq27742 (battery gauge) * ltc2952 (poweroff) * stih416 (restart) * syscon-reboot (restart) * gpio-restart (restart) - cleanup of power supply core - misc. fixes in power supply and reset drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJUOWD7AAoJENju1/PIO/qaj5QP/AtbG96NRpX6Ou8W+qulhSbe npTKItRMERZhV+lJUAdmLxQq8Cy6I5Cobdj140oIM6HIKZ7Dh6jChxd2t+RCLsO0 Y16VMq45L9E/mQuVCLqSSNCPKrRjbF7hW+8gpym/Yc846o3Fv8nhTQzkWxCsVrDR rNerRnrUyvKpqRpCN88u4ZuOHjm/146nZtTsAzvL8nGnQBZFJHv4eu7D5nwSN0QS FrLiI/mE7h1KFcdK5LrZf51IfEP9w4RVV3vs1wEuP0iPS5dguv2dNzXXVZa5eD/2 AYSSouVKkgdwryLAvIIaDLD405UYl5Xgt5J6d+QKXxm2+eeZMEY36+PEK5WtRtBX CykzO2BgvxyOdz2HU9D8irR1Li71jVSWwQITMJ6bQsbKEQAhltmkmsTOxtqB+ekd 254rKcKWTqllmEhg1LR8PCidf8OZNEcKzXi4XmQdQIcKNF9tvdTXP5c0/FzuIAlQ tqWZW1kQK6/TTfx+XxeeszJWY5VknIaka2Bi83pOZYtu94CtUFBdEVkpaYmC433+ qLOto7VMy+AKYViTJJDDEnSEFiLNZz/zF+SUDf+YB1RelULSF3zC8CbDcZQa5fNm 4qTU1fl2gGrIZ1jDPPihm6xP8r9WkeuQWEytG2UJZoa+l4XmzfLHBUUDVo0zqTd3 txfmKca8Y3GhXGwINi1m =sj9S -----END PGP SIGNATURE----- Merge tag 'for-v3.18' of git://git.infradead.org/battery-2.6 Pull power supply and reset updates from Sebastian Reichel: - Initial support for the following chips * max77836 (charger) * max14577 (charger) * bq27742 (battery gauge) * ltc2952 (poweroff) * stih416 (restart) * syscon-reboot (restart) * gpio-restart (restart) - cleanup of power supply core - misc fixes in power supply and reset drivers * tag 'for-v3.18' of git://git.infradead.org/battery-2.6: (48 commits) power: ab8500_fg: Fix build warning Documentation: charger: max14577: Update the date of introducing ABI power: reset: corrections for simple syscon reboot driver Documentation: power: reset: Add documentation for generic SYSCON reboot driver power: reset: Add generic SYSCON register mapped reset bq27x00_battery: Fix flag reading for bq27742 power: reset: use restart_notifier mechanism for msm-poweroff power: Add simple gpio-restart driver power: reset: st: Provide DT bindings for ST's Power Reset driver power: reset: Add restart functionality for STiH41x platforms power: charger-manager: Fix NULL pointer exception with missing cm-fuel-gauge power: max14577: Fix circular config SYSFS dependency power: gpio-charger: do not use gpio value directly power: max8925: Use of_get_child_by_name power: max8925: Fix NULL ptr dereference on memory allocation failure bq27x00_battery: Add support to bq27742 Documentation: charger: max14577: Document exported sysfs entry devicetree: mfd: max14577: Add device tree bindings document power: max17040: Add ID for MAX77836 Fuel Gauge block charger: max14577: Configure battery-dependent settings from DTS and sysfs ... Conflicts: drivers/power/reset/Kconfig drivers/power/reset/Makefiletirimbino
commit
50fa86172b
@ -0,0 +1,54 @@ |
||||
Drive a GPIO line that can be used to restart the system from a restart |
||||
handler. |
||||
|
||||
This binding supports level and edge triggered reset. At driver load |
||||
time, the driver will request the given gpio line and install a restart |
||||
handler. If the optional properties 'open-source' is not found, the GPIO line |
||||
will be driven in the inactive state. Otherwise its not driven until |
||||
the restart is initiated. |
||||
|
||||
When the system is restarted, the restart handler will be invoked in |
||||
priority order. The gpio is configured as an output, and driven active, |
||||
triggering a level triggered reset condition. This will also cause an |
||||
inactive->active edge condition, triggering positive edge triggered |
||||
reset. After a delay specified by active-delay, the GPIO is set to |
||||
inactive, thus causing an active->inactive edge, triggering negative edge |
||||
triggered reset. After a delay specified by inactive-delay, the GPIO |
||||
is driven active again. After a delay specified by wait-delay, the |
||||
restart handler completes allowing other restart handlers to be attempted. |
||||
|
||||
Required properties: |
||||
- compatible : should be "gpio-restart". |
||||
- gpios : The GPIO to set high/low, see "gpios property" in |
||||
Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be |
||||
low to reset the board set it to "Active Low", otherwise set |
||||
gpio to "Active High". |
||||
|
||||
Optional properties: |
||||
- open-source : Treat the GPIO as being open source and defer driving |
||||
it to when the restart is initiated. If this optional property is not |
||||
specified, the GPIO is initialized as an output in its inactive state. |
||||
- priority : A priority ranging from 0 to 255 (default 128) according to |
||||
the following guidelines: |
||||
0: Restart handler of last resort, with limited restart |
||||
capabilities |
||||
128: Default restart handler; use if no other restart handler is |
||||
expected to be available, and/or if restart functionality is |
||||
sufficient to restart the entire system |
||||
255: Highest priority restart handler, will preempt all other |
||||
restart handlers |
||||
- active-delay: Delay (default 100) to wait after driving gpio active [ms] |
||||
- inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms] |
||||
- wait-delay: Delay (default 3000) to wait after completing restart |
||||
sequence [ms] |
||||
|
||||
Examples: |
||||
|
||||
gpio-restart { |
||||
compatible = "gpio-restart"; |
||||
gpios = <&gpio 4 0>; |
||||
priority = <128>; |
||||
active-delay = <100>; |
||||
inactive-delay = <100>; |
||||
wait-delay = <3000>; |
||||
}; |
@ -0,0 +1,146 @@ |
||||
Maxim MAX14577/77836 Multi-Function Device |
||||
|
||||
MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+ |
||||
Battery Charger and SFOUT LDO output for powering USB devices. It is |
||||
interfaced to host controller using I2C. |
||||
|
||||
MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge. |
||||
|
||||
|
||||
Required properties: |
||||
- compatible : Must be "maxim,max14577" or "maxim,max77836". |
||||
- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836) |
||||
- interrupts : IRQ line for the chip. |
||||
- interrupt-parent : The parent interrupt controller. |
||||
|
||||
|
||||
Required nodes: |
||||
- charger : |
||||
Node for configuring the charger driver. |
||||
Required properties: |
||||
- compatible : "maxim,max14577-charger" |
||||
or "maxim,max77836-charger" |
||||
- maxim,fast-charge-uamp : Current in uA for Fast Charge; |
||||
Valid values: |
||||
- for max14577: 90000 - 950000; |
||||
- for max77836: 45000 - 475000; |
||||
- maxim,eoc-uamp : Current in uA for End-Of-Charge mode; |
||||
Valid values: |
||||
- for max14577: 50000 - 200000; |
||||
- for max77836: 5000 - 100000; |
||||
- maxim,ovp-uvolt : OverVoltage Protection Threshold in uV; |
||||
In an overvoltage condition, INT asserts and charging |
||||
stops. Valid values: |
||||
- 6000000, 6500000, 7000000, 7500000; |
||||
- maxim,constant-uvolt : Battery Constant Voltage in uV; |
||||
Valid values: |
||||
- 4000000 - 4280000 (step by 20000); |
||||
- 4350000; |
||||
|
||||
|
||||
Optional nodes: |
||||
- max14577-muic/max77836-muic : |
||||
Node used only by extcon consumers. |
||||
Required properties: |
||||
- compatible : "maxim,max14577-muic" or "maxim,max77836-muic" |
||||
|
||||
- regulators : |
||||
Required properties: |
||||
- compatible : "maxim,max14577-regulator" |
||||
or "maxim,max77836-regulator" |
||||
|
||||
May contain a sub-node per regulator from the list below. Each |
||||
sub-node should contain the constraints and initialization information |
||||
for that regulator. See regulator.txt for a description of standard |
||||
properties for these sub-nodes. |
||||
|
||||
List of valid regulator names: |
||||
- for max14577: CHARGER, SAFEOUT. |
||||
- for max77836: CHARGER, SAFEOUT, LDO1, LDO2. |
||||
|
||||
The SAFEOUT is a fixed voltage regulator so there is no need to specify |
||||
voltages for it. |
||||
|
||||
|
||||
Example: |
||||
|
||||
#include <dt-bindings/interrupt-controller/irq.h> |
||||
|
||||
max14577@25 { |
||||
compatible = "maxim,max14577"; |
||||
reg = <0x25>; |
||||
interrupt-parent = <&gpx1>; |
||||
interrupts = <5 IRQ_TYPE_NONE>; |
||||
|
||||
muic: max14577-muic { |
||||
compatible = "maxim,max14577-muic"; |
||||
}; |
||||
|
||||
regulators { |
||||
compatible = "maxim,max14577-regulator"; |
||||
|
||||
SAFEOUT { |
||||
regulator-name = "SAFEOUT"; |
||||
}; |
||||
CHARGER { |
||||
regulator-name = "CHARGER"; |
||||
regulator-min-microamp = <90000>; |
||||
regulator-max-microamp = <950000>; |
||||
regulator-boot-on; |
||||
}; |
||||
}; |
||||
|
||||
charger { |
||||
compatible = "maxim,max14577-charger"; |
||||
|
||||
maxim,constant-uvolt = <4350000>; |
||||
maxim,fast-charge-uamp = <450000>; |
||||
maxim,eoc-uamp = <50000>; |
||||
maxim,ovp-uvolt = <6500000>; |
||||
}; |
||||
}; |
||||
|
||||
|
||||
max77836@25 { |
||||
compatible = "maxim,max77836"; |
||||
reg = <0x25>; |
||||
interrupt-parent = <&gpx1>; |
||||
interrupts = <5 IRQ_TYPE_NONE>; |
||||
|
||||
muic: max77836-muic { |
||||
compatible = "maxim,max77836-muic"; |
||||
}; |
||||
|
||||
regulators { |
||||
compatible = "maxim,max77836-regulator"; |
||||
|
||||
SAFEOUT { |
||||
regulator-name = "SAFEOUT"; |
||||
}; |
||||
CHARGER { |
||||
regulator-name = "CHARGER"; |
||||
regulator-min-microamp = <90000>; |
||||
regulator-max-microamp = <950000>; |
||||
regulator-boot-on; |
||||
}; |
||||
LDO1 { |
||||
regulator-name = "LDO1"; |
||||
regulator-min-microvolt = <2700000>; |
||||
regulator-max-microvolt = <2700000>; |
||||
}; |
||||
LDO2 { |
||||
regulator-name = "LDO2"; |
||||
regulator-min-microvolt = <800000>; |
||||
regulator-max-microvolt = <3950000>; |
||||
}; |
||||
}; |
||||
|
||||
charger { |
||||
compatible = "maxim,max77836-charger"; |
||||
|
||||
maxim,constant-uvolt = <4350000>; |
||||
maxim,fast-charge-uamp = <225000>; |
||||
maxim,eoc-uamp = <7500>; |
||||
maxim,ovp-uvolt = <6500000>; |
||||
}; |
||||
}; |
@ -0,0 +1,26 @@ |
||||
Binding for the LTC2952 PowerPath controller |
||||
|
||||
This chip is used to externally trigger a system shut down. Once the trigger has |
||||
been sent, the chips' watchdog has to be reset to gracefully shut down. |
||||
If the Linux systems decides to shut down it powers off the platform via the |
||||
kill signal. |
||||
|
||||
Required properties: |
||||
|
||||
- compatible: Must contain: "lltc,ltc2952" |
||||
- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the |
||||
chip's trigger line |
||||
- watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the |
||||
chip's watchdog line |
||||
- kill-gpios: phandle + gpio-specifier for the GPIO connected to the |
||||
chip's kill line |
||||
|
||||
Example: |
||||
|
||||
ltc2952 { |
||||
compatible = "lltc,ltc2952"; |
||||
|
||||
trigger-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; |
||||
watchdog-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; |
||||
kill-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; |
||||
}; |
@ -0,0 +1,11 @@ |
||||
*Device-Tree bindings for ST SW reset functionality |
||||
|
||||
Required properties: |
||||
- compatible: should be "st,<chip>-restart". |
||||
- st,syscfg: should be a phandle of the syscfg node. |
||||
|
||||
Example node: |
||||
restart { |
||||
compatible = "st,stih416-restart"; |
||||
st,syscfg = <&syscfg_sbc>; |
||||
}; |
@ -0,0 +1,23 @@ |
||||
Generic SYSCON mapped register reset driver |
||||
|
||||
This is a generic reset driver using syscon to map the reset register. |
||||
The reset is generally performed with a write to the reset register |
||||
defined by the register map pointed by syscon reference plus the offset |
||||
with the mask defined in the reboot node. |
||||
|
||||
Required properties: |
||||
- compatible: should contain "syscon-reboot" |
||||
- regmap: this is phandle to the register map node |
||||
- offset: offset in the register map for the reboot register (in bytes) |
||||
- mask: the reset value written to the reboot register (32 bit access) |
||||
|
||||
Default will be little endian mode, 32 bit access only. |
||||
|
||||
Examples: |
||||
|
||||
reboot { |
||||
compatible = "syscon-reboot"; |
||||
regmap = <®mapnode>; |
||||
offset = <0x0>; |
||||
mask = <0x1>; |
||||
}; |
@ -0,0 +1,149 @@ |
||||
/*
|
||||
* Toggles a GPIO pin to restart a device |
||||
* |
||||
* Copyright (C) 2014 Google, Inc. |
||||
* |
||||
* This software is licensed under the terms of the GNU General Public |
||||
* License version 2, as published by the Free Software Foundation, and |
||||
* may be copied, distributed, and modified under those terms. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* Based on the gpio-poweroff driver. |
||||
*/ |
||||
#include <linux/reboot.h> |
||||
#include <linux/kernel.h> |
||||
#include <linux/init.h> |
||||
#include <linux/delay.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/gpio/consumer.h> |
||||
#include <linux/of_platform.h> |
||||
#include <linux/module.h> |
||||
|
||||
struct gpio_restart { |
||||
struct gpio_desc *reset_gpio; |
||||
struct notifier_block restart_handler; |
||||
u32 active_delay_ms; |
||||
u32 inactive_delay_ms; |
||||
u32 wait_delay_ms; |
||||
}; |
||||
|
||||
static int gpio_restart_notify(struct notifier_block *this, |
||||
unsigned long mode, void *cmd) |
||||
{ |
||||
struct gpio_restart *gpio_restart = |
||||
container_of(this, struct gpio_restart, restart_handler); |
||||
|
||||
/* drive it active, also inactive->active edge */ |
||||
gpiod_direction_output(gpio_restart->reset_gpio, 1); |
||||
mdelay(gpio_restart->active_delay_ms); |
||||
|
||||
/* drive inactive, also active->inactive edge */ |
||||
gpiod_set_value(gpio_restart->reset_gpio, 0); |
||||
mdelay(gpio_restart->inactive_delay_ms); |
||||
|
||||
/* drive it active, also inactive->active edge */ |
||||
gpiod_set_value(gpio_restart->reset_gpio, 1); |
||||
|
||||
/* give it some time */ |
||||
mdelay(gpio_restart->wait_delay_ms); |
||||
|
||||
WARN_ON(1); |
||||
|
||||
return NOTIFY_DONE; |
||||
} |
||||
|
||||
static int gpio_restart_probe(struct platform_device *pdev) |
||||
{ |
||||
struct gpio_restart *gpio_restart; |
||||
bool open_source = false; |
||||
u32 property; |
||||
int ret; |
||||
|
||||
gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart), |
||||
GFP_KERNEL); |
||||
if (!gpio_restart) |
||||
return -ENOMEM; |
||||
|
||||
open_source = of_property_read_bool(pdev->dev.of_node, "open-source"); |
||||
|
||||
gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, |
||||
open_source ? GPIOD_IN : GPIOD_OUT_LOW); |
||||
if (IS_ERR(gpio_restart->reset_gpio)) { |
||||
dev_err(&pdev->dev, "Could net get reset GPIO\n"); |
||||
return PTR_ERR(gpio_restart->reset_gpio); |
||||
} |
||||
|
||||
gpio_restart->restart_handler.notifier_call = gpio_restart_notify; |
||||
gpio_restart->restart_handler.priority = 128; |
||||
gpio_restart->active_delay_ms = 100; |
||||
gpio_restart->inactive_delay_ms = 100; |
||||
gpio_restart->wait_delay_ms = 3000; |
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "priority", &property); |
||||
if (!ret) { |
||||
if (property > 255) |
||||
dev_err(&pdev->dev, "Invalid priority property: %u\n", |
||||
property); |
||||
else |
||||
gpio_restart->restart_handler.priority = property; |
||||
} |
||||
|
||||
of_property_read_u32(pdev->dev.of_node, "active-delay", |
||||
&gpio_restart->active_delay_ms); |
||||
of_property_read_u32(pdev->dev.of_node, "inactive-delay", |
||||
&gpio_restart->inactive_delay_ms); |
||||
of_property_read_u32(pdev->dev.of_node, "wait-delay", |
||||
&gpio_restart->wait_delay_ms); |
||||
|
||||
platform_set_drvdata(pdev, gpio_restart); |
||||
|
||||
ret = register_restart_handler(&gpio_restart->restart_handler); |
||||
if (ret) { |
||||
dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", |
||||
__func__, ret); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int gpio_restart_remove(struct platform_device *pdev) |
||||
{ |
||||
struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); |
||||
int ret; |
||||
|
||||
ret = unregister_restart_handler(&gpio_restart->restart_handler); |
||||
if (ret) { |
||||
dev_err(&pdev->dev, |
||||
"%s: cannot unregister restart handler, %d\n", |
||||
__func__, ret); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct of_device_id of_gpio_restart_match[] = { |
||||
{ .compatible = "gpio-restart", }, |
||||
{}, |
||||
}; |
||||
|
||||
static struct platform_driver gpio_restart_driver = { |
||||
.probe = gpio_restart_probe, |
||||
.remove = gpio_restart_remove, |
||||
.driver = { |
||||
.name = "restart-gpio", |
||||
.owner = THIS_MODULE, |
||||
.of_match_table = of_gpio_restart_match, |
||||
}, |
||||
}; |
||||
|
||||
module_platform_driver(gpio_restart_driver); |
||||
|
||||
MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); |
||||
MODULE_DESCRIPTION("GPIO restart driver"); |
||||
MODULE_LICENSE("GPL"); |
@ -0,0 +1,386 @@ |
||||
/*
|
||||
* LTC2952 (PowerPath) driver |
||||
* |
||||
* Copyright (C) 2014, Xsens Technologies BV <info@xsens.com> |
||||
* Maintainer: René Moll <linux@r-moll.nl> |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License |
||||
* as published by the Free Software Foundation; either version 2 |
||||
* of the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* ---------------------------------------- |
||||
* - Description |
||||
* ---------------------------------------- |
||||
* |
||||
* This driver is to be used with an external PowerPath Controller (LTC2952). |
||||
* Its function is to determine when a external shut down is triggered |
||||
* and react by properly shutting down the system. |
||||
* |
||||
* This driver expects a device tree with a ltc2952 entry for pin mapping. |
||||
* |
||||
* ---------------------------------------- |
||||
* - GPIO |
||||
* ---------------------------------------- |
||||
* |
||||
* The following GPIOs are used: |
||||
* - trigger (input) |
||||
* A level change indicates the shut-down trigger. If it's state reverts |
||||
* within the time-out defined by trigger_delay, the shut down is not |
||||
* executed. |
||||
* |
||||
* - watchdog (output) |
||||
* Once a shut down is triggered, the driver will toggle this signal, |
||||
* with an internal (wde_interval) to stall the hardware shut down. |
||||
* |
||||
* - kill (output) |
||||
* The last action during shut down is triggering this signalling, such |
||||
* that the PowerPath Control will power down the hardware. |
||||
* |
||||
* ---------------------------------------- |
||||
* - Interrupts |
||||
* ---------------------------------------- |
||||
* |
||||
* The driver requires a non-shared, edge-triggered interrupt on the trigger |
||||
* GPIO. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/init.h> |
||||
#include <linux/interrupt.h> |
||||
#include <linux/device.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/ktime.h> |
||||
#include <linux/slab.h> |
||||
#include <linux/kmod.h> |
||||
#include <linux/module.h> |
||||
#include <linux/gpio/consumer.h> |
||||
#include <linux/reboot.h> |
||||
|
||||
struct ltc2952_poweroff_data { |
||||
struct hrtimer timer_trigger; |
||||
struct hrtimer timer_wde; |
||||
|
||||
ktime_t trigger_delay; |
||||
ktime_t wde_interval; |
||||
|
||||
struct device *dev; |
||||
|
||||
unsigned int virq; |
||||
|
||||
/**
|
||||
* 0: trigger |
||||
* 1: watchdog |
||||
* 2: kill |
||||
*/ |
||||
struct gpio_desc *gpio[3]; |
||||
}; |
||||
|
||||
static int ltc2952_poweroff_panic; |
||||
static struct ltc2952_poweroff_data *ltc2952_data; |
||||
|
||||
#define POWERPATH_IO_TRIGGER 0 |
||||
#define POWERPATH_IO_WATCHDOG 1 |
||||
#define POWERPATH_IO_KILL 2 |
||||
|
||||
/**
|
||||
* ltc2952_poweroff_timer_wde - Timer callback |
||||
* Toggles the watchdog reset signal each wde_interval |
||||
* |
||||
* @timer: corresponding timer |
||||
* |
||||
* Returns HRTIMER_RESTART for an infinite loop which will only stop when the |
||||
* machine actually shuts down |
||||
*/ |
||||
static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) |
||||
{ |
||||
ktime_t now; |
||||
int state; |
||||
unsigned long overruns; |
||||
|
||||
if (ltc2952_poweroff_panic) |
||||
return HRTIMER_NORESTART; |
||||
|
||||
state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); |
||||
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); |
||||
|
||||
now = hrtimer_cb_get_time(timer); |
||||
overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); |
||||
|
||||
return HRTIMER_RESTART; |
||||
} |
||||
|
||||
static enum hrtimer_restart ltc2952_poweroff_timer_trigger( |
||||
struct hrtimer *timer) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = hrtimer_start(<c2952_data->timer_wde, |
||||
ltc2952_data->wde_interval, HRTIMER_MODE_REL); |
||||
|
||||
if (ret) { |
||||
dev_err(ltc2952_data->dev, "unable to start the timer\n"); |
||||
/*
|
||||
* The device will not toggle the watchdog reset, |
||||
* thus shut down is only safe if the PowerPath controller |
||||
* has a long enough time-off before triggering a hardware |
||||
* power-off. |
||||
* |
||||
* Only sending a warning as the system will power-off anyway |
||||
*/ |
||||
} |
||||
|
||||
dev_info(ltc2952_data->dev, "executing shutdown\n"); |
||||
|
||||
orderly_poweroff(true); |
||||
|
||||
return HRTIMER_NORESTART; |
||||
} |
||||
|
||||
/**
|
||||
* ltc2952_poweroff_handler - Interrupt handler |
||||
* Triggered each time the trigger signal changes state and (de)activates a |
||||
* time-out (timer_trigger). Once the time-out is actually reached the shut |
||||
* down is executed. |
||||
* |
||||
* @irq: IRQ number |
||||
* @dev_id: pointer to the main data structure |
||||
*/ |
||||
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) |
||||
{ |
||||
int ret; |
||||
struct ltc2952_poweroff_data *data = dev_id; |
||||
|
||||
if (ltc2952_poweroff_panic) |
||||
goto irq_ok; |
||||
|
||||
if (hrtimer_active(&data->timer_wde)) { |
||||
/* shutdown is already triggered, nothing to do any more */ |
||||
goto irq_ok; |
||||
} |
||||
|
||||
if (!hrtimer_active(&data->timer_trigger)) { |
||||
ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, |
||||
HRTIMER_MODE_REL); |
||||
|
||||
if (ret) |
||||
dev_err(data->dev, "unable to start the wait timer\n"); |
||||
} else { |
||||
ret = hrtimer_cancel(&data->timer_trigger); |
||||
/* omitting return value check, timer should have been valid */ |
||||
} |
||||
|
||||
irq_ok: |
||||
return IRQ_HANDLED; |
||||
} |
||||
|
||||
static void ltc2952_poweroff_kill(void) |
||||
{ |
||||
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); |
||||
} |
||||
|
||||
static int ltc2952_poweroff_suspend(struct platform_device *pdev, |
||||
pm_message_t state) |
||||
{ |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
static int ltc2952_poweroff_resume(struct platform_device *pdev) |
||||
{ |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) |
||||
{ |
||||
unsigned int i; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->gpio); i++) |
||||
data->gpio[i] = NULL; |
||||
|
||||
data->wde_interval = ktime_set(0, 300L*1E6L); |
||||
data->trigger_delay = ktime_set(2, 500L*1E6L); |
||||
|
||||
hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
||||
data->timer_trigger.function = <c2952_poweroff_timer_trigger; |
||||
|
||||
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
||||
data->timer_wde.function = <c2952_poweroff_timer_wde; |
||||
} |
||||
|
||||
static int ltc2952_poweroff_init(struct platform_device *pdev) |
||||
{ |
||||
int ret, virq; |
||||
unsigned int i; |
||||
struct ltc2952_poweroff_data *data; |
||||
|
||||
static char *name[] = { |
||||
"trigger", |
||||
"watchdog", |
||||
"kill", |
||||
NULL |
||||
}; |
||||
|
||||
data = ltc2952_data; |
||||
ltc2952_poweroff_default(ltc2952_data); |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { |
||||
ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); |
||||
|
||||
if (IS_ERR(ltc2952_data->gpio[i])) { |
||||
ret = PTR_ERR(ltc2952_data->gpio[i]); |
||||
dev_err(&pdev->dev, |
||||
"unable to claim the following gpio: %s\n", |
||||
name[i]); |
||||
goto err_io; |
||||
} |
||||
} |
||||
|
||||
ret = gpiod_direction_output( |
||||
ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); |
||||
if (ret) { |
||||
dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); |
||||
goto err_io; |
||||
} |
||||
|
||||
ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); |
||||
if (ret) { |
||||
dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); |
||||
goto err_io; |
||||
} |
||||
|
||||
virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); |
||||
if (virq < 0) { |
||||
dev_err(&pdev->dev, "cannot map GPIO as interrupt"); |
||||
goto err_io; |
||||
} |
||||
|
||||
ltc2952_data->virq = virq; |
||||
ret = request_irq(virq, |
||||
ltc2952_poweroff_handler, |
||||
(IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), |
||||
"ltc2952-poweroff", |
||||
ltc2952_data |
||||
); |
||||
|
||||
if (ret) { |
||||
dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); |
||||
goto err_io; |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
err_io: |
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) |
||||
if (ltc2952_data->gpio[i]) |
||||
gpiod_put(ltc2952_data->gpio[i]); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int ltc2952_poweroff_probe(struct platform_device *pdev) |
||||
{ |
||||
int ret; |
||||
|
||||
if (pm_power_off) { |
||||
dev_err(&pdev->dev, "pm_power_off already registered"); |
||||
return -EBUSY; |
||||
} |
||||
|
||||
ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); |
||||
if (!ltc2952_data) |
||||
return -ENOMEM; |
||||
|
||||
ltc2952_data->dev = &pdev->dev; |
||||
|
||||
ret = ltc2952_poweroff_init(pdev); |
||||
if (ret) |
||||
goto err; |
||||
|
||||
pm_power_off = <c2952_poweroff_kill; |
||||
|
||||
dev_info(&pdev->dev, "probe successful\n"); |
||||
|
||||
return 0; |
||||
|
||||
err: |
||||
kfree(ltc2952_data); |
||||
return ret; |
||||
} |
||||
|
||||
static int ltc2952_poweroff_remove(struct platform_device *pdev) |
||||
{ |
||||
unsigned int i; |
||||
|
||||
pm_power_off = NULL; |
||||
|
||||
if (ltc2952_data) { |
||||
free_irq(ltc2952_data->virq, ltc2952_data); |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) |
||||
gpiod_put(ltc2952_data->gpio[i]); |
||||
|
||||
kfree(ltc2952_data); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct of_device_id of_ltc2952_poweroff_match[] = { |
||||
{ .compatible = "lltc,ltc2952"}, |
||||
{}, |
||||
}; |
||||
MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); |
||||
|
||||
static struct platform_driver ltc2952_poweroff_driver = { |
||||
.probe = ltc2952_poweroff_probe, |
||||
.remove = ltc2952_poweroff_remove, |
||||
.driver = { |
||||
.name = "ltc2952-poweroff", |
||||
.owner = THIS_MODULE, |
||||
.of_match_table = of_ltc2952_poweroff_match, |
||||
}, |
||||
.suspend = ltc2952_poweroff_suspend, |
||||
.resume = ltc2952_poweroff_resume, |
||||
}; |
||||
|
||||
static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, |
||||
unsigned long code, void *unused) |
||||
{ |
||||
ltc2952_poweroff_panic = 1; |
||||
return NOTIFY_DONE; |
||||
} |
||||
|
||||
static struct notifier_block ltc2952_poweroff_panic_nb = { |
||||
.notifier_call = ltc2952_poweroff_notify_panic, |
||||
}; |
||||
|
||||
static int __init ltc2952_poweroff_platform_init(void) |
||||
{ |
||||
ltc2952_poweroff_panic = 0; |
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list, |
||||
<c2952_poweroff_panic_nb); |
||||
|
||||
return platform_driver_register(<c2952_poweroff_driver); |
||||
} |
||||
|
||||
static void __exit ltc2952_poweroff_platform_exit(void) |
||||
{ |
||||
atomic_notifier_chain_unregister(&panic_notifier_list, |
||||
<c2952_poweroff_panic_nb); |
||||
|
||||
platform_driver_unregister(<c2952_poweroff_driver); |
||||
} |
||||
|
||||
module_init(ltc2952_poweroff_platform_init); |
||||
module_exit(ltc2952_poweroff_platform_exit); |
||||
|
||||
MODULE_AUTHOR("René Moll <rene.moll@xsens.com>"); |
||||
MODULE_DESCRIPTION("LTC PowerPath power-off driver"); |
||||
MODULE_LICENSE("GPL v2"); |
@ -0,0 +1,151 @@ |
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics |
||||
* |
||||
* Power off Restart driver, used in STMicroelectronics devices. |
||||
* |
||||
* Author: Christophe Kerello <christophe.kerello@st.com> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2, as |
||||
* published by the Free Software Foundation. |
||||
*/ |
||||
|
||||
#include <linux/module.h> |
||||
#include <linux/of.h> |
||||
#include <linux/of_platform.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/mfd/syscon.h> |
||||
#include <linux/regmap.h> |
||||
|
||||
#include <asm/system_misc.h> |
||||
|
||||
struct reset_syscfg { |
||||
struct regmap *regmap; |
||||
/* syscfg used for reset */ |
||||
unsigned int offset_rst; |
||||
unsigned int mask_rst; |
||||
/* syscfg used for unmask the reset */ |
||||
unsigned int offset_rst_msk; |
||||
unsigned int mask_rst_msk; |
||||
}; |
||||
|
||||
/* STiH415 */ |
||||
#define STIH415_SYSCFG_11 0x2c |
||||
#define STIH415_SYSCFG_15 0x3c |
||||
|
||||
static struct reset_syscfg stih415_reset = { |
||||
.offset_rst = STIH415_SYSCFG_11, |
||||
.mask_rst = BIT(0), |
||||
.offset_rst_msk = STIH415_SYSCFG_15, |
||||
.mask_rst_msk = BIT(0) |
||||
}; |
||||
|
||||
/* STiH416 */ |
||||
#define STIH416_SYSCFG_500 0x7d0 |
||||
#define STIH416_SYSCFG_504 0x7e0 |
||||
|
||||
static struct reset_syscfg stih416_reset = { |
||||
.offset_rst = STIH416_SYSCFG_500, |
||||
.mask_rst = BIT(0), |
||||
.offset_rst_msk = STIH416_SYSCFG_504, |
||||
.mask_rst_msk = BIT(0) |
||||
}; |
||||
|
||||
/* STiH407 */ |
||||
#define STIH407_SYSCFG_4000 0x0 |
||||
#define STIH407_SYSCFG_4008 0x20 |
||||
|
||||
static struct reset_syscfg stih407_reset = { |
||||
.offset_rst = STIH407_SYSCFG_4000, |
||||
.mask_rst = BIT(0), |
||||
.offset_rst_msk = STIH407_SYSCFG_4008, |
||||
.mask_rst_msk = BIT(0) |
||||
}; |
||||
|
||||
/* STiD127 */ |
||||
#define STID127_SYSCFG_700 0x0 |
||||
#define STID127_SYSCFG_773 0x124 |
||||
|
||||
static struct reset_syscfg stid127_reset = { |
||||
.offset_rst = STID127_SYSCFG_773, |
||||
.mask_rst = BIT(0), |
||||
.offset_rst_msk = STID127_SYSCFG_700, |
||||
.mask_rst_msk = BIT(8) |
||||
}; |
||||
|
||||
static struct reset_syscfg *st_restart_syscfg; |
||||
|
||||
static void st_restart(enum reboot_mode reboot_mode, const char *cmd) |
||||
{ |
||||
/* reset syscfg updated */ |
||||
regmap_update_bits(st_restart_syscfg->regmap, |
||||
st_restart_syscfg->offset_rst, |
||||
st_restart_syscfg->mask_rst, |
||||
0); |
||||
|
||||
/* unmask the reset */ |
||||
regmap_update_bits(st_restart_syscfg->regmap, |
||||
st_restart_syscfg->offset_rst_msk, |
||||
st_restart_syscfg->mask_rst_msk, |
||||
0); |
||||
} |
||||
|
||||
static struct of_device_id st_reset_of_match[] = { |
||||
{ |
||||
.compatible = "st,stih415-restart", |
||||
.data = (void *)&stih415_reset, |
||||
}, { |
||||
.compatible = "st,stih416-restart", |
||||
.data = (void *)&stih416_reset, |
||||
}, { |
||||
.compatible = "st,stih407-restart", |
||||
.data = (void *)&stih407_reset, |
||||
}, { |
||||
.compatible = "st,stid127-restart", |
||||
.data = (void *)&stid127_reset, |
||||
}, |
||||
{} |
||||
}; |
||||
|
||||
static int st_reset_probe(struct platform_device *pdev) |
||||
{ |
||||
struct device_node *np = pdev->dev.of_node; |
||||
const struct of_device_id *match; |
||||
struct device *dev = &pdev->dev; |
||||
|
||||
match = of_match_device(st_reset_of_match, dev); |
||||
if (!match) |
||||
return -ENODEV; |
||||
|
||||
st_restart_syscfg = (struct reset_syscfg *)match->data; |
||||
|
||||
st_restart_syscfg->regmap = |
||||
syscon_regmap_lookup_by_phandle(np, "st,syscfg"); |
||||
if (IS_ERR(st_restart_syscfg->regmap)) { |
||||
dev_err(dev, "No syscfg phandle specified\n"); |
||||
return PTR_ERR(st_restart_syscfg->regmap); |
||||
} |
||||
|
||||
arm_pm_restart = st_restart; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct platform_driver st_reset_driver = { |
||||
.probe = st_reset_probe, |
||||
.driver = { |
||||
.name = "st_reset", |
||||
.of_match_table = st_reset_of_match, |
||||
}, |
||||
}; |
||||
|
||||
static int __init st_reset_init(void) |
||||
{ |
||||
return platform_driver_register(&st_reset_driver); |
||||
} |
||||
|
||||
device_initcall(st_reset_init); |
||||
|
||||
MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>"); |
||||
MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver"); |
||||
MODULE_LICENSE("GPL v2"); |
@ -0,0 +1,91 @@ |
||||
/*
|
||||
* Generic Syscon Reboot Driver |
||||
* |
||||
* Copyright (c) 2013, Applied Micro Circuits Corporation |
||||
* Author: Feng Kan <fkan@apm.com> |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
*/ |
||||
#include <linux/delay.h> |
||||
#include <linux/io.h> |
||||
#include <linux/notifier.h> |
||||
#include <linux/mfd/syscon.h> |
||||
#include <linux/of_address.h> |
||||
#include <linux/of_device.h> |
||||
#include <linux/platform_device.h> |
||||
#include <linux/reboot.h> |
||||
#include <linux/regmap.h> |
||||
|
||||
struct syscon_reboot_context { |
||||
struct regmap *map; |
||||
u32 offset; |
||||
u32 mask; |
||||
struct notifier_block restart_handler; |
||||
}; |
||||
|
||||
static int syscon_restart_handle(struct notifier_block *this, |
||||
unsigned long mode, void *cmd) |
||||
{ |
||||
struct syscon_reboot_context *ctx = |
||||
container_of(this, struct syscon_reboot_context, |
||||
restart_handler); |
||||
|
||||
/* Issue the reboot */ |
||||
regmap_write(ctx->map, ctx->offset, ctx->mask); |
||||
|
||||
mdelay(1000); |
||||
|
||||
pr_emerg("Unable to restart system\n"); |
||||
return NOTIFY_DONE; |
||||
} |
||||
|
||||
static int syscon_reboot_probe(struct platform_device *pdev) |
||||
{ |
||||
struct syscon_reboot_context *ctx; |
||||
struct device *dev = &pdev->dev; |
||||
int err; |
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
||||
if (!ctx) |
||||
return -ENOMEM; |
||||
|
||||
ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap"); |
||||
if (IS_ERR(ctx->map)) |
||||
return PTR_ERR(ctx->map); |
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) |
||||
return -EINVAL; |
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask)) |
||||
return -EINVAL; |
||||
|
||||
ctx->restart_handler.notifier_call = syscon_restart_handle; |
||||
ctx->restart_handler.priority = 128; |
||||
err = register_restart_handler(&ctx->restart_handler); |
||||
if (err) |
||||
dev_err(dev, "can't register restart notifier (err=%d)\n", err); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
static struct of_device_id syscon_reboot_of_match[] = { |
||||
{ .compatible = "syscon-reboot" }, |
||||
{} |
||||
}; |
||||
|
||||
static struct platform_driver syscon_reboot_driver = { |
||||
.probe = syscon_reboot_probe, |
||||
.driver = { |
||||
.name = "syscon-reboot", |
||||
.of_match_table = syscon_reboot_of_match, |
||||
}, |
||||
}; |
||||
module_platform_driver(syscon_reboot_driver); |
Loading…
Reference in new issue