xref: /openbmc/qemu/hw/timer/digic-timer.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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"
320b8fa32fSMarkus Armbruster #include "qemu/module.h"
3303dd024fSPaolo Bonzini #include "qemu/log.h"
34576e99cbSAntony Pavlov 
35576e99cbSAntony Pavlov #include "hw/timer/digic-timer.h"
36d6454270SMarkus Armbruster #include "migration/vmstate.h"
37576e99cbSAntony Pavlov 
38576e99cbSAntony Pavlov static const VMStateDescription vmstate_digic_timer = {
39576e99cbSAntony Pavlov     .name = "digic.timer",
40576e99cbSAntony Pavlov     .version_id = 1,
41576e99cbSAntony Pavlov     .minimum_version_id = 1,
42ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
43576e99cbSAntony Pavlov         VMSTATE_PTIMER(ptimer, DigicTimerState),
44576e99cbSAntony Pavlov         VMSTATE_UINT32(control, DigicTimerState),
45576e99cbSAntony Pavlov         VMSTATE_UINT32(relvalue, DigicTimerState),
46576e99cbSAntony Pavlov         VMSTATE_END_OF_LIST()
47576e99cbSAntony Pavlov     }
48576e99cbSAntony Pavlov };
49576e99cbSAntony Pavlov 
digic_timer_reset(DeviceState * dev)50576e99cbSAntony Pavlov static void digic_timer_reset(DeviceState *dev)
51576e99cbSAntony Pavlov {
52576e99cbSAntony Pavlov     DigicTimerState *s = DIGIC_TIMER(dev);
53576e99cbSAntony Pavlov 
5430e22c87SPeter Maydell     ptimer_transaction_begin(s->ptimer);
55576e99cbSAntony Pavlov     ptimer_stop(s->ptimer);
5630e22c87SPeter Maydell     ptimer_transaction_commit(s->ptimer);
57576e99cbSAntony Pavlov     s->control = 0;
58576e99cbSAntony Pavlov     s->relvalue = 0;
59576e99cbSAntony Pavlov }
60576e99cbSAntony Pavlov 
digic_timer_read(void * opaque,hwaddr offset,unsigned size)61576e99cbSAntony Pavlov static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size)
62576e99cbSAntony Pavlov {
63576e99cbSAntony Pavlov     DigicTimerState *s = opaque;
64576e99cbSAntony Pavlov     uint64_t ret = 0;
65576e99cbSAntony Pavlov 
66576e99cbSAntony Pavlov     switch (offset) {
67576e99cbSAntony Pavlov     case DIGIC_TIMER_CONTROL:
68576e99cbSAntony Pavlov         ret = s->control;
69576e99cbSAntony Pavlov         break;
70576e99cbSAntony Pavlov     case DIGIC_TIMER_RELVALUE:
71576e99cbSAntony Pavlov         ret = s->relvalue;
72576e99cbSAntony Pavlov         break;
73576e99cbSAntony Pavlov     case DIGIC_TIMER_VALUE:
74576e99cbSAntony Pavlov         ret = ptimer_get_count(s->ptimer) & 0xffff;
75576e99cbSAntony Pavlov         break;
76576e99cbSAntony Pavlov     default:
77576e99cbSAntony Pavlov         qemu_log_mask(LOG_UNIMP,
78576e99cbSAntony Pavlov                       "digic-timer: read access to unknown register 0x"
79883f2c59SPhilippe Mathieu-Daudé                       HWADDR_FMT_plx "\n", offset);
80576e99cbSAntony Pavlov     }
81576e99cbSAntony Pavlov 
82576e99cbSAntony Pavlov     return ret;
83576e99cbSAntony Pavlov }
84576e99cbSAntony Pavlov 
digic_timer_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)85576e99cbSAntony Pavlov static void digic_timer_write(void *opaque, hwaddr offset,
86576e99cbSAntony Pavlov                               uint64_t value, unsigned size)
87576e99cbSAntony Pavlov {
88576e99cbSAntony Pavlov     DigicTimerState *s = opaque;
89576e99cbSAntony Pavlov 
90576e99cbSAntony Pavlov     switch (offset) {
91576e99cbSAntony Pavlov     case DIGIC_TIMER_CONTROL:
92576e99cbSAntony Pavlov         if (value & DIGIC_TIMER_CONTROL_RST) {
93576e99cbSAntony Pavlov             digic_timer_reset((DeviceState *)s);
94576e99cbSAntony Pavlov             break;
95576e99cbSAntony Pavlov         }
96576e99cbSAntony Pavlov 
9730e22c87SPeter Maydell         ptimer_transaction_begin(s->ptimer);
98576e99cbSAntony Pavlov         if (value & DIGIC_TIMER_CONTROL_EN) {
99576e99cbSAntony Pavlov             ptimer_run(s->ptimer, 0);
100576e99cbSAntony Pavlov         }
101576e99cbSAntony Pavlov 
102576e99cbSAntony Pavlov         s->control = (uint32_t)value;
10330e22c87SPeter Maydell         ptimer_transaction_commit(s->ptimer);
104576e99cbSAntony Pavlov         break;
105576e99cbSAntony Pavlov 
106576e99cbSAntony Pavlov     case DIGIC_TIMER_RELVALUE:
107576e99cbSAntony Pavlov         s->relvalue = extract32(value, 0, 16);
10830e22c87SPeter Maydell         ptimer_transaction_begin(s->ptimer);
109576e99cbSAntony Pavlov         ptimer_set_limit(s->ptimer, s->relvalue, 1);
11030e22c87SPeter Maydell         ptimer_transaction_commit(s->ptimer);
111576e99cbSAntony Pavlov         break;
112576e99cbSAntony Pavlov 
113576e99cbSAntony Pavlov     case DIGIC_TIMER_VALUE:
114576e99cbSAntony Pavlov         break;
115576e99cbSAntony Pavlov 
116576e99cbSAntony Pavlov     default:
117576e99cbSAntony Pavlov         qemu_log_mask(LOG_UNIMP,
118576e99cbSAntony Pavlov                       "digic-timer: read access to unknown register 0x"
119883f2c59SPhilippe Mathieu-Daudé                       HWADDR_FMT_plx "\n", offset);
120576e99cbSAntony Pavlov     }
121576e99cbSAntony Pavlov }
122576e99cbSAntony Pavlov 
123576e99cbSAntony Pavlov static const MemoryRegionOps digic_timer_ops = {
124576e99cbSAntony Pavlov     .read = digic_timer_read,
125576e99cbSAntony Pavlov     .write = digic_timer_write,
126576e99cbSAntony Pavlov     .impl = {
127576e99cbSAntony Pavlov         .min_access_size = 4,
128576e99cbSAntony Pavlov         .max_access_size = 4,
129576e99cbSAntony Pavlov     },
130576e99cbSAntony Pavlov     .endianness = DEVICE_NATIVE_ENDIAN,
131576e99cbSAntony Pavlov };
132576e99cbSAntony Pavlov 
digic_timer_tick(void * opaque)13330e22c87SPeter Maydell static void digic_timer_tick(void *opaque)
13430e22c87SPeter Maydell {
13530e22c87SPeter Maydell     /* Nothing to do on timer rollover */
13630e22c87SPeter Maydell }
13730e22c87SPeter Maydell 
digic_timer_init(Object * obj)138576e99cbSAntony Pavlov static void digic_timer_init(Object *obj)
139576e99cbSAntony Pavlov {
140576e99cbSAntony Pavlov     DigicTimerState *s = DIGIC_TIMER(obj);
141576e99cbSAntony Pavlov 
1429598c1bbSPeter Maydell     s->ptimer = ptimer_init(digic_timer_tick, NULL, PTIMER_POLICY_LEGACY);
143576e99cbSAntony Pavlov 
144576e99cbSAntony Pavlov     /*
145576e99cbSAntony Pavlov      * FIXME: there is no documentation on Digic timer
146576e99cbSAntony Pavlov      * frequency setup so let it always run at 1 MHz
147576e99cbSAntony Pavlov      */
14830e22c87SPeter Maydell     ptimer_transaction_begin(s->ptimer);
149576e99cbSAntony Pavlov     ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
15030e22c87SPeter Maydell     ptimer_transaction_commit(s->ptimer);
151576e99cbSAntony Pavlov 
152576e99cbSAntony Pavlov     memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
153576e99cbSAntony Pavlov                           TYPE_DIGIC_TIMER, 0x100);
154576e99cbSAntony Pavlov     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
155576e99cbSAntony Pavlov }
156576e99cbSAntony Pavlov 
digic_timer_finalize(Object * obj)157ea492b12SGan Qixin static void digic_timer_finalize(Object *obj)
158ea492b12SGan Qixin {
159ea492b12SGan Qixin     DigicTimerState *s = DIGIC_TIMER(obj);
160ea492b12SGan Qixin 
161ea492b12SGan Qixin     ptimer_free(s->ptimer);
162ea492b12SGan Qixin }
163ea492b12SGan Qixin 
digic_timer_class_init(ObjectClass * klass,void * class_data)164576e99cbSAntony Pavlov static void digic_timer_class_init(ObjectClass *klass, void *class_data)
165576e99cbSAntony Pavlov {
166576e99cbSAntony Pavlov     DeviceClass *dc = DEVICE_CLASS(klass);
167576e99cbSAntony Pavlov 
168*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, digic_timer_reset);
169576e99cbSAntony Pavlov     dc->vmsd = &vmstate_digic_timer;
170576e99cbSAntony Pavlov }
171576e99cbSAntony Pavlov 
172576e99cbSAntony Pavlov static const TypeInfo digic_timer_info = {
173576e99cbSAntony Pavlov     .name = TYPE_DIGIC_TIMER,
174576e99cbSAntony Pavlov     .parent = TYPE_SYS_BUS_DEVICE,
175576e99cbSAntony Pavlov     .instance_size = sizeof(DigicTimerState),
176576e99cbSAntony Pavlov     .instance_init = digic_timer_init,
177ea492b12SGan Qixin     .instance_finalize = digic_timer_finalize,
178576e99cbSAntony Pavlov     .class_init = digic_timer_class_init,
179576e99cbSAntony Pavlov };
180576e99cbSAntony Pavlov 
digic_timer_register_type(void)181576e99cbSAntony Pavlov static void digic_timer_register_type(void)
182576e99cbSAntony Pavlov {
183576e99cbSAntony Pavlov     type_register_static(&digic_timer_info);
184576e99cbSAntony Pavlov }
185576e99cbSAntony Pavlov 
186576e99cbSAntony Pavlov type_init(digic_timer_register_type)
187