12655f51dSCatalin Marinas /* 22655f51dSCatalin Marinas * This program is free software; you can redistribute it and/or modify 32655f51dSCatalin Marinas * it under the terms of the GNU General Public License version 2 as 42655f51dSCatalin Marinas * published by the Free Software Foundation. 52655f51dSCatalin Marinas * 62655f51dSCatalin Marinas * This program is distributed in the hope that it will be useful, 72655f51dSCatalin Marinas * but WITHOUT ANY WARRANTY; without even the implied warranty of 82655f51dSCatalin Marinas * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 92655f51dSCatalin Marinas * GNU General Public License for more details. 102655f51dSCatalin Marinas * 112655f51dSCatalin Marinas * Copyright (C) 2012 ARM Limited 122655f51dSCatalin Marinas */ 132655f51dSCatalin Marinas 14d08b8037SPawel Moll #include <linux/delay.h> 1546c99ac6SGuenter Roeck #include <linux/notifier.h> 162655f51dSCatalin Marinas #include <linux/of.h> 172655f51dSCatalin Marinas #include <linux/of_device.h> 182655f51dSCatalin Marinas #include <linux/platform_device.h> 1946c99ac6SGuenter Roeck #include <linux/reboot.h> 202655f51dSCatalin Marinas #include <linux/stat.h> 212655f51dSCatalin Marinas #include <linux/vexpress.h> 222655f51dSCatalin Marinas 232655f51dSCatalin Marinas static void vexpress_reset_do(struct device *dev, const char *what) 242655f51dSCatalin Marinas { 252655f51dSCatalin Marinas int err = -ENOENT; 263b9334acSPawel Moll struct regmap *reg = dev_get_drvdata(dev); 272655f51dSCatalin Marinas 283b9334acSPawel Moll if (reg) { 293b9334acSPawel Moll err = regmap_write(reg, 0, 0); 30d08b8037SPawel Moll if (!err) 31d08b8037SPawel Moll mdelay(1000); 322655f51dSCatalin Marinas } 332655f51dSCatalin Marinas 342655f51dSCatalin Marinas dev_emerg(dev, "Unable to %s (%d)\n", what, err); 352655f51dSCatalin Marinas } 362655f51dSCatalin Marinas 372655f51dSCatalin Marinas static struct device *vexpress_power_off_device; 3809bebb1aSSudeep Holla static atomic_t vexpress_restart_nb_refcnt = ATOMIC_INIT(0); 392655f51dSCatalin Marinas 4065deb782SCatalin Marinas static void vexpress_power_off(void) 412655f51dSCatalin Marinas { 422655f51dSCatalin Marinas vexpress_reset_do(vexpress_power_off_device, "power off"); 432655f51dSCatalin Marinas } 442655f51dSCatalin Marinas 452655f51dSCatalin Marinas static struct device *vexpress_restart_device; 462655f51dSCatalin Marinas 4746c99ac6SGuenter Roeck static int vexpress_restart(struct notifier_block *this, unsigned long mode, 4846c99ac6SGuenter Roeck void *cmd) 492655f51dSCatalin Marinas { 502655f51dSCatalin Marinas vexpress_reset_do(vexpress_restart_device, "restart"); 5146c99ac6SGuenter Roeck 5246c99ac6SGuenter Roeck return NOTIFY_DONE; 532655f51dSCatalin Marinas } 542655f51dSCatalin Marinas 5546c99ac6SGuenter Roeck static struct notifier_block vexpress_restart_nb = { 5646c99ac6SGuenter Roeck .notifier_call = vexpress_restart, 5746c99ac6SGuenter Roeck .priority = 128, 5846c99ac6SGuenter Roeck }; 5946c99ac6SGuenter Roeck 602655f51dSCatalin Marinas static ssize_t vexpress_reset_active_show(struct device *dev, 612655f51dSCatalin Marinas struct device_attribute *attr, char *buf) 622655f51dSCatalin Marinas { 632655f51dSCatalin Marinas return sprintf(buf, "%d\n", vexpress_restart_device == dev); 642655f51dSCatalin Marinas } 652655f51dSCatalin Marinas 662655f51dSCatalin Marinas static ssize_t vexpress_reset_active_store(struct device *dev, 672655f51dSCatalin Marinas struct device_attribute *attr, const char *buf, size_t count) 682655f51dSCatalin Marinas { 692655f51dSCatalin Marinas long value; 702655f51dSCatalin Marinas int err = kstrtol(buf, 0, &value); 712655f51dSCatalin Marinas 722655f51dSCatalin Marinas if (!err && value) 732655f51dSCatalin Marinas vexpress_restart_device = dev; 742655f51dSCatalin Marinas 752655f51dSCatalin Marinas return err ? err : count; 762655f51dSCatalin Marinas } 772655f51dSCatalin Marinas 780a14d280SBen Dooks static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 792655f51dSCatalin Marinas vexpress_reset_active_store); 802655f51dSCatalin Marinas 812655f51dSCatalin Marinas 822655f51dSCatalin Marinas enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 832655f51dSCatalin Marinas 848fb08855SFabian Frederick static const struct of_device_id vexpress_reset_of_match[] = { 852655f51dSCatalin Marinas { 862655f51dSCatalin Marinas .compatible = "arm,vexpress-reset", 872655f51dSCatalin Marinas .data = (void *)FUNC_RESET, 882655f51dSCatalin Marinas }, { 892655f51dSCatalin Marinas .compatible = "arm,vexpress-shutdown", 902655f51dSCatalin Marinas .data = (void *)FUNC_SHUTDOWN 912655f51dSCatalin Marinas }, { 922655f51dSCatalin Marinas .compatible = "arm,vexpress-reboot", 932655f51dSCatalin Marinas .data = (void *)FUNC_REBOOT 942655f51dSCatalin Marinas }, 952655f51dSCatalin Marinas {} 962655f51dSCatalin Marinas }; 972655f51dSCatalin Marinas 9846c99ac6SGuenter Roeck static int _vexpress_register_restart_handler(struct device *dev) 9946c99ac6SGuenter Roeck { 10046c99ac6SGuenter Roeck int err; 10146c99ac6SGuenter Roeck 10246c99ac6SGuenter Roeck vexpress_restart_device = dev; 10309bebb1aSSudeep Holla if (atomic_inc_return(&vexpress_restart_nb_refcnt) == 1) { 10446c99ac6SGuenter Roeck err = register_restart_handler(&vexpress_restart_nb); 10546c99ac6SGuenter Roeck if (err) { 10646c99ac6SGuenter Roeck dev_err(dev, "cannot register restart handler (err=%d)\n", err); 10709bebb1aSSudeep Holla atomic_dec(&vexpress_restart_nb_refcnt); 10846c99ac6SGuenter Roeck return err; 10946c99ac6SGuenter Roeck } 11009bebb1aSSudeep Holla } 11146c99ac6SGuenter Roeck device_create_file(dev, &dev_attr_active); 11246c99ac6SGuenter Roeck 11346c99ac6SGuenter Roeck return 0; 11446c99ac6SGuenter Roeck } 11546c99ac6SGuenter Roeck 1162655f51dSCatalin Marinas static int vexpress_reset_probe(struct platform_device *pdev) 1172655f51dSCatalin Marinas { 1182655f51dSCatalin Marinas const struct of_device_id *match = 1192655f51dSCatalin Marinas of_match_device(vexpress_reset_of_match, &pdev->dev); 1203b9334acSPawel Moll struct regmap *regmap; 12146c99ac6SGuenter Roeck int ret = 0; 1222655f51dSCatalin Marinas 1238f961c0aSPawel Moll if (!match) 1248f961c0aSPawel Moll return -EINVAL; 1252655f51dSCatalin Marinas 1263b9334acSPawel Moll regmap = devm_regmap_init_vexpress_config(&pdev->dev); 1273b9334acSPawel Moll if (IS_ERR(regmap)) 1283b9334acSPawel Moll return PTR_ERR(regmap); 1293b9334acSPawel Moll dev_set_drvdata(&pdev->dev, regmap); 130d08b8037SPawel Moll 1318f961c0aSPawel Moll switch ((enum vexpress_reset_func)match->data) { 1322655f51dSCatalin Marinas case FUNC_SHUTDOWN: 1332655f51dSCatalin Marinas vexpress_power_off_device = &pdev->dev; 13465deb782SCatalin Marinas pm_power_off = vexpress_power_off; 1352655f51dSCatalin Marinas break; 1362655f51dSCatalin Marinas case FUNC_RESET: 1372655f51dSCatalin Marinas if (!vexpress_restart_device) 13846c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1392655f51dSCatalin Marinas break; 1402655f51dSCatalin Marinas case FUNC_REBOOT: 14146c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1422655f51dSCatalin Marinas break; 1432655f51dSCatalin Marinas }; 1442655f51dSCatalin Marinas 14546c99ac6SGuenter Roeck return ret; 1462655f51dSCatalin Marinas } 1472655f51dSCatalin Marinas 1482655f51dSCatalin Marinas static struct platform_driver vexpress_reset_driver = { 1492655f51dSCatalin Marinas .probe = vexpress_reset_probe, 1502655f51dSCatalin Marinas .driver = { 1512655f51dSCatalin Marinas .name = "vexpress-reset", 1522655f51dSCatalin Marinas .of_match_table = vexpress_reset_of_match, 1532655f51dSCatalin Marinas }, 1542655f51dSCatalin Marinas }; 1552655f51dSCatalin Marinas 1562655f51dSCatalin Marinas static int __init vexpress_reset_init(void) 1572655f51dSCatalin Marinas { 1582655f51dSCatalin Marinas return platform_driver_register(&vexpress_reset_driver); 1592655f51dSCatalin Marinas } 1602655f51dSCatalin Marinas device_initcall(vexpress_reset_init); 161