diff options
author | Leo Wang <leozwang@google.com> | 2016-01-13 16:20:35 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-01-13 16:20:35 +0000 |
commit | e1513cd0d5d72f711b370c1f3867752147ce1c3b (patch) | |
tree | 2212e90a29c249aeb0986bf2ac06c4ea0dab379f | |
parent | c0711310c229de00d7eb155377419ca9a33fd11f (diff) | |
parent | a9cbdf4964e0e7dab73da3e9b1ce0a1c408ddafc (diff) | |
download | v4.1-e1513cd0d5d72f711b370c1f3867752147ce1c3b.tar.gz |
Merge "FROMLIST: power: reset: add reboot mode driver"
-rw-r--r-- | drivers/power/reset/Kconfig | 16 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/reset/reboot-mode.c | 100 | ||||
-rw-r--r-- | drivers/power/reset/reboot-mode.h | 6 | ||||
-rw-r--r-- | drivers/power/reset/syscon-reboot-mode.c | 62 |
5 files changed, 186 insertions, 0 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 17d93a73c51..76a6251b85a 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -166,5 +166,21 @@ config POWER_RESET_RMOBILE help Reboot support for Renesas R-Mobile and SH-Mobile SoCs. +config REBOOT_MODE + tristate + depends on OF + help + This driver will help to pass the system reboot mode + to bootloader + +config SYSCON_REBOOT_MODE + bool "Generic SYSCON regmap reboot mode driver" + select REBOOT_MODE + help + Say y here will enable reboot mode driver. This will + get reboot mode arguments and store it in SYSCON mapped + register, then the bootloader can read it to take different + action according to the mode. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dbe06c36874..e9765502f31 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -19,3 +19,5 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o +obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o +obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c new file mode 100644 index 00000000000..92b7b4dcef7 --- /dev/null +++ b/drivers/power/reset/reboot-mode.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * 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. + */ +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/reboot.h> +#include "reboot-mode.h" + +struct mode_info { + const char *mode; + int magic; + struct list_head list; +}; + +struct reboot_mode_driver { + struct list_head head; + int (*write)(int magic); + struct notifier_block reboot_notifier; +}; + +static int get_reboot_mode_magic(struct reboot_mode_driver *reboot, + const char *cmd) +{ + const char *normal = "normal"; + int magic = 0; + struct mode_info *info; + + if (!cmd) + cmd = normal; + + list_for_each_entry(info, &reboot->head, list) { + if (!strcmp(info->mode, cmd)) { + magic = info->magic; + break; + } + } + + return magic; +} + +static int reboot_mode_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct reboot_mode_driver *reboot; + int magic; + + reboot = container_of(this, struct reboot_mode_driver, reboot_notifier); + magic = get_reboot_mode_magic(reboot, cmd); + if (magic) + reboot->write(magic); + + return NOTIFY_DONE; +} + +int reboot_mode_register(struct device *dev, int (*write)(int)) +{ + struct reboot_mode_driver *reboot; + struct mode_info *info; + struct device_node *child; + int ret; + + reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + reboot->write = write; + INIT_LIST_HEAD(&reboot->head); + for_each_child_of_node(dev->of_node, child) { + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->mode = of_get_property(child, "linux,mode", NULL); + if (of_property_read_u32(child, "loader,magic", &info->magic)) { + dev_err(dev, "reboot mode %s without magic number\n", + info->mode); + devm_kfree(dev, info); + continue; + } + list_add_tail(&info->list, &reboot->head); + } + reboot->reboot_notifier.notifier_call = reboot_mode_notify; + ret = register_reboot_notifier(&reboot->reboot_notifier); + if (ret) + dev_err(dev, "can't register reboot notifier\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(reboot_mode_register); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("System reboot mode driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h new file mode 100644 index 00000000000..76646e7111e --- /dev/null +++ b/drivers/power/reset/reboot-mode.h @@ -0,0 +1,6 @@ +#ifndef __REBOOT_MODE_H__ +#define __REBOOT_MODE_H__ + +extern int reboot_mode_register(struct device *dev, int (*write)(int)); + +#endif diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c new file mode 100644 index 00000000000..b9370f3eb74 --- /dev/null +++ b/drivers/power/reset/syscon-reboot-mode.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include "reboot-mode.h" + +static struct regmap *map; +static u32 offset; + +static int syscon_reboot_mode_write(int magic) +{ + regmap_write(map, offset, magic); + + return 0; +} + +static int syscon_reboot_mode_probe(struct platform_device *pdev) +{ + int ret; + + map = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(map)) + return PTR_ERR(map); + if (of_property_read_u32(pdev->dev.of_node, "offset", &offset)) + return -EINVAL; + ret = reboot_mode_register(&pdev->dev, syscon_reboot_mode_write); + if (ret) + dev_err(&pdev->dev, "can't register reboot mode\n"); + + return ret; +} + +static const struct of_device_id syscon_reboot_mode_of_match[] = { + { .compatible = "syscon-reboot-mode" }, + {} +}; + +static struct platform_driver syscon_reboot_mode_driver = { + .probe = syscon_reboot_mode_probe, + .driver = { + .name = "syscon-reboot-mode", + .of_match_table = syscon_reboot_mode_of_match, + }, +}; +module_platform_driver(syscon_reboot_mode_driver); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("SYSCON reboot mode driver"); +MODULE_LICENSE("GPL v2"); |