Path: blob/main/sys/arm64/rockchip/rk3399_emmcphy.c
39478 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 Ganbold Tsagaankhuu <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728/*29* Rockchip RK3399 eMMC PHY30*/3132#include <sys/param.h>33#include <sys/systm.h>34#include <sys/bus.h>35#include <sys/rman.h>36#include <sys/kernel.h>37#include <sys/module.h>38#include <sys/gpio.h>39#include <machine/bus.h>4041#include <dev/fdt/fdt_common.h>42#include <dev/ofw/ofw_bus.h>43#include <dev/ofw/ofw_bus_subr.h>4445#include <dev/clk/clk.h>46#include <dev/syscon/syscon.h>47#include <dev/phy/phy.h>4849#include "syscon_if.h"5051#define GRF_EMMCPHY_BASE 0xf78052#define GRF_EMMCPHY_CON0 (GRF_EMMCPHY_BASE + 0x00)53#define PHYCTRL_FRQSEL (1 << 13) | (1 << 12)54#define PHYCTRL_FRQSEL_200M 055#define PHYCTRL_FRQSEL_50M 156#define PHYCTRL_FRQSEL_100M 257#define PHYCTRL_FRQSEL_150M 358#define PHYCTRL_OTAPDLYENA (1 << 11)59#define PHYCTRL_OTAPDLYSEL (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7)60#define PHYCTRL_ITAPCHGWIN (1 << 6)61#define PHYCTRL_ITAPDLYSEL (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | \62(1 << 1)63#define PHYCTRL_ITAPDLYENA (1 << 0)64#define GRF_EMMCPHY_CON1 (GRF_EMMCPHY_BASE + 0x04)65#define PHYCTRL_CLKBUFSEL (1 << 8) | (1 << 7) | (1 << 6)66#define PHYCTRL_SELDLYTXCLK (1 << 5)67#define PHYCTRL_SELDLYRXCLK (1 << 4)68#define PHYCTRL_STRBSEL 0xf69#define GRF_EMMCPHY_CON2 (GRF_EMMCPHY_BASE + 0x08)70#define PHYCTRL_REN_STRB (1 << 9)71#define PHYCTRL_REN_CMD (1 << 8)72#define PHYCTRL_REN_DAT 0xff73#define GRF_EMMCPHY_CON3 (GRF_EMMCPHY_BASE + 0x0c)74#define PHYCTRL_PU_STRB (1 << 9)75#define PHYCTRL_PU_CMD (1 << 8)76#define PHYCTRL_PU_DAT 0xff77#define GRF_EMMCPHY_CON4 (GRF_EMMCPHY_BASE + 0x10)78#define PHYCTRL_OD_RELEASE_CMD (1 << 9)79#define PHYCTRL_OD_RELEASE_STRB (1 << 8)80#define PHYCTRL_OD_RELEASE_DAT 0xff81#define GRF_EMMCPHY_CON5 (GRF_EMMCPHY_BASE + 0x14)82#define PHYCTRL_ODEN_STRB (1 << 9)83#define PHYCTRL_ODEN_CMD (1 << 8)84#define PHYCTRL_ODEN_DAT 0xff85#define GRF_EMMCPHY_CON6 (GRF_EMMCPHY_BASE + 0x18)86#define PHYCTRL_DLL_TRM_ICP (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9)87#define PHYCTRL_EN_RTRIM (1 << 8)88#define PHYCTRL_RETRIM (1 << 7)89#define PHYCTRL_DR_TY (1 << 6) | (1 << 5) | (1 << 4)90#define PHYCTRL_RETENB (1 << 3)91#define PHYCTRL_RETEN (1 << 2)92#define PHYCTRL_ENDLL (1 << 1)93#define PHYCTRL_PDB (1 << 0)94#define GRF_EMMCPHY_STATUS (GRF_EMMCPHY_BASE + 0x20)95#define PHYCTRL_CALDONE (1 << 6)96#define PHYCTRL_DLLRDY (1 << 5)97#define PHYCTRL_RTRIM (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1)98#define PHYCTRL_EXR_NINST (1 << 0)99100static struct ofw_compat_data compat_data[] = {101{ "rockchip,rk3399-emmc-phy", 1 },102{ NULL, 0 }103};104105struct rk_emmcphy_softc {106struct syscon *syscon;107struct rk_emmcphy_conf *phy_conf;108clk_t clk;109};110111#define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask))112#define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask))113114/* Phy class and methods. */115static int rk_emmcphy_enable(struct phynode *phynode, bool enable);116static phynode_method_t rk_emmcphy_phynode_methods[] = {117PHYNODEMETHOD(phynode_enable, rk_emmcphy_enable),118PHYNODEMETHOD_END119};120121DEFINE_CLASS_1(rk_emmcphy_phynode, rk_emmcphy_phynode_class,122rk_emmcphy_phynode_methods, 0, phynode_class);123124static int125rk_emmcphy_enable(struct phynode *phynode, bool enable)126{127struct rk_emmcphy_softc *sc;128device_t dev;129intptr_t phy;130uint64_t rate, frqsel;131uint32_t mask, val;132int error;133134dev = phynode_get_device(phynode);135phy = phynode_get_id(phynode);136sc = device_get_softc(dev);137138if (bootverbose)139device_printf(dev, "Phy id: %ld\n", phy);140141if (phy != 0) {142device_printf(dev, "Unknown phy: %ld\n", phy);143return (ERANGE);144}145if (enable) {146/* Drive strength */147mask = PHYCTRL_DR_TY;148val = SHIFTIN(0, PHYCTRL_DR_TY);149SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6,150(mask << 16) | val);151152/* Enable output tap delay */153mask = PHYCTRL_OTAPDLYENA | PHYCTRL_OTAPDLYSEL;154val = PHYCTRL_OTAPDLYENA | SHIFTIN(4, PHYCTRL_OTAPDLYSEL);155SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0,156(mask << 16) | val);157}158159/* Power down PHY and disable DLL before making changes */160mask = PHYCTRL_ENDLL | PHYCTRL_PDB;161val = 0;162SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);163164if (enable == false)165return (0);166167sc->phy_conf = (struct rk_emmcphy_conf *)ofw_bus_search_compatible(dev,168compat_data)->ocd_data;169170/* Get clock */171error = clk_get_by_ofw_name(dev, 0, "emmcclk", &sc->clk);172if (error != 0) {173device_printf(dev, "cannot get emmcclk clock, continue\n");174sc->clk = NULL;175} else176device_printf(dev, "got emmcclk clock\n");177178if (sc->clk) {179error = clk_get_freq(sc->clk, &rate);180if (error != 0) {181device_printf(dev, "cannot get clock frequency\n");182return (ENXIO);183}184} else185rate = 0;186187if (rate != 0) {188if (rate < 75000000)189frqsel = PHYCTRL_FRQSEL_50M;190else if (rate < 125000000)191frqsel = PHYCTRL_FRQSEL_100M;192else if (rate < 175000000)193frqsel = PHYCTRL_FRQSEL_150M;194else195frqsel = PHYCTRL_FRQSEL_200M;196} else197frqsel = PHYCTRL_FRQSEL_200M;198199DELAY(3);200201/* Power up PHY */202mask = PHYCTRL_PDB;203val = PHYCTRL_PDB;204SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);205206/* Wait for calibration */207DELAY(10);208val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);209if ((val & PHYCTRL_CALDONE) == 0) {210device_printf(dev, "PHY calibration did not complete\n");211return (ENXIO);212}213214/* Set DLL frequency */215mask = PHYCTRL_FRQSEL;216val = SHIFTIN(frqsel, PHYCTRL_FRQSEL);217SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0, (mask << 16) | val);218219/* Enable DLL */220mask = PHYCTRL_ENDLL;221val = PHYCTRL_ENDLL;222SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);223224if (rate != 0) {225/*226* Rockchip RK3399 TRM V1.3 Part2.pdf says in page 698:227* After the DLL control loop reaches steady state a DLL228* ready signal is generated by the DLL circuits229* 'phyctrl_dllrdy'.230* The time from 'phyctrl_endll' to DLL ready signal231* 'phyctrl_dllrdy' varies with the clock frequency.232* At 200MHz clock frequency the DLL ready delay is 2.56us,233* at 100MHz clock frequency the DLL ready delay is 5.112us and234* at 50 MHz clock frequency the DLL ready delay is 10.231us.235* We could use safe values for wait, 12us, 8us, 6us and 4us236* respectively.237* However due to some unknown reason it is not working and238* DLL seems to take extra long time to lock.239* So we will use more safe value 50ms here.240*/241242/* Wait for DLL ready */243DELAY(50000);244val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);245if ((val & PHYCTRL_DLLRDY) == 0) {246device_printf(dev, "DLL loop failed to lock\n");247return (ENXIO);248}249}250251return (0);252}253254static int255rk_emmcphy_probe(device_t dev)256{257258if (!ofw_bus_status_okay(dev))259return (ENXIO);260261if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)262return (ENXIO);263264device_set_desc(dev, "Rockchip RK3399 eMMC PHY");265return (BUS_PROBE_DEFAULT);266}267268static int269rk_emmcphy_attach(device_t dev)270{271struct phynode_init_def phy_init;272struct phynode *phynode;273struct rk_emmcphy_softc *sc;274phandle_t node;275phandle_t xnode;276pcell_t handle;277intptr_t phy;278279sc = device_get_softc(dev);280node = ofw_bus_get_node(dev);281282if (OF_getencprop(node, "clocks", (void *)&handle,283sizeof(handle)) <= 0) {284device_printf(dev, "cannot get clocks handle\n");285return (ENXIO);286}287xnode = OF_node_from_xref(handle);288if (OF_hasprop(xnode, "arasan,soc-ctl-syscon") &&289syscon_get_by_ofw_property(dev, xnode,290"arasan,soc-ctl-syscon", &sc->syscon) != 0) {291device_printf(dev, "cannot get grf driver handle\n");292return (ENXIO);293}294295if (sc->syscon == NULL) {296device_printf(dev, "failed to get syscon\n");297return (ENXIO);298}299300/* Create and register phy */301bzero(&phy_init, sizeof(phy_init));302phy_init.id = 0;303phy_init.ofw_node = ofw_bus_get_node(dev);304phynode = phynode_create(dev, &rk_emmcphy_phynode_class, &phy_init);305if (phynode == NULL) {306device_printf(dev, "failed to create eMMC PHY\n");307return (ENXIO);308}309if (phynode_register(phynode) == NULL) {310device_printf(dev, "failed to register eMMC PHY\n");311return (ENXIO);312}313if (bootverbose) {314phy = phynode_get_id(phynode);315device_printf(dev, "Attached phy id: %ld\n", phy);316}317return (0);318}319320static device_method_t rk_emmcphy_methods[] = {321/* Device interface */322DEVMETHOD(device_probe, rk_emmcphy_probe),323DEVMETHOD(device_attach, rk_emmcphy_attach),324325DEVMETHOD_END326};327328static driver_t rk_emmcphy_driver = {329"rk_emmcphy",330rk_emmcphy_methods,331sizeof(struct rk_emmcphy_softc)332};333334EARLY_DRIVER_MODULE(rk_emmcphy, simplebus, rk_emmcphy_driver, 0, 0,335BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);336MODULE_VERSION(rk_emmcphy, 1);337338339