xref: /linux/drivers/net/phy/phy_package.c (revision dc5a6164fedacdcdbb51a2a340ca264191a5b82c)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * PHY package support
4  */
5 
6 #include <linux/of.h>
7 #include <linux/phy.h>
8 
9 #include "phylib.h"
10 
11 struct device_node *phy_package_get_node(struct phy_device *phydev)
12 {
13 	return phydev->shared->np;
14 }
15 EXPORT_SYMBOL_GPL(phy_package_get_node);
16 
17 void *phy_package_get_priv(struct phy_device *phydev)
18 {
19 	return phydev->shared->priv;
20 }
21 EXPORT_SYMBOL_GPL(phy_package_get_priv);
22 
23 /**
24  * phy_package_join - join a common PHY group
25  * @phydev: target phy_device struct
26  * @base_addr: cookie and base PHY address of PHY package for offset
27  *   calculation of global register access
28  * @priv_size: if non-zero allocate this amount of bytes for private data
29  *
30  * This joins a PHY group and provides a shared storage for all phydevs in
31  * this group. This is intended to be used for packages which contain
32  * more than one PHY, for example a quad PHY transceiver.
33  *
34  * The base_addr parameter serves as cookie which has to have the same values
35  * for all members of one group and as the base PHY address of the PHY package
36  * for offset calculation to access generic registers of a PHY package.
37  * Usually, one of the PHY addresses of the different PHYs in the package
38  * provides access to these global registers.
39  * The address which is given here, will be used in the phy_package_read()
40  * and phy_package_write() convenience functions as base and added to the
41  * passed offset in those functions.
42  *
43  * This will set the shared pointer of the phydev to the shared storage.
44  * If this is the first call for a this cookie the shared storage will be
45  * allocated. If priv_size is non-zero, the given amount of bytes are
46  * allocated for the priv member.
47  *
48  * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
49  * with the same cookie but a different priv_size is an error.
50  */
51 int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
52 {
53 	struct mii_bus *bus = phydev->mdio.bus;
54 	struct phy_package_shared *shared;
55 	int ret;
56 
57 	if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
58 		return -EINVAL;
59 
60 	mutex_lock(&bus->shared_lock);
61 	shared = bus->shared[base_addr];
62 	if (!shared) {
63 		ret = -ENOMEM;
64 		shared = kzalloc(sizeof(*shared), GFP_KERNEL);
65 		if (!shared)
66 			goto err_unlock;
67 		if (priv_size) {
68 			shared->priv = kzalloc(priv_size, GFP_KERNEL);
69 			if (!shared->priv)
70 				goto err_free;
71 			shared->priv_size = priv_size;
72 		}
73 		shared->base_addr = base_addr;
74 		shared->np = NULL;
75 		refcount_set(&shared->refcnt, 1);
76 		bus->shared[base_addr] = shared;
77 	} else {
78 		ret = -EINVAL;
79 		if (priv_size && priv_size != shared->priv_size)
80 			goto err_unlock;
81 		refcount_inc(&shared->refcnt);
82 	}
83 	mutex_unlock(&bus->shared_lock);
84 
85 	phydev->shared = shared;
86 
87 	return 0;
88 
89 err_free:
90 	kfree(shared);
91 err_unlock:
92 	mutex_unlock(&bus->shared_lock);
93 	return ret;
94 }
95 EXPORT_SYMBOL_GPL(phy_package_join);
96 
97 /**
98  * of_phy_package_join - join a common PHY group in PHY package
99  * @phydev: target phy_device struct
100  * @priv_size: if non-zero allocate this amount of bytes for private data
101  *
102  * This is a variant of phy_package_join for PHY package defined in DT.
103  *
104  * The parent node of the @phydev is checked as a valid PHY package node
105  * structure (by matching the node name "ethernet-phy-package") and the
106  * base_addr for the PHY package is passed to phy_package_join.
107  *
108  * With this configuration the shared struct will also have the np value
109  * filled to use additional DT defined properties in PHY specific
110  * probe_once and config_init_once PHY package OPs.
111  *
112  * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
113  * with the same cookie but a different priv_size is an error. Or a parent
114  * node is not detected or is not valid or doesn't match the expected node
115  * name for PHY package.
116  */
117 int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
118 {
119 	struct device_node *node = phydev->mdio.dev.of_node;
120 	struct device_node *package_node;
121 	u32 base_addr;
122 	int ret;
123 
124 	if (!node)
125 		return -EINVAL;
126 
127 	package_node = of_get_parent(node);
128 	if (!package_node)
129 		return -EINVAL;
130 
131 	if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
132 		ret = -EINVAL;
133 		goto exit;
134 	}
135 
136 	if (of_property_read_u32(package_node, "reg", &base_addr)) {
137 		ret = -EINVAL;
138 		goto exit;
139 	}
140 
141 	ret = phy_package_join(phydev, base_addr, priv_size);
142 	if (ret)
143 		goto exit;
144 
145 	phydev->shared->np = package_node;
146 
147 	return 0;
148 exit:
149 	of_node_put(package_node);
150 	return ret;
151 }
152 EXPORT_SYMBOL_GPL(of_phy_package_join);
153 
154 /**
155  * phy_package_leave - leave a common PHY group
156  * @phydev: target phy_device struct
157  *
158  * This leaves a PHY group created by phy_package_join(). If this phydev
159  * was the last user of the shared data between the group, this data is
160  * freed. Resets the phydev->shared pointer to NULL.
161  */
162 void phy_package_leave(struct phy_device *phydev)
163 {
164 	struct phy_package_shared *shared = phydev->shared;
165 	struct mii_bus *bus = phydev->mdio.bus;
166 
167 	if (!shared)
168 		return;
169 
170 	/* Decrease the node refcount on leave if present */
171 	if (shared->np)
172 		of_node_put(shared->np);
173 
174 	if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
175 		bus->shared[shared->base_addr] = NULL;
176 		mutex_unlock(&bus->shared_lock);
177 		kfree(shared->priv);
178 		kfree(shared);
179 	}
180 
181 	phydev->shared = NULL;
182 }
183 EXPORT_SYMBOL_GPL(phy_package_leave);
184 
185 static void devm_phy_package_leave(struct device *dev, void *res)
186 {
187 	phy_package_leave(*(struct phy_device **)res);
188 }
189 
190 /**
191  * devm_phy_package_join - resource managed phy_package_join()
192  * @dev: device that is registering this PHY package
193  * @phydev: target phy_device struct
194  * @base_addr: cookie and base PHY address of PHY package for offset
195  *   calculation of global register access
196  * @priv_size: if non-zero allocate this amount of bytes for private data
197  *
198  * Managed phy_package_join(). Shared storage fetched by this function,
199  * phy_package_leave() is automatically called on driver detach. See
200  * phy_package_join() for more information.
201  */
202 int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
203 			  int base_addr, size_t priv_size)
204 {
205 	struct phy_device **ptr;
206 	int ret;
207 
208 	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
209 			   GFP_KERNEL);
210 	if (!ptr)
211 		return -ENOMEM;
212 
213 	ret = phy_package_join(phydev, base_addr, priv_size);
214 
215 	if (!ret) {
216 		*ptr = phydev;
217 		devres_add(dev, ptr);
218 	} else {
219 		devres_free(ptr);
220 	}
221 
222 	return ret;
223 }
224 EXPORT_SYMBOL_GPL(devm_phy_package_join);
225 
226 /**
227  * devm_of_phy_package_join - resource managed of_phy_package_join()
228  * @dev: device that is registering this PHY package
229  * @phydev: target phy_device struct
230  * @priv_size: if non-zero allocate this amount of bytes for private data
231  *
232  * Managed of_phy_package_join(). Shared storage fetched by this function,
233  * phy_package_leave() is automatically called on driver detach. See
234  * of_phy_package_join() for more information.
235  */
236 int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
237 			     size_t priv_size)
238 {
239 	struct phy_device **ptr;
240 	int ret;
241 
242 	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
243 			   GFP_KERNEL);
244 	if (!ptr)
245 		return -ENOMEM;
246 
247 	ret = of_phy_package_join(phydev, priv_size);
248 
249 	if (!ret) {
250 		*ptr = phydev;
251 		devres_add(dev, ptr);
252 	} else {
253 		devres_free(ptr);
254 	}
255 
256 	return ret;
257 }
258 EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
259