]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/at91/at91_spi.c
This commit was generated by cvs2svn to compensate for changes in r156803,
[FreeBSD/FreeBSD.git] / sys / arm / at91 / at91_spi.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 THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/conf.h>
32 #include <sys/kernel.h>
33 #include <sys/lock.h>
34 #include <sys/mbuf.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/rman.h>
39 #include <machine/bus.h>
40
41 #include <arm/at91/at91_spireg.h>
42 #include <arm/at91/at91_spiio.h>
43
44 struct at91_spi_softc
45 {
46         device_t dev;                   /* Myself */
47         void *intrhand;                 /* Interrupt handle */
48         struct resource *irq_res;       /* IRQ resource */
49         struct resource *mem_res;       /* Memory resource */
50         struct mtx sc_mtx;              /* basically a perimeter lock */
51         int flags;
52 #define XFER_PENDING    1               /* true when transfer taking place */
53 #define OPENED          2               /* Device opened */
54 #define RXRDY           4
55 #define TXCOMP          8
56 #define TXRDY           0x10
57         struct cdev *cdev;
58 };
59
60 static inline uint32_t
61 RD4(struct at91_spi_softc *sc, bus_size_t off)
62 {
63         return bus_read_4(sc->mem_res, off);
64 }
65
66 static inline void
67 WR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val)
68 {
69         bus_write_4(sc->mem_res, off, val);
70 }
71
72 #define AT91_SPI_LOCK(_sc)              mtx_lock(&(_sc)->sc_mtx)
73 #define AT91_SPI_UNLOCK(_sc)            mtx_unlock(&(_sc)->sc_mtx)
74 #define AT91_SPI_LOCK_INIT(_sc) \
75         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
76             "spi", MTX_DEF)
77 #define AT91_SPI_LOCK_DESTROY(_sc)      mtx_destroy(&_sc->sc_mtx);
78 #define AT91_SPI_ASSERT_LOCKED(_sc)     mtx_assert(&_sc->sc_mtx, MA_OWNED);
79 #define AT91_SPI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
80 #define CDEV2SOFTC(dev)         ((dev)->si_drv1)
81
82 static devclass_t at91_spi_devclass;
83
84 /* bus entry points */
85
86 static int at91_spi_probe(device_t dev);
87 static int at91_spi_attach(device_t dev);
88 static int at91_spi_detach(device_t dev);
89 static void at91_spi_intr(void *);
90
91 /* helper routines */
92 static int at91_spi_activate(device_t dev);
93 static void at91_spi_deactivate(device_t dev);
94
95 /* cdev routines */
96 static d_open_t at91_spi_open;
97 static d_close_t at91_spi_close;
98 static d_ioctl_t at91_spi_ioctl;
99
100 static struct cdevsw at91_spi_cdevsw =
101 {
102         .d_version = D_VERSION,
103         .d_open = at91_spi_open,
104         .d_close = at91_spi_close,
105         .d_ioctl = at91_spi_ioctl
106 };
107
108 static int
109 at91_spi_probe(device_t dev)
110 {
111         device_set_desc(dev, "SPI");
112         return (0);
113 }
114
115 static int
116 at91_spi_attach(device_t dev)
117 {
118         struct at91_spi_softc *sc = device_get_softc(dev);
119         int err;
120
121         sc->dev = dev;
122         err = at91_spi_activate(dev);
123         if (err)
124                 goto out;
125
126         AT91_SPI_LOCK_INIT(sc);
127
128         /*
129          * Activate the interrupt
130          */
131         err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
132             at91_spi_intr, sc, &sc->intrhand);
133         if (err) {
134                 AT91_SPI_LOCK_DESTROY(sc);
135                 goto out;
136         }
137         sc->cdev = make_dev(&at91_spi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
138             "spi%d", device_get_unit(dev));
139         if (sc->cdev == NULL) {
140                 err = ENOMEM;
141                 goto out;
142         }
143         sc->cdev->si_drv1 = sc;
144 #if 0
145         /* init */
146         sc->cwgr = SPI_CWGR_CKDIV(1) |
147             SPI_CWGR_CHDIV(SPI_CWGR_DIV(SPI_DEF_CLK)) |
148             SPI_CWGR_CLDIV(SPI_CWGR_DIV(SPI_DEF_CLK));
149
150         WR4(sc, SPI_CR, SPI_CR_SWRST);
151         WR4(sc, SPI_CR, SPI_CR_MSEN | SPI_CR_SVDIS);
152         WR4(sc, SPI_CWGR, sc->cwgr);
153 #endif
154 out:;
155         if (err)
156                 at91_spi_deactivate(dev);
157         return (err);
158 }
159
160 static int
161 at91_spi_detach(device_t dev)
162 {
163         return (EBUSY); /* XXX */
164 }
165
166 static int
167 at91_spi_activate(device_t dev)
168 {
169         struct at91_spi_softc *sc;
170         int rid;
171
172         sc = device_get_softc(dev);
173         rid = 0;
174         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
175             RF_ACTIVE);
176         if (sc->mem_res == NULL)
177                 goto errout;
178         rid = 0;
179         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
180             RF_ACTIVE);
181         if (sc->mem_res == NULL)
182                 goto errout;
183         return (0);
184 errout:
185         at91_spi_deactivate(dev);
186         return (ENOMEM);
187 }
188
189 static void
190 at91_spi_deactivate(device_t dev)
191 {
192         struct at91_spi_softc *sc;
193
194         sc = device_get_softc(dev);
195         if (sc->intrhand)
196                 bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
197         sc->intrhand = 0;
198         bus_generic_detach(sc->dev);
199         if (sc->mem_res)
200                 bus_release_resource(dev, SYS_RES_IOPORT,
201                     rman_get_rid(sc->mem_res), sc->mem_res);
202         sc->mem_res = 0;
203         if (sc->irq_res)
204                 bus_release_resource(dev, SYS_RES_IRQ,
205                     rman_get_rid(sc->irq_res), sc->irq_res);
206         sc->irq_res = 0;
207         return;
208 }
209
210 static void
211 at91_spi_intr(void *xsc)
212 {
213         struct at91_spi_softc *sc = xsc;
214 #if 0
215         uint32_t status;
216
217         /* Reading the status also clears the interrupt */
218         status = RD4(sc, SPI_SR);
219         if (status == 0)
220                 return;
221         AT91_SPI_LOCK(sc);
222         if (status & SPI_SR_RXRDY)
223                 sc->flags |= RXRDY;
224         if (status & SPI_SR_TXCOMP)
225                 sc->flags |= TXCOMP;
226         if (status & SPI_SR_TXRDY)
227                 sc->flags |= TXRDY;
228         AT91_SPI_UNLOCK(sc);
229 #endif
230         wakeup(sc);
231         return;
232 }
233
234 static int 
235 at91_spi_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
236 {
237         struct at91_spi_softc *sc;
238
239         sc = CDEV2SOFTC(dev);
240         AT91_SPI_LOCK(sc);
241         if (!(sc->flags & OPENED)) {
242                 sc->flags |= OPENED;
243 #if 0
244                 WR4(sc, SPI_IER, SPI_SR_TXCOMP | SPI_SR_RXRDY | SPI_SR_TXRDY |
245                     SPI_SR_OVRE | SPI_SR_UNRE | SPI_SR_NACK);
246 #endif
247         }
248         AT91_SPI_UNLOCK(sc);
249         return (0);
250 }
251
252 static int
253 at91_spi_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
254 {
255         struct at91_spi_softc *sc;
256
257         sc = CDEV2SOFTC(dev);
258         AT91_SPI_LOCK(sc);
259         sc->flags &= ~OPENED;
260 #if 0
261         WR4(sc, SPI_IDR, SPI_SR_TXCOMP | SPI_SR_RXRDY | SPI_SR_TXRDY |
262             SPI_SR_OVRE | SPI_SR_UNRE | SPI_SR_NACK);
263 #endif
264         AT91_SPI_UNLOCK(sc);
265         return (0);
266 }
267
268 static int
269 at91_spi_read_master(struct at91_spi_softc *sc, struct at91_spi_io *xfr)
270 {
271 #if 1
272     return ENOTTY;
273 #else
274         uint8_t *walker;
275         uint8_t buffer[256];
276         size_t len;
277         int err = 0;
278
279         if (xfr->xfer_len > sizeof(buffer))
280                 return (EINVAL);
281         walker = buffer;
282         len = xfr->xfer_len;
283         RD4(sc, SPI_RHR);
284         // Master mode, with the right address and interal addr size
285         WR4(sc, SPI_MMR, SPI_MMR_IADRSZ(xfr->iadrsz) | SPI_MMR_MREAD |
286             SPI_MMR_DADR(xfr->dadr));
287         WR4(sc, SPI_IADR, xfr->iadr);
288         WR4(sc, SPI_CR, SPI_CR_START);
289         while (len-- > 1) {
290                 while (!(sc->flags & RXRDY)) {
291                         err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "spird",
292                             0);
293                         if (err)
294                                 return (err);
295                 }
296                 sc->flags &= ~RXRDY;
297                 *walker++ = RD4(sc, SPI_RHR) & 0xff;
298         }
299         WR4(sc, SPI_CR, SPI_CR_STOP);
300         while (!(sc->flags & TXCOMP)) {
301                 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "spird2", 0);
302                 if (err)
303                         return (err);
304         }
305         sc->flags &= ~TXCOMP;
306         *walker = RD4(sc, SPI_RHR) & 0xff;
307         if (xfr->xfer_buf) {
308                 AT91_SPI_UNLOCK(sc);
309                 err = copyout(buffer, xfr->xfer_buf, xfr->xfer_len);
310                 AT91_SPI_LOCK(sc);
311         }
312         return (err);
313 #endif
314 }
315
316 static int
317 at91_spi_write_master(struct at91_spi_softc *sc, struct at91_spi_io *xfr)
318 {
319 #if 1
320     return ENOTTY;
321 #else
322         uint8_t *walker;
323         uint8_t buffer[256];
324         size_t len;
325         int err;
326
327         if (xfr->xfer_len > sizeof(buffer))
328                 return (EINVAL);
329         walker = buffer;
330         len = xfr->xfer_len;
331         AT91_SPI_UNLOCK(sc);
332         err = copyin(xfr->xfer_buf, buffer, xfr->xfer_len);
333         AT91_SPI_LOCK(sc);
334         if (err)
335                 return (err);
336         /* Setup the xfr for later readback */
337         xfr->xfer_buf = 0;
338         xfr->xfer_len = 1;
339         while (len--) {
340                 WR4(sc, SPI_MMR, SPI_MMR_IADRSZ(xfr->iadrsz) | SPI_MMR_MWRITE |
341                     SPI_MMR_DADR(xfr->dadr));
342                 WR4(sc, SPI_IADR, xfr->iadr++);
343                 WR4(sc, SPI_THR, *walker++);
344                 WR4(sc, SPI_CR, SPI_CR_START);
345                 /*
346                  * If we get signal while waiting for TXRDY, make sure we
347                  * try to stop this device
348                  */
349                 while (!(sc->flags & TXRDY)) {
350                         err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "spiwr",
351                             0);
352                         if (err)
353                                 break;
354                 }
355                 WR4(sc, SPI_CR, SPI_CR_STOP);
356                 if (err)
357                         return (err);
358                 while (!(sc->flags & TXCOMP)) {
359                         err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "spiwr2",
360                             0);
361                         if (err)
362                                 return (err);
363                 }
364                 /* Readback */
365                 at91_spi_read_master(sc, xfr);
366         }
367         return (err);
368 #endif
369 }
370
371 static int
372 at91_spi_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
373     struct thread *td)
374 {
375         int err = 0;
376         struct at91_spi_softc *sc;
377
378         sc = CDEV2SOFTC(dev);
379         AT91_SPI_LOCK(sc);
380         while (sc->flags & XFER_PENDING) {
381                 err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH,
382                     "spiwait", 0);
383                 if (err) {
384                         AT91_SPI_UNLOCK(sc);
385                         return (err);
386                 }
387         }
388         sc->flags |= XFER_PENDING;
389
390         switch (cmd)
391         {
392         case SPIIOCXFER:
393         {
394                 struct at91_spi_io *xfr = (struct at91_spi_io *)data;
395                 switch (xfr->type)
396                 {
397                 case SPI_IO_READ_MASTER:
398                         err = at91_spi_read_master(sc, xfr);
399                         break;
400                 case SPI_IO_WRITE_MASTER:
401                         err = at91_spi_write_master(sc, xfr);
402                         break;
403                 default:
404                         err = EINVAL;
405                         break;
406                 }
407                 break;
408         }
409
410         case SPIIOCSETCLOCK:
411         {
412 #if 0
413                 struct at91_spi_clock *spick = (struct at91_spi_clock *)data;
414
415                 sc->cwgr = SPI_CWGR_CKDIV(spick->ckdiv) |
416                     SPI_CWGR_CHDIV(SPI_CWGR_DIV(spick->high_rate)) |
417                     SPI_CWGR_CLDIV(SPI_CWGR_DIV(spick->low_rate));
418                 WR4(sc, SPI_CR, SPI_CR_SWRST);
419                 WR4(sc, SPI_CR, SPI_CR_MSEN | SPI_CR_SVDIS);
420                 WR4(sc, SPI_CWGR, sc->cwgr);
421 #else
422                 err = ENOTTY;
423 #endif
424                 break;
425         }
426         default:
427                 err = ENOTTY;
428                 break;
429         }
430         sc->flags &= ~XFER_PENDING;
431         AT91_SPI_UNLOCK(sc);
432         wakeup(sc);
433         return err;
434 }
435
436 static device_method_t at91_spi_methods[] = {
437         /* Device interface */
438         DEVMETHOD(device_probe,         at91_spi_probe),
439         DEVMETHOD(device_attach,        at91_spi_attach),
440         DEVMETHOD(device_detach,        at91_spi_detach),
441
442         { 0, 0 }
443 };
444
445 static driver_t at91_spi_driver = {
446         "at91_spi",
447         at91_spi_methods,
448         sizeof(struct at91_spi_softc),
449 };
450
451 DRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, 0, 0);