Statistics
| Branch: | Revision:

root / hw / omap_gpmc.c @ 2507c12a

History | View | Annotate | Download (12.3 kB)

1
/*
2
 * TI OMAP general purpose memory controller emulation.
3
 *
4
 * Copyright (C) 2007-2009 Nokia Corporation
5
 * Original code written by Andrzej Zaborowski <andrew@openedhand.com>
6
 * Enhancements for OMAP3 and NAND support written by Juha Riihimäki
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, see <http://www.gnu.org/licenses/>.
20
 */
21
#include "hw.h"
22
#include "flash.h"
23
#include "omap.h"
24

    
25
/* General-Purpose Memory Controller */
26
struct omap_gpmc_s {
27
    qemu_irq irq;
28

    
29
    uint8_t sysconfig;
30
    uint16_t irqst;
31
    uint16_t irqen;
32
    uint16_t timeout;
33
    uint16_t config;
34
    uint32_t prefconfig[2];
35
    int prefcontrol;
36
    int preffifo;
37
    int prefcount;
38
    struct omap_gpmc_cs_file_s {
39
        uint32_t config[7];
40
        target_phys_addr_t base;
41
        size_t size;
42
        int iomemtype;
43
        void (*base_update)(void *opaque, target_phys_addr_t new);
44
        void (*unmap)(void *opaque);
45
        void *opaque;
46
    } cs_file[8];
47
    int ecc_cs;
48
    int ecc_ptr;
49
    uint32_t ecc_cfg;
50
    ECCState ecc[9];
51
};
52

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

    
58
static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask)
59
{
60
    /* TODO: check for overlapping regions and report access errors */
61
    if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
62
                    (base < 0 || base >= 0x40) ||
63
                    (base & 0x0f & ~mask)) {
64
        fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
65
                        __FUNCTION__);
66
        return;
67
    }
68

    
69
    if (!f->opaque)
70
        return;
71

    
72
    f->base = base << 24;
73
    f->size = (0x0fffffff & ~(mask << 24)) + 1;
74
    /* TODO: rather than setting the size of the mapping (which should be
75
     * constant), the mask should cause wrapping of the address space, so
76
     * that the same memory becomes accessible at every <i>size</i> bytes
77
     * starting from <i>base</i>.  */
78
    if (f->iomemtype)
79
        cpu_register_physical_memory(f->base, f->size, f->iomemtype);
80

    
81
    if (f->base_update)
82
        f->base_update(f->opaque, f->base);
83
}
84

    
85
static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f)
86
{
87
    if (f->size) {
88
        if (f->unmap)
89
            f->unmap(f->opaque);
90
        if (f->iomemtype)
91
            cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED);
92
        f->base = 0;
93
        f->size = 0;
94
    }
