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