Statistics
| Branch: | Revision:

root / hw / omap_spi.c @ 0d09e41a

History | View | Annotate | Download (10.3 kB)

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) any later version 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/hw.h"
23
#include "hw/arm/omap.h"
24

    
25
/* Multichannel SPI */
26
struct omap_mcspi_s {
27
    MemoryRegion iomem;
28
    qemu_irq irq;
29
    int chnum;
30

    
31
    uint32_t sysconfig;
32
    uint32_t systest;
33
    uint32_t irqst;
34
    uint32_t irqen;
35
    uint32_t wken;
36
    uint32_t control;
37

    
38
    struct omap_mcspi_ch_s {
39
        qemu_irq txdrq;
40
        qemu_irq rxdrq;
41
        uint32_t (*txrx)(void *opaque, uint32_t, int);
42
        void *opaque;
43

    
44
        uint32_t tx;
45
        uint32_t rx;
46

    
47
        uint32_t config;
48
        uint32_t status;
49
        uint32_t control;
50
    } ch[4];
51
};
52

    
53
static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
54
{
55
    qemu_set_irq(s->irq, s->irqst & s->irqen);
56
}
57

    
58
static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
59
{
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 */
70
}
71

    
72
static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
73
{
74
    struct omap_mcspi_ch_s *ch = s->ch + chnum;
75

    
76
    if (!(ch->control & 1))                                /* EN */
77
        return;
78
    if ((ch->status & (1 << 0)) &&                        /* RXS */
79
                    ((ch->config >> 12) & 3) != 2 &&        /* TRM */
80
                    !(ch->config & (1 << 19)))                /* TURBO */
81
        goto intr_update;
82
    if ((ch->status & (1 << 1)) &&                        /* TXS */
83
                    ((ch->config >> 12) & 3) != 1)        /* TRM */
84
        goto intr_update;
85

    
86
    if (!(s->control & 1) ||                                /* SINGLE */
87
                    (ch->config & (1 << 20))) {                /* FORCE */
88
        if (ch->txrx)
89
            ch->rx = ch->txrx(ch->opaque, ch->tx,        /* WL */
90
                            1 + (0x1f & (ch->config >> 7)));
91
    }
92

    
93
    ch->tx = 0;
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 */
98

    
99
intr_update:
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);
109
}
110

    
111
void omap_mcspi_reset(struct omap_mcspi_s *s)
112
{
113
    int ch;
114

    
115
    s->sysconfig = 0;
116
    s->systest = 0;
117
    s->irqst = 0;
118
    s->irqen = 0;
119
    s->wken = 0;
120
    s->control = 4;
121

    
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;
126

    
127
        omap_mcspi_dmarequest_update(s->ch + ch);
128
    }
129

    
130
    omap_mcspi_interrupt_update(s);
131
}
132

    
133
static uint64_t omap_mcspi_read(void *opaque, hwaddr addr,
134
                                unsigned size)
135
{
136
    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
137
    int ch = 0;
138
    uint32_t ret;
139

    
140
    if (size != 4) {
141
        return omap_badwidth_read32(opaque, addr);
142
    }
143

    
144
    switch (addr) {
145
    case 0x00:        /* MCSPI_REVISION */
146
        return 0x91;
147

    
148
    case 0x10:        /* MCSPI_SYSCONFIG */
149
        return s->sysconfig;
150

    
151
    case 0x14:        /* MCSPI_SYSSTATUS */
152
        return 1;                                        /* RESETDONE */
153

    
154
    case 0x18:        /* MCSPI_IRQSTATUS */
155
        return s->irqst;
156

    
157
    case 0x1c:        /* MCSPI_IRQENABLE */
158
        return s->irqen;
159

    
160
    case 0x20:        /* MCSPI_WAKEUPENABLE */
161
        return s->wken;
162

    
163
    case 0x24:        /* MCSPI_SYST */
164
        return s->systest;
165

    
166
    case 0x28:        /* MCSPI_MODULCTRL */
167
        return s->control;
168

    
169
    case 0x68: ch ++;
170
        /* fall through */
171
    case 0x54: ch ++;
172
        /* fall through */
173
    case 0x40: ch ++;
174
        /* fall through */
175
    case 0x2c:        /* MCSPI_CHCONF */
176
        return s->ch[ch].config;
177

    
178
    case 0x6c: ch ++;
179
        /* fall through */
180
    case 0x58: ch ++;
181
        /* fall through */
182
    case 0x44: ch ++;
183
        /* fall through */
184
    case 0x30:        /* MCSPI_CHSTAT */
185
        return s->ch[ch].status;
186

    
187
    case 0x70: ch ++;
188
        /* fall through */
189
    case 0x5c: ch ++;
190
        /* fall through */
191
    case 0x48: ch ++;
192
        /* fall through */
193
    case 0x34:        /* MCSPI_CHCTRL */
194
        return s->ch[ch].control;
195

    
196
    case 0x74: ch ++;
197
        /* fall through */
198
    case 0x60: ch ++;
199
        /* fall through */
200
    case 0x4c: ch ++;
201
        /* fall through */
202
    case 0x38:        /* MCSPI_TX */
203
        return s->ch[ch].tx;
204

    
205
    case 0x78: ch ++;
206
        /* fall through */
207
    case 0x64: ch ++;
208
        /* fall through */
209
    case 0x50: ch ++;
210
        /* fall through */
211
    case 0x3c:        /* MCSPI_RX */
212
        s->ch[ch].status &= ~(1 << 0);                        /* RXS */
213
        ret = s->ch[ch].rx;
214
        omap_mcspi_transfer_run(s, ch);
215
        return ret;
216
    }
217

    
218
    OMAP_BAD_REG(addr);
219
    return 0;
220
}
221

    
222
static void omap_mcspi_write(void *opaque, hwaddr addr,
223
                             uint64_t value, unsigned size)
