694ca85d5a70f0a56bce6be5adb364d5eab8f28b
[firefly-linux-kernel-4.4.55.git] / drivers / extcon / devres.c
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 <linux/extcon.h>
18
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
29 static void devm_extcon_dev_release(struct device *dev, void *res)
30 {
31         extcon_dev_free(*(struct extcon_dev **)res);
32 }
33
34
35 static void devm_extcon_dev_unreg(struct device *dev, void *res)
36 {
37         extcon_dev_unregister(*(struct extcon_dev **)res);
38 }
39
40 /**
41  * devm_extcon_dev_allocate - Allocate managed extcon device
42  * @dev:                device owning the extcon device being created
43  * @supported_cable:    Array of supported extcon ending with EXTCON_NONE.
44  *                      If supported_cable is NULL, cable name related APIs
45  *                      are disabled.
46  *
47  * This function manages automatically the memory of extcon device using device
48  * resource management and simplify the control of freeing the memory of extcon
49  * device.
50  *
51  * Returns the pointer memory of allocated extcon_dev if success
52  * or ERR_PTR(err) if fail
53  */
54 struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
55                                         const unsigned int *supported_cable)
56 {
57         struct extcon_dev **ptr, *edev;
58
59         ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
60         if (!ptr)
61                 return ERR_PTR(-ENOMEM);
62
63         edev = extcon_dev_allocate(supported_cable);
64         if (IS_ERR(edev)) {
65                 devres_free(ptr);
66                 return edev;
67         }
68
69         edev->dev.parent = dev;
70
71         *ptr = edev;
72         devres_add(dev, ptr);
73
74         return edev;
75 }
76 EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
77
78 /**
79  * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister()
80  * @dev:        device the extcon belongs to
81  * @edev:       the extcon device to unregister
82  *
83  * Free the memory that is allocated with devm_extcon_dev_allocate()
84  * function.
85  */
86 void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
87 {
88         WARN_ON(devres_release(dev, devm_extcon_dev_release,
89                                devm_extcon_dev_match, edev));
90 }
91 EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
92
93 /**
94  * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
95  * @dev:        device to allocate extcon device
96  * @edev:       the new extcon device to register
97  *
98  * Managed extcon_dev_register() function. If extcon device is attached with
99  * this function, that extcon device is automatically unregistered on driver
100  * detach. Internally this function calls extcon_dev_register() function.
101  * To get more information, refer that function.
102  *
103  * If extcon device is registered with this function and the device needs to be
104  * unregistered separately, devm_extcon_dev_unregister() should be used.
105  *
106  * Returns 0 if success or negaive error number if failure.
107  */
108 int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
109 {
110         struct extcon_dev **ptr;
111         int ret;
112
113         ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
114         if (!ptr)
115                 return -ENOMEM;
116
117         ret = extcon_dev_register(edev);
118         if (ret) {
119                 devres_free(ptr);
120                 return ret;
121         }
122
123         *ptr = edev;
124         devres_add(dev, ptr);
125
126         return 0;
127 }
128 EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
129
130 /**
131  * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
132  * @dev:        device the extcon belongs to
133  * @edev:       the extcon device to unregister
134  *
135  * Unregister extcon device that is registered with devm_extcon_dev_register()
136  * function.
137  */
138 void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
139 {
140         WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
141                                devm_extcon_dev_match, edev));
142 }
143 EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);