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