224
{
225
    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
226
    int ch = 0;
227

    
228
    if (size != 4) {
229
        return omap_badwidth_write32(opaque, addr, value);
230
    }
231

    
232
    switch (addr) {
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 */
243
        OMAP_RO_REG(addr);
244
        return;
245

    
246
    case 0x10:        /* MCSPI_SYSCONFIG */
247
        if (value & (1 << 1))                                /* SOFTRESET */
248
            omap_mcspi_reset(s);
249
        s->sysconfig = value & 0x31d;
250
        break;
251

    
252
    case 0x18:        /* MCSPI_IRQSTATUS */
253
        if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
254
            s->irqst &= ~value;
255
            omap_mcspi_interrupt_update(s);
256
        }
257
        break;
258

    
259
    case 0x1c:        /* MCSPI_IRQENABLE */
260
        s->irqen = value & 0x1777f;
261
        omap_mcspi_interrupt_update(s);
262
        break;
263

    
264
    case 0x20:        /* MCSPI_WAKEUPENABLE */
265
        s->wken = value & 1;
266
        break;
267

    
268
    case 0x24:        /* MCSPI_SYST */
269
        if (s->control & (1 << 3))                        /* SYSTEM_TEST */
270
            if (value & (1 << 11)) {                        /* SSB */
271
                s->irqst |= 0x1777f;
272
                omap_mcspi_interrupt_update(s);
273
            }
274
        s->systest = value & 0xfff;
275
        break;
276

    
277
    case 0x28:        /* MCSPI_MODULCTRL */
278
        if (value & (1 << 3))                                /* SYSTEM_TEST */
279
            if (s->systest & (1 << 11)) {                /* SSB */
280
                s->irqst |= 0x1777f;
281
                omap_mcspi_interrupt_update(s);
282
            }
283
        s->control = value & 0xf;
284
        break;
285

    
286
    case 0x68: ch ++;
287
        /* fall through */
288
    case 0x54: ch ++;
289
        /* fall through */
290
    case 0x40: ch ++;
291
        /* fall through */
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;
301
        break;
302

    
303
    case 0x70: ch ++;
304
        /* fall through */
305
    case 0x5c: ch ++;
306
        /* fall through */
307
    case 0x48: ch ++;
308
        /* fall through */
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);
313
        } else
314
            s->ch[ch].control = value & 1;
315
        break;
316

    
317
    case 0x74: ch ++;
318
        /* fall through */
319
    case 0x60: ch ++;
320
        /* fall through */
321
    case 0x4c: ch ++;
322
        /* fall through */
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);
327
        break;
328

    
329
    default:
330
        OMAP_BAD_REG(addr);
331
        return;
332
    }
333
}
334

    
335
static const MemoryRegionOps omap_mcspi_ops = {
336
    .read = omap_mcspi_read,
337
    .write = omap_mcspi_write,
338
    .endianness = DEVICE_NATIVE_ENDIAN,
339
};
340

    
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)
343
{
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;
347

    
348
    s->irq = irq;
349
    s->chnum = chnum;
350
    while (chnum --) {
351
        ch->txdrq = *drq ++;
352
        ch->rxdrq = *drq ++;
353
        ch ++;
354
    }
355
    omap_mcspi_reset(s);
356

    
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);
360

    
361
    return s;
362
}
363

    
364
void omap_mcspi_attach(struct omap_mcspi_s *s,
365
                uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
366
                int chipselect)
367
{
368
    if (chipselect < 0 || chipselect >= s->chnum)
369
        hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
370

    
371
    s->ch[chipselect].txrx = txrx;
372
    s->ch[chipselect].opaque = opaque;
373
}