3 * Samsung S3C24XX PWM emulation
5 * Copyright 2009 Daniel Silverstone and Vincent Sanders
7 * Copyright 2010, 2013 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/register.h" /* register_savevm_live */
17 #include "qemu/timer.h"
21 /* The S3c24xx timer peripheral has five separate timers. The first four (0-3)
22 * have physical external connections and can be used for PWM control. The
23 * fifth has no external connection but can generate interrupts because of this
24 * it is almost always used to generate the Operating system clock tick
27 * The timers can be fed from the peripheral clock (pclk) or from one of two
28 * external inputs (tclk0 and 1). The external inputs are split so tclk0 is
29 * used for timer 0 and 1 and tclk1 feeds the remaining three timers.
31 * The emulation presented here only iplements the fifth timer (timer 4) as
32 * there is no sensible way to interpret the external physical PWM signals from
35 * ticks_per_sec is ticks per second for the qemu clocks
36 * TCLK1 is the assumed input for timer4
37 * Thus, period in ticks of timer4 is:
39 * (timer4_period * ticks_per_sec) / TCLK1
42 /* Timer configuration 0 */
43 #define S3C_TIMERS_TCFG0 0
44 /* Timer configuration 1 */
45 #define S3C_TIMERS_TCFG1 1
47 #define S3C_TIMERS_TCON 2
48 /* Timer count buffer 0 */
49 #define S3C_TIMERS_TCNTB0 3
50 /* Timer compare buffer 0 */
51 #define S3C_TIMERS_TCMPB0 4
52 /* Timer count observation 0 */
53 #define S3C_TIMERS_TCNTO0 5
54 /* Timer count buffer 1 */
55 #define S3C_TIMERS_TCNTB1 6
56 /* Timer compare buffer 1 */
57 #define S3C_TIMERS_TCMPB1 7
58 /* Timer count observation 1 */
59 #define S3C_TIMERS_TCNTO1 8
60 /* Timer count buffer 2 */
61 #define S3C_TIMERS_TCNTB2 9
62 /* Timer compare buffer 2 */
63 #define S3C_TIMERS_TCMPB2 10
64 /* Timer count observation 2 */
65 #define S3C_TIMERS_TCNTO2 11
66 /* Timer count buffer 3 */
67 #define S3C_TIMERS_TCNTB3 12
68 /* Timer compare buffer 3 */
69 #define S3C_TIMERS_TCMPB3 13
70 /* Timer count observation 3 */
71 #define S3C_TIMERS_TCNTO3 14
72 /* Timer count buffer 4 */
73 #define S3C_TIMERS_TCNTB4 15
74 /* Timer count observation 4 */
75 #define S3C_TIMERS_TCNTO4 16
77 /* timer controller state */
78 struct s3c24xx_timers_state_s
{
80 uint32_t tclk0
; /* first timer clock source frequency */
81 uint32_t tclk1
; /* second timer clock source frequency */
83 uint32_t timers_reg
[17]; /* registers */
85 /* resources for each timer */
88 uint32_t timer_reload_value
[5];
89 int64_t timer_last_ticked
[5];
95 s3c24xx_schedule_timer(struct s3c24xx_timers_state_s
*s
, int num
)
97 s
->timers_reg
[S3C_TIMERS_TCNTB4
] = s
->timer_reload_value
[num
];
98 s
->timer_last_ticked
[num
] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
99 timer_mod(s
->timer
[num
],
100 s
->timer_last_ticked
[num
] +
101 ((s
->timer_reload_value
[num
] * NANOSECONDS_PER_SECOND
) / s
->tclk1
));
105 s3c24xx_timer4_tick(void *opaque
)
107 struct s3c24xx_timers_state_s
*s
= (struct s3c24xx_timers_state_s
*)opaque
;
110 qemu_set_irq(s
->irqs
[4], 1);
112 /* if auto reload is set rescedule the next tick */
113 if (s
->timers_reg
[S3C_TIMERS_TCON
] & (1<<22)) {
114 s3c24xx_schedule_timer(s
, 4);
118 static void s3c24xx_timers_write_f(void *opaque
, hwaddr addr_
,
119 uint64_t value
, unsigned size
)
121 struct s3c24xx_timers_state_s
*s
= opaque
;
122 int addr
= (addr_
>> 2) & 0x1f;
124 s
->timers_reg
[addr
] = value
;
126 if (addr
== S3C_TIMERS_TCON
) {
127 if (value
& (1 << 21)) {
128 /* Timer4 manual update is set, copy in the reload value */
129 s
->timer_reload_value
[4] = s
->timers_reg
[S3C_TIMERS_TCNTB4
];
131 /* Timer4 manual update is not set */
132 if (value
& (1 << 20)) {
133 /* The timer is supposed to be running so start it */
134 s3c24xx_schedule_timer(s
, 4);
141 s3c24xx_timers_read_f(void *opaque
, hwaddr addr_
, unsigned size
)
143 struct s3c24xx_timers_state_s
*s
= opaque
;
144 int addr
= (addr_
>> 2) & 0x1f;
146 if (addr
== S3C_TIMERS_TCNTO4
) {
147 return s
->timer_reload_value
[4] -
148 (((qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) - s
->timer_last_ticked
[4]) * s
->tclk1
) / NANOSECONDS_PER_SECOND
);
150 return s
->timers_reg
[addr
];
153 static const MemoryRegionOps s3c24xx_timers_ops
= {
154 .read
= s3c24xx_timers_read_f
,
155 .write
= s3c24xx_timers_write_f
,
156 .endianness
= DEVICE_NATIVE_ENDIAN
,
158 .min_access_size
= 1,
163 static void s3c24xx_timers_save(QEMUFile
*f
, void *opaque
)
165 struct s3c24xx_timers_state_s
*s
= (struct s3c24xx_timers_state_s
*)opaque
;
168 for (i
= 0; i
< 17; i
++)
169 qemu_put_be32s(f
, &s
->timers_reg
[i
]);
172 static int s3c24xx_timers_load(QEMUFile
*f
, void *opaque
, int version_id
)
174 struct s3c24xx_timers_state_s
*s
= (struct s3c24xx_timers_state_s
*)opaque
;
177 for (i
= 0; i
< 17; i
++)
178 qemu_get_be32s(f
, &s
->timers_reg
[i
]);
183 static SaveVMHandlers savevm_s3c24xx_timers
= {
184 .save_state
= s3c24xx_timers_save
,
185 .load_state
= s3c24xx_timers_load
188 /* S3c24xx timer initialisation */
189 struct s3c24xx_timers_state_s
*
190 s3c24xx_timers_init(S3CState
*soc
, hwaddr base_addr
, uint32_t tclk0
, uint32_t tclk1
)
192 MemoryRegion
*system_memory
= get_system_memory();
193 struct s3c24xx_timers_state_s
*s
;
196 s
= g_malloc0(sizeof(struct s3c24xx_timers_state_s
));
198 memory_region_init_io(&s
->mmio
, OBJECT(s
),
199 &s3c24xx_timers_ops
, s
, "s3c24xx-timers", 17 * 4);
200 memory_region_add_subregion(system_memory
, base_addr
, &s
->mmio
);
202 register_savevm_live(NULL
, "s3c24xx_timers", 0, 0,
203 &savevm_s3c24xx_timers
, s
);
208 /* set up per timer values */
209 for (i
= 0; i
< 5; i
++) {
210 s
->irqs
[i
] = s3c24xx_get_irq(soc
->irq
, 10 + i
);
211 s
->timer_reload_value
[i
] = 0;
212 s
->timer_last_ticked
[i
] = 0;
215 s
->timer
[4] = timer_new_ns(QEMU_CLOCK_VIRTUAL
, s3c24xx_timer4_tick
, s
);