aboutsummaryrefslogtreecommitdiff
path: root/hw/arm/stellaris.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm/stellaris.c')
-rw-r--r--hw/arm/stellaris.c170
1 files changed, 129 insertions, 41 deletions
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 652823195b..ad72c0959f 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -26,6 +26,7 @@
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "migration/vmstate.h"
#include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
#include "cpu.h"
#include "qom/object.h"
@@ -357,7 +358,12 @@ static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
/* System controller. */
-typedef struct {
+#define TYPE_STELLARIS_SYS "stellaris-sys"
+OBJECT_DECLARE_SIMPLE_TYPE(ssys_state, STELLARIS_SYS)
+
+struct ssys_state {
+ SysBusDevice parent_obj;
+
MemoryRegion iomem;
uint32_t pborctl;
uint32_t ldopctl;
@@ -371,11 +377,19 @@ typedef struct {
uint32_t dcgc[3];
uint32_t clkvclr;
uint32_t ldoarst;
+ qemu_irq irq;
+ Clock *sysclk;
+ /* Properties (all read-only registers) */
uint32_t user0;
uint32_t user1;
- qemu_irq irq;
- stellaris_board_info *board;
-} ssys_state;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc0;
+ uint32_t dc1;
+ uint32_t dc2;
+ uint32_t dc3;
+ uint32_t dc4;
+};
static void ssys_update(ssys_state *s)
{
@@ -430,7 +444,7 @@ static uint32_t pllcfg_fury[16] = {
static int ssys_board_class(const ssys_state *s)
{
- uint32_t did0 = s->board->did0;
+ uint32_t did0 = s->did0;
switch (did0 & DID0_VER_MASK) {
case DID0_VER_0:
return DID0_CLASS_SANDSTORM;
@@ -456,19 +470,19 @@ static uint64_t ssys_read(void *opaque, hwaddr offset,
switch (offset) {
case 0x000: /* DID0 */
- return s->board->did0;
+ return s->did0;
case 0x004: /* DID1 */
- return s->board->did1;
+ return s->did1;
case 0x008: /* DC0 */
- return s->board->dc0;
+ return s->dc0;
case 0x010: /* DC1 */
- return s->board->dc1;
+ return s->dc1;
case 0x014: /* DC2 */
- return s->board->dc2;
+ return s->dc2;
case 0x018: /* DC3 */
- return s->board->dc3;
+ return s->dc3;
case 0x01c: /* DC4 */
- return s->board->dc4;
+ return s->dc4;
case 0x030: /* PBORCTL */
return s->pborctl;
case 0x034: /* LDOPCTL */
@@ -543,15 +557,26 @@ static bool ssys_use_rcc2(ssys_state *s)
}
/*
- * Caculate the sys. clock period in ms.
+ * Calculate the system clock period. We only want to propagate
+ * this change to the rest of the system if we're not being called
+ * from migration post-load.
*/
-static void ssys_calculate_system_clock(ssys_state *s)
+static void ssys_calculate_system_clock(ssys_state *s, bool propagate_clock)
{
+ /*
+ * SYSDIV field specifies divisor: 0 == /1, 1 == /2, etc. Input
+ * clock is 200MHz, which is a period of 5 ns. Dividing the clock
+ * frequency by X is the same as multiplying the period by X.
+ */
if (ssys_use_rcc2(s)) {
system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
} else {
system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
}
+ clock_set_ns(s->sysclk, system_clock_scale);
+ if (propagate_clock) {
+ clock_propagate(s->sysclk);
+ }
}
static void ssys_write(void *opaque, hwaddr offset,
@@ -586,7 +611,7 @@ static void ssys_write(void *opaque, hwaddr offset,
s->int_status |= (1 << 6);
}
s->rcc = value;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, true);
break;
case 0x070: /* RCC2 */
if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) {
@@ -598,7 +623,7 @@ static void ssys_write(void *opaque, hwaddr offset,
s->int_status |= (1 << 6);
}
s->rcc2 = value;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, true);
break;
case 0x100: /* RCGC0 */
s->rcgc[0] = value;
@@ -646,9 +671,9 @@ static const MemoryRegionOps ssys_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void ssys_reset(void *opaque)
+static void stellaris_sys_reset_enter(Object *obj, ResetType type)
{
- ssys_state *s = (ssys_state *)opaque;
+ ssys_state *s = STELLARIS_SYS(obj);
s->pborctl = 0x7ffd;
s->rcc = 0x078e3ac0;
@@ -661,14 +686,25 @@ static void ssys_reset(void *opaque)
s->rcgc[0] = 1;
s->scgc[0] = 1;
s->dcgc[0] = 1;
- ssys_calculate_system_clock(s);
+}
+
+static void stellaris_sys_reset_hold(Object *obj)
+{
+ ssys_state *s = STELLARIS_SYS(obj);
+
+ /* OK to propagate clocks from the hold phase */
+ ssys_calculate_system_clock(s, true);
+}
+
+static void stellaris_sys_reset_exit(Object *obj)
+{
}
static int stellaris_sys_post_load(void *opaque, int version_id)
{
ssys_state *s = opaque;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, false);
return 0;
}
@@ -691,30 +727,61 @@ static const VMStateDescription vmstate_stellaris_sys = {
VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3),
VMSTATE_UINT32(clkvclr, ssys_state),
VMSTATE_UINT32(ldoarst, ssys_state),
+ /* No field for sysclk -- handled in post-load instead */
VMSTATE_END_OF_LIST()
}
};
-static int stellaris_sys_init(uint32_t base, qemu_irq irq,
- stellaris_board_info * board,
- uint8_t *macaddr)
-{
- ssys_state *s;
+static Property stellaris_sys_properties[] = {
+ DEFINE_PROP_UINT32("user0", ssys_state, user0, 0),
+ DEFINE_PROP_UINT32("user1", ssys_state, user1, 0),
+ DEFINE_PROP_UINT32("did0", ssys_state, did0, 0),
+ DEFINE_PROP_UINT32("did1", ssys_state, did1, 0),
+ DEFINE_PROP_UINT32("dc0", ssys_state, dc0, 0),
+ DEFINE_PROP_UINT32("dc1", ssys_state, dc1, 0),
+ DEFINE_PROP_UINT32("dc2", ssys_state, dc2, 0),
+ DEFINE_PROP_UINT32("dc3", ssys_state, dc3, 0),
+ DEFINE_PROP_UINT32("dc4", ssys_state, dc4, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
- s = g_new0(ssys_state, 1);
- s->irq = irq;
- s->board = board;
- /* Most devices come preprogrammed with a MAC address in the user data. */
- s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
- s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+static void stellaris_sys_instance_init(Object *obj)
+{
+ ssys_state *s = STELLARIS_SYS(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
- memory_region_init_io(&s->iomem, NULL, &ssys_ops, s, "ssys", 0x00001000);
- memory_region_add_subregion(get_system_memory(), base, &s->iomem);
- ssys_reset(s);
- vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_stellaris_sys, s);
- return 0;
+ memory_region_init_io(&s->iomem, obj, &ssys_ops, s, "ssys", 0x00001000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK");
}
+static DeviceState *stellaris_sys_init(uint32_t base, qemu_irq irq,
+ stellaris_board_info *board,
+ uint8_t *macaddr)
+{
+ DeviceState *dev = qdev_new(TYPE_STELLARIS_SYS);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ /* Most devices come preprogrammed with a MAC address in the user data. */
+ qdev_prop_set_uint32(dev, "user0",
+ macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
+ qdev_prop_set_uint32(dev, "user1",
+ macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
+ qdev_prop_set_uint32(dev, "did0", board->did0);
+ qdev_prop_set_uint32(dev, "did1", board->did1);
+ qdev_prop_set_uint32(dev, "dc0", board->dc0);
+ qdev_prop_set_uint32(dev, "dc1", board->dc1);
+ qdev_prop_set_uint32(dev, "dc2", board->dc2);
+ qdev_prop_set_uint32(dev, "dc3", board->dc3);
+ qdev_prop_set_uint32(dev, "dc4", board->dc4);
+
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, irq);
+
+ return dev;
+}
/* I2C controller. */
@@ -1280,6 +1347,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
int flash_size;
I2CBus *i2c;
DeviceState *dev;
+ DeviceState *ssys_dev;
int i;
int j;
@@ -1330,16 +1398,15 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
}
}
- stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
- board, nd_table[0].macaddr.a);
+ ssys_dev = stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
+ board, nd_table[0].macaddr.a);
if (board->dc1 & (1 << 3)) { /* watchdog present */
dev = qdev_new(TYPE_LUMINARY_WATCHDOG);
- /* system_clock_scale is valid now */
- uint32_t mainclk = NANOSECONDS_PER_SECOND / system_clock_scale;
- qdev_prop_set_uint32(dev, "wdogclk-frq", mainclk);
+ qdev_connect_clock_in(dev, "WDOGCLK",
+ qdev_get_clock_out(ssys_dev, "SYSCLK"));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev),
@@ -1553,11 +1620,32 @@ static const TypeInfo stellaris_adc_info = {
.class_init = stellaris_adc_class_init,
};
+static void stellaris_sys_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stellaris_sys;
+ rc->phases.enter = stellaris_sys_reset_enter;
+ rc->phases.hold = stellaris_sys_reset_hold;
+ rc->phases.exit = stellaris_sys_reset_exit;
+ device_class_set_props(dc, stellaris_sys_properties);
+}
+
+static const TypeInfo stellaris_sys_info = {
+ .name = TYPE_STELLARIS_SYS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ssys_state),
+ .instance_init = stellaris_sys_instance_init,
+ .class_init = stellaris_sys_class_init,
+};
+
static void stellaris_register_types(void)
{
type_register_static(&stellaris_i2c_info);
type_register_static(&stellaris_gptm_info);
type_register_static(&stellaris_adc_info);
+ type_register_static(&stellaris_sys_info);
}
type_init(stellaris_register_types)