summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/arm64/conf/GENERIC4
-rw-r--r--sys/arch/arm64/conf/RAMDISK4
-rw-r--r--sys/dev/fdt/files.fdt6
-rw-r--r--sys/dev/fdt/rkiic.c375
4 files changed, 386 insertions, 3 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index ccc9f8ec3ed..071e023aac3 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.35 2017/08/25 10:29:54 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.36 2017/08/27 22:09:21 kettenis Exp $
#
# GENERIC machine description file
#
@@ -89,6 +89,8 @@ rkclock* at fdt? early 1
rkgrf* at fdt? early 1
rkpinctrl* at fdt? early 1
rkgpio* at fdt?
+rkiic* at fdt?
+iic* at rkiic?
rktemp* at fdt?
rkdwusb* at fdt?
dwmmc* at fdt?
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index 172b51f46f7..8c599bed372 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -1,4 +1,4 @@
-# $OpenBSD: RAMDISK,v 1.33 2017/08/27 12:42:22 kettenis Exp $
+# $OpenBSD: RAMDISK,v 1.34 2017/08/27 22:09:21 kettenis Exp $
#
# GENERIC machine description file
#
@@ -100,6 +100,8 @@ rkclock* at fdt? early 1
rkgrf* at fdt? early 1
rkpinctrl* at fdt? early 1
rkgpio* at fdt?
+rkiic* at fdt?
+iic* at rkiic?
rkdwusb* at fdt?
dwmmc* at fdt?
sdmmc* at dwmmc?
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index fa6198aa074..1e47803ac43 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -1,4 +1,4 @@
-# $OpenBSD: files.fdt,v 1.24 2017/08/25 20:09:34 patrick Exp $
+# $OpenBSD: files.fdt,v 1.25 2017/08/27 22:09:21 kettenis Exp $
#
# Config file and device description for machine-independent FDT code.
# Included by ports that need it.
@@ -92,6 +92,10 @@ device rkgpio
attach rkgpio at fdt
file dev/fdt/rkgpio.c rkgpio
+device rkiic: i2cbus
+attach rkiic at fdt
+file dev/fdt/rkiic.c rkiic
+
device rkpinctrl: fdt
attach rkpinctrl at fdt
file dev/fdt/rkpinctrl.c rkpinctrl
diff --git a/sys/dev/fdt/rkiic.c b/sys/dev/fdt/rkiic.c
new file mode 100644
index 00000000000..0260f96fd8a
--- /dev/null
+++ b/sys/dev/fdt/rkiic.c
@@ -0,0 +1,375 @@
+/* $OpenBSD: rkiic.c,v 1.1 2017/08/27 22:09:21 kettenis Exp $ */
+/*
+ * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#define _I2C_PRIVATE
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_pinctrl.h>
+#include <dev/ofw/fdt.h>
+
+/* Registers. */
+#define RKI2C_CON 0x0000
+#define RKI2C_CON_ACT2NAK (1 << 6)
+#define RKI2C_CON_ACK (0 << 5)
+#define RKI2C_CON_NAK (1 << 5)
+#define RKI2C_CON_STOP (1 << 4)
+#define RKI2C_CON_START (1 << 3)
+#define RKI2C_CON_I2C_MODE_MASK (3 << 1)
+#define RKI2C_CON_I2C_MODE_TX (0 << 1)
+#define RKI2C_CON_I2C_MODE_RRX (1 << 1)
+#define RKI2C_CON_I2C_MODE_RX (2 << 1)
+#define RKI2C_CON_I2C_MODE_BROKEN (3 << 1)
+#define RKI2C_CON_I2C_EN (1 << 0)
+#define RKI2C_CLKDIV 0x0004
+#define RKI2C_MRXADDR 0x0008
+#define RKI2C_MRXADDR_ADDLVLD (1 << 24)
+#define RKI2C_MRXRADDR 0x000c
+#define RKI2C_MRXRADDR_SRADDLVLD (1 << 24)
+#define RKI2C_MTXCNT 0x0010
+#define RKI2C_MRXCNT 0x0014
+#define RKI2C_IEN 0x0018
+#define RKI2C_IEN_START (1 << 4)
+#define RKI2C_IPD 0x001c
+#define RKI2C_IPD_STOP (1 << 5)
+#define RKI2C_IPD_START (1 << 4)
+#define RKI2C_IPD_MBRF (1 << 3)
+#define RKI2C_IPD_MBTF (1 << 2)
+#define RKI2C_IPD_ALL 0xff
+#define RKI2C_FCNT 0x0020
+#define RKI2C_SCL_OE_DB 0x0024
+#define RKI2C_TXDATA0 0x0100
+#define RKI2C_RXDATA0 0x0200
+#define RKI2C_ST 0x0220
+
+#define HREAD4(sc, reg) \
+ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+#define HSET4(sc, reg, bits) \
+ HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
+#define HCLR4(sc, reg, bits) \
+ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
+
+struct rkiic_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ int sc_node;
+ struct i2c_controller sc_ic;
+};
+
+int rkiic_match(struct device *, void *, void *);
+void rkiic_attach(struct device *, struct device *, void *);
+
+struct cfattach rkiic_ca = {
+ sizeof (struct rkiic_softc), rkiic_match, rkiic_attach
+};
+
+struct cfdriver rkiic_cd = {
+ NULL, "rkiic", DV_DULL
+};
+
+int rkiic_acquire_bus(void *, int);
+void rkiic_release_bus(void *, int);
+int rkiic_send_start(void *, int);
+int rkiic_send_stop(void *, int);
+int rkiic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
+ void *, size_t, int);
+
+void rkiic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
+
+int
+rkiic_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "rockchip,rk3399-i2c");
+}
+
+void
+rkiic_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rkiic_softc *sc = (struct rkiic_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ struct i2cbus_attach_args iba;
+ uint32_t clock_speed, bus_speed;
+ uint32_t div, divl, divh;
+ uint32_t clkdivl, clkdivh;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ sc->sc_iot = faa->fa_iot;
+ sc->sc_node = faa->fa_node;
+
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ printf("\n");
+
+ pinctrl_byname(sc->sc_node, "default");
+
+ clock_enable(sc->sc_node, "i2c");
+ clock_enable(sc->sc_node, "pclk");
+
+ clock_speed = clock_get_frequency(sc->sc_node, "i2c");
+ bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000);
+
+ div = 2;
+ while (clock_speed > div * bus_speed * 8)
+ div++;
+ divl = div / 2;
+ divh = div - divl;
+ clkdivl = (divl - 1) & 0xffff;
+ clkdivh = (divh - 1) & 0xffff;
+ HWRITE4(sc, RKI2C_CLKDIV, clkdivh << 16 | clkdivl);
+
+ sc->sc_ic.ic_cookie = sc;
+ sc->sc_ic.ic_acquire_bus = rkiic_acquire_bus;
+ sc->sc_ic.ic_release_bus = rkiic_release_bus;
+ sc->sc_ic.ic_exec = rkiic_exec;
+
+ /* Configure its children */
+ memset(&iba, 0, sizeof(iba));
+ iba.iba_name = "iic";
+ iba.iba_tag = &sc->sc_ic;
+ iba.iba_bus_scan = rkiic_bus_scan;
+ iba.iba_bus_scan_arg = &sc->sc_node;
+
+ config_found(&sc->sc_dev, &iba, iicbus_print);
+}
+
+int
+rkiic_acquire_bus(void *cookie, int flags)
+{
+ struct rkiic_softc *sc = cookie;
+
+ HSET4(sc, RKI2C_CON, RKI2C_CON_I2C_EN);
+ return 0;
+}
+
+void
+rkiic_release_bus(void *cookie, int flags)
+{
+ struct rkiic_softc *sc = cookie;
+
+ HCLR4(sc, RKI2C_CON, RKI2C_CON_I2C_EN);
+}
+
+int
+rkiic_send_start(void *cookie, int flags)
+{
+ struct rkiic_softc *sc = cookie;
+ int timo;
+
+ HSET4(sc, RKI2C_IPD, RKI2C_IPD_START);
+ HSET4(sc, RKI2C_CON, RKI2C_CON_START);
+ for (timo = 1000; timo > 0; timo--) {
+ if (HREAD4(sc, RKI2C_IPD) & RKI2C_IPD_START)
+ break;
+ delay(10);
+ }
+ HCLR4(sc, RKI2C_CON, RKI2C_CON_START);
+ if (timo == 0)
+ return ETIMEDOUT;
+ return 0;
+}
+
+int
+rkiic_send_stop(void *cookie, int flags)
+{
+ struct rkiic_softc *sc = cookie;
+ int timo;
+
+ HSET4(sc, RKI2C_IPD, RKI2C_IPD_STOP);
+ HSET4(sc, RKI2C_CON, RKI2C_CON_STOP);
+ for (timo = 1000; timo > 0; timo--) {
+ if (HREAD4(sc, RKI2C_IPD) & RKI2C_IPD_STOP)
+ break;
+ delay(10);
+ }
+ HCLR4(sc, RKI2C_CON, RKI2C_CON_STOP);
+ if (timo == 0)
+ return ETIMEDOUT;
+ return 0;
+}
+
+int
+rkiic_write(struct rkiic_softc *sc, i2c_addr_t addr, const void *cmd,
+ size_t cmdlen, void *buf, size_t buflen)
+{
+ uint8_t txbuf[32];
+ int len = 0;
+ int timo, i;
+
+ /*
+ * Lump slave address, command and data into one single buffer
+ * and transfer it in a single operation.
+ */
+ txbuf[len++] = addr << 1;
+ for (i = 0; i < cmdlen; i++)
+ txbuf[len++] = ((uint8_t *)cmd)[i];
+ for (i = 0; i < buflen; i++)
+ txbuf[len++] = ((uint8_t *)buf)[i];
+
+ for (i = 0; i < len; i += 4) {
+ HWRITE4(sc, RKI2C_TXDATA0 + i,
+ *((uint32_t *)&txbuf[i]));
+ }
+
+ /* Start operation. */
+ HWRITE4(sc, RKI2C_MTXCNT, len);
+
+ /* Wait for completion. */
+ for (timo = 1000; timo > 0; timo--) {
+ if (HREAD4(sc, RKI2C_IPD) & RKI2C_IPD_MBTF)
+ break;
+ delay(10);
+ }
+ if (timo == 0)
+ return ETIMEDOUT;
+
+ return 0;
+}
+
+int
+rkiic_read(struct rkiic_softc *sc, i2c_addr_t addr, const void *cmd,
+ size_t cmdlen, void *buf, size_t buflen)
+{
+ uint32_t mrxraddr, rxdata;
+ int timo, i;
+
+ HWRITE4(sc, RKI2C_MRXADDR, (addr << 1) | RKI2C_MRXADDR_ADDLVLD);
+
+ /* Send the command as "register address". */
+ mrxraddr = 0;
+ for (i = 0; i < cmdlen; i++) {
+ mrxraddr |= ((uint8_t *)cmd)[i] << (i * 8);
+ mrxraddr |= RKI2C_MRXRADDR_SRADDLVLD << i;
+ }
+ HWRITE4(sc, RKI2C_MRXRADDR, mrxraddr);
+
+ /* Indicate that we're done after this operation. */
+ HSET4(sc, RKI2C_CON, RKI2C_CON_NAK);
+
+ /* Start operation. */
+ HWRITE4(sc, RKI2C_MRXCNT, buflen);
+
+ /* Wait for completion. */
+ for (timo = 1000; timo > 0; timo--) {
+ if (HREAD4(sc, RKI2C_IPD) & RKI2C_IPD_MBRF)
+ break;
+ delay(10);
+ }
+ if (timo == 0)
+ return ETIMEDOUT;
+
+ for (i = 0; i < buflen; i++) {
+ if (i % 4 == 0)
+ rxdata = HREAD4(sc, RKI2C_RXDATA0 + i);
+ ((uint8_t *)buf)[i] = rxdata;
+ rxdata >>= 8;
+ }
+
+ return 0;
+}
+
+int
+rkiic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
+ size_t cmdlen, void *buf, size_t buflen, int flags)
+{
+ struct rkiic_softc *sc = cookie;
+ uint32_t con;
+ int error;
+
+ if (cmdlen > 3 || buflen > 28)
+ return EINVAL;
+
+ /* Clear interrupts. */
+ HWRITE4(sc, RKI2C_IPD, RKI2C_IPD_ALL);
+
+ /* Configure transfer more. */
+ con = HREAD4(sc, RKI2C_CON);
+ con &= ~RKI2C_CON_I2C_MODE_MASK;
+ if (I2C_OP_WRITE_P(op))
+ con |= RKI2C_CON_I2C_MODE_TX;
+ else
+ con |= RKI2C_CON_I2C_MODE_RRX;
+ con &= ~RKI2C_CON_NAK;
+ con |= RKI2C_CON_ACT2NAK;
+ HWRITE4(sc, RKI2C_CON, con);
+
+ error = rkiic_send_start(sc, flags);
+ if (error)
+ return error;
+
+ if (I2C_OP_WRITE_P(op))
+ error = rkiic_write(sc, addr, cmd, cmdlen, buf, buflen);
+ else
+ error = rkiic_read(sc, addr, cmd, cmdlen, buf, buflen);
+
+ if (I2C_OP_STOP_P(op))
+ rkiic_send_stop(sc, flags);
+
+ return error;
+}
+
+void
+rkiic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
+{
+ int iba_node = *(int *)arg;
+ struct i2c_attach_args ia;
+ char name[32];
+ uint32_t reg[1];
+ int node;
+
+ for (node = OF_child(iba_node); node; node = OF_peer(node)) {
+ memset(name, 0, sizeof(name));
+ memset(reg, 0, sizeof(reg));
+
+ if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
+ continue;
+ if (name[0] == '\0')
+ continue;
+
+ if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
+ continue;
+
+ memset(&ia, 0, sizeof(ia));
+ ia.ia_tag = iba->iba_tag;
+ ia.ia_addr = bemtoh32(&reg[0]);
+ ia.ia_name = name;
+ ia.ia_cookie = &node;
+ config_found(self, &ia, iic_print);
+ }
+}