diff options
Diffstat (limited to 'drivers/power/reset/vexpress-poweroff.c')
| -rw-r--r-- | drivers/power/reset/vexpress-poweroff.c | 146 | 
1 files changed, 146 insertions, 0 deletions
diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c new file mode 100644 index 00000000000..469e6962b2c --- /dev/null +++ b/drivers/power/reset/vexpress-poweroff.c @@ -0,0 +1,146 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * Copyright (C) 2012 ARM Limited + */ + +#include <linux/jiffies.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/stat.h> +#include <linux/vexpress.h> + +#include <asm/system_misc.h> + +static void vexpress_reset_do(struct device *dev, const char *what) +{ +	int err = -ENOENT; +	struct vexpress_config_func *func = +			vexpress_config_func_get_by_dev(dev); + +	if (func) { +		unsigned long timeout; + +		err = vexpress_config_write(func, 0, 0); + +		timeout = jiffies + HZ; +		while (time_before(jiffies, timeout)) +			cpu_relax(); +	} + +	dev_emerg(dev, "Unable to %s (%d)\n", what, err); +} + +static struct device *vexpress_power_off_device; + +static void vexpress_power_off(void) +{ +	vexpress_reset_do(vexpress_power_off_device, "power off"); +} + +static struct device *vexpress_restart_device; + +static void vexpress_restart(char str, const char *cmd) +{ +	vexpress_reset_do(vexpress_restart_device, "restart"); +} + +static ssize_t vexpress_reset_active_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "%d\n", vexpress_restart_device == dev); +} + +static ssize_t vexpress_reset_active_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	long value; +	int err = kstrtol(buf, 0, &value); + +	if (!err && value) +		vexpress_restart_device = dev; + +	return err ? err : count; +} + +DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, +		vexpress_reset_active_store); + + +enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; + +static struct of_device_id vexpress_reset_of_match[] = { +	{ +		.compatible = "arm,vexpress-reset", +		.data = (void *)FUNC_RESET, +	}, { +		.compatible = "arm,vexpress-shutdown", +		.data = (void *)FUNC_SHUTDOWN +	}, { +		.compatible = "arm,vexpress-reboot", +		.data = (void *)FUNC_REBOOT +	}, +	{} +}; + +static int vexpress_reset_probe(struct platform_device *pdev) +{ +	enum vexpress_reset_func func; +	const struct of_device_id *match = +			of_match_device(vexpress_reset_of_match, &pdev->dev); + +	if (match) +		func = (enum vexpress_reset_func)match->data; +	else +		func = pdev->id_entry->driver_data; + +	switch (func) { +	case FUNC_SHUTDOWN: +		vexpress_power_off_device = &pdev->dev; +		pm_power_off = vexpress_power_off; +		break; +	case FUNC_RESET: +		if (!vexpress_restart_device) +			vexpress_restart_device = &pdev->dev; +		arm_pm_restart = vexpress_restart; +		device_create_file(&pdev->dev, &dev_attr_active); +		break; +	case FUNC_REBOOT: +		vexpress_restart_device = &pdev->dev; +		arm_pm_restart = vexpress_restart; +		device_create_file(&pdev->dev, &dev_attr_active); +		break; +	}; + +	return 0; +} + +static const struct platform_device_id vexpress_reset_id_table[] = { +	{ .name = "vexpress-reset", .driver_data = FUNC_RESET, }, +	{ .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, }, +	{ .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, }, +	{} +}; + +static struct platform_driver vexpress_reset_driver = { +	.probe = vexpress_reset_probe, +	.driver = { +		.name = "vexpress-reset", +		.of_match_table = vexpress_reset_of_match, +	}, +	.id_table = vexpress_reset_id_table, +}; + +static int __init vexpress_reset_init(void) +{ +	return platform_driver_register(&vexpress_reset_driver); +} +device_initcall(vexpress_reset_init);  |