at91_twi.c revision 237432
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/arm/at91/at91_twi.c 237432 2012-06-22 06:44:22Z imp $");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/bus.h>
32#include <sys/conf.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/mbuf.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/mutex.h>
39#include <sys/rman.h>
40#include <machine/bus.h>
41
42#include <arm/at91/at91_twireg.h>
43#include <arm/at91/at91var.h>
44
45#include <dev/iicbus/iiconf.h>
46#include <dev/iicbus/iicbus.h>
47#include "iicbus_if.h"
48
49#define	TWI_SLOW_CLOCK		 1500
50#define	TWI_FAST_CLOCK		45000
51#define	TWI_FASTEST_CLOCK	90000
52
53struct at91_twi_softc
54{
55	device_t dev;			/* Myself */
56	void *intrhand;			/* Interrupt handle */
57	struct resource *irq_res;	/* IRQ resource */
58	struct resource	*mem_res;	/* Memory resource */
59	struct mtx sc_mtx;		/* basically a perimeter lock */
60	volatile uint32_t flags;
61	uint32_t cwgr;
62	int	sc_started;
63	int	twi_addr;
64	device_t iicbus;
65};
66
67static inline uint32_t
68RD4(struct at91_twi_softc *sc, bus_size_t off)
69{
70
71	return bus_read_4(sc->mem_res, off);
72}
73
74static inline void
75WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val)
76{
77
78	bus_write_4(sc->mem_res, off, val);
79}
80
81#define	AT91_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
82#define	AT91_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
83#define	AT91_TWI_LOCK_INIT(_sc) \
84	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
85	    "twi", MTX_DEF)
86#define	AT91_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
87#define	AT91_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
88#define	AT91_TWI_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
89#define	TWI_DEF_CLK	100000
90
91static devclass_t at91_twi_devclass;
92
93/* bus entry points */
94
95static int at91_twi_probe(device_t dev);
96static int at91_twi_attach(device_t dev);
97static int at91_twi_detach(device_t dev);
98static void at91_twi_intr(void *);
99
100/* helper routines */
101static int at91_twi_activate(device_t dev);
102static void at91_twi_deactivate(device_t dev);
103
104static int
105at91_twi_probe(device_t dev)
106{
107
108	device_set_desc(dev, "TWI");
109	return (0);
110}
111
112static int
113at91_twi_attach(device_t dev)
114{
115	struct at91_twi_softc *sc = device_get_softc(dev);
116	int err;
117
118	sc->dev = dev;
119	err = at91_twi_activate(dev);
120	if (err)
121		goto out;
122
123	AT91_TWI_LOCK_INIT(sc);
124
125	/*
126	 * Activate the interrupt
127	 */
128	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
129	    NULL, at91_twi_intr, sc, &sc->intrhand);
130	if (err) {
131		AT91_TWI_LOCK_DESTROY(sc);
132		goto out;
133	}
134	sc->cwgr = TWI_CWGR_CKDIV(8 * at91_master_clock / TWI_FASTEST_CLOCK) |
135	    TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) |
136	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK));
137	WR4(sc, TWI_CR, TWI_CR_SWRST);
138	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
139	WR4(sc, TWI_CWGR, sc->cwgr);
140
141	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
142		device_printf(dev, "could not allocate iicbus instance\n");
143	/* probe and attach the iicbus */
144	bus_generic_attach(dev);
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	AT91_TWI_LOCK_DESTROY(sc);
163
164	return (0);
165}
166
167static int
168at91_twi_activate(device_t dev)
169{
170	struct at91_twi_softc *sc;
171	int rid;
172
173	sc = device_get_softc(dev);
174	rid = 0;
175	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
176	    RF_ACTIVE);
177	if (sc->mem_res == NULL)
178		goto errout;
179	rid = 0;
180	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
181	    RF_ACTIVE);
182	if (sc->irq_res == NULL)
183		goto errout;
184	return (0);
185errout:
186	at91_twi_deactivate(dev);
187	return (ENOMEM);
188}
189
190static void
191at91_twi_deactivate(device_t dev)
192{
193	struct at91_twi_softc *sc;
194
195	sc = device_get_softc(dev);
196	if (sc->intrhand)
197		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
198	sc->intrhand = 0;
199	bus_generic_detach(sc->dev);
200	if (sc->mem_res)
201		bus_release_resource(dev, SYS_RES_MEMORY,
202		    rman_get_rid(sc->mem_res), sc->mem_res);
203	sc->mem_res = 0;
204	if (sc->irq_res)
205		bus_release_resource(dev, SYS_RES_IRQ,
206		    rman_get_rid(sc->irq_res), sc->irq_res);
207	sc->irq_res = 0;
208	return;
209}
210
211static void
212at91_twi_intr(void *xsc)
213{
214	struct at91_twi_softc *sc = xsc;
215	uint32_t status;
216
217	status = RD4(sc, TWI_SR);
218	if (status == 0)
219		return;
220	AT91_TWI_LOCK(sc);
221	sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK);
222	if (status & TWI_SR_RXRDY)
223		sc->flags |= TWI_SR_RXRDY;
224	if (status & TWI_SR_TXRDY)
225		sc->flags |= TWI_SR_TXRDY;
226	if (status & TWI_SR_TXCOMP)
227		sc->flags |= TWI_SR_TXCOMP;
228	WR4(sc, TWI_IDR, status);
229	wakeup(sc);
230	AT91_TWI_UNLOCK(sc);
231	return;
232}
233
234static int
235at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit)
236{
237	int err = 0;
238	int counter = 100000;
239	uint32_t sr;
240
241	AT91_TWI_ASSERT_LOCKED(sc);
242	while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0 &&
243	    !(sr & TWI_SR_NACK))
244		continue;
245	if (counter <= 0)
246		err = EBUSY;
247	else if (sr & TWI_SR_NACK)
248		err = ENXIO;		// iic nack convention
249	return (err);
250}
251
252static int
253at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
254{
255	struct at91_twi_softc *sc;
256	int clk;
257
258	sc = device_get_softc(dev);
259	AT91_TWI_LOCK(sc);
260	if (oldaddr)
261		*oldaddr = sc->twi_addr;
262	sc->twi_addr = addr;
263
264	/*
265	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
266	 */
267	switch (speed) {
268	case IIC_SLOW:
269		clk = TWI_SLOW_CLOCK;
270		break;
271
272	case IIC_FAST:
273		clk = TWI_FAST_CLOCK;
274		break;
275
276	case IIC_UNKNOWN:
277	case IIC_FASTEST:
278	default:
279		clk = TWI_FASTEST_CLOCK;
280		break;
281	}
282	sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) |
283	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk));
284	WR4(sc, TWI_CR, TWI_CR_SWRST);
285	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
286	WR4(sc, TWI_CWGR, sc->cwgr);
287	AT91_TWI_UNLOCK(sc);
288
289	return 0;
290}
291
292static int
293at91_twi_callback(device_t dev, int index, caddr_t data)
294{
295	int error = 0;
296
297	switch (index) {
298	case IIC_REQUEST_BUS:
299		break;
300
301	case IIC_RELEASE_BUS:
302		break;
303
304	default:
305		error = EINVAL;
306	}
307
308	return (error);
309}
310
311static int
312at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
313{
314	struct at91_twi_softc *sc;
315	int i, len, err;
316	uint32_t rdwr;
317	uint8_t *buf;
318	uint32_t sr;
319
320	sc = device_get_softc(dev);
321	err = 0;
322	AT91_TWI_LOCK(sc);
323	for (i = 0; i < nmsgs; i++) {
324		/*
325		 * The linux atmel driver doesn't use the internal device
326		 * address feature of twi.  A separate i2c message needs to
327		 * be written to use this.
328		 * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
329		 * for details.  Upon reflection, we could use this as an
330		 * optimization, but it is unclear the code bloat will
331		 * result in faster/better operations.
332		 */
333		rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0;
334		WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr);
335		len = msgs[i].len;
336		buf = msgs[i].buf;
337		/* zero byte transfers aren't allowed */
338		if (len == 0 || buf == NULL) {
339			err = EINVAL;
340			goto out;
341		}
342		if (len == 1 && msgs[i].flags & IIC_M_RD)
343			WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP);
344		else
345			WR4(sc, TWI_CR, TWI_CR_START);
346		if (msgs[i].flags & IIC_M_RD) {
347			sr = RD4(sc, TWI_SR);
348			while (!(sr & TWI_SR_TXCOMP)) {
349				if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) {
350					len--;
351					*buf++ = RD4(sc, TWI_RHR) & 0xff;
352					if (len == 1)
353						WR4(sc, TWI_CR, TWI_CR_STOP);
354				}
355			}
356			if (len > 0 || (sr & TWI_SR_NACK)) {
357				err = ENXIO;		// iic nack convention
358				goto out;
359			}
360		} else {
361			while (len--) {
362				if ((err = at91_twi_wait(sc, TWI_SR_TXRDY)))
363					goto out;
364				WR4(sc, TWI_THR, *buf++);
365			}
366			WR4(sc, TWI_CR, TWI_CR_STOP);
367		}
368		if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP)))
369			break;
370	}
371out:
372	if (err) {
373		WR4(sc, TWI_CR, TWI_CR_SWRST);
374		WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
375		WR4(sc, TWI_CWGR, sc->cwgr);
376	}
377	AT91_TWI_UNLOCK(sc);
378	return (err);
379}
380
381static device_method_t at91_twi_methods[] = {
382	/* Device interface */
383	DEVMETHOD(device_probe,		at91_twi_probe),
384	DEVMETHOD(device_attach,	at91_twi_attach),
385	DEVMETHOD(device_detach,	at91_twi_detach),
386
387	/* iicbus interface */
388	DEVMETHOD(iicbus_callback,	at91_twi_callback),
389	DEVMETHOD(iicbus_reset,		at91_twi_rst_card),
390	DEVMETHOD(iicbus_transfer,	at91_twi_transfer),
391	DEVMETHOD_END
392};
393
394static driver_t at91_twi_driver = {
395	"at91_twi",
396	at91_twi_methods,
397	sizeof(struct at91_twi_softc),
398};
399
400DRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, NULL,
401    NULL);
402DRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, NULL, NULL);
403MODULE_DEPEND(at91_twi, iicbus, 1, 1, 1);
404