18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/extcon/devres.c - EXTCON device's resource management 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Samsung Electronics 68c2ecf20Sopenharmony_ci * Author: Chanwoo Choi <cw00.choi@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "extcon.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int devm_extcon_dev_match(struct device *dev, void *res, void *data) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct extcon_dev **r = res; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci if (WARN_ON(!r || !*r)) 168c2ecf20Sopenharmony_ci return 0; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci return *r == data; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void devm_extcon_dev_release(struct device *dev, void *res) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci extcon_dev_free(*(struct extcon_dev **)res); 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void devm_extcon_dev_unreg(struct device *dev, void *res) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci extcon_dev_unregister(*(struct extcon_dev **)res); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct extcon_dev_notifier_devres { 338c2ecf20Sopenharmony_ci struct extcon_dev *edev; 348c2ecf20Sopenharmony_ci unsigned int id; 358c2ecf20Sopenharmony_ci struct notifier_block *nb; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void devm_extcon_dev_notifier_unreg(struct device *dev, void *res) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct extcon_dev_notifier_devres *this = res; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci extcon_unregister_notifier(this->edev, this->id, this->nb); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct extcon_dev_notifier_devres *this = res; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci extcon_unregister_notifier_all(this->edev, this->nb); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * devm_extcon_dev_allocate - Allocate managed extcon device 548c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 558c2ecf20Sopenharmony_ci * @supported_cable: the array of the supported external connectors 568c2ecf20Sopenharmony_ci * ending with EXTCON_NONE. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * This function manages automatically the memory of extcon device using device 598c2ecf20Sopenharmony_ci * resource management and simplify the control of freeing the memory of extcon 608c2ecf20Sopenharmony_ci * device. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Returns the pointer memory of allocated extcon_dev if success 638c2ecf20Sopenharmony_ci * or ERR_PTR(err) if fail 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistruct extcon_dev *devm_extcon_dev_allocate(struct device *dev, 668c2ecf20Sopenharmony_ci const unsigned int *supported_cable) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct extcon_dev **ptr, *edev; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL); 718c2ecf20Sopenharmony_ci if (!ptr) 728c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci edev = extcon_dev_allocate(supported_cable); 758c2ecf20Sopenharmony_ci if (IS_ERR(edev)) { 768c2ecf20Sopenharmony_ci devres_free(ptr); 778c2ecf20Sopenharmony_ci return edev; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci edev->dev.parent = dev; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci *ptr = edev; 838c2ecf20Sopenharmony_ci devres_add(dev, ptr); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return edev; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_extcon_dev_allocate); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/** 908c2ecf20Sopenharmony_ci * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister() 918c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 928c2ecf20Sopenharmony_ci * @edev: the extcon device to be freed 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * Free the memory that is allocated with devm_extcon_dev_allocate() 958c2ecf20Sopenharmony_ci * function. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_civoid devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_extcon_dev_release, 1008c2ecf20Sopenharmony_ci devm_extcon_dev_match, edev)); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_extcon_dev_free); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/** 1058c2ecf20Sopenharmony_ci * devm_extcon_dev_register() - Resource-managed extcon_dev_register() 1068c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 1078c2ecf20Sopenharmony_ci * @edev: the extcon device to be registered 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * this function, that extcon device is automatically unregistered on driver 1108c2ecf20Sopenharmony_ci * detach. Internally this function calls extcon_dev_register() function. 1118c2ecf20Sopenharmony_ci * To get more information, refer that function. 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * If extcon device is registered with this function and the device needs to be 1148c2ecf20Sopenharmony_ci * unregistered separately, devm_extcon_dev_unregister() should be used. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Returns 0 if success or negaive error number if failure. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ciint devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct extcon_dev **ptr; 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL); 1248c2ecf20Sopenharmony_ci if (!ptr) 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = extcon_dev_register(edev); 1288c2ecf20Sopenharmony_ci if (ret) { 1298c2ecf20Sopenharmony_ci devres_free(ptr); 1308c2ecf20Sopenharmony_ci return ret; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci *ptr = edev; 1348c2ecf20Sopenharmony_ci devres_add(dev, ptr); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_extcon_dev_register); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/** 1418c2ecf20Sopenharmony_ci * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister() 1428c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 1438c2ecf20Sopenharmony_ci * @edev: the extcon device to unregistered 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * Unregister extcon device that is registered with devm_extcon_dev_register() 1468c2ecf20Sopenharmony_ci * function. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_civoid devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_extcon_dev_unreg, 1518c2ecf20Sopenharmony_ci devm_extcon_dev_match, edev)); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_extcon_dev_unregister); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier() 1578c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 1588c2ecf20Sopenharmony_ci * @edev: the extcon device 1598c2ecf20Sopenharmony_ci * @id: the unique id among the extcon enumeration 1608c2ecf20Sopenharmony_ci * @nb: a notifier block to be registered 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * This function manages automatically the notifier of extcon device using 1638c2ecf20Sopenharmony_ci * device resource management and simplify the control of unregistering 1648c2ecf20Sopenharmony_ci * the notifier of extcon device. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Note that the second parameter given to the callback of nb (val) is 1678c2ecf20Sopenharmony_ci * "old_state", not the current state. The current state can be retrieved 1688c2ecf20Sopenharmony_ci * by looking at the third pameter (edev pointer)'s state value. 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Returns 0 if success or negaive error number if failure. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint devm_extcon_register_notifier(struct device *dev, struct extcon_dev *edev, 1738c2ecf20Sopenharmony_ci unsigned int id, struct notifier_block *nb) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct extcon_dev_notifier_devres *ptr; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_extcon_dev_notifier_unreg, sizeof(*ptr), 1798c2ecf20Sopenharmony_ci GFP_KERNEL); 1808c2ecf20Sopenharmony_ci if (!ptr) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ret = extcon_register_notifier(edev, id, nb); 1848c2ecf20Sopenharmony_ci if (ret) { 1858c2ecf20Sopenharmony_ci devres_free(ptr); 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ptr->edev = edev; 1908c2ecf20Sopenharmony_ci ptr->id = id; 1918c2ecf20Sopenharmony_ci ptr->nb = nb; 1928c2ecf20Sopenharmony_ci devres_add(dev, ptr); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_extcon_register_notifier); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/** 1998c2ecf20Sopenharmony_ci * devm_extcon_unregister_notifier() 2008c2ecf20Sopenharmony_ci * - Resource-managed extcon_unregister_notifier() 2018c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 2028c2ecf20Sopenharmony_ci * @edev: the extcon device 2038c2ecf20Sopenharmony_ci * @id: the unique id among the extcon enumeration 2048c2ecf20Sopenharmony_ci * @nb: a notifier block to be registered 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_civoid devm_extcon_unregister_notifier(struct device *dev, 2078c2ecf20Sopenharmony_ci struct extcon_dev *edev, unsigned int id, 2088c2ecf20Sopenharmony_ci struct notifier_block *nb) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_extcon_dev_notifier_unreg, 2118c2ecf20Sopenharmony_ci devm_extcon_dev_match, edev)); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_extcon_unregister_notifier); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/** 2168c2ecf20Sopenharmony_ci * devm_extcon_register_notifier_all() 2178c2ecf20Sopenharmony_ci * - Resource-managed extcon_register_notifier_all() 2188c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 2198c2ecf20Sopenharmony_ci * @edev: the extcon device 2208c2ecf20Sopenharmony_ci * @nb: a notifier block to be registered 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * This function manages automatically the notifier of extcon device using 2238c2ecf20Sopenharmony_ci * device resource management and simplify the control of unregistering 2248c2ecf20Sopenharmony_ci * the notifier of extcon device. To get more information, refer that function. 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * Returns 0 if success or negaive error number if failure. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ciint devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev, 2298c2ecf20Sopenharmony_ci struct notifier_block *nb) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct extcon_dev_notifier_devres *ptr; 2328c2ecf20Sopenharmony_ci int ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr), 2358c2ecf20Sopenharmony_ci GFP_KERNEL); 2368c2ecf20Sopenharmony_ci if (!ptr) 2378c2ecf20Sopenharmony_ci return -ENOMEM; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = extcon_register_notifier_all(edev, nb); 2408c2ecf20Sopenharmony_ci if (ret) { 2418c2ecf20Sopenharmony_ci devres_free(ptr); 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ptr->edev = edev; 2468c2ecf20Sopenharmony_ci ptr->nb = nb; 2478c2ecf20Sopenharmony_ci devres_add(dev, ptr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_extcon_register_notifier_all); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * devm_extcon_unregister_notifier_all() 2558c2ecf20Sopenharmony_ci * - Resource-managed extcon_unregister_notifier_all() 2568c2ecf20Sopenharmony_ci * @dev: the device owning the extcon device being created 2578c2ecf20Sopenharmony_ci * @edev: the extcon device 2588c2ecf20Sopenharmony_ci * @nb: a notifier block to be registered 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_civoid devm_extcon_unregister_notifier_all(struct device *dev, 2618c2ecf20Sopenharmony_ci struct extcon_dev *edev, 2628c2ecf20Sopenharmony_ci struct notifier_block *nb) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg, 2658c2ecf20Sopenharmony_ci devm_extcon_dev_match, edev)); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_extcon_unregister_notifier_all); 268