2 * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include "opt_platform.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/queue.h>
37 #include <sys/systm.h>
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
45 #include <dev/extres/phy/phy.h>
47 #include "phydev_if.h"
49 MALLOC_DEFINE(M_PHY, "phy", "Phy framework");
51 /* Forward declarations. */
55 typedef TAILQ_HEAD(phynode_list, phynode) phynode_list_t;
56 typedef TAILQ_HEAD(phy_list, phy) phy_list_t;
58 /* Default phy methods. */
59 static int phynode_method_init(struct phynode *phynode);
60 static int phynode_method_enable(struct phynode *phynode, bool disable);
61 static int phynode_method_status(struct phynode *phynode, int *status);
65 * Phy controller methods.
67 static phynode_method_t phynode_methods[] = {
68 PHYNODEMETHOD(phynode_init, phynode_method_init),
69 PHYNODEMETHOD(phynode_enable, phynode_method_enable),
70 PHYNODEMETHOD(phynode_status, phynode_method_status),
74 DEFINE_CLASS_0(phynode, phynode_class, phynode_methods, 0);
82 TAILQ_ENTRY(phynode) phylist_link; /* Global list entry */
83 phy_list_t consumers_list; /* Consumers list */
86 /* Details of this device. */
87 const char *name; /* Globally unique name */
89 device_t pdev; /* Producer device_t */
90 void *softc; /* Producer softc */
91 intptr_t id; /* Per producer unique id */
93 phandle_t ofw_node; /* OFW node of phy */
95 struct sx lock; /* Lock for this phy */
96 int ref_cnt; /* Reference counter */
97 int enable_cnt; /* Enabled counter */
101 device_t cdev; /* consumer device*/
102 struct phynode *phynode;
103 TAILQ_ENTRY(phy) link; /* Consumers list entry */
108 static phynode_list_t phynode_list = TAILQ_HEAD_INITIALIZER(phynode_list);
110 static struct sx phynode_topo_lock;
111 SX_SYSINIT(phy_topology, &phynode_topo_lock, "Phy topology lock");
113 #define PHY_TOPO_SLOCK() sx_slock(&phynode_topo_lock)
114 #define PHY_TOPO_XLOCK() sx_xlock(&phynode_topo_lock)
115 #define PHY_TOPO_UNLOCK() sx_unlock(&phynode_topo_lock)
116 #define PHY_TOPO_ASSERT() sx_assert(&phynode_topo_lock, SA_LOCKED)
117 #define PHY_TOPO_XASSERT() sx_assert(&phynode_topo_lock, SA_XLOCKED)
119 #define PHYNODE_SLOCK(_sc) sx_slock(&((_sc)->lock))
120 #define PHYNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock))
121 #define PHYNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock))
123 /* ----------------------------------------------------------------------------
125 * Default phy methods for base class.
130 phynode_method_init(struct phynode *phynode)
137 phynode_method_enable(struct phynode *phynode, bool enable)
147 phynode_method_status(struct phynode *phynode, int *status)
149 *status = PHY_STATUS_ENABLED;
153 /* ----------------------------------------------------------------------------
155 * Internal functions.
159 * Create and initialize phy object, but do not register it.
162 phynode_create(device_t pdev, phynode_class_t phynode_class,
163 struct phynode_init_def *def)
165 struct phynode *phynode;
168 /* Create object and initialize it. */
169 phynode = malloc(sizeof(struct phynode), M_PHY, M_WAITOK | M_ZERO);
170 kobj_init((kobj_t)phynode, (kobj_class_t)phynode_class);
171 sx_init(&phynode->lock, "Phy node lock");
173 /* Allocate softc if required. */
174 if (phynode_class->size > 0) {
175 phynode->softc = malloc(phynode_class->size, M_PHY,
180 TAILQ_INIT(&phynode->consumers_list);
181 phynode->id = def->id;
182 phynode->pdev = pdev;
184 phynode->ofw_node = def->ofw_node;
190 /* Register phy object. */
192 phynode_register(struct phynode *phynode)
197 if (phynode->ofw_node <= 0)
198 phynode->ofw_node = ofw_bus_get_node(phynode->pdev);
199 if (phynode->ofw_node <= 0)
203 rv = PHYNODE_INIT(phynode);
205 printf("PHYNODE_INIT failed: %d\n", rv);
210 TAILQ_INSERT_TAIL(&phynode_list, phynode, phylist_link);
213 OF_device_register_xref(OF_xref_from_node(phynode->ofw_node),
219 static struct phynode *
220 phynode_find_by_id(device_t dev, intptr_t id)
222 struct phynode *entry;
226 TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
227 if ((entry->pdev == dev) && (entry->id == id))
234 /* --------------------------------------------------------------------------
236 * Phy providers interface
241 phynode_get_softc(struct phynode *phynode)
244 return (phynode->softc);
248 phynode_get_device(struct phynode *phynode)
251 return (phynode->pdev);
254 intptr_t phynode_get_id(struct phynode *phynode)
257 return (phynode->id);
262 phynode_get_ofw_node(struct phynode *phynode)
265 return (phynode->ofw_node);
269 /* --------------------------------------------------------------------------
271 * Real consumers executive
279 phynode_enable(struct phynode *phynode)
285 PHYNODE_XLOCK(phynode);
286 if (phynode->enable_cnt == 0) {
287 rv = PHYNODE_ENABLE(phynode, true);
289 PHYNODE_UNLOCK(phynode);
293 phynode->enable_cnt++;
294 PHYNODE_UNLOCK(phynode);
302 phynode_disable(struct phynode *phynode)
308 PHYNODE_XLOCK(phynode);
309 if (phynode->enable_cnt == 1) {
310 rv = PHYNODE_ENABLE(phynode, false);
312 PHYNODE_UNLOCK(phynode);
316 phynode->enable_cnt--;
317 PHYNODE_UNLOCK(phynode);
323 * Get phy status. (PHY_STATUS_*)
326 phynode_status(struct phynode *phynode, int *status)
332 PHYNODE_XLOCK(phynode);
333 rv = PHYNODE_STATUS(phynode, status);
334 PHYNODE_UNLOCK(phynode);
338 /* --------------------------------------------------------------------------
340 * Phy consumers interface.
344 /* Helper function for phy_get*() */
346 phy_create(struct phynode *phynode, device_t cdev)
352 phy = malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO);
354 phy->phynode = phynode;
357 PHYNODE_XLOCK(phynode);
359 TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link);
360 PHYNODE_UNLOCK(phynode);
366 phy_enable(phy_t phy)
369 struct phynode *phynode;
371 phynode = phy->phynode;
372 KASSERT(phynode->ref_cnt > 0,
373 ("Attempt to access unreferenced phy.\n"));
376 rv = phynode_enable(phynode);
384 phy_disable(phy_t phy)
387 struct phynode *phynode;
389 phynode = phy->phynode;
390 KASSERT(phynode->ref_cnt > 0,
391 ("Attempt to access unreferenced phy.\n"));
392 KASSERT(phy->enable_cnt > 0,
393 ("Attempt to disable already disabled phy.\n"));
396 rv = phynode_disable(phynode);
404 phy_status(phy_t phy, int *status)
407 struct phynode *phynode;
409 phynode = phy->phynode;
410 KASSERT(phynode->ref_cnt > 0,
411 ("Attempt to access unreferenced phy.\n"));
414 rv = phynode_status(phynode, status);
420 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id,
423 struct phynode *phynode;
427 phynode = phynode_find_by_id(provider_dev, id);
428 if (phynode == NULL) {
432 *phy = phy_create(phynode, consumer_dev);
439 phy_release(phy_t phy)
441 struct phynode *phynode;
443 phynode = phy->phynode;
444 KASSERT(phynode->ref_cnt > 0,
445 ("Attempt to access unreferenced phy.\n"));
448 while (phy->enable_cnt > 0) {
449 phynode_disable(phynode);
452 PHYNODE_XLOCK(phynode);
453 TAILQ_REMOVE(&phynode->consumers_list, phy, link);
455 PHYNODE_UNLOCK(phynode);
462 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells,
463 pcell_t *cells, intptr_t *id)
465 struct phynode *entry;
468 /* Single device can register multiple subnodes. */
471 node = OF_node_from_xref(xref);
473 TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
474 if ((entry->pdev == provider) &&
475 (entry->ofw_node == node)) {
485 /* First cell is ID. */
491 /* No default way how to get ID, custom mapper is required. */
496 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy)
505 cnode = ofw_bus_get_node(consumer_dev);
507 device_printf(consumer_dev,
508 "%s called on not ofw based device\n", __func__);
511 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx,
512 &xnode, &ncells, &cells);
516 /* Tranlate provider to device. */
517 phydev = OF_device_from_xref(xnode);
518 if (phydev == NULL) {
522 /* Map phy to number. */
523 rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id);
528 return (phy_get_by_id(consumer_dev, phydev, id, phy));
532 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name,
538 cnode = ofw_bus_get_node(consumer_dev);
540 device_printf(consumer_dev,
541 "%s called on not ofw based device\n", __func__);
544 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx);
547 return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy));
551 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name,
560 cnode = ofw_bus_get_node(consumer_dev);
562 device_printf(consumer_dev,
563 "%s called on not ofw based device\n", __func__);
566 ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t),
571 /* Tranlate provider to device. */
572 phydev = OF_device_from_xref(cells[0]);
573 if (phydev == NULL) {
577 /* Map phy to number. */
578 rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id);
583 return (phy_get_by_id(consumer_dev, phydev, id, phy));