1 /* 2 * LTC2952 (PowerPath) driver 3 * 4 * Copyright (C) 2014, Xsens Technologies BV <info@xsens.com> 5 * Maintainer: Ren� Moll <linux@r-moll.nl> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * ---------------------------------------- 18 * - Description 19 * ---------------------------------------- 20 * 21 * This driver is to be used with an external PowerPath Controller (LTC2952). 22 * Its function is to determine when a external shut down is triggered 23 * and react by properly shutting down the system. 24 * 25 * This driver expects a device tree with a ltc2952 entry for pin mapping. 26 * 27 * ---------------------------------------- 28 * - GPIO 29 * ---------------------------------------- 30 * 31 * The following GPIOs are used: 32 * - trigger (input) 33 * A level change indicates the shut-down trigger. If it's state reverts 34 * within the time-out defined by trigger_delay, the shut down is not 35 * executed. 36 * 37 * - watchdog (output) 38 * Once a shut down is triggered, the driver will toggle this signal, 39 * with an internal (wde_interval) to stall the hardware shut down. 40 * 41 * - kill (output) 42 * The last action during shut down is triggering this signalling, such 43 * that the PowerPath Control will power down the hardware. 44 * 45 * ---------------------------------------- 46 * - Interrupts 47 * ---------------------------------------- 48 * 49 * The driver requires a non-shared, edge-triggered interrupt on the trigger 50 * GPIO. 51 * 52 */ 53 54 #include <linux/kernel.h> 55 #include <linux/init.h> 56 #include <linux/interrupt.h> 57 #include <linux/device.h> 58 #include <linux/platform_device.h> 59 #include <linux/ktime.h> 60 #include <linux/slab.h> 61 #include <linux/kmod.h> 62 #include <linux/module.h> 63 #include <linux/gpio/consumer.h> 64 #include <linux/reboot.h> 65 66 struct ltc2952_poweroff_data { 67 struct hrtimer timer_trigger; 68 struct hrtimer timer_wde; 69 70 ktime_t trigger_delay; 71 ktime_t wde_interval; 72 73 struct device *dev; 74 75 /** 76 * 0: trigger 77 * 1: watchdog 78 * 2: kill 79 */ 80 struct gpio_desc *gpio[3]; 81 }; 82 83 static int ltc2952_poweroff_panic; 84 static struct ltc2952_poweroff_data *ltc2952_data; 85 86 #define POWERPATH_IO_TRIGGER 0 87 #define POWERPATH_IO_WATCHDOG 1 88 #define POWERPATH_IO_KILL 2 89 90 /** 91 * ltc2952_poweroff_timer_wde - Timer callback 92 * Toggles the watchdog reset signal each wde_interval 93 * 94 * @timer: corresponding timer 95 * 96 * Returns HRTIMER_RESTART for an infinite loop which will only stop when the 97 * machine actually shuts down 98 */ 99 static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) 100 { 101 ktime_t now; 102 int state; 103 unsigned long overruns; 104 105 if (ltc2952_poweroff_panic) 106 return HRTIMER_NORESTART; 107 108 state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); 109 gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); 110 111 now = hrtimer_cb_get_time(timer); 112 overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); 113 114 return HRTIMER_RESTART; 115 } 116 117 static enum hrtimer_restart ltc2952_poweroff_timer_trigger( 118 struct hrtimer *timer) 119 { 120 int ret; 121 122 ret = hrtimer_start(<c2952_data->timer_wde, 123 ltc2952_data->wde_interval, HRTIMER_MODE_REL); 124 125 if (ret) { 126 dev_err(ltc2952_data->dev, "unable to start the timer\n"); 127 /* 128 * The device will not toggle the watchdog reset, 129 * thus shut down is only safe if the PowerPath controller 130 * has a long enough time-off before triggering a hardware 131 * power-off. 132 * 133 * Only sending a warning as the system will power-off anyway 134 */ 135 } 136 137 dev_info(ltc2952_data->dev, "executing shutdown\n"); 138 139 orderly_poweroff(true); 140 141 return HRTIMER_NORESTART; 142 } 143 144 /** 145 * ltc2952_poweroff_handler - Interrupt handler 146 * Triggered each time the trigger signal changes state and (de)activates a 147 * time-out (timer_trigger). Once the time-out is actually reached the shut 148 * down is executed. 149 * 150 * @irq: IRQ number 151 * @dev_id: pointer to the main data structure 152 */ 153 static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) 154 { 155 int ret; 156 struct ltc2952_poweroff_data *data = dev_id; 157 158 if (ltc2952_poweroff_panic) 159 goto irq_ok; 160 161 if (hrtimer_active(&data->timer_wde)) { 162 /* shutdown is already triggered, nothing to do any more */ 163 goto irq_ok; 164 } 165 166 if (!hrtimer_active(&data->timer_trigger)) { 167 ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, 168 HRTIMER_MODE_REL); 169 170 if (ret) 171 dev_err(data->dev, "unable to start the wait timer\n"); 172 } else { 173 ret = hrtimer_cancel(&data->timer_trigger); 174 /* omitting return value check, timer should have been valid */ 175 } 176 177 irq_ok: 178 return IRQ_HANDLED; 179 } 180 181 static void ltc2952_poweroff_kill(void) 182 { 183 gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); 184 } 185 186 static int ltc2952_poweroff_suspend(struct platform_device *pdev, 187 pm_message_t state) 188 { 189 return -ENOSYS; 190 } 191 192 static int ltc2952_poweroff_resume(struct platform_device *pdev) 193 { 194 return -ENOSYS; 195 } 196 197 static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) 198 { 199 unsigned int i; 200 201 for (i = 0; i < ARRAY_SIZE(data->gpio); i++) 202 data->gpio[i] = NULL; 203 204 data->wde_interval = ktime_set(0, 300L*1E6L); 205 data->trigger_delay = ktime_set(2, 500L*1E6L); 206 207 hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 208 data->timer_trigger.function = <c2952_poweroff_timer_trigger; 209 210 hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 211 data->timer_wde.function = <c2952_poweroff_timer_wde; 212 } 213 214 static int ltc2952_poweroff_init(struct platform_device *pdev) 215 { 216 int ret, virq; 217 unsigned int i; 218 struct ltc2952_poweroff_data *data; 219 220 static char *name[] = { 221 "trigger", 222 "watchdog", 223 "kill", 224 NULL 225 }; 226 227 data = ltc2952_data; 228 ltc2952_poweroff_default(ltc2952_data); 229 230 for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { 231 ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); 232 233 if (IS_ERR(ltc2952_data->gpio[i])) { 234 ret = PTR_ERR(ltc2952_data->gpio[i]); 235 dev_err(&pdev->dev, 236 "unable to claim the following gpio: %s\n", 237 name[i]); 238 goto err_io; 239 } 240 } 241 242 ret = gpiod_direction_output( 243 ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); 244 if (ret) { 245 dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); 246 goto err_io; 247 } 248 249 ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); 250 if (ret) { 251 dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); 252 goto err_io; 253 } 254 255 virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); 256 if (virq < 0) { 257 dev_err(&pdev->dev, "cannot map GPIO as interrupt"); 258 goto err_io; 259 } 260 261 ret = devm_request_irq(&pdev->dev, virq, 262 ltc2952_poweroff_handler, 263 (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), 264 "ltc2952-poweroff", 265 ltc2952_data); 266 267 if (ret) { 268 dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); 269 goto err_io; 270 } 271 272 return 0; 273 274 err_io: 275 for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) 276 if (ltc2952_data->gpio[i]) 277 gpiod_put(ltc2952_data->gpio[i]); 278 279 return ret; 280 } 281 282 static int ltc2952_poweroff_probe(struct platform_device *pdev) 283 { 284 int ret; 285 286 if (pm_power_off) { 287 dev_err(&pdev->dev, "pm_power_off already registered"); 288 return -EBUSY; 289 } 290 291 ltc2952_data = devm_kzalloc(&pdev->dev, sizeof(*ltc2952_data), 292 GFP_KERNEL); 293 if (!ltc2952_data) 294 return -ENOMEM; 295 296 ltc2952_data->dev = &pdev->dev; 297 298 ret = ltc2952_poweroff_init(pdev); 299 if (ret) 300 return ret; 301 302 pm_power_off = <c2952_poweroff_kill; 303 304 dev_info(&pdev->dev, "probe successful\n"); 305 306 return 0; 307 } 308 309 static int ltc2952_poweroff_remove(struct platform_device *pdev) 310 { 311 unsigned int i; 312 313 pm_power_off = NULL; 314 315 if (ltc2952_data) 316 for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) 317 gpiod_put(ltc2952_data->gpio[i]); 318 319 return 0; 320 } 321 322 static const struct of_device_id of_ltc2952_poweroff_match[] = { 323 { .compatible = "lltc,ltc2952"}, 324 {}, 325 }; 326 MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); 327 328 static struct platform_driver ltc2952_poweroff_driver = { 329 .probe = ltc2952_poweroff_probe, 330 .remove = ltc2952_poweroff_remove, 331 .driver = { 332 .name = "ltc2952-poweroff", 333 .of_match_table = of_ltc2952_poweroff_match, 334 }, 335 .suspend = ltc2952_poweroff_suspend, 336 .resume = ltc2952_poweroff_resume, 337 }; 338 339 static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, 340 unsigned long code, void *unused) 341 { 342 ltc2952_poweroff_panic = 1; 343 return NOTIFY_DONE; 344 } 345 346 static struct notifier_block ltc2952_poweroff_panic_nb = { 347 .notifier_call = ltc2952_poweroff_notify_panic, 348 }; 349 350 static int __init ltc2952_poweroff_platform_init(void) 351 { 352 ltc2952_poweroff_panic = 0; 353 354 atomic_notifier_chain_register(&panic_notifier_list, 355 <c2952_poweroff_panic_nb); 356 357 return platform_driver_register(<c2952_poweroff_driver); 358 } 359 360 static void __exit ltc2952_poweroff_platform_exit(void) 361 { 362 atomic_notifier_chain_unregister(&panic_notifier_list, 363 <c2952_poweroff_panic_nb); 364 365 platform_driver_unregister(<c2952_poweroff_driver); 366 } 367 368 module_init(ltc2952_poweroff_platform_init); 369 module_exit(ltc2952_poweroff_platform_exit); 370 371 MODULE_AUTHOR("Ren� Moll <rene.moll@xsens.com>"); 372 MODULE_DESCRIPTION("LTC PowerPath power-off driver"); 373 MODULE_LICENSE("GPL v2"); 374