qemu

Форк
0
/
pl061.c 
603 строки · 16.6 Кб
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 licensed under the GPL.
9
 *
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
16
 *  + 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.
30
 */
31

32
#include "qemu/osdep.h"
33
#include "hw/irq.h"
34
#include "hw/sysbus.h"
35
#include "hw/qdev-properties.h"
36
#include "migration/vmstate.h"
37
#include "qapi/error.h"
38
#include "qemu/log.h"
39
#include "qemu/module.h"
40
#include "qom/object.h"
41
#include "trace.h"
42

43
static const uint8_t pl061_id[12] =
44
  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45
static const uint8_t pl061_id_luminary[12] =
46
  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
47

48
#define TYPE_PL061 "pl061"
49
OBJECT_DECLARE_SIMPLE_TYPE(PL061State, PL061)
50

51
#define N_GPIOS 8
52

53
struct PL061State {
54
    SysBusDevice parent_obj;
55

56
    MemoryRegion iomem;
57
    uint32_t locked;
58
    uint32_t data;
59
    uint32_t old_out_data;
60
    uint32_t old_in_data;
61
    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;
77
    uint32_t amsel;
78
    qemu_irq irq;
79
    qemu_irq out[N_GPIOS];
80
    const unsigned char *id;
81
    /* Properties, for non-Luminary PL061 */
82
    uint32_t pullups;
83
    uint32_t pulldowns;
84
};
85

86
static const VMStateDescription vmstate_pl061 = {
87
    .name = "pl061",
88
    .version_id = 4,
89
    .minimum_version_id = 4,
90
    .fields = (const VMStateField[]) {
91
        VMSTATE_UINT32(locked, PL061State),
92
        VMSTATE_UINT32(data, PL061State),
93
        VMSTATE_UINT32(old_out_data, PL061State),
94
        VMSTATE_UINT32(old_in_data, PL061State),
95
        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),
111
        VMSTATE_UINT32_V(amsel, PL061State, 2),
112
        VMSTATE_END_OF_LIST()
113
    }
114
};
115

116
static 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 {
131
        floating = ~(s->pullups | s->pulldowns);
132
    }
133
    return floating & ~s->dir;
134
}
135

136
static 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 {
152
        pullups = s->pullups;
153
    }
154
    return pullups & ~s->dir;
155
}
156

157
static void pl061_update(PL061State *s)
158
{
159
    uint8_t changed;
160
    uint8_t mask;
161
    uint8_t out;
162
    int i;
163
    uint8_t pullups = pl061_pullups(s);
164
    uint8_t floating = pl061_floating(s);
165

166
    trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data,
167
                       pullups, floating);
168

169
    /*
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);
176
    changed = s->old_out_data ^ out;
177
    if (changed) {
178
        s->old_out_data = out;
179
        for (i = 0; i < N_GPIOS; i++) {
180
            mask = 1 << i;
181
            if (changed & mask) {
182
                int level = (out & mask) != 0;
183
                trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
184
                qemu_set_irq(s->out[i], level);
185
            }
186
        }
187
    }
188

189
    /* Inputs */
190
    changed = (s->old_in_data ^ s->data) & ~s->dir;
