Author: manu
Date: Wed Mar  4 20:43:29 2020
New Revision: 358640
URL: https://svnweb.freebsd.org/changeset/base/358640

Log:
  MFC r349638, r350161, r351186
  
  r349638 by ganbold:
  Subclass Rockchip's General Register Files driver from Simple MFD driver.
  
  r350161 by ganbold:
  Add driver for Rockchip RK3399 eMMC PHY.
  Tested on NanoPC-T4 board.
  
  Reviewed by:  manu
  Differential Revision:        https://reviews.freebsd.org/D20840
  
  r351186 by mmel:
  Improve rk_i2c driver:
  - Properly handle IIC_M_NOSTOP and IIC_M_NOSTART flags.
  - add polling mode, so driver can be used even if interrupts are not
    enabled (this is necessary for proper support of PMICs).
  - add support for RK3288

Added:
  stable/12/sys/arm64/rockchip/rk3399_emmcphy.c
     - copied unchanged from r350161, head/sys/arm64/rockchip/rk3399_emmcphy.c
Modified:
  stable/12/sys/arm64/conf/GENERIC
  stable/12/sys/arm64/rockchip/rk_grf.c
  stable/12/sys/arm64/rockchip/rk_i2c.c
  stable/12/sys/conf/files.arm64
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/arm64/conf/GENERIC
==============================================================================
--- stable/12/sys/arm64/conf/GENERIC    Wed Mar  4 20:41:45 2020        
(r358639)
+++ stable/12/sys/arm64/conf/GENERIC    Wed Mar  4 20:43:29 2020        
(r358640)
@@ -166,6 +166,7 @@ device              aw_mmc                  # Allwinner 
SD/MMC controller
 device         mmc                     # mmc/sd bus
 device         mmcsd                   # mmc/sd flash cards
 device         dwmmc
+device         rk_emmcphy
 
 # Serial (COM) ports
 device         uart            # Generic UART driver

