at v4.8 6.3 kB view raw
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 19static 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 29static void devm_extcon_dev_release(struct device *dev, void *res) 30{ 31 extcon_dev_free(*(struct extcon_dev **)res); 32} 33 34 35static void devm_extcon_dev_unreg(struct device *dev, void *res) 36{ 37 extcon_dev_unregister(*(struct extcon_dev **)res); 38} 39 40struct extcon_dev_notifier_devres { 41 struct extcon_dev *edev; 42 unsigned int id; 43 struct notifier_block *nb; 44}; 45 46static 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 */ 67struct 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} 89EXPORT_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 */ 99void 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} 104EXPORT_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 */ 121int 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} 141EXPORT_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 */ 151void 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} 156EXPORT_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 */ 175int 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} 199EXPORT_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 */ 209void 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} 216EXPORT_SYMBOL(devm_extcon_unregister_notifier);