Merge tag 'v9.0.0-rc3'
[qemu/ar7.git] / hw / arm / s3c24xx_clkcon.c
blob7f6d982f4b01f7148fb7942a3719c13d4222fa79
1 /* hw/s3c24xx_clkcon.c
3 * Samsung S3C24XX Clock control emulation
5 * Copyright 2006, 2007, 2008 Daniel Silverstone and Vincent Sanders
7 * Copyright 2010, 2013, 2020 Stefan Weil
9 * This file is under the terms of the GNU General Public License Version 2.
12 #include "qemu/osdep.h"
13 #include "cpu.h"
14 #include "hw/hw.h"
15 #include "exec/address-spaces.h" /* get_system_memory */
16 #include "migration/qemu-file-types.h" /* qemu_put_be32s */
17 #include "migration/register.h" /* register_savevm_live */
19 #include "s3c24xx.h"
21 /* Lock time RW */
22 #define S3C_REG_LOCKTIME 0
24 /* MPLL Control RW */
25 #define S3C_REG_MPLLCON 1
27 /* UPLL Control RW */
28 #define S3C_REG_UPLLCON 2
30 /* Clock Generator Control RW */
31 #define S3C_REG_CLKCON 3
33 /* CLKCON IDLE */
34 #define S3C_REG_CLKCON_IDLE (1<<2)
36 /* Slow Clock Control RW */
37 #define S3C_REG_CLKSLOW 4
39 /* Clock divider control RW */
40 #define S3C_REG_CLKDIVN 5
42 /* Clock controller state */
43 struct s3c24xx_clkcon_state_s {
44 MemoryRegion mmio;
45 CPUARMState *cpu_env;
46 uint32_t ref_freq; /* frequency of reference xtal or extclock */
47 uint32_t clkcon_reg[7];
50 static void s3c24xx_clkcon_write(void *opaque, hwaddr addr_,
51 uint64_t value, unsigned size)
53 struct s3c24xx_clkcon_state_s *s = opaque;
54 unsigned addr = (addr_ & 0x1F) >> 2;
55 int idle_rising_edge = 0;
57 assert(addr < ARRAY_SIZE(s->clkcon_reg));
59 if (addr == S3C_REG_CLKCON) {
60 if (!(s->clkcon_reg[addr] & S3C_REG_CLKCON_IDLE) &&
61 (value & S3C_REG_CLKCON_IDLE))
62 idle_rising_edge = 1;
65 s->clkcon_reg[addr] = value;
67 if (idle_rising_edge) {
68 cpu_interrupt(CPU(s), CPU_INTERRUPT_HALT);
72 static uint64_t s3c24xx_clkcon_read(void *opaque, hwaddr addr_,
73 unsigned size)
75 struct s3c24xx_clkcon_state_s *s = opaque;
76 unsigned addr = (addr_ & 0x1F) >> 2;
78 assert(addr < ARRAY_SIZE(s->clkcon_reg));
80 return s->clkcon_reg[addr];
83 static const MemoryRegionOps s3c24xx_clkcon_ops = {
84 .read = s3c24xx_clkcon_read,
85 .write = s3c24xx_clkcon_write,
86 .endianness = DEVICE_NATIVE_ENDIAN,
87 .valid = {
88 .min_access_size = 1,
89 .max_access_size = 4
93 static void s3c24xx_clkcon_save(QEMUFile *f, void *opaque)
95 struct s3c24xx_clkcon_state_s *s = (struct s3c24xx_clkcon_state_s *)opaque;
96 int i;
98 for (i = 0; i < ARRAY_SIZE(s->clkcon_reg); i ++) {
99 qemu_put_be32s(f, &s->clkcon_reg[i]);
103 static int s3c24xx_clkcon_load(QEMUFile *f, void *opaque, int version_id)
105 struct s3c24xx_clkcon_state_s *s = opaque;
106 int i;
108 for (i = 0; i < ARRAY_SIZE(s->clkcon_reg); i ++) {
109 qemu_get_be32s(f, &s->clkcon_reg[i]);
112 return 0;
115 static SaveVMHandlers savevm_s3c24xx_clkcon = {
116 .save_state = s3c24xx_clkcon_save,
117 .load_state = s3c24xx_clkcon_load
120 struct s3c24xx_clkcon_state_s *
121 s3c24xx_clkcon_init(S3CState *soc, hwaddr base_addr, uint32_t ref_freq)
123 struct s3c24xx_clkcon_state_s *s = g_new0(struct s3c24xx_clkcon_state_s, 1);
125 memory_region_init_io(&s->mmio, OBJECT(s), &s3c24xx_clkcon_ops, s,
126 "s3c24xx.clkcon", ARRAY_SIZE(s->clkcon_reg) * 4);
127 memory_region_add_subregion(get_system_memory(), base_addr, &s->mmio);
128 register_savevm_live("s3c24xx_clkcon", 0, 0, &savevm_s3c24xx_clkcon, s);
130 s->cpu_env = &soc->cpu->env;
131 s->ref_freq = ref_freq;
133 /* initialise register values to power on defaults */
134 s->clkcon_reg[S3C_REG_LOCKTIME] = 0x00FFFFFF;
135 s->clkcon_reg[S3C_REG_MPLLCON] = 0x0005C080;
136 s->clkcon_reg[S3C_REG_UPLLCON] = 0x00028080;
137 s->clkcon_reg[S3C_REG_CLKCON] = 0x0007FFF0;
138 s->clkcon_reg[S3C_REG_CLKSLOW] = 0x00000004;
139 s->clkcon_reg[S3C_REG_CLKDIVN] = 0x00000000;
141 return s;