Copied: stable/12/sys/arm64/rockchip/rk3399_emmcphy.c (from r350161, 
head/sys/arm64/rockchip/rk3399_emmcphy.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ stable/12/sys/arm64/rockchip/rk3399_emmcphy.c       Wed Mar  4 20:43:29 
2020        (r358640, copy of r350161, head/sys/arm64/rockchip/rk3399_emmcphy.c)
@@ -0,0 +1,341 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Ganbold Tsagaankhuu <ganb...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Rockchip RK3399 eMMC PHY
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/syscon/syscon.h>
+#include <dev/extres/phy/phy.h>
+
+#include "syscon_if.h"
+
+#define        GRF_EMMCPHY_BASE        0xf780
+#define        GRF_EMMCPHY_CON0        (GRF_EMMCPHY_BASE + 0x00)
+#define         PHYCTRL_FRQSEL         (1 << 13) | (1 << 12)
+#define          PHYCTRL_FRQSEL_200M   0
+#define          PHYCTRL_FRQSEL_50M    1
+#define          PHYCTRL_FRQSEL_100M   2
+#define          PHYCTRL_FRQSEL_150M   3
+#define         PHYCTRL_OTAPDLYENA     (1 << 11)
+#define         PHYCTRL_OTAPDLYSEL     (1 << 10) | (1 << 9) | (1 << 8) | (1 << 
7)
+#define         PHYCTRL_ITAPCHGWIN     (1 << 6)
+#define         PHYCTRL_ITAPDLYSEL     (1 << 5) | (1 << 4)  | (1 << 3) | (1 << 
2) | \
+    (1 << 1)
+#define         PHYCTRL_ITAPDLYENA     (1 << 0)
+#define        GRF_EMMCPHY_CON1        (GRF_EMMCPHY_BASE + 0x04)
+#define         PHYCTRL_CLKBUFSEL      (1 << 8) | (1 << 7) | (1 << 6)
+#define         PHYCTRL_SELDLYTXCLK    (1 << 5)
+#define         PHYCTRL_SELDLYRXCLK    (1 << 4)
+#define         PHYCTRL_STRBSEL        0xf
+#define        GRF_EMMCPHY_CON2        (GRF_EMMCPHY_BASE + 0x08)
+#define         PHYCTRL_REN_STRB       (1 << 9)
+#define         PHYCTRL_REN_CMD        (1 << 8)
+#define         PHYCTRL_REN_DAT        0xff
+#define        GRF_EMMCPHY_CON3        (GRF_EMMCPHY_BASE + 0x0c)
+#define         PHYCTRL_PU_STRB        (1 << 9)
+#define         PHYCTRL_PU_CMD         (1 << 8)
+#define         PHYCTRL_PU_DAT         0xff
+#define        GRF_EMMCPHY_CON4        (GRF_EMMCPHY_BASE + 0x10)
+#define         PHYCTRL_OD_RELEASE_CMD         (1 << 9)
+#define         PHYCTRL_OD_RELEASE_STRB        (1 << 8)
+#define         PHYCTRL_OD_RELEASE_DAT         0xff
+#define        GRF_EMMCPHY_CON5        (GRF_EMMCPHY_BASE + 0x14)
+#define         PHYCTRL_ODEN_STRB      (1 << 9)
+#define         PHYCTRL_ODEN_CMD       (1 << 8)
+#define         PHYCTRL_ODEN_DAT       0xff
+#define        GRF_EMMCPHY_CON6        (GRF_EMMCPHY_BASE + 0x18)
+#define         PHYCTRL_DLL_TRM_ICP    (1 << 12) | (1 << 11) | (1 << 10) | (1 
<< 9)
+#define         PHYCTRL_EN_RTRIM       (1 << 8)
+#define         PHYCTRL_RETRIM         (1 << 7)
+#define         PHYCTRL_DR_TY          (1 << 6) | (1 << 5) | (1 << 4)
+#define         PHYCTRL_RETENB         (1 << 3)
+#define         PHYCTRL_RETEN          (1 << 2)
+#define         PHYCTRL_ENDLL          (1 << 1)
+#define         PHYCTRL_PDB            (1 << 0)
+#define        GRF_EMMCPHY_STATUS      (GRF_EMMCPHY_BASE + 0x20)
+#define         PHYCTRL_CALDONE        (1 << 6)
+#define         PHYCTRL_DLLRDY         (1 << 5)
+#define         PHYCTRL_RTRIM          (1 << 4) | (1 << 3) | (1 << 2) | (1 << 
1)
+#define         PHYCTRL_EXR_NINST      (1 << 0)
+
+static struct ofw_compat_data compat_data[] = {
+       { "rockchip,rk3399-emmc-phy",   1 },
+       { NULL,                         0 }
+};
+
+struct rk_emmcphy_softc {
+       struct syscon           *syscon;
+       struct rk_emmcphy_conf  *phy_conf;
+       clk_t                   clk;
+};
+
+#define        LOWEST_SET_BIT(mask)    ((((mask) - 1) & (mask)) ^ (mask))
+#define        SHIFTIN(x, mask)        ((x) * LOWEST_SET_BIT(mask))
+
+/* Phy class and methods. */
+static int rk_emmcphy_enable(struct phynode *phynode, bool enable);
+static phynode_method_t rk_emmcphy_phynode_methods[] = {
+       PHYNODEMETHOD(phynode_enable,   rk_emmcphy_enable),
+       PHYNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(rk_emmcphy_phynode, rk_emmcphy_phynode_class,
+    rk_emmcphy_phynode_methods, 0, phynode_class);
+
+static int
+rk_emmcphy_enable(struct phynode *phynode, bool enable)
+{
+       struct rk_emmcphy_softc *sc;
+       device_t dev;
+       intptr_t phy;
+       uint64_t rate, frqsel;
+       uint32_t mask, val;
+       int error;
+
+       dev = phynode_get_device(phynode);
+       phy = phynode_get_id(phynode);
+       sc = device_get_softc(dev);
+
+       if (bootverbose)
+               device_printf(dev, "Phy id: %ld\n", phy);
+
+       if (phy != 0) {
+               device_printf(dev, "Unknown phy: %ld\n", phy);
+               return (ERANGE);
+       }
+       if (enable) {
+               /* Drive strength */
+               mask = PHYCTRL_DR_TY;
+               val = SHIFTIN(0, PHYCTRL_DR_TY);
+               SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6,
+                   (mask << 16) | val);
+
+               /* Enable output tap delay */
+               mask = PHYCTRL_OTAPDLYENA | PHYCTRL_OTAPDLYSEL;
+               val = PHYCTRL_OTAPDLYENA | SHIFTIN(4, PHYCTRL_OTAPDLYSEL);
+               SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0,
+                   (mask << 16) | val);
+       }
+
+       /* Power down PHY and disable DLL before making changes */
+       mask = PHYCTRL_ENDLL | PHYCTRL_PDB;
+       val = 0;
+       SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
+
+       if (enable == false)
+               return (0);
+
+       sc->phy_conf = (struct rk_emmcphy_conf *)ofw_bus_search_compatible(dev,
+           compat_data)->ocd_data;
+
+       /* Get clock */
+       error = clk_get_by_ofw_name(dev, 0, "emmcclk", &sc->clk);
+       if (error != 0) {
+               device_printf(dev, "cannot get emmcclk clock, continue\n");
+               sc->clk = NULL;
+       } else
+               device_printf(dev, "got emmcclk clock\n");
+
+       if (sc->clk) {
+               error = clk_get_freq(sc->clk, &rate);
+               if (error != 0) {
+                       device_printf(dev, "cannot get clock frequency\n");
+                       return (ENXIO);
+               }
+       } else
+               rate = 0;
+
+       if (rate != 0) {
+               if (rate < 75000000)
+                       frqsel = PHYCTRL_FRQSEL_50M;
+               else if (rate < 125000000)
+                       frqsel = PHYCTRL_FRQSEL_100M;
+               else if (rate < 175000000)
+                       frqsel = PHYCTRL_FRQSEL_150M;
+               else
+                       frqsel = PHYCTRL_FRQSEL_200M;
+       } else
+               frqsel = PHYCTRL_FRQSEL_200M;
+
+       DELAY(3);
+
+       /* Power up PHY */
+       mask = PHYCTRL_PDB;
+       val = PHYCTRL_PDB;
+       SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
+
+       /* Wait for calibration */
+       DELAY(10);
+       val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);
+       if ((val & PHYCTRL_CALDONE) == 0) {
+               device_printf(dev, "PHY calibration did not complete\n");
+               return (ENXIO);
+       }
+
+       /* Set DLL frequency */
+       mask = PHYCTRL_FRQSEL;
+       val = SHIFTIN(frqsel, PHYCTRL_FRQSEL);
+       SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON0, (mask << 16) | val);
+
+       /* Enable DLL */
+       mask = PHYCTRL_ENDLL;
+       val = PHYCTRL_ENDLL;
+       SYSCON_WRITE_4(sc->syscon, GRF_EMMCPHY_CON6, (mask << 16) | val);
+
+       if (rate != 0) {
+               /*
+                * Rockchip RK3399 TRM V1.3 Part2.pdf says in page 698:
+                * After the DLL control loop reaches steady state a DLL
+                * ready signal is generated by the DLL circuits
+                * 'phyctrl_dllrdy'.
+                * The time from 'phyctrl_endll' to DLL ready signal
+                * 'phyctrl_dllrdy' varies with the clock frequency.
+                * At 200MHz clock frequency the DLL ready delay is 2.56us,
+                * at 100MHz clock frequency the DLL ready delay is 5.112us and
+                * at 50 MHz clock frequency the DLL ready delay is 10.231us.
+                * We could use safe values for wait, 12us, 8us, 6us and 4us
+                * respectively.
+                * However due to some unknown reason it is not working and
+                * DLL seems to take extra long time to lock.
+                * So we will use more safe value 50ms here.
+                */
+
+               /* Wait for DLL ready */
+               DELAY(50000);
+               val = SYSCON_READ_4(sc->syscon, GRF_EMMCPHY_STATUS);
+               if ((val & PHYCTRL_DLLRDY) == 0) {
+                       device_printf(dev, "DLL loop failed to lock\n");
+                       return (ENXIO);
+               }
+       }
+
+       return (0);
+}
+
+static int
+rk_emmcphy_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+               return (ENXIO);
+
+       device_set_desc(dev, "Rockchip RK3399 eMMC PHY");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+rk_emmcphy_attach(device_t dev)
+{
+       struct phynode_init_def phy_init;
+       struct phynode *phynode;
+       struct rk_emmcphy_softc *sc;
+       phandle_t node;
+       phandle_t xnode;
+       pcell_t handle;
+       intptr_t phy;
+
+       sc = device_get_softc(dev);
+       node = ofw_bus_get_node(dev);
+
+       if (OF_getencprop(node, "clocks", (void *)&handle,
+           sizeof(handle)) <= 0) {
+               device_printf(dev, "cannot get clocks handle\n");
+               return (ENXIO);
+       }
+       xnode = OF_node_from_xref(handle);
+       if (OF_hasprop(xnode, "arasan,soc-ctl-syscon") &&
+           syscon_get_by_ofw_property(dev, xnode,
+           "arasan,soc-ctl-syscon", &sc->syscon) != 0) {
+               device_printf(dev, "cannot get grf driver handle\n");
+               return (ENXIO);
+       }
+
+       if (sc->syscon == NULL) {
+               device_printf(dev, "failed to get syscon\n");
+               return (ENXIO);
+       }
+
+       /* Create and register phy */
+       bzero(&phy_init, sizeof(phy_init));
+       phy_init.id = 0;
+       phy_init.ofw_node = ofw_bus_get_node(dev);
+       phynode = phynode_create(dev, &rk_emmcphy_phynode_class, &phy_init);
+       if (phynode == NULL) {
+               device_printf(dev, "failed to create eMMC PHY\n");
+               return (ENXIO);
+       }
+       if (phynode_register(phynode) == NULL) {
+               device_printf(dev, "failed to register eMMC PHY\n");
+               return (ENXIO);
+       }
+       if (bootverbose) {
+               phy = phynode_get_id(phynode);
+               device_printf(dev, "Attached phy id: %ld\n", phy);
+       }
+       return (0);
+}
+
+static device_method_t rk_emmcphy_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         rk_emmcphy_probe),
+       DEVMETHOD(device_attach,        rk_emmcphy_attach),
+
+       DEVMETHOD_END
+};
+
+static driver_t rk_emmcphy_driver = {
+       "rk_emmcphy",
+       rk_emmcphy_methods,
+       sizeof(struct rk_emmcphy_softc)
+};
+
+static devclass_t rk_emmcphy_devclass;
+EARLY_DRIVER_MODULE(rk_emmcphy, simplebus, rk_emmcphy_driver,
+    rk_emmcphy_devclass, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(rk_emmcphy, 1);

Modified: stable/12/sys/arm64/rockchip/rk_grf.c
==============================================================================
--- stable/12/sys/arm64/rockchip/rk_grf.c       Wed Mar  4 20:41:45 2020        
(r358639)
+++ stable/12/sys/arm64/rockchip/rk_grf.c       Wed Mar  4 20:43:29 2020        
(r358640)
@@ -42,7 +42,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus_subr.h>
 
 #include <dev/extres/syscon/syscon.h>
-#include <dev/extres/syscon/syscon_generic.h>
+#include <dev/fdt/simple_mfd.h>
 
 #include "opt_soc.h"
 
@@ -77,7 +77,7 @@ static device_method_t rk_grf_methods[] = {
 };
 
 DEFINE_CLASS_1(rk_grf, rk_grf_driver, rk_grf_methods,
-    sizeof(struct syscon_generic_softc), syscon_generic_driver);
+    sizeof(struct simple_mfd_softc), simple_mfd_driver);
 
 static devclass_t rk_grf_devclass;
 EARLY_DRIVER_MODULE(rk_grf, simplebus, rk_grf_driver, rk_grf_devclass,

Modified: stable/12/sys/arm64/rockchip/rk_i2c.c
==============================================================================
--- stable/12/sys/arm64/rockchip/rk_i2c.c       Wed Mar  4 20:41:45 2020        
(r358639)
+++ stable/12/sys/arm64/rockchip/rk_i2c.c       Wed Mar  4 20:43:29 2020        
(r358640)
@@ -46,9 +46,7 @@ __FBSDID("$FreeBSD$");
 
 #include "iicbus_if.h"
 
-#include "opt_soc.h"
 
-
 #define        RK_I2C_CON                      0x00
 #define         RK_I2C_CON_EN                  (1 << 0)
 #define         RK_I2C_CON_MODE_SHIFT          1
@@ -61,6 +59,7 @@ __FBSDID("$FreeBSD$");
 #define         RK_I2C_CON_STOP                (1 << 4)
 #define         RK_I2C_CON_LASTACK             (1 << 5)
 #define         RK_I2C_CON_NAKSTOP             (1 << 6)
+#define         RK_I2C_CON_CTRL_MASK           0xFF
 
 #define        RK_I2C_CLKDIV           0x04
 #define         RK_I2C_CLKDIVL_MASK    0xFFFF
@@ -91,8 +90,7 @@ __FBSDID("$FreeBSD$");
 #define         RK_I2C_IEN_STARTIEN    (1 << 4)
 #define         RK_I2C_IEN_STOPIEN     (1 << 5)
 #define         RK_I2C_IEN_NAKRCVIEN   (1 << 6)
-#define         RK_I2C_IEN_ALL         (RK_I2C_IEN_BTFIEN | \
-       RK_I2C_IEN_BRFIEN | RK_I2C_IEN_MBTFIEN | RK_I2C_IEN_MBRFIEN | \
+#define         RK_I2C_IEN_ALL         (RK_I2C_IEN_MBTFIEN | 
RK_I2C_IEN_MBRFIEN | \
        RK_I2C_IEN_STARTIEN | RK_I2C_IEN_STOPIEN | RK_I2C_IEN_NAKRCVIEN)
 
 #define        RK_I2C_IPD              0x1C
