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