Module Name: src
Committed By: jmcneill
Date: Sun May 10 23:50:21 UTC 2015
Modified Files:
src/sys/arch/arm/nvidia: files.tegra tegra_car.c tegra_carreg.h
tegra_intr.h tegra_io.c tegra_reg.h tegra_var.h
Added Files:
src/sys/arch/arm/nvidia: tegra_i2c.c tegra_i2creg.h
Log Message:
Tegra I2C driver
To generate a diff of this commit:
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/nvidia/files.tegra \
src/sys/arch/arm/nvidia/tegra_car.c src/sys/arch/arm/nvidia/tegra_reg.h
cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/nvidia/tegra_carreg.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/nvidia/tegra_i2c.c \
src/sys/arch/arm/nvidia/tegra_i2creg.h
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/nvidia/tegra_intr.h
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/nvidia/tegra_io.c
cvs rdiff -u -r1.13 -r1.14 src/sys/arch/arm/nvidia/tegra_var.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/arm/nvidia/files.tegra
diff -u src/sys/arch/arm/nvidia/files.tegra:1.8 src/sys/arch/arm/nvidia/files.tegra:1.9
--- src/sys/arch/arm/nvidia/files.tegra:1.8 Thu May 7 23:55:11 2015
+++ src/sys/arch/arm/nvidia/files.tegra Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-# $NetBSD: files.tegra,v 1.8 2015/05/07 23:55:11 jmcneill Exp $
+# $NetBSD: files.tegra,v 1.9 2015/05/10 23:50:21 jmcneill Exp $
#
# Configuration info for NVIDIA Tegra ARM Peripherals
#
@@ -50,6 +50,11 @@ file arch/arm/nvidia/tegra_mpio.c tegra
attach com at tegraio with tegra_com
file arch/arm/nvidia/tegra_com.c tegra_com needs-flag
+# I2C
+device tegrai2c: i2cbus, i2cexec
+attach tegrai2c at tegraio with tegra_i2c
+file arch/arm/nvidia/tegra_i2c.c tegra_i2c
+
# RTC
device tegrartc
attach tegrartc at tegraio with tegra_rtc
Index: src/sys/arch/arm/nvidia/tegra_car.c
diff -u src/sys/arch/arm/nvidia/tegra_car.c:1.8 src/sys/arch/arm/nvidia/tegra_car.c:1.9
--- src/sys/arch/arm/nvidia/tegra_car.c:1.8 Sun May 10 15:31:48 2015
+++ src/sys/arch/arm/nvidia/tegra_car.c Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_car.c,v 1.8 2015/05/10 15:31:48 jmcneill Exp $ */
+/* $NetBSD: tegra_car.c,v 1.9 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include "locators.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.8 2015/05/10 15:31:48 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.9 2015/05/10 23:50:21 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -437,3 +437,70 @@ tegra_car_periph_sata_enable(void)
bus_space_write_4(bst, bsh, CAR_RST_DEV_W_CLR_REG, CAR_DEV_W_SATACOLD);
bus_space_write_4(bst, bsh, CAR_RST_DEV_V_CLR_REG, CAR_DEV_V_SATA);
}
+
+int
+tegra_car_periph_i2c_enable(u_int port, u_int rate)
+{
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ bus_size_t rst_reg, enb_reg, clksrc_reg;
+ uint32_t dev_bit;
+
+ tegra_car_get_bs(&bst, &bsh);
+
+ switch (port) {
+ case 0:
+ rst_reg = CAR_RST_DEV_L_SET_REG;
+ enb_reg = CAR_CLK_ENB_L_SET_REG;
+ dev_bit = CAR_DEV_L_I2C1;
+ clksrc_reg = CAR_CLKSRC_I2C1_REG;
+ break;
+ case 1:
+ rst_reg = CAR_RST_DEV_H_SET_REG;
+ enb_reg = CAR_CLK_ENB_H_SET_REG;
+ dev_bit = CAR_DEV_H_I2C2;
+ clksrc_reg = CAR_CLKSRC_I2C2_REG;
+ break;
+ case 2:
+ rst_reg = CAR_RST_DEV_U_SET_REG;
+ enb_reg = CAR_CLK_ENB_U_SET_REG;
+ dev_bit = CAR_DEV_U_I2C3;
+ clksrc_reg = CAR_CLKSRC_I2C3_REG;
+ break;
+ case 3:
+ rst_reg = CAR_RST_DEV_V_SET_REG;
+ enb_reg = CAR_CLK_ENB_V_SET_REG;
+ dev_bit = CAR_DEV_V_I2C4;
+ clksrc_reg = CAR_CLKSRC_I2C4_REG;
+ break;
+ case 4:
+ rst_reg = CAR_RST_DEV_H_SET_REG;
+ enb_reg = CAR_CLK_ENB_V_SET_REG;
+ dev_bit = CAR_DEV_H_I2C5;
+ clksrc_reg = CAR_CLKSRC_I2C5_REG;
+ break;
+ case 5:
+ rst_reg = CAR_RST_DEV_X_SET_REG;
+ enb_reg = CAR_CLK_ENB_X_SET_REG;
+ dev_bit = CAR_DEV_X_I2C6;
+ clksrc_reg = CAR_CLKSRC_I2C6_REG;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ /* Enter reset, enable clock */
+ bus_space_write_4(bst, bsh, rst_reg, dev_bit);
+ bus_space_write_4(bst, bsh, enb_reg, dev_bit);
+
+ /* Set clock source to PLLP */
+ const u_int div = howmany(tegra_car_pllp0_rate() / 1000, rate / 1000);
+ bus_space_write_4(bst, bsh, clksrc_reg,
+ __SHIFTIN(CAR_CLKSRC_I2C_SRC_PLLP_OUT0, CAR_CLKSRC_I2C_SRC) |
+ __SHIFTIN(div - 1, CAR_CLKSRC_I2C_DIV));
+
+ /* Leave reset */
+ bus_space_write_4(bst, bsh, rst_reg+4, dev_bit);
+
+ return 0;
+}
Index: src/sys/arch/arm/nvidia/tegra_reg.h
diff -u src/sys/arch/arm/nvidia/tegra_reg.h:1.8 src/sys/arch/arm/nvidia/tegra_reg.h:1.9
--- src/sys/arch/arm/nvidia/tegra_reg.h:1.8 Thu May 7 23:55:11 2015
+++ src/sys/arch/arm/nvidia/tegra_reg.h Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_reg.h,v 1.8 2015/05/07 23:55:11 jmcneill Exp $ */
+/* $NetBSD: tegra_reg.h,v 1.9 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -78,6 +78,18 @@
#define TEGRA_UARTC_SIZE 0x100
#define TEGRA_UARTD_OFFSET 0x00006300
#define TEGRA_UARTD_SIZE 0x100
+#define TEGRA_I2C1_OFFSET 0x0000c000
+#define TEGRA_I2C1_SIZE 0x100
+#define TEGRA_I2C2_OFFSET 0x0000c400
+#define TEGRA_I2C2_SIZE 0x100
+#define TEGRA_I2C3_OFFSET 0x0000c500
+#define TEGRA_I2C3_SIZE 0x100
+#define TEGRA_I2C4_OFFSET 0x0000c700
+#define TEGRA_I2C4_SIZE 0x100
+#define TEGRA_I2C5_OFFSET 0x0000d000
+#define TEGRA_I2C5_SIZE 0x100
+#define TEGRA_I2C6_OFFSET 0x0000d100
+#define TEGRA_I2C6_SIZE 0x100
#define TEGRA_RTC_OFFSET 0x0000e000
#define TEGRA_RTC_SIZE 0x100
#define TEGRA_KBC_OFFSET 0x0000e200
Index: src/sys/arch/arm/nvidia/tegra_carreg.h
diff -u src/sys/arch/arm/nvidia/tegra_carreg.h:1.10 src/sys/arch/arm/nvidia/tegra_carreg.h:1.11
--- src/sys/arch/arm/nvidia/tegra_carreg.h:1.10 Sun May 10 15:31:48 2015
+++ src/sys/arch/arm/nvidia/tegra_carreg.h Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_carreg.h,v 1.10 2015/05/10 15:31:48 jmcneill Exp $ */
+/* $NetBSD: tegra_carreg.h,v 1.11 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -100,6 +100,22 @@
#define CAR_PLLE_MISC_REG 0xec
+#define CAR_CLKSRC_I2C1_REG 0x124
+#define CAR_CLKSRC_I2C2_REG 0x198
+#define CAR_CLKSRC_I2C3_REG 0x1b8
+#define CAR_CLKSRC_I2C4_REG 0x3c4
+#define CAR_CLKSRC_I2C5_REG 0x128
+#define CAR_CLKSRC_I2C6_REG 0x65c
+
+#define CAR_CLKSRC_I2C_SRC __BITS(31,29)
+#define CAR_CLKSRC_I2C_SRC_PLLP_OUT0 0
+#define CAR_CLKSRC_I2C_SRC_PLLC2_OUT0 1
+#define CAR_CLKSRC_I2C_SRC_PLLC_OUT0 2
+#define CAR_CLKSRC_I2C_SRC_PLLC3_OUT0 3
+#define CAR_CLKSRC_I2C_SRC_PLLM_OUT0 4
+#define CAR_CLKSRC_I2C_SRC_CLK_M 6
+#define CAR_CLKSRC_I2C_DIV __BITS(15,0)
+
#define CAR_CLKSRC_UARTA_REG 0x178
#define CAR_CLKSRC_UARTB_REG 0x17c
#define CAR_CLKSRC_UARTC_REG 0x1a0
@@ -140,6 +156,8 @@
#define CAR_RST_DEV_V_CLR_REG 0x434
#define CAR_RST_DEV_W_SET_REG 0x438
#define CAR_RST_DEV_W_CLR_REG 0x43c
+#define CAR_RST_DEV_X_SET_REG 0x290
+#define CAR_RST_DEV_X_CLR_REG 0x294
#define CAR_CLK_ENB_L_SET_REG 0x320
#define CAR_CLK_ENB_L_CLR_REG 0x324
@@ -151,6 +169,8 @@
#define CAR_CLK_ENB_V_CLR_REG 0x444
#define CAR_CLK_ENB_W_SET_REG 0x448
#define CAR_CLK_ENB_W_CLR_REG 0x44c
+#define CAR_CLK_ENB_X_SET_REG 0x284
+#define CAR_CLK_ENB_X_CLR_REG 0x288
#define CAR_DEV_L_CACHE2 __BIT(31)
#define CAR_DEV_L_I2S0 __BIT(30)
@@ -261,6 +281,21 @@
#define CAR_DEV_W_SATACOLD __BIT(1)
#define CAR_DEV_W_HDA2HDMICODEC __BIT(0)
+#define CAR_DEV_X_AMX1 __BIT(25)
+#define CAR_DEV_X_GPU __BIT(24)
+#define CAR_DEV_X_SOR0 __BIT(22)
+#define CAR_DEV_X_DPAUX __BIT(21)
+#define CAR_DEV_X_ADX1 __BIT(20)
+#define CAR_DEV_X_VIC __BIT(18)
+#define CAR_DEV_X_CLK72MHZ __BIT(17)
+#define CAR_DEV_X_HDMI_AUDIO __BIT(16)
+#define CAR_DEV_X_EMC_DLL __BIT(14)
+#define CAR_DEV_X_VIM2_CLK __BIT(11)
+#define CAR_DEV_X_I2C6 __BIT(6)
+#define CAR_DEV_X_CAM_MCLK2 __BIT(5)
+#define CAR_DEV_X_CAM_MCLK __BIT(4)
+#define CAR_DEV_X_SPARE __BIT(0)
+
#define CAR_UTMIP_PLL_CFG0_REG 0x480
#define CAR_UTMIP_PLL_CFG1_REG 0x484
Index: src/sys/arch/arm/nvidia/tegra_intr.h
diff -u src/sys/arch/arm/nvidia/tegra_intr.h:1.3 src/sys/arch/arm/nvidia/tegra_intr.h:1.4
--- src/sys/arch/arm/nvidia/tegra_intr.h:1.3 Sun May 3 01:07:44 2015
+++ src/sys/arch/arm/nvidia/tegra_intr.h Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_intr.h,v 1.3 2015/05/03 01:07:44 jmcneill Exp $ */
+/* $NetBSD: tegra_intr.h,v 1.4 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -46,12 +46,18 @@
#define TEGRA_INTR_SDMMC4 TEGRA_INTR(31)
#define TEGRA_INTR_UARTA TEGRA_INTR(36)
#define TEGRA_INTR_UARTB TEGRA_INTR(37)
+#define TEGRA_INTR_I2C1 TEGRA_INTR(38)
#define TEGRA_INTR_UARTC TEGRA_INTR(46)
+#define TEGRA_INTR_I2C5 TEGRA_INTR(53)
+#define TEGRA_INTR_I2C6 TEGRA_INTR(63)
#define TEGRA_INTR_HDA TEGRA_INTR(81)
+#define TEGRA_INTR_I2C2 TEGRA_INTR(84)
#define TEGRA_INTR_UARTD TEGRA_INTR(90)
+#define TEGRA_INTR_I2C3 TEGRA_INTR(92)
#define TEGRA_INTR_USB3 TEGRA_INTR(97)
#define TEGRA_INTR_PCIE_INT TEGRA_INTR(98)
#define TEGRA_INTR_PCIE_MSI TEGRA_INTR(99)
#define TEGRA_INTR_PCIE_WAKE TEGRA_INTR(100)
+#define TEGRA_INTR_I2C4 TEGRA_INTR(120)
#endif /* _ARM_TEGRA_INTR_H */
Index: src/sys/arch/arm/nvidia/tegra_io.c
diff -u src/sys/arch/arm/nvidia/tegra_io.c:1.7 src/sys/arch/arm/nvidia/tegra_io.c:1.8
--- src/sys/arch/arm/nvidia/tegra_io.c:1.7 Thu May 7 23:55:11 2015
+++ src/sys/arch/arm/nvidia/tegra_io.c Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_io.c,v 1.7 2015/05/07 23:55:11 jmcneill Exp $ */
+/* $NetBSD: tegra_io.c,v 1.8 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include "opt_tegra.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_io.c,v 1.7 2015/05/07 23:55:11 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_io.c,v 1.8 2015/05/10 23:50:21 jmcneill Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -75,6 +75,18 @@ static const struct tegra_locators tegra
TEGRA_PMC_OFFSET, TEGRA_PMC_SIZE, NOPORT, NOINTR },
{ "tegrampio",
TEGRA_MPIO_OFFSET, TEGRA_MPIO_SIZE, NOPORT, NOINTR },
+ { "tegrai2c",
+ TEGRA_I2C1_OFFSET, TEGRA_I2C1_SIZE, 0, TEGRA_INTR_I2C1 },
+ { "tegrai2c",
+ TEGRA_I2C2_OFFSET, TEGRA_I2C2_SIZE, 1, TEGRA_INTR_I2C2 },
+ { "tegrai2c",
+ TEGRA_I2C3_OFFSET, TEGRA_I2C3_SIZE, 2, TEGRA_INTR_I2C3 },
+ { "tegrai2c",
+ TEGRA_I2C4_OFFSET, TEGRA_I2C4_SIZE, 3, TEGRA_INTR_I2C4 },
+ { "tegrai2c",
+ TEGRA_I2C5_OFFSET, TEGRA_I2C5_SIZE, 4, TEGRA_INTR_I2C5 },
+ { "tegrai2c",
+ TEGRA_I2C6_OFFSET, TEGRA_I2C6_SIZE, 5, TEGRA_INTR_I2C6 },
{ "com",
TEGRA_UARTA_OFFSET, TEGRA_UARTA_SIZE, 0, TEGRA_INTR_UARTA },
{ "com",
Index: src/sys/arch/arm/nvidia/tegra_var.h
diff -u src/sys/arch/arm/nvidia/tegra_var.h:1.13 src/sys/arch/arm/nvidia/tegra_var.h:1.14
--- src/sys/arch/arm/nvidia/tegra_var.h:1.13 Sun May 10 15:31:48 2015
+++ src/sys/arch/arm/nvidia/tegra_var.h Sun May 10 23:50:21 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_var.h,v 1.13 2015/05/10 15:31:48 jmcneill Exp $ */
+/* $NetBSD: tegra_var.h,v 1.14 2015/05/10 23:50:21 jmcneill Exp $ */
/*-
* Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -85,6 +85,7 @@ int tegra_car_periph_sdmmc_set_div(u_int
int tegra_car_periph_usb_enable(u_int);
void tegra_car_periph_hda_enable(void);
void tegra_car_periph_sata_enable(void);
+int tegra_car_periph_i2c_enable(u_int, u_int);
void tegra_car_utmip_init(void);
void tegra_car_utmip_enable(u_int);
Added files:
Index: src/sys/arch/arm/nvidia/tegra_i2c.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_i2c.c:1.1
--- /dev/null Sun May 10 23:50:21 2015
+++ src/sys/arch/arm/nvidia/tegra_i2c.c Sun May 10 23:50:21 2015
@@ -0,0 +1,352 @@
+/* $NetBSD: tegra_i2c.c,v 1.1 2015/05/10 23:50:21 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <[email protected]>
+ * 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 ``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 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.
+ */
+
+#include "locators.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_i2c.c,v 1.1 2015/05/10 23:50:21 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <arm/nvidia/tegra_reg.h>
+#include <arm/nvidia/tegra_i2creg.h>
+#include <arm/nvidia/tegra_var.h>
+
+static int tegra_i2c_match(device_t, cfdata_t, void *);
+static void tegra_i2c_attach(device_t, device_t, void *);
+
+struct tegra_i2c_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ void * sc_ih;
+
+ struct i2c_controller sc_ic;
+ kmutex_t sc_lock;
+ kcondvar_t sc_cv;
+ device_t sc_i2cdev;
+};
+
+static void tegra_i2c_init(struct tegra_i2c_softc *);
+static int tegra_i2c_intr(void *);
+
+static int tegra_i2c_acquire_bus(void *, int);
+static void tegra_i2c_release_bus(void *, int);
+static int tegra_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
+ size_t, void *, size_t, int);
+
+static int tegra_i2c_wait(struct tegra_i2c_softc *, int);
+static int tegra_i2c_write(struct tegra_i2c_softc *, i2c_addr_t,
+ const uint8_t *, size_t, int);
+static int tegra_i2c_read(struct tegra_i2c_softc *, i2c_addr_t, uint8_t *,
+ size_t, int);
+
+CFATTACH_DECL_NEW(tegra_i2c, sizeof(struct tegra_i2c_softc),
+ tegra_i2c_match, tegra_i2c_attach, NULL, NULL);
+
+#define I2C_WRITE(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+#define I2C_READ(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define I2C_SET_CLEAR(sc, reg, setval, clrval) \
+ tegra_reg_set_clear((sc)->sc_bst, (sc)->sc_bsh, (reg), (setval), (clrval))
+
+static int
+tegra_i2c_match(device_t parent, cfdata_t cf, void *aux)
+{
+ struct tegraio_attach_args * const tio = aux;
+ const struct tegra_locators * const loc = &tio->tio_loc;
+
+ if (loc->loc_port == TEGRAIOCF_PORT_DEFAULT)
+ return 0;
+
+ return 1;
+}
+
+static void
+tegra_i2c_attach(device_t parent, device_t self, void *aux)
+{
+ struct tegra_i2c_softc * const sc = device_private(self);
+ struct tegraio_attach_args * const tio = aux;
+ const struct tegra_locators * const loc = &tio->tio_loc;
+ struct i2cbus_attach_args iba;
+
+ sc->sc_dev = self;
+ sc->sc_bst = tio->tio_bst;
+ bus_space_subregion(tio->tio_bst, tio->tio_bsh,
+ loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
+ cv_init(&sc->sc_cv, device_xname(self));
+
+ aprint_naive("\n");
+ aprint_normal(": I2C%d\n", loc->loc_port + 1);
+
+ sc->sc_ih = intr_establish(loc->loc_intr, IPL_VM, IST_LEVEL|IST_MPSAFE,
+ tegra_i2c_intr, sc);
+ if (sc->sc_ih == NULL) {
+ aprint_error_dev(self, "couldn't establish interrupt %d\n",
+ loc->loc_intr);
+ return;
+ }
+ aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intr);
+
+ /* Recommended setting for standard mode */
+ tegra_car_periph_i2c_enable(loc->loc_port, 204000000);
+
+ tegra_i2c_init(sc);
+
+ sc->sc_ic.ic_cookie = sc;
+ sc->sc_ic.ic_acquire_bus = tegra_i2c_acquire_bus;
+ sc->sc_ic.ic_release_bus = tegra_i2c_release_bus;
+ sc->sc_ic.ic_exec = tegra_i2c_exec;
+
+ iba.iba_tag = &sc->sc_ic;
+ sc->sc_i2cdev = config_found_ia(self, "i2cbus", &iba, iicbus_print);
+}
+
+static void
+tegra_i2c_init(struct tegra_i2c_softc *sc)
+{
+ I2C_WRITE(sc, I2C_CLK_DIVISOR_REG,
+ __SHIFTIN(0x19, I2C_CLK_DIVISOR_STD_FAST_MODE) |
+ __SHIFTIN(0x1, I2C_CLK_DIVISOR_HSMODE));
+
+ I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG, 0);
+ I2C_WRITE(sc, I2C_CNFG_REG, I2C_CNFG_NEW_MASTER_FSM);
+ I2C_SET_CLEAR(sc, I2C_SL_CNFG_REG, I2C_SL_CNFG_NEWSL, 0);
+}
+
+static int
+tegra_i2c_intr(void *priv)
+{
+ struct tegra_i2c_softc * const sc = priv;
+
+ const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
+ if (istatus == 0)
+ return 0;
+ I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
+
+ mutex_enter(&sc->sc_lock);
+ cv_broadcast(&sc->sc_cv);
+ mutex_exit(&sc->sc_lock);
+
+ return 1;
+}
+
+static int
+tegra_i2c_acquire_bus(void *priv, int flags)
+{
+ struct tegra_i2c_softc * const sc = priv;
+
+ mutex_enter(&sc->sc_lock);
+
+ return 0;
+}
+
+static void
+tegra_i2c_release_bus(void *priv, int flags)
+{
+ struct tegra_i2c_softc * const sc = priv;
+
+ mutex_exit(&sc->sc_lock);
+}
+
+static int
+tegra_i2c_exec(void *priv, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
+ size_t cmdlen, void *buf, size_t buflen, int flags)
+{
+ struct tegra_i2c_softc * const sc = priv;
+ int retry, error;
+
+#if notyet
+ if (cold)
+#endif
+ flags |= I2C_F_POLL;
+
+ KASSERT(mutex_owned(&sc->sc_lock));
+
+ if ((flags & I2C_F_POLL) == 0) {
+ I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG,
+ I2C_INTERRUPT_MASK_NOACK | I2C_INTERRUPT_MASK_ARB_LOST |
+ I2C_INTERRUPT_MASK_TIMEOUT |
+ I2C_INTERRUPT_MASK_ALL_PACKETS_XFER_COMPLETE);
+ }
+
+ const uint32_t flush_mask =
+ I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
+
+ I2C_SET_CLEAR(sc, I2C_FIFO_CONTROL_REG, flush_mask, 0);
+ for (retry = 10000; retry > 0; retry--) {
+ const uint32_t v = I2C_READ(sc, I2C_FIFO_CONTROL_REG);
+ if ((v & flush_mask) == 0)
+ break;
+ delay(1);
+ }
+ if (retry == 0) {
+ device_printf(sc->sc_dev, "timeout flushing FIFO\n");
+ return EIO;
+ }
+
+ if (cmdlen > 0) {
+ error = tegra_i2c_write(sc, addr, cmdbuf, cmdlen, flags);
+ if (error) {
+ goto done;
+ }
+ }
+
+ if (I2C_OP_READ_P(op)) {
+ error = tegra_i2c_read(sc, addr, buf, buflen, flags);
+ } else {
+ error = tegra_i2c_write(sc, addr, buf, buflen, flags);
+ }
+
+done:
+ if ((flags & I2C_F_POLL) == 0) {
+ I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG, 0);
+ }
+ return error;
+}
+
+static int
+tegra_i2c_wait(struct tegra_i2c_softc *sc, int flags)
+{
+ const struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 };
+ struct timeval tnow, tend;
+ uint32_t stat;
+ int error;
+
+ getmicrotime(&tnow);
+ timeradd(&tnow, &timeout, &tend);
+
+ for (;;) {
+ getmicrotime(&tnow);
+ if (timercmp(&tnow, &tend, >=)) {
+ return ETIMEDOUT;
+ }
+ if ((flags & I2C_F_POLL) == 0) {
+ struct timeval trem;
+ timersub(&tend, &tnow, &trem);
+ const u_int ms = (trem.tv_sec * 1000) +
+ (trem.tv_usec / 1000);
+ KASSERT(ms > 0);
+ error = cv_timedwait_sig(&sc->sc_cv, &sc->sc_lock,
+ max(mstohz(ms), 1));
+ if (error) {
+ return error;
+ }
+ }
+ stat = I2C_READ(sc, I2C_STATUS_REG);
+ if ((stat & I2C_STATUS_BUSY) == 0) {
+ break;
+ }
+ if (flags & I2C_F_POLL) {
+ delay(1);
+ }
+ }
+
+
+ if (__SHIFTOUT(stat, I2C_STATUS_CMD1_STAT) != 0)
+ return EIO;
+
+ return 0;
+}
+
+static int
+tegra_i2c_write(struct tegra_i2c_softc *sc, i2c_addr_t addr, const uint8_t *buf,
+ size_t buflen, int flags)
+{
+ uint32_t data, cnfg;
+ size_t n;
+
+ if (buflen > 4)
+ return EINVAL;
+
+ I2C_WRITE(sc, I2C_CMD_ADDR0_REG, addr << 1);
+ for (n = 0, data = 0; n < buflen; n++) {
+ data |= (uint32_t)buf[n] << (n * 8);
+ }
+ I2C_WRITE(sc, I2C_CMD_DATA1_REG, data);
+
+ cnfg = I2C_READ(sc, I2C_CNFG_REG);
+ cnfg &= ~I2C_CNFG_DEBOUNCE_CNT;
+ cnfg |= __SHIFTIN(2, I2C_CNFG_DEBOUNCE_CNT);
+ cnfg &= ~I2C_CNFG_LENGTH;
+ cnfg |= __SHIFTIN(buflen - 1, I2C_CNFG_LENGTH);
+ cnfg &= ~I2C_CNFG_SLV2;
+ cnfg &= ~I2C_CNFG_CMD1;
+ cnfg &= ~I2C_CNFG_NOACK;
+ cnfg &= ~I2C_CNFG_A_MOD;
+ I2C_WRITE(sc, I2C_CNFG_REG, cnfg);
+
+ I2C_SET_CLEAR(sc, I2C_BUS_CONFIG_LOAD_REG,
+ I2C_BUS_CONFIG_LOAD_MSTR_CONFIG_LOAD, 0);
+
+ I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0);
+
+ return tegra_i2c_wait(sc, flags);
+}
+
+static int
+tegra_i2c_read(struct tegra_i2c_softc *sc, i2c_addr_t addr, uint8_t *buf,
+ size_t buflen, int flags)
+{
+ uint32_t data, cnfg;
+ int error;
+ size_t n;
+
+ if (buflen > 4)
+ return EINVAL;
+
+ I2C_WRITE(sc, I2C_CMD_ADDR0_REG, (addr << 1) | 1);
+ cnfg = I2C_READ(sc, I2C_CNFG_REG);
+ cnfg &= ~I2C_CNFG_SLV2;
+ cnfg |= I2C_CNFG_CMD1;
+ cnfg &= ~I2C_CNFG_LENGTH;
+ cnfg |= __SHIFTIN(buflen - 1, I2C_CNFG_LENGTH);
+ I2C_WRITE(sc, I2C_CNFG_REG, cnfg);
+
+ I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0);
+
+ error = tegra_i2c_wait(sc, flags);
+ if (error)
+ return error;
+
+ data = I2C_READ(sc, I2C_CMD_DATA1_REG);
+ for (n = 0; n < buflen; n++) {
+ buf[n] = (data >> (n * 8)) & 0xff;
+ }
+
+ return 0;
+}
Index: src/sys/arch/arm/nvidia/tegra_i2creg.h
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_i2creg.h:1.1
--- /dev/null Sun May 10 23:50:21 2015
+++ src/sys/arch/arm/nvidia/tegra_i2creg.h Sun May 10 23:50:21 2015
@@ -0,0 +1,134 @@
+/* $NetBSD: tegra_i2creg.h,v 1.1 2015/05/10 23:50:21 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <[email protected]>
+ * 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 ``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 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.
+ */
+
+#ifndef _ARM_TEGRA_I2CREG_H
+#define _ARM_TEGRA_I2CREG_H
+
+#define I2C_CNFG_REG 0x00
+#define I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT __BIT(15)
+#define I2C_CNFG_DEBOUNCE_CNT __BITS(14,12)
+#define I2C_CNFG_NEW_MASTER_FSM __BIT(11)
+#define I2C_CNFG_PACKET_MODE_EN __BIT(10)
+#define I2C_CNFG_SEND __BIT(9)
+#define I2C_CNFG_NOACK __BIT(8)
+#define I2C_CNFG_CMD2 __BIT(7)
+#define I2C_CNFG_CMD1 __BIT(6)
+#define I2C_CNFG_START __BIT(5)
+#define I2C_CNFG_SLV2 __BIT(4)
+#define I2C_CNFG_LENGTH __BITS(3,1)
+#define I2C_CNFG_A_MOD __BIT(0)
+
+#define I2C_CMD_ADDR0_REG 0x04
+#define I2C_CMD_ADDR1_REG 0x08
+#define I2C_CMD_DATA1_REG 0x0c
+#define I2C_CMD_DATA2_REG 0x10
+
+#define I2C_STATUS_REG 0x1c
+#define I2C_STATUS_BUSY __BIT(8)
+#define I2C_STATUS_CMD2_STAT __BITS(7,4)
+#define I2C_STATUS_CMD1_STAT __BITS(3,0)
+
+#define I2C_SL_CNFG_REG 0x20
+#define I2C_SL_CNFG_FIFO_XFER_EN __BIT(20)
+#define I2C_SL_CNFG_BUFFER_SIZE __BITS(19,8)
+#define I2C_SL_CNFG_ACK_LAST_BYTE_VALID __BIT(7)
+#define I2C_SL_CNFG_ACK_LAST_BYTE __BIT(6)
+#define I2C_SL_CNFG_ACK_WITHHOLD_EN __BIT(5)
+#define I2C_SL_CNFG_PKT_MODE_EN __BIT(4)
+#define I2C_SL_CNFG_ENABLE_SL __BIT(3)
+#define I2C_SL_CNFG_NEWSL __BIT(2)
+#define I2C_SL_CNFG_NACK __BIT(1)
+#define I2C_SL_CNFG_RESP __BIT(0)
+
+#define I2C_SL_RCVD_REG 0x24
+#define I2C_SL_STATUS_REG 0x28
+#define I2C_SL_ADDR1_REG 0x2c
+#define I2C_SL_ADDR2_REG 0x30
+#define I2C_TLOW_SEXT_REG 0x34
+#define I2C_SL_DELAY_COUNT_REG 0x3c
+#define I2C_SL_INT_MASK_REG 0x40
+#define I2C_SL_INT_SOURCE_REG 0x44
+#define I2C_SL_INT_SET_REG 0x48
+#define I2C_TX_PACKET_FIFO_REG 0x50
+#define I2C_RX_FIFO_REG 0x54
+#define I2C_PACKET_TRANSFER_STATUS_REG 0x58
+
+#define I2C_FIFO_CONTROL_REG 0x5c
+#define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG __BITS(15,13)
+#define I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG __BITS(12,10)
+#define I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH __BIT(9)
+#define I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH __BIT(8)
+#define I2C_FIFO_CONTROL_TX_FIFO_TRIG __BITS(7,5)
+#define I2C_FIFO_CONTROL_RX_FIFO_TRIG __BITS(4,2)
+#define I2C_FIFO_CONTROL_TX_FIFO_FLUSH __BIT(1)
+#define I2C_FIFO_CONTROL_RX_FIFO_FLUSH __BIT(0)
+
+#define I2C_FIFO_STATUS_REG 0x60
+#define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON __BIT(25)
+#define I2C_FIFO_STATUS_SLV_TX_FIFO_EMPTY_CNT __BITS(23,20)
+#define I2C_FIFO_STATUS_SLV_RX_FIFO_EMPTY_CNT __BITS(19,16)
+#define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT __BITS(7,4)
+#define I2C_FIFO_STATUS_RX_FIFO_EMPTY_CNT __BITS(3,0)
+
+#define I2C_INTERRUPT_MASK_REG 0x64
+#define I2C_INTERRUPT_MASK_TIMEOUT __BIT(8)
+#define I2C_INTERRUPT_MASK_PACKET_XFER_COMPLETE __BIT(7)
+#define I2C_INTERRUPT_MASK_ALL_PACKETS_XFER_COMPLETE __BIT(6)
+#define I2C_INTERRUPT_MASK_NOACK __BIT(3)
+#define I2C_INTERRUPT_MASK_ARB_LOST __BIT(2)
+#define I2C_INTERRUPT_MASK_TFIFO_DATA_REQ __BIT(1)
+#define I2C_INTERRUPT_MASK_RFIFO_DATA_REQ __BIT(0)
+
+#define I2C_INTERRUPT_STATUS_REG 0x68
+
+#define I2C_CLK_DIVISOR_REG 0x6c
+#define I2C_CLK_DIVISOR_STD_FAST_MODE __BITS(31,16)
+#define I2C_CLK_DIVISOR_HSMODE __BITS(15,0)
+
+#define I2C_INTERRUPT_SOURCE_REG 0x70
+#define I2C_INTERRUPT_SET_REG 0x74
+#define I2C_SLV_TX_PACKET_FIFO_REG 0x78
+#define I2C_SLV_RX_FIFO_REG 0x7c
+#define I2C_SLV_PACKET_STATUS_REG 0x80
+#define I2C_BUS_CLEAR_CONFIG_REG 0x84
+#define I2C_BUS_CLEAR_STATUS_REG 0x88
+
+#define I2C_BUS_CONFIG_LOAD_REG 0x8c
+#define I2C_BUS_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD __BIT(2)
+#define I2C_BUS_CONFIG_LOAD_SLV_CONFIG_LOAD __BIT(1)
+#define I2C_BUS_CONFIG_LOAD_MSTR_CONFIG_LOAD __BIT(0)
+
+#define I2C_INTERFACE_TIMING0_REG 0x94
+#define I2C_INTERFACE_TIMING0_THIGH __BITS(13,8)
+#define I2C_INTERFACE_TIMING0_TLOW __BITS(5,0)
+
+#define I2C_INTERFACE_TIMING1_REG 0x98
+#define I2C_HS_INTERFACE_TIMING0_REG 0x9c
+#define I2C_HS_INTERFACE_TIMING1_REG 0xa0
+
+#endif /* _ARM_TEGRA_I2CREG_H */