svn commit: r358640 - in stable/12/sys: arm64/conf arm64/rockchip conf

Emmanuel Vadot manu at FreeBSD.org
Wed Mar 4 20:43:32 UTC 2020


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 <ganbold at 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


More information about the svn-src-all mailing list