xref: /qemu/hw/timer/digic-timer.c (revision 03dd024ff57733a55cd2e455f361d053c81b1b29)
1576e99cbSAntony Pavlov /*
2576e99cbSAntony Pavlov  * QEMU model of the Canon DIGIC timer block.
3576e99cbSAntony Pavlov  *
4576e99cbSAntony Pavlov  * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
5576e99cbSAntony Pavlov  *
6576e99cbSAntony Pavlov  * This model is based on reverse engineering efforts
7576e99cbSAntony Pavlov  * made by CHDK (http://chdk.wikia.com) and
8576e99cbSAntony Pavlov  * Magic Lantern (http://www.magiclantern.fm) projects
9576e99cbSAntony Pavlov  * contributors.
10576e99cbSAntony Pavlov  *
11576e99cbSAntony Pavlov  * See "Timer/Clock Module" docs here:
12576e99cbSAntony Pavlov  *   http://magiclantern.wikia.com/wiki/Register_Map
13576e99cbSAntony Pavlov  *
14576e99cbSAntony Pavlov  * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao
15576e99cbSAntony Pavlov  * is used as a template.
16576e99cbSAntony Pavlov  *
17576e99cbSAntony Pavlov  * This program is free software; you can redistribute it and/or modify
18576e99cbSAntony Pavlov  * it under the terms of the GNU General Public License as published by
19576e99cbSAntony Pavlov  * the Free Software Foundation; either version 2 of the License, or
20576e99cbSAntony Pavlov  * (at your option) any later version.
21576e99cbSAntony Pavlov  *
22576e99cbSAntony Pavlov  * This program is distributed in the hope that it will be useful,
23576e99cbSAntony Pavlov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24576e99cbSAntony Pavlov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25576e99cbSAntony Pavlov  * GNU General Public License for more details.
26576e99cbSAntony Pavlov  *
27576e99cbSAntony Pavlov  */
28576e99cbSAntony Pavlov 
298ef94f0bSPeter Maydell #include "qemu/osdep.h"
30576e99cbSAntony Pavlov #include "hw/sysbus.h"
31576e99cbSAntony Pavlov #include "hw/ptimer.h"
32576e99cbSAntony Pavlov #include "qemu/main-loop.h"
33*03dd024fSPaolo Bonzini #include "qemu/log.h"
34576e99cbSAntony Pavlov 
35576e99cbSAntony Pavlov #include "hw/timer/digic-timer.h"
36576e99cbSAntony Pavlov 
37576e99cbSAntony Pavlov static const VMStateDescription vmstate_digic_timer = {
38576e99cbSAntony Pavlov     .name = "digic.timer",
39576e99cbSAntony Pavlov     .version_id = 1,
40576e99cbSAntony Pavlov     .minimum_version_id = 1,
41576e99cbSAntony Pavlov     .fields = (VMStateField[]) {
42576e99cbSAntony Pavlov         VMSTATE_PTIMER(ptimer, DigicTimerState),
43576e99cbSAntony Pavlov         VMSTATE_UINT32(control, DigicTimerState),
44576e99cbSAntony Pavlov         VMSTATE_UINT32(relvalue, DigicTimerState),
45576e99cbSAntony Pavlov         VMSTATE_END_OF_LIST()
46576e99cbSAntony Pavlov     }
47576e99cbSAntony Pavlov };
48576e99cbSAntony Pavlov 
49576e99cbSAntony Pavlov static void digic_timer_reset(DeviceState *dev)
50576e99cbSAntony Pavlov {
51576e99cbSAntony Pavlov     DigicTimerState *s = DIGIC_TIMER(dev);
52576e99cbSAntony Pavlov 
53576e99cbSAntony Pavlov     ptimer_stop(s->ptimer);
54576e99cbSAntony Pavlov     s->control = 0;
55576e99cbSAntony Pavlov     s->relvalue = 0;
56576e99cbSAntony Pavlov }
57576e99cbSAntony Pavlov 
58576e99cbSAntony Pavlov static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size)
59576e99cbSAntony Pavlov {
60576e99cbSAntony Pavlov     DigicTimerState *s = opaque;
61576e99cbSAntony Pavlov     uint64_t ret = 0;
62576e99cbSAntony Pavlov 
63576e99cbSAntony Pavlov     switch (offset) {
64576e99cbSAntony Pavlov     case DIGIC_TIMER_CONTROL:
65576e99cbSAntony Pavlov         ret = s->control;
66576e99cbSAntony Pavlov         break;
67576e99cbSAntony Pavlov     case DIGIC_TIMER_RELVALUE:
68576e99cbSAntony Pavlov         ret = s->relvalue;
69576e99cbSAntony Pavlov         break;
70576e99cbSAntony Pavlov     case DIGIC_TIMER_VALUE:
71576e99cbSAntony Pavlov         ret = ptimer_get_count(s->ptimer) & 0xffff;
72576e99cbSAntony Pavlov         break;
73576e99cbSAntony Pavlov     default:
74576e99cbSAntony Pavlov         qemu_log_mask(LOG_UNIMP,
75576e99cbSAntony Pavlov                       "digic-timer: read access to unknown register 0x"
76576e99cbSAntony Pavlov                       TARGET_FMT_plx, offset);
77576e99cbSAntony Pavlov     }
78576e99cbSAntony Pavlov 
79576e99cbSAntony Pavlov     return ret;
80576e99cbSAntony Pavlov }
81576e99cbSAntony Pavlov 
82576e99cbSAntony Pavlov static void digic_timer_write(void *opaque, hwaddr offset,
83576e99cbSAntony Pavlov                               uint64_t value, unsigned size)
84576e99cbSAntony Pavlov {
85576e99cbSAntony Pavlov     DigicTimerState *s = opaque;
86576e99cbSAntony Pavlov 
87576e99cbSAntony Pavlov     switch (offset) {
88576e99cbSAntony Pavlov     case DIGIC_TIMER_CONTROL:
89576e99cbSAntony Pavlov         if (value & DIGIC_TIMER_CONTROL_RST) {
90576e99cbSAntony Pavlov             digic_timer_reset((DeviceState *)s);
91576e99cbSAntony Pavlov             break;
92576e99cbSAntony Pavlov         }
93576e99cbSAntony Pavlov 
94576e99cbSAntony Pavlov         if (value & DIGIC_TIMER_CONTROL_EN) {
95576e99cbSAntony Pavlov             ptimer_run(s->ptimer, 0);
96576e99cbSAntony Pavlov         }
97576e99cbSAntony Pavlov 
98576e99cbSAntony Pavlov         s->control = (uint32_t)value;
99576e99cbSAntony Pavlov         break;
100576e99cbSAntony Pavlov 
101576e99cbSAntony Pavlov     case DIGIC_TIMER_RELVALUE:
102576e99cbSAntony Pavlov         s->relvalue = extract32(value, 0, 16);
103576e99cbSAntony Pavlov         ptimer_set_limit(s->ptimer, s->relvalue, 1);
104576e99cbSAntony Pavlov         break;
105576e99cbSAntony Pavlov 
106576e99cbSAntony Pavlov     case DIGIC_TIMER_VALUE:
107576e99cbSAntony Pavlov         break;
108576e99cbSAntony Pavlov 
109576e99cbSAntony Pavlov     default:
110576e99cbSAntony Pavlov         qemu_log_mask(LOG_UNIMP,
111576e99cbSAntony Pavlov                       "digic-timer: read access to unknown register 0x"
112576e99cbSAntony Pavlov                       TARGET_FMT_plx, offset);
113576e99cbSAntony Pavlov     }
114576e99cbSAntony Pavlov }
115576e99cbSAntony Pavlov 
116576e99cbSAntony Pavlov static const MemoryRegionOps digic_timer_ops = {
117576e99cbSAntony Pavlov     .read = digic_timer_read,
118576e99cbSAntony Pavlov     .write = digic_timer_write,
119576e99cbSAntony Pavlov     .impl = {
120576e99cbSAntony Pavlov         .min_access_size = 4,
121576e99cbSAntony Pavlov         .max_access_size = 4,
122576e99cbSAntony Pavlov     },
123576e99cbSAntony Pavlov     .endianness = DEVICE_NATIVE_ENDIAN,
124576e99cbSAntony Pavlov };
125576e99cbSAntony Pavlov 
126576e99cbSAntony Pavlov static void digic_timer_init(Object *obj)
127576e99cbSAntony Pavlov {
128576e99cbSAntony Pavlov     DigicTimerState *s = DIGIC_TIMER(obj);
129576e99cbSAntony Pavlov 
130576e99cbSAntony Pavlov     s->ptimer = ptimer_init(NULL);
131576e99cbSAntony Pavlov 
132576e99cbSAntony Pavlov     /*
133576e99cbSAntony Pavlov      * FIXME: there is no documentation on Digic timer
134576e99cbSAntony Pavlov      * frequency setup so let it always run at 1 MHz
135576e99cbSAntony Pavlov      */
136576e99cbSAntony Pavlov     ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
137576e99cbSAntony Pavlov 
138576e99cbSAntony Pavlov     memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
139576e99cbSAntony Pavlov                           TYPE_DIGIC_TIMER, 0x100);
140576e99cbSAntony Pavlov     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
141576e99cbSAntony Pavlov }
142576e99cbSAntony Pavlov 
143576e99cbSAntony Pavlov static void digic_timer_class_init(ObjectClass *klass, void *class_data)
144576e99cbSAntony Pavlov {
145576e99cbSAntony Pavlov     DeviceClass *dc = DEVICE_CLASS(klass);
146576e99cbSAntony Pavlov 
147576e99cbSAntony Pavlov     dc->reset = digic_timer_reset;
148576e99cbSAntony Pavlov     dc->vmsd = &vmstate_digic_timer;
149576e99cbSAntony Pavlov }
150576e99cbSAntony Pavlov 
151576e99cbSAntony Pavlov static const TypeInfo digic_timer_info = {
152576e99cbSAntony Pavlov     .name = TYPE_DIGIC_TIMER,
153576e99cbSAntony Pavlov     .parent = TYPE_SYS_BUS_DEVICE,
154576e99cbSAntony Pavlov     .instance_size = sizeof(DigicTimerState),
155576e99cbSAntony Pavlov     .instance_init = digic_timer_init,
156576e99cbSAntony Pavlov     .class_init = digic_timer_class_init,
157576e99cbSAntony Pavlov };
158576e99cbSAntony Pavlov 
159576e99cbSAntony Pavlov static void digic_timer_register_type(void)
160576e99cbSAntony Pavlov {
161576e99cbSAntony Pavlov     type_register_static(&digic_timer_info);
162576e99cbSAntony Pavlov }
163576e99cbSAntony Pavlov 
164576e99cbSAntony Pavlov type_init(digic_timer_register_type)
165