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 142655f51dSCatalin Marinas #include <linux/jiffies.h> 152655f51dSCatalin Marinas #include <linux/of.h> 162655f51dSCatalin Marinas #include <linux/of_device.h> 172655f51dSCatalin Marinas #include <linux/platform_device.h> 182655f51dSCatalin Marinas #include <linux/stat.h> 192655f51dSCatalin Marinas #include <linux/vexpress.h> 202655f51dSCatalin Marinas 2165deb782SCatalin Marinas #include <asm/system_misc.h> 2265deb782SCatalin Marinas 232655f51dSCatalin Marinas static void vexpress_reset_do(struct device *dev, const char *what) 242655f51dSCatalin Marinas { 252655f51dSCatalin Marinas int err = -ENOENT; 262655f51dSCatalin Marinas struct vexpress_config_func *func = 272655f51dSCatalin Marinas vexpress_config_func_get_by_dev(dev); 282655f51dSCatalin Marinas 292655f51dSCatalin Marinas if (func) { 302655f51dSCatalin Marinas unsigned long timeout; 312655f51dSCatalin Marinas 322655f51dSCatalin Marinas err = vexpress_config_write(func, 0, 0); 332655f51dSCatalin Marinas 342655f51dSCatalin Marinas timeout = jiffies + HZ; 352655f51dSCatalin Marinas while (time_before(jiffies, timeout)) 362655f51dSCatalin Marinas cpu_relax(); 372655f51dSCatalin Marinas } 382655f51dSCatalin Marinas 392655f51dSCatalin Marinas dev_emerg(dev, "Unable to %s (%d)\n", what, err); 402655f51dSCatalin Marinas } 412655f51dSCatalin Marinas 422655f51dSCatalin Marinas static struct device *vexpress_power_off_device; 432655f51dSCatalin Marinas 4465deb782SCatalin Marinas static void vexpress_power_off(void) 452655f51dSCatalin Marinas { 462655f51dSCatalin Marinas vexpress_reset_do(vexpress_power_off_device, "power off"); 472655f51dSCatalin Marinas } 482655f51dSCatalin Marinas 492655f51dSCatalin Marinas static struct device *vexpress_restart_device; 502655f51dSCatalin Marinas 517b6d864bSRobin Holt static void vexpress_restart(enum reboot_mode reboot_mode, const char *cmd) 522655f51dSCatalin Marinas { 532655f51dSCatalin Marinas vexpress_reset_do(vexpress_restart_device, "restart"); 542655f51dSCatalin Marinas } 552655f51dSCatalin Marinas 562655f51dSCatalin Marinas static ssize_t vexpress_reset_active_show(struct device *dev, 572655f51dSCatalin Marinas struct device_attribute *attr, char *buf) 582655f51dSCatalin Marinas { 592655f51dSCatalin Marinas return sprintf(buf, "%d\n", vexpress_restart_device == dev); 602655f51dSCatalin Marinas } 612655f51dSCatalin Marinas 622655f51dSCatalin Marinas static ssize_t vexpress_reset_active_store(struct device *dev, 632655f51dSCatalin Marinas struct device_attribute *attr, const char *buf, size_t count) 642655f51dSCatalin Marinas { 652655f51dSCatalin Marinas long value; 662655f51dSCatalin Marinas int err = kstrtol(buf, 0, &value); 672655f51dSCatalin Marinas 682655f51dSCatalin Marinas if (!err && value) 692655f51dSCatalin Marinas vexpress_restart_device = dev; 702655f51dSCatalin Marinas 712655f51dSCatalin Marinas return err ? err : count; 722655f51dSCatalin Marinas } 732655f51dSCatalin Marinas 742655f51dSCatalin Marinas DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 752655f51dSCatalin Marinas vexpress_reset_active_store); 762655f51dSCatalin Marinas 772655f51dSCatalin Marinas 782655f51dSCatalin Marinas enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 792655f51dSCatalin Marinas 802655f51dSCatalin Marinas static struct of_device_id vexpress_reset_of_match[] = { 812655f51dSCatalin Marinas { 822655f51dSCatalin Marinas .compatible = "arm,vexpress-reset", 832655f51dSCatalin Marinas .data = (void *)FUNC_RESET, 842655f51dSCatalin Marinas }, { 852655f51dSCatalin Marinas .compatible = "arm,vexpress-shutdown", 862655f51dSCatalin Marinas .data = (void *)FUNC_SHUTDOWN 872655f51dSCatalin Marinas }, { 882655f51dSCatalin Marinas .compatible = "arm,vexpress-reboot", 892655f51dSCatalin Marinas .data = (void *)FUNC_REBOOT 902655f51dSCatalin Marinas }, 912655f51dSCatalin Marinas {} 922655f51dSCatalin Marinas }; 932655f51dSCatalin Marinas 942655f51dSCatalin Marinas static int vexpress_reset_probe(struct platform_device *pdev) 952655f51dSCatalin Marinas { 962655f51dSCatalin Marinas enum vexpress_reset_func func; 972655f51dSCatalin Marinas const struct of_device_id *match = 982655f51dSCatalin Marinas of_match_device(vexpress_reset_of_match, &pdev->dev); 992655f51dSCatalin Marinas 1002655f51dSCatalin Marinas if (match) 1012655f51dSCatalin Marinas func = (enum vexpress_reset_func)match->data; 1022655f51dSCatalin Marinas else 1032655f51dSCatalin Marinas func = pdev->id_entry->driver_data; 1042655f51dSCatalin Marinas 1052655f51dSCatalin Marinas switch (func) { 1062655f51dSCatalin Marinas case FUNC_SHUTDOWN: 1072655f51dSCatalin Marinas vexpress_power_off_device = &pdev->dev; 10865deb782SCatalin Marinas pm_power_off = vexpress_power_off; 1092655f51dSCatalin Marinas break; 1102655f51dSCatalin Marinas case FUNC_RESET: 1112655f51dSCatalin Marinas if (!vexpress_restart_device) 1122655f51dSCatalin Marinas vexpress_restart_device = &pdev->dev; 11365deb782SCatalin Marinas arm_pm_restart = vexpress_restart; 1142655f51dSCatalin Marinas device_create_file(&pdev->dev, &dev_attr_active); 1152655f51dSCatalin Marinas break; 1162655f51dSCatalin Marinas case FUNC_REBOOT: 1172655f51dSCatalin Marinas vexpress_restart_device = &pdev->dev; 11865deb782SCatalin Marinas arm_pm_restart = vexpress_restart; 1192655f51dSCatalin Marinas device_create_file(&pdev->dev, &dev_attr_active); 1202655f51dSCatalin Marinas break; 1212655f51dSCatalin Marinas }; 1222655f51dSCatalin Marinas 1232655f51dSCatalin Marinas return 0; 1242655f51dSCatalin Marinas } 1252655f51dSCatalin Marinas 1262655f51dSCatalin Marinas static const struct platform_device_id vexpress_reset_id_table[] = { 1272655f51dSCatalin Marinas { .name = "vexpress-reset", .driver_data = FUNC_RESET, }, 1282655f51dSCatalin Marinas { .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, }, 1292655f51dSCatalin Marinas { .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, }, 1302655f51dSCatalin Marinas {} 1312655f51dSCatalin Marinas }; 1322655f51dSCatalin Marinas 1332655f51dSCatalin Marinas static struct platform_driver vexpress_reset_driver = { 1342655f51dSCatalin Marinas .probe = vexpress_reset_probe, 1352655f51dSCatalin Marinas .driver = { 1362655f51dSCatalin Marinas .name = "vexpress-reset", 1372655f51dSCatalin Marinas .of_match_table = vexpress_reset_of_match, 1382655f51dSCatalin Marinas }, 1392655f51dSCatalin Marinas .id_table = vexpress_reset_id_table, 1402655f51dSCatalin Marinas }; 1412655f51dSCatalin Marinas 1422655f51dSCatalin Marinas static int __init vexpress_reset_init(void) 1432655f51dSCatalin Marinas { 1442655f51dSCatalin Marinas return platform_driver_register(&vexpress_reset_driver); 1452655f51dSCatalin Marinas } 1462655f51dSCatalin Marinas device_initcall(vexpress_reset_init); 147