root / hw / pl080.c @ 0ef654e3
History | View | Annotate | Download (11.4 kB)
1 | 5fafdf24 | ths | /*
|
---|---|---|---|
2 | e69954b9 | pbrook | * Arm PrimeCell PL080/PL081 DMA controller
|
3 | cdbdb648 | pbrook | *
|
4 | cdbdb648 | pbrook | * Copyright (c) 2006 CodeSourcery.
|
5 | cdbdb648 | pbrook | * Written by Paul Brook
|
6 | cdbdb648 | pbrook | *
|
7 | 8e31bf38 | Matthew Fernandez | * This code is licensed under the GPL.
|
8 | cdbdb648 | pbrook | */
|
9 | cdbdb648 | pbrook | |
10 | b4496b13 | Paul Brook | #include "sysbus.h" |
11 | cdbdb648 | pbrook | |
12 | e69954b9 | pbrook | #define PL080_MAX_CHANNELS 8 |
13 | cdbdb648 | pbrook | #define PL080_CONF_E 0x1 |
14 | cdbdb648 | pbrook | #define PL080_CONF_M1 0x2 |
15 | cdbdb648 | pbrook | #define PL080_CONF_M2 0x4 |
16 | cdbdb648 | pbrook | |
17 | cdbdb648 | pbrook | #define PL080_CCONF_H 0x40000 |
18 | cdbdb648 | pbrook | #define PL080_CCONF_A 0x20000 |
19 | cdbdb648 | pbrook | #define PL080_CCONF_L 0x10000 |
20 | cdbdb648 | pbrook | #define PL080_CCONF_ITC 0x08000 |
21 | cdbdb648 | pbrook | #define PL080_CCONF_IE 0x04000 |
22 | cdbdb648 | pbrook | #define PL080_CCONF_E 0x00001 |
23 | cdbdb648 | pbrook | |
24 | cdbdb648 | pbrook | #define PL080_CCTRL_I 0x80000000 |
25 | cdbdb648 | pbrook | #define PL080_CCTRL_DI 0x08000000 |
26 | cdbdb648 | pbrook | #define PL080_CCTRL_SI 0x04000000 |
27 | cdbdb648 | pbrook | #define PL080_CCTRL_D 0x02000000 |
28 | cdbdb648 | pbrook | #define PL080_CCTRL_S 0x01000000 |
29 | cdbdb648 | pbrook | |
30 | cdbdb648 | pbrook | typedef struct { |
31 | cdbdb648 | pbrook | uint32_t src; |
32 | cdbdb648 | pbrook | uint32_t dest; |
33 | cdbdb648 | pbrook | uint32_t lli; |
34 | cdbdb648 | pbrook | uint32_t ctrl; |
35 | cdbdb648 | pbrook | uint32_t conf; |
36 | cdbdb648 | pbrook | } pl080_channel; |
37 | cdbdb648 | pbrook | |
38 | cdbdb648 | pbrook | typedef struct { |
39 | b4496b13 | Paul Brook | SysBusDevice busdev; |
40 | 63b02e04 | Avi Kivity | MemoryRegion iomem; |
41 | cdbdb648 | pbrook | uint8_t tc_int; |
42 | cdbdb648 | pbrook | uint8_t tc_mask; |
43 | cdbdb648 | pbrook | uint8_t err_int; |
44 | cdbdb648 | pbrook | uint8_t err_mask; |
45 | cdbdb648 | pbrook | uint32_t conf; |
46 | cdbdb648 | pbrook | uint32_t sync; |
47 | cdbdb648 | pbrook | uint32_t req_single; |
48 | cdbdb648 | pbrook | uint32_t req_burst; |
49 | e69954b9 | pbrook | pl080_channel chan[PL080_MAX_CHANNELS]; |
50 | e69954b9 | pbrook | int nchannels;
|
51 | cdbdb648 | pbrook | /* Flag to avoid recursive DMA invocations. */
|
52 | cdbdb648 | pbrook | int running;
|
53 | d537cf6c | pbrook | qemu_irq irq; |
54 | cdbdb648 | pbrook | } pl080_state; |
55 | cdbdb648 | pbrook | |
56 | ff175853 | Peter Maydell | static const VMStateDescription vmstate_pl080_channel = { |
57 | ff175853 | Peter Maydell | .name = "pl080_channel",
|
58 | ff175853 | Peter Maydell | .version_id = 1,
|
59 | ff175853 | Peter Maydell | .minimum_version_id = 1,
|
60 | ff175853 | Peter Maydell | .fields = (VMStateField[]) { |
61 | ff175853 | Peter Maydell | VMSTATE_UINT32(src, pl080_channel), |
62 | ff175853 | Peter Maydell | VMSTATE_UINT32(dest, pl080_channel), |
63 | ff175853 | Peter Maydell | VMSTATE_UINT32(lli, pl080_channel), |
64 | ff175853 | Peter Maydell | VMSTATE_UINT32(ctrl, pl080_channel), |
65 | ff175853 | Peter Maydell | VMSTATE_UINT32(conf, pl080_channel), |
66 | ff175853 | Peter Maydell | VMSTATE_END_OF_LIST() |
67 | ff175853 | Peter Maydell | } |
68 | ff175853 | Peter Maydell | }; |
69 | ff175853 | Peter Maydell | |
70 | ff175853 | Peter Maydell | static const VMStateDescription vmstate_pl080 = { |
71 | ff175853 | Peter Maydell | .name = "pl080",
|
72 | ff175853 | Peter Maydell | .version_id = 1,
|
73 | ff175853 | Peter Maydell | .minimum_version_id = 1,
|
74 | ff175853 | Peter Maydell | .fields = (VMStateField[]) { |
75 | ff175853 | Peter Maydell | VMSTATE_UINT8(tc_int, pl080_state), |
76 | ff175853 | Peter Maydell | VMSTATE_UINT8(tc_mask, pl080_state), |
77 | ff175853 | Peter Maydell | VMSTATE_UINT8(err_int, pl080_state), |
78 | ff175853 | Peter Maydell | VMSTATE_UINT8(err_mask, pl080_state), |
79 | ff175853 | Peter Maydell | VMSTATE_UINT32(conf, pl080_state), |
80 | ff175853 | Peter Maydell | VMSTATE_UINT32(sync, pl080_state), |
81 | ff175853 | Peter Maydell | VMSTATE_UINT32(req_single, pl080_state), |
82 | ff175853 | Peter Maydell | VMSTATE_UINT32(req_burst, pl080_state), |
83 | ff175853 | Peter Maydell | VMSTATE_UINT8(tc_int, pl080_state), |
84 | ff175853 | Peter Maydell | VMSTATE_UINT8(tc_int, pl080_state), |
85 | ff175853 | Peter Maydell | VMSTATE_UINT8(tc_int, pl080_state), |
86 | ff175853 | Peter Maydell | VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS, |
87 | ff175853 | Peter Maydell | 1, vmstate_pl080_channel, pl080_channel),
|
88 | ff175853 | Peter Maydell | VMSTATE_INT32(running, pl080_state), |
89 | ff175853 | Peter Maydell | VMSTATE_END_OF_LIST() |
90 | ff175853 | Peter Maydell | } |
91 | ff175853 | Peter Maydell | }; |
92 | ff175853 | Peter Maydell | |
93 | cdbdb648 | pbrook | static const unsigned char pl080_id[] = |
94 | cdbdb648 | pbrook | { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; |
95 | cdbdb648 | pbrook | |
96 | e69954b9 | pbrook | static const unsigned char pl081_id[] = |
97 | e69954b9 | pbrook | { 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; |
98 | e69954b9 | pbrook | |
99 | cdbdb648 | pbrook | static void pl080_update(pl080_state *s) |
100 | cdbdb648 | pbrook | { |
101 | cdbdb648 | pbrook | if ((s->tc_int & s->tc_mask)
|
102 | cdbdb648 | pbrook | || (s->err_int & s->err_mask)) |
103 | d537cf6c | pbrook | qemu_irq_raise(s->irq); |
104 | cdbdb648 | pbrook | else
|
105 | d537cf6c | pbrook | qemu_irq_lower(s->irq); |
106 | cdbdb648 | pbrook | } |
107 | cdbdb648 | pbrook | |
108 | cdbdb648 | pbrook | static void pl080_run(pl080_state *s) |
109 | cdbdb648 | pbrook | { |
110 | cdbdb648 | pbrook | int c;
|
111 | cdbdb648 | pbrook | int flow;
|
112 | cdbdb648 | pbrook | pl080_channel *ch; |
113 | cdbdb648 | pbrook | int swidth;
|
114 | cdbdb648 | pbrook | int dwidth;
|
115 | cdbdb648 | pbrook | int xsize;
|
116 | cdbdb648 | pbrook | int n;
|
117 | cdbdb648 | pbrook | int src_id;
|
118 | cdbdb648 | pbrook | int dest_id;
|
119 | cdbdb648 | pbrook | int size;
|
120 | b55266b5 | blueswir1 | uint8_t buff[4];
|
121 | cdbdb648 | pbrook | uint32_t req; |
122 | cdbdb648 | pbrook | |
123 | cdbdb648 | pbrook | s->tc_mask = 0;
|
124 | e69954b9 | pbrook | for (c = 0; c < s->nchannels; c++) { |
125 | cdbdb648 | pbrook | if (s->chan[c].conf & PL080_CCONF_ITC)
|
126 | cdbdb648 | pbrook | s->tc_mask |= 1 << c;
|
127 | cdbdb648 | pbrook | if (s->chan[c].conf & PL080_CCONF_IE)
|
128 | cdbdb648 | pbrook | s->err_mask |= 1 << c;
|
129 | cdbdb648 | pbrook | } |
130 | cdbdb648 | pbrook | |
131 | cdbdb648 | pbrook | if ((s->conf & PL080_CONF_E) == 0) |
132 | cdbdb648 | pbrook | return;
|
133 | cdbdb648 | pbrook | |
134 | 2ac71179 | Paul Brook | hw_error("DMA active\n");
|
135 | cdbdb648 | pbrook | /* If we are already in the middle of a DMA operation then indicate that
|
136 | cdbdb648 | pbrook | there may be new DMA requests and return immediately. */
|
137 | cdbdb648 | pbrook | if (s->running) {
|
138 | cdbdb648 | pbrook | s->running++; |
139 | cdbdb648 | pbrook | return;
|
140 | cdbdb648 | pbrook | } |
141 | cdbdb648 | pbrook | s->running = 1;
|
142 | cdbdb648 | pbrook | while (s->running) {
|
143 | e69954b9 | pbrook | for (c = 0; c < s->nchannels; c++) { |
144 | cdbdb648 | pbrook | ch = &s->chan[c]; |
145 | cdbdb648 | pbrook | again:
|
146 | cdbdb648 | pbrook | /* Test if thiws channel has any pending DMA requests. */
|
147 | cdbdb648 | pbrook | if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
|
148 | cdbdb648 | pbrook | != PL080_CCONF_E) |
149 | cdbdb648 | pbrook | continue;
|
150 | cdbdb648 | pbrook | flow = (ch->conf >> 11) & 7; |
151 | cdbdb648 | pbrook | if (flow >= 4) { |
152 | 2ac71179 | Paul Brook | hw_error( |
153 | cdbdb648 | pbrook | "pl080_run: Peripheral flow control not implemented\n");
|
154 | cdbdb648 | pbrook | } |
155 | cdbdb648 | pbrook | src_id = (ch->conf >> 1) & 0x1f; |
156 | cdbdb648 | pbrook | dest_id = (ch->conf >> 6) & 0x1f; |
157 | cdbdb648 | pbrook | size = ch->ctrl & 0xfff;
|
158 | cdbdb648 | pbrook | req = s->req_single | s->req_burst; |
159 | cdbdb648 | pbrook | switch (flow) {
|
160 | cdbdb648 | pbrook | case 0: |
161 | cdbdb648 | pbrook | break;
|
162 | cdbdb648 | pbrook | case 1: |
163 | cdbdb648 | pbrook | if ((req & (1u << dest_id)) == 0) |
164 | cdbdb648 | pbrook | size = 0;
|
165 | cdbdb648 | pbrook | break;
|
166 | cdbdb648 | pbrook | case 2: |
167 | cdbdb648 | pbrook | if ((req & (1u << src_id)) == 0) |
168 | cdbdb648 | pbrook | size = 0;
|
169 | cdbdb648 | pbrook | break;
|
170 | cdbdb648 | pbrook | case 3: |
171 | cdbdb648 | pbrook | if ((req & (1u << src_id)) == 0 |
172 | cdbdb648 | pbrook | || (req & (1u << dest_id)) == 0) |
173 | cdbdb648 | pbrook | size = 0;
|
174 | cdbdb648 | pbrook | break;
|
175 | cdbdb648 | pbrook | } |
176 | cdbdb648 | pbrook | if (!size)
|
177 | cdbdb648 | pbrook | continue;
|
178 | cdbdb648 | pbrook | |
179 | cdbdb648 | pbrook | /* Transfer one element. */
|
180 | cdbdb648 | pbrook | /* ??? Should transfer multiple elements for a burst request. */
|
181 | cdbdb648 | pbrook | /* ??? Unclear what the proper behavior is when source and
|
182 | cdbdb648 | pbrook | destination widths are different. */
|
183 | cdbdb648 | pbrook | swidth = 1 << ((ch->ctrl >> 18) & 7); |
184 | cdbdb648 | pbrook | dwidth = 1 << ((ch->ctrl >> 21) & 7); |
185 | cdbdb648 | pbrook | for (n = 0; n < dwidth; n+= swidth) { |
186 | cdbdb648 | pbrook | cpu_physical_memory_read(ch->src, buff + n, swidth); |
187 | cdbdb648 | pbrook | if (ch->ctrl & PL080_CCTRL_SI)
|
188 | cdbdb648 | pbrook | ch->src += swidth; |
189 | cdbdb648 | pbrook | } |
190 | cdbdb648 | pbrook | xsize = (dwidth < swidth) ? swidth : dwidth; |
191 | cdbdb648 | pbrook | /* ??? This may pad the value incorrectly for dwidth < 32. */
|
192 | cdbdb648 | pbrook | for (n = 0; n < xsize; n += dwidth) { |
193 | cdbdb648 | pbrook | cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); |
194 | cdbdb648 | pbrook | if (ch->ctrl & PL080_CCTRL_DI)
|
195 | cdbdb648 | pbrook | ch->dest += swidth; |
196 | cdbdb648 | pbrook | } |
197 | cdbdb648 | pbrook | |
198 | cdbdb648 | pbrook | size--; |
199 | cdbdb648 | pbrook | ch->ctrl = (ch->ctrl & 0xfffff000) | size;
|
200 | cdbdb648 | pbrook | if (size == 0) { |
201 | cdbdb648 | pbrook | /* Transfer complete. */
|
202 | cdbdb648 | pbrook | if (ch->lli) {
|
203 | 75b0646f | Alexander Graf | ch->src = ldl_le_phys(ch->lli); |
204 | 75b0646f | Alexander Graf | ch->dest = ldl_le_phys(ch->lli + 4);
|
205 | 75b0646f | Alexander Graf | ch->ctrl = ldl_le_phys(ch->lli + 12);
|
206 | 75b0646f | Alexander Graf | ch->lli = ldl_le_phys(ch->lli + 8);
|
207 | cdbdb648 | pbrook | } else {
|
208 | cdbdb648 | pbrook | ch->conf &= ~PL080_CCONF_E; |
209 | cdbdb648 | pbrook | } |
210 | cdbdb648 | pbrook | if (ch->ctrl & PL080_CCTRL_I) {
|
211 | cdbdb648 | pbrook | s->tc_int |= 1 << c;
|
212 | cdbdb648 | pbrook | } |
213 | cdbdb648 | pbrook | } |
214 | cdbdb648 | pbrook | goto again;
|
215 | cdbdb648 | pbrook | } |
216 | cdbdb648 | pbrook | if (--s->running)
|
217 | cdbdb648 | pbrook | s->running = 1;
|
218 | cdbdb648 | pbrook | } |
219 | cdbdb648 | pbrook | } |
220 | cdbdb648 | pbrook | |
221 | 63b02e04 | Avi Kivity | static uint64_t pl080_read(void *opaque, target_phys_addr_t offset, |
222 | 63b02e04 | Avi Kivity | unsigned size)
|
223 | cdbdb648 | pbrook | { |
224 | cdbdb648 | pbrook | pl080_state *s = (pl080_state *)opaque; |
225 | cdbdb648 | pbrook | uint32_t i; |
226 | cdbdb648 | pbrook | uint32_t mask; |
227 | cdbdb648 | pbrook | |
228 | cdbdb648 | pbrook | if (offset >= 0xfe0 && offset < 0x1000) { |
229 | e69954b9 | pbrook | if (s->nchannels == 8) { |
230 | e69954b9 | pbrook | return pl080_id[(offset - 0xfe0) >> 2]; |
231 | e69954b9 | pbrook | } else {
|
232 | e69954b9 | pbrook | return pl081_id[(offset - 0xfe0) >> 2]; |
233 | e69954b9 | pbrook | } |
234 | cdbdb648 | pbrook | } |
235 | cdbdb648 | pbrook | if (offset >= 0x100 && offset < 0x200) { |
236 | cdbdb648 | pbrook | i = (offset & 0xe0) >> 5; |
237 | e69954b9 | pbrook | if (i >= s->nchannels)
|
238 | e69954b9 | pbrook | goto bad_offset;
|
239 | cdbdb648 | pbrook | switch (offset >> 2) { |
240 | cdbdb648 | pbrook | case 0: /* SrcAddr */ |
241 | cdbdb648 | pbrook | return s->chan[i].src;
|
242 | cdbdb648 | pbrook | case 1: /* DestAddr */ |
243 | cdbdb648 | pbrook | return s->chan[i].dest;
|
244 | cdbdb648 | pbrook | case 2: /* LLI */ |
245 | cdbdb648 | pbrook | return s->chan[i].lli;
|
246 | cdbdb648 | pbrook | case 3: /* Control */ |
247 | cdbdb648 | pbrook | return s->chan[i].ctrl;
|
248 | cdbdb648 | pbrook | case 4: /* Configuration */ |
249 | cdbdb648 | pbrook | return s->chan[i].conf;
|
250 | cdbdb648 | pbrook | default:
|
251 | cdbdb648 | pbrook | goto bad_offset;
|
252 | cdbdb648 | pbrook | } |
253 | cdbdb648 | pbrook | } |
254 | cdbdb648 | pbrook | switch (offset >> 2) { |
255 | cdbdb648 | pbrook | case 0: /* IntStatus */ |
256 | cdbdb648 | pbrook | return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
|
257 | cdbdb648 | pbrook | case 1: /* IntTCStatus */ |
258 | cdbdb648 | pbrook | return (s->tc_int & s->tc_mask);
|
259 | cdbdb648 | pbrook | case 3: /* IntErrorStatus */ |
260 | cdbdb648 | pbrook | return (s->err_int & s->err_mask);
|
261 | cdbdb648 | pbrook | case 5: /* RawIntTCStatus */ |
262 | cdbdb648 | pbrook | return s->tc_int;
|
263 | cdbdb648 | pbrook | case 6: /* RawIntErrorStatus */ |
264 | cdbdb648 | pbrook | return s->err_int;
|
265 | cdbdb648 | pbrook | case 7: /* EnbldChns */ |
266 | cdbdb648 | pbrook | mask = 0;
|
267 | e69954b9 | pbrook | for (i = 0; i < s->nchannels; i++) { |
268 | cdbdb648 | pbrook | if (s->chan[i].conf & PL080_CCONF_E)
|
269 | cdbdb648 | pbrook | mask |= 1 << i;
|
270 | cdbdb648 | pbrook | } |
271 | cdbdb648 | pbrook | return mask;
|
272 | cdbdb648 | pbrook | case 8: /* SoftBReq */ |
273 | cdbdb648 | pbrook | case 9: /* SoftSReq */ |
274 | cdbdb648 | pbrook | case 10: /* SoftLBReq */ |
275 | cdbdb648 | pbrook | case 11: /* SoftLSReq */ |
276 | cdbdb648 | pbrook | /* ??? Implement these. */
|
277 | cdbdb648 | pbrook | return 0; |
278 | cdbdb648 | pbrook | case 12: /* Configuration */ |
279 | cdbdb648 | pbrook | return s->conf;
|
280 | cdbdb648 | pbrook | case 13: /* Sync */ |
281 | cdbdb648 | pbrook | return s->sync;
|
282 | cdbdb648 | pbrook | default:
|
283 | cdbdb648 | pbrook | bad_offset:
|
284 | 2ac71179 | Paul Brook | hw_error("pl080_read: Bad offset %x\n", (int)offset); |
285 | cdbdb648 | pbrook | return 0; |
286 | cdbdb648 | pbrook | } |
287 | cdbdb648 | pbrook | } |
288 | cdbdb648 | pbrook | |
289 | c227f099 | Anthony Liguori | static void pl080_write(void *opaque, target_phys_addr_t offset, |
290 | 63b02e04 | Avi Kivity | uint64_t value, unsigned size)
|
291 | cdbdb648 | pbrook | { |
292 | cdbdb648 | pbrook | pl080_state *s = (pl080_state *)opaque; |
293 | cdbdb648 | pbrook | int i;
|
294 | cdbdb648 | pbrook | |
295 | cdbdb648 | pbrook | if (offset >= 0x100 && offset < 0x200) { |
296 | cdbdb648 | pbrook | i = (offset & 0xe0) >> 5; |
297 | e69954b9 | pbrook | if (i >= s->nchannels)
|
298 | e69954b9 | pbrook | goto bad_offset;
|
299 | cdbdb648 | pbrook | switch (offset >> 2) { |
300 | cdbdb648 | pbrook | case 0: /* SrcAddr */ |
301 | cdbdb648 | pbrook | s->chan[i].src = value; |
302 | cdbdb648 | pbrook | break;
|
303 | cdbdb648 | pbrook | case 1: /* DestAddr */ |
304 | cdbdb648 | pbrook | s->chan[i].dest = value; |
305 | cdbdb648 | pbrook | break;
|
306 | cdbdb648 | pbrook | case 2: /* LLI */ |
307 | cdbdb648 | pbrook | s->chan[i].lli = value; |
308 | cdbdb648 | pbrook | break;
|
309 | cdbdb648 | pbrook | case 3: /* Control */ |
310 | cdbdb648 | pbrook | s->chan[i].ctrl = value; |
311 | cdbdb648 | pbrook | break;
|
312 | cdbdb648 | pbrook | case 4: /* Configuration */ |
313 | cdbdb648 | pbrook | s->chan[i].conf = value; |
314 | cdbdb648 | pbrook | pl080_run(s); |
315 | cdbdb648 | pbrook | break;
|
316 | cdbdb648 | pbrook | } |
317 | cdbdb648 | pbrook | } |
318 | cdbdb648 | pbrook | switch (offset >> 2) { |
319 | cdbdb648 | pbrook | case 2: /* IntTCClear */ |
320 | cdbdb648 | pbrook | s->tc_int &= ~value; |
321 | cdbdb648 | pbrook | break;
|
322 | cdbdb648 | pbrook | case 4: /* IntErrorClear */ |
323 | cdbdb648 | pbrook | s->err_int &= ~value; |
324 | cdbdb648 | pbrook | break;
|
325 | cdbdb648 | pbrook | case 8: /* SoftBReq */ |
326 | cdbdb648 | pbrook | case 9: /* SoftSReq */ |
327 | cdbdb648 | pbrook | case 10: /* SoftLBReq */ |
328 | cdbdb648 | pbrook | case 11: /* SoftLSReq */ |
329 | cdbdb648 | pbrook | /* ??? Implement these. */
|
330 | 2ac71179 | Paul Brook | hw_error("pl080_write: Soft DMA not implemented\n");
|
331 | cdbdb648 | pbrook | break;
|
332 | cdbdb648 | pbrook | case 12: /* Configuration */ |
333 | cdbdb648 | pbrook | s->conf = value; |
334 | cdbdb648 | pbrook | if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
|
335 | 2ac71179 | Paul Brook | hw_error("pl080_write: Big-endian DMA not implemented\n");
|
336 | cdbdb648 | pbrook | } |
337 | cdbdb648 | pbrook | pl080_run(s); |
338 | cdbdb648 | pbrook | break;
|
339 | cdbdb648 | pbrook | case 13: /* Sync */ |
340 | cdbdb648 | pbrook | s->sync = value; |
341 | cdbdb648 | pbrook | break;
|
342 | cdbdb648 | pbrook | default:
|
343 | e69954b9 | pbrook | bad_offset:
|
344 | 2ac71179 | Paul Brook | hw_error("pl080_write: Bad offset %x\n", (int)offset); |
345 | cdbdb648 | pbrook | } |
346 | cdbdb648 | pbrook | pl080_update(s); |
347 | cdbdb648 | pbrook | } |
348 | cdbdb648 | pbrook | |
349 | 63b02e04 | Avi Kivity | static const MemoryRegionOps pl080_ops = { |
350 | 63b02e04 | Avi Kivity | .read = pl080_read, |
351 | 63b02e04 | Avi Kivity | .write = pl080_write, |
352 | 63b02e04 | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
353 | cdbdb648 | pbrook | }; |
354 | cdbdb648 | pbrook | |
355 | 81a322d4 | Gerd Hoffmann | static int pl08x_init(SysBusDevice *dev, int nchannels) |
356 | cdbdb648 | pbrook | { |
357 | b4496b13 | Paul Brook | pl080_state *s = FROM_SYSBUS(pl080_state, dev); |
358 | cdbdb648 | pbrook | |
359 | 63b02e04 | Avi Kivity | memory_region_init_io(&s->iomem, &pl080_ops, s, "pl080", 0x1000); |
360 | 750ecd44 | Avi Kivity | sysbus_init_mmio(dev, &s->iomem); |
361 | b4496b13 | Paul Brook | sysbus_init_irq(dev, &s->irq); |
362 | e69954b9 | pbrook | s->nchannels = nchannels; |
363 | 81a322d4 | Gerd Hoffmann | return 0; |
364 | cdbdb648 | pbrook | } |
365 | b4496b13 | Paul Brook | |
366 | 81a322d4 | Gerd Hoffmann | static int pl080_init(SysBusDevice *dev) |
367 | b4496b13 | Paul Brook | { |
368 | 81a322d4 | Gerd Hoffmann | return pl08x_init(dev, 8); |
369 | b4496b13 | Paul Brook | } |
370 | b4496b13 | Paul Brook | |
371 | 81a322d4 | Gerd Hoffmann | static int pl081_init(SysBusDevice *dev) |
372 | b4496b13 | Paul Brook | { |
373 | 81a322d4 | Gerd Hoffmann | return pl08x_init(dev, 2); |
374 | b4496b13 | Paul Brook | } |
375 | b4496b13 | Paul Brook | |
376 | 999e12bb | Anthony Liguori | static void pl080_class_init(ObjectClass *klass, void *data) |
377 | 999e12bb | Anthony Liguori | { |
378 | 999e12bb | Anthony Liguori | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
379 | 999e12bb | Anthony Liguori | |
380 | 999e12bb | Anthony Liguori | k->init = pl080_init; |
381 | 999e12bb | Anthony Liguori | } |
382 | 999e12bb | Anthony Liguori | |
383 | 999e12bb | Anthony Liguori | static DeviceInfo pl080_info = {
|
384 | 999e12bb | Anthony Liguori | .name = "pl080",
|
385 | 999e12bb | Anthony Liguori | .size = sizeof(pl080_state),
|
386 | 999e12bb | Anthony Liguori | .vmsd = &vmstate_pl080, |
387 | 999e12bb | Anthony Liguori | .no_user = 1,
|
388 | 999e12bb | Anthony Liguori | .class_init = pl080_class_init, |
389 | ff175853 | Peter Maydell | }; |
390 | ff175853 | Peter Maydell | |
391 | 999e12bb | Anthony Liguori | static void pl081_class_init(ObjectClass *klass, void *data) |
392 | 999e12bb | Anthony Liguori | { |
393 | 999e12bb | Anthony Liguori | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
394 | 999e12bb | Anthony Liguori | |
395 | 999e12bb | Anthony Liguori | k->init = pl081_init; |
396 | 999e12bb | Anthony Liguori | } |
397 | 999e12bb | Anthony Liguori | |
398 | 999e12bb | Anthony Liguori | static DeviceInfo pl081_info = {
|
399 | 999e12bb | Anthony Liguori | .name = "pl081",
|
400 | 999e12bb | Anthony Liguori | .size = sizeof(pl080_state),
|
401 | 999e12bb | Anthony Liguori | .vmsd = &vmstate_pl080, |
402 | 999e12bb | Anthony Liguori | .no_user = 1,
|
403 | 999e12bb | Anthony Liguori | .class_init = pl081_class_init, |
404 | ff175853 | Peter Maydell | }; |
405 | ff175853 | Peter Maydell | |
406 | b4496b13 | Paul Brook | /* The PL080 and PL081 are the same except for the number of channels
|
407 | b4496b13 | Paul Brook | they implement (8 and 2 respectively). */
|
408 | b4496b13 | Paul Brook | static void pl080_register_devices(void) |
409 | b4496b13 | Paul Brook | { |
410 | ff175853 | Peter Maydell | sysbus_register_withprop(&pl080_info); |
411 | ff175853 | Peter Maydell | sysbus_register_withprop(&pl081_info); |
412 | b4496b13 | Paul Brook | } |
413 | b4496b13 | Paul Brook | |
414 | b4496b13 | Paul Brook | device_init(pl080_register_devices) |