Statistics
| Branch: | Revision:

root / hw / omap_spi.c @ 17786d52

History | View | Annotate | Download (9.5 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.h"
23
#include "omap.h"
24

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

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

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

    
43
        uint32_t tx;
44
        uint32_t rx;
45

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

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

    
57
static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
58
{
59
    qemu_set_irq(ch->txdrq,
60
                    (ch->control & 1) &&                /* EN */
61
                    (ch->config & (1 << 14)) &&                /* DMAW */
62
                    (ch->status & (1 << 1)) &&                /* TXS */
63
                    ((ch->config >> 12) & 3) != 1);        /* TRM */
64
    qemu_set_irq(ch->rxdrq,
65
                    (ch->control & 1) &&                /* EN */
66
                    (ch->config & (1 << 15)) &&                /* DMAW */
67
                    (ch->status & (1 << 0)) &&                /* RXS */
68
                    ((ch->config >> 12) & 3) != 2);        /* TRM */
69
}
70

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

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

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

    
92
    ch->tx = 0;
93
    ch->status |= 1 << 2;                                /* EOT */
94
    ch->status |= 1 << 1;                                /* TXS */
95
    if (((ch->config >> 12) & 3) != 2)                        /* TRM */
96
        ch->status |= 1 << 0;                                /* RXS */
97

    
98
intr_update:
99
    if ((ch->status & (1 << 0)) &&                        /* RXS */
100
                    ((ch->config >> 12) & 3) != 2 &&        /* TRM */
101
                    !(ch->config & (1 << 19)))                /* TURBO */
102
        s->irqst |= 1 << (2 + 4 * chnum);                /* RX_FULL */
103
    if ((ch->status & (1 << 1)) &&                        /* TXS */
104
                    ((ch->config >> 12) & 3) != 1)        /* TRM */
105
        s->irqst |= 1 << (0 + 4 * chnum);                /* TX_EMPTY */
106
    omap_mcspi_interrupt_update(s);
107
    omap_mcspi_dmarequest_update(ch);
108
}
109

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

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

    
121
    for (ch = 0; ch < 4; ch ++) {
122
        s->ch[ch].config = 0x060000;
123
        s->ch[ch].status = 2;                                /* TXS */
124
        s->ch[ch].control = 0;
125

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

    
129
    omap_mcspi_interrupt_update(s);
130
}
131

    
132
static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr)
133
{
134
    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
135
    int ch = 0;
136
    uint32_t ret;
137

    
138
    switch (addr) {
139
    case 0x00:        /* MCSPI_REVISION */
140
        return 0x91;
141

    
142
    case 0x10:        /* MCSPI_SYSCONFIG */
143
        return s->sysconfig;
144

    
145
    case 0x14:        /* MCSPI_SYSSTATUS */
146
        return 1;                                        /* RESETDONE */
147

    
148
    case 0x18:        /* MCSPI_IRQSTATUS */
149
        return s->irqst;
150

    
151
    case 0x1c:        /* MCSPI_IRQENABLE */
152
        return s->irqen;
153

    
154
    case 0x20:        /* MCSPI_WAKEUPENABLE */
155
        return s->wken;
156

    
157
    case 0x24:        /* MCSPI_SYST */
158
        return s->systest;
159

    
160
    case 0x28:        /* MCSPI_MODULCTRL */
161
        return s->control;
162

    
163
    case 0x68: ch ++;
164
    case 0x54: ch ++;
165
    case 0x40: ch ++;
166
    case 0x2c:        /* MCSPI_CHCONF */
167
        return s->ch[ch].config;
168

    
169
    case 0x6c: ch ++;
170
    case 0x58: ch ++;
171
    case 0x44: ch ++;
172
    case 0x30:        /* MCSPI_CHSTAT */
173
        return s->ch[ch].status;
174

    
175
    case 0x70: ch ++;
176
    case 0x5c: ch ++;
177
    case 0x48: ch ++;
178
    case 0x34:        /* MCSPI_CHCTRL */
179
        return s->ch[ch].control;
180

    
181
    case 0x74: ch ++;
182
    case 0x60: ch ++;
183
    case 0x4c: ch ++;
184
    case 0x38:        /* MCSPI_TX */
185
        return s->ch[ch].tx;
186

    
187
    case 0x78: ch ++;
188
    case 0x64: ch ++;
189
    case 0x50: ch ++;
190
    case 0x3c:        /* MCSPI_RX */
191
        s->ch[ch].status &= ~(1 << 0);                        /* RXS */
192
        ret = s->ch[ch].rx;
193
        omap_mcspi_transfer_run(s, ch);
194
        return ret;
195
    }
196

    
197
    OMAP_BAD_REG(addr);
198
    return 0;
199
}
200

    
201
static void omap_mcspi_write(void *opaque, target_phys_addr_t addr,
202
                uint32_t value)
