Statistics
| Branch: | Revision:

root / hw / ppc4xx_devs.c @ aaed909a

History | View | Annotate | Download (14.3 kB)

1
/*
2
 * QEMU PowerPC 4xx embedded processors shared devices emulation
3
 *
4
 * Copyright (c) 2007 Jocelyn Mayer
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
#include "vl.h"
25
#include "ppc4xx.h"
26

    
27
extern int loglevel;
28
extern FILE *logfile;
29

    
30
//#define DEBUG_MMIO
31
#define DEBUG_UIC
32

    
33
/*****************************************************************************/
34
/* Generic PowerPC 4xx processor instanciation */
35
CPUState *ppc4xx_init (const unsigned char *cpu_model,
36
                       clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
37
                       uint32_t sysclk)
38
{
39
    CPUState *env;
40

    
41
    /* init CPUs */
42
    env = cpu_init(cpu_model);
43
    if (!env) {
44
        fprintf(stderr, "Unable to find PowerPC %s CPU definition\n",
45
                cpu_model);
46
        exit(1);
47
    }
48
    cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
49
    cpu_clk->opaque = env;
50
    /* Set time-base frequency to sysclk */
51
    tb_clk->cb = ppc_emb_timers_init(env, sysclk);
52
    tb_clk->opaque = env;
53
    ppc_dcr_init(env, NULL, NULL);
54
    /* Register qemu callbacks */
55
    qemu_register_reset(&cpu_ppc_reset, env);
56
    register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
57

    
58
    return env;
59
}
60

    
61
/*****************************************************************************/
62
/* Fake device used to map multiple devices in a single memory page */
63
#define MMIO_AREA_BITS 8
64
#define MMIO_AREA_LEN (1 << MMIO_AREA_BITS)
65
#define MMIO_AREA_NB (1 << (TARGET_PAGE_BITS - MMIO_AREA_BITS))
66
#define MMIO_IDX(addr) (((addr) >> MMIO_AREA_BITS) & (MMIO_AREA_NB - 1))
67
struct ppc4xx_mmio_t {
68
    target_phys_addr_t base;
69
    CPUReadMemoryFunc **mem_read[MMIO_AREA_NB];
70
    CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB];
71
    void *opaque[MMIO_AREA_NB];
72
};
73

    
74
static uint32_t unassigned_mmio_readb (void *opaque, target_phys_addr_t addr)
75
{
76
#ifdef DEBUG_UNASSIGNED
77
    ppc4xx_mmio_t *mmio;
78

    
79
    mmio = opaque;
80
    printf("Unassigned mmio read 0x" PADDRX " base " PADDRX "\n",
81
           addr, mmio->base);
82
#endif
83

    
84
    return 0;
85
}
86

    
87
static void unassigned_mmio_writeb (void *opaque,
88
                                    target_phys_addr_t addr, uint32_t val)
89
{
90
#ifdef DEBUG_UNASSIGNED
91
    ppc4xx_mmio_t *mmio;
92

    
93
    mmio = opaque;
94
    printf("Unassigned mmio write 0x" PADDRX " = 0x%x base " PADDRX "\n",
95
           addr, val, mmio->base);
96
#endif
97
}
98

    
99
static CPUReadMemoryFunc *unassigned_mmio_read[3] = {
100
    unassigned_mmio_readb,
101
    unassigned_mmio_readb,
102
    unassigned_mmio_readb,
103
};
104

    
105
static CPUWriteMemoryFunc *unassigned_mmio_write[3] = {
106
    unassigned_mmio_writeb,
107
    unassigned_mmio_writeb,
108
    unassigned_mmio_writeb,
109
};
110

    
111
static uint32_t mmio_readlen (ppc4xx_mmio_t *mmio,
112
                              target_phys_addr_t addr, int len)
113
{
114
    CPUReadMemoryFunc **mem_read;
115
    uint32_t ret;
116
    int idx;
117

    
118
    idx = MMIO_IDX(addr - mmio->base);
119
#if defined(DEBUG_MMIO)
120
    printf("%s: mmio %p len %d addr " PADDRX " idx %d\n", __func__,
121
           mmio, len, addr, idx);
122
#endif
123
    mem_read = mmio->mem_read[idx];
124
    ret = (*mem_read[len])(mmio->opaque[idx], addr - mmio->base);
125

    
126
    return ret;
127
}
128

    
129
static void mmio_writelen (ppc4xx_mmio_t *mmio,
130
                           target_phys_addr_t addr, uint32_t value, int len)
