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/rk3399_emmcphy.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 Ganbold Tsagaankhuu <ganbold@FreeBSD.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 /*
   30  * Rockchip RK3399 eMMC PHY
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/bus.h>
   39 #include <sys/rman.h>
   40 #include <sys/kernel.h>
   41 #include <sys/module.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 
   49 #include <dev/extres/clk/clk.h>
   50 #include <dev/extres/syscon/syscon.h>
   51 #include <dev/extres/phy/phy.h>
   52 
   53 #include "syscon_if.h"
   54 
   55 #define GRF_EMMCPHY_BASE        0xf780
   56 #define GRF_EMMCPHY_CON0        (GRF_EMMCPHY_BASE + 0x00)
   57 #define  PHYCTRL_FRQSEL         (1 << 13) | (1 << 12)
   58 #define   PHYCTRL_FRQSEL_200M   0
   59 #define   PHYCTRL_FRQSEL_50M    1
   60 #define   PHYCTRL_FRQSEL_100M   2
   61 #define   PHYCTRL_FRQSEL_150M   3
   62 #define  PHYCTRL_OTAPDLYENA     (1 << 11)
   63 #define  PHYCTRL_OTAPDLYSEL     (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7)
   64 #define  PHYCTRL_ITAPCHGWIN     (1 << 6)
   65 #define  PHYCTRL_ITAPDLYSEL     (1 << 5) | (1 << 4)  | (1 << 3) | (1 << 2) | \
   66     (1 << 1)
   67 #define  PHYCTRL_ITAPDLYENA     (1 << 0)
   68 #define GRF_EMMCPHY_CON1        (GRF_EMMCPHY_BASE + 0x04)
   69 #define  PHYCTRL_CLKBUFSEL      (1 << 8) | (1 << 7) | (1 << 6)
   70 #define  PHYCTRL_SELDLYTXCLK    (1 << 5)
   71 #define  PHYCTRL_SELDLYRXCLK    (1 << 4)
   72 #define  PHYCTRL_STRBSEL        0xf
   73 #define GRF_EMMCPHY_CON2        (GRF_EMMCPHY_BASE + 0x08)
   74 #define  PHYCTRL_REN_STRB       (1 << 9)
   75 #define  PHYCTRL_REN_CMD        (1 << 8)
   76 #define  PHYCTRL_REN_DAT        0xff
   77 #define GRF_EMMCPHY_CON3        (GRF_EMMCPHY_BASE + 0x0c)
   78 #define  PHYCTRL_PU_STRB        (1 << 9)
   79 #define  PHYCTRL_PU_CMD         (1 << 8)
   80 #define  PHYCTRL_PU_DAT         0xff
   81 #define GRF_EMMCPHY_CON4        (GRF_EMMCPHY_BASE + 0x10)
   82 #define  PHYCTRL_OD_RELEASE_CMD         (1 << 9)
   83 #define  PHYCTRL_OD_RELEASE_STRB        (1 << 8)
   84 #define  PHYCTRL_OD_RELEASE_DAT         0xff
   85 #define GRF_EMMCPHY_CON5        (GRF_EMMCPHY_BASE + 0x14)
   86 #define  PHYCTRL_ODEN_STRB      (1 << 9)
   87 #define  PHYCTRL_ODEN_CMD       (1 << 8)
   88 #define  PHYCTRL_ODEN_DAT       0xff
   89 #define GRF_EMMCPHY_CON6        (GRF_EMMCPHY_BASE + 0x18)
   90 #define  PHYCTRL_DLL_TRM_ICP    (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9)
   91 #define  PHYCTRL_EN_RTRIM       (1 << 8)
   92 #define  PHYCTRL_RETRIM         (1 << 7)
   93 #define  PHYCTRL_DR_TY          (1 << 6) | (1 << 5) | (1 << 4)
   94 #define  PHYCTRL_RETENB         (1 << 3)
   95 #define  PHYCTRL_RETEN          (1 << 2)
   96 #define  PHYCTRL_ENDLL          (1 << 1)
   97 #define  PHYCTRL_PDB            (1 << 0)
   98 #define GRF_EMMCPHY_STATUS      (GRF_EMMCPHY_BASE + 0x20)
   99 #define  PHYCTRL_CALDONE        (1 << 6)
  100 #define  PHYCTRL_DLLRDY         (1 << 5)
  101 #define  PHYCTRL_RTRIM          (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1)
  102 #define  PHYCTRL_EXR_NINST      (1 << 0)
  103 
  104 static struct ofw_compat_data compat_data[] = {
  105         { "rockchip,rk3399-emmc-phy",   1 },
  106         { NULL,                         0 }
  107 };
  108 
  109 struct rk_emmcphy_softc {
  110         struct syscon           *syscon;
  111         struct rk_emmcphy_conf  *phy_conf;
  112         clk_t                   clk;
  113 };
  114 
  115 #define LOWEST_SET_BIT(mask)    ((((mask) - 1) & (mask)) ^ (mask))
  116 #define SHIFTIN(x, mask)        ((x) * LOWEST_SET_BIT(mask))
  117 
  118 /* Phy class and methods. */
  119 static int rk_emmcphy_enable(struct phynode *phynode, bool enable);
  120 static phynode_method_t rk_emmcphy_phynode_methods[] = {
  121         PHYNODEMETHOD(phynode_enable,   rk_emmcphy_enable),
  122         PHYNODEMETHOD_END
  123 };
  124 
  125 DEFINE_CLASS_1(rk_emmcphy_phynode, rk_emmcphy_phynode_class,
  126     rk_emmcphy_phynode_methods, 0, phynode_class);
  127 
  128 static int
  129 rk_emmcphy_enable(struct phynode *phynode, bool enable)
  130 {
  131         struct rk_emmcphy_softc *sc;
  132         device_t dev;
  133         intptr_t phy;
  134         uint64_t rate, frqsel;
  135         uint32_t mask, val;
  136         int error;
  137 
  138         dev = phynode_get_device(phynode);
  139         phy = phynode_get_id(phynode);
  140         sc = device_get_softc(dev);
  141 
  142         if (bootverbose)
  143                 device_printf(dev, "Phy id: %ld\n", phy);
  144 
  145         if (phy != 0) {
  146                 device_printf(dev, "Unknown phy: %ld\n", phy);
  147                 return (ERANGE);
  148         }
  149         if (enable) {
  150                 /* Drive strength */
  151                 mask = PHYCTRL_DR_TY;
  152                 val = SHIFTIN(0, PHYCTRL_DR_TY);
  153                 SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6,
  154                     (mask << 16) | val);
  155 
  156                 /* Enable output tap delay */
  157                 mask = PHYCTRL_OTAPDLYENA | PHYCTRL_OTAPDLYSEL;
  158                 val = PHYCTRL_OTAPDLYENA | SHIFTIN(4, PHYCTRL_OTAPDLYSEL);
  159                 SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0,
  160                     (mask << 16) | val);
  161         }
  162 
  163         /* Power down PHY and disable DLL before making changes */
  164         mask = PHYCTRL_ENDLL | PHYCTRL_PDB;
  165         val = 0;
  166         SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
  167 
  168         if (enable == false)
  169                 return (0);
  170 
  171         sc->phy_conf = (struct rk_emmcphy_conf *)ofw_bus_search_compatible(dev,
  172             compat_data)->ocd_data;
  173 
  174         /* Get clock */
  175         error = clk_get_by_ofw_name(dev, 0, "emmcclk", &sc->clk);
  176         if (error != 0) {
  177                 device_printf(dev, "cannot get emmcclk clock, continue\n");
  178                 sc->clk = NULL;
  179         } else
  180                 device_printf(dev, "got emmcclk clock\n");
  181 
  182         if (sc->clk) {
  183                 error = clk_get_freq(sc->clk, &rate);
  184                 if (error != 0) {
  185                         device_printf(dev, "cannot get clock frequency\n");
  186                         return (ENXIO);
  187                 }
  188         } else
  189                 rate = 0;
  190 
  191         if (rate != 0) {
  192                 if (rate < 75000000)
  193                         frqsel = PHYCTRL_FRQSEL_50M;
  194                 else if (rate < 125000000)
  195                         frqsel = PHYCTRL_FRQSEL_100M;
  196                 else if (rate < 175000000)
  197                         frqsel = PHYCTRL_FRQSEL_150M;
  198                 else
  199                         frqsel = PHYCTRL_FRQSEL_200M;
  200         } else
  201                 frqsel = PHYCTRL_FRQSEL_200M;
  202 
  203         DELAY(3);
  204 
  205         /* Power up PHY */
  206         mask = PHYCTRL_PDB;
  207         val = PHYCTRL_PDB;
  208         SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
  209 
  210         /* Wait for calibration */
  211         DELAY(10);
  212         val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);
  213         if ((val & PHYCTRL_CALDONE) == 0) {
  214                 device_printf(dev, "PHY calibration did not complete\n");
  215                 return (ENXIO);
  216         }
  217 
  218         /* Set DLL frequency */
  219         mask = PHYCTRL_FRQSEL;
  220         val = SHIFTIN(frqsel, PHYCTRL_FRQSEL);
  221         SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0, (mask << 16) | val);
  222 
  223         /* Enable DLL */
  224         mask = PHYCTRL_ENDLL;
  225         val = PHYCTRL_ENDLL;
  226         SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
  227 
  228         if (rate != 0) {
  229                 /*
  230                  * Rockchip RK3399 TRM V1.3 Part2.pdf says in page 698:
  231                  * After the DLL control loop reaches steady state a DLL
  232                  * ready signal is generated by the DLL circuits
  233                  * 'phyctrl_dllrdy'.
  234                  * The time from 'phyctrl_endll' to DLL ready signal
  235                  * 'phyctrl_dllrdy' varies with the clock frequency.
  236                  * At 200MHz clock frequency the DLL ready delay is 2.56us,
  237                  * at 100MHz clock frequency the DLL ready delay is 5.112us and
  238                  * at 50 MHz clock frequency the DLL ready delay is 10.231us.
  239                  * We could use safe values for wait, 12us, 8us, 6us and 4us
  240                  * respectively.
  241                  * However due to some unknown reason it is not working and
  242                  * DLL seems to take extra long time to lock.
  243                  * So we will use more safe value 50ms here.
  244                  */
  245 
  246                 /* Wait for DLL ready */
  247                 DELAY(50000);
  248                 val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);
  249                 if ((val & PHYCTRL_DLLRDY) == 0) {
  250                         device_printf(dev, "DLL loop failed to lock\n");
  251                         return (ENXIO);
  252                 }
  253         }
  254 
  255         return (0);
  256 }
  257 
  258 static int
  259 rk_emmcphy_probe(device_t dev)
  260 {
  261 
  262         if (!ofw_bus_status_okay(dev))
  263                 return (ENXIO);
  264 
  265         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  266                 return (ENXIO);
  267 
  268         device_set_desc(dev, "Rockchip RK3399 eMMC PHY");
  269         return (BUS_PROBE_DEFAULT);
  270 }
  271 
  272 static int
  273 rk_emmcphy_attach(device_t dev)
  274 {
  275         struct phynode_init_def phy_init;
  276         struct phynode *phynode;
  277         struct rk_emmcphy_softc *sc;
  278         phandle_t node;
  279         phandle_t xnode;
  280         pcell_t handle;
  281         intptr_t phy;
  282 
  283         sc = device_get_softc(dev);
  284         node = ofw_bus_get_node(dev);
  285 
  286         if (OF_getencprop(node, "clocks", (void *)&handle,
  287             sizeof(handle)) <= 0) {
  288                 device_printf(dev, "cannot get clocks handle\n");
  289                 return (ENXIO);
  290         }
  291         xnode = OF_node_from_xref(handle);
  292         if (OF_hasprop(xnode, "arasan,soc-ctl-syscon") &&
  293             syscon_get_by_ofw_property(dev, xnode,
  294             "arasan,soc-ctl-syscon", &sc->syscon) != 0) {
  295                 device_printf(dev, "cannot get grf driver handle\n");
  296                 return (ENXIO);
  297         }
  298 
  299         if (sc->syscon == NULL) {
  300                 device_printf(dev, "failed to get syscon\n");
  301                 return (ENXIO);
  302         }
  303 
  304         /* Create and register phy */
  305         bzero(&phy_init, sizeof(phy_init));
  306         phy_init.id = 0;
  307         phy_init.ofw_node = ofw_bus_get_node(dev);
  308         phynode = phynode_create(dev, &rk_emmcphy_phynode_class, &phy_init);
  309         if (phynode == NULL) {
  310                 device_printf(dev, "failed to create eMMC PHY\n");
  311                 return (ENXIO);
  312         }
  313         if (phynode_register(phynode) == NULL) {
  314                 device_printf(dev, "failed to register eMMC PHY\n");
  315                 return (ENXIO);
  316         }
  317         if (bootverbose) {
  318                 phy = phynode_get_id(phynode);
  319                 device_printf(dev, "Attached phy id: %ld\n", phy);
  320         }
  321         return (0);
  322 }
  323 
  324 static device_method_t rk_emmcphy_methods[] = {
  325         /* Device interface */
  326         DEVMETHOD(device_probe,         rk_emmcphy_probe),
  327         DEVMETHOD(device_attach,        rk_emmcphy_attach),
  328 
  329         DEVMETHOD_END
  330 };
  331 
  332 static driver_t rk_emmcphy_driver = {
  333         "rk_emmcphy",
  334         rk_emmcphy_methods,
  335         sizeof(struct rk_emmcphy_softc)
  336 };
  337 
  338 EARLY_DRIVER_MODULE(rk_emmcphy, simplebus, rk_emmcphy_driver, 0, 0,
  339     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
  340 MODULE_VERSION(rk_emmcphy, 1);

Cache object: 04e1cd914f4fa9eb6cd38a076ed345c6


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