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 |
} |