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