@@ -103,8 +101,7 @@ __FBSDID("$FreeBSD$");
 #define         RK_I2C_IPD_STARTIPD    (1 << 4)
 #define         RK_I2C_IPD_STOPIPD     (1 << 5)
 #define         RK_I2C_IPD_NAKRCVIPD   (1 << 6)
-#define         RK_I2C_IPD_ALL         (RK_I2C_IPD_BTFIPD | \
-       RK_I2C_IPD_BRFIPD | RK_I2C_IPD_MBTFIPD | RK_I2C_IPD_MBRFIPD | \
+#define         RK_I2C_IPD_ALL         (RK_I2C_IPD_MBTFIPD | 
RK_I2C_IPD_MBRFIPD | \
        RK_I2C_IPD_STARTIPD | RK_I2C_IPD_STOPIPD | RK_I2C_IPD_NAKRCVIPD)
 
 #define        RK_I2C_FNCT             0x20
@@ -134,8 +131,10 @@ struct rk_i2c_softc {
        uint32_t        ipd;
        struct iic_msg  *msg;
        size_t          cnt;
-       int             transfer_done;
-       int             nak_recv;
+       int             msg_len;
+       bool            transfer_done;
+       bool            nak_recv;
+       bool            tx_slave_addr;
        uint8_t         mode;
        uint8_t         state;
 
@@ -143,12 +142,9 @@ struct rk_i2c_softc {
 };
 
 static struct ofw_compat_data compat_data[] = {
-#ifdef SOC_ROCKCHIP_RK3328
+       {"rockchip,rk3288-i2c", 1},
        {"rockchip,rk3328-i2c", 1},
-#endif
-#ifdef SOC_ROCKCHIP_RK3399
        {"rockchip,rk3399-i2c", 1},
-#endif
        {NULL,             0}
 };
 
@@ -169,7 +165,7 @@ static int rk_i2c_detach(device_t dev);
 #define        RK_I2C_WRITE(sc, reg, val)      bus_write_4((sc)->res[0], 
(reg), (val))
 
 static uint32_t
