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 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 struct extcon_dev_notifier_devres { 41 struct extcon_dev *edev; 42 unsigned int id; 43 struct notifier_block *nb; 44 }; 45 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 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: device owning the extcon device being created 63 * @supported_cable: Array of supported extcon ending with EXTCON_NONE. 64 * If supported_cable is NULL, cable name related APIs 65 * are disabled. 66 * 67 * This function manages automatically the memory of extcon device using device 68 * resource management and simplify the control of freeing the memory of extcon 69 * device. 70 * 71 * Returns the pointer memory of allocated extcon_dev if success 72 * or ERR_PTR(err) if fail 73 */ 74 struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, 75 const unsigned int *supported_cable) 76 { 77 struct extcon_dev **ptr, *edev; 78 79 ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL); 80 if (!ptr) 81 return ERR_PTR(-ENOMEM); 82 83 edev = extcon_dev_allocate(supported_cable); 84 if (IS_ERR(edev)) { 85 devres_free(ptr); 86 return edev; 87 } 88 89 edev->dev.parent = dev; 90 91 *ptr = edev; 92 devres_add(dev, ptr); 93 94 return edev; 95 } 96 EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate); 97 98 /** 99 * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister() 100 * @dev: device the extcon belongs to 101 * @edev: the extcon device to unregister 102 * 103 * Free the memory that is allocated with devm_extcon_dev_allocate() 104 * function. 105 */ 106 void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev) 107 { 108 WARN_ON(devres_release(dev, devm_extcon_dev_release, 109 devm_extcon_dev_match, edev)); 110 } 111 EXPORT_SYMBOL_GPL(devm_extcon_dev_free); 112 113 /** 114 * devm_extcon_dev_register() - Resource-managed extcon_dev_register() 115 * @dev: device to allocate extcon device 116 * @edev: the new extcon device to register 117 * 118 * Managed extcon_dev_register() function. If extcon device is attached with 119 * this function, that extcon device is automatically unregistered on driver 120 * detach. Internally this function calls extcon_dev_register() function. 121 * To get more information, refer that function. 122 * 123 * If extcon device is registered with this function and the device needs to be 124 * unregistered separately, devm_extcon_dev_unregister() should be used. 125 * 126 * Returns 0 if success or negaive error number if failure. 127 */ 128 int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev) 129 { 130 struct extcon_dev **ptr; 131 int ret; 132 133 ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL); 134 if (!ptr) 135 return -ENOMEM; 136 137 ret = extcon_dev_register(edev); 138 if (ret) { 139 devres_free(ptr); 140 return ret; 141 } 142 143 *ptr = edev; 144 devres_add(dev, ptr); 145 146 return 0; 147 } 148 EXPORT_SYMBOL_GPL(devm_extcon_dev_register); 149 150 /** 151 * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister() 152 * @dev: device the extcon belongs to 153 * @edev: the extcon device to unregister 154 * 155 * Unregister extcon device that is registered with devm_extcon_dev_register() 156 * function. 157 */ 158 void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev) 159 { 160 WARN_ON(devres_release(dev, devm_extcon_dev_unreg, 161 devm_extcon_dev_match, edev)); 162 } 163 EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister); 164 165 /** 166 * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier() 167 * @dev: device to allocate extcon device 168 * @edev: the extcon device that has the external connecotr. 169 * @id: the unique id of each external connector in extcon enumeration. 170 * @nb: a notifier block to be registered. 171 * 172 * This function manages automatically the notifier of extcon device using 173 * device resource management and simplify the control of unregistering 174 * the notifier of extcon device. 175 * 176 * Note that the second parameter given to the callback of nb (val) is 177 * "old_state", not the current state. The current state can be retrieved 178 * by looking at the third pameter (edev pointer)'s state value. 179 * 180 * Returns 0 if success or negaive error number if failure. 181 */ 182 int devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev, 183 unsigned int id, struct notifier_block *nb) 184 { 185 struct extcon_dev_notifier_devres *ptr; 186 int ret; 187 188 ptr = devres_alloc(devm_extcon_dev_notifier_unreg, sizeof(*ptr), 189 GFP_KERNEL); 190 if (!ptr) 191 return -ENOMEM; 192 193 ret = extcon_register_notifier(edev, id, nb); 194 if (ret) { 195 devres_free(ptr); 196 return ret; 197 } 198 199 ptr->edev = edev; 200 ptr->id = id; 201 ptr->nb = nb; 202 devres_add(dev, ptr); 203 204 return 0; 205 } 206 EXPORT_SYMBOL(devm_extcon_register_notifier); 207 208 /** 209 * devm_extcon_unregister_notifier() 210 - Resource-managed extcon_unregister_notifier() 211 * @dev: device to allocate extcon device 212 * @edev: the extcon device that has the external connecotr. 213 * @id: the unique id of each external connector in extcon enumeration. 214 * @nb: a notifier block to be registered. 215 */ 216 void devm_extcon_unregister_notifier(struct device *dev, 217 struct extcon_dev *edev, unsigned int id, 218 struct notifier_block *nb) 219 { 220 WARN_ON(devres_release(dev, devm_extcon_dev_notifier_unreg, 221 devm_extcon_dev_match, edev)); 222 } 223 EXPORT_SYMBOL(devm_extcon_unregister_notifier); 224 225 /** 226 * devm_extcon_register_notifier_all() 227 * - Resource-managed extcon_register_notifier_all() 228 * @dev: device to allocate extcon device 229 * @edev: the extcon device that has the external connecotr. 230 * @nb: a notifier block to be registered. 231 * 232 * This function manages automatically the notifier of extcon device using 233 * device resource management and simplify the control of unregistering 234 * the notifier of extcon device. To get more information, refer that function. 235 * 236 * Returns 0 if success or negaive error number if failure. 237 */ 238 int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev, 239 struct notifier_block *nb) 240 { 241 struct extcon_dev_notifier_devres *ptr; 242 int ret; 243 244 ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr), 245 GFP_KERNEL); 246 if (!ptr) 247 return -ENOMEM; 248 249 ret = extcon_register_notifier_all(edev, nb); 250 if (ret) { 251 devres_free(ptr); 252 return ret; 253 } 254 255 ptr->edev = edev; 256 ptr->nb = nb; 257 devres_add(dev, ptr); 258 259 return 0; 260 } 261 EXPORT_SYMBOL(devm_extcon_register_notifier_all); 262 263 /** 264 * devm_extcon_unregister_notifier_all() 265 * - Resource-managed extcon_unregister_notifier_all() 266 * @dev: device to allocate extcon device 267 * @edev: the extcon device that has the external connecotr. 268 * @nb: a notifier block to be registered. 269 */ 270 void devm_extcon_unregister_notifier_all(struct device *dev, 271 struct extcon_dev *edev, 272 struct notifier_block *nb) 273 { 274 WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg, 275 devm_extcon_dev_match, edev)); 276 } 277 EXPORT_SYMBOL(devm_extcon_unregister_notifier_all); 278