at91_twi.c revision 157563
1/*-
2 * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD: head/sys/arm/at91/at91_twi.c 157563 2006-04-06 04:31:19Z imp $");
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bus.h>
31#include <sys/conf.h>
32#include <sys/kernel.h>
33#include <sys/lock.h>
34#include <sys/mbuf.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/mutex.h>
38#include <sys/rman.h>
39#include <machine/bus.h>
40
41#include <arm/at91/at91rm92reg.h>
42#include <arm/at91/at91_twireg.h>
43
44#include <dev/iicbus/iiconf.h>
45#include <dev/iicbus/iicbus.h>
46#include "iicbus_if.h"
47
48struct at91_twi_softc
49{
50	device_t dev;			/* Myself */
51	void *intrhand;			/* Interrupt handle */
52	struct resource *irq_res;	/* IRQ resource */
53	struct resource	*mem_res;	/* Memory resource */
54	struct mtx sc_mtx;		/* basically a perimeter lock */
55	int flags;
56#define RXRDY		4
57#define TXRDY		0x10
58	uint32_t cwgr;
59	int	sc_started;
60	int	twi_addr;
61	device_t iicbus;
62};
63
64static inline uint32_t
65RD4(struct at91_twi_softc *sc, bus_size_t off)
66{
67	return bus_read_4(sc->mem_res, off);
68}
69
70static inline void
71WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val)
72{
73	bus_write_4(sc->mem_res, off, val);
74}
75
76#define AT91_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
77#define	AT91_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
78#define AT91_TWI_LOCK_INIT(_sc) \
79	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
80	    "twi", MTX_DEF)
81#define AT91_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
82#define AT91_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
83#define AT91_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
84#define TWI_DEF_CLK	100000
85
86static devclass_t at91_twi_devclass;
87
88/* bus entry points */
89
90static int at91_twi_probe(device_t dev);
91static int at91_twi_attach(device_t dev);
92static int at91_twi_detach(device_t dev);
93static void at91_twi_intr(void *);
94
95/* helper routines */
96static int at91_twi_activate(device_t dev);
97static void at91_twi_deactivate(device_t dev);
98
99static int
100at91_twi_probe(device_t dev)
101{
102	device_set_desc(dev, "TWI");
103	return (0);
104}
105
106static int
107at91_twi_attach(device_t dev)
108{
109	struct at91_twi_softc *sc = device_get_softc(dev);
110	int err;
111
112	sc->dev = dev;
113	err = at91_twi_activate(dev);
114	if (err)
115		goto out;
116
117	AT91_TWI_LOCK_INIT(sc);
118
119	/*
120	 * Activate the interrupt
121	 */
122	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
123	    at91_twi_intr, sc, &sc->intrhand);
124	if (err) {
125		AT91_TWI_LOCK_DESTROY(sc);
126		goto out;
127	}
128	sc->cwgr = TWI_CWGR_CKDIV(8 * AT91C_MASTER_CLOCK / 90000) |
129	    TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) |
130	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK));
131
132	WR4(sc, TWI_CR, TWI_CR_SWRST);
133	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
134	WR4(sc, TWI_CWGR, sc->cwgr);
135
136	WR4(sc, TWI_IER, TWI_SR_RXRDY | TWI_SR_OVRE | TWI_SR_UNRE |
137	    TWI_SR_NACK);
138
139	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
140		device_printf(dev, "could not allocate iicbus instance\n");
141
142	/* probe and attach the iicbus */
143	bus_generic_attach(dev);
144
145out:;
146	if (err)
147		at91_twi_deactivate(dev);
148	return (err);
149}
150
151static int
152at91_twi_detach(device_t dev)
153{
154	struct at91_twi_softc *sc;
155	int rv;
156
157	sc = device_get_softc(dev);
158	at91_twi_deactivate(dev);
159	if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0)
160		return (rv);
161
162	return (0);
163}
164
165static int
166at91_twi_activate(device_t dev)
167{
168	struct at91_twi_softc *sc;
169	int rid;
170
171	sc = device_get_softc(dev);
172	rid = 0;
173	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
174	    RF_ACTIVE);
175	if (sc->mem_res == NULL)
176		goto errout;
177	rid = 0;
178	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
179	    RF_ACTIVE);
180	if (sc->mem_res == NULL)
181		goto errout;
182	return (0);
183errout:
184	at91_twi_deactivate(dev);
185	return (ENOMEM);
186}
187
188static void
189at91_twi_deactivate(device_t dev)
190{
191	struct at91_twi_softc *sc;
192
193	sc = device_get_softc(dev);
194	if (sc->intrhand)
195		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
196	sc->intrhand = 0;
197	bus_generic_detach(sc->dev);
198	if (sc->mem_res)
199		bus_release_resource(dev, SYS_RES_IOPORT,
200		    rman_get_rid(sc->mem_res), sc->mem_res);
201	sc->mem_res = 0;
202	if (sc->irq_res)
203		bus_release_resource(dev, SYS_RES_IRQ,
204		    rman_get_rid(sc->irq_res), sc->irq_res);
205	sc->irq_res = 0;
206	return;
207}
208
209static void
210at91_twi_intr(void *xsc)
211{
212	struct at91_twi_softc *sc = xsc;
213	uint32_t status;
214
215	/* Reading the status also clears the interrupt */
216	status = RD4(sc, TWI_SR);
217	printf("status %x\n", status);
218	if (status == 0)
219		return;
220	AT91_TWI_LOCK(sc);
221	if (status & TWI_SR_RXRDY)
222		sc->flags |= RXRDY;
223	if (status & TWI_SR_TXRDY)
224		sc->flags |= TXRDY;
225	AT91_TWI_UNLOCK(sc);
226	wakeup(sc);
227	return;
228}
229
230static int
231at91_twi_wait_stop_done(struct at91_twi_softc *sc)
232{
233	int err = 0;
234
235	while (!(RD4(sc, TWI_SR) & TWI_SR_TXCOMP))
236		continue;
237	return (err);
238}
239
240/*
241 * Stop the transfer by entering a STOP state on the iic bus.  For read
242 * operations, we've already entered the STOP state, since we need to do
243 * that to read the last character.  For write operations, we need to
244 * wait for the TXCOMP bit to turn on before returning.
245 */
246static int
247at91_twi_stop(device_t dev)
248{
249	struct at91_twi_softc *sc;
250	int err = 0;
251
252	sc = device_get_softc(dev);
253	if (sc->sc_started) {
254		WR4(sc, TWI_CR, TWI_CR_STOP);
255		err = at91_twi_wait_stop_done(sc);
256	}
257	return (err);
258}
259
260/*
261 * enter a START condition without requiring the device to be in a STOP
262 * state.
263 */
264static int
265at91_twi_repeated_start(device_t dev, u_char slave, int timeout)
266{
267	struct at91_twi_softc *sc;
268
269	sc = device_get_softc(dev);
270	WR4(sc, TWI_MMR, TWI_MMR_DADR(slave));
271	WR4(sc, TWI_CR, TWI_CR_START);
272	sc->sc_started = 1;
273	return (0);
274}
275
276/*
277 * enter a START condition from an idle state.
278 */
279static int
280at91_twi_start(device_t dev, u_char slave, int timeout)
281{
282	struct at91_twi_softc *sc;
283
284	sc = device_get_softc(dev);
285	WR4(sc, TWI_MMR, TWI_MMR_DADR(slave));
286	WR4(sc, TWI_CR, TWI_CR_START);
287	sc->sc_started = 1;
288	return (0);
289}
290
291static int
292at91_twi_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */)
293{
294	struct at91_twi_softc *sc;
295	uint8_t *walker;
296	int err = 0;
297
298	walker = buf;
299	sc = device_get_softc(dev);
300	WR4(sc, TWI_MMR, TWI_MMR_MWRITE | RD4(sc, TWI_MMR));
301	AT91_TWI_LOCK(sc);
302	WR4(sc, TWI_IER, TWI_SR_TXRDY);
303	while (len--) {
304		WR4(sc, TWI_THR, *walker++);
305		while (!(sc->flags & TXRDY)) {
306			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr",
307			    0);
308			if (err)
309				goto errout;
310		}
311	}
312errout:;
313	WR4(sc, TWI_IDR, TWI_SR_TXRDY);
314	AT91_TWI_UNLOCK(sc);
315	return (err);
316}
317
318static int
319at91_twi_read(device_t dev, char *buf, int len, int *read, int last,
320	 int delay /* us */)
321{
322	struct at91_twi_softc *sc;
323	char *walker;
324	int err = 0;
325
326	walker = buf;
327	sc = device_get_softc(dev);
328	AT91_TWI_LOCK(sc);
329	WR4(sc, TWI_MMR, ~TWI_MMR_MWRITE & RD4(sc, TWI_MMR));
330	WR4(sc, TWI_IER, TWI_SR_RXRDY);
331	while (len-- > 0) {
332		err = 0;
333		while (!(sc->flags & RXRDY)) {
334			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird",
335			    0);
336			if (err)
337				goto errout;
338		}
339		sc->flags &= ~RXRDY;
340		*walker++ = RD4(sc, TWI_RHR) & 0xff;
341		if (len == 1 && last)
342			break;
343	}
344	if (!last)
345		goto errout;
346	WR4(sc, TWI_CR, TWI_CR_STOP);
347	err = at91_twi_wait_stop_done(sc);
348	*walker = RD4(sc, TWI_RHR) & 0xff;
349	if (read)
350		*read = walker - buf;
351	sc->sc_started = 0;
352errout:;
353	WR4(sc, TWI_IDR, TWI_SR_RXRDY);
354	AT91_TWI_UNLOCK(sc);
355	return (err);
356}
357
358static int
359at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
360{
361	struct at91_twi_softc *sc;
362	int ckdiv, rate;
363
364	sc = device_get_softc(dev);
365	if (oldaddr)
366		*oldaddr = sc->twi_addr;
367	if (addr != 0)
368		sc->twi_addr = 0;
369	else
370		sc->twi_addr = addr;
371
372	rate = 1;
373
374	/*
375	 * 8 * is because "rate == 1" -> 4 clocks down, 4 clocks up.  The
376	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
377	 */
378	switch (speed) {
379	case IIC_SLOW:
380		ckdiv = 8 * AT91C_MASTER_CLOCK / 1500;
381		break;
382
383	case IIC_FAST:
384		ckdiv = 8 * AT91C_MASTER_CLOCK / 45000;
385		break;
386
387	case IIC_UNKNOWN:
388	case IIC_FASTEST:
389	default:
390		ckdiv = 8 * AT91C_MASTER_CLOCK / 90000;
391		break;
392	}
393
394	sc->cwgr = TWI_CWGR_CKDIV(ckdiv) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(rate)) |
395	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(rate));
396	WR4(sc, TWI_CR, TWI_CR_SWRST);
397	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
398	WR4(sc, TWI_CWGR, sc->cwgr);
399
400	return EIO;
401}
402
403static int
404at91_twi_callback(device_t dev, int index, caddr_t *data)
405{
406	int error = 0;
407
408	switch (index) {
409	case IIC_REQUEST_BUS:
410		break;
411
412	case IIC_RELEASE_BUS:
413		break;
414
415	default:
416		error = EINVAL;
417	}
418
419	return (error);
420}
421
422static device_method_t at91_twi_methods[] = {
423	/* Device interface */
424	DEVMETHOD(device_probe,		at91_twi_probe),
425	DEVMETHOD(device_attach,	at91_twi_attach),
426	DEVMETHOD(device_detach,	at91_twi_detach),
427
428	/* iicbus interface */
429	DEVMETHOD(iicbus_callback,	at91_twi_callback),
430	DEVMETHOD(iicbus_repeated_start, at91_twi_repeated_start),
431	DEVMETHOD(iicbus_start,		at91_twi_start),
432	DEVMETHOD(iicbus_stop,		at91_twi_stop),
433	DEVMETHOD(iicbus_write,		at91_twi_write),
434	DEVMETHOD(iicbus_read,		at91_twi_read),
435	DEVMETHOD(iicbus_reset,		at91_twi_rst_card),
436	{ 0, 0 }
437};
438
439static driver_t at91_twi_driver = {
440	"at91_twi",
441	at91_twi_methods,
442	sizeof(struct at91_twi_softc),
443};
444
445DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, 0, 0);
446