-rk_i2c_get_clkdiv(struct rk_i2c_softc *sc, uint64_t speed)
+rk_i2c_get_clkdiv(struct rk_i2c_softc *sc, uint32_t speed)
 {
        uint64_t sclk_freq;
        uint32_t clkdiv;
@@ -213,7 +209,7 @@ rk_i2c_reset(device_t dev, u_char speed, u_char addr, 
        return (0);
 }
 
-static void
+static uint8_t
 rk_i2c_fill_tx(struct rk_i2c_softc *sc)
 {
        uint32_t buf32;
@@ -221,7 +217,7 @@ rk_i2c_fill_tx(struct rk_i2c_softc *sc)
        int i, j, len;
 
        if (sc->msg == NULL || sc->msg->len == sc->cnt)
-               return;
+               return (0);
 
        len = sc->msg->len - sc->cnt;
        if (len > 8)
@@ -234,22 +230,23 @@ rk_i2c_fill_tx(struct rk_i2c_softc *sc)
                                break;
 
                        /* Fill the addr if needed */
-                       if (sc->cnt == 0) {
+                       if (sc->cnt == 0 && sc->tx_slave_addr) {
                                buf = sc->msg->slave;
+                               sc->tx_slave_addr = false;
+                       } else {
+                               buf = sc->msg->buf[sc->cnt];
+                               sc->cnt++;
                        }
-                       else
-                               buf = sc->msg->buf[sc->cnt - 1];
-
                        buf32 |= buf << (j * 8);
 
-                       sc->cnt++;
                }
-
                RK_I2C_WRITE(sc, RK_I2C_TXDATA_BASE + 4 * i, buf32);
 
                if (sc->cnt == sc->msg->len)
                        break;
        }
+
+       return (uint8_t)len;
 }
 
 static void
