xref: /openbmc/linux/drivers/extcon/devres.c (revision 9c92ab61)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b225d00fSChanwoo Choi /*
3b225d00fSChanwoo Choi  * drivers/extcon/devres.c - EXTCON device's resource management
4b225d00fSChanwoo Choi  *
5b225d00fSChanwoo Choi  * Copyright (C) 2016 Samsung Electronics
6b225d00fSChanwoo Choi  * Author: Chanwoo Choi <cw00.choi@samsung.com>
7b225d00fSChanwoo Choi  */
8b225d00fSChanwoo Choi 
9e6cf0465SChanwoo Choi #include "extcon.h"
10b225d00fSChanwoo Choi 
devm_extcon_dev_match(struct device * dev,void * res,void * data)11b225d00fSChanwoo Choi static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
12b225d00fSChanwoo Choi {
13b225d00fSChanwoo Choi 	struct extcon_dev **r = res;
14b225d00fSChanwoo Choi 
15b225d00fSChanwoo Choi 	if (WARN_ON(!r || !*r))
16b225d00fSChanwoo Choi 		return 0;
17b225d00fSChanwoo Choi 
18b225d00fSChanwoo Choi 	return *r == data;
19b225d00fSChanwoo Choi }
20b225d00fSChanwoo Choi 
devm_extcon_dev_release(struct device * dev,void * res)21b225d00fSChanwoo Choi static void devm_extcon_dev_release(struct device *dev, void *res)
22b225d00fSChanwoo Choi {
23b225d00fSChanwoo Choi 	extcon_dev_free(*(struct extcon_dev **)res);
24b225d00fSChanwoo Choi }
25b225d00fSChanwoo Choi 
26b225d00fSChanwoo Choi 
devm_extcon_dev_unreg(struct device * dev,void * res)27b225d00fSChanwoo Choi static void devm_extcon_dev_unreg(struct device *dev, void *res)
28b225d00fSChanwoo Choi {
29b225d00fSChanwoo Choi 	extcon_dev_unregister(*(struct extcon_dev **)res);
30b225d00fSChanwoo Choi }
31b225d00fSChanwoo Choi 
3258f38656SChanwoo Choi struct extcon_dev_notifier_devres {
3358f38656SChanwoo Choi 	struct extcon_dev *edev;
3458f38656SChanwoo Choi 	unsigned int id;
3558f38656SChanwoo Choi 	struct notifier_block *nb;
3658f38656SChanwoo Choi };
3758f38656SChanwoo Choi 
devm_extcon_dev_notifier_unreg(struct device * dev,void * res)3858f38656SChanwoo Choi static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res)
3958f38656SChanwoo Choi {
4058f38656SChanwoo Choi 	struct extcon_dev_notifier_devres *this = res;
4158f38656SChanwoo Choi 
4258f38656SChanwoo Choi 	extcon_unregister_notifier(this->edev, this->id, this->nb);
4358f38656SChanwoo Choi }
4458f38656SChanwoo Choi 
devm_extcon_dev_notifier_all_unreg(struct device * dev,void * res)45815429b3SChanwoo Choi static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res)
46815429b3SChanwoo Choi {
47815429b3SChanwoo Choi 	struct extcon_dev_notifier_devres *this = res;
48815429b3SChanwoo Choi 
49815429b3SChanwoo Choi 	extcon_unregister_notifier_all(this->edev, this->nb);
50815429b3SChanwoo Choi }
51815429b3SChanwoo Choi 
52b225d00fSChanwoo Choi /**
53b225d00fSChanwoo Choi  * devm_extcon_dev_allocate - Allocate managed extcon device
546ab6094fSChanwoo Choi  * @dev:		the device owning the extcon device being created
556ab6094fSChanwoo Choi  * @supported_cable:	the array of the supported external connectors
566ab6094fSChanwoo Choi  *			ending with EXTCON_NONE.
57b225d00fSChanwoo Choi  *
58b225d00fSChanwoo Choi  * This function manages automatically the memory of extcon device using device
59b225d00fSChanwoo Choi  * resource management and simplify the control of freeing the memory of extcon
60b225d00fSChanwoo Choi  * device.
61b225d00fSChanwoo Choi  *
62b225d00fSChanwoo Choi  * Returns the pointer memory of allocated extcon_dev if success
63b225d00fSChanwoo Choi  * or ERR_PTR(err) if fail
64b225d00fSChanwoo Choi  */
devm_extcon_dev_allocate(struct device * dev,const unsigned int * supported_cable)65b225d00fSChanwoo Choi struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
66b225d00fSChanwoo Choi 					const unsigned int *supported_cable)
67b225d00fSChanwoo Choi {
68b225d00fSChanwoo Choi 	struct extcon_dev **ptr, *edev;
69b225d00fSChanwoo Choi 
70b225d00fSChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
71b225d00fSChanwoo Choi 	if (!ptr)
72b225d00fSChanwoo Choi 		return ERR_PTR(-ENOMEM);
73b225d00fSChanwoo Choi 
74b225d00fSChanwoo Choi 	edev = extcon_dev_allocate(supported_cable);
75b225d00fSChanwoo Choi 	if (IS_ERR(edev)) {
76b225d00fSChanwoo Choi 		devres_free(ptr);
77b225d00fSChanwoo Choi 		return edev;
78b225d00fSChanwoo Choi 	}
79b225d00fSChanwoo Choi 
80b225d00fSChanwoo Choi 	edev->dev.parent = dev;
81b225d00fSChanwoo Choi 
82b225d00fSChanwoo Choi 	*ptr = edev;
83b225d00fSChanwoo Choi 	devres_add(dev, ptr);
84b225d00fSChanwoo Choi 
85b225d00fSChanwoo Choi 	return edev;
86b225d00fSChanwoo Choi }
87b225d00fSChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
88b225d00fSChanwoo Choi 
89b225d00fSChanwoo Choi /**
90b225d00fSChanwoo Choi  * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister()
916ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
926ab6094fSChanwoo Choi  * @edev:	the extcon device to be freed
93b225d00fSChanwoo Choi  *
94b225d00fSChanwoo Choi  * Free the memory that is allocated with devm_extcon_dev_allocate()
95b225d00fSChanwoo Choi  * function.
96b225d00fSChanwoo Choi  */
devm_extcon_dev_free(struct device * dev,struct extcon_dev * edev)97b225d00fSChanwoo Choi void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
98b225d00fSChanwoo Choi {
99b225d00fSChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_release,
100b225d00fSChanwoo Choi 			       devm_extcon_dev_match, edev));
101b225d00fSChanwoo Choi }
102b225d00fSChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
103b225d00fSChanwoo Choi 
104b225d00fSChanwoo Choi /**
105b225d00fSChanwoo Choi  * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
1066ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
1076ab6094fSChanwoo Choi  * @edev:	the extcon device to be registered
108b225d00fSChanwoo Choi  *
109b225d00fSChanwoo Choi  * this function, that extcon device is automatically unregistered on driver
110b225d00fSChanwoo Choi  * detach. Internally this function calls extcon_dev_register() function.
111b225d00fSChanwoo Choi  * To get more information, refer that function.
112b225d00fSChanwoo Choi  *
113b225d00fSChanwoo Choi  * If extcon device is registered with this function and the device needs to be
114b225d00fSChanwoo Choi  * unregistered separately, devm_extcon_dev_unregister() should be used.
115b225d00fSChanwoo Choi  *
116b225d00fSChanwoo Choi  * Returns 0 if success or negaive error number if failure.
117b225d00fSChanwoo Choi  */
devm_extcon_dev_register(struct device * dev,struct extcon_dev * edev)118b225d00fSChanwoo Choi int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
119b225d00fSChanwoo Choi {
120b225d00fSChanwoo Choi 	struct extcon_dev **ptr;
121b225d00fSChanwoo Choi 	int ret;
122b225d00fSChanwoo Choi 
123b225d00fSChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
124b225d00fSChanwoo Choi 	if (!ptr)
125b225d00fSChanwoo Choi 		return -ENOMEM;
126b225d00fSChanwoo Choi 
127b225d00fSChanwoo Choi 	ret = extcon_dev_register(edev);
128b225d00fSChanwoo Choi 	if (ret) {
129b225d00fSChanwoo Choi 		devres_free(ptr);
130b225d00fSChanwoo Choi 		return ret;
131b225d00fSChanwoo Choi 	}
132b225d00fSChanwoo Choi 
133b225d00fSChanwoo Choi 	*ptr = edev;
134b225d00fSChanwoo Choi 	devres_add(dev, ptr);
135b225d00fSChanwoo Choi 
136b225d00fSChanwoo Choi 	return 0;
137b225d00fSChanwoo Choi }
138b225d00fSChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
139b225d00fSChanwoo Choi 
140b225d00fSChanwoo Choi /**
141b225d00fSChanwoo Choi  * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
1426ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
1436ab6094fSChanwoo Choi  * @edev:	the extcon device to unregistered
144b225d00fSChanwoo Choi  *
145b225d00fSChanwoo Choi  * Unregister extcon device that is registered with devm_extcon_dev_register()
146b225d00fSChanwoo Choi  * function.
147b225d00fSChanwoo Choi  */
devm_extcon_dev_unregister(struct device * dev,struct extcon_dev * edev)148b225d00fSChanwoo Choi void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
149b225d00fSChanwoo Choi {
150b225d00fSChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
151b225d00fSChanwoo Choi 			       devm_extcon_dev_match, edev));
152b225d00fSChanwoo Choi }
153b225d00fSChanwoo Choi EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
15458f38656SChanwoo Choi 
15558f38656SChanwoo Choi /**
15658f38656SChanwoo Choi  * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier()
1576ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
1586ab6094fSChanwoo Choi  * @edev:	the extcon device
1596ab6094fSChanwoo Choi  * @id:		the unique id among the extcon enumeration
1606ab6094fSChanwoo Choi  * @nb:		a notifier block to be registered
16158f38656SChanwoo Choi  *
16258f38656SChanwoo Choi  * This function manages automatically the notifier of extcon device using
16358f38656SChanwoo Choi  * device resource management and simplify the control of unregistering
16458f38656SChanwoo Choi  * the notifier of extcon device.
16558f38656SChanwoo Choi  *
16658f38656SChanwoo Choi  * Note that the second parameter given to the callback of nb (val) is
16758f38656SChanwoo Choi  * "old_state", not the current state. The current state can be retrieved
16858f38656SChanwoo Choi  * by looking at the third pameter (edev pointer)'s state value.
16958f38656SChanwoo Choi  *
17058f38656SChanwoo Choi  * Returns 0 if success or negaive error number if failure.
17158f38656SChanwoo Choi  */
devm_extcon_register_notifier(struct device * dev,struct extcon_dev * edev,unsigned int id,struct notifier_block * nb)17258f38656SChanwoo Choi int devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev,
17358f38656SChanwoo Choi 				unsigned int id, struct notifier_block *nb)
17458f38656SChanwoo Choi {
17558f38656SChanwoo Choi 	struct extcon_dev_notifier_devres *ptr;
17658f38656SChanwoo Choi 	int ret;
17758f38656SChanwoo Choi 
17858f38656SChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_notifier_unreg, sizeof(*ptr),
17958f38656SChanwoo Choi 				GFP_KERNEL);
18058f38656SChanwoo Choi 	if (!ptr)
18158f38656SChanwoo Choi 		return -ENOMEM;
18258f38656SChanwoo Choi 
18358f38656SChanwoo Choi 	ret = extcon_register_notifier(edev, id, nb);
18458f38656SChanwoo Choi 	if (ret) {
18558f38656SChanwoo Choi 		devres_free(ptr);
18658f38656SChanwoo Choi 		return ret;
18758f38656SChanwoo Choi 	}
18858f38656SChanwoo Choi 
18958f38656SChanwoo Choi 	ptr->edev = edev;
19058f38656SChanwoo Choi 	ptr->id = id;
19158f38656SChanwoo Choi 	ptr->nb = nb;
19258f38656SChanwoo Choi 	devres_add(dev, ptr);
19358f38656SChanwoo Choi 
19458f38656SChanwoo Choi 	return 0;
19558f38656SChanwoo Choi }
19658f38656SChanwoo Choi EXPORT_SYMBOL(devm_extcon_register_notifier);
19758f38656SChanwoo Choi 
19858f38656SChanwoo Choi /**
19958f38656SChanwoo Choi  * devm_extcon_unregister_notifier()
200db8b4aefSValdis Klētnieks  *			- Resource-managed extcon_unregister_notifier()
2016ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
2026ab6094fSChanwoo Choi  * @edev:	the extcon device
2036ab6094fSChanwoo Choi  * @id:		the unique id among the extcon enumeration
2046ab6094fSChanwoo Choi  * @nb:		a notifier block to be registered
20558f38656SChanwoo Choi  */
devm_extcon_unregister_notifier(struct device * dev,struct extcon_dev * edev,unsigned int id,struct notifier_block * nb)20658f38656SChanwoo Choi void devm_extcon_unregister_notifier(struct device *dev,
20758f38656SChanwoo Choi 				struct extcon_dev *edev, unsigned int id,
20858f38656SChanwoo Choi 				struct notifier_block *nb)
20958f38656SChanwoo Choi {
21058f38656SChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_notifier_unreg,
21158f38656SChanwoo Choi 			       devm_extcon_dev_match, edev));
21258f38656SChanwoo Choi }
21358f38656SChanwoo Choi EXPORT_SYMBOL(devm_extcon_unregister_notifier);
214815429b3SChanwoo Choi 
215815429b3SChanwoo Choi /**
216815429b3SChanwoo Choi  * devm_extcon_register_notifier_all()
217815429b3SChanwoo Choi  *		- Resource-managed extcon_register_notifier_all()
2186ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
2196ab6094fSChanwoo Choi  * @edev:	the extcon device
2206ab6094fSChanwoo Choi  * @nb:		a notifier block to be registered
221815429b3SChanwoo Choi  *
222815429b3SChanwoo Choi  * This function manages automatically the notifier of extcon device using
223815429b3SChanwoo Choi  * device resource management and simplify the control of unregistering
224815429b3SChanwoo Choi  * the notifier of extcon device. To get more information, refer that function.
225815429b3SChanwoo Choi  *
226815429b3SChanwoo Choi  * Returns 0 if success or negaive error number if failure.
227815429b3SChanwoo Choi  */
devm_extcon_register_notifier_all(struct device * dev,struct extcon_dev * edev,struct notifier_block * nb)228815429b3SChanwoo Choi int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev,
229815429b3SChanwoo Choi 				struct notifier_block *nb)
230815429b3SChanwoo Choi {
231815429b3SChanwoo Choi 	struct extcon_dev_notifier_devres *ptr;
232815429b3SChanwoo Choi 	int ret;
233815429b3SChanwoo Choi 
234815429b3SChanwoo Choi 	ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr),
235815429b3SChanwoo Choi 				GFP_KERNEL);
236815429b3SChanwoo Choi 	if (!ptr)
237815429b3SChanwoo Choi 		return -ENOMEM;
238815429b3SChanwoo Choi 
239815429b3SChanwoo Choi 	ret = extcon_register_notifier_all(edev, nb);
240815429b3SChanwoo Choi 	if (ret) {
241815429b3SChanwoo Choi 		devres_free(ptr);
242815429b3SChanwoo Choi 		return ret;
243815429b3SChanwoo Choi 	}
244815429b3SChanwoo Choi 
245815429b3SChanwoo Choi 	ptr->edev = edev;
246815429b3SChanwoo Choi 	ptr->nb = nb;
247815429b3SChanwoo Choi 	devres_add(dev, ptr);
248815429b3SChanwoo Choi 
249815429b3SChanwoo Choi 	return 0;
250815429b3SChanwoo Choi }
251815429b3SChanwoo Choi EXPORT_SYMBOL(devm_extcon_register_notifier_all);
252815429b3SChanwoo Choi 
253815429b3SChanwoo Choi /**
254815429b3SChanwoo Choi  * devm_extcon_unregister_notifier_all()
255815429b3SChanwoo Choi  *		- Resource-managed extcon_unregister_notifier_all()
2566ab6094fSChanwoo Choi  * @dev:	the device owning the extcon device being created
2576ab6094fSChanwoo Choi  * @edev:	the extcon device
2586ab6094fSChanwoo Choi  * @nb:		a notifier block to be registered
259815429b3SChanwoo Choi  */
devm_extcon_unregister_notifier_all(struct device * dev,struct extcon_dev * edev,struct notifier_block * nb)260815429b3SChanwoo Choi void devm_extcon_unregister_notifier_all(struct device *dev,
261815429b3SChanwoo Choi 				struct extcon_dev *edev,
262815429b3SChanwoo Choi 				struct notifier_block *nb)
263815429b3SChanwoo Choi {
264815429b3SChanwoo Choi 	WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg,
265815429b3SChanwoo Choi 			       devm_extcon_dev_match, edev));
266815429b3SChanwoo Choi }
267815429b3SChanwoo Choi EXPORT_SYMBOL(devm_extcon_unregister_notifier_all);
268