203
{
204
    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
205
    int ch = 0;
206

    
207
    switch (addr) {
208
    case 0x00:        /* MCSPI_REVISION */
209
    case 0x14:        /* MCSPI_SYSSTATUS */
210
    case 0x30:        /* MCSPI_CHSTAT0 */
211
    case 0x3c:        /* MCSPI_RX0 */
212
    case 0x44:        /* MCSPI_CHSTAT1 */
213
    case 0x50:        /* MCSPI_RX1 */
214
    case 0x58:        /* MCSPI_CHSTAT2 */
215
    case 0x64:        /* MCSPI_RX2 */
216
    case 0x6c:        /* MCSPI_CHSTAT3 */
217
    case 0x78:        /* MCSPI_RX3 */
218
        OMAP_RO_REG(addr);
219
        return;
220

    
221
    case 0x10:        /* MCSPI_SYSCONFIG */
222
        if (value & (1 << 1))                                /* SOFTRESET */
223
            omap_mcspi_reset(s);
224
        s->sysconfig = value & 0x31d;
225
        break;
226

    
227
    case 0x18:        /* MCSPI_IRQSTATUS */
228
        if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
229
            s->irqst &= ~value;
230
            omap_mcspi_interrupt_update(s);
231
        }
232
        break;
233

    
234
    case 0x1c:        /* MCSPI_IRQENABLE */
235
        s->irqen = value & 0x1777f;
236
        omap_mcspi_interrupt_update(s);
237
        break;
238

    
239
    case 0x20:        /* MCSPI_WAKEUPENABLE */
240
        s->wken = value & 1;
241
        break;
242

    
243
    case 0x24:        /* MCSPI_SYST */
244
        if (s->control & (1 << 3))                        /* SYSTEM_TEST */
245
            if (value & (1 << 11)) {                        /* SSB */
246
                s->irqst |= 0x1777f;
247
                omap_mcspi_interrupt_update(s);
248
            }
249
        s->systest = value & 0xfff;
250
        break;
251

    
252
    case 0x28:        /* MCSPI_MODULCTRL */
253
        if (value & (1 << 3))                                /* SYSTEM_TEST */
254
            if (s->systest & (1 << 11)) {                /* SSB */
255
                s->irqst |= 0x1777f;
256
                omap_mcspi_interrupt_update(s);
257
            }
258
        s->control = value & 0xf;
259
        break;
260

    
261
    case 0x68: ch ++;
262
    case 0x54: ch ++;
263
    case 0x40: ch ++;
264
    case 0x2c:        /* MCSPI_CHCONF */
265
        if ((value ^ s->ch[ch].config) & (3 << 14))        /* DMAR | DMAW */
266
            omap_mcspi_dmarequest_update(s->ch + ch);
267
        if (((value >> 12) & 3) == 3)                        /* TRM */
268
            fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__);
269
        if (((value >> 7) & 0x1f) < 3)                        /* WL */
270
            fprintf(stderr, "%s: invalid WL value (%i)\n",
271
                            __FUNCTION__, (value >> 7) & 0x1f);
272
        s->ch[ch].config = value & 0x7fffff;
273
        break;
274

    
275
    case 0x70: ch ++;
276
    case 0x5c: ch ++;
277
    case 0x48: ch ++;
278
    case 0x34:        /* MCSPI_CHCTRL */
279
        if (value & ~s->ch[ch].control & 1) {                /* EN */
280
            s->ch[ch].control |= 1;
281
            omap_mcspi_transfer_run(s, ch);
282
        } else
283
            s->ch[ch].control = value & 1;
284
        break;
285

    
286
    case 0x74: ch ++;
287
    case 0x60: ch ++;
288
    case 0x4c: ch ++;
289
    case 0x38:        /* MCSPI_TX */
290
        s->ch[ch].tx = value;
291
        s->ch[ch].status &= ~(1 << 1);                        /* TXS */
292
        omap_mcspi_transfer_run(s, ch);
293
        break;
294

    
295
    default:
296
        OMAP_BAD_REG(addr);
297
        return;
298
    }
299
}
300

    
301
static CPUReadMemoryFunc * const omap_mcspi_readfn[] = {
302
    omap_badwidth_read32,
303
    omap_badwidth_read32,
304
    omap_mcspi_read,
305
};
306

    
307
static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = {
308
    omap_badwidth_write32,
309
    omap_badwidth_write32,
310
    omap_mcspi_write,
311
};
312

    
313
struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
314
                qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
315
{
316
    int iomemtype;
317
    struct omap_mcspi_s *s = (struct omap_mcspi_s *)
318
            qemu_mallocz(sizeof(struct omap_mcspi_s));
319
    struct omap_mcspi_ch_s *ch = s->ch;
320

    
321
    s->irq = irq;
322
    s->chnum = chnum;
323
    while (chnum --) {
324
        ch->txdrq = *drq ++;
325
        ch->rxdrq = *drq ++;
326
        ch ++;
327
    }
328
    omap_mcspi_reset(s);
329

    
330
    iomemtype = l4_register_io_memory(omap_mcspi_readfn,
331
                    omap_mcspi_writefn, s);
332
    omap_l4_attach(ta, 0, iomemtype);
333

    
334
    return s;
335
}
336

    
337
void omap_mcspi_attach(struct omap_mcspi_s *s,
338
                uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
339
                int chipselect)
340
{
341
    if (chipselect < 0 || chipselect >= s->chnum)
342
        hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
343

    
344
    s->ch[chipselect].txrx = txrx;
345
    s->ch[chipselect].opaque = opaque;
346
}