vexpress-poweroff.c (3481B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * 4 * Copyright (C) 2012 ARM Limited 5 */ 6 7#include <linux/delay.h> 8#include <linux/notifier.h> 9#include <linux/of.h> 10#include <linux/of_device.h> 11#include <linux/platform_device.h> 12#include <linux/reboot.h> 13#include <linux/stat.h> 14#include <linux/vexpress.h> 15 16static void vexpress_reset_do(struct device *dev, const char *what) 17{ 18 int err = -ENOENT; 19 struct regmap *reg = dev_get_drvdata(dev); 20 21 if (reg) { 22 err = regmap_write(reg, 0, 0); 23 if (!err) 24 mdelay(1000); 25 } 26 27 dev_emerg(dev, "Unable to %s (%d)\n", what, err); 28} 29 30static struct device *vexpress_power_off_device; 31static atomic_t vexpress_restart_nb_refcnt = ATOMIC_INIT(0); 32 33static void vexpress_power_off(void) 34{ 35 vexpress_reset_do(vexpress_power_off_device, "power off"); 36} 37 38static struct device *vexpress_restart_device; 39 40static int vexpress_restart(struct notifier_block *this, unsigned long mode, 41 void *cmd) 42{ 43 vexpress_reset_do(vexpress_restart_device, "restart"); 44 45 return NOTIFY_DONE; 46} 47 48static struct notifier_block vexpress_restart_nb = { 49 .notifier_call = vexpress_restart, 50 .priority = 128, 51}; 52 53static ssize_t vexpress_reset_active_show(struct device *dev, 54 struct device_attribute *attr, char *buf) 55{ 56 return sprintf(buf, "%d\n", vexpress_restart_device == dev); 57} 58 59static ssize_t vexpress_reset_active_store(struct device *dev, 60 struct device_attribute *attr, const char *buf, size_t count) 61{ 62 long value; 63 int err = kstrtol(buf, 0, &value); 64 65 if (!err && value) 66 vexpress_restart_device = dev; 67 68 return err ? err : count; 69} 70 71static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 72 vexpress_reset_active_store); 73 74 75enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 76 77static const struct of_device_id vexpress_reset_of_match[] = { 78 { 79 .compatible = "arm,vexpress-reset", 80 .data = (void *)FUNC_RESET, 81 }, { 82 .compatible = "arm,vexpress-shutdown", 83 .data = (void *)FUNC_SHUTDOWN 84 }, { 85 .compatible = "arm,vexpress-reboot", 86 .data = (void *)FUNC_REBOOT 87 }, 88 {} 89}; 90 91static int _vexpress_register_restart_handler(struct device *dev) 92{ 93 int err; 94 95 vexpress_restart_device = dev; 96 if (atomic_inc_return(&vexpress_restart_nb_refcnt) == 1) { 97 err = register_restart_handler(&vexpress_restart_nb); 98 if (err) { 99 dev_err(dev, "cannot register restart handler (err=%d)\n", err); 100 atomic_dec(&vexpress_restart_nb_refcnt); 101 return err; 102 } 103 } 104 device_create_file(dev, &dev_attr_active); 105 106 return 0; 107} 108 109static int vexpress_reset_probe(struct platform_device *pdev) 110{ 111 const struct of_device_id *match = 112 of_match_device(vexpress_reset_of_match, &pdev->dev); 113 struct regmap *regmap; 114 int ret = 0; 115 116 if (!match) 117 return -EINVAL; 118 119 regmap = devm_regmap_init_vexpress_config(&pdev->dev); 120 if (IS_ERR(regmap)) 121 return PTR_ERR(regmap); 122 dev_set_drvdata(&pdev->dev, regmap); 123 124 switch ((enum vexpress_reset_func)match->data) { 125 case FUNC_SHUTDOWN: 126 vexpress_power_off_device = &pdev->dev; 127 pm_power_off = vexpress_power_off; 128 break; 129 case FUNC_RESET: 130 if (!vexpress_restart_device) 131 ret = _vexpress_register_restart_handler(&pdev->dev); 132 break; 133 case FUNC_REBOOT: 134 ret = _vexpress_register_restart_handler(&pdev->dev); 135 break; 136 } 137 138 return ret; 139} 140 141static struct platform_driver vexpress_reset_driver = { 142 .probe = vexpress_reset_probe, 143 .driver = { 144 .name = "vexpress-reset", 145 .of_match_table = vexpress_reset_of_match, 146 .suppress_bind_attrs = true, 147 }, 148}; 149builtin_platform_driver(vexpress_reset_driver);