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