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 TYPE_OMAP_MCSPI "omap_mcspi"
37
#define OMAP_MCSPI(obj) OBJECT_CHECK(OMAPSPIState, (obj), TYPE_OMAP_MCSPI)
39
#define SPI_FIFOSIZE 64
40
#define SPI_REV_OMAP2420 0x14
41
#define SPI_REV_OMAP3430 0x21
42
#define IS_OMAP3_SPI(s) ((s)->revision >= SPI_REV_OMAP3430)
44
typedef struct omap_mcspi_bus_s {
59
struct omap_mcspi_fifo_s {
63
uint8_t buf[SPI_FIFOSIZE];
68
struct omap_mcspi_ch_s {
81
typedef struct omap_mcspi_s {
82
SysBusDevice parent_obj;
88
static inline void omap_mcspi_interrupt_update(OMAPSPIBusState *s)
90
qemu_set_irq(s->irq, s->irqst & s->irqen);
93
static inline void omap_mcspi_dmarequest_update(OMAPSPIBusState *s,
96
struct omap_mcspi_ch_s *ch = &s->ch[chnum];
97
if ((ch->control & 1) && /* EN */
98
(ch->config & (1 << 14)) && /* DMAW */
99
(ch->status & (1 << 1)) && /* TXS */
100
((ch->config >> 12) & 3) != 1) { /* TRM */
101
if (!IS_OMAP3_SPI(s) ||
102
!(ch->config & (1 << 27)) || /* FFEW */
103
s->tx_fifo.len <= (s->xferlevel & 0x3f)) /* AEL */
104
qemu_irq_raise(ch->txdrq);
106
qemu_irq_lower(ch->txdrq);
108
if ((ch->control & 1) && /* EN */
109
(ch->config & (1 << 15)) && /* DMAW */
110
(ch->status & (1 << 0)) && /* RXS */
111
((ch->config >> 12) & 3) != 2) { /* TRM */
112
if (!IS_OMAP3_SPI(s) ||
113
!(ch->config & (1 << 28)) || /* FFER */
114
s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
115
qemu_irq_raise(ch->rxdrq);
117
qemu_irq_lower(ch->rxdrq);
121
static void omap_mcspi_fifo_reset(OMAPSPIBusState *s)
123
struct omap_mcspi_ch_s *ch;
127
s->tx_fifo.start = 0;
128
s->rx_fifo.start = 0;
129
if (s->fifo_ch < 0) {
130
s->tx_fifo.size = s->rx_fifo.size = 0;
132
ch = &s->ch[s->fifo_ch];
133
s->tx_fifo.size = ((ch->config >> 27) & 1) ? SPI_FIFOSIZE : 0;
134
s->rx_fifo.size = ((ch->config >> 28) & 1) ? SPI_FIFOSIZE : 0;
135
if (((ch->config >> 27) & 3) == 3) {
136
s->tx_fifo.size >>= 1;
137
s->rx_fifo.size >>= 1;
142
/* returns next word in FIFO or the n first bytes if there is not
143
* enough data in FIFO */
144
static uint32_t omap_mcspi_fifo_get(struct omap_mcspi_fifo_s *s, int wl)
148
for (v = 0, sh = 0; wl > 0 && s->len; wl -= 8, s->len--, sh += 8) {
149
v |= ((uint32_t)s->buf[s->start++]) << sh;
150
if (s->start >= s->size)
156
/* pushes a word to FIFO or the first n bytes of the word if the FIFO
157
* is too full to hold the full word */
158
static void omap_mcspi_fifo_put(struct omap_mcspi_fifo_s *s, int wl,
161
int p = s->start + s->len;
163
for (; wl > 0 && s->len < s->size; wl -=8, v >>= 8, s->len++) {
166
s->buf[p++] = (uint8_t)(v & 0xff);
170
static void omap_mcspi_transfer_run(OMAPSPIBusState *s, int chnum)
172
struct omap_mcspi_ch_s *ch = s->ch + chnum;
173
int trm = (ch->config >> 12) & 3;
176
if (!(ch->control & 1)) /* EN */
178
if ((ch->status & 1) && trm != 2 && /* RXS */
179
!(ch->config & (1 << 19))) /* TURBO */
181
if ((ch->status & (1 << 1)) && trm != 1) /* TXS */
184
if (!(s->control & 1) || /* SINGLE */
185
(ch->config & (1 << 20))) { /* FORCE */
186
wl = 1 + (0x1f & (ch->config >> 7)); /* WL */
187
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
188
!((ch->config >> 27) & 3)) { /* FFER | FFEW */
189
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
191
switch ((ch->config >> 27) & 3) {
192
case 1: /* !FFER, FFEW */
194
ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
195
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
198
case 2: /* FFER, !FFEW */
199
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
201
omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
204
case 3: /* FFER, FFEW */
205
while (s->rx_fifo.len < s->rx_fifo.size &&
206
s->tx_fifo.len && s->fifo_wcnt) {
208
ch->tx = omap_mcspi_fifo_get(&s->tx_fifo, wl);
209
ch->rx = spi_txrx(s->bus, chnum, ch->tx, wl);
211
omap_mcspi_fifo_put(&s->rx_fifo, wl, ch->rx);
218
if ((ch->config & (1 << 28)) && /* FFER */
219
s->rx_fifo.len >= s->rx_fifo.size)
220
ch->status |= 1 << 6; /* RXFFF */
221
ch->status &= ~(1 << 5); /* RXFFE */
222
ch->status &= ~(1 << 4); /* TXFFF */
223
if ((ch->config & (1 << 27)) && /* FFEW */
225
ch->status |= 1 << 3; /* TXFFE */
227
((s->xferlevel >> 16) & 0xffff)) /* WCNT */
228
s->irqst |= 1 << 17; /* EOW */
233
ch->status |= 1 << 2; /* EOT */
234
ch->status |= 1 << 1; /* TXS */
236
ch->status |= 1; /* RXS */
239
if ((ch->status & 1) && trm != 2 && /* RXS */
240
!(ch->config & (1 << 19))) /* TURBO */
241
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
242
!((ch->config >> 28) & 1) || /* FFER */
243
s->rx_fifo.len >= ((s->xferlevel >> 8) & 0x3f)) /* AFL */
244
s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */
245
if ((ch->status & (1 << 1)) && trm != 1) /* TXS */
246
if (!IS_OMAP3_SPI(s) || s->fifo_ch != chnum ||
247
!((ch->config >> 27) & 1) || /* FFEW */
248
s->tx_fifo.len <= (s->xferlevel & 0x3f)) /* AEL */
249
s->irqst |= 1 << (4 * chnum); /* TX_EMPTY */
250
omap_mcspi_interrupt_update(s);
251
omap_mcspi_dmarequest_update(s, chnum);
254
static void omap_mcspi_bus_reset(OMAPSPIBusState *s)
266
omap_mcspi_fifo_reset(s);
268
for (ch = 0; ch < s->chnum; ch ++) {
269
s->ch[ch].config = 0x060000;
270
s->ch[ch].status = 2; /* TXS */
271
s->ch[ch].control = 0;
273
omap_mcspi_dmarequest_update(s, ch);
276
omap_mcspi_interrupt_update(s);
279
static uint64_t omap_mcspi_read(void *opaque, hwaddr addr,
282
OMAPSPIBusState *s = (OMAPSPIBusState *) opaque;
287
return omap_badwidth_read32(opaque, addr);
291
case 0x00: /* MCSPI_REVISION */
292
TRACE("REVISION = 0x%08x", s->revision);
295
case 0x10: /* MCSPI_SYSCONFIG */
296
TRACE("SYSCONFIG = 0x%08x", s->sysconfig);
299
case 0x14: /* MCSPI_SYSSTATUS */
300
TRACE("SYSSTATUS = 0x00000001");
301
return 1; /* RESETDONE */
303
case 0x18: /* MCSPI_IRQSTATUS */
304
TRACE("IRQSTATUS = 0x%08x", s->irqst);
307
case 0x1c: /* MCSPI_IRQENABLE */
308
TRACE("IRQENABLE = 0x%08x", s->irqen);
311
case 0x20: /* MCSPI_WAKEUPENABLE */
312
TRACE("WAKEUPENABLE = 0x%08x", s->wken);
315
case 0x24: /* MCSPI_SYST */
316
TRACE("SYST = 0x%08x", s->systest);
319
case 0x28: /* MCSPI_MODULCTRL */
320
TRACE("MODULCTRL = 0x%08x", s->control);
329
case 0x2c: /* MCSPI_CHCONF */
330
TRACE("CHCONF%d = 0x%08x", ch,
331
(ch < s->chnum) ? s->ch[ch].config : 0);
332
return (ch < s->chnum) ? s->ch[ch].config : 0;
340
case 0x30: /* MCSPI_CHSTAT */
341
TRACE("CHSTAT%d = 0x%08x", ch,
342
(ch < s->chnum) ? s->ch[ch].status : 0);
343
return (ch < s->chnum) ? s->ch[ch].status : 0;
351
case 0x34: /* MCSPI_CHCTRL */
352
TRACE("CHCTRL%d = 0x%08x", ch,
353
(ch < s->chnum) ? s->ch[ch].control : 0);
354
return (ch < s->chnum) ? s->ch[ch].control : 0;
362
case 0x38: /* MCSPI_TX */
363
TRACE("TX%d = 0x%08x", ch,
364
(ch < s->chnum) ? s->ch[ch].tx : 0);
365
return (ch < s->chnum) ? s->ch[ch].tx : 0;
373
case 0x3c: /* MCSPI_RX */
375
if (!IS_OMAP3_SPI(s) || ch != s->fifo_ch ||
376
!(s->ch[ch].config & (1 << 28))) { /* FFER */
377
s->ch[ch].status &= ~1; /* RXS */
379
TRACE("RX%d = 0x%08x", ch, ret);
380
omap_mcspi_transfer_run(s, ch);
383
if (!s->rx_fifo.len) {
384
TRACE("rxfifo underflow!");
386
qemu_irq_lower(s->ch[ch].rxdrq);
387
s->ch[ch].status &= ~(1 << 6); /* RXFFF */
388
if (((s->ch[ch].config >> 12) & 3) != 2) /* TRM */
389
ret = omap_mcspi_fifo_get(&s->rx_fifo,
390
1 + ((s->ch[ch].config >> 7) & 0x1f)); /* WL */
393
TRACE("RX%d = 0x%08x", ch, ret);
394
if (!s->rx_fifo.len) {
395
s->ch[ch].status &= ~1; /* RXS */
396
s->ch[ch].status |= 1 << 5; /* RXFFE */
397
omap_mcspi_transfer_run(s, ch);
402
TRACE("RX%d = 0x00000000", ch);
405
case 0x7c: /* MCSPI_XFERLEVEL */
406
if (IS_OMAP3_SPI(s)) {
407
if ((s->xferlevel >> 16) & 0xffff) /* WCNT */
408
ret = ((s->xferlevel & 0xffff0000) - (s->fifo_wcnt << 16));
410
ret = ((-s->fifo_wcnt) & 0xffff) << 16;
411
TRACE("XFERLEVEL = 0x%08x", (s->xferlevel & 0xffff) | ret);
412
return (s->xferlevel & 0xffff) | ret;
424
static void omap_mcspi_write(void *opaque, hwaddr addr,
425
uint64_t value, unsigned size)
427
OMAPSPIBusState *s = (OMAPSPIBusState *) opaque;
432
return omap_badwidth_write32(opaque, addr, value);
436
case 0x00: /* MCSPI_REVISION */
437
case 0x14: /* MCSPI_SYSSTATUS */
438
case 0x30: /* MCSPI_CHSTAT0 */
439
case 0x3c: /* MCSPI_RX0 */
440
case 0x44: /* MCSPI_CHSTAT1 */
441
case 0x50: /* MCSPI_RX1 */
442
case 0x58: /* MCSPI_CHSTAT2 */
443
case 0x64: /* MCSPI_RX2 */
444
case 0x6c: /* MCSPI_CHSTAT3 */
445
case 0x78: /* MCSPI_RX3 */
446
/* silently ignore */
447
//OMAP_RO_REGV(addr, value);
450
case 0x10: /* MCSPI_SYSCONFIG */
451
TRACE("SYSCONFIG = 0x%08x", value);
452
if (value & (1 << 1)) /* SOFTRESET */
453
omap_mcspi_bus_reset(s);
454
s->sysconfig = value & 0x31d;
457
case 0x18: /* MCSPI_IRQSTATUS */
458
TRACE("IRQSTATUS = 0x%08x", value);
459
if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
461
omap_mcspi_interrupt_update(s);
465
case 0x1c: /* MCSPI_IRQENABLE */
466
TRACE("IRQENABLE = 0x%08x", value);
467
s->irqen = value & (IS_OMAP3_SPI(s) ? 0x3777f : 0x1777f);
468
omap_mcspi_interrupt_update(s);
471
case 0x20: /* MCSPI_WAKEUPENABLE */
472
TRACE("WAKEUPENABLE = 0x%08x", value);
476
case 0x24: /* MCSPI_SYST */
477
TRACE("SYST = 0x%08x", value);
478
if (s->control & (1 << 3)) /* SYSTEM_TEST */
479
if (value & (1 << 11)) { /* SSB */
481
omap_mcspi_interrupt_update(s);
483
s->systest = value & 0xfff;
486
case 0x28: /* MCSPI_MODULCTRL */
487
TRACE("MODULCTRL = 0x%08x", value);
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);
493
s->control = value & 0xf;
502
case 0x2c: /* MCSPI_CHCONF */
503
TRACE("CHCONF%d = 0x%08x", ch, value);
505
old = s->ch[ch].config;
506
s->ch[ch].config = value & (IS_OMAP3_SPI(s)
507
? 0x3fffffff : 0x7fffff);
508
if (IS_OMAP3_SPI(s) &&
509
((value ^ old) & (3 << 27))) { /* FFER | FFEW */
510
s->fifo_ch = ((value & (3 << 27))) ? ch : -1;
511
omap_mcspi_fifo_reset(s);
513
if (((value ^ old) & (3 << 14)) || /* DMAR | DMAW */
515
((value ^ old) & (3 << 27)))) /* FFER | FFEW */
516
omap_mcspi_dmarequest_update(s, ch);
517
if (((value >> 12) & 3) == 3) { /* TRM */
518
TRACE("invalid TRM value (3)");
520
if (((value >> 7) & 0x1f) < 3) { /* WL */
521
TRACE("invalid WL value (%" PRIx64 ")", (value >> 7) & 0x1f);
523
if (IS_OMAP3_SPI(s) && ((value >> 23) & 1)) { /* SBE */
524
TRACE("start-bit mode is not supported");
535
case 0x34: /* MCSPI_CHCTRL */
536
TRACE("CHCTRL%d = 0x%08x", ch, value);
538
old = s->ch[ch].control;
539
s->ch[ch].control = value & (IS_OMAP3_SPI(s) ? 0xff01 : 1);
540
if (value & ~old & 1) { /* EN */
541
if (IS_OMAP3_SPI(s) && s->fifo_ch == ch)
542
omap_mcspi_fifo_reset(s);
543
omap_mcspi_transfer_run(s, ch);
554
case 0x38: /* MCSPI_TX */
555
TRACE("TX%d = 0x%08x", ch, value);
557
if (!IS_OMAP3_SPI(s) || s->fifo_ch != ch ||
558
!(s->ch[ch].config & (1 << 27))) { /* FFEW */
559
s->ch[ch].tx = value;
560
s->ch[ch].status &= ~0x06; /* EOT | TXS */
561
omap_mcspi_transfer_run(s, ch);
563
if (s->tx_fifo.len >= s->tx_fifo.size) {
564
TRACE("txfifo overflow!");
566
qemu_irq_lower(s->ch[ch].txdrq);
567
s->ch[ch].status &= ~0x0e; /* TXFFE | EOT | TXS */
568
if (((s->ch[ch].config >> 12) & 3) != 1) { /* TRM */
571
1 + ((s->ch[ch].config >> 7) & 0x1f), /* WL */
573
if (s->tx_fifo.len >= s->tx_fifo.size)
574
s->ch[ch].status |= 1 << 4; /* TXFFF */
575
if (s->tx_fifo.len >= (s->xferlevel & 0x3f))
576
omap_mcspi_transfer_run(s, ch);
578
s->ch[ch].tx = value;
579
omap_mcspi_transfer_run(s, ch);
586
case 0x7c: /* MCSPI_XFERLEVEL */
587
TRACE("XFERLEVEL = 0x%08x", value);
588
if (IS_OMAP3_SPI(s)) {
589
if (value != s->xferlevel) {
590
s->fifo_wcnt = (value >> 16) & 0xffff;
591
s->xferlevel = value & 0xffff3f3f;
592
omap_mcspi_fifo_reset(s);
604
static const MemoryRegionOps omap_mcspi_ops = {
605
.read = omap_mcspi_read,
606
.write = omap_mcspi_write,
607
.endianness = DEVICE_NATIVE_ENDIAN,
610
static void omap_mcspi_reset(DeviceState *qdev)
613
OMAPSPIState *s = OMAP_MCSPI(qdev);
614
for (i = 0; i < s->buscount; i++) {
615
omap_mcspi_bus_reset(&s->bus[i]);
619
static int omap_mcspi_init(SysBusDevice *sbd)
623
OMAPSPIState *s = OMAP_MCSPI(sbd);
625
s->buscount = (s->mpu_model < omap3430) ? 2 : 4;
626
s->bus = g_new0(OMAPSPIBusState, s->buscount);
627
for (i = 0; i < s->buscount; i++) {
629
if (s->mpu_model < omap3430) {
630
bs->revision = SPI_REV_OMAP2420;
631
bs->chnum = i ? 2 : 4;
633
bs->revision = SPI_REV_OMAP3430;
634
bs->chnum = (i > 2) ? 1 : (i ? 2 : 4);
636
sysbus_init_irq(sbd, &bs->irq);
637
bs->bus = spi_init_bus(DEVICE(sbd), NULL, bs->chnum);
638
bs->ch = g_new0(struct omap_mcspi_ch_s, bs->chnum);
639
for (j = 0; j < bs->chnum; j++) {
640
sysbus_init_irq(sbd, &bs->ch[j].txdrq);
641
sysbus_init_irq(sbd, &bs->ch[j].rxdrq);
643
memory_region_init_io(&bs->iomem, NULL,
644
&omap_mcspi_ops, bs, "omap.mcspi",
646
sysbus_init_mmio(sbd, &bs->iomem);
651
SPIBus *omap_mcspi_bus(DeviceState *qdev, int bus_number)
653
OMAPSPIState *s = OMAP_MCSPI(qdev);
655
if (bus_number < s->buscount) {
656
return s->bus[bus_number].bus;
658
hw_error("%s: invalid bus number %d\n", __FUNCTION__, bus_number);
661
static Property omap_mcspi_properties[] = {
662
DEFINE_PROP_INT32("mpu_model", OMAPSPIState, mpu_model, 0),
663
DEFINE_PROP_END_OF_LIST()
666
static void omap_mcspi_class_init(ObjectClass *klass, void *data)
668
DeviceClass *dc = DEVICE_CLASS(klass);
669
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
670
k->init = omap_mcspi_init;
671
dc->props = omap_mcspi_properties;
672
dc->reset = omap_mcspi_reset;
675
static TypeInfo omap_mcspi_info = {
676
.name = TYPE_OMAP_MCSPI,
677
.parent = TYPE_SYS_BUS_DEVICE,
678
.instance_size = sizeof(OMAPSPIState),
679
.class_init = omap_mcspi_class_init,
682
static void omap_mcspi_register_types(void)
684
type_register_static(&omap_mcspi_info);
687
type_init(omap_mcspi_register_types)