The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/extres/phy/phy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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/extres/phy/phy.h>
   44 #include  <dev/extres/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
   81 phynode_method_init(struct phynode *phynode)
   82 {
   83 
   84         return (0);
   85 }
   86 
   87 static int
   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
   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 *
  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 *
  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 *
  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 *
  192 phynode_get_softc(struct phynode *phynode)
  193 {
  194 
  195         return (phynode->softc);
  196 }
  197 
  198 device_t
  199 phynode_get_device(struct phynode *phynode)
  200 {
  201 
  202         return (phynode->pdev);
  203 }
  204 
  205 intptr_t phynode_get_id(struct phynode *phynode)
  206 {
  207 
  208         return (phynode->id);
  209 }
  210 
  211 #ifdef FDT
  212 phandle_t
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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

Cache object: 8630c89da3b29c0ee2fb444af98fc4a9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.