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"
25
/* Multichannel SPI */
38
struct omap_mcspi_ch_s {
41
uint32_t (*txrx)(void *opaque, uint32_t, int);
53
static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
55
qemu_set_irq(s->irq, s->irqst & s->irqen);
58
static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
60
qemu_set_irq(ch->txdrq,
61
(ch->control & 1) && /* EN */
62
(ch->config & (1 << 14)) && /* DMAW */
63
(ch->status & (1 << 1)) && /* TXS */
64
((ch->config >> 12) & 3) != 1); /* TRM */
65
qemu_set_irq(ch->rxdrq,
66
(ch->control & 1) && /* EN */
67
(ch->config & (1 << 15)) && /* DMAW */
68
(ch->status & (1 << 0)) && /* RXS */
69
((ch->config >> 12) & 3) != 2); /* TRM */
72
static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
74
struct omap_mcspi_ch_s *ch = s->ch + chnum;
76
if (!(ch->control & 1)) /* EN */
78
if ((ch->status & (1 << 0)) && /* RXS */
79
((ch->config >> 12) & 3) != 2 && /* TRM */
80
!(ch->config & (1 << 19))) /* TURBO */
82
if ((ch->status & (1 << 1)) && /* TXS */
83
((ch->config >> 12) & 3) != 1) /* TRM */
86
if (!(s->control & 1) || /* SINGLE */
87
(ch->config & (1 << 20))) { /* FORCE */
89
ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */
90
1 + (0x1f & (ch->config >> 7)));
94
ch->status |= 1 << 2; /* EOT */
95
ch->status |= 1 << 1; /* TXS */
96
if (((ch->config >> 12) & 3) != 2) /* TRM */
97
ch->status |= 1 << 0; /* RXS */
100
if ((ch->status & (1 << 0)) && /* RXS */
101
((ch->config >> 12) & 3) != 2 && /* TRM */
102
!(ch->config & (1 << 19))) /* TURBO */
103
s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */
104
if ((ch->status & (1 << 1)) && /* TXS */
105
((ch->config >> 12) & 3) != 1) /* TRM */
106
s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */
107
omap_mcspi_interrupt_update(s);
108
omap_mcspi_dmarequest_update(ch);
111
void omap_mcspi_reset(struct omap_mcspi_s *s)
122
for (ch = 0; ch < 4; ch ++) {
123
s->ch[ch].config = 0x060000;
124
s->ch[ch].status = 2; /* TXS */
125
s->ch[ch].control = 0;
127
omap_mcspi_dmarequest_update(s->ch + ch);
130
omap_mcspi_interrupt_update(s);
133
static uint64_t omap_mcspi_read(void *opaque, hwaddr addr,
136
struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
141
return omap_badwidth_read32(opaque, addr);
145
case 0x00: /* MCSPI_REVISION */
148
case 0x10: /* MCSPI_SYSCONFIG */
151
case 0x14: /* MCSPI_SYSSTATUS */
152
return 1; /* RESETDONE */
154
case 0x18: /* MCSPI_IRQSTATUS */
157
case 0x1c: /* MCSPI_IRQENABLE */
160
case 0x20: /* MCSPI_WAKEUPENABLE */
163
case 0x24: /* MCSPI_SYST */
166
case 0x28: /* MCSPI_MODULCTRL */
175
case 0x2c: /* MCSPI_CHCONF */
176
return s->ch[ch].config;
184
case 0x30: /* MCSPI_CHSTAT */
185
return s->ch[ch].status;
193
case 0x34: /* MCSPI_CHCTRL */
194
return s->ch[ch].control;
202
case 0x38: /* MCSPI_TX */
211
case 0x3c: /* MCSPI_RX */
212
s->ch[ch].status &= ~(1 << 0); /* RXS */
214
omap_mcspi_transfer_run(s, ch);
222
static void omap_mcspi_write(void *opaque, hwaddr addr,
223
uint64_t value, unsigned size)
225
struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
229
return omap_badwidth_write32(opaque, addr, value);
233
case 0x00: /* MCSPI_REVISION */
234
case 0x14: /* MCSPI_SYSSTATUS */
235
case 0x30: /* MCSPI_CHSTAT0 */
236
case 0x3c: /* MCSPI_RX0 */
237
case 0x44: /* MCSPI_CHSTAT1 */
238
case 0x50: /* MCSPI_RX1 */
239
case 0x58: /* MCSPI_CHSTAT2 */
240
case 0x64: /* MCSPI_RX2 */
241
case 0x6c: /* MCSPI_CHSTAT3 */
242
case 0x78: /* MCSPI_RX3 */
246
case 0x10: /* MCSPI_SYSCONFIG */
247
if (value & (1 << 1)) /* SOFTRESET */
249
s->sysconfig = value & 0x31d;
252
case 0x18: /* MCSPI_IRQSTATUS */
253
if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
255
omap_mcspi_interrupt_update(s);
259
case 0x1c: /* MCSPI_IRQENABLE */
260
s->irqen = value & 0x1777f;
261
omap_mcspi_interrupt_update(s);
264
case 0x20: /* MCSPI_WAKEUPENABLE */
268
case 0x24: /* MCSPI_SYST */
269
if (s->control & (1 << 3)) /* SYSTEM_TEST */
270
if (value & (1 << 11)) { /* SSB */
272
omap_mcspi_interrupt_update(s);
274
s->systest = value & 0xfff;
277
case 0x28: /* MCSPI_MODULCTRL */
278
if (value & (1 << 3)) /* SYSTEM_TEST */
279
if (s->systest & (1 << 11)) { /* SSB */
281
omap_mcspi_interrupt_update(s);
283
s->control = value & 0xf;
292
case 0x2c: /* MCSPI_CHCONF */
293
if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */
294
omap_mcspi_dmarequest_update(s->ch + ch);
295
if (((value >> 12) & 3) == 3) /* TRM */
296
fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__);
297
if (((value >> 7) & 0x1f) < 3) /* WL */
298
fprintf(stderr, "%s: invalid WL value (%" PRIx64 ")\n",
299
__FUNCTION__, (value >> 7) & 0x1f);
300
s->ch[ch].config = value & 0x7fffff;
309
case 0x34: /* MCSPI_CHCTRL */
310
if (value & ~s->ch[ch].control & 1) { /* EN */
311
s->ch[ch].control |= 1;
312
omap_mcspi_transfer_run(s, ch);
314
s->ch[ch].control = value & 1;
323
case 0x38: /* MCSPI_TX */
324
s->ch[ch].tx = value;
325
s->ch[ch].status &= ~(1 << 1); /* TXS */
326
omap_mcspi_transfer_run(s, ch);
335
static const MemoryRegionOps omap_mcspi_ops = {
336
.read = omap_mcspi_read,
337
.write = omap_mcspi_write,
338
.endianness = DEVICE_NATIVE_ENDIAN,
341
struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
342
qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
344
struct omap_mcspi_s *s = (struct omap_mcspi_s *)
345
g_malloc0(sizeof(struct omap_mcspi_s));
346
struct omap_mcspi_ch_s *ch = s->ch;
357
memory_region_init_io(&s->iomem, &omap_mcspi_ops, s, "omap.mcspi",
358
omap_l4_region_size(ta, 0));
359
omap_l4_attach(ta, 0, &s->iomem);
364
void omap_mcspi_attach(struct omap_mcspi_s *s,
365
uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
368
if (chipselect < 0 || chipselect >= s->chnum)
369
hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
371
s->ch[chipselect].txrx = txrx;
372
s->ch[chipselect].opaque = opaque;