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"
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 */
22 #define S3C_REG_LOCKTIME 0
25 #define S3C_REG_MPLLCON 1
28 #define S3C_REG_UPLLCON 2
30 /* Clock Generator Control RW */
31 #define S3C_REG_CLKCON 3
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
{
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
))
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_
,
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
,
93 static void s3c24xx_clkcon_save(QEMUFile
*f
, void *opaque
)
95 struct s3c24xx_clkcon_state_s
*s
= (struct s3c24xx_clkcon_state_s
*)opaque
;
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
;
108 for (i
= 0; i
< ARRAY_SIZE(s
->clkcon_reg
); i
++) {
109 qemu_get_be32s(f
, &s
->clkcon_reg
[i
]);
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;