Skip to content

Commit ca1c608

Browse files
committed
Starting to get timers in order
1 parent 698fd65 commit ca1c608

File tree

4 files changed

+342
-9
lines changed

4 files changed

+342
-9
lines changed

hw/arm/stm32.c

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,16 @@ const char *stm32_periph_name(stm32_periph_t periph)
110110
*/
111111

112112
static DeviceState *stm32_init_periph(DeviceState *dev, stm32_periph_t periph,
113-
hwaddr addr, qemu_irq irq)
113+
hwaddr addr, qemu_irq *irq, int num_irqs)
114114
{
115+
int i;
115116
qdev_init_nofail(dev);
116117
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
117-
if (irq) {
118-
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
119-
}
118+
for (i = 0; i < num_irqs; i++) {
119+
if (irq[i]) {
120+
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[i]);
121+
}
122+
}
120123
return dev;
121124
}
122125

@@ -138,7 +141,29 @@ static void stm32_create_uart_dev(
138141
qdev_prop_set_ptr(uart_dev, "stm32_afio", afio_dev);
139142
snprintf(child_name, sizeof(child_name), "uart[%i]", uart_num);
140143
object_property_add_child(stm32_container, child_name, OBJECT(uart_dev), NULL);
141-
stm32_init_periph(uart_dev, periph, addr, irq);
144+
stm32_init_periph(uart_dev, periph, addr, &irq, 1);
145+
}
146+
147+
static void stm32_create_timer_dev(
148+
Object *stm32_container,
149+
stm32_periph_t periph,
150+
int timer_num,
151+
DeviceState *rcc_dev,
152+
DeviceState **gpio_dev,
153+
DeviceState *afio_dev,
154+
hwaddr addr,
155+
qemu_irq *irq,
156+
int num_irqs)
157+
{
158+
char child_name[9];
159+
DeviceState *timer_dev = qdev_create(NULL, "stm32-timer");
160+
QDEV_PROP_SET_PERIPH_T(timer_dev, "periph", periph);
161+
qdev_prop_set_ptr(timer_dev, "stm32_rcc", rcc_dev);
162+
qdev_prop_set_ptr(timer_dev, "stm32_gpio", gpio_dev);
163+
qdev_prop_set_ptr(timer_dev, "stm32_afio", afio_dev);
164+
snprintf(child_name, sizeof(child_name), "timer[%i]", timer_num);
165+
object_property_add_child(stm32_container, child_name, OBJECT(timer_dev), NULL);
166+
stm32_init_periph(timer_dev, periph, addr, irq, num_irqs);
142167
}
143168

144169

