xref: /netbsd-src/sys/arch/mips/ingenic/jziic.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: jziic.c,v 1.9 2021/08/07 16:18:59 thorpej Exp $ */
2fb06cc11Smacallan 
3fb06cc11Smacallan /*-
4fb06cc11Smacallan  * Copyright (c) 2015 Michael Lorenz
5fb06cc11Smacallan  * All rights reserved.
6fb06cc11Smacallan  *
7fb06cc11Smacallan  * Redistribution and use in source and binary forms, with or without
8fb06cc11Smacallan  * modification, are permitted provided that the following conditions
9fb06cc11Smacallan  * are met:
10fb06cc11Smacallan  * 1. Redistributions of source code must retain the above copyright
11fb06cc11Smacallan  *    notice, this list of conditions and the following disclaimer.
12fb06cc11Smacallan  * 2. Redistributions in binary form must reproduce the above copyright
13fb06cc11Smacallan  *    notice, this list of conditions and the following disclaimer in the
14fb06cc11Smacallan  *    documentation and/or other materials provided with the distribution.
15fb06cc11Smacallan  *
16fb06cc11Smacallan  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17fb06cc11Smacallan  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18fb06cc11Smacallan  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19fb06cc11Smacallan  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20fb06cc11Smacallan  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21fb06cc11Smacallan  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22fb06cc11Smacallan  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23fb06cc11Smacallan  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24fb06cc11Smacallan  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25fb06cc11Smacallan  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26fb06cc11Smacallan  * POSSIBILITY OF SUCH DAMAGE.
27fb06cc11Smacallan  */
28fb06cc11Smacallan 
29fb06cc11Smacallan #include <sys/cdefs.h>
30*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.9 2021/08/07 16:18:59 thorpej Exp $");
31fb06cc11Smacallan 
32fb06cc11Smacallan /*
33fb06cc11Smacallan  * a preliminary driver for JZ4780's on-chip SMBus controllers
34fb06cc11Smacallan  * - needs more error handling and interrupt support
35fb06cc11Smacallan  * - transfers can't be more than the chip's FIFO, supposedly 16 bytes per
36fb06cc11Smacallan  *   direction
37fb06cc11Smacallan  * so, good enough for RTCs but not much else yet
38fb06cc11Smacallan  */
39fb06cc11Smacallan 
40fb06cc11Smacallan #include <sys/param.h>
41fb06cc11Smacallan #include <sys/systm.h>
4266d1b808Smacallan #include <sys/kernel.h>
43fb06cc11Smacallan #include <sys/device.h>
44fb06cc11Smacallan #include <sys/mutex.h>
45fb06cc11Smacallan #include <sys/bus.h>
46fb06cc11Smacallan #include <sys/mutex.h>
4766d1b808Smacallan #include <sys/condvar.h>
48fb06cc11Smacallan 
49fb06cc11Smacallan #include <mips/ingenic/ingenic_var.h>
50fb06cc11Smacallan #include <mips/ingenic/ingenic_regs.h>
51fb06cc11Smacallan 
52fb06cc11Smacallan #include <dev/i2c/i2cvar.h>
53fb06cc11Smacallan 
54fb06cc11Smacallan #include "opt_ingenic.h"
55fb06cc11Smacallan 
56fb06cc11Smacallan #ifdef JZIIC_DEBUG
57fb06cc11Smacallan #define DPRINTF aprint_error
5866d1b808Smacallan #define STATIC /* */
59fb06cc11Smacallan #else
60fb06cc11Smacallan #define DPRINTF while (0) printf
6166d1b808Smacallan #define STATIC static
62fb06cc11Smacallan #endif
6366d1b808Smacallan 
6466d1b808Smacallan STATIC int jziic_match(device_t, struct cfdata *, void *);
6566d1b808Smacallan STATIC void jziic_attach(device_t, device_t, void *);
66fb06cc11Smacallan 
67fb06cc11Smacallan struct jziic_softc {
68fb06cc11Smacallan 	device_t 		sc_dev;
69fb06cc11Smacallan 	bus_space_tag_t 	sc_memt;
70fb06cc11Smacallan 	bus_space_handle_t 	sc_memh;
71fb06cc11Smacallan 	struct i2c_controller 	sc_i2c;
72601e1783Sthorpej 	kmutex_t		sc_cvlock;
73fb06cc11Smacallan 	uint32_t		sc_pclk;
7466d1b808Smacallan 	/* stuff used for interrupt-driven transfers */
7566d1b808Smacallan 	const uint8_t		*sc_cmd;
7666d1b808Smacallan 	uint8_t			*sc_buf;
7766d1b808Smacallan 	uint32_t		sc_cmdlen, sc_buflen;
7866d1b808Smacallan 	uint32_t		sc_cmdptr, sc_bufptr, sc_rds;
7966d1b808Smacallan 	uint32_t		sc_abort;
8066d1b808Smacallan 	kcondvar_t		sc_ping;
8166d1b808Smacallan 	uint8_t			sc_txbuf[256];
8266d1b808Smacallan 	boolean_t		sc_reading;
83fb06cc11Smacallan };
84fb06cc11Smacallan 
85fb06cc11Smacallan CFATTACH_DECL_NEW(jziic, sizeof(struct jziic_softc),
86fb06cc11Smacallan     jziic_match, jziic_attach, NULL, NULL);
87fb06cc11Smacallan 
8866d1b808Smacallan STATIC int jziic_enable(struct jziic_softc *);
8966d1b808Smacallan STATIC void jziic_disable(struct jziic_softc *);
9066d1b808Smacallan STATIC int jziic_wait(struct jziic_softc *);
9166d1b808Smacallan STATIC void jziic_set_speed(struct jziic_softc *);
9266d1b808Smacallan STATIC int jziic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
93fb06cc11Smacallan 		    void *, size_t, int);
9466d1b808Smacallan STATIC int jziic_i2c_exec_poll(struct jziic_softc *, i2c_op_t, i2c_addr_t,
9566d1b808Smacallan     const void *, size_t, void *, size_t, int);
9666d1b808Smacallan STATIC int jziic_i2c_exec_intr(struct jziic_softc *, i2c_op_t, i2c_addr_t,
9766d1b808Smacallan     const void *, size_t, void *, size_t, int);
9866d1b808Smacallan 
9966d1b808Smacallan STATIC int jziic_intr(void *);
100fb06cc11Smacallan 
101fb06cc11Smacallan 
102fb06cc11Smacallan /* ARGSUSED */
10366d1b808Smacallan STATIC int
jziic_match(device_t parent,struct cfdata * match,void * aux)104fb06cc11Smacallan jziic_match(device_t parent, struct cfdata *match, void *aux)
105fb06cc11Smacallan {
106fb06cc11Smacallan 	struct apbus_attach_args *aa = aux;
107fb06cc11Smacallan 
108fb06cc11Smacallan 	if (strcmp(aa->aa_name, "jziic") != 0)
109fb06cc11Smacallan 		return 0;
110fb06cc11Smacallan 
111fb06cc11Smacallan 	return 1;
112fb06cc11Smacallan }
113fb06cc11Smacallan 
114fb06cc11Smacallan /* ARGSUSED */
11566d1b808Smacallan STATIC void
jziic_attach(device_t parent,device_t self,void * aux)116fb06cc11Smacallan jziic_attach(device_t parent, device_t self, void *aux)
117fb06cc11Smacallan {
118fb06cc11Smacallan 	struct jziic_softc *sc = device_private(self);
119fb06cc11Smacallan 	struct apbus_attach_args *aa = aux;
120fb06cc11Smacallan 	struct i2cbus_attach_args iba;
121fb06cc11Smacallan 	int error;
12266d1b808Smacallan 	void *ih;
123fb06cc11Smacallan #ifdef JZIIC_DEBUG
124fb06cc11Smacallan 	int i;
125fb06cc11Smacallan 	uint8_t in[1] = {0}, out[16];
126fb06cc11Smacallan #endif
127fb06cc11Smacallan 
128fb06cc11Smacallan 	sc->sc_dev = self;
129fb06cc11Smacallan 	sc->sc_pclk = aa->aa_pclk;
130fb06cc11Smacallan 	sc->sc_memt = aa->aa_bst;
131fb06cc11Smacallan 
132fb06cc11Smacallan 	error = bus_space_map(aa->aa_bst, aa->aa_addr, 0x100, 0, &sc->sc_memh);
133fb06cc11Smacallan 	if (error) {
134fb06cc11Smacallan 		aprint_error_dev(self,
135fb06cc11Smacallan 		    "can't map registers for %s: %d\n", aa->aa_name, error);
136fb06cc11Smacallan 		return;
137fb06cc11Smacallan 	}
138fb06cc11Smacallan 
13966d1b808Smacallan 	mutex_init(&sc->sc_cvlock, MUTEX_DEFAULT, IPL_NONE);
14066d1b808Smacallan 	cv_init(&sc->sc_ping, device_xname(self));
141fb06cc11Smacallan 
142fb06cc11Smacallan 	aprint_naive(": SMBus controller\n");
143fb06cc11Smacallan 	aprint_normal(": SMBus controller\n");
144fb06cc11Smacallan 
14566d1b808Smacallan 	ih = evbmips_intr_establish(aa->aa_irq, jziic_intr, sc);
146fb06cc11Smacallan 
147fb06cc11Smacallan 	if (ih == NULL) {
148fb06cc11Smacallan 		aprint_error_dev(self, "failed to establish interrupt %d\n",
149fb06cc11Smacallan 		     aa->aa_irq);
150fb06cc11Smacallan 		goto fail;
151fb06cc11Smacallan 	}
152fb06cc11Smacallan 
153fb06cc11Smacallan #ifdef JZIIC_DEBUG
154fb06cc11Smacallan 	if (jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP, 0x51, in, 1, out, 9, 0)
155fb06cc11Smacallan 	    >= 0) {
156fb06cc11Smacallan 		for (i = 0; i < 9; i++)
157fb06cc11Smacallan 			printf(" %02x", out[i]);
158fb06cc11Smacallan 		printf("\n");
159fb06cc11Smacallan 		delay(1000000);
160fb06cc11Smacallan 		jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP,
161fb06cc11Smacallan 		    0x51, in, 1, out, 9, 0);
162fb06cc11Smacallan 		for (i = 0; i < 9; i++)
163fb06cc11Smacallan 			printf(" %02x", out[i]);
164fb06cc11Smacallan 		printf("\n");
165fb06cc11Smacallan 		delay(1000000);
166fb06cc11Smacallan 		jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP,
167fb06cc11Smacallan 		    0x51, in, 1, out, 9, 0);
168fb06cc11Smacallan 		for (i = 0; i < 9; i++)
169fb06cc11Smacallan 			printf(" %02x", out[i]);
170fb06cc11Smacallan 		printf("\n");
171fb06cc11Smacallan 	}
172fb06cc11Smacallan #endif
173fb06cc11Smacallan 
174fb06cc11Smacallan 	/* fill in the i2c tag */
175601e1783Sthorpej 	iic_tag_init(&sc->sc_i2c);
176fb06cc11Smacallan 	sc->sc_i2c.ic_cookie = sc;
177fb06cc11Smacallan 	sc->sc_i2c.ic_exec = jziic_i2c_exec;
178fb06cc11Smacallan 
17921e70d0cSmacallan 	memset(&iba, 0, sizeof(iba));
180fb06cc11Smacallan 	iba.iba_tag = &sc->sc_i2c;
181*c7fb772bSthorpej 	config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE);
182fb06cc11Smacallan 
183fb06cc11Smacallan 
184fb06cc11Smacallan 	return;
185fb06cc11Smacallan 
186fb06cc11Smacallan fail:
187fb06cc11Smacallan 	if (ih) {
188fb06cc11Smacallan 		evbmips_intr_disestablish(ih);
189fb06cc11Smacallan 	}
190fb06cc11Smacallan 	bus_space_unmap(sc->sc_memt, sc->sc_memh, 0x100);
191fb06cc11Smacallan }
192fb06cc11Smacallan 
19366d1b808Smacallan STATIC int
jziic_enable(struct jziic_softc * sc)194fb06cc11Smacallan jziic_enable(struct jziic_softc *sc)
195fb06cc11Smacallan {
196fb06cc11Smacallan 	int bail = 100000;
197fb06cc11Smacallan 	uint32_t reg;
198fb06cc11Smacallan 
199fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBENB, JZ_ENABLE);
200fb06cc11Smacallan 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
201fb06cc11Smacallan 	DPRINTF("status: %02x\n", reg);
202fb06cc11Smacallan 	while ((bail > 0) && (reg == 0)) {
203fb06cc11Smacallan 		bail--;
204fb06cc11Smacallan 		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
205fb06cc11Smacallan 	}
206fb06cc11Smacallan 	DPRINTF("bail: %d\n", bail);
207fb06cc11Smacallan 	return (reg != 0);
208fb06cc11Smacallan }
209fb06cc11Smacallan 
21066d1b808Smacallan STATIC void
jziic_disable(struct jziic_softc * sc)211fb06cc11Smacallan jziic_disable(struct jziic_softc *sc)
212fb06cc11Smacallan {
213fb06cc11Smacallan 	int bail = 100000;
214fb06cc11Smacallan 	uint32_t reg;
215fb06cc11Smacallan 
216fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBENB, 0);
217fb06cc11Smacallan 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
218fb06cc11Smacallan 	DPRINTF("status: %02x\n", reg);
219fb06cc11Smacallan 	while ((bail > 0) && (reg != 0)) {
220fb06cc11Smacallan 		bail--;
221fb06cc11Smacallan 		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
222fb06cc11Smacallan 	}
223fb06cc11Smacallan 	DPRINTF("bail: %d\n", bail);
224fb06cc11Smacallan }
225fb06cc11Smacallan 
22666d1b808Smacallan STATIC int
jziic_wait(struct jziic_softc * sc)227fb06cc11Smacallan jziic_wait(struct jziic_softc *sc)
228fb06cc11Smacallan {
229fb06cc11Smacallan 	uint32_t reg;
230fb06cc11Smacallan 	int bail = 10000;
231fb06cc11Smacallan 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
232fb06cc11Smacallan 	while ((reg & JZ_MSTACT) && (bail > 0)) {
233fb06cc11Smacallan 		delay(100);
234fb06cc11Smacallan 		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
235fb06cc11Smacallan 		bail--;
236fb06cc11Smacallan 	}
237fb06cc11Smacallan 	return ((reg & JZ_MSTACT) == 0);
238fb06cc11Smacallan }
239fb06cc11Smacallan 
24066d1b808Smacallan STATIC void
jziic_set_speed(struct jziic_softc * sc)24166d1b808Smacallan jziic_set_speed(struct jziic_softc *sc)
242fb06cc11Smacallan {
243fb06cc11Smacallan 	int ticks, hcnt, lcnt, hold, setup;
244fb06cc11Smacallan 
245fb06cc11Smacallan 	/* PCLK ticks per SMBus cycle */
246fb06cc11Smacallan 	ticks = sc->sc_pclk / 100; /* assuming 100kHz for now */
247fb06cc11Smacallan 	hcnt = (ticks * 40 / (40 + 47)) - 8;
248fb06cc11Smacallan 	lcnt = (ticks * 47 / (40 + 47)) - 1;
249fb06cc11Smacallan 	hold = sc->sc_pclk * 4 / 10000 - 1; /* ... * 400 / 1000000 ... */
250d1579b2dSriastradh 	hold = uimax(1, hold);
251fb06cc11Smacallan 	hold |= JZ_HDENB;
252fb06cc11Smacallan 	setup = sc->sc_pclk * 3 / 10000 + 1; /* ... * 300 / 1000000 ... */
253fb06cc11Smacallan 	DPRINTF("hcnt %d lcnt %d hold %d\n", hcnt, lcnt, hold);
254fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSHCNT, hcnt);
255fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSLCNT, lcnt);
256fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSDAHD, hold);
257fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSDASU, setup);
258fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
259fb06cc11Smacallan 	    JZ_SLVDIS | JZ_STPHLD | JZ_REST | JZ_SPD_100KB | JZ_MD);
260fb06cc11Smacallan 	(void)bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT);
26166d1b808Smacallan }
26266d1b808Smacallan 
26366d1b808Smacallan STATIC int
jziic_i2c_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * vcmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)26466d1b808Smacallan jziic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
26566d1b808Smacallan     size_t cmdlen, void *vbuf, size_t buflen, int flags)
26666d1b808Smacallan {
26766d1b808Smacallan 	struct jziic_softc *sc = cookie;
26866d1b808Smacallan 
26948bd655eSthorpej 	if (flags & I2C_F_POLL) {
27066d1b808Smacallan 		return jziic_i2c_exec_poll(sc, op, addr, vcmd, cmdlen, vbuf,
27166d1b808Smacallan 		    buflen, flags);
27266d1b808Smacallan 	} else {
27366d1b808Smacallan #ifdef JZIIC_DEBUG
27466d1b808Smacallan 		uint8_t *b = vbuf;
27566d1b808Smacallan 		int i, ret;
27666d1b808Smacallan 
27766d1b808Smacallan 		memset(vbuf, 0, buflen);
27866d1b808Smacallan 		jziic_i2c_exec_intr(sc, op, addr, vcmd, cmdlen, vbuf,
27966d1b808Smacallan 		    buflen, flags);
28066d1b808Smacallan 		for (i = 0; i < buflen; i++) {
28166d1b808Smacallan 			printf(" %02x", b[i]);
28266d1b808Smacallan 		}
28366d1b808Smacallan 		printf("\n");
28466d1b808Smacallan 		ret = jziic_i2c_exec_poll(sc, op, addr, vcmd, cmdlen, vbuf,
28566d1b808Smacallan 		    buflen, flags);
28666d1b808Smacallan 		for (i = 0; i < buflen; i++) {
28766d1b808Smacallan 			printf(" %02x", b[i]);
28866d1b808Smacallan 		}
28966d1b808Smacallan 		printf("\n");
29066d1b808Smacallan 		return ret;
29166d1b808Smacallan #else
29266d1b808Smacallan 		return jziic_i2c_exec_intr(sc, op, addr, vcmd, cmdlen, vbuf,
29366d1b808Smacallan 		    buflen, flags);
29466d1b808Smacallan #endif
29566d1b808Smacallan 	}
29666d1b808Smacallan }
29766d1b808Smacallan 
29866d1b808Smacallan STATIC int
jziic_i2c_exec_poll(struct jziic_softc * sc,i2c_op_t op,i2c_addr_t addr,const void * vcmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)29966d1b808Smacallan jziic_i2c_exec_poll(struct jziic_softc *sc, i2c_op_t op, i2c_addr_t addr,
30066d1b808Smacallan     const void *vcmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
30166d1b808Smacallan {
30266d1b808Smacallan 	int i, bail = 10000, ret = 0;
30366d1b808Smacallan 	uint32_t abort;
30466d1b808Smacallan 	uint8_t *rx, data;
30566d1b808Smacallan 	const uint8_t *tx;
30666d1b808Smacallan 
30766d1b808Smacallan 	tx = vcmd;
30866d1b808Smacallan 	rx = vbuf;
30966d1b808Smacallan 
31066d1b808Smacallan 	DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
31166d1b808Smacallan 
31266d1b808Smacallan 	jziic_disable(sc);
31366d1b808Smacallan 
31466d1b808Smacallan 	/* we're polling, so disable interrupts */
31566d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
31666d1b808Smacallan 
31766d1b808Smacallan 	jziic_set_speed(sc);
318fb06cc11Smacallan 	jziic_wait(sc);
319fb06cc11Smacallan 	/* try to talk... */
320fb06cc11Smacallan 
321fb06cc11Smacallan 	if (!jziic_enable(sc)) {
322fb06cc11Smacallan 		ret = -1;
323fb06cc11Smacallan 		goto bork;
324fb06cc11Smacallan 	}
32566d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
326fb06cc11Smacallan 
327fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTAR, addr);
328fb06cc11Smacallan 	jziic_wait(sc);
329fb06cc11Smacallan 	DPRINTF("st: %02x\n",
330fb06cc11Smacallan 	    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST));
331fb06cc11Smacallan 	DPRINTF("wr int: %02x\n",
332fb06cc11Smacallan 	    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
333fb06cc11Smacallan 	abort = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBABTSRC);
334fb06cc11Smacallan 	DPRINTF("abort: %02x\n", abort);
335fb06cc11Smacallan 	if ((abort != 0)) {
336fb06cc11Smacallan 		ret = -1;
337fb06cc11Smacallan 		goto bork;
338fb06cc11Smacallan 	}
339fb06cc11Smacallan 
340fb06cc11Smacallan 	do {
341fb06cc11Smacallan 		bail--;
342fb06cc11Smacallan 		delay(100);
343fb06cc11Smacallan 	} while (((bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST) &
344fb06cc11Smacallan 	           JZ_TFE) == 0) && (bail > 0));
345fb06cc11Smacallan 
346fb06cc11Smacallan 	if (cmdlen != 0) {
347fb06cc11Smacallan 		for (i = 0; i < cmdlen; i++) {
348fb06cc11Smacallan 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
349fb06cc11Smacallan 			    JZ_SMBDC, *tx);
350fb06cc11Smacallan 			tx++;
351fb06cc11Smacallan 		}
352fb06cc11Smacallan 	}
353fb06cc11Smacallan 
354fb06cc11Smacallan 	if (I2C_OP_READ_P(op)) {
355fb06cc11Smacallan 		/* now read */
356fb06cc11Smacallan 		for (i = 0; i < (buflen + 1); i++) {
357fb06cc11Smacallan 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
358fb06cc11Smacallan 			    JZ_SMBDC, JZ_CMD);
359fb06cc11Smacallan 		}
360fb06cc11Smacallan 		wbflush();
361fb06cc11Smacallan 		DPRINTF("rd st: %02x\n",
362fb06cc11Smacallan 		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST));
363fb06cc11Smacallan 		DPRINTF("rd int: %02x\n",
364fb06cc11Smacallan 		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
365fb06cc11Smacallan 		DPRINTF("abort: %02x\n",
366fb06cc11Smacallan 		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBABTSRC));
367fb06cc11Smacallan 		for (i = 0; i < buflen; i++) {
368fb06cc11Smacallan 			bail = 10000;
369fb06cc11Smacallan 			while (((bus_space_read_4(sc->sc_memt, sc->sc_memh,
370fb06cc11Smacallan 				  JZ_SMBST) & JZ_RFNE) == 0) && (bail > 0)) {
371fb06cc11Smacallan 				bail--;
372fb06cc11Smacallan 				delay(100);
373fb06cc11Smacallan 			}
374fb06cc11Smacallan 			if (bail == 0) {
375fb06cc11Smacallan 				ret = -1;
376fb06cc11Smacallan 				goto bork;
377fb06cc11Smacallan 			}
378fb06cc11Smacallan 			data = bus_space_read_4(sc->sc_memt, sc->sc_memh,
379fb06cc11Smacallan 			    JZ_SMBDC);
380fb06cc11Smacallan 			DPRINTF("rd st: %02x %d\n",
381fb06cc11Smacallan 			  bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST),
382fb06cc11Smacallan 			  bail);
383fb06cc11Smacallan 			DPRINTF("rd int: %02x\n",
384fb06cc11Smacallan 			  bus_space_read_4(sc->sc_memt, sc->sc_memh,
385fb06cc11Smacallan 			   JZ_SMBINTST));
386fb06cc11Smacallan 			DPRINTF("abort: %02x\n", abort);
387fb06cc11Smacallan 			DPRINTF("rd data: %02x\n", data);
388fb06cc11Smacallan 			*rx = data;
389fb06cc11Smacallan 			rx++;
390fb06cc11Smacallan 		}
391fb06cc11Smacallan 	} else {
392fb06cc11Smacallan 		tx = vbuf;
393fb06cc11Smacallan 		for (i = 0; i < buflen; i++) {
394fb06cc11Smacallan 			DPRINTF("wr data: %02x\n", *tx);
395fb06cc11Smacallan 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
396fb06cc11Smacallan 			    JZ_SMBDC, *tx);
397fb06cc11Smacallan 			wbflush();
398fb06cc11Smacallan 			tx++;
399fb06cc11Smacallan 		}
400fb06cc11Smacallan 		jziic_wait(sc);
401fb06cc11Smacallan 		abort = bus_space_read_4(sc->sc_memt, sc->sc_memh,
402fb06cc11Smacallan 		    JZ_SMBABTSRC);
403fb06cc11Smacallan 		DPRINTF("abort: %02x\n", abort);
404fb06cc11Smacallan 		if ((abort != 0)) {
405fb06cc11Smacallan 			ret = -1;
406fb06cc11Smacallan 			goto bork;
407fb06cc11Smacallan 		}
408fb06cc11Smacallan 
409fb06cc11Smacallan 		DPRINTF("st: %02x %d\n",
410fb06cc11Smacallan 		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST), bail);
411fb06cc11Smacallan 		DPRINTF("wr int: %02x\n",
412fb06cc11Smacallan 		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
413fb06cc11Smacallan 	}
414fb06cc11Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
415fb06cc11Smacallan 	    JZ_SLVDIS | JZ_REST | JZ_SPD_100KB | JZ_MD);
416fb06cc11Smacallan bork:
417fb06cc11Smacallan 	jziic_disable(sc);
418fb06cc11Smacallan 	return ret;
419fb06cc11Smacallan }
42066d1b808Smacallan 
42166d1b808Smacallan STATIC int
jziic_i2c_exec_intr(struct jziic_softc * sc,i2c_op_t op,i2c_addr_t addr,const void * vcmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)42266d1b808Smacallan jziic_i2c_exec_intr(struct jziic_softc *sc, i2c_op_t op, i2c_addr_t addr,
42366d1b808Smacallan     const void *vcmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
42466d1b808Smacallan {
42566d1b808Smacallan 	int i, ret = 0, bail;
42666d1b808Smacallan 
42766d1b808Smacallan 	DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
42866d1b808Smacallan 
42966d1b808Smacallan 	jziic_disable(sc);
43066d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
43166d1b808Smacallan 
43266d1b808Smacallan 	mutex_enter(&sc->sc_cvlock);
43366d1b808Smacallan 
43466d1b808Smacallan 	sc->sc_reading = FALSE;
43566d1b808Smacallan 
43666d1b808Smacallan 	if (I2C_OP_READ_P(op)) {
43766d1b808Smacallan 		sc->sc_cmd = vcmd;
43866d1b808Smacallan 		sc->sc_cmdlen = cmdlen;
43966d1b808Smacallan 		sc->sc_buf = vbuf;
44066d1b808Smacallan 		sc->sc_buflen = buflen;
44166d1b808Smacallan 		memset(vbuf, 0, buflen);
44266d1b808Smacallan 	} else {
44366d1b808Smacallan 		if ((cmdlen + buflen) > 256)
44466d1b808Smacallan 			return -1;
44566d1b808Smacallan 		memcpy(sc->sc_txbuf, vcmd, cmdlen);
44666d1b808Smacallan 		memcpy(sc->sc_txbuf + cmdlen, vbuf, buflen);
44766d1b808Smacallan 		sc->sc_cmd = sc->sc_txbuf;
44866d1b808Smacallan 		sc->sc_cmdlen = cmdlen + buflen;
44966d1b808Smacallan 		sc->sc_buf = NULL;
45066d1b808Smacallan 		sc->sc_buflen = 0;
45166d1b808Smacallan 	}
45266d1b808Smacallan 	sc->sc_cmdptr = 0;
45366d1b808Smacallan 	sc->sc_bufptr = 0;
45466d1b808Smacallan 	sc->sc_rds = 0;
45566d1b808Smacallan 	sc->sc_abort = 0;
45666d1b808Smacallan 
45766d1b808Smacallan 	jziic_set_speed(sc);
45866d1b808Smacallan 	jziic_wait(sc);
45966d1b808Smacallan 
46066d1b808Smacallan 	/* set FIFO levels */
46166d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTXTL, 4);
46266d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBRXTL, 0
46366d1b808Smacallan 	    /*min(7, max(0, buflen - 2 ))*/);
46466d1b808Smacallan 
46566d1b808Smacallan 	/* try to talk... */
46666d1b808Smacallan 
46766d1b808Smacallan 	if (!jziic_enable(sc)) {
46866d1b808Smacallan 		ret = -1;
46966d1b808Smacallan 		goto bork;
47066d1b808Smacallan 	}
47166d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
47266d1b808Smacallan 
47366d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTAR, addr);
47466d1b808Smacallan 	jziic_wait(sc);
47566d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT, JZ_CLEARALL);
47666d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM,
47766d1b808Smacallan 	    JZ_TXABT | JZ_TXEMP);
47866d1b808Smacallan 
47966d1b808Smacallan 	bail = 100 * sc->sc_cmdlen;
48066d1b808Smacallan 	while ((sc->sc_cmdptr < sc->sc_cmdlen) && (bail > 0)) {
48166d1b808Smacallan 		cv_timedwait(&sc->sc_ping, &sc->sc_cvlock, 1);
48266d1b808Smacallan 		if (sc->sc_abort) {
48366d1b808Smacallan 			/* we received an abort interrupt -> bailout */
48466d1b808Smacallan 		    	DPRINTF("abort: %x\n", sc->sc_abort);
48566d1b808Smacallan 		    	ret = -1;
48666d1b808Smacallan 		    	goto bork;
48766d1b808Smacallan 		}
48866d1b808Smacallan 	    	bail--;
48966d1b808Smacallan 	}
49066d1b808Smacallan 
49166d1b808Smacallan 	if (sc->sc_cmdptr < sc->sc_cmdlen) {
49266d1b808Smacallan 		/* we didn't send everything? */
49366d1b808Smacallan 	    	DPRINTF("sent %d of %d\n", sc->sc_cmdptr, sc->sc_cmdlen);
49466d1b808Smacallan 	    	ret = -1;
49566d1b808Smacallan 	    	goto bork;
49666d1b808Smacallan 	}
49766d1b808Smacallan 
49866d1b808Smacallan 	if (I2C_OP_READ_P(op)) {
49966d1b808Smacallan 		/* now read */
50066d1b808Smacallan 		sc->sc_reading = TRUE;
50166d1b808Smacallan 		bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM,
50266d1b808Smacallan 		    JZ_TXABT | JZ_RXFL | JZ_TXEMP);
50366d1b808Smacallan 
504d1579b2dSriastradh 		for (i = 0; i < uimin((buflen + 1), 4); i++) {
50566d1b808Smacallan 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
50666d1b808Smacallan 			    JZ_SMBDC, JZ_CMD);
50766d1b808Smacallan 			wbflush();
50866d1b808Smacallan 		}
50966d1b808Smacallan 		sc->sc_rds = i;
51066d1b808Smacallan 
51166d1b808Smacallan 		bail = 10 * sc->sc_buflen; /* 10 ticks per byte should be ok */
51266d1b808Smacallan 		while ((sc->sc_bufptr < sc->sc_buflen) && (bail > 0)) {
51366d1b808Smacallan 			cv_timedwait(&sc->sc_ping, &sc->sc_cvlock, 1);
51466d1b808Smacallan 			if (sc->sc_abort) {
51566d1b808Smacallan 				/* we received an abort interrupt -> bailout */
51666d1b808Smacallan 		  	  	DPRINTF("rx abort: %x\n", sc->sc_abort);
51766d1b808Smacallan 			    	ret = -1;
51866d1b808Smacallan 			    	goto bork;
51966d1b808Smacallan 			}
52066d1b808Smacallan 			bail--;
52166d1b808Smacallan 		}
52266d1b808Smacallan 
52366d1b808Smacallan 		if (sc->sc_bufptr < sc->sc_buflen) {
52466d1b808Smacallan 			/* we didn't get everything? */
52566d1b808Smacallan 		    	DPRINTF("rcvd %d of %d\n", sc->sc_bufptr, sc->sc_buflen);
52666d1b808Smacallan 		    	ret = -1;
52766d1b808Smacallan 		    	goto bork;
52866d1b808Smacallan 		}
52966d1b808Smacallan 	}
53066d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
53166d1b808Smacallan 	    JZ_SLVDIS | JZ_REST | JZ_SPD_100KB | JZ_MD);
53266d1b808Smacallan bork:
53366d1b808Smacallan 	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
53466d1b808Smacallan 	jziic_disable(sc);
53566d1b808Smacallan 	mutex_exit(&sc->sc_cvlock);
53666d1b808Smacallan 	return ret;
53766d1b808Smacallan }
53866d1b808Smacallan 
53966d1b808Smacallan STATIC int
jziic_intr(void * cookie)54066d1b808Smacallan jziic_intr(void *cookie)
54166d1b808Smacallan {
54266d1b808Smacallan 	struct jziic_softc *sc = cookie;
54366d1b808Smacallan 	uint32_t stat, data, rstat;
54466d1b808Smacallan 	int i;
54566d1b808Smacallan 
54666d1b808Smacallan 	stat = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST);
54766d1b808Smacallan 	if (stat & JZ_TXEMP) {
54866d1b808Smacallan 		if (sc->sc_reading) {
54966d1b808Smacallan 			if (sc->sc_rds < (sc->sc_buflen + 1)) {
55066d1b808Smacallan 				for (i = 0;
551d1579b2dSriastradh 				     i < uimin(4, (sc->sc_buflen + 1) -
55266d1b808Smacallan 				                 sc->sc_rds);
55366d1b808Smacallan 				     i++) {
55466d1b808Smacallan 					bus_space_write_4( sc->sc_memt,
55566d1b808Smacallan 					    sc->sc_memh,
55666d1b808Smacallan 					    JZ_SMBDC, JZ_CMD);
55766d1b808Smacallan 					wbflush();
55866d1b808Smacallan 				}
55966d1b808Smacallan 				sc->sc_rds += i;
56066d1b808Smacallan 			} else {
56166d1b808Smacallan 				/* we're done, so turn TX FIFO interrupt off */
56266d1b808Smacallan 				bus_space_write_4(sc->sc_memt, sc->sc_memh,
56366d1b808Smacallan 				    JZ_SMBINTM,
56466d1b808Smacallan 				    JZ_TXABT | JZ_RXFL);
56566d1b808Smacallan 			}
56666d1b808Smacallan 		} else {
56766d1b808Smacallan 			rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
56866d1b808Smacallan 			    JZ_SMBST);
56966d1b808Smacallan 			while ((rstat & JZ_TFNF) &&
57066d1b808Smacallan 			         (sc->sc_cmdptr < sc->sc_cmdlen)) {
57166d1b808Smacallan 				data = *sc->sc_cmd;
57266d1b808Smacallan 				sc->sc_cmd++;
57366d1b808Smacallan 				sc->sc_cmdptr++;
57466d1b808Smacallan 				bus_space_write_4(sc->sc_memt, sc->sc_memh,
57566d1b808Smacallan 				    JZ_SMBDC, data & 0xff);
57666d1b808Smacallan 				rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
57766d1b808Smacallan 				    JZ_SMBST);
57866d1b808Smacallan 			};
57966d1b808Smacallan 			/* no need to clear this one */
58066d1b808Smacallan 			if (sc->sc_cmdptr >= sc->sc_cmdlen) {
58166d1b808Smacallan 				cv_signal(&sc->sc_ping);
58266d1b808Smacallan 				bus_space_write_4(sc->sc_memt, sc->sc_memh,
58366d1b808Smacallan 				    JZ_SMBINTM, JZ_TXABT);
58466d1b808Smacallan 			}
58566d1b808Smacallan 		}
58666d1b808Smacallan 	}
58766d1b808Smacallan 	if (stat & JZ_RXFL) {
58866d1b808Smacallan 		rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
58966d1b808Smacallan 		while ((rstat & JZ_RFNE) && (sc->sc_bufptr < sc->sc_buflen)) {
59066d1b808Smacallan 			data = bus_space_read_4(sc->sc_memt, sc->sc_memh,
59166d1b808Smacallan 			   JZ_SMBDC);
59266d1b808Smacallan 			*sc->sc_buf = (uint8_t)(data & 0xff);
59366d1b808Smacallan 			sc->sc_buf++;
59466d1b808Smacallan 			sc->sc_bufptr++;
59566d1b808Smacallan 			rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
59666d1b808Smacallan 			    JZ_SMBST);
59766d1b808Smacallan 		}
59866d1b808Smacallan 		if (sc->sc_bufptr >= sc->sc_buflen)
59966d1b808Smacallan 			cv_signal(&sc->sc_ping);
60066d1b808Smacallan 	}
60166d1b808Smacallan 	if (stat & JZ_TXABT) {
60266d1b808Smacallan 		sc->sc_abort = bus_space_read_4(sc->sc_memt, sc->sc_memh,
60366d1b808Smacallan 		    JZ_SMBABTSRC);
60466d1b808Smacallan 		cv_signal(&sc->sc_ping);
60566d1b808Smacallan 		bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT,
60666d1b808Smacallan 		    JZ_CLEARALL);
60766d1b808Smacallan 		bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
60866d1b808Smacallan 	}
60966d1b808Smacallan 	return 0;
61066d1b808Smacallan }
611