162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/device.h>
462306a36Sopenharmony_ci#include <linux/of_mdio.h>
562306a36Sopenharmony_ci#include <linux/phy.h>
662306a36Sopenharmony_ci#include <linux/stddef.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistruct mdiobus_devres {
962306a36Sopenharmony_ci	struct mii_bus *mii;
1062306a36Sopenharmony_ci};
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic void devm_mdiobus_free(struct device *dev, void *this)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	struct mdiobus_devres *dr = this;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	mdiobus_free(dr->mii);
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/**
2062306a36Sopenharmony_ci * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size()
2162306a36Sopenharmony_ci * @dev:		Device to allocate mii_bus for
2262306a36Sopenharmony_ci * @sizeof_priv:	Space to allocate for private structure
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Managed mdiobus_alloc_size. mii_bus allocated with this function is
2562306a36Sopenharmony_ci * automatically freed on driver detach.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * RETURNS:
2862306a36Sopenharmony_ci * Pointer to allocated mii_bus on success, NULL on out-of-memory error.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistruct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct mdiobus_devres *dr;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	dr = devres_alloc(devm_mdiobus_free, sizeof(*dr), GFP_KERNEL);
3562306a36Sopenharmony_ci	if (!dr)
3662306a36Sopenharmony_ci		return NULL;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	dr->mii = mdiobus_alloc_size(sizeof_priv);
3962306a36Sopenharmony_ci	if (!dr->mii) {
4062306a36Sopenharmony_ci		devres_free(dr);
4162306a36Sopenharmony_ci		return NULL;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	devres_add(dev, dr);
4562306a36Sopenharmony_ci	return dr->mii;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ciEXPORT_SYMBOL(devm_mdiobus_alloc_size);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void devm_mdiobus_unregister(struct device *dev, void *this)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct mdiobus_devres *dr = this;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	mdiobus_unregister(dr->mii);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int mdiobus_devres_match(struct device *dev,
5762306a36Sopenharmony_ci				void *this, void *match_data)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct mdiobus_devres *res = this;
6062306a36Sopenharmony_ci	struct mii_bus *mii = match_data;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return mii == res->mii;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * __devm_mdiobus_register - Resource-managed variant of mdiobus_register()
6762306a36Sopenharmony_ci * @dev:	Device to register mii_bus for
6862306a36Sopenharmony_ci * @bus:	MII bus structure to register
6962306a36Sopenharmony_ci * @owner:	Owning module
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * Returns 0 on success, negative error number on failure.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ciint __devm_mdiobus_register(struct device *dev, struct mii_bus *bus,
7462306a36Sopenharmony_ci			    struct module *owner)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct mdiobus_devres *dr;
7762306a36Sopenharmony_ci	int ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (WARN_ON(!devres_find(dev, devm_mdiobus_free,
8062306a36Sopenharmony_ci				 mdiobus_devres_match, bus)))
8162306a36Sopenharmony_ci		return -EINVAL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	dr = devres_alloc(devm_mdiobus_unregister, sizeof(*dr), GFP_KERNEL);
8462306a36Sopenharmony_ci	if (!dr)
8562306a36Sopenharmony_ci		return -ENOMEM;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ret = __mdiobus_register(bus, owner);
8862306a36Sopenharmony_ci	if (ret) {
8962306a36Sopenharmony_ci		devres_free(dr);
9062306a36Sopenharmony_ci		return ret;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dr->mii = bus;
9462306a36Sopenharmony_ci	devres_add(dev, dr);
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ciEXPORT_SYMBOL(__devm_mdiobus_register);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_MDIO)
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * __devm_of_mdiobus_register - Resource managed variant of of_mdiobus_register()
10262306a36Sopenharmony_ci * @dev:	Device to register mii_bus for
10362306a36Sopenharmony_ci * @mdio:	MII bus structure to register
10462306a36Sopenharmony_ci * @np:		Device node to parse
10562306a36Sopenharmony_ci * @owner:	Owning module
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ciint __devm_of_mdiobus_register(struct device *dev, struct mii_bus *mdio,
10862306a36Sopenharmony_ci			       struct device_node *np, struct module *owner)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct mdiobus_devres *dr;
11162306a36Sopenharmony_ci	int ret;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (WARN_ON(!devres_find(dev, devm_mdiobus_free,
11462306a36Sopenharmony_ci				 mdiobus_devres_match, mdio)))
11562306a36Sopenharmony_ci		return -EINVAL;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dr = devres_alloc(devm_mdiobus_unregister, sizeof(*dr), GFP_KERNEL);
11862306a36Sopenharmony_ci	if (!dr)
11962306a36Sopenharmony_ci		return -ENOMEM;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ret = __of_mdiobus_register(mdio, np, owner);
12262306a36Sopenharmony_ci	if (ret) {
12362306a36Sopenharmony_ci		devres_free(dr);
12462306a36Sopenharmony_ci		return ret;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	dr->mii = mdio;
12862306a36Sopenharmony_ci	devres_add(dev, dr);
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ciEXPORT_SYMBOL(__devm_of_mdiobus_register);
13262306a36Sopenharmony_ci#endif /* CONFIG_OF_MDIO */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
135