xref: /OK3568_Linux_fs/kernel/drivers/power/reset/ltc2952-poweroff.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * LTC2952 (PowerPath) driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2014, Xsens Technologies BV <info@xsens.com>
6*4882a593Smuzhiyun  * Maintainer: René Moll <linux@r-moll.nl>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * ----------------------------------------
9*4882a593Smuzhiyun  * - Description
10*4882a593Smuzhiyun  * ----------------------------------------
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * This driver is to be used with an external PowerPath Controller (LTC2952).
13*4882a593Smuzhiyun  * Its function is to determine when a external shut down is triggered
14*4882a593Smuzhiyun  * and react by properly shutting down the system.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * This driver expects a device tree with a ltc2952 entry for pin mapping.
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * ----------------------------------------
19*4882a593Smuzhiyun  * - GPIO
20*4882a593Smuzhiyun  * ----------------------------------------
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * The following GPIOs are used:
23*4882a593Smuzhiyun  * - trigger (input)
24*4882a593Smuzhiyun  *     A level change indicates the shut-down trigger. If it's state reverts
25*4882a593Smuzhiyun  *     within the time-out defined by trigger_delay, the shut down is not
26*4882a593Smuzhiyun  *     executed. If no pin is assigned to this input, the driver will start the
27*4882a593Smuzhiyun  *     watchdog toggle immediately. The chip will only power off the system if
28*4882a593Smuzhiyun  *     it is requested to do so through the kill line.
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * - watchdog (output)
31*4882a593Smuzhiyun  *     Once a shut down is triggered, the driver will toggle this signal,
32*4882a593Smuzhiyun  *     with an internal (wde_interval) to stall the hardware shut down.
33*4882a593Smuzhiyun  *
34*4882a593Smuzhiyun  * - kill (output)
35*4882a593Smuzhiyun  *     The last action during shut down is triggering this signalling, such
36*4882a593Smuzhiyun  *     that the PowerPath Control will power down the hardware.
37*4882a593Smuzhiyun  *
38*4882a593Smuzhiyun  * ----------------------------------------
39*4882a593Smuzhiyun  * - Interrupts
40*4882a593Smuzhiyun  * ----------------------------------------
41*4882a593Smuzhiyun  *
42*4882a593Smuzhiyun  * The driver requires a non-shared, edge-triggered interrupt on the trigger
43*4882a593Smuzhiyun  * GPIO.
44*4882a593Smuzhiyun  */
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #include <linux/kernel.h>
47*4882a593Smuzhiyun #include <linux/init.h>
48*4882a593Smuzhiyun #include <linux/interrupt.h>
49*4882a593Smuzhiyun #include <linux/device.h>
50*4882a593Smuzhiyun #include <linux/platform_device.h>
51*4882a593Smuzhiyun #include <linux/ktime.h>
52*4882a593Smuzhiyun #include <linux/slab.h>
53*4882a593Smuzhiyun #include <linux/kmod.h>
54*4882a593Smuzhiyun #include <linux/module.h>
55*4882a593Smuzhiyun #include <linux/mod_devicetable.h>
56*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
57*4882a593Smuzhiyun #include <linux/reboot.h>
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun struct ltc2952_poweroff {
60*4882a593Smuzhiyun 	struct hrtimer timer_trigger;
61*4882a593Smuzhiyun 	struct hrtimer timer_wde;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	ktime_t trigger_delay;
64*4882a593Smuzhiyun 	ktime_t wde_interval;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	struct device *dev;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	struct gpio_desc *gpio_trigger;
69*4882a593Smuzhiyun 	struct gpio_desc *gpio_watchdog;
70*4882a593Smuzhiyun 	struct gpio_desc *gpio_kill;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	bool kernel_panic;
73*4882a593Smuzhiyun 	struct notifier_block panic_notifier;
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun #define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m)
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /*
79*4882a593Smuzhiyun  * This global variable is only needed for pm_power_off. We should
80*4882a593Smuzhiyun  * remove it entirely once we don't need the global state anymore.
81*4882a593Smuzhiyun  */
82*4882a593Smuzhiyun static struct ltc2952_poweroff *ltc2952_data;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /**
85*4882a593Smuzhiyun  * ltc2952_poweroff_timer_wde - Timer callback
86*4882a593Smuzhiyun  * Toggles the watchdog reset signal each wde_interval
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  * @timer: corresponding timer
89*4882a593Smuzhiyun  *
90*4882a593Smuzhiyun  * Returns HRTIMER_RESTART for an infinite loop which will only stop when the
91*4882a593Smuzhiyun  * machine actually shuts down
92*4882a593Smuzhiyun  */
ltc2952_poweroff_timer_wde(struct hrtimer * timer)93*4882a593Smuzhiyun static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	ktime_t now;
96*4882a593Smuzhiyun 	int state;
97*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (data->kernel_panic)
100*4882a593Smuzhiyun 		return HRTIMER_NORESTART;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	state = gpiod_get_value(data->gpio_watchdog);
103*4882a593Smuzhiyun 	gpiod_set_value(data->gpio_watchdog, !state);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	now = hrtimer_cb_get_time(timer);
106*4882a593Smuzhiyun 	hrtimer_forward(timer, now, data->wde_interval);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return HRTIMER_RESTART;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
ltc2952_poweroff_start_wde(struct ltc2952_poweroff * data)111*4882a593Smuzhiyun static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	hrtimer_start(&data->timer_wde, data->wde_interval, HRTIMER_MODE_REL);
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun static enum hrtimer_restart
ltc2952_poweroff_timer_trigger(struct hrtimer * timer)117*4882a593Smuzhiyun ltc2952_poweroff_timer_trigger(struct hrtimer *timer)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	ltc2952_poweroff_start_wde(data);
122*4882a593Smuzhiyun 	dev_info(data->dev, "executing shutdown\n");
123*4882a593Smuzhiyun 	orderly_poweroff(true);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	return HRTIMER_NORESTART;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun /**
129*4882a593Smuzhiyun  * ltc2952_poweroff_handler - Interrupt handler
130*4882a593Smuzhiyun  * Triggered each time the trigger signal changes state and (de)activates a
131*4882a593Smuzhiyun  * time-out (timer_trigger). Once the time-out is actually reached the shut
132*4882a593Smuzhiyun  * down is executed.
133*4882a593Smuzhiyun  *
134*4882a593Smuzhiyun  * @irq: IRQ number
135*4882a593Smuzhiyun  * @dev_id: pointer to the main data structure
136*4882a593Smuzhiyun  */
ltc2952_poweroff_handler(int irq,void * dev_id)137*4882a593Smuzhiyun static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = dev_id;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (data->kernel_panic || hrtimer_active(&data->timer_wde)) {
142*4882a593Smuzhiyun 		/* shutdown is already triggered, nothing to do any more */
143*4882a593Smuzhiyun 		return IRQ_HANDLED;
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	if (gpiod_get_value(data->gpio_trigger)) {
147*4882a593Smuzhiyun 		hrtimer_start(&data->timer_trigger, data->trigger_delay,
148*4882a593Smuzhiyun 			      HRTIMER_MODE_REL);
149*4882a593Smuzhiyun 	} else {
150*4882a593Smuzhiyun 		hrtimer_cancel(&data->timer_trigger);
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 	return IRQ_HANDLED;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
ltc2952_poweroff_kill(void)155*4882a593Smuzhiyun static void ltc2952_poweroff_kill(void)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	gpiod_set_value(ltc2952_data->gpio_kill, 1);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
ltc2952_poweroff_default(struct ltc2952_poweroff * data)160*4882a593Smuzhiyun static void ltc2952_poweroff_default(struct ltc2952_poweroff *data)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	data->wde_interval = 300L * NSEC_PER_MSEC;
163*4882a593Smuzhiyun 	data->trigger_delay = ktime_set(2, 500L * NSEC_PER_MSEC);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
166*4882a593Smuzhiyun 	data->timer_trigger.function = ltc2952_poweroff_timer_trigger;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
169*4882a593Smuzhiyun 	data->timer_wde.function = ltc2952_poweroff_timer_wde;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
ltc2952_poweroff_init(struct platform_device * pdev)172*4882a593Smuzhiyun static int ltc2952_poweroff_init(struct platform_device *pdev)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	int ret;
175*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	ltc2952_poweroff_default(data);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog",
180*4882a593Smuzhiyun 					     GPIOD_OUT_LOW);
181*4882a593Smuzhiyun 	if (IS_ERR(data->gpio_watchdog)) {
182*4882a593Smuzhiyun 		ret = PTR_ERR(data->gpio_watchdog);
183*4882a593Smuzhiyun 		dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n");
184*4882a593Smuzhiyun 		return ret;
185*4882a593Smuzhiyun 	}
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW);
188*4882a593Smuzhiyun 	if (IS_ERR(data->gpio_kill)) {
189*4882a593Smuzhiyun 		ret = PTR_ERR(data->gpio_kill);
190*4882a593Smuzhiyun 		dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n");
191*4882a593Smuzhiyun 		return ret;
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	data->gpio_trigger = devm_gpiod_get_optional(&pdev->dev, "trigger",
195*4882a593Smuzhiyun 						     GPIOD_IN);
196*4882a593Smuzhiyun 	if (IS_ERR(data->gpio_trigger)) {
197*4882a593Smuzhiyun 		/*
198*4882a593Smuzhiyun 		 * It's not a problem if the trigger gpio isn't available, but
199*4882a593Smuzhiyun 		 * it is worth a warning if its use was defined in the device
200*4882a593Smuzhiyun 		 * tree.
201*4882a593Smuzhiyun 		 */
202*4882a593Smuzhiyun 		dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n");
203*4882a593Smuzhiyun 		data->gpio_trigger = NULL;
204*4882a593Smuzhiyun 	}
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger),
207*4882a593Smuzhiyun 			     ltc2952_poweroff_handler,
208*4882a593Smuzhiyun 			     (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
209*4882a593Smuzhiyun 			     "ltc2952-poweroff",
210*4882a593Smuzhiyun 			     data)) {
211*4882a593Smuzhiyun 		/*
212*4882a593Smuzhiyun 		 * Some things may have happened:
213*4882a593Smuzhiyun 		 * - No trigger input was defined
214*4882a593Smuzhiyun 		 * - Claiming the GPIO failed
215*4882a593Smuzhiyun 		 * - We could not map to an IRQ
216*4882a593Smuzhiyun 		 * - We couldn't register an interrupt handler
217*4882a593Smuzhiyun 		 *
218*4882a593Smuzhiyun 		 * None of these really are problems, but all of them
219*4882a593Smuzhiyun 		 * disqualify the push button from controlling the power.
220*4882a593Smuzhiyun 		 *
221*4882a593Smuzhiyun 		 * It is therefore important to note that if the ltc2952
222*4882a593Smuzhiyun 		 * detects a button press for long enough, it will still start
223*4882a593Smuzhiyun 		 * its own powerdown window and cut the power on us if we don't
224*4882a593Smuzhiyun 		 * start the watchdog trigger.
225*4882a593Smuzhiyun 		 */
226*4882a593Smuzhiyun 		if (data->gpio_trigger) {
227*4882a593Smuzhiyun 			dev_warn(&pdev->dev,
228*4882a593Smuzhiyun 				 "unable to configure the trigger interrupt\n");
229*4882a593Smuzhiyun 			devm_gpiod_put(&pdev->dev, data->gpio_trigger);
230*4882a593Smuzhiyun 			data->gpio_trigger = NULL;
231*4882a593Smuzhiyun 		}
232*4882a593Smuzhiyun 		dev_info(&pdev->dev,
233*4882a593Smuzhiyun 			 "power down trigger input will not be used\n");
234*4882a593Smuzhiyun 		ltc2952_poweroff_start_wde(data);
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	return 0;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
ltc2952_poweroff_notify_panic(struct notifier_block * nb,unsigned long code,void * unused)240*4882a593Smuzhiyun static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
241*4882a593Smuzhiyun 					 unsigned long code, void *unused)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	data->kernel_panic = true;
246*4882a593Smuzhiyun 	return NOTIFY_DONE;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
ltc2952_poweroff_probe(struct platform_device * pdev)249*4882a593Smuzhiyun static int ltc2952_poweroff_probe(struct platform_device *pdev)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun 	int ret;
252*4882a593Smuzhiyun 	struct ltc2952_poweroff *data;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	if (pm_power_off) {
255*4882a593Smuzhiyun 		dev_err(&pdev->dev, "pm_power_off already registered");
256*4882a593Smuzhiyun 		return -EBUSY;
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
260*4882a593Smuzhiyun 	if (!data)
261*4882a593Smuzhiyun 		return -ENOMEM;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	data->dev = &pdev->dev;
264*4882a593Smuzhiyun 	platform_set_drvdata(pdev, data);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	ret = ltc2952_poweroff_init(pdev);
267*4882a593Smuzhiyun 	if (ret)
268*4882a593Smuzhiyun 		return ret;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	/* TODO: remove ltc2952_data */
271*4882a593Smuzhiyun 	ltc2952_data = data;
272*4882a593Smuzhiyun 	pm_power_off = ltc2952_poweroff_kill;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic;
275*4882a593Smuzhiyun 	atomic_notifier_chain_register(&panic_notifier_list,
276*4882a593Smuzhiyun 				       &data->panic_notifier);
277*4882a593Smuzhiyun 	dev_info(&pdev->dev, "probe successful\n");
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun 
ltc2952_poweroff_remove(struct platform_device * pdev)282*4882a593Smuzhiyun static int ltc2952_poweroff_remove(struct platform_device *pdev)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	pm_power_off = NULL;
287*4882a593Smuzhiyun 	hrtimer_cancel(&data->timer_trigger);
288*4882a593Smuzhiyun 	hrtimer_cancel(&data->timer_wde);
289*4882a593Smuzhiyun 	atomic_notifier_chain_unregister(&panic_notifier_list,
290*4882a593Smuzhiyun 					 &data->panic_notifier);
291*4882a593Smuzhiyun 	return 0;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun static const struct of_device_id of_ltc2952_poweroff_match[] = {
295*4882a593Smuzhiyun 	{ .compatible = "lltc,ltc2952"},
296*4882a593Smuzhiyun 	{},
297*4882a593Smuzhiyun };
298*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun static struct platform_driver ltc2952_poweroff_driver = {
301*4882a593Smuzhiyun 	.probe = ltc2952_poweroff_probe,
302*4882a593Smuzhiyun 	.remove = ltc2952_poweroff_remove,
303*4882a593Smuzhiyun 	.driver = {
304*4882a593Smuzhiyun 		.name = "ltc2952-poweroff",
305*4882a593Smuzhiyun 		.of_match_table = of_ltc2952_poweroff_match,
306*4882a593Smuzhiyun 	},
307*4882a593Smuzhiyun };
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun module_platform_driver(ltc2952_poweroff_driver);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun MODULE_AUTHOR("René Moll <rene.moll@xsens.com>");
312*4882a593Smuzhiyun MODULE_DESCRIPTION("LTC PowerPath power-off driver");
313*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
314