at91_twi.c revision 164503
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 164503 2006-11-22 06:51:59Z 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	volatile 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	WR4(sc, TWI_CR, TWI_CR_SWRST);
132	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
133	WR4(sc, TWI_CWGR, sc->cwgr);
134//	WR4(sc, TWI_IER, TWI_SR_RXRDY | TWI_SR_OVRE | TWI_SR_UNRE |
135//	    TWI_SR_NACK);
136
137	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
138		device_printf(dev, "could not allocate iicbus instance\n");
139	/* probe and attach the iicbus */
140	bus_generic_attach(dev);
141out:;
142	if (err)
143		at91_twi_deactivate(dev);
144	return (err);
145}
146
147static int
148at91_twi_detach(device_t dev)
149{
150	struct at91_twi_softc *sc;
151	int rv;
152
153	sc = device_get_softc(dev);
154	at91_twi_deactivate(dev);
155	if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0)
156		return (rv);
157
158	return (0);
159}
160
161static int
162at91_twi_activate(device_t dev)
163{
164	struct at91_twi_softc *sc;
165	int rid;
166
167	sc = device_get_softc(dev);
168	rid = 0;
169	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
170	    RF_ACTIVE);
171	if (sc->mem_res == NULL)
172		goto errout;
173	rid = 0;
174	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
175	    RF_ACTIVE);
176	if (sc->irq_res == NULL)
177		goto errout;
178	return (0);
179errout:
180	at91_twi_deactivate(dev);
181	return (ENOMEM);
182}
183
184static void
185at91_twi_deactivate(device_t dev)
186{
187	struct at91_twi_softc *sc;
188
189	sc = device_get_softc(dev);
190	if (sc->intrhand)
191		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
192	sc->intrhand = 0;
193	bus_generic_detach(sc->dev);
194	if (sc->mem_res)
195		bus_release_resource(dev, SYS_RES_IOPORT,
196		    rman_get_rid(sc->mem_res), sc->mem_res);
197	sc->mem_res = 0;
198	if (sc->irq_res)
199		bus_release_resource(dev, SYS_RES_IRQ,
200		    rman_get_rid(sc->irq_res), sc->irq_res);
201	sc->irq_res = 0;
202	return;
203}
204
205static void
206at91_twi_intr(void *xsc)
207{
208	struct at91_twi_softc *sc = xsc;
209	uint32_t status;
210
211	/* Reading the status also clears the interrupt */
212	status = RD4(sc, TWI_SR);
213	printf("status %x\n", status);
214	if (status == 0)
215		return;
216	AT91_TWI_LOCK(sc);
217	if (status & TWI_SR_RXRDY)
218		sc->flags |= RXRDY;
219	if (status & TWI_SR_TXRDY)
220		sc->flags |= TXRDY;
221	AT91_TWI_UNLOCK(sc);
222	wakeup(sc);
223	return;
224}
225
226static int
227at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit)
228{
229	int err = 0;
230	int counter = 100000;
231
232	while (!(RD4(sc, TWI_SR) & bit) && counter-- >= 0)
233		continue;
234	if (counter <= 0)
235		err = EIO;
236	return (err);
237}
238
239static int
240at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
241{
242	struct at91_twi_softc *sc;
243	int ckdiv, rate;
244
245	sc = device_get_softc(dev);
246	if (oldaddr)
247		*oldaddr = sc->twi_addr;
248	if (addr != 0)
249		sc->twi_addr = 0;
250	else
251		sc->twi_addr = addr;
252
253	rate = 1;
254
255	/*
256	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
257	 */
258	switch (speed) {
259	case IIC_SLOW:
260		ckdiv = AT91C_MASTER_CLOCK / (1500 * 4) - 2;
261		break;
262
263	case IIC_FAST:
264		ckdiv = AT91C_MASTER_CLOCK / (45000 * 4) - 2;
265		break;
266
267	case IIC_UNKNOWN:
268	case IIC_FASTEST:
269	default:
270		ckdiv = AT91C_MASTER_CLOCK / (90000 * 4) - 2;
271		break;
272	}
273
274	sc->cwgr = TWI_CWGR_CKDIV(ckdiv) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(rate)) |
275	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(rate));
276	WR4(sc, TWI_CR, TWI_CR_SWRST);
277	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
278	WR4(sc, TWI_CWGR, sc->cwgr);
279
280	return 0;
281}
282
283static int
284at91_twi_callback(device_t dev, int index, caddr_t *data)
285{
286	int error = 0;
287
288	switch (index) {
289	case IIC_REQUEST_BUS:
290		break;
291
292	case IIC_RELEASE_BUS:
293		break;
294
295	default:
296		error = EINVAL;
297	}
298
299	return (error);
300}
301
302static int
303at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
304{
305	struct at91_twi_softc *sc;
306	int i, len;
307	uint32_t rdwr;
308	uint8_t *buf;
309
310	sc = device_get_softc(dev);
311	for (i = 0; i < nmsgs; i++) {
312		/*
313		 * The linux atmel driver doesn't use the internal device
314		 * address feature of twi.  A separate i2c message needs to
315		 * be written to use this.
316		 * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
317		 * for details.
318		 */
319		rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0;
320		WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr);
321		len = msgs[i].len;
322		buf = msgs[i].buf;
323		if (len == 0 || buf == NULL)
324			return (EINVAL);
325		WR4(sc, TWI_CR, TWI_CR_START);
326		if (msgs[i].flags & IIC_M_RD) {
327			while (len--) {
328				if (len == 0)
329					WR4(sc, TWI_CR, TWI_CR_STOP);
330				if (at91_twi_wait(sc, TWI_SR_RXRDY))
331					return (EIO);
332				*buf++ = RD4(sc, TWI_RHR) & 0xff;
333			}
334		} else {
335			while (len--) {
336				WR4(sc, TWI_THR, *buf++);
337				if (len == 0)
338					WR4(sc, TWI_CR, TWI_CR_STOP);
339				if (at91_twi_wait(sc, TWI_SR_TXRDY))
340					return (EIO);
341			}
342		}
343		if (at91_twi_wait(sc, TWI_SR_TXCOMP))
344			return (EIO);
345	}
346	return (0);
347}
348
349static device_method_t at91_twi_methods[] = {
350	/* Device interface */
351	DEVMETHOD(device_probe,		at91_twi_probe),
352	DEVMETHOD(device_attach,	at91_twi_attach),
353	DEVMETHOD(device_detach,	at91_twi_detach),
354
355	/* iicbus interface */
356	DEVMETHOD(iicbus_callback,	at91_twi_callback),
357	DEVMETHOD(iicbus_reset,		at91_twi_rst_card),
358	DEVMETHOD(iicbus_transfer,	at91_twi_transfer),
359	{ 0, 0 }
360};
361
362static driver_t at91_twi_driver = {
363	"at91_twi",
364	at91_twi_methods,
365	sizeof(struct at91_twi_softc),
366};
367
368DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, 0, 0);
369DRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, 0, 0);
370