1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Force-disables a regulator to power down a device 4 * 5 * Michael Klein <michael@fossekall.de> 6 * 7 * Copyright (C) 2020 Michael Klein 8 * 9 * Based on the gpio-poweroff driver. 10 */ 11 #include <linux/delay.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/pm.h> 16 #include <linux/reboot.h> 17 #include <linux/regulator/consumer.h> 18 19 #define TIMEOUT_MS 3000 20 21 static int regulator_poweroff_do_poweroff(struct sys_off_data *data) 22 { 23 struct regulator *cpu_regulator = data->cb_data; 24 25 if (cpu_regulator && regulator_is_enabled(cpu_regulator)) 26 regulator_force_disable(cpu_regulator); 27 28 /* give it some time */ 29 mdelay(TIMEOUT_MS); 30 31 WARN_ON(1); 32 33 return NOTIFY_DONE; 34 } 35 36 static int regulator_poweroff_probe(struct platform_device *pdev) 37 { 38 struct regulator *cpu_regulator; 39 40 cpu_regulator = devm_regulator_get(&pdev->dev, "cpu"); 41 if (IS_ERR(cpu_regulator)) 42 return PTR_ERR(cpu_regulator); 43 44 /* Set this handler to low priority to not override an existing handler */ 45 return devm_register_sys_off_handler(&pdev->dev, 46 SYS_OFF_MODE_POWER_OFF, 47 SYS_OFF_PRIO_LOW, 48 regulator_poweroff_do_poweroff, 49 cpu_regulator); 50 } 51 52 static const struct of_device_id of_regulator_poweroff_match[] = { 53 { .compatible = "regulator-poweroff", }, 54 {}, 55 }; 56 MODULE_DEVICE_TABLE(of, of_regulator_poweroff_match); 57 58 static struct platform_driver regulator_poweroff_driver = { 59 .probe = regulator_poweroff_probe, 60 .driver = { 61 .name = "poweroff-regulator", 62 .of_match_table = of_regulator_poweroff_match, 63 }, 64 }; 65 66 module_platform_driver(regulator_poweroff_driver); 67 68 MODULE_AUTHOR("Michael Klein <michael@fossekall.de>"); 69 MODULE_DESCRIPTION("Regulator poweroff driver"); 70 MODULE_ALIAS("platform:poweroff-regulator"); 71