@@ -186,7 +211,7 @@ void stm32_init(
186211
qdev_prop_set_uint32(rcc_dev, "osc_freq", osc_freq);
187212
qdev_prop_set_uint32(rcc_dev, "osc32_freq", osc32_freq);
188213
object_property_add_child(stm32_container, "rcc", OBJECT(rcc_dev), NULL);
189-
stm32_init_periph(rcc_dev, STM32_RCC, 0x40021000, pic[STM32_RCC_IRQ]);
214+
stm32_init_periph(rcc_dev, STM32_RCC, 0x40021000, pic[STM32_RCC_IRQ], 1);
190215

191216
DeviceState **gpio_dev = (DeviceState **)g_malloc0(sizeof(DeviceState *) * STM32_GPIO_COUNT);
192217
for(i = 0; i < STM32_GPIO_COUNT; i++) {
@@ -197,12 +222,12 @@ void stm32_init(
197222
qdev_prop_set_ptr(gpio_dev[i], "stm32_rcc", rcc_dev);
198223
snprintf(child_name, sizeof(child_name), "gpio[%c]", 'a' + i);
199224
object_property_add_child(stm32_container, child_name, OBJECT(gpio_dev[i]), NULL);
200-
stm32_init_periph(gpio_dev[i], periph, 0x40010800 + (i * 0x400), NULL);
225+
stm32_init_periph(gpio_dev[i], periph, 0x40010800 + (i * 0x400), NULL, 0);
201226
}
202227

203228
DeviceState *exti_dev = qdev_create(NULL, TYPE_STM32_EXTI);
204229
object_property_add_child(stm32_container, "exti", OBJECT(exti_dev), NULL);
205-
stm32_init_periph(exti_dev, STM32_EXTI, 0x40010400, NULL);
230+
stm32_init_periph(exti_dev, STM32_EXTI, 0x40010400, NULL, 1);
206231
SysBusDevice *exti_busdev = SYS_BUS_DEVICE(exti_dev);
207232
sysbus_connect_irq(exti_busdev, 0, pic[STM32_EXTI0_IRQ]);
208233
sysbus_connect_irq(exti_busdev, 1, pic[STM32_EXTI1_IRQ]);
@@ -226,11 +251,15 @@ void stm32_init(
226251
object_property_set_link(OBJECT(afio_dev), OBJECT(gpio_dev[6]), "gpio[g]", NULL);
227252
object_property_set_link(OBJECT(afio_dev), OBJECT(exti_dev), "exti", NULL);
228253
object_property_add_child(stm32_container, "afio", OBJECT(afio_dev), NULL);
229-
stm32_init_periph(afio_dev, STM32_AFIO_PERIPH, 0x40010000, NULL);
254+
stm32_init_periph(afio_dev, STM32_AFIO_PERIPH, 0x40010000, NULL, 0);
230255

231256
stm32_create_uart_dev(stm32_container, STM32_UART1, 1, rcc_dev, gpio_dev, afio_dev, 0x40013800, pic[STM32_UART1_IRQ]);
232257
stm32_create_uart_dev(stm32_container, STM32_UART2, 2, rcc_dev, gpio_dev, afio_dev, 0x40004400, pic[STM32_UART2_IRQ]);
233258
stm32_create_uart_dev(stm32_container, STM32_UART3, 3, rcc_dev, gpio_dev, afio_dev, 0x40004800, pic[STM32_UART3_IRQ]);
234259
stm32_create_uart_dev(stm32_container, STM32_UART4, 4, rcc_dev, gpio_dev, afio_dev, 0x40004c00, pic[STM32_UART4_IRQ]);
235260
stm32_create_uart_dev(stm32_container, STM32_UART5, 5, rcc_dev, gpio_dev, afio_dev, 0x40005000, pic[STM32_UART5_IRQ]);
261+
262+
qemu_irq tim_irqs[] = { pic[TIM1_BRK_IRQn], pic[TIM1_UP_IRQn], pic[TIM1_TRG_COM_IRQn],
263+
pic[TIM1_CC_IRQn]};
264+
stm32_create_timer_dev(stm32_container, STM32_TIM1, 1, rcc_dev, gpio_dev, afio_dev, 0x40012C00, tim_irqs, 5);
236265
}

hw/arm/stm32_rcc.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,15 @@ static void stm32_rcc_init_clk(Stm32Rcc *s)
737737
s->PERIPHCLK[STM32_UART3] = clktree_create_clk("UART3", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
738738
s->PERIPHCLK[STM32_UART4] = clktree_create_clk("UART4", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
739739
s->PERIPHCLK[STM32_UART5] = clktree_create_clk("UART5", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
740+
741+
s->PERIPHCLK[STM32_TIM1] = clktree_create_clk("TIM1", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK2, NULL);
742+
s->PERIPHCLK[STM32_TIM2] = clktree_create_clk("TIM2", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
743+
s->PERIPHCLK[STM32_TIM3] = clktree_create_clk("TIM3", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
744+
s->PERIPHCLK[STM32_TIM4] = clktree_create_clk("TIM4", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
745+
s->PERIPHCLK[STM32_TIM5] = clktree_create_clk("TIM5", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
746+
s->PERIPHCLK[STM32_TIM6] = clktree_create_clk("TIM6", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
747+
s->PERIPHCLK[STM32_TIM7] = clktree_create_clk("TIM7", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK1, NULL);
748+
s->PERIPHCLK[STM32_TIM8] = clktree_create_clk("TIM8", 1, 1, false, CLKTREE_NO_MAX_FREQ, 0, s->PCLK2, NULL);
740749
}
741750

742751

hw/timer/stm32.c

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* STM32 Microcontroller Timer module
3+
*
4+
* Copyright (C) 2010 Andrew Hankins
5+
*
6+
* Source code based on pl011.c
7+
*
8+
* This program is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU General Public License as
10+
* published by the Free Software Foundation; either version 2 of
11+
* the License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License along
19+
* with this program; if not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
#include "hw/sysbus.h"
23+
#include "qemu/timer.h"
24+
#include "sysemu/sysemu.h"
25+
26+
27+
/* DEFINITIONS*/
28+
29+
/* See the README file for details on these settings. */
30+
#define DEBUG_STM32_TIMER
31+
//#define STM32_TIMER_NO_BAUD_DELAY
32+
//#define STM32_TIMER_ENABLE_OVERRUN
33+
34+
#ifdef DEBUG_STM32_TIMER
35+
#define DPRINTF(fmt, ...) \
36+
do { printf("STM32_TIMER: " fmt , ## __VA_ARGS__); } while (0)
37+
#else
38+
#define DPRINTF(fmt, ...)
39+
#endif
40+
41+
#define RTC_DR 0x00 /* Data read register */
42+
#define RTC_MR 0x04 /* Match register */
43+
#define RTC_LR 0x08 /* Data load register */
44+
#define RTC_CR 0x0c /* Control register */
45+
#define RTC_IMSC 0x10 /* Interrupt mask and set register */
46+
#define RTC_RIS 0x14 /* Raw interrupt status register */
47+
#define RTC_MIS 0x18 /* Masked interrupt status register */
48+
#define RTC_ICR 0x1c /* Interrupt clear register */
49+
50+
typedef struct {
51+
SysBusDevice busdev;
52+
MemoryRegion iomem;
53+
QEMUTimer *timer;
54+
qemu_irq irq;
55+
56+
/* Needed to preserve the tick_count across migration, even if the
57+
* absolute value of the rtc_clock is different on the source and
58+
* destination.
59+
*/
60+
uint32_t tick_offset_vmstate;
61+
uint32_t tick_offset;
62+
63+
uint32_t mr;
64+
uint32_t lr;
65+
uint32_t cr;
66+
uint32_t im;
67+
uint32_t is;
68+
} pl031_state;
69+
70+
static const unsigned char pl031_id[] = {
71+
0x31, 0x10, 0x14, 0x00, /* Device ID */
72+
0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
73+
};
74+
75+
static void pl031_update(pl031_state *s)
76+
{
77+
qemu_set_irq(s->irq, s->is & s->im);
78+
}
79+
80+
static void pl031_interrupt(void * opaque)
81+
{
82+
pl031_state *s = (pl031_state *)opaque;
83+
84+
s->is = 1;
85+
DPRINTF("Alarm raised\n");
86+
pl031_update(s);
87+
}
88+
89+
static uint32_t pl031_get_count(pl031_state *s)
90+
{
91+
int64_t now = qemu_get_clock_ns(rtc_clock);
92+
return s->tick_offset + now / get_ticks_per_sec();
93+
}
94+
95+
static void pl031_set_alarm(pl031_state *s)
96+
{
97+
uint32_t ticks;
98+
99+
/* The timer wraps around. This subtraction also wraps in the same way,
100+
and gives correct results when alarm < now_ticks. */
101+
ticks = s->mr - pl031_get_count(s);
102+
DPRINTF("Alarm set in %ud ticks\n", ticks);
103+
if (ticks == 0) {
104+
qemu_del_timer(s->timer);
105+
pl031_interrupt(s);
106+
} else {
107+
int64_t now = qemu_get_clock_ns(rtc_clock);
108+
qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
109+
}
110+
}
111+
112+
static uint64_t pl031_read(void *opaque, hwaddr offset,
113+
unsigned size)
114+
{
115+
pl031_state *s = (pl031_state *)opaque;
116+
117+
if (offset >= 0xfe0 && offset < 0x1000)
118+
return pl031_id[(offset - 0xfe0) >> 2];
119+
120+
switch (offset) {
121+
case RTC_DR:
122+
return pl031_get_count(s);
123+
case RTC_MR:
124+
return s->mr;
125+
case RTC_IMSC:
126+
return s->im;
127+
case RTC_RIS:
128+
return s->is;
129+
case RTC_LR:
130+
return s->lr;
131+
case RTC_CR:
132+
/* RTC is permanently enabled. */
133+
return 1;
134+
case RTC_MIS:
135+
return s->is & s->im;
136+
case RTC_ICR:
137+
qemu_log_mask(LOG_GUEST_ERROR,
138+
"pl031: read of write-only register at offset 0x%x\n",
139+
(int)offset);
140+
break;
141+
default:
142+
qemu_log_mask(LOG_GUEST_ERROR,
143+
"pl031_read: Bad offset 0x%x\n", (int)offset);
144+
break;
145+
}
146+
147+
return 0;
148+
}
149+
150+
static void pl031_write(void * opaque, hwaddr offset,
151+
uint64_t value, unsigned size)
152+
{
153+
pl031_state *s = (pl031_state *)opaque;
154+
155+
156+
switch (offset) {
157+
case RTC_LR:
158+
s->tick_offset += value - pl031_get_count(s);
159+
pl031_set_alarm(s);
160+
break;
161+
case RTC_MR:
162+
s->mr = value;
163+
pl031_set_alarm(s);
164+
break;
165+
case RTC_IMSC:
166+
s->im = value & 1;
167+
DPRINTF("Interrupt mask %d\n", s->im);
168+
pl031_update(s);
169+
break;
170+
case RTC_ICR:
171+
/* The PL031 documentation (DDI0224B) states that the interrupt is
172+
cleared when bit 0 of the written value is set. However the
173+
arm926e documentation (DDI0287B) states that the interrupt is
174+
cleared when any value is written. */
175+
DPRINTF("Interrupt cleared");
176+
s->is = 0;
177+
pl031_update(s);
178+
break;
179+
case RTC_CR:
180+
/* Written value is ignored. */
181+
break;
182+
183+
case RTC_DR:
184+
case RTC_MIS:
185+
case RTC_RIS:
186+
qemu_log_mask(LOG_GUEST_ERROR,
187+
"pl031: write to read-only register at offset 0x%x\n",
188+
(int)offset);
189+
break;
190+
191+
default:
192+
qemu_log_mask(LOG_GUEST_ERROR,
193+
"pl031_write: Bad offset 0x%x\n", (int)offset);
194+
break;
195+
}
196+
}
197+
198+
static const MemoryRegionOps pl031_ops = {
199+
.read = pl031_read,
200+
.write = pl031_write,
201+
.endianness = DEVICE_NATIVE_ENDIAN,
202+
};
203+
204+
static int pl031_init(SysBusDevice *dev)
205+
{
206+
pl031_state *s = FROM_SYSBUS(pl031_state, dev);
207+
struct tm tm;
208+
209+
memory_region_init_io(&s->iomem, &pl031_ops, s, "pl031", 0x1000);
210+
sysbus_init_mmio(dev, &s->iomem);
211+
212+
sysbus_init_irq(dev, &s->irq);
213+
qemu_get_timedate(&tm, 0);
214+
s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec();
215+
216+
s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s);
217+
return 0;
218+
}
219+
220+
static void pl031_pre_save(void *opaque)
221+
{
222+
pl031_state *s = opaque;
223+
224+
/* tick_offset is base_time - rtc_clock base time. Instead, we want to
225+
* store the base time relative to the vm_clock for backwards-compatibility. */
226+
int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
227+
s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
228+
}
229+
230+
static int pl031_post_load(void *opaque, int version_id)
231+
{
232+
pl031_state *s = opaque;
233+
234+
int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
235+
s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
236+
pl031_set_alarm(s);
237+
return 0;
238+
}
239+
240+
static const VMStateDescription vmstate_pl031 = {
241+
.name = "pl031",
242+
.version_id = 1,
243+
.minimum_version_id = 1,
244+
.pre_save = pl031_pre_save,
245+
.post_load = pl031_post_load,
246+
.fields = (VMStateField[]) {
247+
VMSTATE_UINT32(tick_offset_vmstate, pl031_state),
248+
VMSTATE_UINT32(mr, pl031_state),
249+
VMSTATE_UINT32(lr, pl031_state),
250+
VMSTATE_UINT32(cr, pl031_state),
251+
VMSTATE_UINT32(im, pl031_state),
252+
VMSTATE_UINT32(is, pl031_state),
253+
VMSTATE_END_OF_LIST()
254+
}
255+
};
256+
257+
static void pl031_class_init(ObjectClass *klass, void *data)
258+
{
259+
DeviceClass *dc = DEVICE_CLASS(klass);
260+
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
261+
262+
k->init = pl031_init;
263+
dc->no_user = 1;
264+
dc->vmsd = &vmstate_pl031;
265+
}
266+
267+
static const TypeInfo pl031_info = {
268+
.name = "pl031",
269+
.parent = TYPE_SYS_BUS_DEVICE,
270+
.instance_size = sizeof(pl031_state),
271+
.class_init = pl031_class_init,
272+
};
273+
274+
static void pl031_register_types(void)
275+
{
276+
type_register_static(&pl031_info);
277+
}
278+
279+
type_init(pl031_register_types)

0 commit comments

Comments
 (0)