at91_twi.c revision 234293
1204076Spjd/*-
2204076Spjd * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3211877Spjd *
4204076Spjd * Redistribution and use in source and binary forms, with or without
5204076Spjd * modification, are permitted provided that the following conditions
6204076Spjd * are met:
7204076Spjd * 1. Redistributions of source code must retain the above copyright
8204076Spjd *    notice, this list of conditions and the following disclaimer.
9204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
10204076Spjd *    notice, this list of conditions and the following disclaimer in the
11204076Spjd *    documentation and/or other materials provided with the distribution.
12204076Spjd *
13204076Spjd * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23204076Spjd * SUCH DAMAGE.
24204076Spjd */
25204076Spjd
26204076Spjd#include <sys/cdefs.h>
27204076Spjd__FBSDID("$FreeBSD: head/sys/arm/at91/at91_twi.c 234293 2012-04-14 17:27:34Z marius $");
28204076Spjd
29204076Spjd#include <sys/param.h>
30204076Spjd#include <sys/systm.h>
31204076Spjd#include <sys/bus.h>
32204076Spjd#include <sys/conf.h>
33204076Spjd#include <sys/kernel.h>
34204076Spjd#include <sys/lock.h>
35204076Spjd#include <sys/mbuf.h>
36204076Spjd#include <sys/malloc.h>
37204076Spjd#include <sys/module.h>
38204076Spjd#include <sys/mutex.h>
39204076Spjd#include <sys/rman.h>
40204076Spjd#include <machine/bus.h>
41204076Spjd
42204076Spjd#include <arm/at91/at91_twireg.h>
43204076Spjd#include <arm/at91/at91var.h>
44204076Spjd
45213009Spjd#include <dev/iicbus/iiconf.h>
46204076Spjd#include <dev/iicbus/iicbus.h>
47204076Spjd#include "iicbus_if.h"
48204076Spjd
49204076Spjd#define	TWI_SLOW_CLOCK		 1500
50204076Spjd#define	TWI_FAST_CLOCK		45000
51204076Spjd#define	TWI_FASTEST_CLOCK	90000
52204076Spjd
53204076Spjdstruct at91_twi_softc
54204076Spjd{
55204076Spjd	device_t dev;			/* Myself */
56204076Spjd	void *intrhand;			/* Interrupt handle */
57212038Spjd	struct resource *irq_res;	/* IRQ resource */
58204076Spjd	struct resource	*mem_res;	/* Memory resource */
59204076Spjd	struct mtx sc_mtx;		/* basically a perimeter lock */
60204076Spjd	volatile uint32_t flags;
61211977Spjd	uint32_t cwgr;
62204076Spjd	int	sc_started;
63204076Spjd	int	twi_addr;
64204076Spjd	device_t iicbus;
65204076Spjd};
66204076Spjd
67204076Spjdstatic inline uint32_t
68219864SpjdRD4(struct at91_twi_softc *sc, bus_size_t off)
69219864Spjd{
70204076Spjd
71204076Spjd	return bus_read_4(sc->mem_res, off);
72204076Spjd}
73204076Spjd
74204076Spjdstatic inline void
75204076SpjdWR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val)
76204076Spjd{
77204076Spjd
78211984Spjd	bus_write_4(sc->mem_res, off, val);
79211984Spjd}
80204076Spjd
81204076Spjd#define	AT91_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
82204076Spjd#define	AT91_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
83204076Spjd#define	AT91_TWI_LOCK_INIT(_sc) \
84204076Spjd	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
85204076Spjd	    "twi", MTX_DEF)
86204076Spjd#define	AT91_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
87204076Spjd#define	AT91_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
88204076Spjd#define	AT91_TWI_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
89204076Spjd#define	TWI_DEF_CLK	100000
90204076Spjd
91204076Spjdstatic devclass_t at91_twi_devclass;
92204076Spjd
93204076Spjd/* bus entry points */
94204076Spjd
95204076Spjdstatic int at91_twi_probe(device_t dev);
96204076Spjdstatic int at91_twi_attach(device_t dev);
97204076Spjdstatic int at91_twi_detach(device_t dev);
98204076Spjdstatic void at91_twi_intr(void *);
99204076Spjd
100204076Spjd/* helper routines */
101204076Spjdstatic int at91_twi_activate(device_t dev);
102204076Spjdstatic void at91_twi_deactivate(device_t dev);
103204076Spjd
104204076Spjdstatic int
105204076Spjdat91_twi_probe(device_t dev)
106204076Spjd{
107204076Spjd
108204076Spjd	device_set_desc(dev, "TWI");
109204076Spjd	return (0);
110211877Spjd}
111211877Spjd
112211877Spjdstatic int
113211877Spjdat91_twi_attach(device_t dev)
114211877Spjd{
115211877Spjd	struct at91_twi_softc *sc = device_get_softc(dev);
116211877Spjd	int err;
117211877Spjd
118211877Spjd	sc->dev = dev;
119211877Spjd	err = at91_twi_activate(dev);
120211877Spjd	if (err)
121211877Spjd		goto out;
122211877Spjd
123211877Spjd	AT91_TWI_LOCK_INIT(sc);
124211877Spjd
125211877Spjd	/*
126211877Spjd	 * Activate the interrupt
127211877Spjd	 */
128211877Spjd	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
129211877Spjd	    NULL, at91_twi_intr, sc, &sc->intrhand);
130204076Spjd	if (err) {
131204076Spjd		AT91_TWI_LOCK_DESTROY(sc);
132204076Spjd		goto out;
133204076Spjd	}
134204076Spjd	sc->cwgr = TWI_CWGR_CKDIV(8 * at91_master_clock / TWI_FASTEST_CLOCK) |
135204076Spjd	    TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) |
136204076Spjd	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK));
137204076Spjd	WR4(sc, TWI_CR, TWI_CR_SWRST);
138204076Spjd	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
139204076Spjd	WR4(sc, TWI_CWGR, sc->cwgr);
140204076Spjd
141204076Spjd	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
142204076Spjd		device_printf(dev, "could not allocate iicbus instance\n");
143204076Spjd	/* probe and attach the iicbus */
144204076Spjd	bus_generic_attach(dev);
145204076Spjdout:
146204076Spjd	if (err)
147204076Spjd		at91_twi_deactivate(dev);
148204076Spjd	return (err);
149204076Spjd}
150204076Spjd
151204076Spjdstatic int
152204076Spjdat91_twi_detach(device_t dev)
153204076Spjd{
154204076Spjd	struct at91_twi_softc *sc;
155210879Spjd	int rv;
156210879Spjd
157210879Spjd	sc = device_get_softc(dev);
158204076Spjd	at91_twi_deactivate(dev);
159204076Spjd	if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0)
160204076Spjd		return (rv);
161204076Spjd
162210879Spjd	AT91_TWI_LOCK_DESTROY(sc);
163210879Spjd
164210879Spjd	return (0);
165204076Spjd}
166204076Spjd
167204076Spjdstatic int
168204076Spjdat91_twi_activate(device_t dev)
169204076Spjd{
170204076Spjd	struct at91_twi_softc *sc;
171204076Spjd	int rid;
172204076Spjd
173204076Spjd	sc = device_get_softc(dev);
174204076Spjd	rid = 0;
175204076Spjd	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
176204076Spjd	    RF_ACTIVE);
177204076Spjd	if (sc->mem_res == NULL)
178204076Spjd		goto errout;
179204076Spjd	rid = 0;
180204076Spjd	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
181204076Spjd	    RF_ACTIVE);
182204076Spjd	if (sc->irq_res == NULL)
183204076Spjd		goto errout;
184204076Spjd	return (0);
185204076Spjderrout:
186223181Strociny	at91_twi_deactivate(dev);
187220271Spjd	return (ENOMEM);
188220271Spjd}
189220271Spjd
190223181Strocinystatic void
191220271Spjdat91_twi_deactivate(device_t dev)
192204076Spjd{
193204076Spjd	struct at91_twi_softc *sc;
194204076Spjd
195204076Spjd	sc = device_get_softc(dev);
196204076Spjd	if (sc->intrhand)
197204076Spjd		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
198204076Spjd	sc->intrhand = 0;
199204076Spjd	bus_generic_detach(sc->dev);
200204076Spjd	if (sc->mem_res)
201204076Spjd		bus_release_resource(dev, SYS_RES_MEMORY,
202204076Spjd		    rman_get_rid(sc->mem_res), sc->mem_res);
203204076Spjd	sc->mem_res = 0;
204204076Spjd	if (sc->irq_res)
205204076Spjd		bus_release_resource(dev, SYS_RES_IRQ,
206204076Spjd		    rman_get_rid(sc->irq_res), sc->irq_res);
207204076Spjd	sc->irq_res = 0;
208204076Spjd	return;
209204076Spjd}
210204076Spjd
211204076Spjdstatic void
212204076Spjdat91_twi_intr(void *xsc)
213204076Spjd{
214204076Spjd	struct at91_twi_softc *sc = xsc;
215204076Spjd	uint32_t status;
216204076Spjd
217204076Spjd	status = RD4(sc, TWI_SR);
218204076Spjd	if (status == 0)
219204076Spjd		return;
220204076Spjd	AT91_TWI_LOCK(sc);
221204076Spjd	sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK);
222204076Spjd	if (status & TWI_SR_RXRDY)
223204076Spjd		sc->flags |= TWI_SR_RXRDY;
224204076Spjd	if (status & TWI_SR_TXRDY)
225204076Spjd		sc->flags |= TWI_SR_TXRDY;
226204076Spjd	if (status & TWI_SR_TXCOMP)
227204076Spjd		sc->flags |= TWI_SR_TXCOMP;
228204076Spjd	WR4(sc, TWI_IDR, status);
229204076Spjd	wakeup(sc);
230204076Spjd	AT91_TWI_UNLOCK(sc);
231204076Spjd	return;
232204076Spjd}
233204076Spjd
234204076Spjdstatic int
235204076Spjdat91_twi_wait(struct at91_twi_softc *sc, uint32_t bit)
236204076Spjd{
237204076Spjd	int err = 0;
238204076Spjd	int counter = 100000;
239204076Spjd	uint32_t sr;
240204076Spjd
241204076Spjd	AT91_TWI_ASSERT_LOCKED(sc);
242204076Spjd	while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0 &&
243204076Spjd	    !(sr & TWI_SR_NACK))
244204076Spjd		continue;
245204076Spjd	if (counter <= 0)
246204076Spjd		err = EBUSY;
247204076Spjd	else if (sr & TWI_SR_NACK)
248204076Spjd		err = ENXIO;		// iic nack convention
249204076Spjd	return (err);
250214284Spjd}
251214284Spjd
252214284Spjdstatic int
253214284Spjdat91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
254204076Spjd{
255218138Spjd	struct at91_twi_softc *sc;
256204076Spjd	int clk;
257204076Spjd
258204076Spjd	sc = device_get_softc(dev);
259214284Spjd	AT91_TWI_LOCK(sc);
260214284Spjd	if (oldaddr)
261214284Spjd		*oldaddr = sc->twi_addr;
262214284Spjd	sc->twi_addr = addr;
263214284Spjd
264214284Spjd	/*
265214284Spjd	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
266220865Spjd	 */
267204076Spjd	switch (speed) {
268219830Spjd	case IIC_SLOW:
269219830Spjd		clk = TWI_SLOW_CLOCK;
270219830Spjd		break;
271219830Spjd
272219830Spjd	case IIC_FAST:
273219830Spjd		clk = TWI_FAST_CLOCK;
274219830Spjd		break;
275219830Spjd
276219830Spjd	case IIC_UNKNOWN:
277219830Spjd	case IIC_FASTEST:
278219830Spjd	default:
279219830Spjd		clk = TWI_FASTEST_CLOCK;
280219831Spjd		break;
281219830Spjd	}
282204076Spjd	sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) |
283204076Spjd	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk));
284204076Spjd	WR4(sc, TWI_CR, TWI_CR_SWRST);
285204076Spjd	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
286219843Spjd	WR4(sc, TWI_CWGR, sc->cwgr);
287204076Spjd	printf("setting cwgr to %#x\n", sc->cwgr);
288204076Spjd	AT91_TWI_UNLOCK(sc);
289204076Spjd
290204076Spjd	return 0;
291204076Spjd}
292204076Spjd
293204076Spjdstatic int
294204076Spjdat91_twi_callback(device_t dev, int index, caddr_t data)
295204076Spjd{
296204076Spjd	int error = 0;
297204076Spjd
298204076Spjd	switch (index) {
299204076Spjd	case IIC_REQUEST_BUS:
300204076Spjd		break;
301204076Spjd
302204076Spjd	case IIC_RELEASE_BUS:
303204076Spjd		break;
304204076Spjd
305204076Spjd	default:
306204076Spjd		error = EINVAL;
307204076Spjd	}
308204076Spjd
309204076Spjd	return (error);
310204076Spjd}
311204076Spjd
312204076Spjdstatic int
313204076Spjdat91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
314204076Spjd{
315204076Spjd	struct at91_twi_softc *sc;
316204076Spjd	int i, len, err;
317204076Spjd	uint32_t rdwr;
318204076Spjd	uint8_t *buf;
319204076Spjd	uint32_t sr;
320204076Spjd
321204076Spjd	sc = device_get_softc(dev);
322204076Spjd	err = 0;
323204076Spjd	AT91_TWI_LOCK(sc);
324204076Spjd	for (i = 0; i < nmsgs; i++) {
325204076Spjd		/*
326204076Spjd		 * The linux atmel driver doesn't use the internal device
327204076Spjd		 * address feature of twi.  A separate i2c message needs to
328204076Spjd		 * be written to use this.
329218138Spjd		 * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
330204076Spjd		 * for details.  Upon reflection, we could use this as an
331204076Spjd		 * optimization, but it is unclear the code bloat will
332204076Spjd		 * result in faster/better operations.
333204076Spjd		 */
334204076Spjd		rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0;
335204076Spjd		WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr);
336204076Spjd		len = msgs[i].len;
337204076Spjd		buf = msgs[i].buf;
338204076Spjd		/* zero byte transfers aren't allowed */
339204076Spjd		if (len == 0 || buf == NULL) {
340204076Spjd			err = EINVAL;
341204076Spjd			goto out;
342204076Spjd		}
343204076Spjd		if (len == 1 && msgs[i].flags & IIC_M_RD)
344204076Spjd			WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP);
345204076Spjd		else
346204076Spjd			WR4(sc, TWI_CR, TWI_CR_START);
347204076Spjd		if (msgs[i].flags & IIC_M_RD) {
348220007Spjd			sr = RD4(sc, TWI_SR);
349204076Spjd			while (!(sr & TWI_SR_TXCOMP)) {
350214276Spjd				if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) {
351204076Spjd					len--;
352204076Spjd					*buf++ = RD4(sc, TWI_RHR) & 0xff;
353214275Spjd					if (len == 1)
354214275Spjd						WR4(sc, TWI_CR, TWI_CR_STOP);
355209182Spjd				}
356223181Strociny			}
357220271Spjd			if (len > 0 || (sr & TWI_SR_NACK)) {
358220271Spjd				err = ENXIO;		// iic nack convention
359220271Spjd				goto out;
360223181Strociny			}
361204076Spjd		} else {
362204076Spjd			while (len--) {
363204076Spjd				if ((err = at91_twi_wait(sc, TWI_SR_TXRDY)))
364212038Spjd					goto out;
365204076Spjd				WR4(sc, TWI_THR, *buf++);
366204076Spjd			}
367204076Spjd			WR4(sc, TWI_CR, TWI_CR_STOP);
368204076Spjd		}
369204076Spjd		if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP)))
370204076Spjd			break;
371204076Spjd	}
372213009Spjdout:
373204076Spjd	if (err) {
374204076Spjd		WR4(sc, TWI_CR, TWI_CR_SWRST);
375219482Strociny		WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
376204076Spjd		WR4(sc, TWI_CWGR, sc->cwgr);
377204076Spjd	}
378204076Spjd	AT91_TWI_UNLOCK(sc);
379204076Spjd	return (err);
380219818Spjd}
381204076Spjd
382204076Spjdstatic device_method_t at91_twi_methods[] = {
383204076Spjd	/* Device interface */
384204076Spjd	DEVMETHOD(device_probe,		at91_twi_probe),
385212038Spjd	DEVMETHOD(device_attach,	at91_twi_attach),
386212038Spjd	DEVMETHOD(device_detach,	at91_twi_detach),
387212038Spjd
388219818Spjd	/* iicbus interface */
389212038Spjd	DEVMETHOD(iicbus_callback,	at91_twi_callback),
390212038Spjd	DEVMETHOD(iicbus_reset,		at91_twi_rst_card),
391212038Spjd	DEVMETHOD(iicbus_transfer,	at91_twi_transfer),
392212038Spjd	DEVMETHOD_END
393204076Spjd};
394204076Spjd
395204076Spjdstatic driver_t at91_twi_driver = {
396204076Spjd	"at91_twi",
397204076Spjd	at91_twi_methods,
398204076Spjd	sizeof(struct at91_twi_softc),
399204076Spjd};
400204076Spjd
401204076SpjdDRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, NULL,
402204076Spjd    NULL);
403204076SpjdDRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, NULL, NULL);
404204076SpjdMODULE_DEPEND(at91_twi, iicbus, 1, 1, 1);
405204076Spjd