@@ -274,29 +271,11 @@ rk_i2c_drain_rx(struct rk_i2c_softc *sc)
                        buf32 = RK_I2C_READ(sc, RK_I2C_RXDATA_BASE + (i / 4) * 
4);
 
                buf8 = (buf32 >> ((i % 4) * 8)) & 0xFF;
-
                sc->msg->buf[sc->cnt++] = buf8;
        }
 }
 
 static void
-rk_i2c_send_start(struct rk_i2c_softc *sc)
-{
-       uint32_t reg;
-
-       RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STARTIEN);
-
-       sc->state = STATE_START;
-
-       reg = RK_I2C_READ(sc, RK_I2C_CON);
-       reg |= RK_I2C_CON_START;
-       reg |= RK_I2C_CON_EN;
-       reg &= ~RK_I2C_CON_MODE_MASK;
-       reg |= sc->mode << RK_I2C_CON_MODE_SHIFT;
-       RK_I2C_WRITE(sc, RK_I2C_CON, reg);
-}
-
-static void
 rk_i2c_send_stop(struct rk_i2c_softc *sc)
 {
        uint32_t reg;
@@ -311,18 +290,29 @@ rk_i2c_send_stop(struct rk_i2c_softc *sc)
 }
 
 static void
-rk_i2c_intr(void *arg)
+rk_i2c_intr_locked(struct rk_i2c_softc *sc)
 {
-       struct rk_i2c_softc *sc;
        uint32_t reg;
 
-       sc = (struct rk_i2c_softc *)arg;
+       sc->ipd = RK_I2C_READ(sc, RK_I2C_IPD);
 
-       RK_I2C_LOCK(sc);
+       /* Something to handle? */
+       if ((sc->ipd & RK_I2C_IPD_ALL) == 0)
+               return;
 
-       sc->ipd = RK_I2C_READ(sc, RK_I2C_IPD);
        RK_I2C_WRITE(sc, RK_I2C_IPD, sc->ipd);
+       sc->ipd &= RK_I2C_IPD_ALL;
 
+       if (sc->ipd & RK_I2C_IPD_NAKRCVIPD) {
+               /* NACK received */
+               sc->ipd &= ~RK_I2C_IPD_NAKRCVIPD;
+               sc->nak_recv = 1;
+               /* XXXX last byte !!!, signal error !!! */
+               sc->transfer_done = 1;
+               sc->state = STATE_IDLE;
+               goto err;
+       }
+
        switch (sc->state) {
        case STATE_START:
                /* Disable start bit */
@@ -359,10 +349,12 @@ rk_i2c_intr(void *arg)
 
                break;
        case STATE_WRITE:
-               if (sc->cnt == sc->msg->len)
+               if (sc->cnt == sc->msg->len &&
+                    !(sc->msg->flags & IIC_M_NOSTOP)) {
                        rk_i2c_send_stop(sc);
-
-               break;
+                       break;
+               }
+               /* passthru */
        case STATE_STOP:
                /* Disable stop bit */
                reg = RK_I2C_READ(sc, RK_I2C_CON);
@@ -376,77 +368,177 @@ rk_i2c_intr(void *arg)
                break;
        }
 
+err:
        wakeup(sc);
+}
+
+static void
+rk_i2c_intr(void *arg)
+{
+       struct rk_i2c_softc *sc;
+
+       sc = (struct rk_i2c_softc *)arg;
+
+       RK_I2C_LOCK(sc);
+       rk_i2c_intr_locked(sc);
        RK_I2C_UNLOCK(sc);
 }
 
