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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
@@ -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 <jmcne...@invisible.ca>
+ * 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 <jmcne...@invisible.ca>
+ * 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 */

Reply via email to