Statistics
| Branch: | Revision:

root / hw / omap_gpmc.c @ 7c470ff1

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
#include "memory.h"
25
#include "exec-memory.h"
26

    
27
/* General-Purpose Memory Controller */
28
struct omap_gpmc_s {
29
    qemu_irq irq;
30
    MemoryRegion iomem;
31

    
32
    uint8_t revision;
33
    uint8_t sysconfig;
34
    uint16_t irqst;
35
    uint16_t irqen;
36
    uint16_t timeout;
37
    uint16_t config;
38
    uint32_t prefconfig[2];
39
    int prefcontrol;
40
    int preffifo;
41
    int prefcount;
42
    struct omap_gpmc_cs_file_s {
43
        uint32_t config[7];
44
        MemoryRegion *iomem;
45
        MemoryRegion container;
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_s *s, int cs)
59
{
60
    struct omap_gpmc_cs_file_s *f = &s->cs_file[cs];
61
    uint32_t mask = (f->config[6] >> 8) & 0xf;
62
    uint32_t base = f->config[6] & 0x3f;
63
    uint32_t size;
64

    
65
    if (!f->iomem) {
66
        return;
67
    }
68

    
69
    if (!(f->config[6] & (1 << 6))) {
70
        /* Do nothing unless CSVALID */
71
        return;
72
    }
73

    
74
    /* TODO: check for overlapping regions and report access errors */
75
    if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
76
        (base & 0x0f & ~mask)) {
77
        fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
78
                        __FUNCTION__);
79
        return;
80
    }
81

    
82
    base <<= 24;
83
    size = (0x0fffffff & ~(mask << 24)) + 1;
84
    /* TODO: rather than setting the size of the mapping (which should be
85
     * constant), the mask should cause wrapping of the address space, so
86
     * that the same memory becomes accessible at every <i>size</i> bytes
87
     * starting from <i>base</i>.  */
88
    memory_region_init(&f->container, "omap-gpmc-file", size);
89
    memory_region_add_subregion(&f->container, 0, f->iomem);
90
    memory_region_add_subregion(get_system_memory(), base,
91
                                &f->container);
92
}
93

    
94
static void omap_gpmc_cs_unmap(struct omap_gpmc_s *s, int cs)
95
{
96
    struct omap_gpmc_cs_file_s *f = &s->cs_file[cs];
97
    if (!(f->config[6] & (1 << 6))) {
98
        /* Do nothing unless CSVALID */
99
        return;
100
    }
101
    if (!f->iomem) {
102
        return;
103
    }
104

    
105
    memory_region_del_subregion(get_system_memory(), &f->container);
106
    memory_region_del_subregion(&f->container, f->iomem);
107
    memory_region_destroy(&f->container);
108
}
109

    
110
void omap_gpmc_reset(struct omap_gpmc_s *s)
111
{
112
    int i;
113

    
114
    s->sysconfig = 0;
115
    s->irqst = 0;
116
    s->irqen = 0;
117
    omap_gpmc_int_update(s);
118
    s->timeout = 0;
119
    s->config = 0xa00;
120
    s->prefconfig[0] = 0x00004000;
121
    s->prefconfig[1] = 0x00000000;
122
    s->prefcontrol = 0;
123
    s->preffifo = 0;
124
    s->prefcount = 0;
125
    for (i = 0; i < 8; i ++) {
126
        omap_gpmc_cs_unmap(s, i);
127
        s->cs_file[i].config[1] = 0x101001;
128
        s->cs_file[i].config[2] = 0x020201;
129
        s->cs_file[i].config[3] = 0x10031003;
130
        s->cs_file[i].config[4] = 0x10f1111;
131
        s->cs_file[i].config[5] = 0;
132
        s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
133

    
134
        s->cs_file[i].config[6] = 0xf00;
135
        /* In theory we could probe attached devices for some CFG1
136
         * bits here, but we just retain them across resets as they
137
         * were set initially by omap_gpmc_attach().
138
         */
139
        if (i == 0) {
140
            s->cs_file[i].config[0] &= 0x00433e00;
141
            s->cs_file[i].config[6] |= 1 << 6; /* CSVALID */
142
            omap_gpmc_cs_map(s, i);
143
        } else {
144
            s->cs_file[i].config[0] &= 0x00403c00;
145
        }
146
    }
147
    s->ecc_cs = 0;
148
    s->ecc_ptr = 0;
149
    s->ecc_cfg = 0x3fcff000;
150
    for (i = 0; i < 9; i ++)
151
        ecc_reset(&s->ecc[i]);
152
}
153

    
154
static uint64_t omap_gpmc_read(void *opaque, target_phys_addr_t addr,
155
                               unsigned size)
