1 /* 2 * drivers/extcon/devres.c - EXTCON device's resource management 3 * 4 * Copyright (C) 2016 Samsung Electronics 5 * Author: Chanwoo Choi <[email protected]> 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); 144