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