Statistics
| Branch: | Revision:

root / hw / pl031.c @ 5ea3c2b4

History | View | Annotate | Download (5.9 kB)

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
    QEMUTimer *timer;
36
    qemu_irq irq;
37

    
38
    uint32_t tick_offset;
39

    
40
    uint32_t mr;
41
    uint32_t lr;
42
    uint32_t cr;
43
    uint32_t im;
44
    uint32_t is;
45
} pl031_state;
46

    
47
static const VMStateDescription vmstate_pl031 = {
48
    .name = "pl031",
49
    .version_id = 1,
50
    .minimum_version_id = 1,
51
    .fields = (VMStateField[]) {
52
        VMSTATE_UINT32(tick_offset, pl031_state),
53
        VMSTATE_UINT32(mr, pl031_state),
54
        VMSTATE_UINT32(lr, pl031_state),
55
        VMSTATE_UINT32(cr, pl031_state),
56
        VMSTATE_UINT32(im, pl031_state),
57
        VMSTATE_UINT32(is, pl031_state),
58
        VMSTATE_END_OF_LIST()
59
    }
60
};
61

    
62
static const unsigned char pl031_id[] = {
63
    0x31, 0x10, 0x14, 0x00,         /* Device ID        */
64
    0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
65
};
66

    
67
static void pl031_update(pl031_state *s)
68
{
69
    qemu_set_irq(s->irq, s->is & s->im);
70
}
71

    
72
static void pl031_interrupt(void * opaque)
73
{
74
    pl031_state *s = (pl031_state *)opaque;
75

    
76
    s->im = 1;
77
    DPRINTF("Alarm raised\n");
78
    pl031_update(s);
79
}
80

    
81
static uint32_t pl031_get_count(pl031_state *s)
82
{
83
    /* This assumes qemu_get_clock_ns returns the time since the machine was
84
       created.  */
85
    return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec();
86
}
87

    
88
static void pl031_set_alarm(pl031_state *s)
89
{
90
    int64_t now;
91
    uint32_t ticks;
92

    
93
    now = qemu_get_clock_ns(vm_clock);
94
    ticks = s->tick_offset + now / get_ticks_per_sec();
95

    
96
    /* The timer wraps around.  This subtraction also wraps in the same way,
97
       and gives correct results when alarm < now_ticks.  */
98
    ticks = s->mr - ticks;
99
    DPRINTF("Alarm set in %ud ticks\n", ticks);
100
    if (ticks == 0) {
101
        qemu_del_timer(s->timer);
102
        pl031_interrupt(s);
103
    } else {
104
        qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
105
    }
106
}
107

    
108
static uint32_t pl031_read(void *opaque, target_phys_addr_t offset)
109
{
110
    pl031_state *s = (pl031_state *)opaque;
111

    
112
    if (offset >= 0xfe0  &&  offset < 0x1000)
113
        return pl031_id[(offset - 0xfe0) >> 2];
114

    
115
    switch (offset) {
116
    case RTC_DR:
117
        return pl031_get_count(s);
118
    case RTC_MR:
119
        return s->mr;
120
    case RTC_IMSC:
121
        return s->im;
122
    case RTC_RIS:
123
        return s->is;
124
    case RTC_LR:
125
        return s->lr;
126
    case RTC_CR:
127
        /* RTC is permanently enabled.  */
128
        return 1;
129
    case RTC_MIS:
130
        return s->is & s->im;
131
    case RTC_ICR:
132
        fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
133
                (int)offset);
134
        break;
135
    default:
136
        hw_error("pl031_read: Bad offset 0x%x\n", (int)offset);
137
        break;
138
    }
139

    
140
    return 0;
141
}
142

    
143
static void pl031_write(void * opaque, target_phys_addr_t offset,
144
                        uint32_t value)
145
{
146
    pl031_state *s = (pl031_state *)opaque;
147

    
148

    
149
    switch (offset) {
150
    case RTC_LR:
151
        s->tick_offset += value - pl031_get_count(s);
152
        pl031_set_alarm(s);
153
        break;
154
    case RTC_MR:
155
        s->mr = value;
156
        pl031_set_alarm(s);
157
        break;
158
    case RTC_IMSC:
159
        s->im = value & 1;
160
        DPRINTF("Interrupt mask %d\n", s->im);
161
        pl031_update(s);
162
        break;
163
    case RTC_ICR:
164
        /* The PL031 documentation (DDI0224B) states that the interrupt is
165
           cleared when bit 0 of the written value is set.  However the
166
           arm926e documentation (DDI0287B) states that the interrupt is
167
           cleared when any value is written.  */
168
        DPRINTF("Interrupt cleared");
169
        s->is = 0;
170
        pl031_update(s);
171
        break;
172
    case RTC_CR:
173
        /* Written value is ignored.  */
174
        break;
175

    
176
    case RTC_DR:
177
    case RTC_MIS:
178
    case RTC_RIS:
179
        fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
180
                (int)offset);
181
        break;
182

    
183
    default:
184
        hw_error("pl031_write: Bad offset 0x%x\n", (int)offset);
185
        break;
186
    }
187
}
188

    
189
static CPUWriteMemoryFunc * const  pl031_writefn[] = {
190
    pl031_write,
191
    pl031_write,
192
    pl031_write
193
};
194

    
195
static CPUReadMemoryFunc * const  pl031_readfn[] = {
196
    pl031_read,
197
    pl031_read,
198
    pl031_read
199
};
200

    
201
static int pl031_init(SysBusDevice *dev)
202
{
203
    int iomemtype;
204
    pl031_state *s = FROM_SYSBUS(pl031_state, dev);
205
    struct tm tm;
206

    
207
    iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s,
208
                                       DEVICE_NATIVE_ENDIAN);
209
    if (iomemtype == -1) {
210
        hw_error("pl031_init: Can't register I/O memory\n");
211
    }
212

    
213
    sysbus_init_mmio(dev, 0x1000, iomemtype);
214

    
215
    sysbus_init_irq(dev, &s->irq);
216
    /* ??? We assume vm_clock is zero at this point.  */
217
    qemu_get_timedate(&tm, 0);
218
    s->tick_offset = mktimegm(&tm);
219

    
220
    s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s);
221
    return 0;
222
}
223

    
224
static SysBusDeviceInfo pl031_info = {
225
    .init = pl031_init,
226
    .qdev.name = "pl031",
227
    .qdev.size = sizeof(pl031_state),
228
    .qdev.vmsd = &vmstate_pl031,
229
    .qdev.no_user = 1,
230
};
231

    
232
static void pl031_register_devices(void)
233
{
234
    sysbus_register_withprop(&pl031_info);
235
}
236

    
237
device_init(pl031_register_devices)