]> Git Repo - qemu.git/blob - hw/pl031.c
Merge remote-tracking branch 'stefanha/trivial-patches' into staging
[qemu.git] / hw / pl031.c
1 /*
2  * ARM AMBA PrimeCell PL031 RTC
3  *
4  * Copyright (c) 2007 CodeSourcery
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11
12 #include "sysbus.h"
13 #include "qemu-timer.h"
14
15 //#define DEBUG_PL031
16
17 #ifdef DEBUG_PL031
18 #define DPRINTF(fmt, ...) \
19 do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
20 #else
21 #define DPRINTF(fmt, ...) do {} while(0)
22 #endif
23
24 #define RTC_DR      0x00    /* Data read register */
25 #define RTC_MR      0x04    /* Match register */
26 #define RTC_LR      0x08    /* Data load register */
27 #define RTC_CR      0x0c    /* Control register */
28 #define RTC_IMSC    0x10    /* Interrupt mask and set register */
29 #define RTC_RIS     0x14    /* Raw interrupt status register */
30 #define RTC_MIS     0x18    /* Masked interrupt status register */
31 #define RTC_ICR     0x1c    /* Interrupt clear register */
32
33 typedef struct {
34     SysBusDevice busdev;
35     MemoryRegion iomem;
36     QEMUTimer *timer;
37     qemu_irq irq;
38
39     uint32_t tick_offset;
40
41     uint32_t mr;
42     uint32_t lr;
43     uint32_t cr;
44     uint32_t im;
45     uint32_t is;
46 } pl031_state;
47
48 static const VMStateDescription vmstate_pl031 = {
49     .name = "pl031",
50     .version_id = 1,
51     .minimum_version_id = 1,
52     .fields = (VMStateField[]) {
53         VMSTATE_UINT32(tick_offset, pl031_state),
54         VMSTATE_UINT32(mr, pl031_state),
55         VMSTATE_UINT32(lr, pl031_state),
56         VMSTATE_UINT32(cr, pl031_state),
57         VMSTATE_UINT32(im, pl031_state),
58         VMSTATE_UINT32(is, pl031_state),
59         VMSTATE_END_OF_LIST()
60     }
61 };
62
63 static const unsigned char pl031_id[] = {
64     0x31, 0x10, 0x14, 0x00,         /* Device ID        */
65     0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
66 };
67
68 static void pl031_update(pl031_state *s)
69 {
70     qemu_set_irq(s->irq, s->is & s->im);
71 }
72
73 static void pl031_interrupt(void * opaque)
74 {
75     pl031_state *s = (pl031_state *)opaque;
76
77     s->im = 1;
78     DPRINTF("Alarm raised\n");
79     pl031_update(s);
80 }
81
82 static uint32_t pl031_get_count(pl031_state *s)
83 {
84     /* This assumes qemu_get_clock_ns returns the time since the machine was
85        created.  */
86     return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec();
87 }
88
89 static void pl031_set_alarm(pl031_state *s)
90 {
91     int64_t now;
92     uint32_t ticks;
93
94     now = qemu_get_clock_ns(vm_clock);
95     ticks = s->tick_offset + now / get_ticks_per_sec();
96
97     /* The timer wraps around.  This subtraction also wraps in the same way,
98        and gives correct results when alarm < now_ticks.  */
99     ticks = s->mr - ticks;
100     DPRINTF("Alarm set in %ud ticks\n", ticks);
101     if (ticks == 0) {
102         qemu_del_timer(s->timer);
103         pl031_interrupt(s);
104     } else {
105         qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
106     }
107 }
108
109 static uint64_t pl031_read(void *opaque, target_phys_addr_t offset,
110                            unsigned size)
111 {
112     pl031_state *s = (pl031_state *)opaque;
113
114     if (offset >= 0xfe0  &&  offset < 0x1000)
115         return pl031_id[(offset - 0xfe0) >> 2];
116
117     switch (offset) {
118     case RTC_DR:
119         return pl031_get_count(s);
120     case RTC_MR:
121         return s->mr;
122     case RTC_IMSC:
123         return s->im;
124     case RTC_RIS:
125         return s->is;
126     case RTC_LR:
127         return s->lr;
128     case RTC_CR:
129         /* RTC is permanently enabled.  */
130         return 1;
131     case RTC_MIS:
132         return s->is & s->im;
133     case RTC_ICR:
134         fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
135                 (int)offset);
136         break;
137     default:
138         hw_error("pl031_read: Bad offset 0x%x\n", (int)offset);
139         break;
140     }
141
142     return 0;
143 }
144
145 static void pl031_write(void * opaque, target_phys_addr_t offset,
146                         uint64_t value, unsigned size)
147 {
148     pl031_state *s = (pl031_state *)opaque;
149
150
151     switch (offset) {
152     case RTC_LR:
153         s->tick_offset += value - pl031_get_count(s);
154         pl031_set_alarm(s);
155         break;
156     case RTC_MR:
157         s->mr = value;
158         pl031_set_alarm(s);
159         break;
160     case RTC_IMSC:
161         s->im = value & 1;
162         DPRINTF("Interrupt mask %d\n", s->im);
163         pl031_update(s);
164         break;
165     case RTC_ICR:
166         /* The PL031 documentation (DDI0224B) states that the interrupt is
167            cleared when bit 0 of the written value is set.  However the
168            arm926e documentation (DDI0287B) states that the interrupt is
169            cleared when any value is written.  */
170         DPRINTF("Interrupt cleared");
171         s->is = 0;
172         pl031_update(s);
173         break;
174     case RTC_CR:
175         /* Written value is ignored.  */
176         break;
177
178     case RTC_DR:
179     case RTC_MIS:
180     case RTC_RIS:
181         fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
182                 (int)offset);
183         break;
184
185     default:
186         hw_error("pl031_write: Bad offset 0x%x\n", (int)offset);
187         break;
188     }
189 }
190
191 static const MemoryRegionOps pl031_ops = {
192     .read = pl031_read,
193     .write = pl031_write,
194     .endianness = DEVICE_NATIVE_ENDIAN,
195 };
196
197 static int pl031_init(SysBusDevice *dev)
198 {
199     pl031_state *s = FROM_SYSBUS(pl031_state, dev);
200     struct tm tm;
201
202     memory_region_init_io(&s->iomem, &pl031_ops, s, "pl031", 0x1000);
203     sysbus_init_mmio(dev, &s->iomem);
204
205     sysbus_init_irq(dev, &s->irq);
206     /* ??? We assume vm_clock is zero at this point.  */
207     qemu_get_timedate(&tm, 0);
208     s->tick_offset = mktimegm(&tm);
209
210     s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s);
211     return 0;
212 }
213
214 static SysBusDeviceInfo pl031_info = {
215     .init = pl031_init,
216     .qdev.name = "pl031",
217     .qdev.size = sizeof(pl031_state),
218     .qdev.vmsd = &vmstate_pl031,
219     .qdev.no_user = 1,
220 };
221
222 static void pl031_register_devices(void)
223 {
224     sysbus_register_withprop(&pl031_info);
225 }
226
227 device_init(pl031_register_devices)
This page took 0.036782 seconds and 4 git commands to generate.