xref: /linux/drivers/net/phy/phy_package.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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 #include "phylib-internal.h"
11 
12 /**
13  * struct phy_package_shared - Shared information in PHY packages
14  * @base_addr: Base PHY address of PHY package used to combine PHYs
15  *   in one package and for offset calculation of phy_package_read/write
16  * @np: Pointer to the Device Node if PHY package defined in DT
17  * @refcnt: Number of PHYs connected to this shared data
18  * @flags: Initialization of PHY package
19  * @priv_size: Size of the shared private data @priv
20  * @priv: Driver private data shared across a PHY package
21  *
22  * Represents a shared structure between different phydev's in the same
23  * package, for example a quad PHY. See phy_package_join() and
24  * phy_package_leave().
25  */
26 struct phy_package_shared {
27 	u8 base_addr;
28 	/* With PHY package defined in DT this points to the PHY package node */
29 	struct device_node *np;
30 	refcount_t refcnt;
31 	unsigned long flags;
32 	size_t priv_size;
33 
34 	/* private data pointer */
35 	/* note that this pointer is shared between different phydevs and
36 	 * the user has to take care of appropriate locking. It is allocated
37 	 * and freed automatically by phy_package_join() and
38 	 * phy_package_leave().
39 	 */
40 	void *priv;
41 };
42 
phy_package_get_node(struct phy_device * phydev)43 struct device_node *phy_package_get_node(struct phy_device *phydev)
44 {
45 	return phydev->shared->np;
46 }
47 EXPORT_SYMBOL_GPL(phy_package_get_node);
48 
phy_package_get_priv(struct phy_device * phydev)49 void *phy_package_get_priv(struct phy_device *phydev)
50 {
51 	return phydev->shared->priv;
52 }
53 EXPORT_SYMBOL_GPL(phy_package_get_priv);
54 
phy_package_address(struct phy_device * phydev,unsigned int addr_offset)55 static int phy_package_address(struct phy_device *phydev,
56 			       unsigned int addr_offset)
57 {
58 	struct phy_package_shared *shared = phydev->shared;
59 	u8 base_addr = shared->base_addr;
60 
61 	if (addr_offset >= PHY_MAX_ADDR - base_addr)
62 		return -EIO;
63 
64 	/* we know that addr will be in the range 0..31 and thus the
65 	 * implicit cast to a signed int is not a problem.
66 	 */
67 	return base_addr + addr_offset;
68 }
69 
__phy_package_read(struct phy_device * phydev,unsigned int addr_offset,u32 regnum)70 int __phy_package_read(struct phy_device *phydev, unsigned int addr_offset,
71 		       u32 regnum)
72 {
73 	int addr = phy_package_address(phydev, addr_offset);
74 
75 	if (addr < 0)
76 		return addr;
77 
78 	return __mdiobus_read(phydev->mdio.bus, addr, regnum);
79 }
80 EXPORT_SYMBOL_GPL(__phy_package_read);
81 
__phy_package_write(struct phy_device * phydev,unsigned int addr_offset,u32 regnum,u16 val)82 int __phy_package_write(struct phy_device *phydev, unsigned int addr_offset,
83 			u32 regnum, u16 val)
84 {
85 	int addr = phy_package_address(phydev, addr_offset);
86 
87 	if (addr < 0)
88 		return addr;
89 
90 	return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
91 }
92 EXPORT_SYMBOL_GPL(__phy_package_write);
93 
94 /**
95  * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
96  * @phydev: The phy_device struct
97  * @addr_offset: The offset to be added to PHY package base_addr
98  * @devad: The MMD to read from
99  * @regnum: The register on the MMD to read
100  *
101  * Convenience helper for reading a register of an MMD on a given PHY
102  * using the PHY package base address. The base address is added to
103  * the addr_offset value.
104  *
105  * Same calling rules as for __phy_read();
106  *
107  * NOTE: It's assumed that the entire PHY package is either C22 or C45.
108  */
__phy_package_read_mmd(struct phy_device * phydev,unsigned int addr_offset,int devad,u32 regnum)109 int __phy_package_read_mmd(struct phy_device *phydev,
110 			   unsigned int addr_offset, int devad,
111 			   u32 regnum)
112 {
113 	int addr = phy_package_address(phydev, addr_offset);
114 
115 	if (addr < 0)
116 		return addr;
117 
118 	if (regnum > (u16)~0 || devad > 32)
119 		return -EINVAL;
120 
121 	return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
122 			    regnum);
123 }
124 EXPORT_SYMBOL(__phy_package_read_mmd);
125 
126 /**
127  * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
128  * @phydev: The phy_device struct
129  * @addr_offset: The offset to be added to PHY package base_addr
130  * @devad: The MMD to write to
131  * @regnum: The register on the MMD to write
132  * @val: value to write to @regnum
133  *
134  * Convenience helper for writing a register of an MMD on a given PHY
135  * using the PHY package base address. The base address is added to
136  * the addr_offset value.
137  *
138  * Same calling rules as for __phy_write();
139  *
140  * NOTE: It's assumed that the entire PHY package is either C22 or C45.
141  */
__phy_package_write_mmd(struct phy_device * phydev,unsigned int addr_offset,int devad,u32 regnum,u16 val)142 int __phy_package_write_mmd(struct phy_device *phydev,
143 			    unsigned int addr_offset, int devad,
144 			    u32 regnum, u16 val)
145 {
146 	int addr = phy_package_address(phydev, addr_offset);
147 
148 	if (addr < 0)
149 		return addr;
150 
151 	if (regnum > (u16)~0 || devad > 32)
152 		return -EINVAL;
153 
154 	return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
155 			     regnum, val);
156 }
157 EXPORT_SYMBOL(__phy_package_write_mmd);
158 
__phy_package_set_once(struct phy_device * phydev,unsigned int b)159 static bool __phy_package_set_once(struct phy_device *phydev, unsigned int b)
160 {
161 	struct phy_package_shared *shared = phydev->shared;
162 
163 	if (!shared)
164 		return false;
165 
166 	return !test_and_set_bit(b, &shared->flags);
167 }
168 
phy_package_init_once(struct phy_device * phydev)169 bool phy_package_init_once(struct phy_device *phydev)
170 {
171 	return __phy_package_set_once(phydev, 0);
172 }
173 EXPORT_SYMBOL_GPL(phy_package_init_once);
174 
phy_package_probe_once(struct phy_device * phydev)175 bool phy_package_probe_once(struct phy_device *phydev)
176 {
177 	return __phy_package_set_once(phydev, 1);
178 }
179 EXPORT_SYMBOL_GPL(phy_package_probe_once);
180 
181 /**
182  * phy_package_join - join a common PHY group
183  * @phydev: target phy_device struct
184  * @base_addr: cookie and base PHY address of PHY package for offset
185  *   calculation of global register access
186  * @priv_size: if non-zero allocate this amount of bytes for private data
187  *
188  * This joins a PHY group and provides a shared storage for all phydevs in
189  * this group. This is intended to be used for packages which contain
190  * more than one PHY, for example a quad PHY transceiver.
191  *
192  * The base_addr parameter serves as cookie which has to have the same values
193  * for all members of one group and as the base PHY address of the PHY package
194  * for offset calculation to access generic registers of a PHY package.
195  * Usually, one of the PHY addresses of the different PHYs in the package
196  * provides access to these global registers.
197  * The address which is given here, will be used in the phy_package_read()
198  * and phy_package_write() convenience functions as base and added to the
199  * passed offset in those functions.
200  *
201  * This will set the shared pointer of the phydev to the shared storage.
202  * If this is the first call for a this cookie the shared storage will be
203  * allocated. If priv_size is non-zero, the given amount of bytes are
204  * allocated for the priv member.
205  *
206  * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
207  * with the same cookie but a different priv_size is an error.
208  */
phy_package_join(struct phy_device * phydev,int base_addr,size_t priv_size)209 int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
210 {
211 	struct mii_bus *bus = phydev->mdio.bus;
212 	struct phy_package_shared *shared;
213 	int ret;
214 
215 	if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
216 		return -EINVAL;
217 
218 	mutex_lock(&bus->shared_lock);
219 	shared = bus->shared[base_addr];
220 	if (!shared) {
221 		ret = -ENOMEM;
222 		shared = kzalloc(sizeof(*shared), GFP_KERNEL);
223 		if (!shared)
224 			goto err_unlock;
225 		if (priv_size) {
226 			shared->priv = kzalloc(priv_size, GFP_KERNEL);
227 			if (!shared->priv)
228 				goto err_free;
229 			shared->priv_size = priv_size;
230 		}
231 		shared->base_addr = base_addr;
232 		shared->np = NULL;
233 		refcount_set(&shared->refcnt, 1);
234 		bus->shared[base_addr] = shared;
235 	} else {
236 		ret = -EINVAL;
237 		if (priv_size && priv_size != shared->priv_size)
238 			goto err_unlock;
239 		refcount_inc(&shared->refcnt);
240 	}
241 	mutex_unlock(&bus->shared_lock);
242 
243 	phydev->shared = shared;
244 
245 	return 0;
246 
247 err_free:
248 	kfree(shared);
249 err_unlock:
250 	mutex_unlock(&bus->shared_lock);
251 	return ret;
252 }
253 EXPORT_SYMBOL_GPL(phy_package_join);
254 
255 /**
256  * of_phy_package_join - join a common PHY group in PHY package
257  * @phydev: target phy_device struct
258  * @priv_size: if non-zero allocate this amount of bytes for private data
259  *
260  * This is a variant of phy_package_join for PHY package defined in DT.
261  *
262  * The parent node of the @phydev is checked as a valid PHY package node
263  * structure (by matching the node name "ethernet-phy-package") and the
264  * base_addr for the PHY package is passed to phy_package_join.
265  *
266  * With this configuration the shared struct will also have the np value
267  * filled to use additional DT defined properties in PHY specific
268  * probe_once and config_init_once PHY package OPs.
269  *
270  * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
271  * with the same cookie but a different priv_size is an error. Or a parent
272  * node is not detected or is not valid or doesn't match the expected node
273  * name for PHY package.
274  */
of_phy_package_join(struct phy_device * phydev,size_t priv_size)275 int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
276 {
277 	struct device_node *node = phydev->mdio.dev.of_node;
278 	struct device_node *package_node;
279 	u32 base_addr;
280 	int ret;
281 
282 	if (!node)
283 		return -EINVAL;
284 
285 	package_node = of_get_parent(node);
286 	if (!package_node)
287 		return -EINVAL;
288 
289 	if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
290 		ret = -EINVAL;
291 		goto exit;
292 	}
293 
294 	if (of_property_read_u32(package_node, "reg", &base_addr)) {
295 		ret = -EINVAL;
296 		goto exit;
297 	}
298 
299 	ret = phy_package_join(phydev, base_addr, priv_size);
300 	if (ret)
301 		goto exit;
302 
303 	phydev->shared->np = package_node;
304 
305 	return 0;
306 exit:
307 	of_node_put(package_node);
308 	return ret;
309 }
310 EXPORT_SYMBOL_GPL(of_phy_package_join);
311 
312 /**
313  * phy_package_leave - leave a common PHY group
314  * @phydev: target phy_device struct
315  *
316  * This leaves a PHY group created by phy_package_join(). If this phydev
317  * was the last user of the shared data between the group, this data is
318  * freed. Resets the phydev->shared pointer to NULL.
319  */
phy_package_leave(struct phy_device * phydev)320 void phy_package_leave(struct phy_device *phydev)
321 {
322 	struct phy_package_shared *shared = phydev->shared;
323 	struct mii_bus *bus = phydev->mdio.bus;
324 
325 	if (!shared)
326 		return;
327 
328 	/* Decrease the node refcount on leave if present */
329 	if (shared->np)
330 		of_node_put(shared->np);
331 
332 	if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
333 		bus->shared[shared->base_addr] = NULL;
334 		mutex_unlock(&bus->shared_lock);
335 		kfree(shared->priv);
336 		kfree(shared);
337 	}
338 
339 	phydev->shared = NULL;
340 }
341 EXPORT_SYMBOL_GPL(phy_package_leave);
342 
devm_phy_package_leave(struct device * dev,void * res)343 static void devm_phy_package_leave(struct device *dev, void *res)
344 {
345 	phy_package_leave(*(struct phy_device **)res);
346 }
347 
348 /**
349  * devm_phy_package_join - resource managed phy_package_join()
350  * @dev: device that is registering this PHY package
351  * @phydev: target phy_device struct
352  * @base_addr: cookie and base PHY address of PHY package for offset
353  *   calculation of global register access
354  * @priv_size: if non-zero allocate this amount of bytes for private data
355  *
356  * Managed phy_package_join(). Shared storage fetched by this function,
357  * phy_package_leave() is automatically called on driver detach. See
358  * phy_package_join() for more information.
359  */
devm_phy_package_join(struct device * dev,struct phy_device * phydev,int base_addr,size_t priv_size)360 int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
361 			  int base_addr, size_t priv_size)
362 {
363 	struct phy_device **ptr;
364 	int ret;
365 
366 	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
367 			   GFP_KERNEL);
368 	if (!ptr)
369 		return -ENOMEM;
370 
371 	ret = phy_package_join(phydev, base_addr, priv_size);
372 
373 	if (!ret) {
374 		*ptr = phydev;
375 		devres_add(dev, ptr);
376 	} else {
377 		devres_free(ptr);
378 	}
379 
380 	return ret;
381 }
382 EXPORT_SYMBOL_GPL(devm_phy_package_join);
383 
384 /**
385  * devm_of_phy_package_join - resource managed of_phy_package_join()
386  * @dev: device that is registering this PHY package
387  * @phydev: target phy_device struct
388  * @priv_size: if non-zero allocate this amount of bytes for private data
389  *
390  * Managed of_phy_package_join(). Shared storage fetched by this function,
391  * phy_package_leave() is automatically called on driver detach. See
392  * of_phy_package_join() for more information.
393  */
devm_of_phy_package_join(struct device * dev,struct phy_device * phydev,size_t priv_size)394 int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
395 			     size_t priv_size)
396 {
397 	struct phy_device **ptr;
398 	int ret;
399 
400 	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
401 			   GFP_KERNEL);
402 	if (!ptr)
403 		return -ENOMEM;
404 
405 	ret = of_phy_package_join(phydev, priv_size);
406 
407 	if (!ret) {
408 		*ptr = phydev;
409 		devres_add(dev, ptr);
410 	} else {
411 		devres_free(ptr);
412 	}
413 
414 	return ret;
415 }
416 EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
417 
418 MODULE_DESCRIPTION("PHY package support");
419 MODULE_LICENSE("GPL");
420