jziic.c revision 1.1
1/*	$NetBSD: jziic.c,v 1.1 2015/04/04 12:28:52 macallan Exp $ */
2
3/*-
4 * Copyright (c) 2015 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.1 2015/04/04 12:28:52 macallan Exp $");
31
32/*
33 * a preliminary driver for JZ4780's on-chip SMBus controllers
34 * - needs more error handling and interrupt support
35 * - transfers can't be more than the chip's FIFO, supposedly 16 bytes per
36 *   direction
37 * so, good enough for RTCs but not much else yet
38 */
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/device.h>
43#include <sys/mutex.h>
44#include <sys/bus.h>
45#include <sys/mutex.h>
46
47#include <mips/ingenic/ingenic_var.h>
48#include <mips/ingenic/ingenic_regs.h>
49
50#include <dev/i2c/i2cvar.h>
51
52#include "opt_ingenic.h"
53
54#ifdef JZIIC_DEBUG
55#define DPRINTF aprint_error
56#else
57#define DPRINTF while (0) printf
58#endif
59static int jziic_match(device_t, struct cfdata *, void *);
60static void jziic_attach(device_t, device_t, void *);
61
62struct jziic_softc {
63	device_t 		sc_dev;
64	bus_space_tag_t 	sc_memt;
65	bus_space_handle_t 	sc_memh;
66	struct i2c_controller 	sc_i2c;
67	kmutex_t		sc_buslock;
68	uint32_t		sc_pclk;
69};
70
71CFATTACH_DECL_NEW(jziic, sizeof(struct jziic_softc),
72    jziic_match, jziic_attach, NULL, NULL);
73
74static int jziic_enable(struct jziic_softc *);
75static void jziic_disable(struct jziic_softc *);
76static int jziic_i2c_acquire_bus(void *, int);
77static void jziic_i2c_release_bus(void *, int);
78static int jziic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
79		    void *, size_t, int);
80
81
82/* ARGSUSED */
83static int
84jziic_match(device_t parent, struct cfdata *match, void *aux)
85{
86	struct apbus_attach_args *aa = aux;
87
88	if (strcmp(aa->aa_name, "jziic") != 0)
89		return 0;
90
91	return 1;
92}
93
94/* ARGSUSED */
95static void
96jziic_attach(device_t parent, device_t self, void *aux)
97{
98	struct jziic_softc *sc = device_private(self);
99	struct apbus_attach_args *aa = aux;
100	struct i2cbus_attach_args iba;
101	int error;
102#ifdef JZIIC_DEBUG
103	int i;
104	uint8_t in[1] = {0}, out[16];
105#endif
106
107	sc->sc_dev = self;
108	sc->sc_pclk = aa->aa_pclk;
109	sc->sc_memt = aa->aa_bst;
110
111	error = bus_space_map(aa->aa_bst, aa->aa_addr, 0x100, 0, &sc->sc_memh);
112	if (error) {
113		aprint_error_dev(self,
114		    "can't map registers for %s: %d\n", aa->aa_name, error);
115		return;
116	}
117
118	mutex_init(&sc->sc_buslock, MUTEX_DEFAULT, IPL_NONE);
119
120	aprint_naive(": SMBus controller\n");
121	aprint_normal(": SMBus controller\n");
122
123#if notyet
124	ih = evbmips_intr_establish(aa->aa_irq, ohci_intr, sc);
125
126	if (ih == NULL) {
127		aprint_error_dev(self, "failed to establish interrupt %d\n",
128		     aa->aa_irq);
129		goto fail;
130	}
131#endif
132
133#ifdef JZIIC_DEBUG
134	if (jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP, 0x51, in, 1, out, 9, 0)
135	    >= 0) {
136		for (i = 0; i < 9; i++)
137			printf(" %02x", out[i]);
138		printf("\n");
139		delay(1000000);
140		jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP,
141		    0x51, in, 1, out, 9, 0);
142		for (i = 0; i < 9; i++)
143			printf(" %02x", out[i]);
144		printf("\n");
145		delay(1000000);
146		jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP,
147		    0x51, in, 1, out, 9, 0);
148		for (i = 0; i < 9; i++)
149			printf(" %02x", out[i]);
150		printf("\n");
151	}
152#endif
153
154	/* fill in the i2c tag */
155	sc->sc_i2c.ic_cookie = sc;
156	sc->sc_i2c.ic_acquire_bus = jziic_i2c_acquire_bus;
157	sc->sc_i2c.ic_release_bus = jziic_i2c_release_bus;
158	sc->sc_i2c.ic_send_start = NULL;
159	sc->sc_i2c.ic_send_stop = NULL;
160	sc->sc_i2c.ic_initiate_xfer = NULL;
161	sc->sc_i2c.ic_read_byte = NULL;
162	sc->sc_i2c.ic_write_byte = NULL;
163	sc->sc_i2c.ic_exec = jziic_i2c_exec;
164
165	iba.iba_tag = &sc->sc_i2c;
166	(void) config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print);
167
168
169	return;
170
171#if notyet
172fail:
173	if (ih) {
174		evbmips_intr_disestablish(ih);
175	}
176	bus_space_unmap(sc->sc_memt, sc->sc_memh, 0x100);
177#endif
178}
179
180static int
181jziic_enable(struct jziic_softc *sc)
182{
183	int bail = 100000;
184	uint32_t reg;
185
186	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBENB, JZ_ENABLE);
187	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
188	DPRINTF("status: %02x\n", reg);
189	while ((bail > 0) && (reg == 0)) {
190		bail--;
191		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
192	}
193	DPRINTF("bail: %d\n", bail);
194	return (reg != 0);
195}
196
197static void
198jziic_disable(struct jziic_softc *sc)
199{
200	int bail = 100000;
201	uint32_t reg;
202
203	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBENB, 0);
204	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
205	DPRINTF("status: %02x\n", reg);
206	while ((bail > 0) && (reg != 0)) {
207		bail--;
208		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBENBST);
209	}
210	DPRINTF("bail: %d\n", bail);
211}
212
213static int
214jziic_i2c_acquire_bus(void *cookie, int flags)
215{
216	struct jziic_softc *sc = cookie;
217
218	mutex_enter(&sc->sc_buslock);
219	return 0;
220}
221
222static void
223jziic_i2c_release_bus(void *cookie, int flags)
224{
225	struct jziic_softc *sc = cookie;
226
227	mutex_exit(&sc->sc_buslock);
228}
229
230static int
231jziic_wait(struct jziic_softc *sc)
232{
233	uint32_t reg;
234	int bail = 10000;
235	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
236	while ((reg & JZ_MSTACT) && (bail > 0)) {
237		delay(100);
238		reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
239		bail--;
240	}
241	return ((reg & JZ_MSTACT) == 0);
242}
243
244int
245jziic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
246    size_t cmdlen, void *vbuf, size_t buflen, int flags)
247{
248	struct jziic_softc *sc = cookie;
249	int ticks, hcnt, lcnt, hold, setup;
250	int i, bail = 10000, ret = 0;
251	uint32_t abort;
252	uint8_t *rx, data;
253	const uint8_t *tx;
254
255	tx = vcmd;
256	rx = vbuf;
257
258	DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
259
260	jziic_disable(sc);
261
262	/* set speed and such */
263
264	/* PCLK ticks per SMBus cycle */
265	ticks = sc->sc_pclk / 100; /* assuming 100kHz for now */
266	hcnt = (ticks * 40 / (40 + 47)) - 8;
267	lcnt = (ticks * 47 / (40 + 47)) - 1;
268	hold = sc->sc_pclk * 4 / 10000 - 1; /* ... * 400 / 1000000 ... */
269	hold = max(1, hold);
270	hold |= JZ_HDENB;
271	setup = sc->sc_pclk * 3 / 10000 + 1; /* ... * 300 / 1000000 ... */
272	DPRINTF("hcnt %d lcnt %d hold %d\n", hcnt, lcnt, hold);
273	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSHCNT, hcnt);
274	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSLCNT, lcnt);
275	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSDAHD, hold);
276	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBSDASU, setup);
277	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
278	    JZ_SLVDIS | JZ_STPHLD | JZ_REST | JZ_SPD_100KB | JZ_MD);
279	(void)bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT);
280	jziic_wait(sc);
281	/* try to talk... */
282
283	if (!jziic_enable(sc)) {
284		ret = -1;
285		goto bork;
286	}
287
288	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTAR, addr);
289	jziic_wait(sc);
290	DPRINTF("st: %02x\n",
291	    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST));
292	DPRINTF("wr int: %02x\n",
293	    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
294	abort = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBABTSRC);
295	DPRINTF("abort: %02x\n", abort);
296	if ((abort != 0)) {
297		ret = -1;
298		goto bork;
299	}
300
301	do {
302		bail--;
303		delay(100);
304	} while (((bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST) &
305	           JZ_TFE) == 0) && (bail > 0));
306
307	if (cmdlen != 0) {
308		for (i = 0; i < cmdlen; i++) {
309			bus_space_write_4(sc->sc_memt, sc->sc_memh,
310			    JZ_SMBDC, *tx);
311			tx++;
312		}
313	}
314
315	if (I2C_OP_READ_P(op)) {
316		/* now read */
317		for (i = 0; i < (buflen + 1); i++) {
318			bus_space_write_4(sc->sc_memt, sc->sc_memh,
319			    JZ_SMBDC, JZ_CMD);
320		}
321		wbflush();
322		DPRINTF("rd st: %02x\n",
323		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST));
324		DPRINTF("rd int: %02x\n",
325		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
326		DPRINTF("abort: %02x\n",
327		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBABTSRC));
328		for (i = 0; i < buflen; i++) {
329			bail = 10000;
330			while (((bus_space_read_4(sc->sc_memt, sc->sc_memh,
331				  JZ_SMBST) & JZ_RFNE) == 0) && (bail > 0)) {
332				bail--;
333				delay(100);
334			}
335			if (bail == 0) {
336				ret = -1;
337				goto bork;
338			}
339			data = bus_space_read_4(sc->sc_memt, sc->sc_memh,
340			    JZ_SMBDC);
341			DPRINTF("rd st: %02x %d\n",
342			  bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST),
343			  bail);
344			DPRINTF("rd int: %02x\n",
345			  bus_space_read_4(sc->sc_memt, sc->sc_memh,
346			   JZ_SMBINTST));
347			DPRINTF("abort: %02x\n", abort);
348			DPRINTF("rd data: %02x\n", data);
349			*rx = data;
350			rx++;
351		}
352	} else {
353		tx = vbuf;
354		for (i = 0; i < buflen; i++) {
355			DPRINTF("wr data: %02x\n", *tx);
356			bus_space_write_4(sc->sc_memt, sc->sc_memh,
357			    JZ_SMBDC, *tx);
358			wbflush();
359			tx++;
360		}
361		jziic_wait(sc);
362		abort = bus_space_read_4(sc->sc_memt, sc->sc_memh,
363		    JZ_SMBABTSRC);
364		DPRINTF("abort: %02x\n", abort);
365		if ((abort != 0)) {
366			ret = -1;
367			goto bork;
368		}
369
370		DPRINTF("st: %02x %d\n",
371		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST), bail);
372		DPRINTF("wr int: %02x\n",
373		    bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST));
374	}
375	bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
376	    JZ_SLVDIS | JZ_REST | JZ_SPD_100KB | JZ_MD);
377bork:
378	jziic_disable(sc);
379	return ret;
380}
381