1 /*-
2 * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27
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/phy/phy.h>
44 #include <dev/phy/phy_internal.h>
45
46 #ifdef FDT
47 #include "phydev_if.h"
48 #endif
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 * Set phy mode (protocol and its variant).
274 */
275 int
phynode_set_mode(struct phynode * phynode,phy_mode_t mode,phy_submode_t submode)276 phynode_set_mode(struct phynode *phynode, phy_mode_t mode,
277 phy_submode_t submode)
278 {
279 int rv;
280
281 PHY_TOPO_ASSERT();
282
283 PHYNODE_XLOCK(phynode);
284 rv = PHYNODE_SET_MODE(phynode, mode, submode);
285 PHYNODE_UNLOCK(phynode);
286 return (rv);
287 }
288
289 /*
290 * Get phy status. (PHY_STATUS_*)
291 */
292 int
phynode_status(struct phynode * phynode,int * status)293 phynode_status(struct phynode *phynode, int *status)
294 {
295 int rv;
296
297 PHY_TOPO_ASSERT();
298
299 PHYNODE_XLOCK(phynode);
300 rv = PHYNODE_STATUS(phynode, status);
301 PHYNODE_UNLOCK(phynode);
302 return (rv);
303 }
304
305 /* --------------------------------------------------------------------------
306 *
307 * Phy consumers interface.
308 *
309 */
310
311 /* Helper function for phy_get*() */
312 static phy_t
phy_create(struct phynode * phynode,device_t cdev)313 phy_create(struct phynode *phynode, device_t cdev)
314 {
315 struct phy *phy;
316
317 PHY_TOPO_ASSERT();
318
319 phy = malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO);
320 phy->cdev = cdev;
321 phy->phynode = phynode;
322 phy->enable_cnt = 0;
323
324 PHYNODE_XLOCK(phynode);
325 phynode->ref_cnt++;
326 TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link);
327 PHYNODE_UNLOCK(phynode);
328
329 return (phy);
330 }
331
332 int
phy_enable(phy_t phy)333 phy_enable(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
342 PHY_TOPO_SLOCK();
343 rv = phynode_enable(phynode);
344 if (rv == 0)
345 phy->enable_cnt++;
346 PHY_TOPO_UNLOCK();
347 return (rv);
348 }
349
350 int
phy_disable(phy_t phy)351 phy_disable(phy_t phy)
352 {
353 int rv;
354 struct phynode *phynode;
355
356 phynode = phy->phynode;
357 KASSERT(phynode->ref_cnt > 0,
358 ("Attempt to access unreferenced phy.\n"));
359 KASSERT(phy->enable_cnt > 0,
360 ("Attempt to disable already disabled phy.\n"));
361
362 PHY_TOPO_SLOCK();
363 rv = phynode_disable(phynode);
364 if (rv == 0)
365 phy->enable_cnt--;
366 PHY_TOPO_UNLOCK();
367 return (rv);
368 }
369
370 int
phy_set_mode(phy_t phy,phy_mode_t mode,phy_submode_t submode)371 phy_set_mode(phy_t phy, phy_mode_t mode, phy_submode_t submode)
372 {
373 int rv;
374 struct phynode *phynode;
375
376 phynode = phy->phynode;
377 KASSERT(phynode->ref_cnt > 0,
378 ("Attempt to access unreferenced phy.\n"));
379
380 PHY_TOPO_SLOCK();
381 rv = phynode_set_mode(phynode, mode, submode);
382 PHY_TOPO_UNLOCK();
383 return (rv);
384 }
385
386 int
phy_status(phy_t phy,int * status)387 phy_status(phy_t phy, int *status)
388 {
389 int rv;
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 rv = phynode_status(phynode, status);
398 PHY_TOPO_UNLOCK();
399 return (rv);
400 }
401
402 int
phy_get_by_id(device_t consumer_dev,device_t provider_dev,intptr_t id,phy_t * phy)403 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id,
404 phy_t *phy)
405 {
406 struct phynode *phynode;
407
408 PHY_TOPO_SLOCK();
409
410 phynode = phynode_find_by_id(provider_dev, id);
411 if (phynode == NULL) {
412 PHY_TOPO_UNLOCK();
413 return (ENODEV);
414 }
415 *phy = phy_create(phynode, consumer_dev);
416 PHY_TOPO_UNLOCK();
417
418 return (0);
419 }
420
421 void
phy_release(phy_t phy)422 phy_release(phy_t phy)
423 {
424 struct phynode *phynode;
425
426 phynode = phy->phynode;
427 KASSERT(phynode->ref_cnt > 0,
428 ("Attempt to access unreferenced phy.\n"));
429
430 PHY_TOPO_SLOCK();
431 while (phy->enable_cnt > 0) {
432 phynode_disable(phynode);
433 phy->enable_cnt--;
434 }
435 PHYNODE_XLOCK(phynode);
436 TAILQ_REMOVE(&phynode->consumers_list, phy, link);
437 phynode->ref_cnt--;
438 PHYNODE_UNLOCK(phynode);
439 PHY_TOPO_UNLOCK();
440
441 free(phy, M_PHY);
442 }
443
444 #ifdef FDT
phydev_default_ofw_map(device_t provider,phandle_t xref,int ncells,pcell_t * cells,intptr_t * id)445 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells,
446 pcell_t *cells, intptr_t *id)
447 {
448 struct phynode *entry;
449 phandle_t node;
450
451 /* Single device can register multiple subnodes. */
452 if (ncells == 0) {
453
454 node = OF_node_from_xref(xref);
455 PHY_TOPO_XLOCK();
456 TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
457 if ((entry->pdev == provider) &&
458 (entry->ofw_node == node)) {
459 *id = entry->id;
460 PHY_TOPO_UNLOCK();
461 return (0);
462 }
463 }
464 PHY_TOPO_UNLOCK();
465 return (ERANGE);
466 }
467
468 /* First cell is ID. */
469 if (ncells == 1) {
470 *id = cells[0];
471 return (0);
472 }
473
474 /* No default way how to get ID, custom mapper is required. */
475 return (ERANGE);
476 }
477
478 int
phy_get_by_ofw_idx(device_t consumer_dev,phandle_t cnode,int idx,phy_t * phy)479 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy)
480 {
481 phandle_t xnode;
482 pcell_t *cells;
483 device_t phydev;
484 int ncells, rv;
485 intptr_t id;
486
487 if (cnode <= 0)
488 cnode = ofw_bus_get_node(consumer_dev);
489 if (cnode <= 0) {
490 device_printf(consumer_dev,
491 "%s called on not ofw based device\n", __func__);
492 return (ENXIO);
493 }
494 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx,
495 &xnode, &ncells, &cells);
496 if (rv != 0)
497 return (rv);
498
499 /* Tranlate provider to device. */
500 phydev = OF_device_from_xref(xnode);
501 if (phydev == NULL) {
502 OF_prop_free(cells);
503 return (ENODEV);
504 }
505 /* Map phy to number. */
506 rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id);
507 OF_prop_free(cells);
508 if (rv != 0)
509 return (rv);
510
511 return (phy_get_by_id(consumer_dev, phydev, id, phy));
512 }
513
514 int
phy_get_by_ofw_name(device_t consumer_dev,phandle_t cnode,char * name,phy_t * phy)515 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name,
516 phy_t *phy)
517 {
518 int rv, idx;
519
520 if (cnode <= 0)
521 cnode = ofw_bus_get_node(consumer_dev);
522 if (cnode <= 0) {
523 device_printf(consumer_dev,
524 "%s called on not ofw based device\n", __func__);
525 return (ENXIO);
526 }
527 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx);
528 if (rv != 0)
529 return (rv);
530 return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy));
531 }
532
533 int
phy_get_by_ofw_property(device_t consumer_dev,phandle_t cnode,char * name,phy_t * phy)534 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name,
535 phy_t *phy)
536 {
537 pcell_t *cells;
538 device_t phydev;
539 int ncells, rv;
540 intptr_t id;
541
542 if (cnode <= 0)
543 cnode = ofw_bus_get_node(consumer_dev);
544 if (cnode <= 0) {
545 device_printf(consumer_dev,
546 "%s called on not ofw based device\n", __func__);
547 return (ENXIO);
548 }
549 ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t),
550 (void **)&cells);
551 if (ncells < 1)
552 return (ENOENT);
553
554 /* Tranlate provider to device. */
555 phydev = OF_device_from_xref(cells[0]);
556 if (phydev == NULL) {
557 OF_prop_free(cells);
558 return (ENODEV);
559 }
560 /* Map phy to number. */
561 rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id);
562 OF_prop_free(cells);
563 if (rv != 0)
564 return (rv);
565
566 return (phy_get_by_id(consumer_dev, phydev, id, phy));
567 }
568 #endif
569