Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
0004  */
0005 
0006 #include <linux/device.h>
0007 #include <linux/init.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/reboot.h>
0012 #include <linux/reboot-mode.h>
0013 
0014 #define PREFIX "mode-"
0015 
0016 struct mode_info {
0017     const char *mode;
0018     u32 magic;
0019     struct list_head list;
0020 };
0021 
0022 static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
0023                       const char *cmd)
0024 {
0025     const char *normal = "normal";
0026     int magic = 0;
0027     struct mode_info *info;
0028 
0029     if (!cmd)
0030         cmd = normal;
0031 
0032     list_for_each_entry(info, &reboot->head, list) {
0033         if (!strcmp(info->mode, cmd)) {
0034             magic = info->magic;
0035             break;
0036         }
0037     }
0038 
0039     return magic;
0040 }
0041 
0042 static int reboot_mode_notify(struct notifier_block *this,
0043                   unsigned long mode, void *cmd)
0044 {
0045     struct reboot_mode_driver *reboot;
0046     unsigned int magic;
0047 
0048     reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
0049     magic = get_reboot_mode_magic(reboot, cmd);
0050     if (magic)
0051         reboot->write(reboot, magic);
0052 
0053     return NOTIFY_DONE;
0054 }
0055 
0056 /**
0057  * reboot_mode_register - register a reboot mode driver
0058  * @reboot: reboot mode driver
0059  *
0060  * Returns: 0 on success or a negative error code on failure.
0061  */
0062 int reboot_mode_register(struct reboot_mode_driver *reboot)
0063 {
0064     struct mode_info *info;
0065     struct property *prop;
0066     struct device_node *np = reboot->dev->of_node;
0067     size_t len = strlen(PREFIX);
0068     int ret;
0069 
0070     INIT_LIST_HEAD(&reboot->head);
0071 
0072     for_each_property_of_node(np, prop) {
0073         if (strncmp(prop->name, PREFIX, len))
0074             continue;
0075 
0076         info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
0077         if (!info) {
0078             ret = -ENOMEM;
0079             goto error;
0080         }
0081 
0082         if (of_property_read_u32(np, prop->name, &info->magic)) {
0083             dev_err(reboot->dev, "reboot mode %s without magic number\n",
0084                 info->mode);
0085             devm_kfree(reboot->dev, info);
0086             continue;
0087         }
0088 
0089         info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
0090         if (!info->mode) {
0091             ret =  -ENOMEM;
0092             goto error;
0093         } else if (info->mode[0] == '\0') {
0094             kfree_const(info->mode);
0095             ret = -EINVAL;
0096             dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
0097                 prop->name);
0098             goto error;
0099         }
0100 
0101         list_add_tail(&info->list, &reboot->head);
0102     }
0103 
0104     reboot->reboot_notifier.notifier_call = reboot_mode_notify;
0105     register_reboot_notifier(&reboot->reboot_notifier);
0106 
0107     return 0;
0108 
0109 error:
0110     list_for_each_entry(info, &reboot->head, list)
0111         kfree_const(info->mode);
0112 
0113     return ret;
0114 }
0115 EXPORT_SYMBOL_GPL(reboot_mode_register);
0116 
0117 /**
0118  * reboot_mode_unregister - unregister a reboot mode driver
0119  * @reboot: reboot mode driver
0120  */
0121 int reboot_mode_unregister(struct reboot_mode_driver *reboot)
0122 {
0123     struct mode_info *info;
0124 
0125     unregister_reboot_notifier(&reboot->reboot_notifier);
0126 
0127     list_for_each_entry(info, &reboot->head, list)
0128         kfree_const(info->mode);
0129 
0130     return 0;
0131 }
0132 EXPORT_SYMBOL_GPL(reboot_mode_unregister);
0133 
0134 static void devm_reboot_mode_release(struct device *dev, void *res)
0135 {
0136     reboot_mode_unregister(*(struct reboot_mode_driver **)res);
0137 }
0138 
0139 /**
0140  * devm_reboot_mode_register() - resource managed reboot_mode_register()
0141  * @dev: device to associate this resource with
0142  * @reboot: reboot mode driver
0143  *
0144  * Returns: 0 on success or a negative error code on failure.
0145  */
0146 int devm_reboot_mode_register(struct device *dev,
0147                   struct reboot_mode_driver *reboot)
0148 {
0149     struct reboot_mode_driver **dr;
0150     int rc;
0151 
0152     dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
0153     if (!dr)
0154         return -ENOMEM;
0155 
0156     rc = reboot_mode_register(reboot);
0157     if (rc) {
0158         devres_free(dr);
0159         return rc;
0160     }
0161 
0162     *dr = reboot;
0163     devres_add(dev, dr);
0164 
0165     return 0;
0166 }
0167 EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
0168 
0169 static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
0170 {
0171     struct reboot_mode_driver **p = res;
0172 
0173     if (WARN_ON(!p || !*p))
0174         return 0;
0175 
0176     return *p == data;
0177 }
0178 
0179 /**
0180  * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
0181  * @dev: device to associate this resource with
0182  * @reboot: reboot mode driver
0183  */
0184 void devm_reboot_mode_unregister(struct device *dev,
0185                  struct reboot_mode_driver *reboot)
0186 {
0187     WARN_ON(devres_release(dev,
0188                    devm_reboot_mode_release,
0189                    devm_reboot_mode_match, reboot));
0190 }
0191 EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
0192 
0193 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
0194 MODULE_DESCRIPTION("System reboot mode core library");
0195 MODULE_LICENSE("GPL v2");