0001
0002
0003
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
0058
0059
0060
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
0119
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
0141
0142
0143
0144
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
0181
0182
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");