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