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 */