Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
 
 
 
 
 
  4 */
  5
  6#include <linux/device.h>
  7#include <linux/init.h>
  8#include <linux/kernel.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/reboot.h>
 12#include <linux/reboot-mode.h>
 13
 14#define PREFIX "mode-"
 15
 16struct mode_info {
 17	const char *mode;
 18	u32 magic;
 19	struct list_head list;
 20};
 21
 22static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
 23					  const char *cmd)
 24{
 25	const char *normal = "normal";
 26	int magic = 0;
 27	struct mode_info *info;
 28
 29	if (!cmd)
 30		cmd = normal;
 31
 32	list_for_each_entry(info, &reboot->head, list) {
 33		if (!strcmp(info->mode, cmd)) {
 34			magic = info->magic;
 35			break;
 36		}
 37	}
 38
 39	return magic;
 40}
 41
 42static int reboot_mode_notify(struct notifier_block *this,
 43			      unsigned long mode, void *cmd)
 44{
 45	struct reboot_mode_driver *reboot;
 46	unsigned int magic;
 47
 48	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
 49	magic = get_reboot_mode_magic(reboot, cmd);
 50	if (magic)
 51		reboot->write(reboot, magic);
 52
 53	return NOTIFY_DONE;
 54}
 55
 56/**
 57 * reboot_mode_register - register a reboot mode driver
 58 * @reboot: reboot mode driver
 59 *
 60 * Returns: 0 on success or a negative error code on failure.
 61 */
 62int reboot_mode_register(struct reboot_mode_driver *reboot)
 63{
 64	struct mode_info *info;
 65	struct property *prop;
 66	struct device_node *np = reboot->dev->of_node;
 67	size_t len = strlen(PREFIX);
 68	int ret;
 69
 70	INIT_LIST_HEAD(&reboot->head);
 71
 72	for_each_property_of_node(np, prop) {
 73		if (strncmp(prop->name, PREFIX, len))
 74			continue;
 75
 76		info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
 77		if (!info) {
 78			ret = -ENOMEM;
 79			goto error;
 80		}
 81
 82		if (of_property_read_u32(np, prop->name, &info->magic)) {
 83			dev_err(reboot->dev, "reboot mode %s without magic number\n",
 84				info->mode);
 85			devm_kfree(reboot->dev, info);
 86			continue;
 87		}
 88
 89		info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
 90		if (!info->mode) {
 91			ret =  -ENOMEM;
 92			goto error;
 93		} else if (info->mode[0] == '\0') {
 94			kfree_const(info->mode);
 95			ret = -EINVAL;
 96			dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
 97				prop->name);
 98			goto error;
 99		}
100
101		list_add_tail(&info->list, &reboot->head);
102	}
103
104	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
105	register_reboot_notifier(&reboot->reboot_notifier);
106
107	return 0;
108
109error:
110	list_for_each_entry(info, &reboot->head, list)
111		kfree_const(info->mode);
112
113	return ret;
114}
115EXPORT_SYMBOL_GPL(reboot_mode_register);
116
117/**
118 * reboot_mode_unregister - unregister a reboot mode driver
119 * @reboot: reboot mode driver
120 */
121int reboot_mode_unregister(struct reboot_mode_driver *reboot)
122{
123	struct mode_info *info;
124
125	unregister_reboot_notifier(&reboot->reboot_notifier);
126
127	list_for_each_entry(info, &reboot->head, list)
128		kfree_const(info->mode);
129
130	return 0;
131}
132EXPORT_SYMBOL_GPL(reboot_mode_unregister);
133
134static void devm_reboot_mode_release(struct device *dev, void *res)
135{
136	reboot_mode_unregister(*(struct reboot_mode_driver **)res);
137}
138
139/**
140 * devm_reboot_mode_register() - resource managed reboot_mode_register()
141 * @dev: device to associate this resource with
142 * @reboot: reboot mode driver
143 *
144 * Returns: 0 on success or a negative error code on failure.
145 */
146int devm_reboot_mode_register(struct device *dev,
147			      struct reboot_mode_driver *reboot)
148{
149	struct reboot_mode_driver **dr;
150	int rc;
151
152	dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
153	if (!dr)
154		return -ENOMEM;
155
156	rc = reboot_mode_register(reboot);
157	if (rc) {
158		devres_free(dr);
159		return rc;
160	}
161
162	*dr = reboot;
163	devres_add(dev, dr);
164
165	return 0;
166}
167EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
168
169static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
170{
171	struct reboot_mode_driver **p = res;
172
173	if (WARN_ON(!p || !*p))
174		return 0;
175
176	return *p == data;
177}
178
179/**
180 * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
181 * @dev: device to associate this resource with
182 * @reboot: reboot mode driver
183 */
184void devm_reboot_mode_unregister(struct device *dev,
185				 struct reboot_mode_driver *reboot)
186{
187	WARN_ON(devres_release(dev,
188			       devm_reboot_mode_release,
189			       devm_reboot_mode_match, reboot));
190}
191EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
192
193MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
194MODULE_DESCRIPTION("System reboot mode core library");
195MODULE_LICENSE("GPL v2");
v4.17
 
  1/*
  2 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation; either version 2 of the License, or
  7 * (at your option) any later version.
  8 */
  9
 10#include <linux/device.h>
 11#include <linux/init.h>
 12#include <linux/kernel.h>
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/reboot.h>
 16#include <linux/reboot-mode.h>
 17
 18#define PREFIX "mode-"
 19
 20struct mode_info {
 21	const char *mode;
 22	u32 magic;
 23	struct list_head list;
 24};
 25
 26static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
 27					  const char *cmd)
 28{
 29	const char *normal = "normal";
 30	int magic = 0;
 31	struct mode_info *info;
 32
 33	if (!cmd)
 34		cmd = normal;
 35
 36	list_for_each_entry(info, &reboot->head, list) {
 37		if (!strcmp(info->mode, cmd)) {
 38			magic = info->magic;
 39			break;
 40		}
 41	}
 42
 43	return magic;
 44}
 45
 46static int reboot_mode_notify(struct notifier_block *this,
 47			      unsigned long mode, void *cmd)
 48{
 49	struct reboot_mode_driver *reboot;
 50	unsigned int magic;
 51
 52	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
 53	magic = get_reboot_mode_magic(reboot, cmd);
 54	if (magic)
 55		reboot->write(reboot, magic);
 56
 57	return NOTIFY_DONE;
 58}
 59
 60/**
 61 * reboot_mode_register - register a reboot mode driver
 62 * @reboot: reboot mode driver
 63 *
 64 * Returns: 0 on success or a negative error code on failure.
 65 */
 66int reboot_mode_register(struct reboot_mode_driver *reboot)
 67{
 68	struct mode_info *info;
 69	struct property *prop;
 70	struct device_node *np = reboot->dev->of_node;
 71	size_t len = strlen(PREFIX);
 72	int ret;
 73
 74	INIT_LIST_HEAD(&reboot->head);
 75
 76	for_each_property_of_node(np, prop) {
 77		if (strncmp(prop->name, PREFIX, len))
 78			continue;
 79
 80		info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
 81		if (!info) {
 82			ret = -ENOMEM;
 83			goto error;
 84		}
 85
 86		if (of_property_read_u32(np, prop->name, &info->magic)) {
 87			dev_err(reboot->dev, "reboot mode %s without magic number\n",
 88				info->mode);
 89			devm_kfree(reboot->dev, info);
 90			continue;
 91		}
 92
 93		info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
 94		if (!info->mode) {
 95			ret =  -ENOMEM;
 96			goto error;
 97		} else if (info->mode[0] == '\0') {
 98			kfree_const(info->mode);
 99			ret = -EINVAL;
100			dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
101				prop->name);
102			goto error;
103		}
104
105		list_add_tail(&info->list, &reboot->head);
106	}
107
108	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
109	register_reboot_notifier(&reboot->reboot_notifier);
110
111	return 0;
112
113error:
114	list_for_each_entry(info, &reboot->head, list)
115		kfree_const(info->mode);
116
117	return ret;
118}
119EXPORT_SYMBOL_GPL(reboot_mode_register);
120
121/**
122 * reboot_mode_unregister - unregister a reboot mode driver
123 * @reboot: reboot mode driver
124 */
125int reboot_mode_unregister(struct reboot_mode_driver *reboot)
126{
127	struct mode_info *info;
128
129	unregister_reboot_notifier(&reboot->reboot_notifier);
130
131	list_for_each_entry(info, &reboot->head, list)
132		kfree_const(info->mode);
133
134	return 0;
135}
136EXPORT_SYMBOL_GPL(reboot_mode_unregister);
137
138static void devm_reboot_mode_release(struct device *dev, void *res)
139{
140	reboot_mode_unregister(*(struct reboot_mode_driver **)res);
141}
142
143/**
144 * devm_reboot_mode_register() - resource managed reboot_mode_register()
145 * @dev: device to associate this resource with
146 * @reboot: reboot mode driver
147 *
148 * Returns: 0 on success or a negative error code on failure.
149 */
150int devm_reboot_mode_register(struct device *dev,
151			      struct reboot_mode_driver *reboot)
152{
153	struct reboot_mode_driver **dr;
154	int rc;
155
156	dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
157	if (!dr)
158		return -ENOMEM;
159
160	rc = reboot_mode_register(reboot);
161	if (rc) {
162		devres_free(dr);
163		return rc;
164	}
165
166	*dr = reboot;
167	devres_add(dev, dr);
168
169	return 0;
170}
171EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
172
173static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
174{
175	struct reboot_mode_driver **p = res;
176
177	if (WARN_ON(!p || !*p))
178		return 0;
179
180	return *p == data;
181}
182
183/**
184 * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
185 * @dev: device to associate this resource with
186 * @reboot: reboot mode driver
187 */
188void devm_reboot_mode_unregister(struct device *dev,
189				 struct reboot_mode_driver *reboot)
190{
191	WARN_ON(devres_release(dev,
192			       devm_reboot_mode_release,
193			       devm_reboot_mode_match, reboot));
194}
195EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
196
197MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
198MODULE_DESCRIPTION("System reboot mode core library");
199MODULE_LICENSE("GPL v2");