95
}
96

    
97
void omap_gpmc_reset(struct omap_gpmc_s *s)
98
{
99
    int i;
100

    
101
    s->sysconfig = 0;
102
    s->irqst = 0;
103
    s->irqen = 0;
104
    omap_gpmc_int_update(s);
105
    s->timeout = 0;
106
    s->config = 0xa00;
107
    s->prefconfig[0] = 0x00004000;
108
    s->prefconfig[1] = 0x00000000;
109
    s->prefcontrol = 0;
110
    s->preffifo = 0;
111
    s->prefcount = 0;
112
    for (i = 0; i < 8; i ++) {
113
        if (s->cs_file[i].config[6] & (1 << 6))                        /* CSVALID */
114
            omap_gpmc_cs_unmap(s->cs_file + i);
115
        s->cs_file[i].config[0] = i ? 1 << 12 : 0;
116
        s->cs_file[i].config[1] = 0x101001;
117
        s->cs_file[i].config[2] = 0x020201;
118
        s->cs_file[i].config[3] = 0x10031003;
119
        s->cs_file[i].config[4] = 0x10f1111;
120
        s->cs_file[i].config[5] = 0;
121
        s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
122
        if (s->cs_file[i].config[6] & (1 << 6))                        /* CSVALID */
123
            omap_gpmc_cs_map(&s->cs_file[i],
124
                            s->cs_file[i].config[6] & 0x1f,        /* MASKADDR */
125
                        (s->cs_file[i].config[6] >> 8 & 0xf));        /* BASEADDR */
126
    }
127
    omap_gpmc_cs_map(s->cs_file, 0, 0xf);
128
    s->ecc_cs = 0;
129
    s->ecc_ptr = 0;
130
    s->ecc_cfg = 0x3fcff000;
131
    for (i = 0; i < 9; i ++)
132
        ecc_reset(&s->ecc[i]);
133
}
134

    
135
static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
136
{
137
    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
138
    int cs;
139
    struct omap_gpmc_cs_file_s *f;
140

    
141
    switch (addr) {
142
    case 0x000:        /* GPMC_REVISION */
143
        return 0x20;
144

    
145
    case 0x010:        /* GPMC_SYSCONFIG */
146
        return s->sysconfig;
147

    
148
    case 0x014:        /* GPMC_SYSSTATUS */
149
        return 1;                                                /* RESETDONE */
150

    
151
    case 0x018:        /* GPMC_IRQSTATUS */
152
        return s->irqst;
153

    
154
    case 0x01c:        /* GPMC_IRQENABLE */
155
        return s->irqen;
156

    
157
    case 0x040:        /* GPMC_TIMEOUT_CONTROL */
158
        return s->timeout;
159

    
160
    case 0x044:        /* GPMC_ERR_ADDRESS */
161
    case 0x048:        /* GPMC_ERR_TYPE */
162
        return 0;
163

    
164
    case 0x050:        /* GPMC_CONFIG */
165
        return s->config;
166

    
167
    case 0x054:        /* GPMC_STATUS */
168
        return 0x001;
169

    
170
    case 0x060 ... 0x1d4:
171
        cs = (addr - 0x060) / 0x30;
172
        addr -= cs * 0x30;
173
        f = s->cs_file + cs;
174
        switch (addr) {
175
            case 0x60:        /* GPMC_CONFIG1 */
176
                return f->config[0];
177
            case 0x64:        /* GPMC_CONFIG2 */
178
                return f->config[1];
179
            case 0x68:        /* GPMC_CONFIG3 */
180
                return f->config[2];
181
            case 0x6c:        /* GPMC_CONFIG4 */
182
                return f->config[3];
183
            case 0x70:        /* GPMC_CONFIG5 */
184
                return f->config[4];
185
            case 0x74:        /* GPMC_CONFIG6 */
186
                return f->config[5];
187
            case 0x78:        /* GPMC_CONFIG7 */
188
                return f->config[6];
189
            case 0x84:        /* GPMC_NAND_DATA */
190
                return 0;
191
        }
192
        break;
193

    
194
    case 0x1e0:        /* GPMC_PREFETCH_CONFIG1 */
195
        return s->prefconfig[0];
196
    case 0x1e4:        /* GPMC_PREFETCH_CONFIG2 */
197
        return s->prefconfig[1];
198
    case 0x1ec:        /* GPMC_PREFETCH_CONTROL */
199
        return s->prefcontrol;
200
    case 0x1f0:        /* GPMC_PREFETCH_STATUS */
201
        return (s->preffifo << 24) |
202
                ((s->preffifo >
203
                  ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
204
                s->prefcount;
205

    
206
    case 0x1f4:        /* GPMC_ECC_CONFIG */
207
        return s->ecc_cs;
208
    case 0x1f8:        /* GPMC_ECC_CONTROL */
209
        return s->ecc_ptr;
210
    case 0x1fc:        /* GPMC_ECC_SIZE_CONFIG */
211
        return s->ecc_cfg;
212
    case 0x200 ... 0x220:        /* GPMC_ECC_RESULT */
213
        cs = (addr & 0x1f) >> 2;
214
        /* TODO: check correctness */
215
        return
216
                ((s->ecc[cs].cp    &  0x07) <<  0) |
217
                ((s->ecc[cs].cp    &  0x38) << 13) |
218
                ((s->ecc[cs].lp[0] & 0x1ff) <<  3) |
219
                ((s->ecc[cs].lp[1] & 0x1ff) << 19);
220

    
221
    case 0x230:        /* GPMC_TESTMODE_CTRL */
222
        return 0;
223
    case 0x234:        /* GPMC_PSA_LSB */
224
    case 0x238:        /* GPMC_PSA_MSB */
225
        return 0x00000000;
226
    }
227

    
228
    OMAP_BAD_REG(addr);
229
    return 0;
230
}
231

    
232
static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
233
                uint32_t value)
234
{
235
    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
236
    int cs;
237
    struct omap_gpmc_cs_file_s *f;
238

    
239
    switch (addr) {
240
    case 0x000:        /* GPMC_REVISION */
241
    case 0x014:        /* GPMC_SYSSTATUS */
242
    case 0x054:        /* GPMC_STATUS */
243
    case 0x1f0:        /* GPMC_PREFETCH_STATUS */
244
    case 0x200 ... 0x220:        /* GPMC_ECC_RESULT */
245
    case 0x234:        /* GPMC_PSA_LSB */
246
    case 0x238:        /* GPMC_PSA_MSB */
247
        OMAP_RO_REG(addr);
248
        break;
249

    
250
    case 0x010:        /* GPMC_SYSCONFIG */
251
        if ((value >> 3) == 0x3)
252
            fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
253
                            __FUNCTION__, value >> 3);
254
        if (value & 2)
255
            omap_gpmc_reset(s);
256
        s->sysconfig = value & 0x19;
257
        break;
258

    
259
    case 0x018:        /* GPMC_IRQSTATUS */
260
        s->irqen = ~value;
261
        omap_gpmc_int_update(s);
262
        break;
263

    
264
    case 0x01c:        /* GPMC_IRQENABLE */
265
        s->irqen = value & 0xf03;
266
        omap_gpmc_int_update(s);
267
        break;
268

    
269
    case 0x040:        /* GPMC_TIMEOUT_CONTROL */
270
        s->timeout = value & 0x1ff1;
271
        break;
272

    
273
    case 0x044:        /* GPMC_ERR_ADDRESS */
274
    case 0x048:        /* GPMC_ERR_TYPE */
275
        break;
276

    
277
    case 0x050:        /* GPMC_CONFIG */
278
        s->config = value & 0xf13;
279
        break;
280

    
281
    case 0x060 ... 0x1d4:
282
        cs = (addr - 0x060) / 0x30;
283
        addr -= cs * 0x30;
284
        f = s->cs_file + cs;
285
        switch (addr) {
286
            case 0x60:        /* GPMC_CONFIG1 */
287
                f->config[0] = value & 0xffef3e13;
288
                break;
289
            case 0x64:        /* GPMC_CONFIG2 */
290
                f->config[1] = value & 0x001f1f8f;
291
                break;
292
            case 0x68:        /* GPMC_CONFIG3 */
293
                f->config[2] = value & 0x001f1f8f;
294
                break;
295
            case 0x6c:        /* GPMC_CONFIG4 */
296
                f->config[3] = value & 0x1f8f1f8f;
297
                break;
298
            case 0x70:        /* GPMC_CONFIG5 */
299
                f->config[4] = value & 0x0f1f1f1f;
300
                break;
301
            case 0x74:        /* GPMC_CONFIG6 */
302
                f->config[5] = value & 0x00000fcf;
303
                break;
304
            case 0x78:        /* GPMC_CONFIG7 */
305
                if ((f->config[6] ^ value) & 0xf7f) {
306
                    if (f->config[6] & (1 << 6))                /* CSVALID */
307
                        omap_gpmc_cs_unmap(f);
308
                    if (value & (1 << 6))                        /* CSVALID */
309
                        omap_gpmc_cs_map(f, value & 0x1f,        /* MASKADDR */
310
                                        (value >> 8 & 0xf));        /* BASEADDR */
311
                }
312
                f->config[6] = value & 0x00000f7f;
313
                break;
314
            case 0x7c:        /* GPMC_NAND_COMMAND */
315
            case 0x80:        /* GPMC_NAND_ADDRESS */
316
            case 0x84:        /* GPMC_NAND_DATA */
317
                break;
318

    
319
            default:
320
                goto bad_reg;
321
        }
322
        break;
323

    
324
    case 0x1e0:        /* GPMC_PREFETCH_CONFIG1 */
325
        s->prefconfig[0] = value & 0x7f8f7fbf;
326
        /* TODO: update interrupts, fifos, dmas */
327
        break;
328

    
329
    case 0x1e4:        /* GPMC_PREFETCH_CONFIG2 */
330
        s->prefconfig[1] = value & 0x3fff;
331
        break;
332

    
333
    case 0x1ec:        /* GPMC_PREFETCH_CONTROL */
334
        s->prefcontrol = value & 1;
335
        if (s->prefcontrol) {
336
            if (s->prefconfig[0] & 1)
337
                s->preffifo = 0x40;
338
            else
339
                s->preffifo = 0x00;
340
        }
341
        /* TODO: start */
342
        break;
343

    
344
    case 0x1f4:        /* GPMC_ECC_CONFIG */
345
        s->ecc_cs = 0x8f;
346
        break;
347
    case 0x1f8:        /* GPMC_ECC_CONTROL */
348
        if (value & (1 << 8))
349
            for (cs = 0; cs < 9; cs ++)
350
                ecc_reset(&s->ecc[cs]);
351
        s->ecc_ptr = value & 0xf;
352
        if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
353
            s->ecc_ptr = 0;
354
            s->ecc_cs &= ~1;
355
        }
356
        break;
357
    case 0x1fc:        /* GPMC_ECC_SIZE_CONFIG */
358
        s->ecc_cfg = value & 0x3fcff1ff;
359
        break;
360
    case 0x230:        /* GPMC_TESTMODE_CTRL */
361
        if (value & 7)
362
            fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
363
        break;
364

    
365
    default:
366
    bad_reg:
367
        OMAP_BAD_REG(addr);
368
        return;
369
    }
