blob: 899be861cc5f94ba5cb08728c926d0dfcbb308b5 [file] [log] [blame]
pbrook9ee6e8b2007-11-11 00:04:49 +00001/*
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 *
Matthew Fernandez8e31bf32011-06-26 12:21:35 +10008 * This code is licensed under the GPL.
Peter Maydell455736d2021-07-02 11:40:12 +01009 *
10 * QEMU interface:
11 * + sysbus MMIO region 0: the device registers
12 * + sysbus IRQ: the GPIOINTR interrupt line
13 * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines
14 * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as
15 * outputs
Peter Maydellc1e69e92021-07-02 11:40:14 +010016 * + QOM property "pullups": an integer defining whether non-floating lines
17 * configured as inputs should be pulled up to logical 1 (ie whether in
18 * real hardware they have a pullup resistor on the line out of the PL061).
19 * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
20 * be pulled high, bit 1 configures line 1, and so on. The default is 0xff,
21 * indicating that all GPIO lines are pulled up to logical 1.
22 * + QOM property "pulldowns": an integer defining whether non-floating lines
23 * configured as inputs should be pulled down to logical 0 (ie whether in
24 * real hardware they have a pulldown resistor on the line out of the PL061).
25 * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
26 * be pulled low, bit 1 configures line 1, and so on. The default is 0x0.
27 * It is an error to set a bit in both "pullups" and "pulldowns". If a bit
28 * is 0 in both, then the line is considered to be floating, and it will
29 * not have qemu_set_irq() called on it when it is configured as an input.
pbrook9ee6e8b2007-11-11 00:04:49 +000030 */
31
Peter Maydell8ef94f02016-01-26 18:17:05 +000032#include "qemu/osdep.h"
Markus Armbruster64552b62019-08-12 07:23:42 +020033#include "hw/irq.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010034#include "hw/sysbus.h"
Peter Maydellc1e69e92021-07-02 11:40:14 +010035#include "hw/qdev-properties.h"
Markus Armbrusterd6454272019-08-12 07:23:45 +020036#include "migration/vmstate.h"
Peter Maydellc1e69e92021-07-02 11:40:14 +010037#include "qapi/error.h"
Paolo Bonzini03dd0242015-12-15 13:16:16 +010038#include "qemu/log.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020039#include "qemu/module.h"
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040040#include "qom/object.h"
Peter Maydell102d7d12021-07-02 11:40:09 +010041#include "trace.h"
pbrook9ee6e8b2007-11-11 00:04:49 +000042
43static const uint8_t pl061_id[12] =
Peter Maydell7063f492011-02-21 20:57:51 +000044 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45static const uint8_t pl061_id_luminary[12] =
pbrook9ee6e8b2007-11-11 00:04:49 +000046 { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
47
Andreas Färber692a76d2013-07-26 17:31:46 +020048#define TYPE_PL061 "pl061"
Eduardo Habkost80633962020-09-16 14:25:19 -040049OBJECT_DECLARE_SIMPLE_TYPE(PL061State, PL061)
Andreas Färber692a76d2013-07-26 17:31:46 +020050
Geert Uytterhoevenfaf58e52020-05-19 10:51:43 +020051#define N_GPIOS 8
52
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040053struct PL061State {
Andreas Färber692a76d2013-07-26 17:31:46 +020054 SysBusDevice parent_obj;
55
Avi Kivity3cf89f82011-10-10 17:18:44 +020056 MemoryRegion iomem;
Peter Maydella35faa92011-08-03 23:13:45 +010057 uint32_t locked;
58 uint32_t data;
Colin Leitnerbfb27e62014-09-12 14:06:48 +010059 uint32_t old_out_data;
60 uint32_t old_in_data;
Peter Maydella35faa92011-08-03 23:13:45 +010061 uint32_t dir;
62 uint32_t isense;
63 uint32_t ibe;
64 uint32_t iev;
65 uint32_t im;
66 uint32_t istate;
67 uint32_t afsel;
68 uint32_t dr2r;
69 uint32_t dr4r;
70 uint32_t dr8r;
71 uint32_t odr;
72 uint32_t pur;
73 uint32_t pdr;
74 uint32_t slr;
75 uint32_t den;
76 uint32_t cr;
Peter Maydellb3aaff12011-08-03 23:04:49 +010077 uint32_t amsel;
pbrook9ee6e8b2007-11-11 00:04:49 +000078 qemu_irq irq;
Geert Uytterhoevenfaf58e52020-05-19 10:51:43 +020079 qemu_irq out[N_GPIOS];
Peter Maydell7063f492011-02-21 20:57:51 +000080 const unsigned char *id;
Peter Maydellc1e69e92021-07-02 11:40:14 +010081 /* Properties, for non-Luminary PL061 */
82 uint32_t pullups;
83 uint32_t pulldowns;
Eduardo Habkostdb1015e2020-09-03 16:43:22 -040084};
pbrook9ee6e8b2007-11-11 00:04:49 +000085
Peter Maydella35faa92011-08-03 23:13:45 +010086static const VMStateDescription vmstate_pl061 = {
87 .name = "pl061",
Wei Huangc3a86b32016-02-18 14:16:17 +000088 .version_id = 4,
89 .minimum_version_id = 4,
Peter Maydella35faa92011-08-03 23:13:45 +010090 .fields = (VMStateField[]) {
Andreas Färberee663e92013-07-26 17:21:21 +020091 VMSTATE_UINT32(locked, PL061State),
92 VMSTATE_UINT32(data, PL061State),
Colin Leitnerbfb27e62014-09-12 14:06:48 +010093 VMSTATE_UINT32(old_out_data, PL061State),
94 VMSTATE_UINT32(old_in_data, PL061State),
Andreas Färberee663e92013-07-26 17:21:21 +020095 VMSTATE_UINT32(dir, PL061State),
96 VMSTATE_UINT32(isense, PL061State),
97 VMSTATE_UINT32(ibe, PL061State),
98 VMSTATE_UINT32(iev, PL061State),
99 VMSTATE_UINT32(im, PL061State),
100 VMSTATE_UINT32(istate, PL061State),
101 VMSTATE_UINT32(afsel, PL061State),
102 VMSTATE_UINT32(dr2r, PL061State),
103 VMSTATE_UINT32(dr4r, PL061State),
104 VMSTATE_UINT32(dr8r, PL061State),
105 VMSTATE_UINT32(odr, PL061State),
106 VMSTATE_UINT32(pur, PL061State),
107 VMSTATE_UINT32(pdr, PL061State),
108 VMSTATE_UINT32(slr, PL061State),
109 VMSTATE_UINT32(den, PL061State),
110 VMSTATE_UINT32(cr, PL061State),
Andreas Färberee663e92013-07-26 17:21:21 +0200111 VMSTATE_UINT32_V(amsel, PL061State, 2),
Peter Maydella35faa92011-08-03 23:13:45 +0100112 VMSTATE_END_OF_LIST()
113 }
114};
115
Peter Maydellad06d562021-07-02 11:40:13 +0100116static uint8_t pl061_floating(PL061State *s)
117{
118 /*
119 * Return mask of bits which correspond to pins configured as inputs
120 * and which are floating (neither pulled up to 1 nor down to 0).
121 */
122 uint8_t floating;
123
124 if (s->id == pl061_id_luminary) {
125 /*
126 * If both PUR and PDR bits are clear, there is neither a pullup
127 * nor a pulldown in place, and the output truly floats.
128 */
129 floating = ~(s->pur | s->pdr);
130 } else {
Peter Maydellc1e69e92021-07-02 11:40:14 +0100131 floating = ~(s->pullups | s->pulldowns);
Peter Maydellad06d562021-07-02 11:40:13 +0100132 }
133 return floating & ~s->dir;
134}
135
136static uint8_t pl061_pullups(PL061State *s)
137{
138 /*
139 * Return mask of bits which correspond to pins configured as inputs
140 * and which are pulled up to 1.
141 */
142 uint8_t pullups;
143
144 if (s->id == pl061_id_luminary) {
145 /*
146 * The Luminary variant of the PL061 has an extra registers which
147 * the guest can use to configure whether lines should be pullup
148 * or pulldown.
149 */
150 pullups = s->pur;
151 } else {
Peter Maydellc1e69e92021-07-02 11:40:14 +0100152 pullups = s->pullups;
Peter Maydellad06d562021-07-02 11:40:13 +0100153 }
154 return pullups & ~s->dir;
155}
156
Andreas Färberee663e92013-07-26 17:21:21 +0200157static void pl061_update(PL061State *s)
pbrook9ee6e8b2007-11-11 00:04:49 +0000158{
159 uint8_t changed;
160 uint8_t mask;
pbrook775616c2007-11-24 23:35:08 +0000161 uint8_t out;
pbrook9ee6e8b2007-11-11 00:04:49 +0000162 int i;
Peter Maydellad06d562021-07-02 11:40:13 +0100163 uint8_t pullups = pl061_pullups(s);
164 uint8_t floating = pl061_floating(s);
pbrook9ee6e8b2007-11-11 00:04:49 +0000165
Peter Maydellad06d562021-07-02 11:40:13 +0100166 trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data,
167 pullups, floating);
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100168
Peter Maydellad06d562021-07-02 11:40:13 +0100169 /*
170 * Pins configured as output are driven from the data register;
171 * otherwise if they're pulled up they're 1, and if they're floating
172 * then we give them the same value they had previously, so we don't
173 * report any change to the other end.
174 */
175 out = (s->data & s->dir) | pullups | (s->old_out_data & floating);
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100176 changed = s->old_out_data ^ out;
177 if (changed) {
178 s->old_out_data = out;
Geert Uytterhoevenfaf58e52020-05-19 10:51:43 +0200179 for (i = 0; i < N_GPIOS; i++) {
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100180 mask = 1 << i;
181 if (changed & mask) {
Peter Maydell102d7d12021-07-02 11:40:09 +0100182 int level = (out & mask) != 0;
183 trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
184 qemu_set_irq(s->out[i], level);
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100185 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000186 }
187 }
188
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100189 /* Inputs */
190 changed = (s->old_in_data ^ s->data) & ~s->dir;
191 if (changed) {
192 s->old_in_data = s->data;
Geert Uytterhoevenfaf58e52020-05-19 10:51:43 +0200193 for (i = 0; i < N_GPIOS; i++) {
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100194 mask = 1 << i;
195 if (changed & mask) {
Peter Maydell102d7d12021-07-02 11:40:09 +0100196 trace_pl061_input_change(DEVICE(s)->canonical_path, i,
197 (s->data & mask) != 0);
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100198
199 if (!(s->isense & mask)) {
200 /* Edge interrupt */
201 if (s->ibe & mask) {
202 /* Any edge triggers the interrupt */
203 s->istate |= mask;
204 } else {
205 /* Edge is selected by IEV */
206 s->istate |= ~(s->data ^ s->iev) & mask;
207 }
208 }
209 }
210 }
211 }
212
213 /* Level interrupt */
214 s->istate |= ~(s->data ^ s->iev) & s->isense;
215
Peter Maydell102d7d12021-07-02 11:40:09 +0100216 trace_pl061_update_istate(DEVICE(s)->canonical_path,
217 s->istate, s->im, (s->istate & s->im) != 0);
Colin Leitnerbfb27e62014-09-12 14:06:48 +0100218
219 qemu_set_irq(s->irq, (s->istate & s->im) != 0);
pbrook9ee6e8b2007-11-11 00:04:49 +0000220}
221
Avi Kivitya8170e52012-10-23 12:30:10 +0200222static uint64_t pl061_read(void *opaque, hwaddr offset,
Avi Kivity3cf89f82011-10-10 17:18:44 +0200223 unsigned size)
pbrook9ee6e8b2007-11-11 00:04:49 +0000224{
Andreas Färberee663e92013-07-26 17:21:21 +0200225 PL061State *s = (PL061State *)opaque;
Peter Maydell74d359b2021-07-02 11:40:11 +0100226 uint64_t r = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000227
pbrook9ee6e8b2007-11-11 00:04:49 +0000228 switch (offset) {
Peter Maydelle24a9f62021-07-02 11:40:10 +0100229 case 0x0 ... 0x3ff: /* Data */
Peter Maydell74d359b2021-07-02 11:40:11 +0100230 r = s->data & (offset >> 2);
231 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000232 case 0x400: /* Direction */
Peter Maydell74d359b2021-07-02 11:40:11 +0100233 r = s->dir;
234 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000235 case 0x404: /* Interrupt sense */
Peter Maydell74d359b2021-07-02 11:40:11 +0100236 r = s->isense;
237 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000238 case 0x408: /* Interrupt both edges */
Peter Maydell74d359b2021-07-02 11:40:11 +0100239 r = s->ibe;
240 break;
Stefan Weilff2712b2011-04-28 17:20:35 +0200241 case 0x40c: /* Interrupt event */
Peter Maydell74d359b2021-07-02 11:40:11 +0100242 r = s->iev;
243 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000244 case 0x410: /* Interrupt mask */
Peter Maydell74d359b2021-07-02 11:40:11 +0100245 r = s->im;
246 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000247 case 0x414: /* Raw interrupt status */
Peter Maydell74d359b2021-07-02 11:40:11 +0100248 r = s->istate;
249 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000250 case 0x418: /* Masked interrupt status */
Peter Maydell74d359b2021-07-02 11:40:11 +0100251 r = s->istate & s->im;
252 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000253 case 0x420: /* Alternate function select */
Peter Maydell74d359b2021-07-02 11:40:11 +0100254 r = s->afsel;
255 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000256 case 0x500: /* 2mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100257 if (s->id != pl061_id_luminary) {
258 goto bad_offset;
259 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100260 r = s->dr2r;
261 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000262 case 0x504: /* 4mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100263 if (s->id != pl061_id_luminary) {
264 goto bad_offset;
265 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100266 r = s->dr4r;
267 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000268 case 0x508: /* 8mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100269 if (s->id != pl061_id_luminary) {
270 goto bad_offset;
271 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100272 r = s->dr8r;
273 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000274 case 0x50c: /* Open drain */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100275 if (s->id != pl061_id_luminary) {
276 goto bad_offset;
277 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100278 r = s->odr;
279 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000280 case 0x510: /* Pull-up */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100281 if (s->id != pl061_id_luminary) {
282 goto bad_offset;
283 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100284 r = s->pur;
285 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000286 case 0x514: /* Pull-down */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100287 if (s->id != pl061_id_luminary) {
288 goto bad_offset;
289 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100290 r = s->pdr;
291 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000292 case 0x518: /* Slew rate control */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100293 if (s->id != pl061_id_luminary) {
294 goto bad_offset;
295 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100296 r = s->slr;
297 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000298 case 0x51c: /* Digital enable */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100299 if (s->id != pl061_id_luminary) {
300 goto bad_offset;
301 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100302 r = s->den;
303 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000304 case 0x520: /* Lock */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100305 if (s->id != pl061_id_luminary) {
306 goto bad_offset;
307 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100308 r = s->locked;
309 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000310 case 0x524: /* Commit */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100311 if (s->id != pl061_id_luminary) {
312 goto bad_offset;
313 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100314 r = s->cr;
315 break;
Peter Maydellb3aaff12011-08-03 23:04:49 +0100316 case 0x528: /* Analog mode select */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100317 if (s->id != pl061_id_luminary) {
318 goto bad_offset;
319 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100320 r = s->amsel;
321 break;
Peter Maydelle24a9f62021-07-02 11:40:10 +0100322 case 0xfd0 ... 0xfff: /* ID registers */
Peter Maydell74d359b2021-07-02 11:40:11 +0100323 r = s->id[(offset - 0xfd0) >> 2];
324 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000325 default:
Peter Maydelle24a9f62021-07-02 11:40:10 +0100326 bad_offset:
327 qemu_log_mask(LOG_GUEST_ERROR,
328 "pl061_read: Bad offset %x\n", (int)offset);
Wei Huang09aa3bf2016-02-18 11:56:20 -0500329 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000330 }
Peter Maydell74d359b2021-07-02 11:40:11 +0100331
332 trace_pl061_read(DEVICE(s)->canonical_path, offset, r);
333 return r;
pbrook9ee6e8b2007-11-11 00:04:49 +0000334}
335
Avi Kivitya8170e52012-10-23 12:30:10 +0200336static void pl061_write(void *opaque, hwaddr offset,
Avi Kivity3cf89f82011-10-10 17:18:44 +0200337 uint64_t value, unsigned size)
pbrook9ee6e8b2007-11-11 00:04:49 +0000338{
Andreas Färberee663e92013-07-26 17:21:21 +0200339 PL061State *s = (PL061State *)opaque;
pbrook9ee6e8b2007-11-11 00:04:49 +0000340 uint8_t mask;
341
Peter Maydell74d359b2021-07-02 11:40:11 +0100342 trace_pl061_write(DEVICE(s)->canonical_path, offset, value);
343
Peter Maydelle24a9f62021-07-02 11:40:10 +0100344 switch (offset) {
345 case 0 ... 0x3ff:
pbrook9ee6e8b2007-11-11 00:04:49 +0000346 mask = (offset >> 2) & s->dir;
347 s->data = (s->data & ~mask) | (value & mask);
348 pl061_update(s);
349 return;
pbrook9ee6e8b2007-11-11 00:04:49 +0000350 case 0x400: /* Direction */
Peter Maydella35faa92011-08-03 23:13:45 +0100351 s->dir = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000352 break;
353 case 0x404: /* Interrupt sense */
Peter Maydella35faa92011-08-03 23:13:45 +0100354 s->isense = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000355 break;
356 case 0x408: /* Interrupt both edges */
Peter Maydella35faa92011-08-03 23:13:45 +0100357 s->ibe = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000358 break;
Stefan Weilff2712b2011-04-28 17:20:35 +0200359 case 0x40c: /* Interrupt event */
Peter Maydella35faa92011-08-03 23:13:45 +0100360 s->iev = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000361 break;
362 case 0x410: /* Interrupt mask */
Peter Maydella35faa92011-08-03 23:13:45 +0100363 s->im = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000364 break;
365 case 0x41c: /* Interrupt clear */
366 s->istate &= ~value;
367 break;
368 case 0x420: /* Alternate function select */
369 mask = s->cr;
370 s->afsel = (s->afsel & ~mask) | (value & mask);
371 break;
372 case 0x500: /* 2mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100373 if (s->id != pl061_id_luminary) {
374 goto bad_offset;
375 }
Peter Maydella35faa92011-08-03 23:13:45 +0100376 s->dr2r = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000377 break;
378 case 0x504: /* 4mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100379 if (s->id != pl061_id_luminary) {
380 goto bad_offset;
381 }
Peter Maydella35faa92011-08-03 23:13:45 +0100382 s->dr4r = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000383 break;
384 case 0x508: /* 8mA drive */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100385 if (s->id != pl061_id_luminary) {
386 goto bad_offset;
387 }
Peter Maydella35faa92011-08-03 23:13:45 +0100388 s->dr8r = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000389 break;
390 case 0x50c: /* Open drain */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100391 if (s->id != pl061_id_luminary) {
392 goto bad_offset;
393 }
Peter Maydella35faa92011-08-03 23:13:45 +0100394 s->odr = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000395 break;
396 case 0x510: /* Pull-up */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100397 if (s->id != pl061_id_luminary) {
398 goto bad_offset;
399 }
Peter Maydella35faa92011-08-03 23:13:45 +0100400 s->pur = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000401 break;
402 case 0x514: /* Pull-down */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100403 if (s->id != pl061_id_luminary) {
404 goto bad_offset;
405 }
Peter Maydella35faa92011-08-03 23:13:45 +0100406 s->pdr = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000407 break;
408 case 0x518: /* Slew rate control */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100409 if (s->id != pl061_id_luminary) {
410 goto bad_offset;
411 }
Peter Maydella35faa92011-08-03 23:13:45 +0100412 s->slr = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000413 break;
414 case 0x51c: /* Digital enable */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100415 if (s->id != pl061_id_luminary) {
416 goto bad_offset;
417 }
Peter Maydella35faa92011-08-03 23:13:45 +0100418 s->den = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000419 break;
420 case 0x520: /* Lock */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100421 if (s->id != pl061_id_luminary) {
422 goto bad_offset;
423 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000424 s->locked = (value != 0xacce551);
425 break;
426 case 0x524: /* Commit */
Peter Maydelle24a9f62021-07-02 11:40:10 +0100427 if (s->id != pl061_id_luminary) {
428 goto bad_offset;
429 }
pbrook9ee6e8b2007-11-11 00:04:49 +0000430 if (!s->locked)
Peter Maydella35faa92011-08-03 23:13:45 +0100431 s->cr = value & 0xff;
pbrook9ee6e8b2007-11-11 00:04:49 +0000432 break;
Peter Maydellb3aaff12011-08-03 23:04:49 +0100433 case 0x528:
Peter Maydelle24a9f62021-07-02 11:40:10 +0100434 if (s->id != pl061_id_luminary) {
435 goto bad_offset;
436 }
Peter Maydellb3aaff12011-08-03 23:04:49 +0100437 s->amsel = value & 0xff;
438 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000439 default:
Peter Maydelle24a9f62021-07-02 11:40:10 +0100440 bad_offset:
441 qemu_log_mask(LOG_GUEST_ERROR,
442 "pl061_write: Bad offset %x\n", (int)offset);
443 return;
pbrook9ee6e8b2007-11-11 00:04:49 +0000444 }
445 pl061_update(s);
Wei Huang09aa3bf2016-02-18 11:56:20 -0500446 return;
pbrook9ee6e8b2007-11-11 00:04:49 +0000447}
448
Peter Maydellef4989b2021-07-02 11:40:16 +0100449static void pl061_enter_reset(Object *obj, ResetType type)
pbrook9ee6e8b2007-11-11 00:04:49 +0000450{
Peter Maydellef4989b2021-07-02 11:40:16 +0100451 PL061State *s = PL061(obj);
452
453 trace_pl061_reset(DEVICE(s)->canonical_path);
Wei Huangb527db42016-02-18 14:16:17 +0000454
455 /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */
Peter Maydell0642e152021-07-02 11:40:17 +0100456
457 /*
458 * FIXME: For the LM3S6965, not all of the PL061 instances have the
459 * same reset values for GPIOPUR, GPIOAFSEL and GPIODEN, so in theory
460 * we should allow the board to configure these via properties.
461 * In practice, we don't wire anything up to the affected GPIO lines
462 * (PB7, PC0, PC1, PC2, PC3 -- they're used for JTAG), so we can
463 * get away with this inaccuracy.
464 */
Wei Huangb527db42016-02-18 14:16:17 +0000465 s->data = 0;
Wei Huangb527db42016-02-18 14:16:17 +0000466 s->old_in_data = 0;
467 s->dir = 0;
468 s->isense = 0;
469 s->ibe = 0;
470 s->iev = 0;
471 s->im = 0;
472 s->istate = 0;
473 s->afsel = 0;
474 s->dr2r = 0xff;
475 s->dr4r = 0;
476 s->dr8r = 0;
477 s->odr = 0;
478 s->pur = 0;
479 s->pdr = 0;
480 s->slr = 0;
481 s->den = 0;
482 s->locked = 1;
483 s->cr = 0xff;
484 s->amsel = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +0000485}
486
Peter Maydellef4989b2021-07-02 11:40:16 +0100487static void pl061_hold_reset(Object *obj)
488{
489 PL061State *s = PL061(obj);
490 int i, level;
491 uint8_t floating = pl061_floating(s);
492 uint8_t pullups = pl061_pullups(s);
493
494 for (i = 0; i < N_GPIOS; i++) {
495 if (extract32(floating, i, 1)) {
496 continue;
497 }
498 level = extract32(pullups, i, 1);
499 trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
500 qemu_set_irq(s->out[i], level);
501 }
502 s->old_out_data = pullups;
503}
504
pbrook9596ebb2007-11-18 01:44:38 +0000505static void pl061_set_irq(void * opaque, int irq, int level)
pbrook9ee6e8b2007-11-11 00:04:49 +0000506{
Andreas Färberee663e92013-07-26 17:21:21 +0200507 PL061State *s = (PL061State *)opaque;
pbrook9ee6e8b2007-11-11 00:04:49 +0000508 uint8_t mask;
509
510 mask = 1 << irq;
511 if ((s->dir & mask) == 0) {
512 s->data &= ~mask;
513 if (level)
514 s->data |= mask;
515 pl061_update(s);
516 }
517}
518
Avi Kivity3cf89f82011-10-10 17:18:44 +0200519static const MemoryRegionOps pl061_ops = {
520 .read = pl061_read,
521 .write = pl061_write,
522 .endianness = DEVICE_NATIVE_ENDIAN,
pbrook9ee6e8b2007-11-11 00:04:49 +0000523};
524
Andreas Färber692a76d2013-07-26 17:31:46 +0200525static void pl061_luminary_init(Object *obj)
Peter Maydell7063f492011-02-21 20:57:51 +0000526{
Andreas Färber692a76d2013-07-26 17:31:46 +0200527 PL061State *s = PL061(obj);
528
529 s->id = pl061_id_luminary;
Peter Maydell7063f492011-02-21 20:57:51 +0000530}
531
Andreas Färber692a76d2013-07-26 17:31:46 +0200532static void pl061_init(Object *obj)
Peter Maydell7063f492011-02-21 20:57:51 +0000533{
Andreas Färber692a76d2013-07-26 17:31:46 +0200534 PL061State *s = PL061(obj);
xiaoqiang zhao09e6fb32016-06-14 15:59:13 +0100535 DeviceState *dev = DEVICE(obj);
536 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
Andreas Färber692a76d2013-07-26 17:31:46 +0200537
538 s->id = pl061_id;
xiaoqiang zhao09e6fb32016-06-14 15:59:13 +0100539
540 memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000);
541 sysbus_init_mmio(sbd, &s->iomem);
542 sysbus_init_irq(sbd, &s->irq);
Geert Uytterhoevenfaf58e52020-05-19 10:51:43 +0200543 qdev_init_gpio_in(dev, pl061_set_irq, N_GPIOS);
544 qdev_init_gpio_out(dev, s->out, N_GPIOS);
Peter Maydell7063f492011-02-21 20:57:51 +0000545}
546
Peter Maydellc1e69e92021-07-02 11:40:14 +0100547static void pl061_realize(DeviceState *dev, Error **errp)
548{
549 PL061State *s = PL061(dev);
550
551 if (s->pullups > 0xff) {
552 error_setg(errp, "pullups property must be between 0 and 0xff");
553 return;
554 }
555 if (s->pulldowns > 0xff) {
556 error_setg(errp, "pulldowns property must be between 0 and 0xff");
557 return;
558 }
559 if (s->pullups & s->pulldowns) {
560 error_setg(errp, "no bit may be set both in pullups and pulldowns");
561 return;
562 }
563}
564
565static Property pl061_props[] = {
566 DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff),
567 DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0),
568 DEFINE_PROP_END_OF_LIST()
569};
570
Anthony Liguori999e12b2012-01-24 13:12:29 -0600571static void pl061_class_init(ObjectClass *klass, void *data)
572{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600573 DeviceClass *dc = DEVICE_CLASS(klass);
Peter Maydellef4989b2021-07-02 11:40:16 +0100574 ResettableClass *rc = RESETTABLE_CLASS(klass);
Anthony Liguori999e12b2012-01-24 13:12:29 -0600575
Anthony Liguori39bffca2011-12-07 21:34:16 -0600576 dc->vmsd = &vmstate_pl061;
Peter Maydellc1e69e92021-07-02 11:40:14 +0100577 dc->realize = pl061_realize;
578 device_class_set_props(dc, pl061_props);
Peter Maydellef4989b2021-07-02 11:40:16 +0100579 rc->phases.enter = pl061_enter_reset;
580 rc->phases.hold = pl061_hold_reset;
Anthony Liguori999e12b2012-01-24 13:12:29 -0600581}
582
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100583static const TypeInfo pl061_info = {
Andreas Färber692a76d2013-07-26 17:31:46 +0200584 .name = TYPE_PL061,
Anthony Liguori39bffca2011-12-07 21:34:16 -0600585 .parent = TYPE_SYS_BUS_DEVICE,
Andreas Färberee663e92013-07-26 17:21:21 +0200586 .instance_size = sizeof(PL061State),
Andreas Färber692a76d2013-07-26 17:31:46 +0200587 .instance_init = pl061_init,
Anthony Liguori39bffca2011-12-07 21:34:16 -0600588 .class_init = pl061_class_init,
Peter Maydella35faa92011-08-03 23:13:45 +0100589};
590
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100591static const TypeInfo pl061_luminary_info = {
Anthony Liguori39bffca2011-12-07 21:34:16 -0600592 .name = "pl061_luminary",
Andreas Färber692a76d2013-07-26 17:31:46 +0200593 .parent = TYPE_PL061,
594 .instance_init = pl061_luminary_init,
Peter Maydella35faa92011-08-03 23:13:45 +0100595};
596
Andreas Färber83f7d432012-02-09 15:20:55 +0100597static void pl061_register_types(void)
Paul Brook40905a62009-06-03 15:16:49 +0100598{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600599 type_register_static(&pl061_info);
600 type_register_static(&pl061_luminary_info);
Paul Brook40905a62009-06-03 15:16:49 +0100601}
602
Andreas Färber83f7d432012-02-09 15:20:55 +0100603type_init(pl061_register_types)