power: reset: Add generic SYSCON register mapped reset

Add a generic SYSCON register mapped reset mechanism.

Signed-off-by: Feng Kan <fkan@apm.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
This commit is contained in:
Feng Kan 2014-09-30 16:25:03 -07:00 committed by Sebastian Reichel
parent a3c0c3e790
commit 09fb07bcaf
3 changed files with 102 additions and 0 deletions

View File

@ -118,3 +118,8 @@ config POWER_RESET_KEYSTONE
help
Reboot support for the KEYSTONE SoCs.
config POWER_RESET_SYSCON
bool "Generic SYSCON regmap reset driver"
depends on POWER_RESET && MFD_SYSCON && OF
help
Reboot support for generic SYSCON mapped register reset.

View File

@ -13,3 +13,4 @@ obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o

View File

@ -0,0 +1,96 @@
/*
* Generic Syscon Reboot Driver
*
* Copyright (c) 2013, Applied Micro Circuits Corporation
* Author: Feng Kan <fkan@apm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* 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.
*/
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/notifier.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/reboot.h>
struct syscon_reboot_context {
struct regmap *map;
u32 offset;
u32 mask;
struct notifier_block restart_handler;
};
static struct syscon_reboot_context *syscon_reboot_ctx;
static int syscon_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct syscon_reboot_context *ctx = syscon_reboot_ctx;
unsigned long timeout;
/* Issue the reboot */
if (ctx->map)
regmap_write(ctx->map, ctx->offset, ctx->mask);
timeout = jiffies + HZ;
while (time_before(jiffies, timeout))
cpu_relax();
pr_emerg("Unable to restart system\n");
return NOTIFY_DONE;
}
static int syscon_reboot_probe(struct platform_device *pdev)
{
struct syscon_reboot_context *ctx;
struct device *dev = &pdev->dev;
int err;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
if (IS_ERR(ctx->map))
return PTR_ERR(ctx->map);
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
return -EINVAL;
if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask))
return -EINVAL;
ctx->restart_handler.notifier_call = syscon_restart_handle;
ctx->restart_handler.priority = 128;
err = register_restart_handler(&ctx->restart_handler);
if (err)
dev_err(dev, "can't register restart notifier (err=%d)\n", err);
syscon_reboot_ctx = ctx;
return 0;
}
static struct of_device_id syscon_reboot_of_match[] = {
{ .compatible = "syscon-reboot" },
{}
};
static struct platform_driver syscon_reboot_driver = {
.probe = syscon_reboot_probe,
.driver = {
.name = "syscon-reboot",
.of_match_table = syscon_reboot_of_match,
},
};
module_platform_driver(syscon_reboot_driver);