Statistics
| Branch: | Revision:

root / hw / pl031.c @ 17786d52

History | View | Annotate | Download (5.2 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 unsigned char pl031_id[] = {
48
    0x31, 0x10, 0x14, 0x00,         /* Device ID        */
49
    0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
50
};
51

    
52
static void pl031_update(pl031_state *s)
53
{
54
    qemu_set_irq(s->irq, s->is & s->im);
55
}
56

    
57
static void pl031_interrupt(void * opaque)
58
{
59
    pl031_state *s = (pl031_state *)opaque;
60

    
61
    s->im = 1;
62
    DPRINTF("Alarm raised\n");
63
    pl031_update(s);
64
}
65

    
66
static uint32_t pl031_get_count(pl031_state *s)
67
{
68
    /* This assumes qemu_get_clock returns the time since the machine was
69
       created.  */
70
    return s->tick_offset + qemu_get_clock(vm_clock) / get_ticks_per_sec();
71
}
72

    
73
static void pl031_set_alarm(pl031_state *s)
74
{
75
    int64_t now;
76
    uint32_t ticks;
77

    
78
    now = qemu_get_clock(vm_clock);
79
    ticks = s->tick_offset + now / get_ticks_per_sec();
80

    
81
    /* The timer wraps around.  This subtraction also wraps in the same way,
82
       and gives correct results when alarm < now_ticks.  */
83
    ticks = s->mr - ticks;
84
    DPRINTF("Alarm set in %ud ticks\n", ticks);
85
    if (ticks == 0) {
86
        qemu_del_timer(s->timer);
87
        pl031_interrupt(s);
88
    } else {
89
        qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
90
    }
91
}
92

    
93
static uint32_t pl031_read(void *opaque, target_phys_addr_t offset)
94
{
95
    pl031_state *s = (pl031_state *)opaque;
96

    
97
    if (offset >= 0xfe0  &&  offset < 0x1000)
98
        return pl031_id[(offset - 0xfe0) >> 2];
99

    
100
    switch (offset) {
101
    case RTC_DR:
102
        return pl031_get_count(s);
103
    case RTC_MR:
104
        return s->mr;
105
    case RTC_IMSC:
106
        return s->im;
107
    case RTC_RIS:
108
        return s->is;
109
    case RTC_LR:
110
        return s->lr;
111
    case RTC_CR:
112
        /* RTC is permanently enabled.  */
113
        return 1;
114
    case RTC_MIS:
115
        return s->is & s->im;
116
    case RTC_ICR:
117
        fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
118
                (int)offset);
119
        break;
120
    default:
121
        hw_error("pl031_read: Bad offset 0x%x\n", (int)offset);
122
        break;
123
    }
124

    
125
    return 0;
126
}
127

    
128
static void pl031_write(void * opaque, target_phys_addr_t offset,
129
                        uint32_t value)
130
{
131
    pl031_state *s = (pl031_state *)opaque;
132

    
133

    
134
    switch (offset) {
135
    case RTC_LR:
136
        s->tick_offset += value - pl031_get_count(s);
137
        pl031_set_alarm(s);
138
        break;
139
    case RTC_MR:
140
        s->mr = value;
141
        pl031_set_alarm(s);
142
        break;
143
    case RTC_IMSC:
144
        s->im = value & 1;
145
        DPRINTF("Interrupt mask %d\n", s->im);
146
        pl031_update(s);
147
        break;
148
    case RTC_ICR:
149
        /* The PL031 documentation (DDI0224B) states that the interupt is
150
           cleared when bit 0 of the written value is set.  However the
151
           arm926e documentation (DDI0287B) states that the interrupt is
152
           cleared when any value is written.  */
153
        DPRINTF("Interrupt cleared");
154
        s->is = 0;
155
        pl031_update(s);
156
        break;
157
    case RTC_CR:
158
        /* Written value is ignored.  */
159
        break;
160

    
161
    case RTC_DR:
162
    case RTC_MIS:
163
    case RTC_RIS:
164
        fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
165
                (int)offset);
166
        break;
167

    
168
    default:
169
        hw_error("pl031_write: Bad offset 0x%x\n", (int)offset);
170
        break;
171
    }
172
}
173

    
174
static CPUWriteMemoryFunc * const  pl031_writefn[] = {
175
    pl031_write,
176
    pl031_write,
177
    pl031_write
178
};
179

    
180
static CPUReadMemoryFunc * const  pl031_readfn[] = {
181
    pl031_read,
182
    pl031_read,
183
    pl031_read
184
};
185

    
186
static int pl031_init(SysBusDevice *dev)
187
{
188
    int iomemtype;
189
    pl031_state *s = FROM_SYSBUS(pl031_state, dev);
190
    struct tm tm;
191

    
192
    iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s);
193
    if (iomemtype == -1) {
194
        hw_error("pl031_init: Can't register I/O memory\n");
195
    }
196

    
197
    sysbus_init_mmio(dev, 0x1000, iomemtype);
198

    
199
    sysbus_init_irq(dev, &s->irq);
200
    /* ??? We assume vm_clock is zero at this point.  */
201
    qemu_get_timedate(&tm, 0);
202
    s->tick_offset = mktimegm(&tm);
203

    
204
    s->timer = qemu_new_timer(vm_clock, pl031_interrupt, s);
205
    return 0;
206
}
207

    
208
static void pl031_register_devices(void)
209
{
210
    sysbus_register_dev("pl031", sizeof(pl031_state), pl031_init);
211
}
212

    
213
device_init(pl031_register_devices)