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; 382655f51dSCatalin Marinas 3965deb782SCatalin Marinas static void vexpress_power_off(void) 402655f51dSCatalin Marinas { 412655f51dSCatalin Marinas vexpress_reset_do(vexpress_power_off_device, "power off"); 422655f51dSCatalin Marinas } 432655f51dSCatalin Marinas 442655f51dSCatalin Marinas static struct device *vexpress_restart_device; 452655f51dSCatalin Marinas 4646c99ac6SGuenter Roeck static int vexpress_restart(struct notifier_block *this, unsigned long mode, 4746c99ac6SGuenter Roeck void *cmd) 482655f51dSCatalin Marinas { 492655f51dSCatalin Marinas vexpress_reset_do(vexpress_restart_device, "restart"); 5046c99ac6SGuenter Roeck 5146c99ac6SGuenter Roeck return NOTIFY_DONE; 522655f51dSCatalin Marinas } 532655f51dSCatalin Marinas 5446c99ac6SGuenter Roeck static struct notifier_block vexpress_restart_nb = { 5546c99ac6SGuenter Roeck .notifier_call = vexpress_restart, 5646c99ac6SGuenter Roeck .priority = 128, 5746c99ac6SGuenter Roeck }; 5846c99ac6SGuenter Roeck 592655f51dSCatalin Marinas static ssize_t vexpress_reset_active_show(struct device *dev, 602655f51dSCatalin Marinas struct device_attribute *attr, char *buf) 612655f51dSCatalin Marinas { 622655f51dSCatalin Marinas return sprintf(buf, "%d\n", vexpress_restart_device == dev); 632655f51dSCatalin Marinas } 642655f51dSCatalin Marinas 652655f51dSCatalin Marinas static ssize_t vexpress_reset_active_store(struct device *dev, 662655f51dSCatalin Marinas struct device_attribute *attr, const char *buf, size_t count) 672655f51dSCatalin Marinas { 682655f51dSCatalin Marinas long value; 692655f51dSCatalin Marinas int err = kstrtol(buf, 0, &value); 702655f51dSCatalin Marinas 712655f51dSCatalin Marinas if (!err && value) 722655f51dSCatalin Marinas vexpress_restart_device = dev; 732655f51dSCatalin Marinas 742655f51dSCatalin Marinas return err ? err : count; 752655f51dSCatalin Marinas } 762655f51dSCatalin Marinas 772655f51dSCatalin Marinas DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 782655f51dSCatalin Marinas vexpress_reset_active_store); 792655f51dSCatalin Marinas 802655f51dSCatalin Marinas 812655f51dSCatalin Marinas enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 822655f51dSCatalin Marinas 832655f51dSCatalin Marinas static struct of_device_id vexpress_reset_of_match[] = { 842655f51dSCatalin Marinas { 852655f51dSCatalin Marinas .compatible = "arm,vexpress-reset", 862655f51dSCatalin Marinas .data = (void *)FUNC_RESET, 872655f51dSCatalin Marinas }, { 882655f51dSCatalin Marinas .compatible = "arm,vexpress-shutdown", 892655f51dSCatalin Marinas .data = (void *)FUNC_SHUTDOWN 902655f51dSCatalin Marinas }, { 912655f51dSCatalin Marinas .compatible = "arm,vexpress-reboot", 922655f51dSCatalin Marinas .data = (void *)FUNC_REBOOT 932655f51dSCatalin Marinas }, 942655f51dSCatalin Marinas {} 952655f51dSCatalin Marinas }; 962655f51dSCatalin Marinas 9746c99ac6SGuenter Roeck static int _vexpress_register_restart_handler(struct device *dev) 9846c99ac6SGuenter Roeck { 9946c99ac6SGuenter Roeck int err; 10046c99ac6SGuenter Roeck 10146c99ac6SGuenter Roeck vexpress_restart_device = dev; 10246c99ac6SGuenter Roeck err = register_restart_handler(&vexpress_restart_nb); 10346c99ac6SGuenter Roeck if (err) { 10446c99ac6SGuenter Roeck dev_err(dev, "cannot register restart handler (err=%d)\n", err); 10546c99ac6SGuenter Roeck return err; 10646c99ac6SGuenter Roeck } 10746c99ac6SGuenter Roeck device_create_file(dev, &dev_attr_active); 10846c99ac6SGuenter Roeck 10946c99ac6SGuenter Roeck return 0; 11046c99ac6SGuenter Roeck } 11146c99ac6SGuenter Roeck 1122655f51dSCatalin Marinas static int vexpress_reset_probe(struct platform_device *pdev) 1132655f51dSCatalin Marinas { 1142655f51dSCatalin Marinas const struct of_device_id *match = 1152655f51dSCatalin Marinas of_match_device(vexpress_reset_of_match, &pdev->dev); 1163b9334acSPawel Moll struct regmap *regmap; 11746c99ac6SGuenter Roeck int ret = 0; 1182655f51dSCatalin Marinas 1198f961c0aSPawel Moll if (!match) 1208f961c0aSPawel Moll return -EINVAL; 1212655f51dSCatalin Marinas 1223b9334acSPawel Moll regmap = devm_regmap_init_vexpress_config(&pdev->dev); 1233b9334acSPawel Moll if (IS_ERR(regmap)) 1243b9334acSPawel Moll return PTR_ERR(regmap); 1253b9334acSPawel Moll dev_set_drvdata(&pdev->dev, regmap); 126d08b8037SPawel Moll 1278f961c0aSPawel Moll switch ((enum vexpress_reset_func)match->data) { 1282655f51dSCatalin Marinas case FUNC_SHUTDOWN: 1292655f51dSCatalin Marinas vexpress_power_off_device = &pdev->dev; 13065deb782SCatalin Marinas pm_power_off = vexpress_power_off; 1312655f51dSCatalin Marinas break; 1322655f51dSCatalin Marinas case FUNC_RESET: 1332655f51dSCatalin Marinas if (!vexpress_restart_device) 13446c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1352655f51dSCatalin Marinas break; 1362655f51dSCatalin Marinas case FUNC_REBOOT: 13746c99ac6SGuenter Roeck ret = _vexpress_register_restart_handler(&pdev->dev); 1382655f51dSCatalin Marinas break; 1392655f51dSCatalin Marinas }; 1402655f51dSCatalin Marinas 14146c99ac6SGuenter Roeck return ret; 1422655f51dSCatalin Marinas } 1432655f51dSCatalin Marinas 1442655f51dSCatalin Marinas static struct platform_driver vexpress_reset_driver = { 1452655f51dSCatalin Marinas .probe = vexpress_reset_probe, 1462655f51dSCatalin Marinas .driver = { 1472655f51dSCatalin Marinas .name = "vexpress-reset", 1482655f51dSCatalin Marinas .of_match_table = vexpress_reset_of_match, 1492655f51dSCatalin Marinas }, 1502655f51dSCatalin Marinas }; 1512655f51dSCatalin Marinas 1522655f51dSCatalin Marinas static int __init vexpress_reset_init(void) 1532655f51dSCatalin Marinas { 1542655f51dSCatalin Marinas return platform_driver_register(&vexpress_reset_driver); 1552655f51dSCatalin Marinas } 1562655f51dSCatalin Marinas device_initcall(vexpress_reset_init); 157