remove noise for demo
[qemu] / hw / omap_spi.c
1 /*
2  * TI OMAP processor's Multichannel SPI emulation.
3  *
4  * Copyright (C) 2007-2009 Nokia Corporation
5  *
6  * Original code for OMAP2 by Andrzej Zaborowski <andrew@openedhand.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 or
11  * (at your option) version 3 of the License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 #include "hw.h"
23 #include "omap.h"
24
25 #define SPI_FIFOSIZE 64
26 #define SPI_REV_OMAP2420 0x14
27 #define SPI_REV_OMAP3530 0x21
28 #define IS_OMAP3_SPI(s) ((s)->revision >= SPI_REV_OMAP3530)
29
30 struct omap_mcspi_s {
31     qemu_irq irq;
32     int chnum;
33     uint8_t revision;
34     
35     uint32_t sysconfig;
36     uint32_t systest;
37     uint32_t irqst;
38     uint32_t irqen;
39     uint32_t wken;
40     uint32_t control;
41     uint32_t xferlevel;
42     struct omap_mcspi_fifo_s {
43         int start;
44         int len;
45         int size;
46         uint8_t buf[SPI_FIFOSIZE];
47     } tx_fifo, rx_fifo;
48     int fifo_ch;
49     int fifo_wcnt;
50     
51     struct omap_mcspi_ch_s {
52         qemu_irq txdrq;
53         qemu_irq rxdrq;
54         uint32_t (*txrx)(void *opaque, uint32_t, int);
55         void *opaque;
56         
57         uint32_t tx;
58         uint32_t rx;
59         
60         uint32_t config;
61         uint32_t status;
62         uint32_t control;
63     } ch[0];
64 };
65
66 static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
67 {
68     qemu_set_irq(s->irq, s->irqst & s->irqen);
69 }
70
71 static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_s *s,
72                                                 int chnum)
73 {
74     struct omap_mcspi_ch_s *ch = &s->ch[chnum];
75     if ((ch->control & 1) &&                         /* EN */
76         (ch->config & (1 << 14)) &&                  /* DMAW */
77         (ch->status & (1 << 1)) &&                   /* TXS */
78         ((ch->config >> 12) & 3) != 1) {             /* TRM */
79         if (!IS_OMAP3_SPI(s) ||
80             !(ch->config & (1 << 27)) ||             /* FFEW */
81             s->tx_fifo.len <= (s->xferlevel & 0x3f)) /* AEL */
82             qemu_irq_raise(ch->txdrq);
83         else
84             qemu_irq_lower(ch->txdrq);
85     }
86     if ((ch->control & 1) &&                                /* EN */
87         (ch->config & (1 << 15)) &&                         /* DMAW */
88         (ch->status & (1 << 0)) &&                          /* RXS */
89         ((ch->config >> 12) & 3) != 2) {                    /* TRM */
90         if (!IS_OMAP3_SPI(s) ||
91             !(ch->config & (1 << 28)) ||                    /* FFER */
92             s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
93             qemu_irq_raise(ch->rxdrq);
94         else
95             qemu_irq_lower(ch->rxdrq);
96     }
97 }
98
99 static void omap_mcspi_fifo_reset(struct omap_mcspi_s *s)
100 {
101     struct omap_mcspi_ch_s *ch;
102     
103     s->tx_fifo.len = 0;
104     s->rx_fifo.len = 0;
105     s->tx_fifo.start = 0;
106     s->rx_fifo.start = 0;
107     if (s->fifo_ch < 0) {
108         s->tx_fifo.size  = s->rx_fifo.size  = 0;
109     } else {
110         ch = &s->ch[s->fifo_ch];
111         s->tx_fifo.size = ((ch->config >> 27) & 1) ? SPI_FIFOSIZE : 0;
112         s->rx_fifo.size = ((ch->config >> 28) & 1) ? SPI_FIFOSIZE : 0;
113         if (((ch->config >> 27) & 3) == 3) {
114             s->tx_fifo.size >>= 1;
115             s->rx_fifo.size >>= 1;
116         }
117     }
118 }
119
120 static void omap_mcspi_save_state(QEMUFile *f, void *opaque)
121 {
122     struct omap_mcspi_s *s = (struct omap_mcspi_s *)opaque;
123     int i;
124     
125     qemu_put_be32(f, s->sysconfig);
126     qemu_put_be32(f, s->systest);
127     qemu_put_be32(f, s->irqst);
128     qemu_put_be32(f, s->irqen);
129     qemu_put_be32(f, s->wken);
130     qemu_put_be32(f, s->control);
131     qemu_put_be32(f, s->xferlevel);
132     qemu_put_sbe32(f, s->tx_fifo.start);
133     qemu_put_sbe32(f, s->tx_fifo.len);
134     qemu_put_sbe32(f, s->tx_fifo.size);
135     qemu_put_buffer(f, s->tx_fifo.buf, sizeof(s->tx_fifo.buf));
136     qemu_put_sbe32(f, s->rx_fifo.start);
137     qemu_put_sbe32(f, s->rx_fifo.len);
138     qemu_put_sbe32(f, s->rx_fifo.size);
139     qemu_put_buffer(f, s->rx_fifo.buf, sizeof(s->rx_fifo.buf));
140     qemu_put_sbe32(f, s->fifo_ch);
141     qemu_put_sbe32(f, s->fifo_wcnt);
142     for (i = 0; i < s->chnum; i++) {
143         qemu_put_be32(f, s->ch[i].tx);
144         qemu_put_be32(f, s->ch[i].rx);
145         qemu_put_be32(f, s->ch[i].config);
146         qemu_put_be32(f, s->ch[i].status);
147         qemu_put_be32(f, s->ch[i].control);
148     }
149 }
150
151 static int omap_mcspi_load_state(QEMUFile *f, void *opaque, int version_id)
152 {
153     struct omap_mcspi_s *s = (struct omap_mcspi_s *)opaque;
154     int i;
155     
156     if (version_id)
157         return -EINVAL;
158     
159     s->sysconfig = qemu_get_be32(f);
160     s->systest = qemu_get_be32(f);
161     s->irqst = qemu_get_be32(f);
162     s->irqen = qemu_get_be32(f);
163     s->wken = qemu_get_be32(f);
164     s->control = qemu_get_be32(f);
165     s->xferlevel = qemu_get_be32(f);
166     s->tx_fifo.start = qemu_get_be32(f);
167     s->tx_fifo.len = qemu_get_be32(f);
168     s->tx_fifo.size = qemu_get_be32(f);
169     qemu_get_buffer(f, s->tx_fifo.buf, sizeof(s->tx_fifo.buf));
170     s->rx_fifo.start = qemu_get_be32(f);
171     s->rx_fifo.len = qemu_get_be32(f);
172     s->rx_fifo.size = qemu_get_be32(f);
173     qemu_get_buffer(f, s->rx_fifo.buf, sizeof(s->rx_fifo.buf));
174     s->fifo_ch = qemu_get_sbe32(f);
175     s->fifo_wcnt = qemu_get_sbe32(f);
176     for (i = 0; i < s->chnum; i++) {
177         s->ch[i].tx = qemu_get_be32(f);
178         s->ch[i].rx = qemu_get_be32(f);
179         s->ch[i].config = qemu_get_be32(f);
180         s->ch[i].status = qemu_get_be32(f);
181         s->ch[i].control = qemu_get_be32(f);
182         omap_mcspi_dmarequest_update(s, i);
183     }
184     omap_mcspi_interrupt_update(s);
185     
186     return 0;
187 }
188
189 /* returns next word in FIFO or the n first bytes if there is not
190  * enough data in FIFO */
191 static uint32_t omap_mcspi_fifo_get(struct omap_mcspi_fifo_s *s, int wl)
192 {
193     uint32_t v, sh;
194     
195     for (v = 0, sh = 0; wl > 0 && s->len; wl -= 8, s->len--, sh += 8) {
196         v |= ((uint32_t)s->buf[s->start++]) << sh;
197         if (s->start >= s->size)
198             s->start = 0;
199     }
200     return v;
201 }
202
203 /* pushes a word to FIFO or the first n bytes of the word if the FIFO
204  * is too full to hold the full word */
205 static void omap_mcspi_fifo_put(struct omap_mcspi_fifo_s *s, int wl,
206                                 uint32_t v)
207 {
208     int p = s->start + s->len;
209
210     for (; wl > 0 && s->len < s->size; wl -=8, v >>= 8, s->len++) {
211         if (p >= s->size)
212             p -= s->size;
213         s->buf[p++] = (uint8_t)(v & 0xff);
214     }
215 }
216
217 static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
218 {
219     struct omap_mcspi_ch_s *ch = s->ch + chnum;
220     int trm = (ch->config >> 12) & 3;
221     int wl;
222     
223     if (!(ch->control & 1))                  /* EN */
224         return;
225     if ((ch->status & 1) && trm != 2 &&      /* RXS */
226         !(ch->config & (1 << 19)))           /* TURBO */
227         goto intr_update;
228     if ((ch->status & (1 << 1)) && trm != 1) /* TXS */
229         goto intr_update;
230     
231     if (!(s->control & 1) ||        /* SINGLE */
232         (ch->config & (1 << 20))) { /* FORCE */
233         if (ch->txrx) {
234             wl = 1 + (0x1f & (ch->config >> 7)); /* WL */
235             if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
236                 !((ch->config >> 27) & 3))       /* FFER | FFEW */
237                 ch->rx = ch->txrx(ch->opaque, ch->tx, wl);
238             else {
239                 switch ((ch->config >> 27) & 3) {
240                     case 1: /* !FFER, FFEW */
241                         if (trm != 1)
242                             ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
243                         ch->rx = ch->txrx(ch->opaque, ch->tx, wl);
244                         s->fifo_wcnt--;
245                         break;
246                     case 2: /* FFER, !FFEW */
247                         ch->rx = ch->txrx(ch->opaque, ch->tx, wl);
248                         if (trm != 2)
249                             omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
250                         s->fifo_wcnt--;
251                         break;
252                     case 3: /* FFER, FFEW */
253                         while (s->rx_fifo.len < s->rx_fifo.size &&
254                                s->tx_fifo.len && s->fifo_wcnt) {
255                             if (trm != 1)
256                                 ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
257                             ch->rx = ch->txrx(ch->opaque, ch->tx, wl);
258                             if (trm != 2)
259                                 omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
260                             s->fifo_wcnt--;
261                         }
262                         break;
263                     default:
264                         break;
265                 }
266                 if ((ch->config & (1 << 28)) &&        /* FFER */
267                     s->rx_fifo.len >= s->rx_fifo.size)
268                     ch->status |= 1 << 6;              /* RXFFF */
269                 ch->status &= ~(1 << 5);               /* RXFFE */
270                 ch->status &= ~(1 << 4);               /* TXFFF */
271                 if ((ch->config & (1 << 27)) &&        /* FFEW */
272                     !s->tx_fifo.len)
273                     ch->status |= 1 << 3;              /* TXFFE */
274                 if (!s->fifo_wcnt &&
275                     ((s->xferlevel >> 16) & 0xffff))   /* WCNT */
276                     s->irqst |= 1 << 17;               /* EOW */
277             }
278         }
279     }
280     
281     ch->tx = 0;
282     ch->status |= 1 << 2;               /* EOT */
283     ch->status |= 1 << 1;               /* TXS */
284     if (trm != 2)
285         ch->status |= 1;                /* RXS */
286         
287 intr_update:
288     if ((ch->status & 1) &&     trm != 2 &&                     /* RXS */
289         !(ch->config & (1 << 19)))                          /* TURBO */
290         if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
291             !((ch->config >> 28) & 1) ||                    /* FFER */
292             s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
293             s->irqst |= 1 << (2 + 4 * chnum);               /* RX_FULL */
294     if ((ch->status & (1 << 1)) && trm != 1)                /* TXS */
295         if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
296             !((ch->config >> 27) & 1) ||                    /* FFEW */
297             s->tx_fifo.len <= (s->xferlevel & 0x3f))        /* AEL */
298             s->irqst |= 1 << (4 * chnum);                   /* TX_EMPTY */
299     omap_mcspi_interrupt_update(s);
300     omap_mcspi_dmarequest_update(s, chnum);
301 }
302
303 void omap_mcspi_reset(struct omap_mcspi_s *s)
304 {
305     int ch;
306     
307     s->sysconfig = 0;
308     s->systest = 0;
309     s->irqst = 0;
310     s->irqen = 0;
311     s->wken = 0;
312     s->control = 4;
313     
314     s->fifo_ch = -1;
315     omap_mcspi_fifo_reset(s);
316     
317     for (ch = 0; ch < s->chnum; ch ++) {
318         s->ch[ch].config = 0x060000;
319         s->ch[ch].status = 2;                           /* TXS */
320         s->ch[ch].control = 0;
321         
322         omap_mcspi_dmarequest_update(s, ch);
323     }
324     
325     omap_mcspi_interrupt_update(s);
326 }
327
328 static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr)
329 {
330     struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
331     int ch = 0;
332     uint32_t ret;
333     
334     switch (addr) {
335         case 0x00:      /* MCSPI_REVISION */
336             return s->revision;
337             
338         case 0x10:      /* MCSPI_SYSCONFIG */
339             return s->sysconfig;
340             
341         case 0x14:      /* MCSPI_SYSSTATUS */
342             return 1;                                   /* RESETDONE */
343             
344         case 0x18:      /* MCSPI_IRQSTATUS */
345             return s->irqst;
346             
347         case 0x1c:      /* MCSPI_IRQENABLE */
348             return s->irqen;
349             
350         case 0x20:      /* MCSPI_WAKEUPENABLE */
351             return s->wken;
352             
353         case 0x24:      /* MCSPI_SYST */
354             return s->systest;
355             
356         case 0x28:      /* MCSPI_MODULCTRL */
357             return s->control;
358             
359         case 0x68: ch ++;
360         case 0x54: ch ++;
361         case 0x40: ch ++;
362         case 0x2c:      /* MCSPI_CHCONF */
363             return (ch < s->chnum) ? s->ch[ch].config : 0;
364             
365         case 0x6c: ch ++;
366         case 0x58: ch ++;
367         case 0x44: ch ++;
368         case 0x30:      /* MCSPI_CHSTAT */
369             return (ch < s->chnum) ? s->ch[ch].status : 0;
370             
371         case 0x70: ch ++;
372         case 0x5c: ch ++;
373         case 0x48: ch ++;
374         case 0x34:      /* MCSPI_CHCTRL */
375             return (ch < s->chnum) ? s->ch[ch].control : 0;
376             
377         case 0x74: ch ++;
378         case 0x60: ch ++;
379         case 0x4c: ch ++;
380         case 0x38:      /* MCSPI_TX */
381             if (ch < s->chnum)
382                 return s->ch[ch].tx;
383             break;
384             
385         case 0x78: ch ++;
386         case 0x64: ch ++;
387         case 0x50: ch ++;
388         case 0x3c:      /* MCSPI_RX */
389             if (ch < s->chnum) {
390                 if (!IS_OMAP3_SPI(s) || ch != s->fifo_ch ||
391                     !(s->ch[ch].config & (1 << 28))) { /* FFER */
392                     s->ch[ch].status &= ~1;            /* RXS */
393                     ret = s->ch[ch].rx;
394                     omap_mcspi_transfer_run(s, ch);
395                     return ret;
396                 }
397                 if (!s->rx_fifo.len)
398                     fprintf(stderr, "%s: rxfifo underflow!\n", __FUNCTION__);
399                 else {
400                     qemu_irq_lower(s->ch[ch].rxdrq);
401                     s->ch[ch].status &= ~(1 << 6);                 /* RXFFF */
402                     if (((s->ch[ch].config >> 12) & 3) != 2)        /* TRM */
403                         ret = omap_mcspi_fifo_get(&s->rx_fifo,
404                             1 + ((s->ch[ch].config >> 7) & 0x1f)); /* WL */
405                     else
406                         ret = s->ch[ch].rx;
407                     if (!s->rx_fifo.len) {
408                         s->ch[ch].status &= ~1;     /* RXS */
409                         s->ch[ch].status |= 1 << 5; /* RXFFE */
410                         omap_mcspi_transfer_run(s, ch);
411                     }
412                     return ret;
413                 }
414             }
415             return 0;
416         
417         case 0x7c: /* MCSPI_XFERLEVEL */
418             if (IS_OMAP3_SPI(s)) {
419                 if ((s->xferlevel >> 16) & 0xffff) /* WCNT */
420                     ret = ((s->xferlevel & 0xffff0000) - (s->fifo_wcnt << 16));
421                 else
422                     ret = ((-s->fifo_wcnt) & 0xffff) << 16;
423                 return (s->xferlevel & 0xffff) | ret;
424             }
425             break;
426             
427         default:
428             break;
429     }
430     
431     OMAP_BAD_REG(addr);
432     return 0;
433 }
434
435 static void omap_mcspi_write(void *opaque, target_phys_addr_t addr,
436                              uint32_t value)
437 {
438     struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
439     uint32_t old;
440     int ch = 0;
441     
442     switch (addr) {
443         case 0x00:      /* MCSPI_REVISION */
444         case 0x14:      /* MCSPI_SYSSTATUS */
445         case 0x30:      /* MCSPI_CHSTAT0 */
446         case 0x3c:      /* MCSPI_RX0 */
447         case 0x44:      /* MCSPI_CHSTAT1 */
448         case 0x50:      /* MCSPI_RX1 */
449         case 0x58:      /* MCSPI_CHSTAT2 */
450         case 0x64:      /* MCSPI_RX2 */
451         case 0x6c:      /* MCSPI_CHSTAT3 */
452         case 0x78:      /* MCSPI_RX3 */
453             OMAP_RO_REGV(addr, value);
454             return;
455             
456         case 0x10:      /* MCSPI_SYSCONFIG */
457             if (value & (1 << 1))                               /* SOFTRESET */
458                 omap_mcspi_reset(s);
459             s->sysconfig = value & 0x31d;
460             break;
461             
462         case 0x18:      /* MCSPI_IRQSTATUS */
463             if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
464                 s->irqst &= ~value;
465                 omap_mcspi_interrupt_update(s);
466             }
467             break;
468             
469         case 0x1c:      /* MCSPI_IRQENABLE */
470             s->irqen = value & (IS_OMAP3_SPI(s) ? 0x3777f : 0x1777f);
471             omap_mcspi_interrupt_update(s);
472             break;
473             
474         case 0x20:      /* MCSPI_WAKEUPENABLE */
475             s->wken = value & 1;
476             break;
477             
478         case 0x24:      /* MCSPI_SYST */
479             if (s->control & (1 << 3))                  /* SYSTEM_TEST */
480                 if (value & (1 << 11)) {                        /* SSB */
481                     s->irqst |= 0x1777f;
482                     omap_mcspi_interrupt_update(s);
483                 }
484             s->systest = value & 0xfff;
485             break;
486             
487         case 0x28:      /* MCSPI_MODULCTRL */
488             if (value & (1 << 3))                               /* SYSTEM_TEST */
489                 if (s->systest & (1 << 11)) {           /* SSB */
490                     s->irqst |= IS_OMAP3_SPI(s) ? 0x3777f : 0x1777f;
491                     omap_mcspi_interrupt_update(s);
492                 }
493             s->control = value & 0xf;
494             break;
495             
496         case 0x68: ch ++;
497         case 0x54: ch ++;
498         case 0x40: ch ++;
499         case 0x2c:      /* MCSPI_CHCONF */
500             if (ch < s->chnum) {
501                 old = s->ch[ch].config;
502                 s->ch[ch].config = value & (IS_OMAP3_SPI(s)
503                                             ? 0x3fffffff : 0x7fffff);
504                 if (IS_OMAP3_SPI(s) &&
505                     ((value ^ old) & (3 << 27))) { /* FFER | FFEW */
506                     s->fifo_ch = ((value & (3 << 27))) ? ch : -1;
507                     omap_mcspi_fifo_reset(s);
508                 }
509                 if (((value ^ old) & (3 << 14)) || /* DMAR | DMAW */
510                     (IS_OMAP3_SPI(s) &&
511                      ((value ^ old) & (3 << 27)))) /* FFER | FFEW */
512                     omap_mcspi_dmarequest_update(s, ch);
513                 if (((value >> 12) & 3) == 3)                   /* TRM */
514                     fprintf(stderr, "%s: invalid TRM value (3)\n",
515                             __FUNCTION__);
516                 if (((value >> 7) & 0x1f) < 3)                  /* WL */
517                     fprintf(stderr, "%s: invalid WL value (%i)\n",
518                             __FUNCTION__, (value >> 7) & 0x1f);
519                 if (IS_OMAP3_SPI(s) &&
520                     ((value >> 23) & 1))               /* SBE */
521                     fprintf(stderr, "%s: start-bit mode is not supported\n",
522                             __FUNCTION__);
523             }
524             break;
525             
526         case 0x70: ch ++;
527         case 0x5c: ch ++;
528         case 0x48: ch ++;
529         case 0x34:      /* MCSPI_CHCTRL */
530             if (ch < s->chnum) {
531                 old = s->ch[ch].control;
532                 s->ch[ch].control = value & (IS_OMAP3_SPI(s) ? 0xff01 : 1);
533                 if (value & ~old & 1) { /* EN */
534                     if (IS_OMAP3_SPI(s) && s->fifo_ch == ch)
535                         omap_mcspi_fifo_reset(s);
536                     omap_mcspi_transfer_run(s, ch);
537                 }
538             }
539             break;
540             
541         case 0x74: ch ++;
542         case 0x60: ch ++;
543         case 0x4c: ch ++;
544         case 0x38:      /* MCSPI_TX */
545             if (ch < s->chnum) {
546                 if (!IS_OMAP3_SPI(s) || s->fifo_ch != ch ||
547                     !(s->ch[ch].config & (1 << 27))) { /* FFEW */
548                     s->ch[ch].tx = value;
549                     s->ch[ch].status &= ~(1 << 1);     /* TXS */
550                     omap_mcspi_transfer_run(s, ch);
551                 } else {
552                     if (s->tx_fifo.len >= s->tx_fifo.size)
553                         fprintf(stderr, "%s: txfifo overflow!\n",
554                                 __FUNCTION__);
555                     else {
556                         qemu_irq_lower(s->ch[ch].txdrq);
557                         s->ch[ch].status &= ~0x0a;            /* TXFFE | TXS */
558                         if (((s->ch[ch].config >> 12) & 3) != 1) {    /* TRM */
559                             omap_mcspi_fifo_put(
560                                 &s->tx_fifo,
561                                 1 + ((s->ch[ch].config >> 7) & 0x1f), /* WL */
562                                 value);
563                             if (s->tx_fifo.len >= s->tx_fifo.size)
564                                 s->ch[ch].status |= 1 << 4;        /* TXFFF */
565                             if (s->tx_fifo.len >= (s->xferlevel & 0x3f))
566                                 omap_mcspi_transfer_run(s, ch);
567                         } else {
568                             s->ch[ch].tx = value;
569                             omap_mcspi_transfer_run(s, ch);
570                         }
571                     }
572                 }
573             }
574             break;
575             
576         case 0x7c:
577             if (IS_OMAP3_SPI(s)) {
578                 if (value != s->xferlevel) {
579                     s->fifo_wcnt = (value >> 16) & 0xffff;
580                     s->xferlevel = value & 0xffff3f3f;
581                     omap_mcspi_fifo_reset(s);
582                 }
583             } else
584                 OMAP_BAD_REGV(addr, value);
585             break;
586             
587         default:
588             OMAP_BAD_REGV(addr, value);
589             return;
590     }
591 }
592
593 static CPUReadMemoryFunc *omap_mcspi_readfn[] = {
594     omap_badwidth_read32,
595     omap_badwidth_read32,
596     omap_mcspi_read,
597 };
598
599 static CPUWriteMemoryFunc *omap_mcspi_writefn[] = {
600     omap_badwidth_write32,
601     omap_badwidth_write32,
602     omap_mcspi_write,
603 };
604
605 struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta,
606                                      struct omap_mpu_state_s *mpu,
607                                      int chnum, qemu_irq irq, qemu_irq *drq,
608                                      omap_clk fclk, omap_clk iclk)
609 {
610     struct omap_mcspi_s *s = (struct omap_mcspi_s *)
611         qemu_mallocz(sizeof(struct omap_mcspi_s) +
612                      chnum * sizeof(struct omap_mcspi_ch_s));
613     struct omap_mcspi_ch_s *ch = s->ch;
614     
615     s->irq = irq;
616     s->chnum = chnum;
617     /* revision was hardcoded as 0x91 in original code -- odd */
618     s->revision = cpu_class_omap3(mpu) ? SPI_REV_OMAP3530 : SPI_REV_OMAP2420;
619     while (chnum --) {
620         ch->txdrq = *drq ++;
621         ch->rxdrq = *drq ++;
622         ch ++;
623     }
624     omap_mcspi_reset(s);
625     
626     omap_l4_attach(ta, 0, l4_register_io_memory(0, omap_mcspi_readfn,
627                                                 omap_mcspi_writefn, s));
628     register_savevm("omap_mcspi", (ta->base >> 8), 0,
629                     omap_mcspi_save_state, omap_mcspi_load_state, s);
630     return s;
631 }
632
633 void omap_mcspi_attach(struct omap_mcspi_s *s,
634                        uint32_t (*txrx)(void *opaque, uint32_t, int),
635                        void *opaque,
636                        int chipselect)
637 {
638     if (chipselect < 0 || chipselect >= s->chnum)
639         cpu_abort(cpu_single_env, "%s: Bad chipselect %i\n",
640                   __FUNCTION__, chipselect);
641     
642     s->ch[chipselect].txrx = txrx;
643     s->ch[chipselect].opaque = opaque;
644 }