370
}
371

    
372
static CPUReadMemoryFunc * const omap_gpmc_readfn[] = {
373
    omap_badwidth_read32,        /* TODO */
374
    omap_badwidth_read32,        /* TODO */
375
    omap_gpmc_read,
376
};
377

    
378
static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = {
379
    omap_badwidth_write32,        /* TODO */
380
    omap_badwidth_write32,        /* TODO */
381
    omap_gpmc_write,
382
};
383

    
384
struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq)
385
{
386
    int iomemtype;
387
    struct omap_gpmc_s *s = (struct omap_gpmc_s *)
388
            qemu_mallocz(sizeof(struct omap_gpmc_s));
389

    
390
    omap_gpmc_reset(s);
391

    
392
    iomemtype = cpu_register_io_memory(omap_gpmc_readfn,
393
                    omap_gpmc_writefn, s, DEVICE_NATIVE_ENDIAN);
394
    cpu_register_physical_memory(base, 0x1000, iomemtype);
395

    
396
    return s;
397
}
398

    
399
void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
400
                void (*base_upd)(void *opaque, target_phys_addr_t new),
401
                void (*unmap)(void *opaque), void *opaque)
402
{
403
    struct omap_gpmc_cs_file_s *f;
404

    
405
    if (cs < 0 || cs >= 8) {
406
        fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
407
        exit(-1);
408
    }
409
    f = &s->cs_file[cs];
410

    
411
    f->iomemtype = iomemtype;
412
    f->base_update = base_upd;
413
    f->unmap = unmap;
414
    f->opaque = opaque;
415

    
416
    if (f->config[6] & (1 << 6))                                /* CSVALID */
417
        omap_gpmc_cs_map(f, f->config[6] & 0x1f,                /* MASKADDR */
418
                        (f->config[6] >> 8 & 0xf));                /* BASEADDR */
419
}