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/arm64/rockchip/rk_pcie_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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 /*
   29  * Rockchip PHY TYPEC
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/rman.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 #include <sys/mutex.h>
   42 #include <sys/gpio.h>
   43 #include <machine/bus.h>
   44 
   45 #include <dev/fdt/fdt_common.h>
   46 #include <dev/ofw/ofw_bus.h>
   47 #include <dev/ofw/ofw_bus_subr.h>
   48 #include <dev/ofw/ofw_subr.h>
   49 
   50 #include <dev/extres/clk/clk.h>
   51 #include <dev/extres/phy/phy.h>
   52 #include <dev/extres/phy/phy_internal.h>
   53 #include <dev/extres/syscon/syscon.h>
   54 #include <dev/extres/hwreset/hwreset.h>
   55 
   56 #include "syscon_if.h"
   57 
   58 #define GRF_HIWORD_SHIFT        16
   59 #define GRF_SOC_CON_5_PCIE      0xE214
   60 #define  CON_5_PCIE_IDLE_OFF(x) (1 <<(((x) & 0x3) + 3))
   61 #define GRF_SOC_CON8            0xE220
   62 #define GRF_SOC_STATUS1         0xE2A4
   63 
   64 /* PHY config registers  - write */
   65 #define PHY_CFG_CLK_TEST        0x10
   66 #define  CLK_TEST_SEPE_RATE             (1 << 3)
   67 #define PHY_CFG_CLK_SCC         0x12
   68 #define  CLK_SCC_PLL_100M               (1 << 3)
   69 
   70 /* PHY config registers  - read */
   71 #define PHY_CFG_PLL_LOCK        0x10
   72 #define  CLK_PLL_LOCKED                 (1 << 1)
   73 #define PHY_CFG_SCC_LOCK        0x12
   74 #define  CLK_SCC_100M_GATE              (1 << 2)
   75 
   76 #define  STATUS1_PLL_LOCKED             (1 << 9)
   77 
   78 static struct ofw_compat_data compat_data[] = {
   79         {"rockchip,rk3399-pcie-phy",    1},
   80         {NULL,                          0}
   81 };
   82 
   83 struct rk_pcie_phy_softc {
   84         device_t                dev;
   85         struct syscon           *syscon;
   86         struct mtx              mtx;
   87         clk_t                   clk_ref;
   88         hwreset_t               hwreset_phy;
   89         int                     enable_count;
   90 };
   91 
   92 #define PHY_LOCK(_sc)           mtx_lock(&(_sc)->mtx)
   93 #define PHY_UNLOCK(_sc)         mtx_unlock(&(_sc)->mtx)
   94 #define PHY_LOCK_INIT(_sc)      mtx_init(&(_sc)->mtx,                   \
   95             device_get_nameunit(_sc->dev), "rk_pcie_phyc", MTX_DEF)
   96 #define PHY_LOCK_DESTROY(_sc)   mtx_destroy(&(_sc)->mtx);
   97 #define PHY_ASSERT_LOCKED(_sc)  mtx_assert(&(_sc)->mtx, MA_OWNED);
   98 #define PHY_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
   99 
  100 #define RD4(sc, reg)            SYSCON_READ_4((sc)->syscon, (reg))
  101 #define WR4(sc, reg, mask, val)                                         \
  102     SYSCON_WRITE_4((sc)->syscon, (reg), ((mask) << GRF_HIWORD_SHIFT) | (val))
  103 
  104 #define MAX_LANE        4
  105 
  106 static void
  107 cfg_write(struct rk_pcie_phy_softc *sc, uint32_t reg, uint32_t data)
  108 {
  109         /* setup register address and data first */
  110         WR4(sc, GRF_SOC_CON8, 0x7FF,
  111             (reg & 0x3F) << 1 | (data & 0x0F) << 7);
  112         /* dummy readback for sync */
  113         RD4(sc, GRF_SOC_CON8);
  114 
  115         /* Do write pulse */
  116         WR4(sc, GRF_SOC_CON8, 1, 1);
  117         RD4(sc, GRF_SOC_CON8);
  118         DELAY(10);
  119         WR4(sc, GRF_SOC_CON8, 1, 0);
  120         RD4(sc, GRF_SOC_CON8);
  121         DELAY(10);
  122 }
  123 
  124 static uint32_t
  125 cfg_read(struct rk_pcie_phy_softc *sc, uint32_t reg)
  126 {
  127         uint32_t val;
  128 
  129         WR4(sc, GRF_SOC_CON8, 0x3FF, reg << 1);
  130         RD4(sc, GRF_SOC_CON8);
  131         DELAY(10);
  132         val = RD4(sc, GRF_SOC_STATUS1);
  133         return ((val >> 8) & 0x0f);
  134 }
  135 
  136 static int
  137 rk_pcie_phy_up(struct rk_pcie_phy_softc *sc, int id)
  138 {
  139         uint32_t val;
  140         int i, rv;
  141 
  142         PHY_LOCK(sc);
  143 
  144         sc->enable_count++;
  145         if (sc->enable_count != 1) {
  146                 PHY_UNLOCK(sc);
  147                 return (0);
  148         }
  149 
  150         rv = hwreset_deassert(sc->hwreset_phy);
  151         if (rv != 0) {
  152                 device_printf(sc->dev, "Cannot deassert 'phy' reset\n");
  153                 PHY_UNLOCK(sc);
  154                 return (rv);
  155         }
  156         /* Un-idle all lanes */
  157         for (i = 0; i < MAX_LANE; i++)
  158                 WR4(sc, GRF_SOC_CON_5_PCIE, CON_5_PCIE_IDLE_OFF(i), 0);
  159 
  160         /* Wait for PLL lock */
  161         for (i = 100; i > 0; i--) {
  162                 val = cfg_read(sc, PHY_CFG_PLL_LOCK);
  163                 if (val & CLK_PLL_LOCKED)
  164                         break;
  165                 DELAY(1000);
  166         }
  167         if (i <= 0) {
  168                 device_printf(sc->dev, "PLL lock timeouted, 0x%02X\n", val);
  169                 PHY_UNLOCK(sc);
  170                 return (ETIMEDOUT);
  171         }
  172         /* Switch PLL to stable 5GHz, rate adjustment is done by divider */
  173         cfg_write(sc, PHY_CFG_CLK_TEST, CLK_TEST_SEPE_RATE);
  174         /* Enable 100MHz output for PCIe ref clock */
  175         cfg_write(sc, PHY_CFG_CLK_SCC, CLK_SCC_PLL_100M);
  176 
  177         /* Wait for ungating of ref clock */
  178         for (i = 100; i > 0; i--) {
  179                 val = cfg_read(sc, PHY_CFG_SCC_LOCK);
  180                 if ((val & CLK_SCC_100M_GATE) == 0)
  181                         break;
  182                 DELAY(1000);
  183         }
  184         if (i <= 0) {
  185                 device_printf(sc->dev, "PLL output enable timeouted\n");
  186                 PHY_UNLOCK(sc);
  187                 return (ETIMEDOUT);
  188         }
  189 
  190         /* Wait for PLL relock (to 5GHz) */
  191         for (i = 100; i > 0; i--) {
  192                 val = cfg_read(sc, PHY_CFG_PLL_LOCK);
  193                 if (val & CLK_PLL_LOCKED)
  194                         break;
  195                 DELAY(1000);
  196         }
  197         if (i <= 0) {
  198                 device_printf(sc->dev, "PLL relock timeouted\n");
  199                 PHY_UNLOCK(sc);
  200                 return (ETIMEDOUT);
  201         }
  202 
  203         PHY_UNLOCK(sc);
  204         return (rv);
  205 }
  206 
  207 static int
  208 rk_pcie_phy_down(struct rk_pcie_phy_softc *sc, int id)
  209 {
  210         int rv;
  211 
  212         PHY_LOCK(sc);
  213 
  214         rv = 0;
  215         if (sc->enable_count <= 0)
  216                 panic("unpaired enable/disable");
  217 
  218         sc->enable_count--;
  219 
  220         /* Idle given lane */
  221         WR4(sc, GRF_SOC_CON_5_PCIE,
  222             CON_5_PCIE_IDLE_OFF(id),
  223             CON_5_PCIE_IDLE_OFF(id));
  224 
  225         if (sc->enable_count == 0) {
  226                 rv = hwreset_assert(sc->hwreset_phy);
  227                 if (rv != 0)
  228                         device_printf(sc->dev, "Cannot assert 'phy' reset\n");
  229         }
  230         PHY_UNLOCK(sc);
  231         return (rv);
  232 }
  233 
  234 static int
  235 rk_pcie_phy_enable(struct phynode *phynode, bool enable)
  236 {
  237         struct rk_pcie_phy_softc *sc;
  238         device_t dev;
  239         intptr_t phy;
  240         int rv;
  241 
  242         dev = phynode_get_device(phynode);
  243         phy = phynode_get_id(phynode);
  244         sc = device_get_softc(dev);
  245 
  246         if (enable)
  247                 rv = rk_pcie_phy_up(sc, (int)phy);
  248          else
  249                 rv = rk_pcie_phy_down(sc, (int) phy);
  250 
  251         return (rv);
  252 }
  253 
  254 /* Phy class and methods. */
  255 static phynode_method_t rk_pcie_phy_phynode_methods[] = {
  256         PHYNODEMETHOD(phynode_enable,            rk_pcie_phy_enable),
  257 
  258         PHYNODEMETHOD_END
  259 };
  260 
  261 DEFINE_CLASS_1( rk_pcie_phy_phynode, rk_pcie_phy_phynode_class,
  262     rk_pcie_phy_phynode_methods, 0, phynode_class);
  263 
  264 static int
  265  rk_pcie_phy_probe(device_t dev)
  266 {
  267 
  268         if (!ofw_bus_status_okay(dev))
  269                 return (ENXIO);
  270 
  271         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  272                 return (ENXIO);
  273 
  274         device_set_desc(dev, "Rockchip RK3399 PCIe PHY");
  275         return (BUS_PROBE_DEFAULT);
  276 }
  277 
  278 static int
  279  rk_pcie_phy_attach(device_t dev)
  280 {
  281         struct rk_pcie_phy_softc *sc;
  282         struct phynode_init_def phy_init;
  283         struct phynode *phynode;
  284         phandle_t node;
  285         int i, rv;
  286 
  287         sc = device_get_softc(dev);
  288         sc->dev = dev;
  289         node = ofw_bus_get_node(dev);
  290         PHY_LOCK_INIT(sc);
  291 
  292         if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 ||
  293             sc->syscon == NULL) {
  294                 device_printf(dev, "cannot get syscon for device\n");
  295                 rv = ENXIO;
  296                 goto fail;
  297         }
  298 
  299         rv = clk_set_assigned(dev, ofw_bus_get_node(dev));
  300         if (rv != 0 && rv != ENOENT) {
  301                 device_printf(dev, "clk_set_assigned failed: %d\n", rv);
  302                 rv = ENXIO;
  303                 goto fail;
  304         }
  305 
  306         rv = clk_get_by_ofw_name(sc->dev, 0, "refclk", &sc->clk_ref);
  307         if (rv != 0) {
  308                 device_printf(sc->dev, "Cannot get 'refclk' clock\n");
  309                 rv = ENXIO;
  310                 goto fail;
  311         }
  312         rv = hwreset_get_by_ofw_name(sc->dev, 0, "phy", &sc->hwreset_phy);
  313         if (rv != 0) {
  314                 device_printf(sc->dev, "Cannot get 'phy' reset\n");
  315                 rv = ENXIO;
  316                 goto fail;
  317         }
  318 
  319         rv = hwreset_assert(sc->hwreset_phy);
  320         if (rv != 0) {
  321                 device_printf(sc->dev, "Cannot assert 'phy' reset\n");
  322                 rv = ENXIO;
  323                 goto fail;
  324         }
  325 
  326         rv = clk_enable(sc->clk_ref);
  327         if (rv != 0) {
  328                 device_printf(sc->dev, "Cannot enable 'ref' clock\n");
  329                 rv = ENXIO;
  330                 goto fail;
  331         }
  332 
  333         for (i = 0; i < MAX_LANE; i++) {
  334                 phy_init.id = i;
  335                 phy_init.ofw_node = node;
  336                 phynode = phynode_create(dev, &rk_pcie_phy_phynode_class,
  337                 &phy_init);
  338                 if (phynode == NULL) {
  339                         device_printf(dev, "Cannot create phy[%d]\n", i);
  340                         rv = ENXIO;
  341                         goto fail;
  342                 }
  343                 if (phynode_register(phynode) == NULL) {
  344                         device_printf(dev, "Cannot register phy[%d]\n", i);
  345                         rv = ENXIO;
  346                         goto fail;
  347                 }
  348         }
  349 
  350         return (0);
  351 
  352 fail:
  353         return (rv);
  354 }
  355 
  356 static device_method_t rk_pcie_phy_methods[] = {
  357         /* Device interface */
  358         DEVMETHOD(device_probe,          rk_pcie_phy_probe),
  359         DEVMETHOD(device_attach,         rk_pcie_phy_attach),
  360 
  361         DEVMETHOD_END
  362 };
  363 
  364 DEFINE_CLASS_0(rk_pcie_phy, rk_pcie_phy_driver, rk_pcie_phy_methods,
  365     sizeof(struct rk_pcie_phy_softc));
  366 
  367 EARLY_DRIVER_MODULE(rk_pcie_phy, simplebus, rk_pcie_phy_driver, NULL, NULL,
  368     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);

Cache object: 6333e3973ca3adfd2ac36de8f3cffc01


[ 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.