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