131
{
132
    CPUWriteMemoryFunc **mem_write;
133
    int idx;
134

    
135
    idx = MMIO_IDX(addr - mmio->base);
136
#if defined(DEBUG_MMIO)
137
    printf("%s: mmio %p len %d addr " PADDRX " idx %d value %08x\n", __func__,
138
           mmio, len, addr, idx, value);
139
#endif
140
    mem_write = mmio->mem_write[idx];
141
    (*mem_write[len])(mmio->opaque[idx], addr - mmio->base, value);
142
}
143

    
144
static uint32_t mmio_readb (void *opaque, target_phys_addr_t addr)
145
{
146
#if defined(DEBUG_MMIO)
147
    printf("%s: addr " PADDRX "\n", __func__, addr);
148
#endif
149

    
150
    return mmio_readlen(opaque, addr, 0);
151
}
152

    
153
static void mmio_writeb (void *opaque,
154
                         target_phys_addr_t addr, uint32_t value)
155
{
156
#if defined(DEBUG_MMIO)
157
    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
158
#endif
159
    mmio_writelen(opaque, addr, value, 0);
160
}
161

    
162
static uint32_t mmio_readw (void *opaque, target_phys_addr_t addr)
163
{
164
#if defined(DEBUG_MMIO)
165
    printf("%s: addr " PADDRX "\n", __func__, addr);
166
#endif
167

    
168
    return mmio_readlen(opaque, addr, 1);
169
}
170

    
171
static void mmio_writew (void *opaque,
172
                         target_phys_addr_t addr, uint32_t value)
173
{
174
#if defined(DEBUG_MMIO)
175
    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
176
#endif
177
    mmio_writelen(opaque, addr, value, 1);
178
}
179

    
180
static uint32_t mmio_readl (void *opaque, target_phys_addr_t addr)
181
{
182
#if defined(DEBUG_MMIO)
183
    printf("%s: addr " PADDRX "\n", __func__, addr);
184
#endif
185

    
186
    return mmio_readlen(opaque, addr, 2);
187
}
188

    
189
static void mmio_writel (void *opaque,
190
                         target_phys_addr_t addr, uint32_t value)
191
{
192
#if defined(DEBUG_MMIO)
193
    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
194
#endif
195
    mmio_writelen(opaque, addr, value, 2);
196
}
197

    
198
static CPUReadMemoryFunc *mmio_read[] = {
199
    &mmio_readb,
200
    &mmio_readw,
201
    &mmio_readl,
202
};
203

    
204
static CPUWriteMemoryFunc *mmio_write[] = {
205
    &mmio_writeb,
206
    &mmio_writew,
207
    &mmio_writel,
208
};
209

    
210
int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio,
211
                          target_phys_addr_t offset, uint32_t len,
212
                          CPUReadMemoryFunc **mem_read,
213
                          CPUWriteMemoryFunc **mem_write, void *opaque)