+static void
+rk_i2c_start_xfer(struct rk_i2c_softc *sc, struct iic_msg *msg, boolean_t last)
+{
+       uint32_t reg;
+       uint8_t len;
+
+       sc->transfer_done = false;
+       sc->nak_recv = false;
+       sc->tx_slave_addr = false;
+       sc->cnt = 0;
+       sc->state = STATE_IDLE;
+       sc->msg = msg;
+       sc->msg_len = sc->msg->len;
+
+       reg = RK_I2C_READ(sc, RK_I2C_CON) & ~RK_I2C_CON_CTRL_MASK;
+       if (!(sc->msg->flags & IIC_M_NOSTART)) {
+               /* Stadard message */
+               if (sc->mode == RK_I2C_CON_MODE_TX) {
+                       sc->msg_len++;  /* Take slave address in account. */
+                       sc->tx_slave_addr = true;
+               }
+               sc->state = STATE_START;
+               reg |= RK_I2C_CON_START;
+
+               RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STARTIEN);
+       } else {
+               /* Continuation message */
+               if (sc->mode == RK_I2C_CON_MODE_RX) {
+                       sc->state = STATE_READ;
+                       if (last)
+                               reg |= RK_I2C_CON_LASTACK;
+
+                       RK_I2C_WRITE(sc, RK_I2C_MRXCNT, sc->msg->len);
+                       RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_MBRFIEN |
+                           RK_I2C_IEN_NAKRCVIEN);
+               } else {
+                       sc->state = STATE_WRITE;
+                       len = rk_i2c_fill_tx(sc);
+
+                       RK_I2C_WRITE(sc, RK_I2C_MTXCNT, len);
+
+                       RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_MBTFIEN |
+                           RK_I2C_IEN_NAKRCVIEN);
+               }
+       }
+       reg |= sc->mode << RK_I2C_CON_MODE_SHIFT;
+       reg |= RK_I2C_CON_EN;
+       RK_I2C_WRITE(sc, RK_I2C_CON, reg);
+}
+
 static int
 rk_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
 {
        struct rk_i2c_softc *sc;
        uint32_t reg;
-       int i, j, msgskip, err = 0;
+       bool last_msg;
+       int i, j, timeout, err;
 
        sc = device_get_softc(dev);
 
+       RK_I2C_LOCK(sc);
+
        while (sc->busy)
                mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0);
