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