xref: /netbsd-src/sys/arch/arm/nvidia/tegra_i2c.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: tegra_i2c.c,v 1.26 2021/01/27 03:10:19 thorpej Exp $ */
2520aad95Sjmcneill 
3520aad95Sjmcneill /*-
4520aad95Sjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5520aad95Sjmcneill  * All rights reserved.
6520aad95Sjmcneill  *
7520aad95Sjmcneill  * Redistribution and use in source and binary forms, with or without
8520aad95Sjmcneill  * modification, are permitted provided that the following conditions
9520aad95Sjmcneill  * are met:
10520aad95Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11520aad95Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12520aad95Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13520aad95Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14520aad95Sjmcneill  *    documentation and/or other materials provided with the distribution.
15520aad95Sjmcneill  *
16520aad95Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17520aad95Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18520aad95Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19520aad95Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20520aad95Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21520aad95Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22520aad95Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23520aad95Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24520aad95Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25520aad95Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26520aad95Sjmcneill  * SUCH DAMAGE.
27520aad95Sjmcneill  */
28520aad95Sjmcneill 
29520aad95Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: tegra_i2c.c,v 1.26 2021/01/27 03:10:19 thorpej Exp $");
31520aad95Sjmcneill 
32520aad95Sjmcneill #include <sys/param.h>
33520aad95Sjmcneill #include <sys/bus.h>
34520aad95Sjmcneill #include <sys/device.h>
35520aad95Sjmcneill #include <sys/intr.h>
36520aad95Sjmcneill #include <sys/systm.h>
37520aad95Sjmcneill #include <sys/kernel.h>
38520aad95Sjmcneill 
39520aad95Sjmcneill #include <dev/i2c/i2cvar.h>
40520aad95Sjmcneill 
41520aad95Sjmcneill #include <arm/nvidia/tegra_reg.h>
42520aad95Sjmcneill #include <arm/nvidia/tegra_i2creg.h>
43520aad95Sjmcneill #include <arm/nvidia/tegra_var.h>
44520aad95Sjmcneill 
45d59db8d0Sjmcneill #include <dev/fdt/fdtvar.h>
46d59db8d0Sjmcneill 
47520aad95Sjmcneill static int	tegra_i2c_match(device_t, cfdata_t, void *);
48520aad95Sjmcneill static void	tegra_i2c_attach(device_t, device_t, void *);
49520aad95Sjmcneill 
50520aad95Sjmcneill struct tegra_i2c_softc {
51520aad95Sjmcneill 	device_t		sc_dev;
52520aad95Sjmcneill 	bus_space_tag_t		sc_bst;
53520aad95Sjmcneill 	bus_space_handle_t	sc_bsh;
54520aad95Sjmcneill 	void *			sc_ih;
5593e0bfebSjmcneill 	struct clk *		sc_clk;
5693e0bfebSjmcneill 	struct fdtbus_reset *	sc_rst;
5793e0bfebSjmcneill 	u_int			sc_cid;
58520aad95Sjmcneill 
59520aad95Sjmcneill 	struct i2c_controller	sc_ic;
607e5beceeSthorpej 	kmutex_t		sc_intr_lock;
617e5beceeSthorpej 	kcondvar_t		sc_intr_wait;
62520aad95Sjmcneill };
63520aad95Sjmcneill 
64520aad95Sjmcneill static void	tegra_i2c_init(struct tegra_i2c_softc *);
65520aad95Sjmcneill static int	tegra_i2c_intr(void *);
66520aad95Sjmcneill 
67520aad95Sjmcneill static int	tegra_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
68520aad95Sjmcneill 			       size_t, void *, size_t, int);
69520aad95Sjmcneill 
70520aad95Sjmcneill static int	tegra_i2c_wait(struct tegra_i2c_softc *, int);
71520aad95Sjmcneill static int	tegra_i2c_write(struct tegra_i2c_softc *, i2c_addr_t,
72c2d1bfbdSjmcneill 				const uint8_t *, size_t, int, bool);
73520aad95Sjmcneill static int	tegra_i2c_read(struct tegra_i2c_softc *, i2c_addr_t, uint8_t *,
74520aad95Sjmcneill 			       size_t, int);
75520aad95Sjmcneill 
76520aad95Sjmcneill CFATTACH_DECL_NEW(tegra_i2c, sizeof(struct tegra_i2c_softc),
77520aad95Sjmcneill 	tegra_i2c_match, tegra_i2c_attach, NULL, NULL);
78520aad95Sjmcneill 
79520aad95Sjmcneill #define I2C_WRITE(sc, reg, val) \
80520aad95Sjmcneill     bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
81520aad95Sjmcneill #define I2C_READ(sc, reg) \
82520aad95Sjmcneill     bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
83520aad95Sjmcneill #define I2C_SET_CLEAR(sc, reg, setval, clrval) \
84520aad95Sjmcneill     tegra_reg_set_clear((sc)->sc_bst, (sc)->sc_bsh, (reg), (setval), (clrval))
85520aad95Sjmcneill 
86*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
87*6e54367aSthorpej 	{ .compat = "nvidia,tegra210-i2c" },
88*6e54367aSthorpej 	{ .compat = "nvidia,tegra124-i2c" },
89*6e54367aSthorpej 	{ .compat = "nvidia,tegra114-i2c" },
90*6e54367aSthorpej 	DEVICE_COMPAT_EOL
91*6e54367aSthorpej };
92*6e54367aSthorpej 
93520aad95Sjmcneill static int
tegra_i2c_match(device_t parent,cfdata_t cf,void * aux)94520aad95Sjmcneill tegra_i2c_match(device_t parent, cfdata_t cf, void *aux)
95520aad95Sjmcneill {
96d59db8d0Sjmcneill 	struct fdt_attach_args * const faa = aux;
97520aad95Sjmcneill 
98*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
99520aad95Sjmcneill }
100520aad95Sjmcneill 
101520aad95Sjmcneill static void
tegra_i2c_attach(device_t parent,device_t self,void * aux)102520aad95Sjmcneill tegra_i2c_attach(device_t parent, device_t self, void *aux)
103520aad95Sjmcneill {
104520aad95Sjmcneill 	struct tegra_i2c_softc * const sc = device_private(self);
105d59db8d0Sjmcneill 	struct fdt_attach_args * const faa = aux;
1064e8cdc22Sjmcneill 	const int phandle = faa->faa_phandle;
107d59db8d0Sjmcneill 	char intrstr[128];
108d59db8d0Sjmcneill 	bus_addr_t addr;
109d59db8d0Sjmcneill 	bus_size_t size;
1104e8cdc22Sjmcneill 	int error;
111d59db8d0Sjmcneill 
1124e8cdc22Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
113d59db8d0Sjmcneill 		aprint_error(": couldn't get registers\n");
114d59db8d0Sjmcneill 		return;
115d59db8d0Sjmcneill 	}
11693e0bfebSjmcneill 	sc->sc_clk = fdtbus_clock_get(phandle, "div-clk");
11793e0bfebSjmcneill 	if (sc->sc_clk == NULL) {
11893e0bfebSjmcneill 		aprint_error(": couldn't get clock div-clk\n");
11993e0bfebSjmcneill 		return;
12093e0bfebSjmcneill 	}
12193e0bfebSjmcneill 	sc->sc_rst = fdtbus_reset_get(phandle, "i2c");
12293e0bfebSjmcneill 	if (sc->sc_rst == NULL) {
12393e0bfebSjmcneill 		aprint_error(": couldn't get reset i2c\n");
12493e0bfebSjmcneill 		return;
12593e0bfebSjmcneill 	}
126520aad95Sjmcneill 
127520aad95Sjmcneill 	sc->sc_dev = self;
128d59db8d0Sjmcneill 	sc->sc_bst = faa->faa_bst;
12993e0bfebSjmcneill 	sc->sc_cid = device_unit(self);
130d59db8d0Sjmcneill 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
131d59db8d0Sjmcneill 	if (error) {
132c8849c78Sskrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d",
133c8849c78Sskrll 		    addr, error);
134d59db8d0Sjmcneill 		return;
135d59db8d0Sjmcneill 	}
1367e5beceeSthorpej 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
1377e5beceeSthorpej 	cv_init(&sc->sc_intr_wait, device_xname(self));
138520aad95Sjmcneill 
139520aad95Sjmcneill 	aprint_naive("\n");
14093e0bfebSjmcneill 	aprint_normal(": I2C\n");
141520aad95Sjmcneill 
1424e8cdc22Sjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
143d59db8d0Sjmcneill 		aprint_error_dev(self, "failed to decode interrupt\n");
144520aad95Sjmcneill 		return;
145520aad95Sjmcneill 	}
146d59db8d0Sjmcneill 
1473f513eddSjmcneill 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
1483f513eddSjmcneill 	    FDT_INTR_MPSAFE, tegra_i2c_intr, sc, device_xname(self));
149d59db8d0Sjmcneill 	if (sc->sc_ih == NULL) {
150d59db8d0Sjmcneill 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
151d59db8d0Sjmcneill 		    intrstr);
152d59db8d0Sjmcneill 		return;
153d59db8d0Sjmcneill 	}
154d59db8d0Sjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
155520aad95Sjmcneill 
156d01d2885Sjmcneill 	/*
157d01d2885Sjmcneill 	 * Recommended setting for standard mode is to use an I2C source div
158d01d2885Sjmcneill 	 * of 20 (Tegra K1 Technical Reference Manual, Table 137)
159d01d2885Sjmcneill 	 */
16093e0bfebSjmcneill 	fdtbus_reset_assert(sc->sc_rst);
16193e0bfebSjmcneill 	error = clk_set_rate(sc->sc_clk, 20400000);
16293e0bfebSjmcneill 	if (error) {
16393e0bfebSjmcneill 		aprint_error_dev(self, "couldn't set frequency: %d\n", error);
16493e0bfebSjmcneill 		return;
16593e0bfebSjmcneill 	}
16693e0bfebSjmcneill 	error = clk_enable(sc->sc_clk);
16793e0bfebSjmcneill 	if (error) {
16893e0bfebSjmcneill 		aprint_error_dev(self, "couldn't enable clock: %d\n", error);
16993e0bfebSjmcneill 		return;
17093e0bfebSjmcneill 	}
17193e0bfebSjmcneill 	fdtbus_reset_deassert(sc->sc_rst);
172520aad95Sjmcneill 
1737e5beceeSthorpej 	mutex_enter(&sc->sc_intr_lock);
174520aad95Sjmcneill 	tegra_i2c_init(sc);
1757e5beceeSthorpej 	mutex_exit(&sc->sc_intr_lock);
176520aad95Sjmcneill 
1777e5beceeSthorpej 	iic_tag_init(&sc->sc_ic);
178520aad95Sjmcneill 	sc->sc_ic.ic_cookie = sc;
179520aad95Sjmcneill 	sc->sc_ic.ic_exec = tegra_i2c_exec;
180520aad95Sjmcneill 
18121b71bc0Sthorpej 	fdtbus_register_i2c_controller(&sc->sc_ic, phandle);
182d59db8d0Sjmcneill 
1837d854132Sjmcneill 	fdtbus_attach_i2cbus(self, phandle, &sc->sc_ic, iicbus_print);
184520aad95Sjmcneill }
185520aad95Sjmcneill 
186520aad95Sjmcneill static void
tegra_i2c_init(struct tegra_i2c_softc * sc)187520aad95Sjmcneill tegra_i2c_init(struct tegra_i2c_softc *sc)
188520aad95Sjmcneill {
1890c927cbdSjmcneill 	int retry = 10000;
1900c927cbdSjmcneill 
191520aad95Sjmcneill 	I2C_WRITE(sc, I2C_CLK_DIVISOR_REG,
192520aad95Sjmcneill 	    __SHIFTIN(0x19, I2C_CLK_DIVISOR_STD_FAST_MODE) |
193520aad95Sjmcneill 	    __SHIFTIN(0x1, I2C_CLK_DIVISOR_HSMODE));
194520aad95Sjmcneill 
195520aad95Sjmcneill 	I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG, 0);
196df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_CNFG_REG,
197df78cf4aSjmcneill 	    I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN);
198520aad95Sjmcneill 	I2C_SET_CLEAR(sc, I2C_SL_CNFG_REG, I2C_SL_CNFG_NEWSL, 0);
1990c927cbdSjmcneill 	I2C_WRITE(sc, I2C_FIFO_CONTROL_REG,
2000c927cbdSjmcneill 	    __SHIFTIN(7, I2C_FIFO_CONTROL_TX_FIFO_TRIG) |
2010c927cbdSjmcneill 	    __SHIFTIN(0, I2C_FIFO_CONTROL_RX_FIFO_TRIG));
2020c927cbdSjmcneill 
203d4beed67Sjmcneill 	I2C_WRITE(sc, I2C_BUS_CONFIG_LOAD_REG,
204d4beed67Sjmcneill 	    I2C_BUS_CONFIG_LOAD_MSTR_CONFIG_LOAD);
2050c927cbdSjmcneill 	while (--retry > 0) {
2060c927cbdSjmcneill 		if (I2C_READ(sc, I2C_BUS_CONFIG_LOAD_REG) == 0)
2070c927cbdSjmcneill 			break;
2080c927cbdSjmcneill 		delay(10);
2090c927cbdSjmcneill 	}
2100c927cbdSjmcneill 	if (retry == 0) {
2110c927cbdSjmcneill 		device_printf(sc->sc_dev, "config load timeout\n");
2120c927cbdSjmcneill 	}
213520aad95Sjmcneill }
214520aad95Sjmcneill 
215520aad95Sjmcneill static int
tegra_i2c_intr(void * priv)216520aad95Sjmcneill tegra_i2c_intr(void *priv)
217520aad95Sjmcneill {
218520aad95Sjmcneill 	struct tegra_i2c_softc * const sc = priv;
219520aad95Sjmcneill 
220520aad95Sjmcneill 	const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
221520aad95Sjmcneill 	if (istatus == 0)
222520aad95Sjmcneill 		return 0;
223520aad95Sjmcneill 	I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
224520aad95Sjmcneill 
2257e5beceeSthorpej 	mutex_enter(&sc->sc_intr_lock);
2267e5beceeSthorpej 	cv_broadcast(&sc->sc_intr_wait);
2277e5beceeSthorpej 	mutex_exit(&sc->sc_intr_lock);
228520aad95Sjmcneill 
229520aad95Sjmcneill 	return 1;
230520aad95Sjmcneill }
231520aad95Sjmcneill 
232520aad95Sjmcneill 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)233520aad95Sjmcneill tegra_i2c_exec(void *priv, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
234520aad95Sjmcneill     size_t cmdlen, void *buf, size_t buflen, int flags)
235520aad95Sjmcneill {
236520aad95Sjmcneill 	struct tegra_i2c_softc * const sc = priv;
237520aad95Sjmcneill 	int retry, error;
238520aad95Sjmcneill 
2397e5beceeSthorpej 	/*
2407e5beceeSthorpej 	 * XXXJRT This is probably no longer necessary?  Before these
2417e5beceeSthorpej 	 * changes, the bus lock was also used for the interrupt handler,
2427e5beceeSthorpej 	 * and there would be a deadlock when the interrupt handler tried to
2437e5beceeSthorpej 	 * acquire it again.  The bus lock is now owned by the mid-layer and
2447e5beceeSthorpej 	 * we have our own interrupt lock.
2457e5beceeSthorpej 	 */
246520aad95Sjmcneill 	flags |= I2C_F_POLL;
247520aad95Sjmcneill 
248fe65f2feSjmcneill 	if (buflen == 0 && cmdlen == 0)
249fe65f2feSjmcneill 		return EINVAL;
250fe65f2feSjmcneill 
2517e5beceeSthorpej 	mutex_enter(&sc->sc_intr_lock);
2527e5beceeSthorpej 
253520aad95Sjmcneill 	if ((flags & I2C_F_POLL) == 0) {
254520aad95Sjmcneill 		I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG,
255520aad95Sjmcneill 		    I2C_INTERRUPT_MASK_NOACK | I2C_INTERRUPT_MASK_ARB_LOST |
256520aad95Sjmcneill 		    I2C_INTERRUPT_MASK_TIMEOUT |
257520aad95Sjmcneill 		    I2C_INTERRUPT_MASK_ALL_PACKETS_XFER_COMPLETE);
258520aad95Sjmcneill 	}
259520aad95Sjmcneill 
260520aad95Sjmcneill 	const uint32_t flush_mask =
261520aad95Sjmcneill 	    I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
262520aad95Sjmcneill 
263520aad95Sjmcneill 	I2C_SET_CLEAR(sc, I2C_FIFO_CONTROL_REG, flush_mask, 0);
264520aad95Sjmcneill 	for (retry = 10000; retry > 0; retry--) {
265520aad95Sjmcneill 		const uint32_t v = I2C_READ(sc, I2C_FIFO_CONTROL_REG);
266520aad95Sjmcneill 		if ((v & flush_mask) == 0)
267520aad95Sjmcneill 			break;
268520aad95Sjmcneill 		delay(1);
269520aad95Sjmcneill 	}
270520aad95Sjmcneill 	if (retry == 0) {
2717e5beceeSthorpej 		mutex_exit(&sc->sc_intr_lock);
272520aad95Sjmcneill 		device_printf(sc->sc_dev, "timeout flushing FIFO\n");
273520aad95Sjmcneill 		return EIO;
274520aad95Sjmcneill 	}
275520aad95Sjmcneill 
276520aad95Sjmcneill 	if (cmdlen > 0) {
277b63e88b1Sjmcneill 		error = tegra_i2c_write(sc, addr, cmdbuf, cmdlen, flags,
278ca349034Sjakllsch 		    buflen > 0 ? true : false);
279520aad95Sjmcneill 		if (error) {
280520aad95Sjmcneill 			goto done;
281520aad95Sjmcneill 		}
282520aad95Sjmcneill 	}
283520aad95Sjmcneill 
284fe65f2feSjmcneill 	if (buflen > 0) {
285520aad95Sjmcneill 		if (I2C_OP_READ_P(op)) {
286520aad95Sjmcneill 			error = tegra_i2c_read(sc, addr, buf, buflen, flags);
287520aad95Sjmcneill 		} else {
288c2d1bfbdSjmcneill 			error = tegra_i2c_write(sc, addr, buf, buflen, flags, false);
289520aad95Sjmcneill 		}
290fe65f2feSjmcneill 	}
291520aad95Sjmcneill 
292520aad95Sjmcneill done:
293520aad95Sjmcneill 	if ((flags & I2C_F_POLL) == 0) {
294520aad95Sjmcneill 		I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG, 0);
295520aad95Sjmcneill 	}
296d4beed67Sjmcneill 
297d4beed67Sjmcneill 	if (error) {
298d4beed67Sjmcneill 		tegra_i2c_init(sc);
299d4beed67Sjmcneill 	}
300d4beed67Sjmcneill 
3017e5beceeSthorpej 	mutex_exit(&sc->sc_intr_lock);
3027e5beceeSthorpej 
303520aad95Sjmcneill 	return error;
304520aad95Sjmcneill }
305520aad95Sjmcneill 
306520aad95Sjmcneill static int
tegra_i2c_wait(struct tegra_i2c_softc * sc,int flags)307520aad95Sjmcneill tegra_i2c_wait(struct tegra_i2c_softc *sc, int flags)
308520aad95Sjmcneill {
309df78cf4aSjmcneill 	int error, retry;
310df78cf4aSjmcneill 	uint32_t stat = 0;
311520aad95Sjmcneill 
312df78cf4aSjmcneill 	retry = (flags & I2C_F_POLL) ? 100000 : 100;
313520aad95Sjmcneill 
314df78cf4aSjmcneill 	while (--retry > 0) {
315520aad95Sjmcneill 		if ((flags & I2C_F_POLL) == 0) {
3167e5beceeSthorpej 			error = cv_timedwait_sig(&sc->sc_intr_wait,
3177e5beceeSthorpej 						 &sc->sc_intr_lock,
318d1579b2dSriastradh 						 uimax(mstohz(10), 1));
319520aad95Sjmcneill 			if (error) {
320520aad95Sjmcneill 				return error;
321520aad95Sjmcneill 			}
322520aad95Sjmcneill 		}
323df78cf4aSjmcneill 		stat = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
324df78cf4aSjmcneill 		if (stat & I2C_INTERRUPT_STATUS_PACKET_XFER_COMPLETE) {
325520aad95Sjmcneill 			break;
326520aad95Sjmcneill 		}
327520aad95Sjmcneill 		if (flags & I2C_F_POLL) {
328df78cf4aSjmcneill 			delay(10);
329520aad95Sjmcneill 		}
330520aad95Sjmcneill 	}
331df78cf4aSjmcneill 	if (retry == 0) {
332fe65f2feSjmcneill #ifdef TEGRA_I2C_DEBUG
333df78cf4aSjmcneill 		device_printf(sc->sc_dev, "timed out, status = %#x\n", stat);
334fe65f2feSjmcneill #endif
335df78cf4aSjmcneill 		return ETIMEDOUT;
336df78cf4aSjmcneill 	}
337520aad95Sjmcneill 
338df78cf4aSjmcneill 	const uint32_t err_mask =
339df78cf4aSjmcneill 	    I2C_INTERRUPT_STATUS_NOACK |
340df78cf4aSjmcneill 	    I2C_INTERRUPT_STATUS_ARB_LOST |
341df78cf4aSjmcneill 	    I2C_INTERRUPT_MASK_TIMEOUT;
342520aad95Sjmcneill 
343df78cf4aSjmcneill 	if (stat & err_mask) {
344df78cf4aSjmcneill 		device_printf(sc->sc_dev, "error, status = %#x\n", stat);
345520aad95Sjmcneill 		return EIO;
346df78cf4aSjmcneill 	}
347520aad95Sjmcneill 
348520aad95Sjmcneill 	return 0;
349520aad95Sjmcneill }
350520aad95Sjmcneill 
351520aad95Sjmcneill static int
tegra_i2c_write(struct tegra_i2c_softc * sc,i2c_addr_t addr,const uint8_t * buf,size_t buflen,int flags,bool repeat_start)352520aad95Sjmcneill tegra_i2c_write(struct tegra_i2c_softc *sc, i2c_addr_t addr, const uint8_t *buf,
353c2d1bfbdSjmcneill     size_t buflen, int flags, bool repeat_start)
354520aad95Sjmcneill {
355df78cf4aSjmcneill 	const uint8_t *p = buf;
356df78cf4aSjmcneill 	size_t n, resid = buflen;
357df78cf4aSjmcneill 	uint32_t data;
358df78cf4aSjmcneill 	int retry;
359520aad95Sjmcneill 
360df78cf4aSjmcneill 	const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
361df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
362520aad95Sjmcneill 
363df78cf4aSjmcneill 	/* Generic Header 0 */
364df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
365df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PROTHDRSZ_REQ,
366df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PROTHDRSZ) |
36793e0bfebSjmcneill 	    __SHIFTIN(sc->sc_cid, I2C_IOPACKET_WORD0_CONTROLLERID) |
368df78cf4aSjmcneill 	    __SHIFTIN(1, I2C_IOPACKET_WORD0_PKTID) |
369df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PROTOCOL_I2C,
370df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PROTOCOL) |
371df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PKTTYPE_REQ,
372df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PKTTYPE));
373df78cf4aSjmcneill 	/* Generic Header 1 */
374df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
375df78cf4aSjmcneill 	    __SHIFTIN(buflen - 1, I2C_IOPACKET_WORD1_PAYLOADSIZE));
376df78cf4aSjmcneill 	/* I2C Master Transmit Packet Header */
377df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
378df78cf4aSjmcneill 	    I2C_IOPACKET_XMITHDR_IE |
379c2d1bfbdSjmcneill 	    (repeat_start ? I2C_IOPACKET_XMITHDR_REPEAT_STARTSTOP : 0) |
380df78cf4aSjmcneill 	    __SHIFTIN((addr << 1), I2C_IOPACKET_XMITHDR_SLAVE_ADDR));
381df78cf4aSjmcneill 
382df78cf4aSjmcneill 	/* Transmit data */
383df78cf4aSjmcneill 	while (resid > 0) {
384df78cf4aSjmcneill 		retry = 10000;
385df78cf4aSjmcneill 		while (--retry > 0) {
386df78cf4aSjmcneill 			const uint32_t fs = I2C_READ(sc, I2C_FIFO_STATUS_REG);
387df78cf4aSjmcneill 			const u_int cnt =
388df78cf4aSjmcneill 			    __SHIFTOUT(fs, I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT);
389df78cf4aSjmcneill 			if (cnt > 0)
390df78cf4aSjmcneill 				break;
391df78cf4aSjmcneill 			delay(10);
392520aad95Sjmcneill 		}
393df78cf4aSjmcneill 		if (retry == 0) {
394df78cf4aSjmcneill 			device_printf(sc->sc_dev, "TX FIFO timeout\n");
395df78cf4aSjmcneill 			return ETIMEDOUT;
396df78cf4aSjmcneill 		}
397520aad95Sjmcneill 
398d1579b2dSriastradh 		for (n = 0, data = 0; n < uimin(resid, 4); n++) {
399df78cf4aSjmcneill 			data |= (uint32_t)p[n] << (n * 8);
400df78cf4aSjmcneill 		}
401df78cf4aSjmcneill 		I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, data);
402d1579b2dSriastradh 		p += uimin(resid, 4);
403d1579b2dSriastradh 		resid -= uimin(resid, 4);
404df78cf4aSjmcneill 	}
405520aad95Sjmcneill 
406520aad95Sjmcneill 	return tegra_i2c_wait(sc, flags);
407520aad95Sjmcneill }
408520aad95Sjmcneill 
409520aad95Sjmcneill static int
tegra_i2c_read(struct tegra_i2c_softc * sc,i2c_addr_t addr,uint8_t * buf,size_t buflen,int flags)410520aad95Sjmcneill tegra_i2c_read(struct tegra_i2c_softc *sc, i2c_addr_t addr, uint8_t *buf,
411520aad95Sjmcneill     size_t buflen, int flags)
412520aad95Sjmcneill {
413df78cf4aSjmcneill 	uint8_t *p = buf;
414df78cf4aSjmcneill 	size_t n, resid = buflen;
415df78cf4aSjmcneill 	uint32_t data;
416d4beed67Sjmcneill 	int retry;
417520aad95Sjmcneill 
418df78cf4aSjmcneill 	const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
419df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
420520aad95Sjmcneill 
421df78cf4aSjmcneill 	/* Generic Header 0 */
422df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
423df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PROTHDRSZ_REQ,
424df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PROTHDRSZ) |
42593e0bfebSjmcneill 	    __SHIFTIN(sc->sc_cid, I2C_IOPACKET_WORD0_CONTROLLERID) |
426df78cf4aSjmcneill 	    __SHIFTIN(1, I2C_IOPACKET_WORD0_PKTID) |
427df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PROTOCOL_I2C,
428df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PROTOCOL) |
429df78cf4aSjmcneill 	    __SHIFTIN(I2C_IOPACKET_WORD0_PKTTYPE_REQ,
430df78cf4aSjmcneill 		      I2C_IOPACKET_WORD0_PKTTYPE));
431df78cf4aSjmcneill 	/* Generic Header 1 */
432df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
433df78cf4aSjmcneill 	    __SHIFTIN(buflen - 1, I2C_IOPACKET_WORD1_PAYLOADSIZE));
434df78cf4aSjmcneill 	/* I2C Master Transmit Packet Header */
435df78cf4aSjmcneill 	I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG,
436df78cf4aSjmcneill 	    I2C_IOPACKET_XMITHDR_IE | I2C_IOPACKET_XMITHDR_READ |
437df78cf4aSjmcneill 	    __SHIFTIN((addr << 1) | 1, I2C_IOPACKET_XMITHDR_SLAVE_ADDR));
438520aad95Sjmcneill 
439df78cf4aSjmcneill 	while (resid > 0) {
440df78cf4aSjmcneill 		retry = 10000;
441df78cf4aSjmcneill 		while (--retry > 0) {
442df78cf4aSjmcneill 			const uint32_t fs = I2C_READ(sc, I2C_FIFO_STATUS_REG);
443df78cf4aSjmcneill 			const u_int cnt =
444df78cf4aSjmcneill 			    __SHIFTOUT(fs, I2C_FIFO_STATUS_RX_FIFO_FULL_CNT);
445df78cf4aSjmcneill 			if (cnt > 0)
446df78cf4aSjmcneill 				break;
447df78cf4aSjmcneill 			delay(10);
448df78cf4aSjmcneill 		}
449df78cf4aSjmcneill 		if (retry == 0) {
450df78cf4aSjmcneill 			device_printf(sc->sc_dev, "RX FIFO timeout\n");
451df78cf4aSjmcneill 			return ETIMEDOUT;
452df78cf4aSjmcneill 		}
453df78cf4aSjmcneill 
454df78cf4aSjmcneill 		data = I2C_READ(sc, I2C_RX_FIFO_REG);
455d1579b2dSriastradh 		for (n = 0; n < uimin(resid, 4); n++) {
456df78cf4aSjmcneill 			p[n] = (data >> (n * 8)) & 0xff;
457df78cf4aSjmcneill 		}
458d1579b2dSriastradh 		p += uimin(resid, 4);
459d1579b2dSriastradh 		resid -= uimin(resid, 4);
460520aad95Sjmcneill 	}
461520aad95Sjmcneill 
462d4beed67Sjmcneill 	return tegra_i2c_wait(sc, flags);
463520aad95Sjmcneill }
464