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