1 /*
2  * drivers/extcon/devres.c - EXTCON device's resource management
3  *
4  * Copyright (C) 2016 Samsung Electronics
5  * Author: Chanwoo Choi <cw00.choi@samsung.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 
17 #include "extcon.h"
18 
devm_extcon_dev_match(struct device * dev,void * res,void * data)19 static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
20 {
21 	struct extcon_dev **r = res;
22 
23 	if (WARN_ON(!r || !*r))
24 		return 0;
25 
26 	return *r == data;
27 }
28 
devm_extcon_dev_release(struct device * dev,void * res)29 static void devm_extcon_dev_release(struct device *dev, void *res)
30 {
31 	extcon_dev_free(*(struct extcon_dev **)res);
32 }
33 
34 
devm_extcon_dev_unreg(struct device * dev,void * res)35 static void devm_extcon_dev_unreg(struct device *dev, void *res)
36 {
37 	extcon_dev_unregister(*(struct extcon_dev **)res);
38 }
39 
40 struct extcon_dev_notifier_devres {
41 	struct extcon_dev *edev;
42 	unsigned int id;
43 	struct notifier_block *nb;
44 };
45 
devm_extcon_dev_notifier_unreg(struct device * dev,void * res)46 static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res)
47 {
48 	struct extcon_dev_notifier_devres *this = res;
49 
50 	extcon_unregister_notifier(this->edev, this->id, this->nb);
51 }
52 
devm_extcon_dev_notifier_all_unreg(struct device * dev,void * res)53 static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res)
54 {
55 	struct extcon_dev_notifier_devres *this = res;
56 
57 	extcon_unregister_notifier_all(this->edev, this->nb);
58 }
59 
60 /**
61  * devm_extcon_dev_allocate - Allocate managed extcon device
62  * @dev:		the device owning the extcon device being created
63  * @supported_cable:	the array of the supported external connectors
64  *			ending with EXTCON_NONE.
65  *
66  * This function manages automatically the memory of extcon device using device
67  * resource management and simplify the control of freeing the memory of extcon
68  * device.
69  *
70  * Returns the pointer memory of allocated extcon_dev if success
71  * or ERR_PTR(err) if fail
72  */
devm_extcon_dev_allocate(struct device * dev,const unsigned int * supported_cable)73 struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
74 					const unsigned int *supported_cable)
75 {
76 	struct extcon_dev **ptr, *edev;
77 
78 	ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
79 	if (!ptr)
80 		return ERR_PTR(-ENOMEM);
81 
82 	edev = extcon_dev_allocate(supported_cable);
83 	if (IS_ERR(edev)) {
84 		devres_free(ptr);
85 		return edev;
86 	}
87 
88 	edev->dev.parent = dev;
89 
90 	*ptr = edev;
91 	devres_add(dev, ptr);
92 
93 	return edev;
94 }
95 EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
96 
97 /**
98  * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister()
99  * @dev:	the device owning the extcon device being created
100  * @edev:	the extcon device to be freed
101  *
102  * Free the memory that is allocated with devm_extcon_dev_allocate()
103  * function.
104  */
devm_extcon_dev_free(struct device * dev,struct extcon_dev * edev)105 void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
106 {
107 	WARN_ON(devres_release(dev, devm_extcon_dev_release,
108 			       devm_extcon_dev_match, edev));
109 }
110 EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
111 
112 /**
113  * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
114  * @dev:	the device owning the extcon device being created
115  * @edev:	the extcon device to be registered
116  *
117  * this function, that extcon device is automatically unregistered on driver
118  * detach. Internally this function calls extcon_dev_register() function.
119  * To get more information, refer that function.
120  *
121  * If extcon device is registered with this function and the device needs to be
122  * unregistered separately, devm_extcon_dev_unregister() should be used.
123  *
124  * Returns 0 if success or negaive error number if failure.
125  */
devm_extcon_dev_register(struct device * dev,struct extcon_dev * edev)126 int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
127 {
128 	struct extcon_dev **ptr;
129 	int ret;
130 
131 	ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
132 	if (!ptr)
133 		return -ENOMEM;
134 
135 	ret = extcon_dev_register(edev);
136 	if (ret) {
137 		devres_free(ptr);
138 		return ret;
139 	}
140 
141 	*ptr = edev;
142 	devres_add(dev, ptr);
143 
144 	return 0;
145 }
146 EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
147 
148 /**
149  * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
150  * @dev:	the device owning the extcon device being created
151  * @edev:	the extcon device to unregistered
152  *
153  * Unregister extcon device that is registered with devm_extcon_dev_register()
154  * function.
155  */
devm_extcon_dev_unregister(struct device * dev,struct extcon_dev * edev)156 void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
157 {
158 	WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
159 			       devm_extcon_dev_match, edev));
160 }
161 EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
162 
163 /**
164  * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier()
165  * @dev:	the device owning the extcon device being created
166  * @edev:	the extcon device
167  * @id:		the unique id among the extcon enumeration
168  * @nb:		a notifier block to be registered
169  *
170  * This function manages automatically the notifier of extcon device using
171  * device resource management and simplify the control of unregistering
172  * the notifier of extcon device.
173  *
174  * Note that the second parameter given to the callback of nb (val) is
175  * "old_state", not the current state. The current state can be retrieved
176  * by looking at the third pameter (edev pointer)'s state value.
177  *
178  * Returns 0 if success or negaive error number if failure.
179  */
devm_extcon_register_notifier(struct device * dev,struct extcon_dev * edev,unsigned int id,struct notifier_block * nb)180 int devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev,
181 				unsigned int id, struct notifier_block *nb)
182 {
183 	struct extcon_dev_notifier_devres *ptr;
184 	int ret;
185 
186 	ptr = devres_alloc(devm_extcon_dev_notifier_unreg, sizeof(*ptr),
187 				GFP_KERNEL);
188 	if (!ptr)
189 		return -ENOMEM;
190 
191 	ret = extcon_register_notifier(edev, id, nb);
192 	if (ret) {
193 		devres_free(ptr);
194 		return ret;
195 	}
196 
197 	ptr->edev = edev;
198 	ptr->id = id;
199 	ptr->nb = nb;
200 	devres_add(dev, ptr);
201 
202 	return 0;
203 }
204 EXPORT_SYMBOL(devm_extcon_register_notifier);
205 
206 /**
207  * devm_extcon_unregister_notifier()
208 			- Resource-managed extcon_unregister_notifier()
209  * @dev:	the device owning the extcon device being created
210  * @edev:	the extcon device
211  * @id:		the unique id among the extcon enumeration
212  * @nb:		a notifier block to be registered
213  */
devm_extcon_unregister_notifier(struct device * dev,struct extcon_dev * edev,unsigned int id,struct notifier_block * nb)214 void devm_extcon_unregister_notifier(struct device *dev,
215 				struct extcon_dev *edev, unsigned int id,
216 				struct notifier_block *nb)
217 {
218 	WARN_ON(devres_release(dev, devm_extcon_dev_notifier_unreg,
219 			       devm_extcon_dev_match, edev));
220 }
221 EXPORT_SYMBOL(devm_extcon_unregister_notifier);
222 
223 /**
224  * devm_extcon_register_notifier_all()
225  *		- Resource-managed extcon_register_notifier_all()
226  * @dev:	the device owning the extcon device being created
227  * @edev:	the extcon device
228  * @nb:		a notifier block to be registered
229  *
230  * This function manages automatically the notifier of extcon device using
231  * device resource management and simplify the control of unregistering
232  * the notifier of extcon device. To get more information, refer that function.
233  *
234  * Returns 0 if success or negaive error number if failure.
235  */
devm_extcon_register_notifier_all(struct device * dev,struct extcon_dev * edev,struct notifier_block * nb)236 int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev,
237 				struct notifier_block *nb)
238 {
239 	struct extcon_dev_notifier_devres *ptr;
240 	int ret;
241 
242 	ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr),
243 				GFP_KERNEL);
244 	if (!ptr)
245 		return -ENOMEM;
246 
247 	ret = extcon_register_notifier_all(edev, nb);
248 	if (ret) {
249 		devres_free(ptr);
250 		return ret;
251 	}
252 
253 	ptr->edev = edev;
254 	ptr->nb = nb;
255 	devres_add(dev, ptr);
256 
257 	return 0;
258 }
259 EXPORT_SYMBOL(devm_extcon_register_notifier_all);
260 
261 /**
262  * devm_extcon_unregister_notifier_all()
263  *		- Resource-managed extcon_unregister_notifier_all()
264  * @dev:	the device owning the extcon device being created
265  * @edev:	the extcon device
266  * @nb:		a notifier block to be registered
267  */
devm_extcon_unregister_notifier_all(struct device * dev,struct extcon_dev * edev,struct notifier_block * nb)268 void devm_extcon_unregister_notifier_all(struct device *dev,
269 				struct extcon_dev *edev,
270 				struct notifier_block *nb)
271 {
272 	WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg,
273 			       devm_extcon_dev_match, edev));
274 }
275 EXPORT_SYMBOL(devm_extcon_unregister_notifier_all);
276