123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- /*
- * 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"
- #define PREFIX "mode-"
- struct mode_info {
- const char *mode;
- u32 magic;
- struct list_head list;
- };
- static unsigned 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;
- unsigned int magic;
- reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
- magic = get_reboot_mode_magic(reboot, cmd);
- if (magic)
- reboot->write(reboot, magic);
- return NOTIFY_DONE;
- }
- /**
- * reboot_mode_register - register a reboot mode driver
- * @reboot: reboot mode driver
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- int reboot_mode_register(struct reboot_mode_driver *reboot)
- {
- struct mode_info *info;
- struct property *prop;
- struct device_node *np = reboot->dev->of_node;
- size_t len = strlen(PREFIX);
- int ret;
- INIT_LIST_HEAD(&reboot->head);
- for_each_property_of_node(np, prop) {
- if (strncmp(prop->name, PREFIX, len))
- continue;
- info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
- if (!info) {
- ret = -ENOMEM;
- goto error;
- }
- if (of_property_read_u32(np, prop->name, &info->magic)) {
- dev_err(reboot->dev, "reboot mode %s without magic number\n",
- info->mode);
- devm_kfree(reboot->dev, info);
- continue;
- }
- info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
- if (!info->mode) {
- ret = -ENOMEM;
- goto error;
- } else if (info->mode[0] == '\0') {
- kfree_const(info->mode);
- ret = -EINVAL;
- dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
- prop->name);
- goto error;
- }
- list_add_tail(&info->list, &reboot->head);
- }
- reboot->reboot_notifier.notifier_call = reboot_mode_notify;
- register_reboot_notifier(&reboot->reboot_notifier);
- return 0;
- error:
- list_for_each_entry(info, &reboot->head, list)
- kfree_const(info->mode);
- return ret;
- }
- EXPORT_SYMBOL_GPL(reboot_mode_register);
- /**
- * reboot_mode_unregister - unregister a reboot mode driver
- * @reboot: reboot mode driver
- */
- int reboot_mode_unregister(struct reboot_mode_driver *reboot)
- {
- struct mode_info *info;
- unregister_reboot_notifier(&reboot->reboot_notifier);
- list_for_each_entry(info, &reboot->head, list)
- kfree_const(info->mode);
- return 0;
- }
- EXPORT_SYMBOL_GPL(reboot_mode_unregister);
- static void devm_reboot_mode_release(struct device *dev, void *res)
- {
- reboot_mode_unregister(*(struct reboot_mode_driver **)res);
- }
- /**
- * devm_reboot_mode_register() - resource managed reboot_mode_register()
- * @dev: device to associate this resource with
- * @reboot: reboot mode driver
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- int devm_reboot_mode_register(struct device *dev,
- struct reboot_mode_driver *reboot)
- {
- struct reboot_mode_driver **dr;
- int rc;
- dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
- rc = reboot_mode_register(reboot);
- if (rc) {
- devres_free(dr);
- return rc;
- }
- *dr = reboot;
- devres_add(dev, dr);
- return 0;
- }
- EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
- static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
- {
- struct reboot_mode_driver **p = res;
- if (WARN_ON(!p || !*p))
- return 0;
- return *p == data;
- }
- /**
- * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
- * @dev: device to associate this resource with
- * @reboot: reboot mode driver
- */
- void devm_reboot_mode_unregister(struct device *dev,
- struct reboot_mode_driver *reboot)
- {
- WARN_ON(devres_release(dev,
- devm_reboot_mode_release,
- devm_reboot_mode_match, reboot));
- }
- EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
- MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
- MODULE_DESCRIPTION("System reboot mode core library");
- MODULE_LICENSE("GPL v2");
|