xref: /freebsd-13-stable/sys/dev/extres/phy/phy.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27  #include <sys/cdefs.h>
28 #include "opt_platform.h"
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/kobj.h>
32 #include <sys/lock.h>
33 #include <sys/malloc.h>
34 #include <sys/queue.h>
35 #include <sys/systm.h>
36 #include <sys/sx.h>
37 
38 #ifdef FDT
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41 #endif
42 
43 #include  <dev/extres/phy/phy.h>
44 #include  <dev/extres/phy/phy_internal.h>
45 
46 #include "phydev_if.h"
47 
48 MALLOC_DEFINE(M_PHY, "phy", "Phy framework");
49 
50 /* Default phy methods. */
51 static int phynode_method_init(struct phynode *phynode);
52 static int phynode_method_enable(struct phynode *phynode, bool disable);
53 static int phynode_method_status(struct phynode *phynode, int *status);
54 
55 
56 /*
57  * Phy controller methods.
58  */
59 static phynode_method_t phynode_methods[] = {
60 	PHYNODEMETHOD(phynode_init,		phynode_method_init),
61 	PHYNODEMETHOD(phynode_enable,		phynode_method_enable),
62 	PHYNODEMETHOD(phynode_status,		phynode_method_status),
63 
64 	PHYNODEMETHOD_END
65 };
66 DEFINE_CLASS_0(phynode, phynode_class, phynode_methods, 0);
67 
68 static phynode_list_t phynode_list = TAILQ_HEAD_INITIALIZER(phynode_list);
69 struct sx phynode_topo_lock;
70 SX_SYSINIT(phy_topology, &phynode_topo_lock, "Phy topology lock");
71 
72 /* ----------------------------------------------------------------------------
73  *
74  * Default phy methods for base class.
75  *
76  */
77 
78 static int
phynode_method_init(struct phynode * phynode)79 phynode_method_init(struct phynode *phynode)
80 {
81 
82 	return (0);
83 }
84 
85 static int
phynode_method_enable(struct phynode * phynode,bool enable)86 phynode_method_enable(struct phynode *phynode, bool enable)
87 {
88 
89 	if (!enable)
90 		return (ENXIO);
91 
92 	return (0);
93 }
94 
95 static int
phynode_method_status(struct phynode * phynode,int * status)96 phynode_method_status(struct phynode *phynode, int *status)
97 {
98 	*status = PHY_STATUS_ENABLED;
99 	return (0);
100 }
101 
102 /* ----------------------------------------------------------------------------
103  *
104  * Internal functions.
105  *
106  */
107 /*
108  * Create and initialize phy object, but do not register it.
109  */
110 struct phynode *
phynode_create(device_t pdev,phynode_class_t phynode_class,struct phynode_init_def * def)111 phynode_create(device_t pdev, phynode_class_t phynode_class,
112     struct phynode_init_def *def)
113 {
114 	struct phynode *phynode;
115 
116 
117 	/* Create object and initialize it. */
118 	phynode = malloc(sizeof(struct phynode), M_PHY, M_WAITOK | M_ZERO);
119 	kobj_init((kobj_t)phynode, (kobj_class_t)phynode_class);
120 	sx_init(&phynode->lock, "Phy node lock");
121 
122 	/* Allocate softc if required. */
123 	if (phynode_class->size > 0) {
124 		phynode->softc = malloc(phynode_class->size, M_PHY,
125 		    M_WAITOK | M_ZERO);
126 	}
127 
128 	/* Rest of init. */
129 	TAILQ_INIT(&phynode->consumers_list);
130 	phynode->id = def->id;
131 	phynode->pdev = pdev;
132 #ifdef FDT
133 	phynode->ofw_node = def->ofw_node;
134 #endif
135 
136 	return (phynode);
137 }
138 
139 /* Register phy object. */
140 struct phynode *
phynode_register(struct phynode * phynode)141 phynode_register(struct phynode *phynode)
142 {
143 	int rv;
144 
145 #ifdef FDT
146 	if (phynode->ofw_node <= 0)
147 		phynode->ofw_node = ofw_bus_get_node(phynode->pdev);
148 	if (phynode->ofw_node <= 0)
149 		return (NULL);
150 #endif
151 
152 	rv = PHYNODE_INIT(phynode);
153 	if (rv != 0) {
154 		printf("PHYNODE_INIT failed: %d\n", rv);
155 		return (NULL);
156 	}
157 
158 	PHY_TOPO_XLOCK();
159 	TAILQ_INSERT_TAIL(&phynode_list, phynode, phylist_link);
160 	PHY_TOPO_UNLOCK();
161 #ifdef FDT
162 	OF_device_register_xref(OF_xref_from_node(phynode->ofw_node),
163 	    phynode->pdev);
164 #endif
165 	return (phynode);
166 }
167 
168 static struct phynode *
phynode_find_by_id(device_t dev,intptr_t id)169 phynode_find_by_id(device_t dev, intptr_t id)
170 {
171 	struct phynode *entry;
172 
173 	PHY_TOPO_ASSERT();
174 
175 	TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
176 		if ((entry->pdev == dev) && (entry->id ==  id))
177 			return (entry);
178 	}
179 
180 	return (NULL);
181 }
182 
183 /* --------------------------------------------------------------------------
184  *
185  * Phy providers interface
186  *
187  */
188 
189 void *
phynode_get_softc(struct phynode * phynode)190 phynode_get_softc(struct phynode *phynode)
191 {
192 
193 	return (phynode->softc);
194 }
195 
196 device_t
phynode_get_device(struct phynode * phynode)197 phynode_get_device(struct phynode *phynode)
198 {
199 
200 	return (phynode->pdev);
201 }
202 
phynode_get_id(struct phynode * phynode)203 intptr_t phynode_get_id(struct phynode *phynode)
204 {
205 
206 	return (phynode->id);
207 }
208 
209 #ifdef FDT
210 phandle_t
phynode_get_ofw_node(struct phynode * phynode)211 phynode_get_ofw_node(struct phynode *phynode)
212 {
213 
214 	return (phynode->ofw_node);
215 }
216 #endif
217 
218 /* --------------------------------------------------------------------------
219  *
220  * Real consumers executive
221  *
222  */
223 
224 /*
225  * Enable phy.
226  */
227 int
phynode_enable(struct phynode * phynode)228 phynode_enable(struct phynode *phynode)
229 {
230 	int rv;
231 
232 	PHY_TOPO_ASSERT();
233 
234 	PHYNODE_XLOCK(phynode);
235 	if (phynode->enable_cnt == 0) {
236 		rv = PHYNODE_ENABLE(phynode, true);
237 		if (rv != 0) {
238 			PHYNODE_UNLOCK(phynode);
239 			return (rv);
240 		}
241 	}
242 	phynode->enable_cnt++;
243 	PHYNODE_UNLOCK(phynode);
244 	return (0);
245 }
246 
247 /*
248  * Disable phy.
249  */
250 int
phynode_disable(struct phynode * phynode)251 phynode_disable(struct phynode *phynode)
252 {
253 	int rv;
254 
255 	PHY_TOPO_ASSERT();
256 
257 	PHYNODE_XLOCK(phynode);
258 	if (phynode->enable_cnt == 1) {
259 		rv = PHYNODE_ENABLE(phynode, false);
260 		if (rv != 0) {
261 			PHYNODE_UNLOCK(phynode);
262 			return (rv);
263 		}
264 	}
265 	phynode->enable_cnt--;
266 	PHYNODE_UNLOCK(phynode);
267 	return (0);
268 }
269 
270 
271 /*
272  * Get phy status. (PHY_STATUS_*)
273  */
274 int
phynode_status(struct phynode * phynode,int * status)275 phynode_status(struct phynode *phynode, int *status)
276 {
277 	int rv;
278 
279 	PHY_TOPO_ASSERT();
280 
281 	PHYNODE_XLOCK(phynode);
282 	rv = PHYNODE_STATUS(phynode, status);
283 	PHYNODE_UNLOCK(phynode);
284 	return (rv);
285 }
286 
287  /* --------------------------------------------------------------------------
288  *
289  * Phy consumers interface.
290  *
291  */
292 
293 /* Helper function for phy_get*() */
294 static phy_t
phy_create(struct phynode * phynode,device_t cdev)295 phy_create(struct phynode *phynode, device_t cdev)
296 {
297 	struct phy *phy;
298 
299 	PHY_TOPO_ASSERT();
300 
301 	phy =  malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO);
302 	phy->cdev = cdev;
303 	phy->phynode = phynode;
304 	phy->enable_cnt = 0;
305 
306 	PHYNODE_XLOCK(phynode);
307 	phynode->ref_cnt++;
308 	TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link);
309 	PHYNODE_UNLOCK(phynode);
310 
311 	return (phy);
312 }
313 
314 int
phy_enable(phy_t phy)315 phy_enable(phy_t phy)
316 {
317 	int rv;
318 	struct phynode *phynode;
319 
320 	phynode = phy->phynode;
321 	KASSERT(phynode->ref_cnt > 0,
322 	    ("Attempt to access unreferenced phy.\n"));
323 
324 	PHY_TOPO_SLOCK();
325 	rv = phynode_enable(phynode);
326 	if (rv == 0)
327 		phy->enable_cnt++;
328 	PHY_TOPO_UNLOCK();
329 	return (rv);
330 }
331 
332 int
phy_disable(phy_t phy)333 phy_disable(phy_t phy)
334 {
335 	int rv;
336 	struct phynode *phynode;
337 
338 	phynode = phy->phynode;
339 	KASSERT(phynode->ref_cnt > 0,
340 	   ("Attempt to access unreferenced phy.\n"));
341 	KASSERT(phy->enable_cnt > 0,
342 	   ("Attempt to disable already disabled phy.\n"));
343 
344 	PHY_TOPO_SLOCK();
345 	rv = phynode_disable(phynode);
346 	if (rv == 0)
347 		phy->enable_cnt--;
348 	PHY_TOPO_UNLOCK();
349 	return (rv);
350 }
351 
352 int
phy_status(phy_t phy,int * status)353 phy_status(phy_t phy, int *status)
354 {
355 	int rv;
356 	struct phynode *phynode;
357 
358 	phynode = phy->phynode;
359 	KASSERT(phynode->ref_cnt > 0,
360 	   ("Attempt to access unreferenced phy.\n"));
361 
362 	PHY_TOPO_SLOCK();
363 	rv = phynode_status(phynode, status);
364 	PHY_TOPO_UNLOCK();
365 	return (rv);
366 }
367 
368 int
phy_get_by_id(device_t consumer_dev,device_t provider_dev,intptr_t id,phy_t * phy)369 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id,
370     phy_t *phy)
371 {
372 	struct phynode *phynode;
373 
374 	PHY_TOPO_SLOCK();
375 
376 	phynode = phynode_find_by_id(provider_dev, id);
377 	if (phynode == NULL) {
378 		PHY_TOPO_UNLOCK();
379 		return (ENODEV);
380 	}
381 	*phy = phy_create(phynode, consumer_dev);
382 	PHY_TOPO_UNLOCK();
383 
384 	return (0);
385 }
386 
387 void
phy_release(phy_t phy)388 phy_release(phy_t phy)
389 {
390 	struct phynode *phynode;
391 
392 	phynode = phy->phynode;
393 	KASSERT(phynode->ref_cnt > 0,
394 	   ("Attempt to access unreferenced phy.\n"));
395 
396 	PHY_TOPO_SLOCK();
397 	while (phy->enable_cnt > 0) {
398 		phynode_disable(phynode);
399 		phy->enable_cnt--;
400 	}
401 	PHYNODE_XLOCK(phynode);
402 	TAILQ_REMOVE(&phynode->consumers_list, phy, link);
403 	phynode->ref_cnt--;
404 	PHYNODE_UNLOCK(phynode);
405 	PHY_TOPO_UNLOCK();
406 
407 	free(phy, M_PHY);
408 }
409 
410 #ifdef FDT
phydev_default_ofw_map(device_t provider,phandle_t xref,int ncells,pcell_t * cells,intptr_t * id)411 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells,
412     pcell_t *cells, intptr_t *id)
413 {
414 	struct phynode *entry;
415 	phandle_t node;
416 
417 	/* Single device can register multiple subnodes. */
418 	if (ncells == 0) {
419 
420 		node = OF_node_from_xref(xref);
421 		PHY_TOPO_XLOCK();
422 		TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
423 			if ((entry->pdev == provider) &&
424 			    (entry->ofw_node == node)) {
425 				*id = entry->id;
426 				PHY_TOPO_UNLOCK();
427 				return (0);
428 			}
429 		}
430 		PHY_TOPO_UNLOCK();
431 		return (ERANGE);
432 	}
433 
434 	/* First cell is ID. */
435 	if (ncells == 1) {
436 		*id = cells[0];
437 		return (0);
438 	}
439 
440 	/* No default way how to get ID, custom mapper is required. */
441 	return  (ERANGE);
442 }
443 
444 int
phy_get_by_ofw_idx(device_t consumer_dev,phandle_t cnode,int idx,phy_t * phy)445 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy)
446 {
447 	phandle_t xnode;
448 	pcell_t *cells;
449 	device_t phydev;
450 	int ncells, rv;
451 	intptr_t id;
452 
453 	if (cnode <= 0)
454 		cnode = ofw_bus_get_node(consumer_dev);
455 	if (cnode <= 0) {
456 		device_printf(consumer_dev,
457 		    "%s called on not ofw based device\n", __func__);
458 		return (ENXIO);
459 	}
460 	rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx,
461 	    &xnode, &ncells, &cells);
462 	if (rv != 0)
463 		return (rv);
464 
465 	/* Tranlate provider to device. */
466 	phydev = OF_device_from_xref(xnode);
467 	if (phydev == NULL) {
468 		OF_prop_free(cells);
469 		return (ENODEV);
470 	}
471 	/* Map phy to number. */
472 	rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id);
473 	OF_prop_free(cells);
474 	if (rv != 0)
475 		return (rv);
476 
477 	return (phy_get_by_id(consumer_dev, phydev, id, phy));
478 }
479 
480 int
phy_get_by_ofw_name(device_t consumer_dev,phandle_t cnode,char * name,phy_t * phy)481 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name,
482     phy_t *phy)
483 {
484 	int rv, idx;
485 
486 	if (cnode <= 0)
487 		cnode = ofw_bus_get_node(consumer_dev);
488 	if (cnode <= 0) {
489 		device_printf(consumer_dev,
490 		    "%s called on not ofw based device\n",  __func__);
491 		return (ENXIO);
492 	}
493 	rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx);
494 	if (rv != 0)
495 		return (rv);
496 	return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy));
497 }
498 
499 int
phy_get_by_ofw_property(device_t consumer_dev,phandle_t cnode,char * name,phy_t * phy)500 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name,
501     phy_t *phy)
502 {
503 	pcell_t *cells;
504 	device_t phydev;
505 	int ncells, rv;
506 	intptr_t id;
507 
508 	if (cnode <= 0)
509 		cnode = ofw_bus_get_node(consumer_dev);
510 	if (cnode <= 0) {
511 		device_printf(consumer_dev,
512 		    "%s called on not ofw based device\n", __func__);
513 		return (ENXIO);
514 	}
515 	ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t),
516 	    (void **)&cells);
517 	if (ncells < 1)
518 		return (ENOENT);
519 
520 	/* Tranlate provider to device. */
521 	phydev = OF_device_from_xref(cells[0]);
522 	if (phydev == NULL) {
523 		OF_prop_free(cells);
524 		return (ENODEV);
525 	}
526 	/* Map phy to number. */
527 	rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id);
528 	OF_prop_free(cells);
529 	if (rv != 0)
530 		return (rv);
531 
532 	return (phy_get_by_id(consumer_dev, phydev, id, phy));
533 }
534 #endif
535