Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
qemu
提交
ab23ebf4
Q
qemu
项目概览
openeuler
/
qemu
通知
10
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Q
qemu
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
ab23ebf4
编写于
12月 12, 2011
作者:
A
Anthony Liguori
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'pmaydell/arm-devs.for-upstream' into staging
上级
9bf4896e
2a6ab1e3
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
751 addition
and
325 deletion
+751
-325
Makefile.target
Makefile.target
+1
-0
hw/a9mpcore.c
hw/a9mpcore.c
+179
-10
hw/arm11mpcore.c
hw/arm11mpcore.c
+129
-1
hw/arm_gic.c
hw/arm_gic.c
+74
-1
hw/arm_mptimer.c
hw/arm_mptimer.c
+332
-0
hw/arm_timer.c
hw/arm_timer.c
+35
-6
hw/mpcore.c
hw/mpcore.c
+0
-283
hw/realview_gic.c
hw/realview_gic.c
+1
-24
未找到文件。
Makefile.target
浏览文件 @
ab23ebf4
...
...
@@ -344,6 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
obj-arm-y
+=
arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y
+=
versatile_pci.o
obj-arm-y
+=
realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-arm-y
+=
arm_mptimer.o
obj-arm-y
+=
armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y
+=
pl061.o
obj-arm-y
+=
arm-semi.o
...
...
hw/a9mpcore.c
浏览文件 @
ab23ebf4
...
...
@@ -2,28 +2,197 @@
* Cortex-A9MPCore internal peripheral emulation.
*
* Copyright (c) 2009 CodeSourcery.
* Written by Paul Brook
* Copyright (c) 2011 Linaro Limited.
* Written by Paul Brook, Peter Maydell.
*
* This code is licensed under the GPL.
*/
/* 64 external IRQ lines. */
#include "sysbus.h"
/* Configuration for arm_gic.c:
* number of external IRQ lines, max number of CPUs, how to ID current CPU
*/
#define GIC_NIRQ 96
#include "mpcore.c"
#define NCPU 4
static
inline
int
gic_get_current_cpu
(
void
)
{
return
cpu_single_env
->
cpu_index
;
}
#include "arm_gic.c"
/* A9MP private memory region. */
typedef
struct
a9mp_priv_state
{
gic_state
gic
;
uint32_t
scu_control
;
uint32_t
old_timer_status
[
8
];
uint32_t
num_cpu
;
qemu_irq
*
timer_irq
;
MemoryRegion
scu_iomem
;
MemoryRegion
ptimer_iomem
;
MemoryRegion
container
;
DeviceState
*
mptimer
;
}
a9mp_priv_state
;
static
uint64_t
a9_scu_read
(
void
*
opaque
,
target_phys_addr_t
offset
,
unsigned
size
)
{
a9mp_priv_state
*
s
=
(
a9mp_priv_state
*
)
opaque
;
switch
(
offset
)
{
case
0x00
:
/* Control */
return
s
->
scu_control
;
case
0x04
:
/* Configuration */
return
(((
1
<<
s
->
num_cpu
)
-
1
)
<<
4
)
|
(
s
->
num_cpu
-
1
);
case
0x08
:
/* CPU Power Status */
return
0
;
case
0x0c
:
/* Invalidate All Registers In Secure State */
return
0
;
case
0x40
:
/* Filtering Start Address Register */
case
0x44
:
/* Filtering End Address Register */
/* RAZ/WI, like an implementation with only one AXI master */
return
0
;
case
0x50
:
/* SCU Access Control Register */
case
0x54
:
/* SCU Non-secure Access Control Register */
/* unimplemented, fall through */
default:
return
0
;
}
}
static
void
a9_scu_write
(
void
*
opaque
,
target_phys_addr_t
offset
,
uint64_t
value
,
unsigned
size
)
{
a9mp_priv_state
*
s
=
(
a9mp_priv_state
*
)
opaque
;
switch
(
offset
)
{
case
0x00
:
/* Control */
s
->
scu_control
=
value
&
1
;
break
;
case
0x4
:
/* Configuration: RO */
break
;
case
0x0c
:
/* Invalidate All Registers In Secure State */
/* no-op as we do not implement caches */
break
;
case
0x40
:
/* Filtering Start Address Register */
case
0x44
:
/* Filtering End Address Register */
/* RAZ/WI, like an implementation with only one AXI master */
break
;
case
0x8
:
/* CPU Power Status */
case
0x50
:
/* SCU Access Control Register */
case
0x54
:
/* SCU Non-secure Access Control Register */
/* unimplemented, fall through */
default:
break
;
}
}
static
const
MemoryRegionOps
a9_scu_ops
=
{
.
read
=
a9_scu_read
,
.
write
=
a9_scu_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
void
a9mpcore_timer_irq_handler
(
void
*
opaque
,
int
irq
,
int
level
)
{
a9mp_priv_state
*
s
=
(
a9mp_priv_state
*
)
opaque
;
if
(
level
&&
!
s
->
old_timer_status
[
irq
])
{
gic_set_pending_private
(
&
s
->
gic
,
irq
>>
1
,
29
+
(
irq
&
1
));
}
s
->
old_timer_status
[
irq
]
=
level
;
}
static
void
a9mp_priv_reset
(
DeviceState
*
dev
)
{
a9mp_priv_state
*
s
=
FROM_SYSBUSGIC
(
a9mp_priv_state
,
sysbus_from_qdev
(
dev
));
int
i
;
s
->
scu_control
=
0
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
s
->
old_timer_status
);
i
++
)
{
s
->
old_timer_status
[
i
]
=
0
;
}
}
static
int
a9mp_priv_init
(
SysBusDevice
*
dev
)
{
a9mp_priv_state
*
s
=
FROM_SYSBUSGIC
(
a9mp_priv_state
,
dev
);
SysBusDevice
*
busdev
;
int
i
;
if
(
s
->
num_cpu
>
NCPU
)
{
hw_error
(
"a9mp_priv_init: num-cpu may not be more than %d
\n
"
,
NCPU
);
}
gic_init
(
&
s
->
gic
,
s
->
num_cpu
);
s
->
mptimer
=
qdev_create
(
NULL
,
"arm_mptimer"
);
qdev_prop_set_uint32
(
s
->
mptimer
,
"num-cpu"
,
s
->
num_cpu
);
qdev_init_nofail
(
s
->
mptimer
);
busdev
=
sysbus_from_qdev
(
s
->
mptimer
);
/* Memory map (addresses are offsets from PERIPHBASE):
* 0x0000-0x00ff -- Snoop Control Unit
* 0x0100-0x01ff -- GIC CPU interface
* 0x0200-0x02ff -- Global Timer
* 0x0300-0x05ff -- nothing
* 0x0600-0x06ff -- private timers and watchdogs
* 0x0700-0x0fff -- nothing
* 0x1000-0x1fff -- GIC Distributor
*
* We should implement the global timer but don't currently do so.
*/
memory_region_init
(
&
s
->
container
,
"a9mp-priv-container"
,
0x2000
);
memory_region_init_io
(
&
s
->
scu_iomem
,
&
a9_scu_ops
,
s
,
"a9mp-scu"
,
0x100
);
memory_region_add_subregion
(
&
s
->
container
,
0
,
&
s
->
scu_iomem
);
/* GIC CPU interface */
memory_region_add_subregion
(
&
s
->
container
,
0x100
,
&
s
->
gic
.
cpuiomem
[
0
]);
/* Note that the A9 exposes only the "timer/watchdog for this core"
* memory region, not the "timer/watchdog for core X" ones 11MPcore has.
*/
memory_region_add_subregion
(
&
s
->
container
,
0x600
,
sysbus_mmio_get_region
(
busdev
,
0
));
memory_region_add_subregion
(
&
s
->
container
,
0x620
,
sysbus_mmio_get_region
(
busdev
,
1
));
memory_region_add_subregion
(
&
s
->
container
,
0x1000
,
&
s
->
gic
.
iomem
);
sysbus_init_mmio
(
dev
,
&
s
->
container
);
/* Wire up the interrupt from each watchdog and timer. */
s
->
timer_irq
=
qemu_allocate_irqs
(
a9mpcore_timer_irq_handler
,
s
,
(
s
->
num_cpu
+
1
)
*
2
);
for
(
i
=
0
;
i
<
s
->
num_cpu
*
2
;
i
++
)
{
sysbus_connect_irq
(
busdev
,
i
,
s
->
timer_irq
[
i
]);
}
return
0
;
}
static
const
VMStateDescription
vmstate_a9mp_priv
=
{
.
name
=
"a9mpcore_priv"
,
.
version_id
=
1
,
.
minimum_version_id
=
1
,
.
fields
=
(
VMStateField
[])
{
VMSTATE_UINT32
(
scu_control
,
a9mp_priv_state
),
VMSTATE_UINT32_ARRAY
(
old_timer_status
,
a9mp_priv_state
,
8
),
VMSTATE_END_OF_LIST
()
}
};
static
SysBusDeviceInfo
mpcore
_priv_info
=
{
.
init
=
mpcore
_priv_init
,
static
SysBusDeviceInfo
a9mp
_priv_info
=
{
.
init
=
a9mp
_priv_init
,
.
qdev
.
name
=
"a9mpcore_priv"
,
.
qdev
.
size
=
sizeof
(
mpcore_priv_state
),
.
qdev
.
size
=
sizeof
(
a9mp_priv_state
),
.
qdev
.
vmsd
=
&
vmstate_a9mp_priv
,
.
qdev
.
reset
=
a9mp_priv_reset
,
.
qdev
.
props
=
(
Property
[])
{
DEFINE_PROP_UINT32
(
"num-cpu"
,
mpcore
_priv_state
,
num_cpu
,
1
),
DEFINE_PROP_UINT32
(
"num-cpu"
,
a9mp
_priv_state
,
num_cpu
,
1
),
DEFINE_PROP_END_OF_LIST
(),
}
};
static
void
a9mp
core
_register_devices
(
void
)
static
void
a9mp_register_devices
(
void
)
{
sysbus_register_withprop
(
&
mpcore
_priv_info
);
sysbus_register_withprop
(
&
a9mp
_priv_info
);
}
device_init
(
a9mp
core
_register_devices
)
device_init
(
a9mp_register_devices
)
hw/arm11mpcore.c
浏览文件 @
ab23ebf4
...
...
@@ -7,11 +7,139 @@
* This code is licensed under the GPL.
*/
#include "sysbus.h"
#include "qemu-timer.h"
/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
(+ 32 internal). However my test chip only exposes/reports 32.
More importantly Linux falls over if more than 32 are present! */
#define GIC_NIRQ 64
#include "mpcore.c"
#define NCPU 4
static
inline
int
gic_get_current_cpu
(
void
)
{
return
cpu_single_env
->
cpu_index
;
}
#include "arm_gic.c"
/* MPCore private memory region. */
typedef
struct
mpcore_priv_state
{
gic_state
gic
;
uint32_t
scu_control
;
int
iomemtype
;
uint32_t
old_timer_status
[
8
];
uint32_t
num_cpu
;
qemu_irq
*
timer_irq
;
MemoryRegion
iomem
;
MemoryRegion
container
;
DeviceState
*
mptimer
;
}
mpcore_priv_state
;
/* Per-CPU private memory mapped IO. */
static
uint64_t
mpcore_scu_read
(
void
*
opaque
,
target_phys_addr_t
offset
,
unsigned
size
)
{
mpcore_priv_state
*
s
=
(
mpcore_priv_state
*
)
opaque
;
int
id
;
offset
&=
0xff
;
/* SCU */
switch
(
offset
)
{
case
0x00
:
/* Control. */
return
s
->
scu_control
;
case
0x04
:
/* Configuration. */
id
=
((
1
<<
s
->
num_cpu
)
-
1
)
<<
4
;
return
id
|
(
s
->
num_cpu
-
1
);
case
0x08
:
/* CPU status. */
return
0
;
case
0x0c
:
/* Invalidate all. */
return
0
;
default:
hw_error
(
"mpcore_priv_read: Bad offset %x
\n
"
,
(
int
)
offset
);
}
}
static
void
mpcore_scu_write
(
void
*
opaque
,
target_phys_addr_t
offset
,
uint64_t
value
,
unsigned
size
)
{
mpcore_priv_state
*
s
=
(
mpcore_priv_state
*
)
opaque
;
offset
&=
0xff
;
/* SCU */
switch
(
offset
)
{
case
0
:
/* Control register. */
s
->
scu_control
=
value
&
1
;
break
;
case
0x0c
:
/* Invalidate all. */
/* This is a no-op as cache is not emulated. */
break
;
default:
hw_error
(
"mpcore_priv_read: Bad offset %x
\n
"
,
(
int
)
offset
);
}
}
static
const
MemoryRegionOps
mpcore_scu_ops
=
{
.
read
=
mpcore_scu_read
,
.
write
=
mpcore_scu_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
void
mpcore_timer_irq_handler
(
void
*
opaque
,
int
irq
,
int
level
)
{
mpcore_priv_state
*
s
=
(
mpcore_priv_state
*
)
opaque
;
if
(
level
&&
!
s
->
old_timer_status
[
irq
])
{
gic_set_pending_private
(
&
s
->
gic
,
irq
>>
1
,
29
+
(
irq
&
1
));
}
s
->
old_timer_status
[
irq
]
=
level
;
}
static
void
mpcore_priv_map_setup
(
mpcore_priv_state
*
s
)
{
int
i
;
SysBusDevice
*
busdev
=
sysbus_from_qdev
(
s
->
mptimer
);
memory_region_init
(
&
s
->
container
,
"mpcode-priv-container"
,
0x2000
);
memory_region_init_io
(
&
s
->
iomem
,
&
mpcore_scu_ops
,
s
,
"mpcore-scu"
,
0x100
);
memory_region_add_subregion
(
&
s
->
container
,
0
,
&
s
->
iomem
);
/* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
* at 0x200, 0x300...
*/
for
(
i
=
0
;
i
<
(
s
->
num_cpu
+
1
);
i
++
)
{
target_phys_addr_t
offset
=
0x100
+
(
i
*
0x100
);
memory_region_add_subregion
(
&
s
->
container
,
offset
,
&
s
->
gic
.
cpuiomem
[
i
]);
}
/* Add the regions for timer and watchdog for "current CPU" and
* for each specific CPU.
*/
s
->
timer_irq
=
qemu_allocate_irqs
(
mpcore_timer_irq_handler
,
s
,
(
s
->
num_cpu
+
1
)
*
2
);
for
(
i
=
0
;
i
<
(
s
->
num_cpu
+
1
)
*
2
;
i
++
)
{
/* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
target_phys_addr_t
offset
=
0x600
+
(
i
>>
1
)
*
0x100
+
(
i
&
1
)
*
0x20
;
memory_region_add_subregion
(
&
s
->
container
,
offset
,
sysbus_mmio_get_region
(
busdev
,
i
));
}
memory_region_add_subregion
(
&
s
->
container
,
0x1000
,
&
s
->
gic
.
iomem
);
/* Wire up the interrupt from each watchdog and timer. */
for
(
i
=
0
;
i
<
s
->
num_cpu
*
2
;
i
++
)
{
sysbus_connect_irq
(
busdev
,
i
,
s
->
timer_irq
[
i
]);
}
}
static
int
mpcore_priv_init
(
SysBusDevice
*
dev
)
{
mpcore_priv_state
*
s
=
FROM_SYSBUSGIC
(
mpcore_priv_state
,
dev
);
gic_init
(
&
s
->
gic
,
s
->
num_cpu
);
s
->
mptimer
=
qdev_create
(
NULL
,
"arm_mptimer"
);
qdev_prop_set_uint32
(
s
->
mptimer
,
"num-cpu"
,
s
->
num_cpu
);
qdev_init_nofail
(
s
->
mptimer
);
mpcore_priv_map_setup
(
s
);
sysbus_init_mmio
(
dev
,
&
s
->
container
);
return
0
;
}
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
controllers. The output of these, plus some of the raw input lines
...
...
hw/arm_gic.c
浏览文件 @
ab23ebf4
...
...
@@ -103,7 +103,14 @@ typedef struct gic_state
int
num_cpu
;
#endif
MemoryRegion
iomem
;
MemoryRegion
iomem
;
/* Distributor */
#ifndef NVIC
/* This is just so we can have an opaque pointer which identifies
* both this GIC and which CPU interface we should be accessing.
*/
struct
gic_state
*
backref
[
NCPU
];
MemoryRegion
cpuiomem
[
NCPU
+
1
];
/* CPU interfaces */
#endif
}
gic_state
;
/* TODO: Many places that call this routine could be optimized. */
...
...
@@ -633,6 +640,54 @@ static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
}
gic_update
(
s
);
}
/* Wrappers to read/write the GIC CPU interface for the current CPU */
static
uint64_t
gic_thiscpu_read
(
void
*
opaque
,
target_phys_addr_t
addr
,
unsigned
size
)
{
gic_state
*
s
=
(
gic_state
*
)
opaque
;
return
gic_cpu_read
(
s
,
gic_get_current_cpu
(),
addr
&
0xff
);
}
static
void
gic_thiscpu_write
(
void
*
opaque
,
target_phys_addr_t
addr
,
uint64_t
value
,
unsigned
size
)
{
gic_state
*
s
=
(
gic_state
*
)
opaque
;
gic_cpu_write
(
s
,
gic_get_current_cpu
(),
addr
&
0xff
,
value
);
}
/* Wrappers to read/write the GIC CPU interface for a specific CPU.
* These just decode the opaque pointer into gic_state* + cpu id.
*/
static
uint64_t
gic_do_cpu_read
(
void
*
opaque
,
target_phys_addr_t
addr
,
unsigned
size
)
{
gic_state
**
backref
=
(
gic_state
**
)
opaque
;
gic_state
*
s
=
*
backref
;
int
id
=
(
backref
-
s
->
backref
);
return
gic_cpu_read
(
s
,
id
,
addr
&
0xff
);
}
static
void
gic_do_cpu_write
(
void
*
opaque
,
target_phys_addr_t
addr
,
uint64_t
value
,
unsigned
size
)
{
gic_state
**
backref
=
(
gic_state
**
)
opaque
;
gic_state
*
s
=
*
backref
;
int
id
=
(
backref
-
s
->
backref
);
gic_cpu_write
(
s
,
id
,
addr
&
0xff
,
value
);
}
static
const
MemoryRegionOps
gic_thiscpu_ops
=
{
.
read
=
gic_thiscpu_read
,
.
write
=
gic_thiscpu_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
const
MemoryRegionOps
gic_cpu_ops
=
{
.
read
=
gic_do_cpu_read
,
.
write
=
gic_do_cpu_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
#endif
static
void
gic_reset
(
gic_state
*
s
)
...
...
@@ -752,6 +807,24 @@ static void gic_init(gic_state *s)
sysbus_init_irq
(
&
s
->
busdev
,
&
s
->
parent_irq
[
i
]);
}
memory_region_init_io
(
&
s
->
iomem
,
&
gic_dist_ops
,
s
,
"gic_dist"
,
0x1000
);
#ifndef NVIC
/* Memory regions for the CPU interfaces (NVIC doesn't have these):
* a region for "CPU interface for this core", then a region for
* "CPU interface for core 0", "for core 1", ...
* NB that the memory region size of 0x100 applies for the 11MPCore
* and also cores following the GIC v1 spec (ie A9).
* GIC v2 defines a larger memory region (0x1000) so this will need
* to be extended when we implement A15.
*/
memory_region_init_io
(
&
s
->
cpuiomem
[
0
],
&
gic_thiscpu_ops
,
s
,
"gic_cpu"
,
0x100
);
for
(
i
=
0
;
i
<
NUM_CPU
(
s
);
i
++
)
{
s
->
backref
[
i
]
=
s
;
memory_region_init_io
(
&
s
->
cpuiomem
[
i
+
1
],
&
gic_cpu_ops
,
&
s
->
backref
[
i
],
"gic_cpu"
,
0x100
);
}
#endif
gic_reset
(
s
);
register_savevm
(
NULL
,
"arm_gic"
,
-
1
,
2
,
gic_save
,
gic_load
,
s
);
}
hw/arm_mptimer.c
0 → 100644
浏览文件 @
ab23ebf4
/*
* Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
*
* Copyright (c) 2006-2007 CodeSourcery.
* Copyright (c) 2011 Linaro Limited
* Written by Paul Brook, Peter Maydell
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "sysbus.h"
#include "qemu-timer.h"
/* This device implements the per-cpu private timer and watchdog block
* which is used in both the ARM11MPCore and Cortex-A9MP.
*/
#define MAX_CPUS 4
/* State of a single timer or watchdog block */
typedef
struct
{
uint32_t
count
;
uint32_t
load
;
uint32_t
control
;
uint32_t
status
;
int64_t
tick
;
QEMUTimer
*
timer
;
qemu_irq
irq
;
MemoryRegion
iomem
;
}
timerblock
;
typedef
struct
{
SysBusDevice
busdev
;
uint32_t
num_cpu
;
timerblock
timerblock
[
MAX_CPUS
*
2
];
MemoryRegion
iomem
[
2
];
}
arm_mptimer_state
;
static
inline
int
get_current_cpu
(
arm_mptimer_state
*
s
)
{
if
(
cpu_single_env
->
cpu_index
>=
s
->
num_cpu
)
{
hw_error
(
"arm_mptimer: num-cpu %d but this cpu is %d!
\n
"
,
s
->
num_cpu
,
cpu_single_env
->
cpu_index
);
}
return
cpu_single_env
->
cpu_index
;
}
static
inline
void
timerblock_update_irq
(
timerblock
*
tb
)
{
qemu_set_irq
(
tb
->
irq
,
tb
->
status
);
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
static
inline
uint32_t
timerblock_scale
(
timerblock
*
tb
)
{
return
(((
tb
->
control
>>
8
)
&
0xff
)
+
1
)
*
10
;
}
static
void
timerblock_reload
(
timerblock
*
tb
,
int
restart
)
{
if
(
tb
->
count
==
0
)
{
return
;
}
if
(
restart
)
{
tb
->
tick
=
qemu_get_clock_ns
(
vm_clock
);
}
tb
->
tick
+=
(
int64_t
)
tb
->
count
*
timerblock_scale
(
tb
);
qemu_mod_timer
(
tb
->
timer
,
tb
->
tick
);
}
static
void
timerblock_tick
(
void
*
opaque
)
{
timerblock
*
tb
=
(
timerblock
*
)
opaque
;
tb
->
status
=
1
;
if
(
tb
->
control
&
2
)
{
tb
->
count
=
tb
->
load
;
timerblock_reload
(
tb
,
0
);
}
else
{
tb
->
count
=
0
;
}
timerblock_update_irq
(
tb
);
}
static
uint64_t
timerblock_read
(
void
*
opaque
,
target_phys_addr_t
addr
,
unsigned
size
)
{
timerblock
*
tb
=
(
timerblock
*
)
opaque
;
int64_t
val
;
addr
&=
0x1f
;
switch
(
addr
)
{
case
0
:
/* Load */
return
tb
->
load
;
case
4
:
/* Counter. */
if
(((
tb
->
control
&
1
)
==
0
)
||
(
tb
->
count
==
0
))
{
return
0
;
}
/* Slow and ugly, but hopefully won't happen too often. */
val
=
tb
->
tick
-
qemu_get_clock_ns
(
vm_clock
);
val
/=
timerblock_scale
(
tb
);
if
(
val
<
0
)
{
val
=
0
;
}
return
val
;
case
8
:
/* Control. */
return
tb
->
control
;
case
12
:
/* Interrupt status. */
return
tb
->
status
;
default:
return
0
;
}
}
static
void
timerblock_write
(
void
*
opaque
,
target_phys_addr_t
addr
,
uint64_t
value
,
unsigned
size
)
{
timerblock
*
tb
=
(
timerblock
*
)
opaque
;
int64_t
old
;
addr
&=
0x1f
;
switch
(
addr
)
{
case
0
:
/* Load */
tb
->
load
=
value
;
/* Fall through. */
case
4
:
/* Counter. */
if
((
tb
->
control
&
1
)
&&
tb
->
count
)
{
/* Cancel the previous timer. */
qemu_del_timer
(
tb
->
timer
);
}
tb
->
count
=
value
;
if
(
tb
->
control
&
1
)
{
timerblock_reload
(
tb
,
1
);
}
break
;
case
8
:
/* Control. */
old
=
tb
->
control
;
tb
->
control
=
value
;
if
(((
old
&
1
)
==
0
)
&&
(
value
&
1
))
{
if
(
tb
->
count
==
0
&&
(
tb
->
control
&
2
))
{
tb
->
count
=
tb
->
load
;
}
timerblock_reload
(
tb
,
1
);
}
break
;
case
12
:
/* Interrupt status. */
tb
->
status
&=
~
value
;
timerblock_update_irq
(
tb
);
break
;
}
}
/* Wrapper functions to implement the "read timer/watchdog for
* the current CPU" memory regions.
*/
static
uint64_t
arm_thistimer_read
(
void
*
opaque
,
target_phys_addr_t
addr
,
unsigned
size
)
{
arm_mptimer_state
*
s
=
(
arm_mptimer_state
*
)
opaque
;
int
id
=
get_current_cpu
(
s
);
return
timerblock_read
(
&
s
->
timerblock
[
id
*
2
],
addr
,
size
);
}
static
void
arm_thistimer_write
(
void
*
opaque
,
target_phys_addr_t
addr
,
uint64_t
value
,
unsigned
size
)
{
arm_mptimer_state
*
s
=
(
arm_mptimer_state
*
)
opaque
;
int
id
=
get_current_cpu
(
s
);
timerblock_write
(
&
s
->
timerblock
[
id
*
2
],
addr
,
value
,
size
);
}
static
uint64_t
arm_thiswdog_read
(
void
*
opaque
,
target_phys_addr_t
addr
,
unsigned
size
)
{
arm_mptimer_state
*
s
=
(
arm_mptimer_state
*
)
opaque
;
int
id
=
get_current_cpu
(
s
);
return
timerblock_read
(
&
s
->
timerblock
[
id
*
2
+
1
],
addr
,
size
);
}
static
void
arm_thiswdog_write
(
void
*
opaque
,
target_phys_addr_t
addr
,
uint64_t
value
,
unsigned
size
)
{
arm_mptimer_state
*
s
=
(
arm_mptimer_state
*
)
opaque
;
int
id
=
get_current_cpu
(
s
);
timerblock_write
(
&
s
->
timerblock
[
id
*
2
+
1
],
addr
,
value
,
size
);
}
static
const
MemoryRegionOps
arm_thistimer_ops
=
{
.
read
=
arm_thistimer_read
,
.
write
=
arm_thistimer_write
,
.
valid
=
{
.
min_access_size
=
4
,
.
max_access_size
=
4
,
},
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
const
MemoryRegionOps
arm_thiswdog_ops
=
{
.
read
=
arm_thiswdog_read
,
.
write
=
arm_thiswdog_write
,
.
valid
=
{
.
min_access_size
=
4
,
.
max_access_size
=
4
,
},
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
const
MemoryRegionOps
timerblock_ops
=
{
.
read
=
timerblock_read
,
.
write
=
timerblock_write
,
.
valid
=
{
.
min_access_size
=
4
,
.
max_access_size
=
4
,
},
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
void
timerblock_reset
(
timerblock
*
tb
)
{
tb
->
count
=
0
;
tb
->
load
=
0
;
tb
->
control
=
0
;
tb
->
status
=
0
;
tb
->
tick
=
0
;
}
static
void
arm_mptimer_reset
(
DeviceState
*
dev
)
{
arm_mptimer_state
*
s
=
FROM_SYSBUS
(
arm_mptimer_state
,
sysbus_from_qdev
(
dev
));
int
i
;
/* We reset every timer in the array, not just the ones we're using,
* because vmsave will look at every array element.
*/
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
s
->
timerblock
);
i
++
)
{
timerblock_reset
(
&
s
->
timerblock
[
i
]);
}
}
static
int
arm_mptimer_init
(
SysBusDevice
*
dev
)
{
arm_mptimer_state
*
s
=
FROM_SYSBUS
(
arm_mptimer_state
,
dev
);
int
i
;
if
(
s
->
num_cpu
<
1
||
s
->
num_cpu
>
MAX_CPUS
)
{
hw_error
(
"%s: num-cpu must be between 1 and %d
\n
"
,
__func__
,
MAX_CPUS
);
}
/* We implement one timer and one watchdog block per CPU, and
* expose multiple MMIO regions:
* * region 0 is "timer for this core"
* * region 1 is "watchdog for this core"
* * region 2 is "timer for core 0"
* * region 3 is "watchdog for core 0"
* * region 4 is "timer for core 1"
* * region 5 is "watchdog for core 1"
* and so on.
* The outgoing interrupt lines are
* * timer for core 0
* * watchdog for core 0
* * timer for core 1
* * watchdog for core 1
* and so on.
*/
memory_region_init_io
(
&
s
->
iomem
[
0
],
&
arm_thistimer_ops
,
s
,
"arm_mptimer_timer"
,
0x20
);
sysbus_init_mmio
(
dev
,
&
s
->
iomem
[
0
]);
memory_region_init_io
(
&
s
->
iomem
[
1
],
&
arm_thiswdog_ops
,
s
,
"arm_mptimer_wdog"
,
0x20
);
sysbus_init_mmio
(
dev
,
&
s
->
iomem
[
1
]);
for
(
i
=
0
;
i
<
(
s
->
num_cpu
*
2
);
i
++
)
{
timerblock
*
tb
=
&
s
->
timerblock
[
i
];
tb
->
timer
=
qemu_new_timer_ns
(
vm_clock
,
timerblock_tick
,
tb
);
sysbus_init_irq
(
dev
,
&
tb
->
irq
);
memory_region_init_io
(
&
tb
->
iomem
,
&
timerblock_ops
,
tb
,
"arm_mptimer_timerblock"
,
0x20
);
sysbus_init_mmio
(
dev
,
&
tb
->
iomem
);
}
return
0
;
}
static
const
VMStateDescription
vmstate_timerblock
=
{
.
name
=
"arm_mptimer_timerblock"
,
.
version_id
=
1
,
.
minimum_version_id
=
1
,
.
fields
=
(
VMStateField
[])
{
VMSTATE_UINT32
(
count
,
timerblock
),
VMSTATE_UINT32
(
load
,
timerblock
),
VMSTATE_UINT32
(
control
,
timerblock
),
VMSTATE_UINT32
(
status
,
timerblock
),
VMSTATE_INT64
(
tick
,
timerblock
),
VMSTATE_END_OF_LIST
()
}
};
static
const
VMStateDescription
vmstate_arm_mptimer
=
{
.
name
=
"arm_mptimer"
,
.
version_id
=
1
,
.
minimum_version_id
=
1
,
.
fields
=
(
VMStateField
[])
{
VMSTATE_STRUCT_ARRAY
(
timerblock
,
arm_mptimer_state
,
(
MAX_CPUS
*
2
),
1
,
vmstate_timerblock
,
timerblock
),
VMSTATE_END_OF_LIST
()
}
};
static
SysBusDeviceInfo
arm_mptimer_info
=
{
.
init
=
arm_mptimer_init
,
.
qdev
.
name
=
"arm_mptimer"
,
.
qdev
.
size
=
sizeof
(
arm_mptimer_state
),
.
qdev
.
vmsd
=
&
vmstate_arm_mptimer
,
.
qdev
.
reset
=
arm_mptimer_reset
,
.
qdev
.
no_user
=
1
,
.
qdev
.
props
=
(
Property
[])
{
DEFINE_PROP_UINT32
(
"num-cpu"
,
arm_mptimer_state
,
num_cpu
,
0
),
DEFINE_PROP_END_OF_LIST
()
}
};
static
void
arm_mptimer_register_devices
(
void
)
{
sysbus_register_withprop
(
&
arm_mptimer_info
);
}
device_init
(
arm_mptimer_register_devices
)
hw/arm_timer.c
浏览文件 @
ab23ebf4
...
...
@@ -170,9 +170,9 @@ static arm_timer_state *arm_timer_init(uint32_t freq)
}
/* ARM PrimeCell SP804 dual timer module.
Docs for this device don't seem to be publicly available. This
implementation is based on guesswork, the linux kernel sources and the
Integrator/CP timer modules.
*/
* Docs at
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
*/
typedef
struct
{
SysBusDevice
busdev
;
...
...
@@ -182,6 +182,13 @@ typedef struct {
qemu_irq
irq
;
}
sp804_state
;
static
const
uint8_t
sp804_ids
[]
=
{
/* Timer ID */
0x04
,
0x18
,
0x14
,
0
,
/* PrimeCell ID */
0xd
,
0xf0
,
0x05
,
0xb1
};
/* Merge the IRQs from the two component devices. */
static
void
sp804_set_irq
(
void
*
opaque
,
int
irq
,
int
level
)
{
...
...
@@ -196,12 +203,27 @@ static uint64_t sp804_read(void *opaque, target_phys_addr_t offset,
{
sp804_state
*
s
=
(
sp804_state
*
)
opaque
;
/* ??? Don't know the PrimeCell ID for this device. */
if
(
offset
<
0x20
)
{
return
arm_timer_read
(
s
->
timer
[
0
],
offset
);
}
else
{
}
if
(
offset
<
0x40
)
{
return
arm_timer_read
(
s
->
timer
[
1
],
offset
-
0x20
);
}
/* TimerPeriphID */
if
(
offset
>=
0xfe0
&&
offset
<=
0xffc
)
{
return
sp804_ids
[(
offset
-
0xfe0
)
>>
2
];
}
switch
(
offset
)
{
/* Integration Test control registers, which we won't support */
case
0xf00
:
/* TimerITCR */
case
0xf04
:
/* TimerITOP (strictly write only but..) */
return
0
;
}
hw_error
(
"%s: Bad offset %x
\n
"
,
__func__
,
(
int
)
offset
);
return
0
;
}
static
void
sp804_write
(
void
*
opaque
,
target_phys_addr_t
offset
,
...
...
@@ -211,9 +233,16 @@ static void sp804_write(void *opaque, target_phys_addr_t offset,
if
(
offset
<
0x20
)
{
arm_timer_write
(
s
->
timer
[
0
],
offset
,
value
);
}
else
{
return
;
}
if
(
offset
<
0x40
)
{
arm_timer_write
(
s
->
timer
[
1
],
offset
-
0x20
,
value
);
return
;
}
/* Technically we could be writing to the Test Registers, but not likely */
hw_error
(
"%s: Bad offset %x
\n
"
,
__func__
,
(
int
)
offset
);
}
static
const
MemoryRegionOps
sp804_ops
=
{
...
...
hw/mpcore.c
已删除
100644 → 0
浏览文件 @
9bf4896e
/*
* ARM MPCore internal peripheral emulation (common code).
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*/
#include "sysbus.h"
#include "qemu-timer.h"
#define NCPU 4
static
inline
int
gic_get_current_cpu
(
void
)
{
return
cpu_single_env
->
cpu_index
;
}
#include "arm_gic.c"
/* MPCore private memory region. */
typedef
struct
{
uint32_t
count
;
uint32_t
load
;
uint32_t
control
;
uint32_t
status
;
uint32_t
old_status
;
int64_t
tick
;
QEMUTimer
*
timer
;
struct
mpcore_priv_state
*
mpcore
;
int
id
;
/* Encodes both timer/watchdog and CPU. */
}
mpcore_timer_state
;
typedef
struct
mpcore_priv_state
{
gic_state
gic
;
uint32_t
scu_control
;
int
iomemtype
;
mpcore_timer_state
timer
[
8
];
uint32_t
num_cpu
;
MemoryRegion
iomem
;
MemoryRegion
container
;
}
mpcore_priv_state
;
/* Per-CPU Timers. */
static
inline
void
mpcore_timer_update_irq
(
mpcore_timer_state
*
s
)
{
if
(
s
->
status
&
~
s
->
old_status
)
{
gic_set_pending_private
(
&
s
->
mpcore
->
gic
,
s
->
id
>>
1
,
29
+
(
s
->
id
&
1
));
}
s
->
old_status
=
s
->
status
;
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
static
inline
uint32_t
mpcore_timer_scale
(
mpcore_timer_state
*
s
)
{
return
(((
s
->
control
>>
8
)
&
0xff
)
+
1
)
*
10
;
}
static
void
mpcore_timer_reload
(
mpcore_timer_state
*
s
,
int
restart
)
{
if
(
s
->
count
==
0
)
return
;
if
(
restart
)
s
->
tick
=
qemu_get_clock_ns
(
vm_clock
);
s
->
tick
+=
(
int64_t
)
s
->
count
*
mpcore_timer_scale
(
s
);
qemu_mod_timer
(
s
->
timer
,
s
->
tick
);
}
static
void
mpcore_timer_tick
(
void
*
opaque
)
{
mpcore_timer_state
*
s
=
(
mpcore_timer_state
*
)
opaque
;
s
->
status
=
1
;
if
(
s
->
control
&
2
)
{
s
->
count
=
s
->
load
;
mpcore_timer_reload
(
s
,
0
);
}
else
{
s
->
count
=
0
;
}
mpcore_timer_update_irq
(
s
);
}
static
uint32_t
mpcore_timer_read
(
mpcore_timer_state
*
s
,
int
offset
)
{
int64_t
val
;
switch
(
offset
)
{
case
0
:
/* Load */
return
s
->
load
;
/* Fall through. */
case
4
:
/* Counter. */
if
(((
s
->
control
&
1
)
==
0
)
||
(
s
->
count
==
0
))
return
0
;
/* Slow and ugly, but hopefully won't happen too often. */
val
=
s
->
tick
-
qemu_get_clock_ns
(
vm_clock
);
val
/=
mpcore_timer_scale
(
s
);
if
(
val
<
0
)
val
=
0
;
return
val
;
case
8
:
/* Control. */
return
s
->
control
;
case
12
:
/* Interrupt status. */
return
s
->
status
;
default:
return
0
;
}
}
static
void
mpcore_timer_write
(
mpcore_timer_state
*
s
,
int
offset
,
uint32_t
value
)
{
int64_t
old
;
switch
(
offset
)
{
case
0
:
/* Load */
s
->
load
=
value
;
/* Fall through. */
case
4
:
/* Counter. */
if
((
s
->
control
&
1
)
&&
s
->
count
)
{
/* Cancel the previous timer. */
qemu_del_timer
(
s
->
timer
);
}
s
->
count
=
value
;
if
(
s
->
control
&
1
)
{
mpcore_timer_reload
(
s
,
1
);
}
break
;
case
8
:
/* Control. */
old
=
s
->
control
;
s
->
control
=
value
;
if
(((
old
&
1
)
==
0
)
&&
(
value
&
1
))
{
if
(
s
->
count
==
0
&&
(
s
->
control
&
2
))
s
->
count
=
s
->
load
;
mpcore_timer_reload
(
s
,
1
);
}
break
;
case
12
:
/* Interrupt status. */
s
->
status
&=
~
value
;
mpcore_timer_update_irq
(
s
);
break
;
}
}
static
void
mpcore_timer_init
(
mpcore_priv_state
*
mpcore
,
mpcore_timer_state
*
s
,
int
id
)
{
s
->
id
=
id
;
s
->
mpcore
=
mpcore
;
s
->
timer
=
qemu_new_timer_ns
(
vm_clock
,
mpcore_timer_tick
,
s
);
}
/* Per-CPU private memory mapped IO. */
static
uint64_t
mpcore_priv_read
(
void
*
opaque
,
target_phys_addr_t
offset
,
unsigned
size
)
{
mpcore_priv_state
*
s
=
(
mpcore_priv_state
*
)
opaque
;
int
id
;
offset
&=
0xfff
;
if
(
offset
<
0x100
)
{
/* SCU */
switch
(
offset
)
{
case
0x00
:
/* Control. */
return
s
->
scu_control
;
case
0x04
:
/* Configuration. */
id
=
((
1
<<
s
->
num_cpu
)
-
1
)
<<
4
;
return
id
|
(
s
->
num_cpu
-
1
);
case
0x08
:
/* CPU status. */
return
0
;
case
0x0c
:
/* Invalidate all. */
return
0
;
default:
goto
bad_reg
;
}
}
else
if
(
offset
<
0x600
)
{
/* Interrupt controller. */
if
(
offset
<
0x200
)
{
id
=
gic_get_current_cpu
();
}
else
{
id
=
(
offset
-
0x200
)
>>
8
;
if
(
id
>=
s
->
num_cpu
)
{
return
0
;
}
}
return
gic_cpu_read
(
&
s
->
gic
,
id
,
offset
&
0xff
);
}
else
if
(
offset
<
0xb00
)
{
/* Timers. */
if
(
offset
<
0x700
)
{
id
=
gic_get_current_cpu
();
}
else
{
id
=
(
offset
-
0x700
)
>>
8
;
if
(
id
>=
s
->
num_cpu
)
{
return
0
;
}
}
id
<<=
1
;
if
(
offset
&
0x20
)
id
++
;
return
mpcore_timer_read
(
&
s
->
timer
[
id
],
offset
&
0xf
);
}
bad_reg:
hw_error
(
"mpcore_priv_read: Bad offset %x
\n
"
,
(
int
)
offset
);
return
0
;
}
static
void
mpcore_priv_write
(
void
*
opaque
,
target_phys_addr_t
offset
,
uint64_t
value
,
unsigned
size
)
{
mpcore_priv_state
*
s
=
(
mpcore_priv_state
*
)
opaque
;
int
id
;
offset
&=
0xfff
;
if
(
offset
<
0x100
)
{
/* SCU */
switch
(
offset
)
{
case
0
:
/* Control register. */
s
->
scu_control
=
value
&
1
;
break
;
case
0x0c
:
/* Invalidate all. */
/* This is a no-op as cache is not emulated. */
break
;
default:
goto
bad_reg
;
}
}
else
if
(
offset
<
0x600
)
{
/* Interrupt controller. */
if
(
offset
<
0x200
)
{
id
=
gic_get_current_cpu
();
}
else
{
id
=
(
offset
-
0x200
)
>>
8
;
}
if
(
id
<
s
->
num_cpu
)
{
gic_cpu_write
(
&
s
->
gic
,
id
,
offset
&
0xff
,
value
);
}
}
else
if
(
offset
<
0xb00
)
{
/* Timers. */
if
(
offset
<
0x700
)
{
id
=
gic_get_current_cpu
();
}
else
{
id
=
(
offset
-
0x700
)
>>
8
;
}
if
(
id
<
s
->
num_cpu
)
{
id
<<=
1
;
if
(
offset
&
0x20
)
id
++
;
mpcore_timer_write
(
&
s
->
timer
[
id
],
offset
&
0xf
,
value
);
}
return
;
}
return
;
bad_reg:
hw_error
(
"mpcore_priv_read: Bad offset %x
\n
"
,
(
int
)
offset
);
}
static
const
MemoryRegionOps
mpcore_priv_ops
=
{
.
read
=
mpcore_priv_read
,
.
write
=
mpcore_priv_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
void
mpcore_priv_map_setup
(
mpcore_priv_state
*
s
)
{
memory_region_init
(
&
s
->
container
,
"mpcode-priv-container"
,
0x2000
);
memory_region_init_io
(
&
s
->
iomem
,
&
mpcore_priv_ops
,
s
,
"mpcode-priv"
,
0x1000
);
memory_region_add_subregion
(
&
s
->
container
,
0
,
&
s
->
iomem
);
memory_region_add_subregion
(
&
s
->
container
,
0x1000
,
&
s
->
gic
.
iomem
);
}
static
int
mpcore_priv_init
(
SysBusDevice
*
dev
)
{
mpcore_priv_state
*
s
=
FROM_SYSBUSGIC
(
mpcore_priv_state
,
dev
);
int
i
;
gic_init
(
&
s
->
gic
,
s
->
num_cpu
);
mpcore_priv_map_setup
(
s
);
sysbus_init_mmio
(
dev
,
&
s
->
container
);
for
(
i
=
0
;
i
<
s
->
num_cpu
*
2
;
i
++
)
{
mpcore_timer_init
(
s
,
&
s
->
timer
[
i
],
i
);
}
return
0
;
}
hw/realview_gic.c
浏览文件 @
ab23ebf4
...
...
@@ -23,36 +23,13 @@ gic_get_current_cpu(void)
typedef
struct
{
gic_state
gic
;
MemoryRegion
iomem
;
MemoryRegion
container
;
}
RealViewGICState
;
static
uint64_t
realview_gic_cpu_read
(
void
*
opaque
,
target_phys_addr_t
offset
,
unsigned
size
)
{
gic_state
*
s
=
(
gic_state
*
)
opaque
;
return
gic_cpu_read
(
s
,
gic_get_current_cpu
(),
offset
);
}
static
void
realview_gic_cpu_write
(
void
*
opaque
,
target_phys_addr_t
offset
,
uint64_t
value
,
unsigned
size
)
{
gic_state
*
s
=
(
gic_state
*
)
opaque
;
gic_cpu_write
(
s
,
gic_get_current_cpu
(),
offset
,
value
);
}
static
const
MemoryRegionOps
realview_gic_cpu_ops
=
{
.
read
=
realview_gic_cpu_read
,
.
write
=
realview_gic_cpu_write
,
.
endianness
=
DEVICE_NATIVE_ENDIAN
,
};
static
void
realview_gic_map_setup
(
RealViewGICState
*
s
)
{
memory_region_init
(
&
s
->
container
,
"realview-gic-container"
,
0x2000
);
memory_region_init_io
(
&
s
->
iomem
,
&
realview_gic_cpu_ops
,
&
s
->
gic
,
"realview-gic"
,
0x1000
);
memory_region_add_subregion
(
&
s
->
container
,
0
,
&
s
->
iomem
);
memory_region_add_subregion
(
&
s
->
container
,
0
,
&
s
->
gic
.
cpuiomem
[
0
]);
memory_region_add_subregion
(
&
s
->
container
,
0x1000
,
&
s
->
gic
.
iomem
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录