214
{
215
    uint32_t end;
216
    int idx, eidx;
217

    
218
    if ((offset + len) > TARGET_PAGE_SIZE)
219
        return -1;
220
    idx = MMIO_IDX(offset);
221
    end = offset + len - 1;
222
    eidx = MMIO_IDX(end);
223
#if defined(DEBUG_MMIO)
224
    printf("%s: offset %08x len %08x %08x %d %d\n", __func__, offset, len,
225
           end, idx, eidx);
226
#endif
227
    for (; idx <= eidx; idx++) {
228
        mmio->mem_read[idx] = mem_read;
229
        mmio->mem_write[idx] = mem_write;
230
        mmio->opaque[idx] = opaque;
231
    }
232

    
233
    return 0;
234
}
235

    
236
ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, target_phys_addr_t base)
237
{
238
    ppc4xx_mmio_t *mmio;
239
    int mmio_memory;
240

    
241
    mmio = qemu_mallocz(sizeof(ppc4xx_mmio_t));
242
    if (mmio != NULL) {
243
        mmio->base = base;
244
        mmio_memory = cpu_register_io_memory(0, mmio_read, mmio_write, mmio);
245
#if defined(DEBUG_MMIO)
246
        printf("%s: %p base %08x len %08x %d\n", __func__,
247
               mmio, base, TARGET_PAGE_SIZE, mmio_memory);
248
#endif
249
        cpu_register_physical_memory(base, TARGET_PAGE_SIZE, mmio_memory);
250
        ppc4xx_mmio_register(env, mmio, 0, TARGET_PAGE_SIZE,
251
                             unassigned_mmio_read, unassigned_mmio_write,
252
                             mmio);
253
    }
254

    
255
    return mmio;
256
}
257

    
258
/*****************************************************************************/
259
/* "Universal" Interrupt controller */
260
enum {
261
    DCR_UICSR  = 0x000,
262
    DCR_UICSRS = 0x001,
263
    DCR_UICER  = 0x002,
264
    DCR_UICCR  = 0x003,
265
    DCR_UICPR  = 0x004,
266
    DCR_UICTR  = 0x005,
267
    DCR_UICMSR = 0x006,
268
    DCR_UICVR  = 0x007,
269
    DCR_UICVCR = 0x008,
270
    DCR_UICMAX = 0x009,
271
};
272

    
273
#define UIC_MAX_IRQ 32
274
typedef struct ppcuic_t ppcuic_t;
275
struct ppcuic_t {
276
    uint32_t dcr_base;
277
    int use_vectors;
278
    uint32_t uicsr;  /* Status register */
279
    uint32_t uicer;  /* Enable register */
280
    uint32_t uiccr;  /* Critical register */
281
    uint32_t uicpr;  /* Polarity register */
282
    uint32_t uictr;  /* Triggering register */
283
    uint32_t uicvcr; /* Vector configuration register */
284
    uint32_t uicvr;
285
    qemu_irq *irqs;
286
};
287

    
288
static void ppcuic_trigger_irq (ppcuic_t *uic)
289
{
290
    uint32_t ir, cr;
291
    int start, end, inc, i;
292

    
293
    /* Trigger interrupt if any is pending */
294
    ir = uic->uicsr & uic->uicer & (~uic->uiccr);
295
    cr = uic->uicsr & uic->uicer & uic->uiccr;
296
#ifdef DEBUG_UIC
297
    if (loglevel & CPU_LOG_INT) {
298
        fprintf(logfile, "%s: uicsr %08x uicer %08x uiccr %08x\n"
299
                "   %08x ir %08x cr %08x\n", __func__,
300
                uic->uicsr, uic->uicer, uic->uiccr,
301
                uic->uicsr & uic->uicer, ir, cr);
302
    }
303
#endif
304
    if (ir != 0x0000000) {
305
#ifdef DEBUG_UIC
306
        if (loglevel & CPU_LOG_INT) {
307
            fprintf(logfile, "Raise UIC interrupt\n");
308
        }
309
#endif
310
        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
311
    } else {
312
#ifdef DEBUG_UIC
313
        if (loglevel & CPU_LOG_INT) {
314
            fprintf(logfile, "Lower UIC interrupt\n");
315
        }
316
#endif
317
        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
318
    }
319
    /* Trigger critical interrupt if any is pending and update vector */
320
    if (cr != 0x0000000) {
321
        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
322
        if (uic->use_vectors) {
323
            /* Compute critical IRQ vector */
324
            if (uic->uicvcr & 1) {
325
                start = 31;
326
                end = 0;
327
                inc = -1;
328
            } else {
329
                start = 0;
330
                end = 31;
331
                inc = 1;
332
            }
333
            uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
334
            for (i = start; i <= end; i += inc) {
335
                if (cr & (1 << i)) {
336
                    uic->uicvr += (i - start) * 512 * inc;
337
                    break;
338
                }
339
            }
340
        }
341
#ifdef DEBUG_UIC
342
        if (loglevel & CPU_LOG_INT) {
343
            fprintf(logfile, "Raise UIC critical interrupt - vector %08x\n",
344
                    uic->uicvr);
345
        }
346
#endif
347
    } else {
348
#ifdef DEBUG_UIC
349
        if (loglevel & CPU_LOG_INT) {
350
            fprintf(logfile, "Lower UIC critical interrupt\n");
351
        }
352
#endif
353
        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
354
        uic->uicvr = 0x00000000;
355
    }
356
}
357

    
358
static void ppcuic_set_irq (void *opaque, int irq_num, int level)
359
{
360
    ppcuic_t *uic;
361
    uint32_t mask, sr;
362

    
363
    uic = opaque;
364
    mask = 1 << irq_num;
365
#ifdef DEBUG_UIC
366
    if (loglevel & CPU_LOG_INT) {
367
        fprintf(logfile, "%s: irq %d level %d uicsr %08x mask %08x => %08x "
368
                "%08x\n", __func__, irq_num, level,
369
                uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
370
    }
371
#endif
372
    if (irq_num < 0 || irq_num > 31)
373
        return;
374
    sr = uic->uicsr;
375
    if (!(uic->uicpr & mask)) {
376
        /* Negatively asserted IRQ */
377
        level = level == 0 ? 1 : 0;
378
    }
379
    /* Update status register */
380
    if (uic->uictr & mask) {
381
        /* Edge sensitive interrupt */
382
        if (level == 1)
383
            uic->uicsr |= mask;
384
    } else {
385
        /* Level sensitive interrupt */
386
        if (level == 1)
387
            uic->uicsr |= mask;
388
        else
389
            uic->uicsr &= ~mask;
390
    }
391
#ifdef DEBUG_UIC
392
    if (loglevel & CPU_LOG_INT) {
393
        fprintf(logfile, "%s: irq %d level %d sr %08x => %08x\n", __func__,
394
                irq_num, level, uic->uicsr, sr);
395
    }
396
#endif
397
    if (sr != uic->uicsr)
398
        ppcuic_trigger_irq(uic);
399
}
400

    
401
static target_ulong dcr_read_uic (void *opaque, int dcrn)
402
{
403
    ppcuic_t *uic;
404
    target_ulong ret;
405

    
406
    uic = opaque;
407
    dcrn -= uic->dcr_base;
408
    switch (dcrn) {
409
    case DCR_UICSR:
410
    case DCR_UICSRS:
411
        ret = uic->uicsr;
412
        break;
413
    case DCR_UICER:
414
        ret = uic->uicer;
415
        break;
416
    case DCR_UICCR:
417
        ret = uic->uiccr;
418
        break;
419
    case DCR_UICPR:
420
        ret = uic->uicpr;
421
        break;
422
    case DCR_UICTR:
423
        ret = uic->uictr;
424
        break;
425
    case DCR_UICMSR:
426
        ret = uic->uicsr & uic->uicer;
427
        break;
428
    case DCR_UICVR:
429
        if (!uic->use_vectors)
430
            goto no_read;
431
        ret = uic->uicvr;
432
        break;
433
    case DCR_UICVCR:
434
        if (!uic->use_vectors)
435
            goto no_read;
436
        ret = uic->uicvcr;
437
        break;
438
    default:
439
    no_read:
440
        ret = 0x00000000;
441
        break;
442
    }
443

    
444
    return ret;
445
}
446

    
447
static void dcr_write_uic (void *opaque, int dcrn, target_ulong val)
448
{
449
    ppcuic_t *uic;
450

    
451
    uic = opaque;
452
    dcrn -= uic->dcr_base;
453
#ifdef DEBUG_UIC
454
    if (loglevel & CPU_LOG_INT) {
455
        fprintf(logfile, "%s: dcr %d val " ADDRX "\n", __func__, dcrn, val);
456
    }
457
#endif
458
    switch (dcrn) {
459
    case DCR_UICSR:
460
        uic->uicsr &= ~val;
461
        ppcuic_trigger_irq(uic);
462
        break;
463
    case DCR_UICSRS:
464
        uic->uicsr |= val;
465
        ppcuic_trigger_irq(uic);
466
        break;
467
    case DCR_UICER:
468
        uic->uicer = val;
469
        ppcuic_trigger_irq(uic);
470
        break;
471
    case DCR_UICCR:
472
        uic->uiccr = val;
473
        ppcuic_trigger_irq(uic);
474
        break;
475
    case DCR_UICPR:
476
        uic->uicpr = val;
477
        ppcuic_trigger_irq(uic);
478
        break;
479
    case DCR_UICTR:
480
        uic->uictr = val;
481
        ppcuic_trigger_irq(uic);
482
        break;
483
    case DCR_UICMSR:
484
        break;
485
    case DCR_UICVR:
486
        break;
487
    case DCR_UICVCR:
488
        uic->uicvcr = val & 0xFFFFFFFD;
489
        ppcuic_trigger_irq(uic);
490
        break;
491
    }
492
}
493

    
494
static void ppcuic_reset (void *opaque)
495
{
496
    ppcuic_t *uic;
497

    
498
    uic = opaque;
499
    uic->uiccr = 0x00000000;
500
    uic->uicer = 0x00000000;
501
    uic->uicpr = 0x00000000;
502
    uic->uicsr = 0x00000000;
503
    uic->uictr = 0x00000000;
504
    if (uic->use_vectors) {
505
        uic->uicvcr = 0x00000000;
506
        uic->uicvr = 0x0000000;
507
    }
508
}
509

    
510
qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
511
                       uint32_t dcr_base, int has_ssr, int has_vr)
512
{
513
    ppcuic_t *uic;
514
    int i;
515

    
516
    uic = qemu_mallocz(sizeof(ppcuic_t));
517
    if (uic != NULL) {
518
        uic->dcr_base = dcr_base;
519
        uic->irqs = irqs;
520
        if (has_vr)
521
            uic->use_vectors = 1;
522
        for (i = 0; i < DCR_UICMAX; i++) {
523
            ppc_dcr_register(env, dcr_base + i, uic,
524
                             &dcr_read_uic, &dcr_write_uic);
525
        }
526
        qemu_register_reset(ppcuic_reset, uic);
527
        ppcuic_reset(uic);
528
    }
529

    
530
    return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
531
}