Statistics
| Branch: | Revision:

root / hw / mpcore.c @ 538ddf65

History | View | Annotate | Download (4 kB)

1
/*
2
 * ARM MPCore internal peripheral emulation (common code).
3
 *
4
 * Copyright (c) 2006-2007 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licensed under the GPL.
8
 */
9

    
10
#include "sysbus.h"
11
#include "qemu-timer.h"
12

    
13
#define NCPU 4
14

    
15
static inline int
16
gic_get_current_cpu(void)
17
{
18
  return cpu_single_env->cpu_index;
19
}
20

    
21
#include "arm_gic.c"
22

    
23
/* MPCore private memory region.  */
24

    
25
typedef struct mpcore_priv_state {
26
    gic_state gic;
27
    uint32_t scu_control;
28
    int iomemtype;
29
    uint32_t old_timer_status[8];
30
    uint32_t num_cpu;
31
    qemu_irq *timer_irq;
32
    MemoryRegion iomem;
33
    MemoryRegion container;
34
    DeviceState *mptimer;
35
} mpcore_priv_state;
36

    
37
/* Per-CPU private memory mapped IO.  */
38

    
39
static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset,
40
                                unsigned size)
41
{
42
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
43
    int id;
44
    offset &= 0xff;
45
    /* SCU */
46
    switch (offset) {
47
    case 0x00: /* Control.  */
48
        return s->scu_control;
49
    case 0x04: /* Configuration.  */
50
        id = ((1 << s->num_cpu) - 1) << 4;
51
        return id | (s->num_cpu - 1);
52
    case 0x08: /* CPU status.  */
53
        return 0;
54
    case 0x0c: /* Invalidate all.  */
55
        return 0;
56
    default:
57
        hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
58
    }
59
}
60

    
61
static void mpcore_scu_write(void *opaque, target_phys_addr_t offset,
62
                             uint64_t value, unsigned size)
63
{
64
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
65
    offset &= 0xff;
66
    /* SCU */
67
    switch (offset) {
68
    case 0: /* Control register.  */
69
        s->scu_control = value & 1;
70
        break;
71
    case 0x0c: /* Invalidate all.  */
72
        /* This is a no-op as cache is not emulated.  */
73
        break;
74
    default:
75
        hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
76
    }
77
}
78

    
79
static const MemoryRegionOps mpcore_scu_ops = {
80
    .read = mpcore_scu_read,
81
    .write = mpcore_scu_write,
82
    .endianness = DEVICE_NATIVE_ENDIAN,
83
};
84

    
85
static void mpcore_timer_irq_handler(void *opaque, int irq, int level)
86
{
87
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
88
    if (level && !s->old_timer_status[irq]) {
89
        gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
90
    }
91
    s->old_timer_status[irq] = level;
92
}
93

    
94
static void mpcore_priv_map_setup(mpcore_priv_state *s)
95
{
96
    int i;
97
    SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
98
    memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
99
    memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100);
100
    memory_region_add_subregion(&s->container, 0, &s->iomem);
101
    /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
102
     * at 0x200, 0x300...
103
     */
104
    for (i = 0; i < (s->num_cpu + 1); i++) {
105
        target_phys_addr_t offset = 0x100 + (i * 0x100);
106
        memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]);
107
    }
108
    /* Add the regions for timer and watchdog for "current CPU" and
109
     * for each specific CPU.
110
     */
111
    s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler,
112
                                      s, (s->num_cpu + 1) * 2);
113
    for (i = 0; i < (s->num_cpu + 1) * 2; i++) {
114
        /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
115
        target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20;
116
        memory_region_add_subregion(&s->container, offset,
117
                                    sysbus_mmio_get_region(busdev, i));
118
    }
119
    memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
120
    /* Wire up the interrupt from each watchdog and timer. */
121
    for (i = 0; i < s->num_cpu * 2; i++) {
122
        sysbus_connect_irq(busdev, i, s->timer_irq[i]);
123
    }
124
}
125

    
126
static int mpcore_priv_init(SysBusDevice *dev)
127
{
128
    mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
129

    
130
    gic_init(&s->gic, s->num_cpu);
131
    s->mptimer = qdev_create(NULL, "arm_mptimer");
132
    qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
133
    qdev_init_nofail(s->mptimer);
134
    mpcore_priv_map_setup(s);
135
    sysbus_init_mmio(dev, &s->container);
136
    return 0;
137
}