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> 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; 26d08b8037SPawel Moll struct vexpress_config_func *func = dev_get_drvdata(dev); 272655f51dSCatalin Marinas 282655f51dSCatalin Marinas if (func) { 292655f51dSCatalin Marinas err = vexpress_config_write(func, 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 467b6d864bSRobin Holt static void vexpress_restart(enum reboot_mode reboot_mode, const char *cmd) 472655f51dSCatalin Marinas { 482655f51dSCatalin Marinas vexpress_reset_do(vexpress_restart_device, "restart"); 492655f51dSCatalin Marinas } 502655f51dSCatalin Marinas 512655f51dSCatalin Marinas static ssize_t vexpress_reset_active_show(struct device *dev, 522655f51dSCatalin Marinas struct device_attribute *attr, char *buf) 532655f51dSCatalin Marinas { 542655f51dSCatalin Marinas return sprintf(buf, "%d\n", vexpress_restart_device == dev); 552655f51dSCatalin Marinas } 562655f51dSCatalin Marinas 572655f51dSCatalin Marinas static ssize_t vexpress_reset_active_store(struct device *dev, 582655f51dSCatalin Marinas struct device_attribute *attr, const char *buf, size_t count) 592655f51dSCatalin Marinas { 602655f51dSCatalin Marinas long value; 612655f51dSCatalin Marinas int err = kstrtol(buf, 0, &value); 622655f51dSCatalin Marinas 632655f51dSCatalin Marinas if (!err && value) 642655f51dSCatalin Marinas vexpress_restart_device = dev; 652655f51dSCatalin Marinas 662655f51dSCatalin Marinas return err ? err : count; 672655f51dSCatalin Marinas } 682655f51dSCatalin Marinas 692655f51dSCatalin Marinas DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, 702655f51dSCatalin Marinas vexpress_reset_active_store); 712655f51dSCatalin Marinas 722655f51dSCatalin Marinas 732655f51dSCatalin Marinas enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; 742655f51dSCatalin Marinas 752655f51dSCatalin Marinas static struct of_device_id vexpress_reset_of_match[] = { 762655f51dSCatalin Marinas { 772655f51dSCatalin Marinas .compatible = "arm,vexpress-reset", 782655f51dSCatalin Marinas .data = (void *)FUNC_RESET, 792655f51dSCatalin Marinas }, { 802655f51dSCatalin Marinas .compatible = "arm,vexpress-shutdown", 812655f51dSCatalin Marinas .data = (void *)FUNC_SHUTDOWN 822655f51dSCatalin Marinas }, { 832655f51dSCatalin Marinas .compatible = "arm,vexpress-reboot", 842655f51dSCatalin Marinas .data = (void *)FUNC_REBOOT 852655f51dSCatalin Marinas }, 862655f51dSCatalin Marinas {} 872655f51dSCatalin Marinas }; 882655f51dSCatalin Marinas 892655f51dSCatalin Marinas static int vexpress_reset_probe(struct platform_device *pdev) 902655f51dSCatalin Marinas { 912655f51dSCatalin Marinas enum vexpress_reset_func func; 922655f51dSCatalin Marinas const struct of_device_id *match = 932655f51dSCatalin Marinas of_match_device(vexpress_reset_of_match, &pdev->dev); 94d08b8037SPawel Moll struct vexpress_config_func *config_func; 952655f51dSCatalin Marinas 962655f51dSCatalin Marinas if (match) 972655f51dSCatalin Marinas func = (enum vexpress_reset_func)match->data; 982655f51dSCatalin Marinas else 992655f51dSCatalin Marinas func = pdev->id_entry->driver_data; 1002655f51dSCatalin Marinas 101d08b8037SPawel Moll config_func = vexpress_config_func_get_by_dev(&pdev->dev); 102d08b8037SPawel Moll if (!config_func) 103d08b8037SPawel Moll return -EINVAL; 104d08b8037SPawel Moll dev_set_drvdata(&pdev->dev, config_func); 105d08b8037SPawel Moll 1062655f51dSCatalin Marinas switch (func) { 1072655f51dSCatalin Marinas case FUNC_SHUTDOWN: 1082655f51dSCatalin Marinas vexpress_power_off_device = &pdev->dev; 10965deb782SCatalin Marinas pm_power_off = vexpress_power_off; 1102655f51dSCatalin Marinas break; 1112655f51dSCatalin Marinas case FUNC_RESET: 1122655f51dSCatalin Marinas if (!vexpress_restart_device) 1132655f51dSCatalin Marinas vexpress_restart_device = &pdev->dev; 11465deb782SCatalin Marinas arm_pm_restart = vexpress_restart; 1152655f51dSCatalin Marinas device_create_file(&pdev->dev, &dev_attr_active); 1162655f51dSCatalin Marinas break; 1172655f51dSCatalin Marinas case FUNC_REBOOT: 1182655f51dSCatalin Marinas vexpress_restart_device = &pdev->dev; 11965deb782SCatalin Marinas arm_pm_restart = vexpress_restart; 1202655f51dSCatalin Marinas device_create_file(&pdev->dev, &dev_attr_active); 1212655f51dSCatalin Marinas break; 1222655f51dSCatalin Marinas }; 1232655f51dSCatalin Marinas 1242655f51dSCatalin Marinas return 0; 1252655f51dSCatalin Marinas } 1262655f51dSCatalin Marinas 1272655f51dSCatalin Marinas static const struct platform_device_id vexpress_reset_id_table[] = { 1282655f51dSCatalin Marinas { .name = "vexpress-reset", .driver_data = FUNC_RESET, }, 1292655f51dSCatalin Marinas { .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, }, 1302655f51dSCatalin Marinas { .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, }, 1312655f51dSCatalin Marinas {} 1322655f51dSCatalin Marinas }; 1332655f51dSCatalin Marinas 1342655f51dSCatalin Marinas static struct platform_driver vexpress_reset_driver = { 1352655f51dSCatalin Marinas .probe = vexpress_reset_probe, 1362655f51dSCatalin Marinas .driver = { 1372655f51dSCatalin Marinas .name = "vexpress-reset", 1382655f51dSCatalin Marinas .of_match_table = vexpress_reset_of_match, 1392655f51dSCatalin Marinas }, 1402655f51dSCatalin Marinas .id_table = vexpress_reset_id_table, 1412655f51dSCatalin Marinas }; 1422655f51dSCatalin Marinas 1432655f51dSCatalin Marinas static int __init vexpress_reset_init(void) 1442655f51dSCatalin Marinas { 1452655f51dSCatalin Marinas return platform_driver_register(&vexpress_reset_driver); 1462655f51dSCatalin Marinas } 1472655f51dSCatalin Marinas device_initcall(vexpress_reset_init); 148