11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22655f51dSCatalin Marinas /* 32655f51dSCatalin Marinas * 42655f51dSCatalin Marinas * Copyright (C) 2012 ARM Limited 52655f51dSCatalin Marinas */ 62655f51dSCatalin Marinas 7d08b8037SPawel Moll #include <linux/delay.h> 846c99ac6SGuenter Roeck #include <linux/notifier.h> 92655f51dSCatalin Marinas #include <linux/of.h> 102655f51dSCatalin Marinas #include <linux/of_device.h> 112655f51dSCatalin Marinas #include <linux/platform_device.h> 1246c99ac6SGuenter Roeck #include <linux/reboot.h> 132655f51dSCatalin Marinas #include <linux/stat.h> 142655f51dSCatalin Marinas #include <linux/vexpress.h> 152655f51dSCatalin Marinas 162655f51dSCatalin Marinas static void vexpress_reset_do(struct device *dev, const char *what) 172655f51dSCatalin Marinas { 182655f51dSCatalin Marinas int err = -ENOENT; 193b9334acSPawel Moll struct regmap *reg = dev_get_drvdata(dev); 202655f51dSCatalin Marinas 213b9334acSPawel Moll if (reg) { 223b9334acSPawel Moll err = regmap_write(reg, 0, 0); 23d08b8037SPawel Moll if (!err) 24d08b8037SPawel Moll mdelay(1000); 252655f51dSCatalin Marinas } 262655f51dSCatalin Marinas 272655f51dSCatalin Marinas dev_emerg(dev, "Unable to %s (%d)\n", what, err); 282655f51dSCatalin Marinas } 292655f51dSCatalin Marinas 302655f51dSCatalin Marinas static struct device *vexpress_power_off_device; 3109bebb1aSSudeep Holla static atomic_t vexpress_restart_nb_refcnt = ATOMIC_INIT(0); 322655f51dSCatalin Marinas 3365deb782SCatalin Marinas static void vexpress_power_off(void) 342655f51dSCatalin Marinas { 352655f51dSCatalin Marinas vexpress_reset_do(vexpress_power_off_device, "power off"); 362655f51dSCatalin Marinas } 372655f51dSCatalin Marinas 382655f51dSCatalin Marinas static struct device *vexpress_restart_device; 392655f51dSCatalin Marinas 4046c99ac6SGuenter Roeck static int vexpress_restart(struct notifier_block *this, unsigned long mode, 4146c99ac6SGuenter Roeck void *cmd) 422655f51dSCatalin Marinas { 432655f51dSCatalin Marinas vexpress_reset_do(vexpress_restart_device, "restart"); 4446c99ac6SGuenter Roeck 4546c99ac6SGuenter Roeck return NOTIFY_DONE; 462655f51dSCatalin Marinas } 472655f51dSCatalin Marinas 4846c99ac6SGuenter Roeck static struct notifier_block vexpress_restart_nb = { 4946c99ac6SGuenter Roeck .notifier_call = vexpress_restart, 5046c99ac6SGuenter Roeck .priority = 128, 5146c99ac6SGuenter Roeck }; 5246c99ac6SGuenter Roeck 532655f51dSCatalin Marinas static ssize_t vexpress_reset_active_show(struct device *dev, 542655f51dSCatalin Marinas struct device_attribute *attr, char *buf) 552655f51dSCatalin Marinas { 562655f51dSCatalin Marinas return sprintf(buf, "%d\n", vexpress_restart_device == dev); 572655f51dSCatalin Marinas } 582655f51dSCatalin Marinas 592655f51dSCatalin Marinas static ssize_t vexpress_reset_active_store(struct device *dev, 602655f51dSCatalin Marinas struct device_attribute *attr, const char *buf, size_t count) 612655f51dSCatalin Marinas { 622655f51dSCatalin Marinas long value; 632655f51dSCatalin Marinas int err = kstrtol(buf, 0, &value); 642655f51dSCatalin Marinas 652655f51dSCatalin Marinas if (!err && value) 662655f51dSCatalin Marinas vexpress_restart_device = dev; 672655f51dSCatalin Marinas 682655f51dSCatalin Marinas return err ? err : count; 692655f51dSCatalin Marinas } 702655f51dSCatalin Marinas 710a14d280SBen Dooks static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 722655f51dSCatalin Marinas vexpress_reset_active_store); 732655f51dSCatalin Marinas 742655f51dSCatalin Marinas 752655f51dSCatalin Marinas enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 762655f51dSCatalin Marinas 778fb08855SFabian Frederick static const struct of_device_id vexpress_reset_of_match[] = { 782655f51dSCatalin Marinas { 792655f51dSCatalin Marinas .compatible = "arm,vexpress-reset", 802655f51dSCatalin Marinas .data = (void *)FUNC_RESET, 812655f51dSCatalin Marinas }, { 822655f51dSCatalin Marinas .compatible = "arm,vexpress-shutdown", 832655f51dSCatalin Marinas .data = (void *)FUNC_SHUTDOWN 842655f51dSCatalin Marinas }, { 852655f51dSCatalin Marinas .compatible = "arm,vexpress-reboot", 862655f51dSCatalin Marinas .data = (void *)FUNC_REBOOT 872655f51dSCatalin Marinas }, 882655f51dSCatalin Marinas {} 892655f51dSCatalin Marinas }; 902655f51dSCatalin Marinas 9146c99ac6SGuenter Roeck static int _vexpress_register_restart_handler(struct device *dev) 9246c99ac6SGuenter Roeck { 9346c99ac6SGuenter Roeck int err; 9446c99ac6SGuenter Roeck 9546c99ac6SGuenter Roeck vexpress_restart_device = dev; 9609bebb1aSSudeep Holla if (atomic_inc_return(&vexpress_restart_nb_refcnt) == 1) { 9746c99ac6SGuenter Roeck err = register_restart_handler(&vexpress_restart_nb); 9846c99ac6SGuenter Roeck if (err) { 9946c99ac6SGuenter Roeck dev_err(dev, "cannot register restart handler (err=%d)\n", err); 10009bebb1aSSudeep Holla atomic_dec(&vexpress_restart_nb_refcnt); 10146c99ac6SGuenter Roeck return err; 10246c99ac6SGuenter Roeck } 10309bebb1aSSudeep Holla } 10446c99ac6SGuenter Roeck device_create_file(dev, &dev_attr_active); 10546c99ac6SGuenter Roeck 10646c99ac6SGuenter Roeck return 0; 10746c99ac6SGuenter Roeck } 10846c99ac6SGuenter Roeck 1092655f51dSCatalin Marinas static int vexpress_reset_probe(struct platform_device *pdev) 1102655f51dSCatalin Marinas { 1112655f51dSCatalin Marinas const struct of_device_id *match = 1122655f51dSCatalin Marinas of_match_device(vexpress_reset_of_match, &pdev->dev); 1133b9334acSPawel Moll struct regmap *regmap; 11446c99ac6SGuenter Roeck int ret = 0; 1152655f51dSCatalin Marinas 1168f961c0aSPawel Moll if (!match) 1178f961c0aSPawel Moll return -EINVAL; 1182655f51dSCatalin Marinas 1193b9334acSPawel Moll regmap = devm_regmap_init_vexpress_config(&pdev->dev); 1203b9334acSPawel Moll if (IS_ERR(regmap)) 1213b9334acSPawel Moll return PTR_ERR(regmap); 1223b9334acSPawel Moll dev_set_drvdata(&pdev->dev, regmap); 123d08b8037SPawel Moll 1248f961c0aSPawel Moll switch ((enum vexpress_reset_func)match->data) { 1252655f51dSCatalin Marinas case FUNC_SHUTDOWN: 1262655f51dSCatalin Marinas vexpress_power_off_device = &pdev->dev; 12765deb782SCatalin Marinas pm_power_off = vexpress_power_off; 1282655f51dSCatalin Marinas break; 1292655f51dSCatalin Marinas case FUNC_RESET: 1302655f51dSCatalin Marinas if (!vexpress_restart_device) 13146c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1322655f51dSCatalin Marinas break; 1332655f51dSCatalin Marinas case FUNC_REBOOT: 13446c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1352655f51dSCatalin Marinas break; 1362655f51dSCatalin Marinas }; 1372655f51dSCatalin Marinas 13846c99ac6SGuenter Roeck return ret; 1392655f51dSCatalin Marinas } 1402655f51dSCatalin Marinas 1412655f51dSCatalin Marinas static struct platform_driver vexpress_reset_driver = { 1422655f51dSCatalin Marinas .probe = vexpress_reset_probe, 1432655f51dSCatalin Marinas .driver = { 1442655f51dSCatalin Marinas .name = "vexpress-reset", 1452655f51dSCatalin Marinas .of_match_table = vexpress_reset_of_match, 14673174accSAnders Roxell .suppress_bind_attrs = true, 1472655f51dSCatalin Marinas }, 1482655f51dSCatalin Marinas }; 1492655f51dSCatalin Marinas 1502655f51dSCatalin Marinas static int __init vexpress_reset_init(void) 1512655f51dSCatalin Marinas { 1522655f51dSCatalin Marinas return platform_driver_register(&vexpress_reset_driver); 1532655f51dSCatalin Marinas } 1542655f51dSCatalin Marinas device_initcall(vexpress_reset_init); 155