~jderose/ubuntu/raring/qemu/vde-again

« back to all changes in this revision

Viewing changes to hw/pl061.c

  • Committer: Bazaar Package Importer
  • Author(s): Aurelien Jarno, Aurelien Jarno
  • Date: 2008-08-25 04:38:35 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20080825043835-8e3tftavy8bujdch
Tags: 0.9.1-6
[ Aurelien Jarno ]
* debian/control: 
  - Update list of supported targets (Closes: bug#488339).
* debian/qemu-make-debian-root:
  - Use mktemp instead of $$ to create temporary directories (Closes: 
    bug#496394).
* debian/links:
  - Add missing links to manpages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Arm PrimeCell PL061 General Purpose IO with additional
 
3
 * Luminary Micro Stellaris bits.
 
4
 *
 
5
 * Copyright (c) 2007 CodeSourcery.
 
6
 * Written by Paul Brook
 
7
 *
 
8
 * This code is licenced under the GPL.
 
9
 */
 
10
 
 
11
#include "hw.h"
 
12
#include "primecell.h"
 
13
 
 
14
//#define DEBUG_PL061 1
 
15
 
 
16
#ifdef DEBUG_PL061
 
17
#define DPRINTF(fmt, args...) \
 
18
do { printf("pl061: " fmt , ##args); } while (0)
 
19
#define BADF(fmt, args...) \
 
20
do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
 
21
#else
 
22
#define DPRINTF(fmt, args...) do {} while(0)
 
23
#define BADF(fmt, args...) \
 
24
do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
 
25
#endif
 
26
 
 
27
static const uint8_t pl061_id[12] =
 
28
  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
 
29
 
 
30
typedef struct {
 
31
    uint32_t base;
 
32
    int locked;
 
33
    uint8_t data;
 
34
    uint8_t old_data;
 
35
    uint8_t dir;
 
36
    uint8_t isense;
 
37
    uint8_t ibe;
 
38
    uint8_t iev;
 
39
    uint8_t im;
 
40
    uint8_t istate;
 
41
    uint8_t afsel;
 
42
    uint8_t dr2r;
 
43
    uint8_t dr4r;
 
44
    uint8_t dr8r;
 
45
    uint8_t odr;
 
46
    uint8_t pur;
 
47
    uint8_t pdr;
 
48
    uint8_t slr;
 
49
    uint8_t den;
 
50
    uint8_t cr;
 
51
    uint8_t float_high;
 
52
    qemu_irq irq;
 
53
    qemu_irq out[8];
 
54
} pl061_state;
 
55
 
 
56
static void pl061_update(pl061_state *s)
 
57
{
 
58
    uint8_t changed;
 
59
    uint8_t mask;
 
60
    uint8_t out;
 
61
    int i;
 
62
 
 
63
    /* Outputs float high.  */
 
64
    /* FIXME: This is board dependent.  */
 
65
    out = (s->data & s->dir) | ~s->dir;
 
66
    changed = s->old_data ^ out;
 
67
    if (!changed)
 
68
        return;
 
69
 
 
70
    s->old_data = out;
 
71
    for (i = 0; i < 8; i++) {
 
72
        mask = 1 << i;
 
73
        if ((changed & mask) && s->out) {
 
74
            DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
 
75
            qemu_set_irq(s->out[i], (out & mask) != 0);
 
76
        }
 
77
    }
 
78
 
 
79
    /* FIXME: Implement input interrupts.  */
 
80
}
 
81
 
 
82
static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
 
83
{
 
84
    pl061_state *s = (pl061_state *)opaque;
 
85
 
 
86
    offset -= s->base;
 
87
    if (offset >= 0xfd0 && offset < 0x1000) {
 
88
        return pl061_id[(offset - 0xfd0) >> 2];
 
89
    }
 
90
    if (offset < 0x400) {
 
91
        return s->data & (offset >> 2);
 
92
    }
 
93
    switch (offset) {
 
94
    case 0x400: /* Direction */
 
95
        return s->dir;
 
96
    case 0x404: /* Interrupt sense */
 
97
        return s->isense;
 
98
    case 0x408: /* Interrupt both edges */
 
99
        return s->ibe;
 
100
    case 0x40c: /* Interupt event */
 
101
        return s->iev;
 
102
    case 0x410: /* Interrupt mask */
 
103
        return s->im;
 
104
    case 0x414: /* Raw interrupt status */
 
105
        return s->istate;
 
106
    case 0x418: /* Masked interrupt status */
 
107
        return s->istate | s->im;
 
108
    case 0x420: /* Alternate function select */
 
109
        return s->afsel;
 
110
    case 0x500: /* 2mA drive */
 
111
        return s->dr2r;
 
112
    case 0x504: /* 4mA drive */
 
113
        return s->dr4r;
 
114
    case 0x508: /* 8mA drive */
 
115
        return s->dr8r;
 
116
    case 0x50c: /* Open drain */
 
117
        return s->odr;
 
118
    case 0x510: /* Pull-up */
 
119
        return s->pur;
 
120
    case 0x514: /* Pull-down */
 
121
        return s->pdr;
 
122
    case 0x518: /* Slew rate control */
 
123
        return s->slr;
 
124
    case 0x51c: /* Digital enable */
 
125
        return s->den;
 
126
    case 0x520: /* Lock */
 
127
        return s->locked;
 
128
    case 0x524: /* Commit */
 
129
        return s->cr;
 
130
    default:
 
131
        cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
 
132
                   (int)offset);
 
133
        return 0;
 
134
    }
 
135
}
 
136
 
 
137
static void pl061_write(void *opaque, target_phys_addr_t offset,
 
138
                        uint32_t value)
 
139
{
 
140
    pl061_state *s = (pl061_state *)opaque;
 
141
    uint8_t mask;
 
142
 
 
143
    offset -= s->base;
 
144
    if (offset < 0x400) {
 
145
        mask = (offset >> 2) & s->dir;
 
146
        s->data = (s->data & ~mask) | (value & mask);
 
147
        pl061_update(s);
 
148
        return;
 
149
    }
 
150
    switch (offset) {
 
151
    case 0x400: /* Direction */
 
152
        s->dir = value;
 
153
        break;
 
154
    case 0x404: /* Interrupt sense */
 
155
        s->isense = value;
 
156
        break;
 
157
    case 0x408: /* Interrupt both edges */
 
158
        s->ibe = value;
 
159
        break;
 
160
    case 0x40c: /* Interupt event */
 
161
        s->iev = value;
 
162
        break;
 
163
    case 0x410: /* Interrupt mask */
 
164
        s->im = value;
 
165
        break;
 
166
    case 0x41c: /* Interrupt clear */
 
167
        s->istate &= ~value;
 
168
        break;
 
169
    case 0x420: /* Alternate function select */
 
170
        mask = s->cr;
 
171
        s->afsel = (s->afsel & ~mask) | (value & mask);
 
172
        break;
 
173
    case 0x500: /* 2mA drive */
 
174
        s->dr2r = value;
 
175
        break;
 
176
    case 0x504: /* 4mA drive */
 
177
        s->dr4r = value;
 
178
        break;
 
179
    case 0x508: /* 8mA drive */
 
180
        s->dr8r = value;
 
181
        break;
 
182
    case 0x50c: /* Open drain */
 
183
        s->odr = value;
 
184
        break;
 
185
    case 0x510: /* Pull-up */
 
186
        s->pur = value;
 
187
        break;
 
188
    case 0x514: /* Pull-down */
 
189
        s->pdr = value;
 
190
        break;
 
191
    case 0x518: /* Slew rate control */
 
192
        s->slr = value;
 
193
        break;
 
194
    case 0x51c: /* Digital enable */
 
195
        s->den = value;
 
196
        break;
 
197
    case 0x520: /* Lock */
 
198
        s->locked = (value != 0xacce551);
 
199
        break;
 
200
    case 0x524: /* Commit */
 
201
        if (!s->locked)
 
202
            s->cr = value;
 
203
        break;
 
204
    default:
 
205
        cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
 
206
                   (int)offset);
 
207
    }
 
208
    pl061_update(s);
 
209
}
 
210
 
 
211
static void pl061_reset(pl061_state *s)
 
212
{
 
213
  s->locked = 1;
 
214
  s->cr = 0xff;
 
215
}
 
216
 
 
217
static void pl061_set_irq(void * opaque, int irq, int level)
 
218
{
 
219
    pl061_state *s = (pl061_state *)opaque;
 
220
    uint8_t mask;
 
221
 
 
222
    mask = 1 << irq;
 
223
    if ((s->dir & mask) == 0) {
 
224
        s->data &= ~mask;
 
225
        if (level)
 
226
            s->data |= mask;
 
227
        pl061_update(s);
 
228
    }
 
229
}
 
230
 
 
231
static CPUReadMemoryFunc *pl061_readfn[] = {
 
232
   pl061_read,
 
233
   pl061_read,
 
234
   pl061_read
 
235
};
 
236
 
 
237
static CPUWriteMemoryFunc *pl061_writefn[] = {
 
238
   pl061_write,
 
239
   pl061_write,
 
240
   pl061_write
 
241
};
 
242
 
 
243
/* Returns an array of inputs.  */
 
244
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
 
245
{
 
246
    int iomemtype;
 
247
    pl061_state *s;
 
248
 
 
249
    s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
 
250
    iomemtype = cpu_register_io_memory(0, pl061_readfn,
 
251
                                       pl061_writefn, s);
 
252
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
 
253
    s->base = base;
 
254
    s->irq = irq;
 
255
    pl061_reset(s);
 
256
    if (out)
 
257
        *out = s->out;
 
258
 
 
259
    /* ??? Save/restore.  */
 
260
    return qemu_allocate_irqs(pl061_set_irq, s, 8);
 
261
}
 
262