Update GitHub action for new Meson based build
[qemu/ar7.git] / hw / arm / s3c24xx_timers.c
blobf712b25fa64d40940d2f251c9c8f2c179ffa762e
1 /* hw/s3c24xx_timers.c
3 * Samsung S3C24XX PWM emulation
5 * Copyright 2009 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 "exec/address-spaces.h" /* get_system_memory */
15 #include "hw/hw.h"
16 #include "hw/irq.h" /* qemu_set_irq */
17 #include "migration/qemu-file-types.h" /* qemu_put_be32s */
18 #include "migration/register.h" /* register_savevm_live */
19 #include "qemu/timer.h"
21 #include "s3c24xx.h"
23 /* The S3c24xx timer peripheral has five separate timers. The first four (0-3)
24 * have physical external connections and can be used for PWM control. The
25 * fifth has no external connection but can generate interrupts because of this
26 * it is almost always used to generate the Operating system clock tick
27 * interrupt.
29 * The timers can be fed from the peripheral clock (pclk) or from one of two
30 * external inputs (tclk0 and 1). The external inputs are split so tclk0 is
31 * used for timer 0 and 1 and tclk1 feeds the remaining three timers.
33 * The emulation presented here only iplements the fifth timer (timer 4) as
34 * there is no sensible way to interpret the external physical PWM signals from
35 * timers 0 to 4 yet.
37 * ticks_per_sec is ticks per second for the qemu clocks
38 * TCLK1 is the assumed input for timer4
39 * Thus, period in ticks of timer4 is:
41 * (timer4_period * ticks_per_sec) / TCLK1
44 /* Timer configuration 0 */
45 #define S3C_TIMERS_TCFG0 0
46 /* Timer configuration 1 */
47 #define S3C_TIMERS_TCFG1 1
48 /* Timer control */
49 #define S3C_TIMERS_TCON 2
50 /* Timer count buffer 0 */
51 #define S3C_TIMERS_TCNTB0 3
52 /* Timer compare buffer 0 */
53 #define S3C_TIMERS_TCMPB0 4
54 /* Timer count observation 0 */
55 #define S3C_TIMERS_TCNTO0 5
56 /* Timer count buffer 1 */
57 #define S3C_TIMERS_TCNTB1 6
58 /* Timer compare buffer 1 */
59 #define S3C_TIMERS_TCMPB1 7
60 /* Timer count observation 1 */
61 #define S3C_TIMERS_TCNTO1 8
62 /* Timer count buffer 2 */
63 #define S3C_TIMERS_TCNTB2 9
64 /* Timer compare buffer 2 */
65 #define S3C_TIMERS_TCMPB2 10
66 /* Timer count observation 2 */
67 #define S3C_TIMERS_TCNTO2 11
68 /* Timer count buffer 3 */
69 #define S3C_TIMERS_TCNTB3 12
70 /* Timer compare buffer 3 */
71 #define S3C_TIMERS_TCMPB3 13
72 /* Timer count observation 3 */
73 #define S3C_TIMERS_TCNTO3 14
74 /* Timer count buffer 4 */
75 #define S3C_TIMERS_TCNTB4 15
76 /* Timer count observation 4 */
77 #define S3C_TIMERS_TCNTO4 16
79 /* timer controller state */
80 struct s3c24xx_timers_state_s {
81 MemoryRegion mmio;
82 uint32_t tclk0; /* first timer clock source frequency */
83 uint32_t tclk1; /* second timer clock source frequency */
85 uint32_t timers_reg[17]; /* registers */
87 /* resources for each timer */
88 QEMUTimer *timer[5];
89 qemu_irq irqs[5];
90 uint32_t timer_reload_value[5];
91 int64_t timer_last_ticked[5];
96 static void
97 s3c24xx_schedule_timer(struct s3c24xx_timers_state_s *s, int num)
99 s->timers_reg[S3C_TIMERS_TCNTB4] = s->timer_reload_value[num];
100 s->timer_last_ticked[num] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
101 timer_mod(s->timer[num],
102 s->timer_last_ticked[num] +
103 ((s->timer_reload_value[num] * NANOSECONDS_PER_SECOND) / s->tclk1));
106 static void
107 s3c24xx_timer4_tick(void *opaque)
109 struct s3c24xx_timers_state_s *s = (struct s3c24xx_timers_state_s *)opaque;
111 /* set IRQ */
112 qemu_set_irq(s->irqs[4], 1);
114 /* if auto reload is set rescedule the next tick */
115 if (s->timers_reg[S3C_TIMERS_TCON] & (1<<22)) {
116 s3c24xx_schedule_timer(s, 4);
120 static void s3c24xx_timers_write_f(void *opaque, hwaddr addr_,
121 uint64_t value, unsigned size)
123 struct s3c24xx_timers_state_s *s = opaque;
124 int addr = (addr_ >> 2) & 0x1f;
126 s->timers_reg[addr] = value;
128 if (addr == S3C_TIMERS_TCON) {
129 if (value & (1 << 21)) {
130 /* Timer4 manual update is set, copy in the reload value */
131 s->timer_reload_value[4] = s->timers_reg[S3C_TIMERS_TCNTB4];
132 } else {
133 /* Timer4 manual update is not set */
134 if (value & (1 << 20)) {
135 /* The timer is supposed to be running so start it */
136 s3c24xx_schedule_timer(s, 4);
142 static uint64_t
143 s3c24xx_timers_read_f(void *opaque, hwaddr addr_, unsigned size)
145 struct s3c24xx_timers_state_s *s = opaque;
146 int addr = (addr_ >> 2) & 0x1f;
148 if (addr == S3C_TIMERS_TCNTO4 ) {
149 return s->timer_reload_value[4] -
150 (((qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->timer_last_ticked[4]) * s->tclk1) / NANOSECONDS_PER_SECOND);
152 return s->timers_reg[addr];
155 static const MemoryRegionOps s3c24xx_timers_ops = {
156 .read = s3c24xx_timers_read_f,
157 .write = s3c24xx_timers_write_f,
158 .endianness = DEVICE_NATIVE_ENDIAN,
159 .valid = {
160 .min_access_size = 1,
161 .max_access_size = 4
165 static void s3c24xx_timers_save(QEMUFile *f, void *opaque)
167 struct s3c24xx_timers_state_s *s = (struct s3c24xx_timers_state_s *)opaque;
168 int i;
170 for (i = 0; i < 17; i ++)
171 qemu_put_be32s(f, &s->timers_reg[i]);
174 static int s3c24xx_timers_load(QEMUFile *f, void *opaque, int version_id)
176 struct s3c24xx_timers_state_s *s = (struct s3c24xx_timers_state_s *)opaque;
177 int i;
179 for (i = 0; i < 17; i ++)
180 qemu_get_be32s(f, &s->timers_reg[i]);
182 return 0;
185 static SaveVMHandlers savevm_s3c24xx_timers = {
186 .save_state = s3c24xx_timers_save,
187 .load_state = s3c24xx_timers_load
190 /* S3c24xx timer initialisation */
191 struct s3c24xx_timers_state_s *
192 s3c24xx_timers_init(S3CState *soc, hwaddr base_addr, uint32_t tclk0, uint32_t tclk1)
194 MemoryRegion *system_memory = get_system_memory();
195 struct s3c24xx_timers_state_s *s;
196 int i;
198 s = g_malloc0(sizeof(struct s3c24xx_timers_state_s));
200 memory_region_init_io(&s->mmio, OBJECT(s),
201 &s3c24xx_timers_ops, s, "s3c24xx-timers", 17 * 4);
202 memory_region_add_subregion(system_memory, base_addr, &s->mmio);
204 register_savevm_live("s3c24xx_timers", 0, 0,
205 &savevm_s3c24xx_timers, s);
207 s->tclk0 = tclk0;
208 s->tclk1 = tclk1;
210 /* set up per timer values */
211 for (i = 0; i < 5; i++) {
212 s->irqs[i] = s3c24xx_get_irq(soc->irq, 10 + i);
213 s->timer_reload_value[i] = 0;
214 s->timer_last_ticked[i] = 0;
217 s->timer[4] = timer_new_ns(QEMU_CLOCK_VIRTUAL, s3c24xx_timer4_tick, s);
219 return s;