Skip to content

Commit

Permalink
power: reset: Add generic SYSCON register mapped reset
Browse files Browse the repository at this point in the history
Add a generic SYSCON register mapped reset mechanism.

Signed-off-by: Feng Kan <fkan@apm.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
  • Loading branch information
Feng Kan authored and Sebastian Reichel committed Oct 1, 2014
1 parent a3c0c3e commit 09fb07b
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
5 changes: 5 additions & 0 deletions drivers/power/reset/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions drivers/power/reset/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
96 changes: 96 additions & 0 deletions drivers/power/reset/syscon-reboot.c
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 09fb07b

Please sign in to comment.