191
    if (changed) {
192
        s->old_in_data = s->data;
193
        for (i = 0; i < N_GPIOS; i++) {
194
            mask = 1 << i;
195
            if (changed & mask) {
196
                trace_pl061_input_change(DEVICE(s)->canonical_path, i,
197
                                         (s->data & mask) != 0);
198

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

216
    trace_pl061_update_istate(DEVICE(s)->canonical_path,
217
                              s->istate, s->im, (s->istate & s->im) != 0);
218

219
    qemu_set_irq(s->irq, (s->istate & s->im) != 0);
220
}
221

222
static uint64_t pl061_read(void *opaque, hwaddr offset,
223
                           unsigned size)
224
{
225
    PL061State *s = (PL061State *)opaque;
226
    uint64_t r = 0;
227

228
    switch (offset) {
229
    case 0x0 ... 0x3ff: /* Data */
230
        r = s->data & (offset >> 2);
231
        break;
232
    case 0x400: /* Direction */
233
        r = s->dir;
234
        break;
235
    case 0x404: /* Interrupt sense */
236
        r = s->isense;
237
        break;
238
    case 0x408: /* Interrupt both edges */
239
        r = s->ibe;
240
        break;
241
    case 0x40c: /* Interrupt event */
242
        r = s->iev;
243
        break;
244
    case 0x410: /* Interrupt mask */
245
        r = s->im;
246
        break;
247
    case 0x414: /* Raw interrupt status */
248
        r = s->istate;
249
        break;
250
    case 0x418: /* Masked interrupt status */
251
        r = s->istate & s->im;
252
        break;
253
    case 0x420: /* Alternate function select */
254
        r = s->afsel;
255
        break;
256
    case 0x500: /* 2mA drive */
257
        if (s->id != pl061_id_luminary) {
258
            goto bad_offset;
259
        }
260
        r = s->dr2r;
261
        break;
262
    case 0x504: /* 4mA drive */
263
        if (s->id != pl061_id_luminary) {
264
            goto bad_offset;
265
        }
266
        r = s->dr4r;
267
        break;
268
    case 0x508: /* 8mA drive */
269
        if (s->id != pl061_id_luminary) {
270
            goto bad_offset;
271
        }
272
        r = s->dr8r;
273
        break;
274
    case 0x50c: /* Open drain */
275
        if (s->id != pl061_id_luminary) {
276
            goto bad_offset;
277
        }
278
        r = s->odr;
279
        break;
280
    case 0x510: /* Pull-up */
281
        if (s->id != pl061_id_luminary) {
282
            goto bad_offset;
283
        }
284
        r = s->pur;
285
        break;
286
    case 0x514: /* Pull-down */
287
        if (s->id != pl061_id_luminary) {
288
            goto bad_offset;
289
        }
290
        r = s->pdr;
291
        break;
292
    case 0x518: /* Slew rate control */
293
        if (s->id != pl061_id_luminary) {
294
            goto bad_offset;
295
        }
296
        r = s->slr;
297
        break;
298
    case 0x51c: /* Digital enable */
299
        if (s->id != pl061_id_luminary) {
300
            goto bad_offset;
301
        }
302
        r = s->den;
303
        break;
304
    case 0x520: /* Lock */
305
        if (s->id != pl061_id_luminary) {
306
            goto bad_offset;
307
        }
308
        r = s->locked;
309
        break;
310
    case 0x524: /* Commit */
311
        if (s->id != pl061_id_luminary) {
312
            goto bad_offset;
313
        }
314
        r = s->cr;
315
        break;
316
    case 0x528: /* Analog mode select */
317
        if (s->id != pl061_id_luminary) {
318
            goto bad_offset;
319
        }
320
        r = s->amsel;
321
        break;
322
    case 0xfd0 ... 0xfff: /* ID registers */
323
        r = s->id[(offset - 0xfd0) >> 2];
324
        break;
325
    default:
326
    bad_offset:
327
        qemu_log_mask(LOG_GUEST_ERROR,
328
                      "pl061_read: Bad offset %x\n", (int)offset);
329
        break;
330
    }
331

332
    trace_pl061_read(DEVICE(s)->canonical_path, offset, r);
333
    return r;
334
}
335

336
static void pl061_write(void *opaque, hwaddr offset,
337
                        uint64_t value, unsigned size)
338
{
339
    PL061State *s = (PL061State *)opaque;
340
    uint8_t mask;
341

342
    trace_pl061_write(DEVICE(s)->canonical_path, offset, value);
343

344
    switch (offset) {
345
    case 0 ... 0x3ff:
346
        mask = (offset >> 2) & s->dir;
347
        s->data = (s->data & ~mask) | (value & mask);
348
        pl061_update(s);
349
        return;
350
    case 0x400: /* Direction */
351
        s->dir = value & 0xff;
352
        break;
353
    case 0x404: /* Interrupt sense */
354
        s->isense = value & 0xff;
355
        break;
356
    case 0x408: /* Interrupt both edges */
357
        s->ibe = value & 0xff;
358
        break;
359
    case 0x40c: /* Interrupt event */
360
        s->iev = value & 0xff;
361
        break;
362
    case 0x410: /* Interrupt mask */
363
        s->im = value & 0xff;
364
        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 */
373
        if (s->id != pl061_id_luminary) {
374
            goto bad_offset;
375
        }
376
        s->dr2r = value & 0xff;
377
        break;
378
    case 0x504: /* 4mA drive */
379
        if (s->id != pl061_id_luminary) {
380
            goto bad_offset;
381
        }
382
        s->dr4r = value & 0xff;
383
        break;
384
    case 0x508: /* 8mA drive */
385
        if (s->id != pl061_id_luminary) {
386
            goto bad_offset;
387
        }
388
        s->dr8r = value & 0xff;
389
        break;
390
    case 0x50c: /* Open drain */
391
        if (s->id != pl061_id_luminary) {
392
            goto bad_offset;
393
        }
394
        s->odr = value & 0xff;
395
        break;
396
    case 0x510: /* Pull-up */
397
        if (s->id != pl061_id_luminary) {
398
            goto bad_offset;
399
        }
400
        s->pur = value & 0xff;
401
        break;
402
    case 0x514: /* Pull-down */
403
        if (s->id != pl061_id_luminary) {
404
            goto bad_offset;
405
        }
406
        s->pdr = value & 0xff;
407
        break;
408
    case 0x518: /* Slew rate control */
409
        if (s->id != pl061_id_luminary) {
410
            goto bad_offset;
411
        }
412
        s->slr = value & 0xff;
413
        break;
414
    case 0x51c: /* Digital enable */
415
        if (s->id != pl061_id_luminary) {
416
            goto bad_offset;
417
        }
418
        s->den = value & 0xff;
419
        break;
420
    case 0x520: /* Lock */
421
        if (s->id != pl061_id_luminary) {
422
            goto bad_offset;
423
        }
424
        s->locked = (value != 0xacce551);
425
        break;
426
    case 0x524: /* Commit */
427
        if (s->id != pl061_id_luminary) {
428
            goto bad_offset;
429
        }
430
        if (!s->locked)
431
            s->cr = value & 0xff;
432
        break;
433
    case 0x528:
434
        if (s->id != pl061_id_luminary) {
435
            goto bad_offset;
436
        }
437
        s->amsel = value & 0xff;
438
        break;
439
    default:
440
    bad_offset:
441
        qemu_log_mask(LOG_GUEST_ERROR,
442
                      "pl061_write: Bad offset %x\n", (int)offset);
443
        return;
444
    }
445
    pl061_update(s);
446
    return;
447
}
448

449
static void pl061_enter_reset(Object *obj, ResetType type)
450
{
451
    PL061State *s = PL061(obj);
452

453
    trace_pl061_reset(DEVICE(s)->canonical_path);
454

455
    /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */
456

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
     */
465
    s->data = 0;
466
    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;
485
}
486

487
static void pl061_hold_reset(Object *obj, ResetType type)
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

505
static void pl061_set_irq(void * opaque, int irq, int level)
506
{
507
    PL061State *s = (PL061State *)opaque;
508
    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

519
static const MemoryRegionOps pl061_ops = {
520
    .read = pl061_read,
521
    .write = pl061_write,
522
    .endianness = DEVICE_NATIVE_ENDIAN,
523
};
524

525
static void pl061_luminary_init(Object *obj)
526
{
527
    PL061State *s = PL061(obj);
528

529
    s->id = pl061_id_luminary;
530
}
531

532
static void pl061_init(Object *obj)
533
{
534
    PL061State *s = PL061(obj);
535
    DeviceState *dev = DEVICE(obj);
536
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
537

538
    s->id = pl061_id;
539

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);
543
    qdev_init_gpio_in(dev, pl061_set_irq, N_GPIOS);
544
    qdev_init_gpio_out(dev, s->out, N_GPIOS);
545
}
546

547
static 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

565
static 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

571
static void pl061_class_init(ObjectClass *klass, void *data)
572
{
573
    DeviceClass *dc = DEVICE_CLASS(klass);
574
    ResettableClass *rc = RESETTABLE_CLASS(klass);
575

576
    dc->vmsd = &vmstate_pl061;
577
    dc->realize = pl061_realize;
578
    device_class_set_props(dc, pl061_props);
579
    rc->phases.enter = pl061_enter_reset;
580
    rc->phases.hold = pl061_hold_reset;
581
}
582

583
static const TypeInfo pl061_info = {
584
    .name          = TYPE_PL061,
585
    .parent        = TYPE_SYS_BUS_DEVICE,
586
    .instance_size = sizeof(PL061State),
587
    .instance_init = pl061_init,
588
    .class_init    = pl061_class_init,
589
};
590

591
static const TypeInfo pl061_luminary_info = {
592
    .name          = "pl061_luminary",
593
    .parent        = TYPE_PL061,
594
    .instance_init = pl061_luminary_init,
595
};
596

597
static void pl061_register_types(void)
598
{
599
    type_register_static(&pl061_info);
600
    type_register_static(&pl061_luminary_info);
601
}
602

603
type_init(pl061_register_types)
604

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.