156
{
157
    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
158
    int cs;
159
    struct omap_gpmc_cs_file_s *f;
160

    
161
    if (size != 4) {
162
        return omap_badwidth_read32(opaque, addr);
163
    }
164

    
165
    switch (addr) {
166
    case 0x000:        /* GPMC_REVISION */
167
        return s->revision;
168

    
169
    case 0x010:        /* GPMC_SYSCONFIG */
170
        return s->sysconfig;
171

    
172
    case 0x014:        /* GPMC_SYSSTATUS */
173
        return 1;                                                /* RESETDONE */
174

    
175
    case 0x018:        /* GPMC_IRQSTATUS */
176
        return s->irqst;
177

    
178
    case 0x01c:        /* GPMC_IRQENABLE */
179
        return s->irqen;
180

    
181
    case 0x040:        /* GPMC_TIMEOUT_CONTROL */
182
        return s->timeout;
183

    
184
    case 0x044:        /* GPMC_ERR_ADDRESS */
185
    case 0x048:        /* GPMC_ERR_TYPE */
186
        return 0;
187

    
188
    case 0x050:        /* GPMC_CONFIG */
189
        return s->config;
190

    
191
    case 0x054:        /* GPMC_STATUS */
192
        return 0x001;
193

    
194
    case 0x060 ... 0x1d4:
195
        cs = (addr - 0x060) / 0x30;
196
        addr -= cs * 0x30;
197
        f = s->cs_file + cs;
198
        switch (addr) {
199
            case 0x60:        /* GPMC_CONFIG1 */
200
                return f->config[0];
201
            case 0x64:        /* GPMC_CONFIG2 */
202
                return f->config[1];
203
            case 0x68:        /* GPMC_CONFIG3 */
204
                return f->config[2];
205
            case 0x6c:        /* GPMC_CONFIG4 */
206
                return f->config[3];
207
            case 0x70:        /* GPMC_CONFIG5 */
208
                return f->config[4];
209
            case 0x74:        /* GPMC_CONFIG6 */
210
                return f->config[5];
211
            case 0x78:        /* GPMC_CONFIG7 */
212
                return f->config[6];
213
            case 0x84:        /* GPMC_NAND_DATA */
214
                return 0;
215
        }
216
        break;
217

    
218
    case 0x1e0:        /* GPMC_PREFETCH_CONFIG1 */
219
        return s->prefconfig[0];
220
    case 0x1e4:        /* GPMC_PREFETCH_CONFIG2 */
221
        return s->prefconfig[1];
222
    case 0x1ec:        /* GPMC_PREFETCH_CONTROL */
223
        return s->prefcontrol;
224
    case 0x1f0:        /* GPMC_PREFETCH_STATUS */
225
        return (s->preffifo << 24) |
226
                ((s->preffifo >=
227
                  ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
228
                s->prefcount;
229

    
230
    case 0x1f4:        /* GPMC_ECC_CONFIG */
231
        return s->ecc_cs;
232
    case 0x1f8:        /* GPMC_ECC_CONTROL */
233
        return s->ecc_ptr;
234
    case 0x1fc:        /* GPMC_ECC_SIZE_CONFIG */
235
        return s->ecc_cfg;
236
    case 0x200 ... 0x220:        /* GPMC_ECC_RESULT */
237
        cs = (addr & 0x1f) >> 2;
238
        /* TODO: check correctness */
239
        return
240
                ((s->ecc[cs].cp    &  0x07) <<  0) |
241
                ((s->ecc[cs].cp    &  0x38) << 13) |
242
                ((s->ecc[cs].lp[0] & 0x1ff) <<  3) |
243
                ((s->ecc[cs].lp[1] & 0x1ff) << 19);
244

    
245
    case 0x230:        /* GPMC_TESTMODE_CTRL */
246
        return 0;
247
    case 0x234:        /* GPMC_PSA_LSB */
248
    case 0x238:        /* GPMC_PSA_MSB */
249
        return 0x00000000;
250
    }
251

    
252
    OMAP_BAD_REG(addr);
253
    return 0;
254
}
255

    
256
static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
257
                            uint64_t value, unsigned size)
258
{
259
    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
260
    int cs;
261
    struct omap_gpmc_cs_file_s *f;
262

    
263
    if (size != 4) {
264
        return omap_badwidth_write32(opaque, addr, value);
265
    }
266

    
267
    switch (addr) {
268
    case 0x000:        /* GPMC_REVISION */
269
    case 0x014:        /* GPMC_SYSSTATUS */
270
    case 0x054:        /* GPMC_STATUS */
271
    case 0x1f0:        /* GPMC_PREFETCH_STATUS */
272
    case 0x200 ... 0x220:        /* GPMC_ECC_RESULT */
273
    case 0x234:        /* GPMC_PSA_LSB */
274
    case 0x238:        /* GPMC_PSA_MSB */
275
        OMAP_RO_REG(addr);
276
        break;
277

    
278
    case 0x010:        /* GPMC_SYSCONFIG */
279
        if ((value >> 3) == 0x3)
280
            fprintf(stderr, "%s: bad SDRAM idle mode %"PRIi64"\n",
281
                            __FUNCTION__, value >> 3);
282
        if (value & 2)
283
            omap_gpmc_reset(s);
284
        s->sysconfig = value & 0x19;
285
        break;
286

    
287
    case 0x018:        /* GPMC_IRQSTATUS */
288
        s->irqen &= ~value;
289
        omap_gpmc_int_update(s);
290
        break;
291

    
292
    case 0x01c:        /* GPMC_IRQENABLE */
293
        s->irqen = value & 0xf03;
294
        omap_gpmc_int_update(s);
295
        break;
296

    
297
    case 0x040:        /* GPMC_TIMEOUT_CONTROL */
298
        s->timeout = value & 0x1ff1;
299
        break;
300

    
301
    case 0x044:        /* GPMC_ERR_ADDRESS */
302
    case 0x048:        /* GPMC_ERR_TYPE */
303
        break;
304

    
305
    case 0x050:        /* GPMC_CONFIG */
306
        s->config = value & 0xf13;
307
        break;
308

    
309
    case 0x060 ... 0x1d4:
310
        cs = (addr - 0x060) / 0x30;
311
        addr -= cs * 0x30;
312
        f = s->cs_file + cs;
313
        switch (addr) {
314
            case 0x60:        /* GPMC_CONFIG1 */
315
                f->config[0] = value & 0xffef3e13;
316
                break;
317
            case 0x64:        /* GPMC_CONFIG2 */
318
                f->config[1] = value & 0x001f1f8f;
319
                break;
320
            case 0x68:        /* GPMC_CONFIG3 */
321
                f->config[2] = value & 0x001f1f8f;
322
                break;
323
            case 0x6c:        /* GPMC_CONFIG4 */
324
                f->config[3] = value & 0x1f8f1f8f;
325
                break;
326
            case 0x70:        /* GPMC_CONFIG5 */
327
                f->config[4] = value & 0x0f1f1f1f;
328
                break;
329
            case 0x74:        /* GPMC_CONFIG6 */
330
                f->config[5] = value & 0x00000fcf;
331
                break;
332
            case 0x78:        /* GPMC_CONFIG7 */
333
                if ((f->config[6] ^ value) & 0xf7f) {
334
                    omap_gpmc_cs_unmap(s, cs);
335
                    f->config[6] = value & 0x00000f7f;
336
                    omap_gpmc_cs_map(s, cs);
337
                }
338
                break;
339
            case 0x7c:        /* GPMC_NAND_COMMAND */
340
            case 0x80:        /* GPMC_NAND_ADDRESS */
341
            case 0x84:        /* GPMC_NAND_DATA */
342
                break;
343

    
344
            default:
345
                goto bad_reg;
346
        }
347
        break;
348

    
349
    case 0x1e0:        /* GPMC_PREFETCH_CONFIG1 */
350
        s->prefconfig[0] = value & 0x7f8f7fbf;
351
        /* TODO: update interrupts, fifos, dmas */
352
        break;
353

    
354
    case 0x1e4:        /* GPMC_PREFETCH_CONFIG2 */
355
        s->prefconfig[1] = value & 0x3fff;
356
        break;
357

    
358
    case 0x1ec:        /* GPMC_PREFETCH_CONTROL */
359
        s->prefcontrol = value & 1;
360
        if (s->prefcontrol) {
361
            if (s->prefconfig[0] & 1)
362
                s->preffifo = 0x40;
363
            else
364
                s->preffifo = 0x00;
365
        }
366
        /* TODO: start */
367
        break;
368

    
369
    case 0x1f4:        /* GPMC_ECC_CONFIG */
370
        s->ecc_cs = 0x8f;
371
        break;
372
    case 0x1f8:        /* GPMC_ECC_CONTROL */
373
        if (value & (1 << 8))
374
            for (cs = 0; cs < 9; cs ++)
375
                ecc_reset(&s->ecc[cs]);
376
        s->ecc_ptr = value & 0xf;
377
        if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
378
            s->ecc_ptr = 0;
379
            s->ecc_cs &= ~1;
380
        }
381
        break;
382
    case 0x1fc:        /* GPMC_ECC_SIZE_CONFIG */
383
        s->ecc_cfg = value & 0x3fcff1ff;
384
        break;
385
    case 0x230:        /* GPMC_TESTMODE_CTRL */
386
        if (value & 7)
387
            fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
388
        break;
389

    
390
    default:
391
    bad_reg:
392
        OMAP_BAD_REG(addr);
393
        return;
394
    }
395
}
396

    
397
static const MemoryRegionOps omap_gpmc_ops = {
398
    .read = omap_gpmc_read,
399
    .write = omap_gpmc_write,
400
    .endianness = DEVICE_NATIVE_ENDIAN,
401
};
402

    
403
struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
404
                                   target_phys_addr_t base, qemu_irq irq)
405
{
406
    struct omap_gpmc_s *s = (struct omap_gpmc_s *)
407
            g_malloc0(sizeof(struct omap_gpmc_s));
408

    
409
    memory_region_init_io(&s->iomem, &omap_gpmc_ops, s, "omap-gpmc", 0x1000);
410
    memory_region_add_subregion(get_system_memory(), base, &s->iomem);
411

    
412
    s->irq = irq;
413
    s->revision = cpu_class_omap3(mpu) ? 0x50 : 0x20;
414
    omap_gpmc_reset(s);
415

    
416
    return s;
417
}
418

    
419
void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem)
420
{
421
    struct omap_gpmc_cs_file_s *f;
422
    assert(iomem);
423

    
424
    if (cs < 0 || cs >= 8) {
425
        fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
426
        exit(-1);
427
    }
428
    f = &s->cs_file[cs];
429

    
430
    omap_gpmc_cs_unmap(s, cs);
431
    f->iomem = iomem;
432
    omap_gpmc_cs_map(s, cs);
433
}