1155324Simp/*-
2155324Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3155324Simp *
4155324Simp * Redistribution and use in source and binary forms, with or without
5155324Simp * modification, are permitted provided that the following conditions
6155324Simp * are met:
7155324Simp * 1. Redistributions of source code must retain the above copyright
8155324Simp *    notice, this list of conditions and the following disclaimer.
9155324Simp * 2. Redistributions in binary form must reproduce the above copyright
10155324Simp *    notice, this list of conditions and the following disclaimer in the
11155324Simp *    documentation and/or other materials provided with the distribution.
12155324Simp *
13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23185265Simp * SUCH DAMAGE.
24155324Simp */
25155324Simp
26155324Simp#include <sys/cdefs.h>
27155324Simp__FBSDID("$FreeBSD$");
28155324Simp
29155324Simp#include <sys/param.h>
30155324Simp#include <sys/systm.h>
31155324Simp#include <sys/bus.h>
32155324Simp#include <sys/conf.h>
33155324Simp#include <sys/kernel.h>
34155324Simp#include <sys/lock.h>
35155324Simp#include <sys/mbuf.h>
36155324Simp#include <sys/malloc.h>
37155324Simp#include <sys/module.h>
38155324Simp#include <sys/mutex.h>
39155324Simp#include <sys/rman.h>
40155324Simp#include <machine/bus.h>
41155324Simp
42155324Simp#include <arm/at91/at91_twireg.h>
43187601Simp#include <arm/at91/at91var.h>
44155324Simp
45157563Simp#include <dev/iicbus/iiconf.h>
46157563Simp#include <dev/iicbus/iicbus.h>
47157563Simp#include "iicbus_if.h"
48157563Simp
49236080Smarius#define	TWI_SLOW_CLOCK		 1500
50236080Smarius#define	TWI_FAST_CLOCK		45000
51236080Smarius#define	TWI_FASTEST_CLOCK	90000
52167852Simp
53155324Simpstruct at91_twi_softc
54155324Simp{
55155324Simp	device_t dev;			/* Myself */
56155324Simp	void *intrhand;			/* Interrupt handle */
57155324Simp	struct resource *irq_res;	/* IRQ resource */
58155324Simp	struct resource	*mem_res;	/* Memory resource */
59155324Simp	struct mtx sc_mtx;		/* basically a perimeter lock */
60164744Simp	volatile uint32_t flags;
61155324Simp	uint32_t cwgr;
62157563Simp	int	sc_started;
63157563Simp	int	twi_addr;
64157563Simp	device_t iicbus;
65155324Simp};
66155324Simp
67155324Simpstatic inline uint32_t
68155324SimpRD4(struct at91_twi_softc *sc, bus_size_t off)
69155324Simp{
70236080Smarius
71155324Simp	return bus_read_4(sc->mem_res, off);
72155324Simp}
73155324Simp
74155324Simpstatic inline void
75155324SimpWR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val)
76155324Simp{
77236080Smarius
78155324Simp	bus_write_4(sc->mem_res, off, val);
79155324Simp}
80155324Simp
81236080Smarius#define	AT91_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
82155324Simp#define	AT91_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
83236080Smarius#define	AT91_TWI_LOCK_INIT(_sc) \
84155324Simp	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
85155324Simp	    "twi", MTX_DEF)
86236080Smarius#define	AT91_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
87236080Smarius#define	AT91_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
88236080Smarius#define	AT91_TWI_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
89236080Smarius#define	TWI_DEF_CLK	100000
90155324Simp
91155324Simpstatic devclass_t at91_twi_devclass;
92155324Simp
93155324Simp/* bus entry points */
94155324Simp
95155324Simpstatic int at91_twi_probe(device_t dev);
96155324Simpstatic int at91_twi_attach(device_t dev);
97155324Simpstatic int at91_twi_detach(device_t dev);
98155324Simpstatic void at91_twi_intr(void *);
99155324Simp
100155324Simp/* helper routines */
101155324Simpstatic int at91_twi_activate(device_t dev);
102155324Simpstatic void at91_twi_deactivate(device_t dev);
103155324Simp
104155324Simpstatic int
105155324Simpat91_twi_probe(device_t dev)
106155324Simp{
107236080Smarius
108155324Simp	device_set_desc(dev, "TWI");
109155324Simp	return (0);
110155324Simp}
111155324Simp
112155324Simpstatic int
113155324Simpat91_twi_attach(device_t dev)
114155324Simp{
115155324Simp	struct at91_twi_softc *sc = device_get_softc(dev);
116155324Simp	int err;
117155324Simp
118155324Simp	sc->dev = dev;
119155324Simp	err = at91_twi_activate(dev);
120155324Simp	if (err)
121155324Simp		goto out;
122155324Simp
123155324Simp	AT91_TWI_LOCK_INIT(sc);
124155324Simp
125155324Simp	/*
126155324Simp	 * Activate the interrupt
127155324Simp	 */
128155324Simp	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
129166901Spiso	    NULL, at91_twi_intr, sc, &sc->intrhand);
130155324Simp	if (err) {
131155324Simp		AT91_TWI_LOCK_DESTROY(sc);
132155324Simp		goto out;
133155324Simp	}
134187601Simp	sc->cwgr = TWI_CWGR_CKDIV(8 * at91_master_clock / TWI_FASTEST_CLOCK) |
135155324Simp	    TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) |
136155324Simp	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK));
137155324Simp	WR4(sc, TWI_CR, TWI_CR_SWRST);
138155324Simp	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
139155324Simp	WR4(sc, TWI_CWGR, sc->cwgr);
140157563Simp
141157563Simp	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
142157563Simp		device_printf(dev, "could not allocate iicbus instance\n");
143157563Simp	/* probe and attach the iicbus */
144157563Simp	bus_generic_attach(dev);
145237093Smariusout:
146155324Simp	if (err)
147155324Simp		at91_twi_deactivate(dev);
148155324Simp	return (err);
149155324Simp}
150155324Simp
151155324Simpstatic int
152155324Simpat91_twi_detach(device_t dev)
153155324Simp{
154157563Simp	struct at91_twi_softc *sc;
155157563Simp	int rv;
156157563Simp
157157563Simp	sc = device_get_softc(dev);
158157563Simp	at91_twi_deactivate(dev);
159157563Simp	if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0)
160157563Simp		return (rv);
161157563Simp
162181303Sjhb	AT91_TWI_LOCK_DESTROY(sc);
163181303Sjhb
164157563Simp	return (0);
165155324Simp}
166155324Simp
167155324Simpstatic int
168155324Simpat91_twi_activate(device_t dev)
169155324Simp{
170155324Simp	struct at91_twi_softc *sc;
171155324Simp	int rid;
172155324Simp
173155324Simp	sc = device_get_softc(dev);
174155324Simp	rid = 0;
175155324Simp	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
176155324Simp	    RF_ACTIVE);
177155324Simp	if (sc->mem_res == NULL)
178155324Simp		goto errout;
179155324Simp	rid = 0;
180155324Simp	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
181155324Simp	    RF_ACTIVE);
182163523Simp	if (sc->irq_res == NULL)
183155324Simp		goto errout;
184155324Simp	return (0);
185155324Simperrout:
186155324Simp	at91_twi_deactivate(dev);
187155324Simp	return (ENOMEM);
188155324Simp}
189155324Simp
190155324Simpstatic void
191155324Simpat91_twi_deactivate(device_t dev)
192155324Simp{
193155324Simp	struct at91_twi_softc *sc;
194155324Simp
195155324Simp	sc = device_get_softc(dev);
196155324Simp	if (sc->intrhand)
197155324Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
198155324Simp	sc->intrhand = 0;
199155324Simp	bus_generic_detach(sc->dev);
200155324Simp	if (sc->mem_res)
201179375Simp		bus_release_resource(dev, SYS_RES_MEMORY,
202155324Simp		    rman_get_rid(sc->mem_res), sc->mem_res);
203155324Simp	sc->mem_res = 0;
204155324Simp	if (sc->irq_res)
205155324Simp		bus_release_resource(dev, SYS_RES_IRQ,
206155324Simp		    rman_get_rid(sc->irq_res), sc->irq_res);
207155324Simp	sc->irq_res = 0;
208155324Simp	return;
209155324Simp}
210155324Simp
211155324Simpstatic void
212155324Simpat91_twi_intr(void *xsc)
213155324Simp{
214155324Simp	struct at91_twi_softc *sc = xsc;
215155324Simp	uint32_t status;
216155324Simp
217155324Simp	status = RD4(sc, TWI_SR);
218155324Simp	if (status == 0)
219155324Simp		return;
220181303Sjhb	AT91_TWI_LOCK(sc);
221164744Simp	sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK);
222155324Simp	if (status & TWI_SR_RXRDY)
223164744Simp		sc->flags |= TWI_SR_RXRDY;
224155324Simp	if (status & TWI_SR_TXRDY)
225164744Simp		sc->flags |= TWI_SR_TXRDY;
226164744Simp	if (status & TWI_SR_TXCOMP)
227164744Simp		sc->flags |= TWI_SR_TXCOMP;
228164744Simp	WR4(sc, TWI_IDR, status);
229155324Simp	wakeup(sc);
230181303Sjhb	AT91_TWI_UNLOCK(sc);
231155324Simp	return;
232155324Simp}
233155324Simp
234157563Simpstatic int
235162130Simpat91_twi_wait(struct at91_twi_softc *sc, uint32_t bit)
236155324Simp{
237157563Simp	int err = 0;
238164503Simp	int counter = 100000;
239164744Simp	uint32_t sr;
240157563Simp
241181303Sjhb	AT91_TWI_ASSERT_LOCKED(sc);
242167852Simp	while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0 &&
243167852Simp	    !(sr & TWI_SR_NACK))
244164503Simp		continue;
245163523Simp	if (counter <= 0)
246164744Simp		err = EBUSY;
247164744Simp	else if (sr & TWI_SR_NACK)
248167852Simp		err = ENXIO;		// iic nack convention
249157563Simp	return (err);
250157563Simp}
251157563Simp
252157563Simpstatic int
253157563Simpat91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
254157563Simp{
255157563Simp	struct at91_twi_softc *sc;
256164744Simp	int clk;
257157563Simp
258157563Simp	sc = device_get_softc(dev);
259181303Sjhb	AT91_TWI_LOCK(sc);
260157563Simp	if (oldaddr)
261157563Simp		*oldaddr = sc->twi_addr;
262164744Simp	sc->twi_addr = addr;
263157563Simp
264157563Simp	/*
265157563Simp	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
266157563Simp	 */
267157563Simp	switch (speed) {
268157563Simp	case IIC_SLOW:
269167852Simp		clk = TWI_SLOW_CLOCK;
270155324Simp		break;
271157563Simp
272157563Simp	case IIC_FAST:
273167852Simp		clk = TWI_FAST_CLOCK;
274157563Simp		break;
275157563Simp
276157563Simp	case IIC_UNKNOWN:
277157563Simp	case IIC_FASTEST:
278157563Simp	default:
279167852Simp		clk = TWI_FASTEST_CLOCK;
280157563Simp		break;
281155324Simp	}
282164744Simp	sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) |
283164744Simp	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk));
284157563Simp	WR4(sc, TWI_CR, TWI_CR_SWRST);
285157563Simp	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
286157563Simp	WR4(sc, TWI_CWGR, sc->cwgr);
287164744Simp	printf("setting cwgr to %#x\n", sc->cwgr);
288181303Sjhb	AT91_TWI_UNLOCK(sc);
289155324Simp
290164503Simp	return 0;
291157563Simp}
292157563Simp
293157563Simpstatic int
294194015Savgat91_twi_callback(device_t dev, int index, caddr_t data)
295157563Simp{
296157563Simp	int error = 0;
297157563Simp
298157563Simp	switch (index) {
299157563Simp	case IIC_REQUEST_BUS:
300155324Simp		break;
301157563Simp
302157563Simp	case IIC_RELEASE_BUS:
303157563Simp		break;
304157563Simp
305155324Simp	default:
306157563Simp		error = EINVAL;
307155324Simp	}
308157563Simp
309157563Simp	return (error);
310155324Simp}
311155324Simp
312162130Simpstatic int
313162130Simpat91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
314162130Simp{
315162130Simp	struct at91_twi_softc *sc;
316164744Simp	int i, len, err;
317162130Simp	uint32_t rdwr;
318162130Simp	uint8_t *buf;
319167852Simp	uint32_t sr;
320162130Simp
321162130Simp	sc = device_get_softc(dev);
322164744Simp	err = 0;
323164744Simp	AT91_TWI_LOCK(sc);
324162130Simp	for (i = 0; i < nmsgs; i++) {
325162130Simp		/*
326162130Simp		 * The linux atmel driver doesn't use the internal device
327162130Simp		 * address feature of twi.  A separate i2c message needs to
328162130Simp		 * be written to use this.
329162130Simp		 * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
330164744Simp		 * for details.  Upon reflection, we could use this as an
331164744Simp		 * optimization, but it is unclear the code bloat will
332164744Simp		 * result in faster/better operations.
333162130Simp		 */
334162130Simp		rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0;
335162130Simp		WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr);
336162130Simp		len = msgs[i].len;
337162130Simp		buf = msgs[i].buf;
338167852Simp		/* zero byte transfers aren't allowed */
339167852Simp		if (len == 0 || buf == NULL) {
340167852Simp			err = EINVAL;
341167852Simp			goto out;
342167852Simp		}
343213203Sticso		if (len == 1 && msgs[i].flags & IIC_M_RD)
344167852Simp			WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP);
345167852Simp		else
346167852Simp			WR4(sc, TWI_CR, TWI_CR_START);
347162130Simp		if (msgs[i].flags & IIC_M_RD) {
348167852Simp			sr = RD4(sc, TWI_SR);
349167852Simp			while (!(sr & TWI_SR_TXCOMP)) {
350167852Simp				if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) {
351167852Simp					len--;
352167852Simp					*buf++ = RD4(sc, TWI_RHR) & 0xff;
353213201Sticso					if (len == 1)
354167852Simp						WR4(sc, TWI_CR, TWI_CR_STOP);
355167852Simp				}
356162130Simp			}
357167852Simp			if (len > 0 || (sr & TWI_SR_NACK)) {
358167852Simp				err = ENXIO;		// iic nack convention
359167852Simp				goto out;
360167852Simp			}
361162130Simp		} else {
362162130Simp			while (len--) {
363167852Simp				if ((err = at91_twi_wait(sc, TWI_SR_TXRDY)))
364164744Simp					goto out;
365167852Simp				WR4(sc, TWI_THR, *buf++);
366162130Simp			}
367236082Smarius			WR4(sc, TWI_CR, TWI_CR_STOP);
368162130Simp		}
369164744Simp		if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP)))
370164744Simp			break;
371162130Simp	}
372237093Smariusout:
373164744Simp	if (err) {
374167852Simp		WR4(sc, TWI_CR, TWI_CR_SWRST);
375167852Simp		WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
376167852Simp		WR4(sc, TWI_CWGR, sc->cwgr);
377164744Simp	}
378164744Simp	AT91_TWI_UNLOCK(sc);
379164744Simp	return (err);
380162130Simp}
381162130Simp
382155324Simpstatic device_method_t at91_twi_methods[] = {
383155324Simp	/* Device interface */
384155324Simp	DEVMETHOD(device_probe,		at91_twi_probe),
385155324Simp	DEVMETHOD(device_attach,	at91_twi_attach),
386155324Simp	DEVMETHOD(device_detach,	at91_twi_detach),
387155324Simp
388157563Simp	/* iicbus interface */
389157563Simp	DEVMETHOD(iicbus_callback,	at91_twi_callback),
390157563Simp	DEVMETHOD(iicbus_reset,		at91_twi_rst_card),
391162130Simp	DEVMETHOD(iicbus_transfer,	at91_twi_transfer),
392236080Smarius	DEVMETHOD_END
393155324Simp};
394155324Simp
395155324Simpstatic driver_t at91_twi_driver = {
396155324Simp	"at91_twi",
397155324Simp	at91_twi_methods,
398155324Simp	sizeof(struct at91_twi_softc),
399155324Simp};
400155324Simp
401236080SmariusDRIVER_MODULE(at91_twi, atmelarm, at91_twi_driver, at91_twi_devclass, NULL,
402236080Smarius    NULL);
403236080SmariusDRIVER_MODULE(iicbus, at91_twi, iicbus_driver, iicbus_devclass, NULL, NULL);
404166532SimpMODULE_DEPEND(at91_twi, iicbus, 1, 1, 1);
405