summaryrefslogtreecommitdiff
path: root/drivers/net/phy/mdio_devres.c
blob: 7fd3377dbd7960adb2af9f19999b1a940b55fc33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-License-Identifier: GPL-2.0-or-later

#include <linux/device.h>
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/stddef.h>

struct mdiobus_devres {
	struct mii_bus *mii;
};

static void devm_mdiobus_free(struct device *dev, void *this)
{
	struct mdiobus_devres *dr = this;

	mdiobus_free(dr->mii);
}

/**
 * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size()
 * @dev:		Device to allocate mii_bus for
 * @sizeof_priv:	Space to allocate for private structure
 *
 * Managed mdiobus_alloc_size. mii_bus allocated with this function is
 * automatically freed on driver detach.
 *
 * RETURNS:
 * Pointer to allocated mii_bus on success, NULL on out-of-memory error.
 */
struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
{
	struct mdiobus_devres *dr;

	dr = devres_alloc(devm_mdiobus_free, sizeof(*dr), GFP_KERNEL);
	if (!dr)
		return NULL;

	dr->mii = mdiobus_alloc_size(sizeof_priv);
	if (!dr->mii) {
		devres_free(dr);
		return NULL;
	}

	devres_add(dev, dr);
	return dr->mii;
}
EXPORT_SYMBOL(devm_mdiobus_alloc_size);

static void devm_mdiobus_unregister(struct device *dev, void *this)
{
	struct mdiobus_devres *dr = this;

	mdiobus_unregister(dr->mii);
}

static int mdiobus_devres_match(struct device *dev,
				void *this, void *match_data)
{
	struct mdiobus_devres *res = this;
	struct mii_bus *mii = match_data;

	return mii == res->mii;
}

/**
 * __devm_mdiobus_register - Resource-managed variant of mdiobus_register()
 * @dev:	Device to register mii_bus for
 * @bus:	MII bus structure to register
 * @owner:	Owning module
 *
 * Returns 0 on success, negative error number on failure.
 */
int __devm_mdiobus_register(struct device *dev, struct mii_bus *bus,
			    struct module *owner)
{
	struct mdiobus_devres *dr;
	int ret;

	if (WARN_ON(!devres_find(dev, devm_mdiobus_free,
				 mdiobus_devres_match, bus)))
		return -EINVAL;

	dr = devres_alloc(devm_mdiobus_unregister, sizeof(*dr), GFP_KERNEL);
	if (!dr)
		return -ENOMEM;

	ret = __mdiobus_register(bus, owner);
	if (ret) {
		devres_free(dr);
		return ret;
	}

	dr->mii = bus;
	devres_add(dev, dr);
	return 0;
}
EXPORT_SYMBOL(__devm_mdiobus_register);

#if IS_ENABLED(CONFIG_OF_MDIO)
/**
 * __devm_of_mdiobus_register - Resource managed variant of of_mdiobus_register()
 * @dev:	Device to register mii_bus for
 * @mdio:	MII bus structure to register
 * @np:		Device node to parse
 * @owner:	Owning module
 */
int __devm_of_mdiobus_register(struct device *dev, struct mii_bus *mdio,
			       struct device_node *np, struct module *owner)
{
	struct mdiobus_devres *dr;
	int ret;

	if (WARN_ON(!devres_find(dev, devm_mdiobus_free,
				 mdiobus_devres_match, mdio)))
		return -EINVAL;

	dr = devres_alloc(devm_mdiobus_unregister, sizeof(*dr), GFP_KERNEL);
	if (!dr)
		return -ENOMEM;

	ret = __of_mdiobus_register(mdio, np, owner);
	if (ret) {
		devres_free(dr);
		return ret;
	}

	dr->mii = mdio;
	devres_add(dev, dr);
	return 0;
}
EXPORT_SYMBOL(__devm_of_mdiobus_register);
#endif /* CONFIG_OF_MDIO */

MODULE_DESCRIPTION("Network MDIO bus devres helpers");
MODULE_LICENSE("GPL");