xref: /openbsd-src/sys/dev/ic/dwiic.c (revision 80bce6dbd02b9b5cd0d32e5e6259fecaeccab7fb)
1*80bce6dbSderaadt /* $OpenBSD: dwiic.c,v 1.21 2024/08/17 02:35:00 deraadt Exp $ */
2c6df0db7Sjcs /*
3c6df0db7Sjcs  * Synopsys DesignWare I2C controller
4c6df0db7Sjcs  *
5c6df0db7Sjcs  * Copyright (c) 2015-2017 joshua stein <jcs@openbsd.org>
6c6df0db7Sjcs  *
7c6df0db7Sjcs  * Permission to use, copy, modify, and/or distribute this software for any
8c6df0db7Sjcs  * purpose with or without fee is hereby granted, provided that the above
9c6df0db7Sjcs  * copyright notice and this permission notice appear in all copies.
10c6df0db7Sjcs  *
11c6df0db7Sjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12c6df0db7Sjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13c6df0db7Sjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14c6df0db7Sjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15c6df0db7Sjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16c6df0db7Sjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17c6df0db7Sjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18c6df0db7Sjcs  */
19c6df0db7Sjcs 
20c6df0db7Sjcs #include <sys/param.h>
21c6df0db7Sjcs #include <sys/systm.h>
22c6df0db7Sjcs #include <sys/kernel.h>
23c6df0db7Sjcs 
24c6df0db7Sjcs #include <dev/i2c/i2cvar.h>
25c6df0db7Sjcs 
26c6df0db7Sjcs #include <dev/ic/dwiicvar.h>
27c6df0db7Sjcs 
28c6df0db7Sjcs struct cfdriver dwiic_cd = {
29c6df0db7Sjcs 	NULL, "dwiic", DV_DULL
30c6df0db7Sjcs };
31c6df0db7Sjcs 
32c6df0db7Sjcs int
33c6df0db7Sjcs dwiic_activate(struct device *self, int act)
34c6df0db7Sjcs {
35c6df0db7Sjcs 	struct dwiic_softc *sc = (struct dwiic_softc *)self;
3699dfaab1Sderaadt 	int rv;
37c6df0db7Sjcs 
38c6df0db7Sjcs 	switch (act) {
39c6df0db7Sjcs 	case DVACT_SUSPEND:
4099dfaab1Sderaadt 		rv = config_activate_children(self, act);
41c6df0db7Sjcs 		/* disable controller */
42c6df0db7Sjcs 		dwiic_enable(sc, 0);
43c6df0db7Sjcs 
44c6df0db7Sjcs 		/* disable interrupts */
45c6df0db7Sjcs 		dwiic_write(sc, DW_IC_INTR_MASK, 0);
46c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_INTR);
47c6df0db7Sjcs 		break;
480fa92fe2Sderaadt 	case DVACT_RESUME:
49c6b110bcSderaadt 		/* if it became enabled for some reason, force it down */
50c6b110bcSderaadt 		dwiic_enable(sc, 0);
51c6b110bcSderaadt 
52c6b110bcSderaadt 		dwiic_write(sc, DW_IC_INTR_MASK, 0);
53c6b110bcSderaadt 		dwiic_read(sc, DW_IC_CLR_INTR);
54c6b110bcSderaadt 
55c6b110bcSderaadt 		/* write standard-mode SCL timing parameters */
56c6b110bcSderaadt 		dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt);
57c6b110bcSderaadt 		dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt);
58c6b110bcSderaadt 
59c6b110bcSderaadt 		/* and fast-mode SCL timing parameters */
60c6b110bcSderaadt 		dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt);
61c6b110bcSderaadt 		dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt);
62c6b110bcSderaadt 
63c6b110bcSderaadt 		/* SDA hold time */
64c6b110bcSderaadt 		dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time);
65c6b110bcSderaadt 
66c6b110bcSderaadt 		dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2);
67c6b110bcSderaadt 		dwiic_write(sc, DW_IC_RX_TL, 0);
68c6b110bcSderaadt 
69c6b110bcSderaadt 		/* configure as i2c master with fast speed */
70c6b110bcSderaadt 		sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
71c6b110bcSderaadt 		    DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
72c6b110bcSderaadt 		dwiic_write(sc, DW_IC_CON, sc->master_cfg);
7399dfaab1Sderaadt 		rv = config_activate_children(self, act);
7499dfaab1Sderaadt 		break;
7599dfaab1Sderaadt 	default:
7699dfaab1Sderaadt 		rv = config_activate_children(self, act);
77c6df0db7Sjcs 		break;
78c6df0db7Sjcs 	}
7999dfaab1Sderaadt 	return rv;
80c6df0db7Sjcs }
81c6df0db7Sjcs 
82c6df0db7Sjcs int
83c6df0db7Sjcs dwiic_i2c_print(void *aux, const char *pnp)
84c6df0db7Sjcs {
85c6df0db7Sjcs 	struct i2c_attach_args *ia = aux;
86c6df0db7Sjcs 
87c6df0db7Sjcs 	if (pnp != NULL)
883eb71d7dSkettenis 		printf("\"%s\" at %s", ia->ia_name, pnp);
89c6df0db7Sjcs 
90c6df0db7Sjcs 	printf(" addr 0x%x", ia->ia_addr);
91c6df0db7Sjcs 
92c6df0db7Sjcs 	return UNCONF;
93c6df0db7Sjcs }
94c6df0db7Sjcs 
95c6df0db7Sjcs uint32_t
96c6df0db7Sjcs dwiic_read(struct dwiic_softc *sc, int offset)
97c6df0db7Sjcs {
98c6df0db7Sjcs 	u_int32_t b = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset);
99c6df0db7Sjcs 
100c6df0db7Sjcs 	DPRINTF(("%s: read at 0x%x = 0x%x\n", sc->sc_dev.dv_xname, offset, b));
101c6df0db7Sjcs 
102c6df0db7Sjcs 	return b;
103c6df0db7Sjcs }
104c6df0db7Sjcs 
105c6df0db7Sjcs void
106c6df0db7Sjcs dwiic_write(struct dwiic_softc *sc, int offset, uint32_t val)
107c6df0db7Sjcs {
108c6df0db7Sjcs 	DPRINTF(("%s: write at 0x%x: 0x%x\n", sc->sc_dev.dv_xname, offset,
109c6df0db7Sjcs 	    val));
110c6df0db7Sjcs 
111c6df0db7Sjcs 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val);
112c6df0db7Sjcs }
113c6df0db7Sjcs 
114c6df0db7Sjcs int
115c6df0db7Sjcs dwiic_i2c_acquire_bus(void *cookie, int flags)
116c6df0db7Sjcs {
117c6df0db7Sjcs 	struct dwiic_softc *sc = cookie;
118c6df0db7Sjcs 
119c6df0db7Sjcs 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
120c6df0db7Sjcs 		return (0);
121c6df0db7Sjcs 
122c6df0db7Sjcs 	return rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR);
123c6df0db7Sjcs }
124c6df0db7Sjcs 
125c6df0db7Sjcs void
126c6df0db7Sjcs dwiic_i2c_release_bus(void *cookie, int flags)
127c6df0db7Sjcs {
128c6df0db7Sjcs 	struct dwiic_softc *sc = cookie;
129c6df0db7Sjcs 
130c6df0db7Sjcs 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
131c6df0db7Sjcs 		return;
132c6df0db7Sjcs 
133c6df0db7Sjcs 	rw_exit(&sc->sc_i2c_lock);
134c6df0db7Sjcs }
135c6df0db7Sjcs 
136c6df0db7Sjcs int
137c6df0db7Sjcs dwiic_init(struct dwiic_softc *sc)
138c6df0db7Sjcs {
139c6df0db7Sjcs 	uint32_t reg;
140a097e4f1Skettenis 	uint8_t tx_fifo_depth;
141a097e4f1Skettenis 	uint8_t rx_fifo_depth;
142c6df0db7Sjcs 
143c6df0db7Sjcs 	/* make sure we're talking to a device we know */
144c6df0db7Sjcs 	reg = dwiic_read(sc, DW_IC_COMP_TYPE);
145c6df0db7Sjcs 	if (reg != DW_IC_COMP_TYPE_VALUE) {
146c6df0db7Sjcs 		DPRINTF(("%s: invalid component type 0x%x\n",
147c6df0db7Sjcs 		    sc->sc_dev.dv_xname, reg));
148c6df0db7Sjcs 		return 1;
149c6df0db7Sjcs 	}
150c6df0db7Sjcs 
1512567c03bSjcs 	/* fetch default timing parameters if not already specified */
1522567c03bSjcs 	if (!sc->ss_hcnt)
1532567c03bSjcs 		sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT);
1542567c03bSjcs 	if (!sc->ss_lcnt)
1552567c03bSjcs 		sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT);
1562567c03bSjcs 	if (!sc->fs_hcnt)
1572567c03bSjcs 		sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT);
1582567c03bSjcs 	if (!sc->fs_lcnt)
1592567c03bSjcs 		sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT);
1602567c03bSjcs 	if (!sc->sda_hold_time)
1612567c03bSjcs 		sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD);
1622567c03bSjcs 
163c6df0db7Sjcs 	/* disable the adapter */
164c6df0db7Sjcs 	dwiic_enable(sc, 0);
165c6df0db7Sjcs 
166c6df0db7Sjcs 	/* write standard-mode SCL timing parameters */
167c6df0db7Sjcs 	dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt);
168c6df0db7Sjcs 	dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt);
169c6df0db7Sjcs 
170c6df0db7Sjcs 	/* and fast-mode SCL timing parameters */
171c6df0db7Sjcs 	dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt);
172c6df0db7Sjcs 	dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt);
173c6df0db7Sjcs 
174c6df0db7Sjcs 	/* SDA hold time */
175c6df0db7Sjcs 	reg = dwiic_read(sc, DW_IC_COMP_VERSION);
176c6df0db7Sjcs 	if (reg >= DW_IC_SDA_HOLD_MIN_VERS)
177c6df0db7Sjcs 		dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time);
178c6df0db7Sjcs 
179c6df0db7Sjcs 	/* FIFO threshold levels */
180c6df0db7Sjcs 	sc->tx_fifo_depth = 32;
181c6df0db7Sjcs 	sc->rx_fifo_depth = 32;
182a097e4f1Skettenis 	reg = dwiic_read(sc, DW_IC_COMP_PARAM_1);
183a097e4f1Skettenis 	tx_fifo_depth = DW_IC_TX_FIFO_DEPTH(reg);
184a097e4f1Skettenis 	rx_fifo_depth = DW_IC_RX_FIFO_DEPTH(reg);
185a097e4f1Skettenis 	if (tx_fifo_depth > 1 && tx_fifo_depth < sc->tx_fifo_depth)
186a097e4f1Skettenis 		sc->tx_fifo_depth = tx_fifo_depth;
187a097e4f1Skettenis 	if (rx_fifo_depth > 1 && rx_fifo_depth < sc->rx_fifo_depth)
188a097e4f1Skettenis 		sc->rx_fifo_depth = rx_fifo_depth;
189a097e4f1Skettenis 
190c6df0db7Sjcs 	dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2);
191c6df0db7Sjcs 	dwiic_write(sc, DW_IC_RX_TL, 0);
192c6df0db7Sjcs 
193c6df0db7Sjcs 	/* configure as i2c master with fast speed */
194c6df0db7Sjcs 	sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
195c6df0db7Sjcs 	    DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
196c6df0db7Sjcs 	dwiic_write(sc, DW_IC_CON, sc->master_cfg);
197c6df0db7Sjcs 
198c6df0db7Sjcs 	return 0;
199c6df0db7Sjcs }
200c6df0db7Sjcs 
201c6df0db7Sjcs void
202c6df0db7Sjcs dwiic_enable(struct dwiic_softc *sc, int enable)
203c6df0db7Sjcs {
204c6df0db7Sjcs 	int retries;
205c6df0db7Sjcs 
206c6df0db7Sjcs 	for (retries = 100; retries > 0; retries--) {
207c6df0db7Sjcs 		dwiic_write(sc, DW_IC_ENABLE, enable);
208c6df0db7Sjcs 		if ((dwiic_read(sc, DW_IC_ENABLE_STATUS) & 1) == enable)
209c6df0db7Sjcs 			return;
210c6df0db7Sjcs 
211c6df0db7Sjcs 		DELAY(25);
212c6df0db7Sjcs 	}
213c6df0db7Sjcs 
214c6df0db7Sjcs 	printf("%s: failed to %sable\n", sc->sc_dev.dv_xname,
215c6df0db7Sjcs 	    (enable ? "en" : "dis"));
216c6df0db7Sjcs }
217c6df0db7Sjcs 
218c6df0db7Sjcs int
219c6df0db7Sjcs dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
220c6df0db7Sjcs     size_t cmdlen, void *buf, size_t len, int flags)
221c6df0db7Sjcs {
222c6df0db7Sjcs 	struct dwiic_softc *sc = cookie;
223c6df0db7Sjcs 	u_int32_t ic_con, st, cmd, resp;
224c6df0db7Sjcs 	int retries, tx_limit, rx_avail, x, readpos;
225c6df0db7Sjcs 	uint8_t *b;
22635d201e4Skettenis 	int s;
227c6df0db7Sjcs 
228c6df0db7Sjcs 	if (sc->sc_busy)
229c6df0db7Sjcs 		return 1;
230c6df0db7Sjcs 
231c6df0db7Sjcs 	sc->sc_busy++;
232c6df0db7Sjcs 
233c6df0db7Sjcs 	DPRINTF(("%s: %s: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
234c6df0db7Sjcs 	    "flags 0x%02x\n", sc->sc_dev.dv_xname, __func__, op, addr, cmdlen,
235c6df0db7Sjcs 	    len, flags));
236c6df0db7Sjcs 
237c6df0db7Sjcs 	/* setup transfer */
238c6df0db7Sjcs 	sc->sc_i2c_xfer.op = op;
239c6df0db7Sjcs 	sc->sc_i2c_xfer.buf = buf;
240c6df0db7Sjcs 	sc->sc_i2c_xfer.len = len;
241c6df0db7Sjcs 	sc->sc_i2c_xfer.flags = flags;
242c6df0db7Sjcs 	sc->sc_i2c_xfer.error = 0;
243c6df0db7Sjcs 
244c6df0db7Sjcs 	/* wait for bus to be idle */
245c6df0db7Sjcs 	for (retries = 100; retries > 0; retries--) {
246c6df0db7Sjcs 		st = dwiic_read(sc, DW_IC_STATUS);
247c6df0db7Sjcs 		if (!(st & DW_IC_STATUS_ACTIVITY))
248c6df0db7Sjcs 			break;
249c6df0db7Sjcs 		DELAY(1000);
250c6df0db7Sjcs 	}
251c6df0db7Sjcs 	DPRINTF(("%s: %s: status 0x%x\n", sc->sc_dev.dv_xname, __func__, st));
252c6df0db7Sjcs 	if (st & DW_IC_STATUS_ACTIVITY) {
253c6df0db7Sjcs 		sc->sc_busy = 0;
254c6df0db7Sjcs 		return (1);
255c6df0db7Sjcs 	}
256c6df0db7Sjcs 
257c6df0db7Sjcs 	if (cold || sc->sc_poll)
258c6df0db7Sjcs 		flags |= I2C_F_POLL;
259c6df0db7Sjcs 
260c6df0db7Sjcs 	/* disable controller */
261c6df0db7Sjcs 	dwiic_enable(sc, 0);
262c6df0db7Sjcs 
263c6df0db7Sjcs 	/* set slave address */
264c6df0db7Sjcs 	ic_con = dwiic_read(sc, DW_IC_CON);
265c6df0db7Sjcs 	ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
266c6df0db7Sjcs 	dwiic_write(sc, DW_IC_CON, ic_con);
267c6df0db7Sjcs 	dwiic_write(sc, DW_IC_TAR, addr);
268c6df0db7Sjcs 
269c6df0db7Sjcs 	/* disable interrupts */
270c6df0db7Sjcs 	dwiic_write(sc, DW_IC_INTR_MASK, 0);
271c6df0db7Sjcs 	dwiic_read(sc, DW_IC_CLR_INTR);
272c6df0db7Sjcs 
273c6df0db7Sjcs 	/* enable controller */
274c6df0db7Sjcs 	dwiic_enable(sc, 1);
275c6df0db7Sjcs 
276c6df0db7Sjcs 	/* wait until the controller is ready for commands */
277c6df0db7Sjcs 	if (flags & I2C_F_POLL)
278c6df0db7Sjcs 		DELAY(200);
279c6df0db7Sjcs 	else {
28035d201e4Skettenis 		s = splbio();
281c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_INTR);
282c6df0db7Sjcs 		dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY);
283c6df0db7Sjcs 
284c7da16f2Scheloha 		if (tsleep_nsec(&sc->sc_writewait, PRIBIO, "dwiic",
285c7da16f2Scheloha 		    MSEC_TO_NSEC(500)) != 0)
286c6df0db7Sjcs 			printf("%s: timed out waiting for tx_empty intr\n",
287c6df0db7Sjcs 			    sc->sc_dev.dv_xname);
28835d201e4Skettenis 		splx(s);
289c6df0db7Sjcs 	}
290c6df0db7Sjcs 
291c6df0db7Sjcs 	/* send our command, one byte at a time */
292c6df0db7Sjcs 	if (cmdlen > 0) {
293c6df0db7Sjcs 		b = (void *)cmdbuf;
294c6df0db7Sjcs 
295c6df0db7Sjcs 		DPRINTF(("%s: %s: sending cmd (len %zu):", sc->sc_dev.dv_xname,
296c6df0db7Sjcs 		    __func__, cmdlen));
297c6df0db7Sjcs 		for (x = 0; x < cmdlen; x++)
298c6df0db7Sjcs 			DPRINTF((" %02x", b[x]));
299c6df0db7Sjcs 		DPRINTF(("\n"));
300c6df0db7Sjcs 
301c6df0db7Sjcs 		tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR);
302c6df0db7Sjcs 		if (cmdlen > tx_limit) {
303c6df0db7Sjcs 			/* TODO */
304c6df0db7Sjcs 			printf("%s: can't write %zu (> %d)\n",
305c6df0db7Sjcs 			    sc->sc_dev.dv_xname, cmdlen, tx_limit);
306c6df0db7Sjcs 			sc->sc_i2c_xfer.error = 1;
307c6df0db7Sjcs 			sc->sc_busy = 0;
308c6df0db7Sjcs 			return (1);
309c6df0db7Sjcs 		}
310c6df0db7Sjcs 
311c6df0db7Sjcs 		for (x = 0; x < cmdlen; x++) {
312c6df0db7Sjcs 			cmd = b[x];
313c6df0db7Sjcs 			/*
314c6df0db7Sjcs 			 * Generate STOP condition if this is the last
315c6df0db7Sjcs 			 * byte of the transfer.
316c6df0db7Sjcs 			 */
317c6df0db7Sjcs 			if (x == (cmdlen - 1) && len == 0 && I2C_OP_STOP_P(op))
318c6df0db7Sjcs 				cmd |= DW_IC_DATA_CMD_STOP;
319c6df0db7Sjcs 			dwiic_write(sc, DW_IC_DATA_CMD, cmd);
320c6df0db7Sjcs 		}
321c6df0db7Sjcs 	}
322c6df0db7Sjcs 
323c6df0db7Sjcs 	b = (void *)buf;
324c6df0db7Sjcs 	x = readpos = 0;
325c6df0db7Sjcs 	tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR);
326c6df0db7Sjcs 
327c6df0db7Sjcs 	DPRINTF(("%s: %s: need to read %zu bytes, can send %d read reqs\n",
328c6df0db7Sjcs 		sc->sc_dev.dv_xname, __func__, len, tx_limit));
329c6df0db7Sjcs 
330c6df0db7Sjcs 	while (x < len) {
331c6df0db7Sjcs 		if (I2C_OP_WRITE_P(op))
332c6df0db7Sjcs 			cmd = b[x];
333c6df0db7Sjcs 		else
334c6df0db7Sjcs 			cmd = DW_IC_DATA_CMD_READ;
335c6df0db7Sjcs 
336c6df0db7Sjcs 		/*
337c6df0db7Sjcs 		 * Generate RESTART condition if we're reversing
338c6df0db7Sjcs 		 * direction.
339c6df0db7Sjcs 		 */
340c6df0db7Sjcs 		if (x == 0 && cmdlen > 0 && I2C_OP_READ_P(op))
341c6df0db7Sjcs 			cmd |= DW_IC_DATA_CMD_RESTART;
342c6df0db7Sjcs 		/*
3434b1a56afSjsg 		 * Generate STOP condition on the last byte of the
344c6df0db7Sjcs 		 * transfer.
345c6df0db7Sjcs 		 */
346c6df0db7Sjcs 		if (x == (len - 1) && I2C_OP_STOP_P(op))
347c6df0db7Sjcs 			cmd |= DW_IC_DATA_CMD_STOP;
348c6df0db7Sjcs 
349c6df0db7Sjcs 		dwiic_write(sc, DW_IC_DATA_CMD, cmd);
350c6df0db7Sjcs 
351e636b2a9Skettenis 		/*
352e636b2a9Skettenis 		 * For a block read, get the byte count before
353e636b2a9Skettenis 		 * continuing to read the data bytes.
354e636b2a9Skettenis 		 */
355e636b2a9Skettenis 		if (I2C_OP_READ_P(op) && I2C_OP_BLKMODE_P(op) && readpos == 0)
356e636b2a9Skettenis 			tx_limit = 1;
357e636b2a9Skettenis 
358c6df0db7Sjcs 		tx_limit--;
359c6df0db7Sjcs 		x++;
360c6df0db7Sjcs 
361c6df0db7Sjcs 		/*
362c6df0db7Sjcs 		 * As TXFLR fills up, we need to clear it out by reading all
363c6df0db7Sjcs 		 * available data.
364c6df0db7Sjcs 		 */
36535d201e4Skettenis 		while (I2C_OP_READ_P(op) && (tx_limit == 0 || x == len)) {
366c6df0db7Sjcs 			DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n",
367c6df0db7Sjcs 			    sc->sc_dev.dv_xname, __func__, tx_limit, x));
368c6df0db7Sjcs 
369c6df0db7Sjcs 			if (flags & I2C_F_POLL) {
37066fc675eSkettenis 				for (retries = 1000; retries > 0; retries--) {
371c6df0db7Sjcs 					rx_avail = dwiic_read(sc, DW_IC_RXFLR);
372c6df0db7Sjcs 					if (rx_avail > 0)
373c6df0db7Sjcs 						break;
374c6df0db7Sjcs 					DELAY(50);
375c6df0db7Sjcs 				}
376c6df0db7Sjcs 			} else {
37735d201e4Skettenis 				s = splbio();
378c6df0db7Sjcs 				dwiic_read(sc, DW_IC_CLR_INTR);
379c6df0db7Sjcs 				dwiic_write(sc, DW_IC_INTR_MASK,
380c6df0db7Sjcs 				    DW_IC_INTR_RX_FULL);
381c6df0db7Sjcs 
382c7da16f2Scheloha 				if (tsleep_nsec(&sc->sc_readwait, PRIBIO,
383c7da16f2Scheloha 				    "dwiic", MSEC_TO_NSEC(500)) != 0)
384c6df0db7Sjcs 					printf("%s: timed out waiting for "
385c6df0db7Sjcs 					    "rx_full intr\n",
386c6df0db7Sjcs 					    sc->sc_dev.dv_xname);
38735d201e4Skettenis 				splx(s);
388c6df0db7Sjcs 
389c6df0db7Sjcs 				rx_avail = dwiic_read(sc, DW_IC_RXFLR);
390c6df0db7Sjcs 			}
391c6df0db7Sjcs 
392c6df0db7Sjcs 			if (rx_avail == 0) {
393c6df0db7Sjcs 				printf("%s: timed out reading remaining %d\n",
394e636b2a9Skettenis 				    sc->sc_dev.dv_xname, (int)(len - readpos));
395c6df0db7Sjcs 				sc->sc_i2c_xfer.error = 1;
396c6df0db7Sjcs 				sc->sc_busy = 0;
397c6df0db7Sjcs 
398c6df0db7Sjcs 				return (1);
399c6df0db7Sjcs 			}
400c6df0db7Sjcs 
401c6df0db7Sjcs 			DPRINTF(("%s: %s: %d avail to read (%zu remaining)\n",
402c6df0db7Sjcs 			    sc->sc_dev.dv_xname, __func__, rx_avail,
403c6df0db7Sjcs 			    len - readpos));
404c6df0db7Sjcs 
405c6df0db7Sjcs 			while (rx_avail > 0) {
406c6df0db7Sjcs 				resp = dwiic_read(sc, DW_IC_DATA_CMD);
407c6df0db7Sjcs 				if (readpos < len) {
408c6df0db7Sjcs 					b[readpos] = resp;
409c6df0db7Sjcs 					readpos++;
410c6df0db7Sjcs 				}
411c6df0db7Sjcs 				rx_avail--;
412c6df0db7Sjcs 			}
413c6df0db7Sjcs 
414e636b2a9Skettenis 			/*
415e636b2a9Skettenis 			 * Update the transfer length when doing a
416e636b2a9Skettenis 			 * block read.
417e636b2a9Skettenis 			 */
418e636b2a9Skettenis 			if (I2C_OP_BLKMODE_P(op) && readpos > 0 && len > b[0])
419e636b2a9Skettenis 				len = b[0] + 1;
420e636b2a9Skettenis 
421c6df0db7Sjcs 			if (readpos >= len)
422c6df0db7Sjcs 				break;
423c6df0db7Sjcs 
424c6df0db7Sjcs 			DPRINTF(("%s: still need to read %d bytes\n",
425c6df0db7Sjcs 			    sc->sc_dev.dv_xname, (int)(len - readpos)));
426c6df0db7Sjcs 			tx_limit = sc->tx_fifo_depth -
427c6df0db7Sjcs 			    dwiic_read(sc, DW_IC_TXFLR);
428c6df0db7Sjcs 		}
4298231eef2Spatrick 
4308231eef2Spatrick 		if (I2C_OP_WRITE_P(op) && tx_limit == 0 && x < len) {
4318231eef2Spatrick 			if (flags & I2C_F_POLL) {
4328231eef2Spatrick 				for (retries = 1000; retries > 0; retries--) {
4338231eef2Spatrick 					tx_limit = sc->tx_fifo_depth -
4348231eef2Spatrick 					    dwiic_read(sc, DW_IC_TXFLR);
4358231eef2Spatrick 					if (tx_limit > 0)
4368231eef2Spatrick 						break;
4378231eef2Spatrick 					DELAY(50);
4388231eef2Spatrick 				}
4398231eef2Spatrick 			} else {
4408231eef2Spatrick 				s = splbio();
4418231eef2Spatrick 				dwiic_read(sc, DW_IC_CLR_INTR);
4428231eef2Spatrick 				dwiic_write(sc, DW_IC_INTR_MASK,
4438231eef2Spatrick 				    DW_IC_INTR_TX_EMPTY);
4448231eef2Spatrick 
4458231eef2Spatrick 				if (tsleep_nsec(&sc->sc_writewait, PRIBIO,
4468231eef2Spatrick 				    "dwiic", MSEC_TO_NSEC(500)) != 0)
4478231eef2Spatrick 					printf("%s: timed out waiting for "
4488231eef2Spatrick 					    "tx_empty intr\n",
4498231eef2Spatrick 					    sc->sc_dev.dv_xname);
4508231eef2Spatrick 				splx(s);
4518231eef2Spatrick 
4528231eef2Spatrick 				tx_limit = sc->tx_fifo_depth -
4538231eef2Spatrick 				    dwiic_read(sc, DW_IC_TXFLR);
4548231eef2Spatrick 			}
4558231eef2Spatrick 
4568231eef2Spatrick 			if (tx_limit == 0) {
4578231eef2Spatrick 				printf("%s: timed out writing remaining %d\n",
4588231eef2Spatrick 				    sc->sc_dev.dv_xname, (int)(len - x));
4598231eef2Spatrick 				sc->sc_i2c_xfer.error = 1;
4608231eef2Spatrick 				sc->sc_busy = 0;
4618231eef2Spatrick 
4628231eef2Spatrick 				return (1);
4638231eef2Spatrick 			}
4648231eef2Spatrick 		}
465c6df0db7Sjcs 	}
466c6df0db7Sjcs 
46735d201e4Skettenis 	if (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) {
46835d201e4Skettenis 		if (flags & I2C_F_POLL) {
46935d201e4Skettenis 			for (retries = 100; retries > 0; retries--) {
4702933375bSkettenis 				st = dwiic_read(sc, DW_IC_RAW_INTR_STAT);
4712933375bSkettenis 				if (st & DW_IC_INTR_STOP_DET)
47235d201e4Skettenis 					break;
47335d201e4Skettenis 				DELAY(1000);
47435d201e4Skettenis 			}
4752933375bSkettenis 			if (!(st & DW_IC_INTR_STOP_DET))
47635d201e4Skettenis 				printf("%s: timed out waiting for bus idle\n",
47735d201e4Skettenis 				    sc->sc_dev.dv_xname);
47835d201e4Skettenis 		} else {
47935d201e4Skettenis 			s = splbio();
48035d201e4Skettenis 			while (sc->sc_busy) {
48135d201e4Skettenis 				dwiic_write(sc, DW_IC_INTR_MASK,
48235d201e4Skettenis 				    DW_IC_INTR_STOP_DET);
483c7da16f2Scheloha 				if (tsleep_nsec(&sc->sc_busy, PRIBIO, "dwiic",
484c7da16f2Scheloha 				    MSEC_TO_NSEC(500)) != 0)
48535d201e4Skettenis 					printf("%s: timed out waiting for "
48635d201e4Skettenis 					    "stop intr\n",
48735d201e4Skettenis 					    sc->sc_dev.dv_xname);
48835d201e4Skettenis 			}
48935d201e4Skettenis 			splx(s);
49035d201e4Skettenis 		}
49135d201e4Skettenis 	}
492c6df0db7Sjcs 	sc->sc_busy = 0;
493c6df0db7Sjcs 
494c6df0db7Sjcs 	return 0;
495c6df0db7Sjcs }
496c6df0db7Sjcs 
497c6df0db7Sjcs uint32_t
498c6df0db7Sjcs dwiic_read_clear_intrbits(struct dwiic_softc *sc)
499c6df0db7Sjcs {
500c6df0db7Sjcs 	uint32_t stat;
501c6df0db7Sjcs 
502c6df0db7Sjcs 	stat = dwiic_read(sc, DW_IC_INTR_STAT);
503c6df0db7Sjcs 
504c6df0db7Sjcs 	if (stat & DW_IC_INTR_RX_UNDER)
505c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_RX_UNDER);
506c6df0db7Sjcs 	if (stat & DW_IC_INTR_RX_OVER)
507c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_RX_OVER);
508c6df0db7Sjcs 	if (stat & DW_IC_INTR_TX_OVER)
509c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_TX_OVER);
510c6df0db7Sjcs 	if (stat & DW_IC_INTR_RD_REQ)
511c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_RD_REQ);
512c6df0db7Sjcs 	if (stat & DW_IC_INTR_TX_ABRT)
513c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_TX_ABRT);
514c6df0db7Sjcs 	if (stat & DW_IC_INTR_RX_DONE)
515c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_RX_DONE);
516c6df0db7Sjcs 	if (stat & DW_IC_INTR_ACTIVITY)
517c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_ACTIVITY);
518c6df0db7Sjcs 	if (stat & DW_IC_INTR_STOP_DET)
519c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_STOP_DET);
520c6df0db7Sjcs 	if (stat & DW_IC_INTR_START_DET)
521c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_START_DET);
522c6df0db7Sjcs 	if (stat & DW_IC_INTR_GEN_CALL)
523c6df0db7Sjcs 		dwiic_read(sc, DW_IC_CLR_GEN_CALL);
524c6df0db7Sjcs 
525c6df0db7Sjcs 	return stat;
526c6df0db7Sjcs }
527c6df0db7Sjcs 
528c6df0db7Sjcs int
529c6df0db7Sjcs dwiic_intr(void *arg)
530c6df0db7Sjcs {
531c6df0db7Sjcs 	struct dwiic_softc *sc = arg;
532c6df0db7Sjcs 	uint32_t en, stat;
533c6df0db7Sjcs 
534c6df0db7Sjcs 	en = dwiic_read(sc, DW_IC_ENABLE);
535c6df0db7Sjcs 	/* probably for the other controller */
536c6df0db7Sjcs 	if (!en)
537c6df0db7Sjcs 		return 0;
538c6df0db7Sjcs 
539c6df0db7Sjcs 	stat = dwiic_read_clear_intrbits(sc);
540c6df0db7Sjcs 	DPRINTF(("%s: %s: enabled=0x%x stat=0x%x\n", sc->sc_dev.dv_xname,
541c6df0db7Sjcs 	    __func__, en, stat));
542c6df0db7Sjcs 	if (!(stat & ~DW_IC_INTR_ACTIVITY))
543c0e86f1bSstsp 		return 0;
544c6df0db7Sjcs 
545c6df0db7Sjcs 	if (stat & DW_IC_INTR_TX_ABRT)
546c6df0db7Sjcs 		sc->sc_i2c_xfer.error = 1;
547c6df0db7Sjcs 
548c6df0db7Sjcs 	if (sc->sc_i2c_xfer.flags & I2C_F_POLL)
549c6df0db7Sjcs 		DPRINTF(("%s: %s: intr in poll mode?\n", sc->sc_dev.dv_xname,
550c6df0db7Sjcs 		    __func__));
551c6df0db7Sjcs 	else {
552c6df0db7Sjcs 		if (stat & DW_IC_INTR_RX_FULL) {
553c6df0db7Sjcs 			dwiic_write(sc, DW_IC_INTR_MASK, 0);
554c6df0db7Sjcs 			DPRINTF(("%s: %s: waking up reader\n",
555c6df0db7Sjcs 			    sc->sc_dev.dv_xname, __func__));
556c6df0db7Sjcs 			wakeup(&sc->sc_readwait);
557c6df0db7Sjcs 		}
558c6df0db7Sjcs 		if (stat & DW_IC_INTR_TX_EMPTY) {
559c6df0db7Sjcs 			dwiic_write(sc, DW_IC_INTR_MASK, 0);
560c6df0db7Sjcs 			DPRINTF(("%s: %s: waking up writer\n",
561c6df0db7Sjcs 			    sc->sc_dev.dv_xname, __func__));
562c6df0db7Sjcs 			wakeup(&sc->sc_writewait);
563c6df0db7Sjcs 		}
56435d201e4Skettenis 		if (stat & DW_IC_INTR_STOP_DET) {
56535d201e4Skettenis 			dwiic_write(sc, DW_IC_INTR_MASK, 0);
56635d201e4Skettenis 			sc->sc_busy = 0;
56735d201e4Skettenis 			wakeup(&sc->sc_busy);
56835d201e4Skettenis 		}
569c6df0db7Sjcs 	}
570c6df0db7Sjcs 
571c6df0db7Sjcs 	return 1;
572c6df0db7Sjcs }
573