-
        sc->busy = 1;
 
-       err = clk_enable(sc->pclk);
-       if (err != 0) {
-               device_printf(dev, "cannot enable pclk clock\n");
-               goto out;
-       }
-       err = clk_enable(sc->sclk);
-       if (err != 0) {
-               device_printf(dev, "cannot enable i2c clock\n");
-               goto out;
-       }
+       /* Disable the module and interrupts */
+       RK_I2C_WRITE(sc, RK_I2C_CON, 0);
+       RK_I2C_WRITE(sc, RK_I2C_IEN, 0);
 
-       RK_I2C_LOCK(sc);
-
        /* Clean stale interrupts */
        RK_I2C_WRITE(sc, RK_I2C_IPD, RK_I2C_IPD_ALL);
 
-       for (i = 0; i < nmsgs; i += msgskip) {
-               if (nmsgs - i >= 2 && !(msgs[i].flags & IIC_M_RD) &&
-                 msgs[i + 1].flags & IIC_M_RD && msgs[i].len <= 4) {
+       err = 0;
+       for (i = 0; i < nmsgs; i++) {
+               /* Validate parameters. */
+               if (msgs == NULL || msgs[i].buf == NULL ||
+                   msgs[i].len == 0) {
+                       err = EINVAL;
+                       break;
+               }
+               /*
+                * If next message have NOSTART flag, then they both
+                * should be same type (read/write) and same address.
+                */
+               if (i < nmsgs - 1) {
+                       if ((msgs[i + 1].flags & IIC_M_NOSTART) &&
+                           ((msgs[i].flags & IIC_M_RD) !=
+                           (msgs[i + 1].flags & IIC_M_RD) ||
+                           (msgs[i].slave !=  msgs[i + 1].slave))) {
+                               err = EINVAL;
+                               break;
+                       }
+               }
+               /*
+                * Detect simple register read case.
+                * The first message should be IIC_M_WR | IIC_M_NOSTOP,
+                * next pure IIC_M_RD (no other flags allowed). Both
+                * messages should have same slave address.
+                */
+
+               if (nmsgs - i >= 2 && msgs[i].len < 4 &&
+                   msgs[i].flags == (IIC_M_WR  | IIC_M_NOSTOP) &&
+                   msgs[i + 1].flags == IIC_M_RD &&
+                   (msgs[i].slave & ~LSB) == (msgs[i + 1].slave & ~LSB)) {
                        sc->mode = RK_I2C_CON_MODE_RRX;
-                       msgskip = 2;
-                       sc->msg = &msgs[i + 1];
 
                        /* Write slave address */
-                       reg = msgs[i].slave | RK_I2C_MRXADDR_VALID(0);
+                       reg = msgs[i].slave & ~LSB;
+                       reg |= RK_I2C_MRXADDR_VALID(0);
                        RK_I2C_WRITE(sc, RK_I2C_MRXADDR, reg);
+
                        /* Write slave register address */
-                       for (j = 0, reg = 0; j < msgs[i].len; j++) {
+                       reg = 0;
+                       for (j = 0; j < msgs[i].len ; j++) {
                                reg |= (msgs[i].buf[j] & 0xff) << (j * 8);
                                reg |= RK_I2C_MRXADDR_VALID(j);
                        }
-
                        RK_I2C_WRITE(sc, RK_I2C_MRXRADDR, reg);
+
+                       i++;
                } else {
                        if (msgs[i].flags & IIC_M_RD) {
-                               sc->mode = RK_I2C_CON_MODE_RX;
-                               msgs[i].slave |= LSB;
-                       }
-                       else {
+                               if (msgs[i].flags & IIC_M_NOSTART) {
+                                       sc->mode = RK_I2C_CON_MODE_RX;
+                               } else {
+                                       sc->mode = RK_I2C_CON_MODE_RRX;
+                                       reg = msgs[i].slave & LSB;
+                                       reg |= RK_I2C_MRXADDR_VALID(0);
+                                       RK_I2C_WRITE(sc, RK_I2C_MRXADDR, reg);
+                                       RK_I2C_WRITE(sc, RK_I2C_MRXRADDR, 0);
+                               }
+                       } else {
                                sc->mode = RK_I2C_CON_MODE_TX;
-                               msgs[i].slave &= ~LSB;
                        }
-                       msgskip = 1;
-                       sc->msg = &msgs[i];
                }
+               /* last message ? */
+               last_msg = (i > nmsgs - 1) ||
+                   !(msgs[i + 1].flags & IIC_M_NOSTART);
+               rk_i2c_start_xfer(sc, msgs + i, last_msg);
 
-               sc->transfer_done = 0;
-               sc->cnt = 0;
-               sc->state = STATE_IDLE;
-               rk_i2c_send_start(sc);
-
-               while (err == 0 && sc->transfer_done != 1) {
-                       err = msleep(sc, &sc->mtx, 0, "rk_i2c", 10 * hz);
+               if (cold) {
+                       for(timeout = 10000; timeout > 0; timeout--)  {
+                               rk_i2c_intr_locked(sc);
+                               if (sc->transfer_done != 0)
+                                       break;
+                               DELAY(100);
+                       }
+                       if (timeout <= 0)
+                               err = ETIMEDOUT;
+               } else {
+                       while (err == 0 && sc->transfer_done != 1) {
+                               err = msleep(sc, &sc->mtx, PZERO, "rk_i2c",
+                                   10 * hz);
+                       }
                }
        }
 
@@ -457,19 +549,6 @@ rk_i2c_transfer(device_t dev, struct iic_msg *msgs, ui
        sc->busy = 0;
 
        RK_I2C_UNLOCK(sc);
-
-       err = clk_disable(sc->pclk);
-       if (err != 0) {
-               device_printf(dev, "cannot enable pclk clock\n");
-               goto out;
-       }
-       err = clk_disable(sc->sclk);
-       if (err != 0) {
-               device_printf(dev, "cannot enable i2c clock\n");
-               goto out;
-       }
-
-out:
        return (err);
 }
 
@@ -519,10 +598,23 @@ rk_i2c_attach(device_t dev)
                device_printf(dev, "cannot get i2c clock\n");
                goto fail;
        }
-       error = clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk);
+       error = clk_enable(sc->sclk);
        if (error != 0) {
+               device_printf(dev, "cannot enable i2c clock\n");
+               goto fail;
+       }
+       /* pclk clock is optional. */
+       error = clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk);
+       if (error != 0 && error != ENOENT) {
                device_printf(dev, "cannot get pclk clock\n");
                goto fail;
+       }
+       if (sc->sclk != NULL) {
+               error = clk_enable(sc->sclk);
+               if (error != 0) {
+                       device_printf(dev, "cannot enable pclk clock\n");
+                       goto fail;
+               }
        }
 
        sc->iicbus = device_add_child(dev, "iicbus", -1);

Modified: stable/12/sys/conf/files.arm64
==============================================================================
--- stable/12/sys/conf/files.arm64      Wed Mar  4 20:41:45 2020        
(r358639)
+++ stable/12/sys/conf/files.arm64      Wed Mar  4 20:43:29 2020        
(r358640)
@@ -278,6 +278,7 @@ cddl/dev/dtrace/aarch64/dtrace_subr.c                       
optional dtrac
 cddl/dev/fbt/aarch64/fbt_isa.c                         optional dtrace_fbt | 
dtraceall compile-with "${FBT_C}"
 
 # RockChip Drivers
+arm64/rockchip/rk3399_emmcphy.c                optional fdt rk_emmcphy 
soc_rockchip_rk3399
 arm64/rockchip/rk_i2c.c                        optional fdt rk_i2c 
soc_rockchip_rk3328 | fdt rk_i2c soc_rockchip_rk3399
 arm64/rockchip/rk805.c                 optional fdt rk805 soc_rockchip_rk3328 
| fdt rk805 soc_rockchip_rk3399
 arm64/rockchip/rk_grf.c                        optional fdt 
soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to