2
* TI OMAP processor's Multichannel SPI emulation.
4
* Copyright (C) 2007-2009 Nokia Corporation
6
* Original code for OMAP2 by Andrzej Zaborowski <andrew@openedhand.com>
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) any later version of the License.
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.
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.
23
#include "hw/arm/omap.h"
24
#include "hw/sysbus.h"
30
#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \
31
__LINE__, ##__VA_ARGS__);
36
#define SPI_FIFOSIZE 64
37
#define SPI_REV_OMAP2420 0x14
38
#define SPI_REV_OMAP3430 0x21
39
#define IS_OMAP3_SPI(s) ((s)->revision >= SPI_REV_OMAP3430)
41
typedef struct omap_mcspi_bus_s {
56
struct omap_mcspi_fifo_s {
60
uint8_t buf[SPI_FIFOSIZE];
65
struct omap_mcspi_ch_s {
78
typedef struct omap_mcspi_s {
85
static inline void omap_mcspi_interrupt_update(OMAPSPIBusState *s)
87
qemu_set_irq(s->irq, s->irqst & s->irqen);
90
static inline void omap_mcspi_dmarequest_update(OMAPSPIBusState *s,
93
struct omap_mcspi_ch_s *ch = &s->ch[chnum];
94
if ((ch->control & 1) && /* EN */
95
(ch->config & (1 << 14)) && /* DMAW */
96
(ch->status & (1 << 1)) && /* TXS */
97
((ch->config >> 12) & 3) != 1) { /* TRM */
98
if (!IS_OMAP3_SPI(s) ||
99
!(ch->config & (1 << 27)) || /* FFEW */
100
s->tx_fifo.len <= (s->xferlevel & 0x3f)) /* AEL */
101
qemu_irq_raise(ch->txdrq);
103
qemu_irq_lower(ch->txdrq);
105
if ((ch->control & 1) && /* EN */
106
(ch->config & (1 << 15)) && /* DMAW */
107
(ch->status & (1 << 0)) && /* RXS */
108
((ch->config >> 12) & 3) != 2) { /* TRM */
109
if (!IS_OMAP3_SPI(s) ||
110
!(ch->config & (1 << 28)) || /* FFER */
111
s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
112
qemu_irq_raise(ch->rxdrq);
114
qemu_irq_lower(ch->rxdrq);
118
static void omap_mcspi_fifo_reset(OMAPSPIBusState *s)
120
struct omap_mcspi_ch_s *ch;
124
s->tx_fifo.start = 0;
125
s->rx_fifo.start = 0;
126
if (s->fifo_ch < 0) {
127
s->tx_fifo.size = s->rx_fifo.size = 0;
129
ch = &s->ch[s->fifo_ch];
130
s->tx_fifo.size = ((ch->config >> 27) & 1) ? SPI_FIFOSIZE : 0;
131
s->rx_fifo.size = ((ch->config >> 28) & 1) ? SPI_FIFOSIZE : 0;
132
if (((ch->config >> 27) & 3) == 3) {
133
s->tx_fifo.size >>= 1;
134
s->rx_fifo.size >>= 1;
139
/* returns next word in FIFO or the n first bytes if there is not
140
* enough data in FIFO */
141
static uint32_t omap_mcspi_fifo_get(struct omap_mcspi_fifo_s *s, int wl)
145
for (v = 0, sh = 0; wl > 0 && s->len; wl -= 8, s->len--, sh += 8) {
146
v |= ((uint32_t)s->buf[s->start++]) << sh;
147
if (s->start >= s->size)
153
/* pushes a word to FIFO or the first n bytes of the word if the FIFO
154
* is too full to hold the full word */
155
static void omap_mcspi_fifo_put(struct omap_mcspi_fifo_s *s, int wl,
158
int p = s->start + s->len;
160
for (; wl > 0 && s->len < s->size; wl -=8, v >>= 8, s->len++) {
163
s->buf[p++] = (uint8_t)(v & 0xff);
167
static void omap_mcspi_transfer_run(OMAPSPIBusState *s, int chnum)
169
struct omap_mcspi_ch_s *ch = s->ch + chnum;
170
int trm = (ch->config >> 12) & 3;
173
if (!(ch->control & 1)) /* EN */
175
if ((ch->status & 1) && trm != 2 && /* RXS */
176
!(ch->config & (1 << 19))) /* TURBO */
178
if ((ch->status & (1 << 1)) && trm != 1) /* TXS */
181
if (!(s->control & 1) || /* SINGLE */
182
(ch->config & (1 << 20))) { /* FORCE */
183
wl = 1 + (0x1f & (ch->config >> 7)); /* WL */
184
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
185
!((ch->config >> 27) & 3)) { /* FFER | FFEW */
186
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
188
switch ((ch->config >> 27) & 3) {
189
case 1: /* !FFER, FFEW */
191
ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
192
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
195
case 2: /* FFER, !FFEW */
196
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
198
omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
201
case 3: /* FFER, FFEW */
202
while (s->rx_fifo.len < s->rx_fifo.size &&
203
s->tx_fifo.len && s->fifo_wcnt) {
205
ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
206
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
208
omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
215
if ((ch->config & (1 << 28)) && /* FFER */
216
s->rx_fifo.len >= s->rx_fifo.size)
217
ch->status |= 1 << 6; /* RXFFF */
218
ch->status &= ~(1 << 5); /* RXFFE */
219
ch->status &= ~(1 << 4); /* TXFFF */
220
if ((ch->config & (1 << 27)) && /* FFEW */
222
ch->status |= 1 << 3; /* TXFFE */
224
((s->xferlevel >> 16) & 0xffff)) /* WCNT */
225
s->irqst |= 1 << 17; /* EOW */
230
ch->status |= 1 << 2; /* EOT */
231
ch->status |= 1 << 1; /* TXS */
233
ch->status |= 1; /* RXS */
236
if ((ch->status & 1) && trm != 2 && /* RXS */
237
!(ch->config & (1 << 19))) /* TURBO */
238
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
239
!((ch->config >> 28) & 1) || /* FFER */
240
s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
241
s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */
242
if ((ch->status & (1 << 1)) && trm != 1) /* TXS */
243
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
244
!((ch->config >> 27) & 1) || /* FFEW */
245
s->tx_fifo.len <= (s->xferlevel & 0x3f)) /* AEL */
246
s->irqst |= 1 << (4 * chnum); /* TX_EMPTY */
247
omap_mcspi_interrupt_update(s);
248
omap_mcspi_dmarequest_update(s, chnum);
251
static void omap_mcspi_bus_reset(OMAPSPIBusState *s)
263
omap_mcspi_fifo_reset(s);
265
for (ch = 0; ch < s->chnum; ch ++) {
266
s->ch[ch].config = 0x060000;
267
s->ch[ch].status = 2; /* TXS */
268
s->ch[ch].control = 0;
270
omap_mcspi_dmarequest_update(s, ch);
273
omap_mcspi_interrupt_update(s);
276
static uint64_t omap_mcspi_read(void *opaque, hwaddr addr,
279
OMAPSPIBusState *s = (OMAPSPIBusState *) opaque;
284
return omap_badwidth_read32(opaque, addr);
288
case 0x00: /* MCSPI_REVISION */
289
TRACE("REVISION = 0x%08x", s->revision);
292
case 0x10: /* MCSPI_SYSCONFIG */
293
TRACE("SYSCONFIG = 0x%08x", s->sysconfig);
296
case 0x14: /* MCSPI_SYSSTATUS */
297
TRACE("SYSSTATUS = 0x00000001");
298
return 1; /* RESETDONE */
300
case 0x18: /* MCSPI_IRQSTATUS */
301
TRACE("IRQSTATUS = 0x%08x", s->irqst);
304
case 0x1c: /* MCSPI_IRQENABLE */
305
TRACE("IRQENABLE = 0x%08x", s->irqen);
308
case 0x20: /* MCSPI_WAKEUPENABLE */
309
TRACE("WAKEUPENABLE = 0x%08x", s->wken);
312
case 0x24: /* MCSPI_SYST */
313
TRACE("SYST = 0x%08x", s->systest);
316
case 0x28: /* MCSPI_MODULCTRL */
317
TRACE("MODULCTRL = 0x%08x", s->control);
326
case 0x2c: /* MCSPI_CHCONF */
327
TRACE("CHCONF%d = 0x%08x", ch,
328
(ch < s->chnum) ? s->ch[ch].config : 0);
329
return (ch < s->chnum) ? s->ch[ch].config : 0;
337
case 0x30: /* MCSPI_CHSTAT */
338
TRACE("CHSTAT%d = 0x%08x", ch,
339
(ch < s->chnum) ? s->ch[ch].status : 0);
340
return (ch < s->chnum) ? s->ch[ch].status : 0;
348
case 0x34: /* MCSPI_CHCTRL */
349
TRACE("CHCTRL%d = 0x%08x", ch,
350
(ch < s->chnum) ? s->ch[ch].control : 0);
351
return (ch < s->chnum) ? s->ch[ch].control : 0;
359
case 0x38: /* MCSPI_TX */
360
TRACE("TX%d = 0x%08x", ch,
361
(ch < s->chnum) ? s->ch[ch].tx : 0);
362
return (ch < s->chnum) ? s->ch[ch].tx : 0;
370
case 0x3c: /* MCSPI_RX */
372
if (!IS_OMAP3_SPI(s) || ch != s->fifo_ch ||
373
!(s->ch[ch].config & (1 << 28))) { /* FFER */
374
s->ch[ch].status &= ~1; /* RXS */
376
TRACE("RX%d = 0x%08x", ch, ret);
377
omap_mcspi_transfer_run(s, ch);
380
if (!s->rx_fifo.len) {
381
TRACE("rxfifo underflow!");
383
qemu_irq_lower(s->ch[ch].rxdrq);
384
s->ch[ch].status &= ~(1 << 6); /* RXFFF */
385
if (((s->ch[ch].config >> 12) & 3) != 2) /* TRM */
386
ret = omap_mcspi_fifo_get(&s->rx_fifo,
387
1 + ((s->ch[ch].config >> 7) & 0x1f)); /* WL */
390
TRACE("RX%d = 0x%08x", ch, ret);
391
if (!s->rx_fifo.len) {
392
s->ch[ch].status &= ~1; /* RXS */
393
s->ch[ch].status |= 1 << 5; /* RXFFE */
394
omap_mcspi_transfer_run(s, ch);
399
TRACE("RX%d = 0x00000000", ch);
402
case 0x7c: /* MCSPI_XFERLEVEL */
403
if (IS_OMAP3_SPI(s)) {
404
if ((s->xferlevel >> 16) & 0xffff) /* WCNT */
405
ret = ((s->xferlevel & 0xffff0000) - (s->fifo_wcnt << 16));
407
ret = ((-s->fifo_wcnt) & 0xffff) << 16;
408
TRACE("XFERLEVEL = 0x%08x", (s->xferlevel & 0xffff) | ret);
409
return (s->xferlevel & 0xffff) | ret;
421
static void omap_mcspi_write(void *opaque, hwaddr addr,
422
uint64_t value, unsigned size)
424
OMAPSPIBusState *s = (OMAPSPIBusState *) opaque;
429
return omap_badwidth_write32(opaque, addr, value);
433
case 0x00: /* MCSPI_REVISION */
434
case 0x14: /* MCSPI_SYSSTATUS */
435
case 0x30: /* MCSPI_CHSTAT0 */
436
case 0x3c: /* MCSPI_RX0 */
437
case 0x44: /* MCSPI_CHSTAT1 */
438
case 0x50: /* MCSPI_RX1 */
439
case 0x58: /* MCSPI_CHSTAT2 */
440
case 0x64: /* MCSPI_RX2 */
441
case 0x6c: /* MCSPI_CHSTAT3 */
442
case 0x78: /* MCSPI_RX3 */
443
/* silently ignore */
444
//OMAP_RO_REGV(addr, value);
447
case 0x10: /* MCSPI_SYSCONFIG */
448
TRACE("SYSCONFIG = 0x%08x", value);
449
if (value & (1 << 1)) /* SOFTRESET */
450
omap_mcspi_bus_reset(s);
451
s->sysconfig = value & 0x31d;
454
case 0x18: /* MCSPI_IRQSTATUS */
455
TRACE("IRQSTATUS = 0x%08x", value);
456
if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
458
omap_mcspi_interrupt_update(s);
462
case 0x1c: /* MCSPI_IRQENABLE */
463
TRACE("IRQENABLE = 0x%08x", value);
464
s->irqen = value & (IS_OMAP3_SPI(s) ? 0x3777f : 0x1777f);
465
omap_mcspi_interrupt_update(s);
468
case 0x20: /* MCSPI_WAKEUPENABLE */
469
TRACE("WAKEUPENABLE = 0x%08x", value);
473
case 0x24: /* MCSPI_SYST */
474
TRACE("SYST = 0x%08x", value);
475
if (s->control & (1 << 3)) /* SYSTEM_TEST */
476
if (value & (1 << 11)) { /* SSB */
478
omap_mcspi_interrupt_update(s);
480
s->systest = value & 0xfff;
483
case 0x28: /* MCSPI_MODULCTRL */
484
TRACE("MODULCTRL = 0x%08x", value);
485
if (value & (1 << 3)) /* SYSTEM_TEST */
486
if (s->systest & (1 << 11)) { /* SSB */
487
s->irqst |= IS_OMAP3_SPI(s) ? 0x3777f : 0x1777f;
488
omap_mcspi_interrupt_update(s);
490
s->control = value & 0xf;
499
case 0x2c: /* MCSPI_CHCONF */
500
TRACE("CHCONF%d = 0x%08x", ch, value);
502
old = s->ch[ch].config;
503
s->ch[ch].config = value & (IS_OMAP3_SPI(s)
504
? 0x3fffffff : 0x7fffff);
505
if (IS_OMAP3_SPI(s) &&
506
((value ^ old) & (3 << 27))) { /* FFER | FFEW */
507
s->fifo_ch = ((value & (3 << 27))) ? ch : -1;
508
omap_mcspi_fifo_reset(s);
510
if (((value ^ old) & (3 << 14)) || /* DMAR | DMAW */
512
((value ^ old) & (3 << 27)))) /* FFER | FFEW */
513
omap_mcspi_dmarequest_update(s, ch);
514
if (((value >> 12) & 3) == 3) { /* TRM */
515
TRACE("invalid TRM value (3)");
517
if (((value >> 7) & 0x1f) < 3) { /* WL */
518
TRACE("invalid WL value (%" PRIx64 ")", (value >> 7) & 0x1f);
520
if (IS_OMAP3_SPI(s) && ((value >> 23) & 1)) { /* SBE */
521
TRACE("start-bit mode is not supported");
532
case 0x34: /* MCSPI_CHCTRL */
533
TRACE("CHCTRL%d = 0x%08x", ch, value);
535
old = s->ch[ch].control;
536
s->ch[ch].control = value & (IS_OMAP3_SPI(s) ? 0xff01 : 1);
537
if (value & ~old & 1) { /* EN */
538
if (IS_OMAP3_SPI(s) && s->fifo_ch == ch)
539
omap_mcspi_fifo_reset(s);
540
omap_mcspi_transfer_run(s, ch);
551
case 0x38: /* MCSPI_TX */
552
TRACE("TX%d = 0x%08x", ch, value);
554
if (!IS_OMAP3_SPI(s) || s->fifo_ch != ch ||
555
!(s->ch[ch].config & (1 << 27))) { /* FFEW */
556
s->ch[ch].tx = value;
557
s->ch[ch].status &= ~0x06; /* EOT | TXS */
558
omap_mcspi_transfer_run(s, ch);
560
if (s->tx_fifo.len >= s->tx_fifo.size) {
561
TRACE("txfifo overflow!");
563
qemu_irq_lower(s->ch[ch].txdrq);
564
s->ch[ch].status &= ~0x0e; /* TXFFE | EOT | TXS */
565
if (((s->ch[ch].config >> 12) & 3) != 1) { /* TRM */
568
1 + ((s->ch[ch].config >> 7) & 0x1f), /* WL */
570
if (s->tx_fifo.len >= s->tx_fifo.size)
571
s->ch[ch].status |= 1 << 4; /* TXFFF */
572
if (s->tx_fifo.len >= (s->xferlevel & 0x3f))
573
omap_mcspi_transfer_run(s, ch);
575
s->ch[ch].tx = value;
576
omap_mcspi_transfer_run(s, ch);
583
case 0x7c: /* MCSPI_XFERLEVEL */
584
TRACE("XFERLEVEL = 0x%08x", value);
585
if (IS_OMAP3_SPI(s)) {
586
if (value != s->xferlevel) {
587
s->fifo_wcnt = (value >> 16) & 0xffff;
588
s->xferlevel = value & 0xffff3f3f;
589
omap_mcspi_fifo_reset(s);
601
static const MemoryRegionOps omap_mcspi_ops = {
602
.read = omap_mcspi_read,
603
.write = omap_mcspi_write,
604
.endianness = DEVICE_NATIVE_ENDIAN,
607
static void omap_mcspi_reset(DeviceState *qdev)
610
OMAPSPIState *s = FROM_SYSBUS(OMAPSPIState, SYS_BUS_DEVICE(qdev));
611
for (i = 0; i < s->buscount; i++) {
612
omap_mcspi_bus_reset(&s->bus[i]);
616
static int omap_mcspi_init(SysBusDevice *busdev)
620
OMAPSPIState *s = FROM_SYSBUS(OMAPSPIState, busdev);
622
s->buscount = (s->mpu_model < omap3430) ? 2 : 4;
623
s->bus = g_new0(OMAPSPIBusState, s->buscount);
624
for (i = 0; i < s->buscount; i++) {
626
if (s->mpu_model < omap3430) {
627
bs->revision = SPI_REV_OMAP2420;
628
bs->chnum = i ? 2 : 4;
630
bs->revision = SPI_REV_OMAP3430;
631
bs->chnum = (i > 2) ? 1 : (i ? 2 : 4);
633
sysbus_init_irq(busdev, &bs->irq);
634
bs->bus = spi_init_bus(&busdev->qdev, NULL, bs->chnum);
635
bs->ch = g_new0(struct omap_mcspi_ch_s, bs->chnum);
636
for (j = 0; j < bs->chnum; j++) {
637
sysbus_init_irq(busdev, &bs->ch[j].txdrq);
638
sysbus_init_irq(busdev, &bs->ch[j].rxdrq);
640
memory_region_init_io(&bs->iomem, &omap_mcspi_ops, bs, "omap.mcspi",
642
sysbus_init_mmio(busdev, &bs->iomem);
647
SPIBus *omap_mcspi_bus(DeviceState *qdev, int bus_number)
649
OMAPSPIState *s = FROM_SYSBUS(OMAPSPIState, SYS_BUS_DEVICE(qdev));
650
if (bus_number < s->buscount) {
651
return s->bus[bus_number].bus;
653
hw_error("%s: invalid bus number %d\n", __FUNCTION__, bus_number);
656
static Property omap_mcspi_properties[] = {
657
DEFINE_PROP_INT32("mpu_model", OMAPSPIState, mpu_model, 0),
658
DEFINE_PROP_END_OF_LIST()
661
static void omap_mcspi_class_init(ObjectClass *klass, void *data)
663
DeviceClass *dc = DEVICE_CLASS(klass);
664
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
665
k->init = omap_mcspi_init;
666
dc->props = omap_mcspi_properties;
667
dc->reset = omap_mcspi_reset;
670
static TypeInfo omap_mcspi_info = {
671
.name = "omap_mcspi",
672
.parent = TYPE_SYS_BUS_DEVICE,
673
.instance_size = sizeof(OMAPSPIState),
674
.class_init = omap_mcspi_class_init,
677
static void omap_mcspi_register_types(void)
679
type_register_static(&omap_mcspi_info);
682
type_init(omap_mcspi_register_types)