VirtualBox

source: vbox/trunk/src/VBox/Devices/Gpio/DevPL061.cpp@ 106465

Last change on this file since 106465 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.8 KB
Line 
1/* $Id: DevPL061.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DevPL061 - ARM PL061 PrimeCell GPIO.
4 *
5 * The documentation for this device was taken from
6 * https://developer.arm.com/documentation/ddi0190/b/programmer-s-model/summary-of-primecell-gpio-registers (2023-05-22).
7 */
8
9/*
10 * Copyright (C) 2023-2024 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * SPDX-License-Identifier: GPL-3.0-only
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#define LOG_GROUP LOG_GROUP_DEV_GPIO
36#include <VBox/vmm/pdmdev.h>
37#include <iprt/assert.h>
38#include <iprt/uuid.h>
39#include <iprt/string.h>
40#include <iprt/semaphore.h>
41#include <iprt/critsect.h>
42
43#include "VBoxDD.h"
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49
50/** The current serial code saved state version. */
51#define PL061_SAVED_STATE_VERSION 1
52
53/** PL061 MMIO region size in bytes. */
54#define PL061_MMIO_SIZE _4K
55/** PL061 number of GPIO pins. */
56#define PL061_GPIO_NUM 8
57
58/** The offset of the GPIODATA (data) register from the beginning of the region. */
59#define PL061_REG_GPIODATA_INDEX 0x0
60/** The last offset of the GPIODATA (data) register from the beginning of the region. */
61#define PL061_REG_GPIODATA_INDEX_END 0x3fc
62/** The offset of the GPIODIR (data direction) register from the beginning of the region. */
63#define PL061_REG_GPIODIR_INDEX 0x400
64/** The offset of the GPIOIS (interrupt sense) register from the beginning of the region. */
65#define PL061_REG_GPIOIS_INDEX 0x404
66/** The offset of the GPIOIBE (interrupt both edges) register from the beginning of the region. */
67#define PL061_REG_GPIOIBE_INDEX 0x408
68/** The offset of the GPIOIEV (interrupt event) register from the beginning of the region. */
69#define PL061_REG_GPIOIEV_INDEX 0x40c
70/** The offset of the GPIOIE (interrupt mask) register from the beginning of the region. */
71#define PL061_REG_GPIOIE_INDEX 0x410
72/** The offset of the GPIORIS (raw interrupt status) register from the beginning of the region. */
73#define PL061_REG_GPIORIS_INDEX 0x414
74/** The offset of the GPIOMIS (masked interrupt status) register from the beginning of the region. */
75#define PL061_REG_GPIOMIS_INDEX 0x418
76/** The offset of the GPIOIC (interrupt clear) register from the beginning of the region. */
77#define PL061_REG_GPIOIC_INDEX 0x41c
78/** The offset of the GPIOAFSEL (mode control select) register from the beginning of the region. */
79#define PL061_REG_GPIOAFSEL_INDEX 0x420
80
81/** The offset of the GPIOPeriphID0 register from the beginning of the region. */
82#define PL061_REG_GPIO_PERIPH_ID0_INDEX 0xfe0
83/** The offset of the GPIOPeriphID1 register from the beginning of the region. */
84#define PL061_REG_GPIO_PERIPH_ID1_INDEX 0xfe4
85/** The offset of the GPIOPeriphID2 register from the beginning of the region. */
86#define PL061_REG_GPIO_PERIPH_ID2_INDEX 0xfe8
87/** The offset of the GPIOPeriphID3 register from the beginning of the region. */
88#define PL061_REG_GPIO_PERIPH_ID3_INDEX 0xfec
89/** The offset of the GPIOPCellID0 register from the beginning of the region. */
90#define PL061_REG_GPIO_PCELL_ID0_INDEX 0xff0
91/** The offset of the GPIOPCellID1 register from the beginning of the region. */
92#define PL061_REG_GPIO_PCELL_ID1_INDEX 0xff4
93/** The offset of the GPIOPCellID2 register from the beginning of the region. */
94#define PL061_REG_GPIO_PCELL_ID2_INDEX 0xff8
95/** The offset of the GPIOPCellID3 register from the beginning of the region. */
96#define PL061_REG_GPIO_PCELL_ID3_INDEX 0xffc
97
98/** Set the specified bits in the given register. */
99#define PL061_REG_SET(a_Reg, a_Set) ((a_Reg) |= (a_Set))
100/** Clear the specified bits in the given register. */
101#define PL061_REG_CLR(a_Reg, a_Clr) ((a_Reg) &= ~(a_Clr))
102
103
104/*********************************************************************************************************************************
105* Structures and Typedefs *
106*********************************************************************************************************************************/
107
108/**
109 * Shared PL061 GPIO device state.
110 */
111typedef struct DEVPL061
112{
113 /** The MMIO handle. */
114 IOMMMIOHANDLE hMmio;
115 /** The base MMIO address the device is registered at. */
116 RTGCPHYS GCPhysMmioBase;
117 /** The IRQ value. */
118 uint16_t u16Irq;
119
120 /** @name Registers.
121 * @{ */
122 /** Data register. */
123 uint8_t u8RegData;
124 /** Direction register. */
125 uint8_t u8RegDir;
126 /** Interrupt sense register. */
127 uint8_t u8RegIs;
128 /** Interrupt both edges register. */
129 uint8_t u8RegIbe;
130 /** Interrupt event register. */
131 uint8_t u8RegIev;
132 /** Interrupt mask register. */
133 uint8_t u8RegIe;
134 /** Raw interrupt status register. */
135 uint8_t u8RegRis;
136 /** Mode control select register. */
137 uint8_t u8RegAfsel;
138 /** @} */
139} DEVPL061;
140/** Pointer to the shared serial device state. */
141typedef DEVPL061 *PDEVPL061;
142
143
144/**
145 * PL061 device state for ring-3.
146 */
147typedef struct DEVPL061R3
148{
149 /** LUN\#0: The base interface. */
150 PDMIBASE IBase;
151 /** GPIO port interface. */
152 PDMIGPIOPORT IGpioPort;
153 /** Pointer to the attached base driver. */
154 R3PTRTYPE(PPDMIBASE) pDrvBase;
155 /** Pointer to the attached GPIO connector interface. */
156 R3PTRTYPE(PPDMIGPIOCONNECTOR) pDrvGpio;
157 /** Pointer to the device instance - only for getting our bearings in
158 * interface methods. */
159 PPDMDEVINS pDevIns;
160} DEVPL061R3;
161/** Pointer to the PL061 device state for ring-3. */
162typedef DEVPL061R3 *PDEVPL061R3;
163
164
165/**
166 * PL061 device state for ring-0.
167 */
168typedef struct DEVPL061R0
169{
170 /** Dummy. */
171 uint8_t bDummy;
172} DEVPL061R0;
173/** Pointer to the PL061 device state for ring-0. */
174typedef DEVPL061R0 *PDEVPL061R0;
175
176
177/**
178 * PL061 device state for raw-mode.
179 */
180typedef struct DEVPL061RC
181{
182 /** Dummy. */
183 uint8_t bDummy;
184} DEVPL061RC;
185/** Pointer to the serial device state for raw-mode. */
186typedef DEVPL061RC *PDEVPL061RC;
187
188/** The PL061 device state for the current context. */
189typedef CTX_SUFF(DEVPL061) DEVPL061CC;
190/** Pointer to the PL016 device state for the current context. */
191typedef CTX_SUFF(PDEVPL061) PDEVPL061CC;
192
193
194/*********************************************************************************************************************************
195* Internal Functions *
196*********************************************************************************************************************************/
197
198#ifndef VBOX_DEVICE_STRUCT_TESTCASE
199
200/**
201 * Updates the IRQ state based on the current device state.
202 *
203 * @param pDevIns The device instance.
204 * @param pThis The shared PL061 instance data.
205 */
206DECLINLINE(void) pl061IrqUpdate(PPDMDEVINS pDevIns, PDEVPL061 pThis)
207{
208 LogFlowFunc(("pThis=%#p u8RegRis=%#x u8RegIe=%#x -> %RTbool\n",
209 pThis, pThis->u8RegRis, pThis->u8RegIe,
210 RT_BOOL(pThis->u8RegRis & pThis->u8RegIe)));
211
212 if (pThis->u8RegRis & pThis->u8RegIe)
213 PDMDevHlpISASetIrqNoWait(pDevIns, pThis->u16Irq, 1);
214 else
215 PDMDevHlpISASetIrqNoWait(pDevIns, pThis->u16Irq, 0);
216}
217
218
219static void pl061InputUpdate(PPDMDEVINS pDevIns, PDEVPL061 pThis, uint8_t u8OldData)
220{
221 /* Edge interrupts. */
222 uint8_t u8ChangedData = pThis->u8RegData ^ u8OldData;
223 if (~pThis->u8RegIs & u8ChangedData)
224 {
225 /* Both edge interrupts can be treated easily. */
226 pThis->u8RegRis |= u8ChangedData & pThis->u8RegIbe;
227
228 /** @todo Single edge. */
229 }
230
231 /* Level interrupts. */
232 pThis->u8RegRis |= (pThis->u8RegIs & pThis->u8RegData) & pThis->u8RegIev;
233 pl061IrqUpdate(pDevIns, pThis);
234}
235
236
237/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
238
239
240/**
241 * @callback_method_impl{FNIOMMMIONEWREAD}
242 */
243static DECLCALLBACK(VBOXSTRICTRC) pl061MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
244{
245 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
246 RT_NOREF(pvUser);
247 Assert(cb == 4);
248 Assert(!(off & (cb - 1))); RT_NOREF(cb);
249
250 /*
251 * From the spec:
252 * Similarly, the values read from this register are determined for each bit, by the mask bit derived from the
253 * address used to access the data register, PADDR[9:2]. Bits that are 1 in the address mask cause the corresponding
254 * bits in GPIODATA to be read, and bits that are 0 in the address mask cause the corresponding bits in GPIODATA
255 * to be read as 0, regardless of their value.
256 */
257 if ( off >= PL061_REG_GPIODATA_INDEX
258 && off < PL061_REG_GPIODATA_INDEX_END + sizeof(uint32_t))
259 {
260 *(uint32_t *)pv = pThis->u8RegData & (uint8_t)(off >> 2);
261 LogFlowFunc(("%RGp cb=%u u32=%RX32\n", off, cb, *(uint32_t *)pv));
262 return VINF_SUCCESS;
263 }
264
265 uint32_t u32Val = 0;
266 VBOXSTRICTRC rc = VINF_SUCCESS;
267 switch (off)
268 {
269 case PL061_REG_GPIODIR_INDEX:
270 u32Val = pThis->u8RegDir;
271 break;
272 case PL061_REG_GPIOIS_INDEX:
273 u32Val = pThis->u8RegIs;
274 break;
275 case PL061_REG_GPIOIBE_INDEX:
276 u32Val = pThis->u8RegIbe;
277 break;
278 case PL061_REG_GPIOIEV_INDEX:
279 u32Val = pThis->u8RegIev;
280 break;
281 case PL061_REG_GPIOIE_INDEX:
282 u32Val = pThis->u8RegIe;
283 break;
284 case PL061_REG_GPIORIS_INDEX:
285 u32Val = pThis->u8RegRis;
286 break;
287 case PL061_REG_GPIOMIS_INDEX:
288 u32Val = pThis->u8RegRis & pThis->u8RegIe;
289 break;
290 case PL061_REG_GPIOAFSEL_INDEX:
291 u32Val = pThis->u8RegAfsel;
292 break;
293 case PL061_REG_GPIO_PERIPH_ID0_INDEX:
294 u32Val = 0x61;
295 break;
296 case PL061_REG_GPIO_PERIPH_ID1_INDEX:
297 u32Val = 0x10;
298 break;
299 case PL061_REG_GPIO_PERIPH_ID2_INDEX:
300 u32Val = 0x04;
301 break;
302 case PL061_REG_GPIO_PERIPH_ID3_INDEX:
303 u32Val = 0x00;
304 break;
305 case PL061_REG_GPIO_PCELL_ID0_INDEX:
306 u32Val = 0x0d;
307 break;
308 case PL061_REG_GPIO_PCELL_ID1_INDEX:
309 u32Val = 0xf0;
310 break;
311 case PL061_REG_GPIO_PCELL_ID2_INDEX:
312 u32Val = 0x05;
313 break;
314 case PL061_REG_GPIO_PCELL_ID3_INDEX:
315 u32Val = 0xb1;
316 break;
317 default:
318 break;
319 }
320
321 if (rc == VINF_SUCCESS)
322 *(uint32_t *)pv = u32Val;
323
324 LogFlowFunc(("%RGp cb=%u u32=%RX32 -> %Rrc\n", off, cb, u32Val, rc));
325 return rc;
326}
327
328
329/**
330 * @callback_method_impl{FNIOMMMIONEWWRITE}
331 */
332static DECLCALLBACK(VBOXSTRICTRC) pl061MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
333{
334 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
335 LogFlowFunc(("cb=%u reg=%RGp val=%llx\n", cb, off, cb == 4 ? *(uint32_t *)pv : cb == 8 ? *(uint64_t *)pv : 0xdeadbeef));
336 RT_NOREF(pvUser);
337 Assert(cb == 4 || cb == 8); RT_NOREF(cb);
338 Assert(!(off & (cb - 1)));
339
340 /*
341 * From the spec:
342 * In order to write to GPIODATA, the corresponding bits in the mask, resulting from the address bus, PADDR[9:2],
343 * must be HIGH. Otherwise the bit values remain unchanged by the write.
344 */
345 if ( off >= PL061_REG_GPIODATA_INDEX
346 && off < PL061_REG_GPIODATA_INDEX_END + sizeof(uint32_t))
347 {
348 uint8_t uMask = (uint8_t)(off >> 2);
349 uint8_t uNewValue = (*(const uint32_t *)pv & uMask) | (pThis->u8RegData & ~uMask);
350 if (pThis->u8RegData ^ uNewValue)
351 {
352 /** @todo Reflect changes. */
353 }
354
355 pThis->u8RegData = uNewValue & pThis->u8RegDir; /* Filter out all pins configured as input. */
356 return VINF_SUCCESS;
357 }
358
359 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
360 uint8_t u8Val = (uint8_t)*(uint32_t *)pv;
361 switch (off)
362 {
363 case PL061_REG_GPIODIR_INDEX:
364 pThis->u8RegDir = u8Val;
365 pl061IrqUpdate(pDevIns, pThis);
366 break;
367 case PL061_REG_GPIOIS_INDEX:
368 pThis->u8RegIs = u8Val;
369 pl061IrqUpdate(pDevIns, pThis);
370 break;
371 case PL061_REG_GPIOIBE_INDEX:
372 pThis->u8RegIbe = u8Val;
373 pl061IrqUpdate(pDevIns, pThis);
374 break;
375 case PL061_REG_GPIOIEV_INDEX:
376 pThis->u8RegIev = u8Val;
377 pl061IrqUpdate(pDevIns, pThis);
378 break;
379 case PL061_REG_GPIOIE_INDEX:
380 pThis->u8RegIe = u8Val;
381 pl061IrqUpdate(pDevIns, pThis);
382 break;
383 case PL061_REG_GPIOIC_INDEX:
384 pThis->u8RegRis &= ~u8Val;
385 pl061IrqUpdate(pDevIns, pThis);
386 break;
387 case PL061_REG_GPIOAFSEL_INDEX:
388 pThis->u8RegAfsel = u8Val;
389 break;
390 default:
391 break;
392
393 }
394 return rcStrict;
395}
396
397
398#ifdef IN_RING3
399
400/* -=-=-=-=-=-=-=-=- PDMIBASE -=-=-=-=-=-=-=-=- */
401
402/**
403 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
404 */
405static DECLCALLBACK(void *) pl061R3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
406{
407 PDEVPL061CC pThisCC = RT_FROM_MEMBER(pInterface, DEVPL061CC, IBase);
408 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
409 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIGPIOPORT, &pThisCC->IGpioPort);
410 return NULL;
411}
412
413
414
415/* -=-=-=-=-=-=-=-=- PDMIGPIOPORT -=-=-=-=-=-=-=-=- */
416
417/**
418 * @interface_method_impl{PDMIGPIOPORT,pfnGpioLineChange}
419 */
420static DECLCALLBACK(int) pl061R3GpioPort_GpioLineChange(PPDMIGPIOPORT pInterface, uint32_t idGpio, bool fVal)
421{
422 PDEVPL061CC pThisCC = RT_FROM_MEMBER(pInterface, DEVPL061CC, IGpioPort);
423 PPDMDEVINS pDevIns = pThisCC->pDevIns;
424 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
425
426 LogFlowFunc(("pInterface=%p idGpio=%u fVal=%RTbool\n", pInterface, idGpio, fVal));
427
428 AssertReturn(idGpio < PL061_GPIO_NUM, VERR_INVALID_PARAMETER);
429
430 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
431 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
432
433 /* Only trigger an update on an actual change and if the GPIO line is configured as an input. */
434 if ( RT_BOOL(pThis->u8RegData & RT_BIT(idGpio)) != fVal
435 && !(RT_BIT(idGpio) & pThis->u8RegDir))
436 {
437 uint8_t u8OldData = pThis->u8RegData;
438
439 if (fVal)
440 PL061_REG_SET(pThis->u8RegData, RT_BIT(idGpio));
441 else
442 PL061_REG_CLR(pThis->u8RegData, RT_BIT(idGpio));
443
444 pl061InputUpdate(pDevIns, pThis, u8OldData);
445 }
446
447 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
448 return VINF_SUCCESS;
449}
450
451
452/**
453 * @interface_method_impl{PDMIGPIOPORT,pfnGpioLineIsInput}
454 */
455static DECLCALLBACK(bool) pl061R3GpioPort_GpioLineIsInput(PPDMIGPIOPORT pInterface, uint32_t idGpio)
456{
457 PDEVPL061CC pThisCC = RT_FROM_MEMBER(pInterface, DEVPL061CC, IGpioPort);
458 PPDMDEVINS pDevIns = pThisCC->pDevIns;
459 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
460
461 AssertReturn(idGpio < PL061_GPIO_NUM, VERR_INVALID_PARAMETER);
462
463 return !RT_BOOL(pThis->u8RegDir & RT_BIT(idGpio)); /* Bit cleared means input. */
464}
465
466
467
468/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
469
470/**
471 * @callback_method_impl{FNSSMDEVLIVEEXEC}
472 */
473static DECLCALLBACK(int) pl061R3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
474{
475 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
476 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
477 RT_NOREF(uPass);
478
479 pHlp->pfnSSMPutU16(pSSM, pThis->u16Irq);
480 pHlp->pfnSSMPutGCPhys(pSSM, pThis->GCPhysMmioBase);
481 return VINF_SSM_DONT_CALL_AGAIN;
482}
483
484
485/**
486 * @callback_method_impl{FNSSMDEVSAVEEXEC}
487 */
488static DECLCALLBACK(int) pl061R3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
489{
490 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
491 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
492
493 pHlp->pfnSSMPutU16(pSSM, pThis->u16Irq);
494 pHlp->pfnSSMPutGCPhys(pSSM, pThis->GCPhysMmioBase);
495
496 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegData);
497 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegDir);
498 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegIs);
499 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegIbe);
500 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegIev);
501 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegIe);
502 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegRis);
503 pHlp->pfnSSMPutU8(pSSM, pThis->u8RegAfsel);
504
505 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
506}
507
508
509/**
510 * @callback_method_impl{FNSSMDEVLOADEXEC}
511 */
512static DECLCALLBACK(int) pl061R3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
513{
514 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
515 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
516 uint16_t u16Irq;
517 RTGCPHYS GCPhysMmioBase;
518 int rc;
519
520 RT_NOREF(uVersion);
521
522 pHlp->pfnSSMGetU16( pSSM, &u16Irq);
523 pHlp->pfnSSMGetGCPhys(pSSM, &GCPhysMmioBase);
524 if (uPass == SSM_PASS_FINAL)
525 {
526 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegData);
527 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegDir);
528 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegIs);
529 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegIbe);
530 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegIev);
531 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegIe);
532 pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegRis);
533 rc = pHlp->pfnSSMGetU8(pSSM, &pThis->u8RegAfsel);
534 AssertRCReturn(rc, rc);
535 }
536
537 if (uPass == SSM_PASS_FINAL)
538 {
539 /* The marker. */
540 uint32_t u32;
541 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
542 AssertRCReturn(rc, rc);
543 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
544 }
545
546 /*
547 * Check the config.
548 */
549 if ( pThis->u16Irq != u16Irq
550 || pThis->GCPhysMmioBase != GCPhysMmioBase)
551 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
552 N_("Config mismatch - saved Irq=%#x GCPhysMmioBase=%#RGp; configured Irq=%#x GCPhysMmioBase=%#RGp"),
553 u16Irq, GCPhysMmioBase, pThis->u16Irq, pThis->GCPhysMmioBase);
554
555 return VINF_SUCCESS;
556}
557
558
559/**
560 * @callback_method_impl{FNSSMDEVLOADDONE}
561 */
562static DECLCALLBACK(int) pl061R3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
563{
564 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
565 PDEVPL061CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL061CC);
566
567 RT_NOREF(pThis, pThisCC, pSSM);
568 return VINF_SUCCESS;
569}
570
571
572/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
573
574/**
575 * @interface_method_impl{PDMDEVREG,pfnReset}
576 */
577static DECLCALLBACK(void) pl061R3Reset(PPDMDEVINS pDevIns)
578{
579 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
580
581 pThis->u8RegData = 0;
582 pThis->u8RegDir = 0;
583 pThis->u8RegIs = 0;
584 pThis->u8RegIbe = 0;
585 pThis->u8RegIev = 0;
586 pThis->u8RegIe = 0;
587 pThis->u8RegRis = 0;
588 pThis->u8RegAfsel = 0;
589}
590
591
592/**
593 * @interface_method_impl{PDMDEVREG,pfnAttach}
594 */
595static DECLCALLBACK(int) pl061R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
596{
597 PDEVPL061CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL061CC);
598 RT_NOREF(fFlags);
599 AssertReturn(iLUN == 0, VERR_PDM_LUN_NOT_FOUND);
600
601 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "PL016 Gpio");
602 if (RT_SUCCESS(rc))
603 {
604 pThisCC->pDrvGpio = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIGPIOCONNECTOR);
605 if (!pThisCC->pDrvGpio)
606 {
607 AssertLogRelMsgFailed(("PL061#%d: instance %d has no GPIO interface!\n", pDevIns->iInstance));
608 return VERR_PDM_MISSING_INTERFACE;
609 }
610 }
611 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
612 {
613 pThisCC->pDrvBase = NULL;
614 LogRel(("PL061#%d: no unit\n", pDevIns->iInstance));
615 }
616 else /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
617 LogRel(("PL061#%d: Failed to attach to GPIO driver. rc=%Rrc\n", pDevIns->iInstance, rc));
618
619 return rc;
620}
621
622
623/**
624 * @interface_method_impl{PDMDEVREG,pfnDetach}
625 */
626static DECLCALLBACK(void) pl061R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
627{
628 PDEVPL061CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL061CC);
629 RT_NOREF(fFlags);
630 AssertReturnVoid(iLUN == 0);
631
632 /* Zero out important members. */
633 pThisCC->pDrvBase = NULL;
634 pThisCC->pDrvGpio = NULL;
635}
636
637
638/**
639 * @interface_method_impl{PDMDEVREG,pfnDestruct}
640 */
641static DECLCALLBACK(int) pl061R3Destruct(PPDMDEVINS pDevIns)
642{
643 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
644
645 /* Nothing to do (for now). */
646 return VINF_SUCCESS;
647}
648
649
650/**
651 * @interface_method_impl{PDMDEVREG,pfnConstruct}
652 */
653static DECLCALLBACK(int) pl061R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
654{
655 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
656 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
657 PDEVPL061CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL061CC);
658 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
659 int rc;
660
661 Assert(iInstance < 4);
662
663 pThisCC->pDevIns = pDevIns;
664
665 /* IBase */
666 pThisCC->IBase.pfnQueryInterface = pl061R3QueryInterface;
667 /* IGpioPort */
668 pThisCC->IGpioPort.pfnGpioLineChange = pl061R3GpioPort_GpioLineChange;
669 pThisCC->IGpioPort.pfnGpioLineIsInput = pl061R3GpioPort_GpioLineIsInput;
670
671 /*
672 * Validate and read the configuration.
673 */
674 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|MmioBase", "");
675
676 uint16_t u16Irq = 0;
677 rc = pHlp->pfnCFGMQueryU16(pCfg, "Irq", &u16Irq);
678 if (RT_FAILURE(rc))
679 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Irq\" value"));
680
681 RTGCPHYS GCPhysMmioBase = 0;
682 rc = pHlp->pfnCFGMQueryU64(pCfg, "MmioBase", &GCPhysMmioBase);
683 if (RT_FAILURE(rc))
684 return PDMDEV_SET_ERROR(pDevIns, rc,
685 N_("Configuration error: Failed to get the \"IOBase\" value"));
686
687 pThis->u16Irq = u16Irq;
688 pThis->GCPhysMmioBase = GCPhysMmioBase;
689
690 /*
691 * Register and map the MMIO region.
692 */
693 rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysMmioBase, PL061_MMIO_SIZE, pl061MmioWrite, pl061MmioRead,
694 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED, "PL061", &pThis->hMmio);
695 AssertRCReturn(rc, rc);
696
697
698 /*
699 * Saved state.
700 */
701 rc = PDMDevHlpSSMRegisterEx(pDevIns, PL061_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
702 NULL, pl061R3LiveExec, NULL,
703 NULL, pl061R3SaveExec, NULL,
704 NULL, pl061R3LoadExec, pl061R3LoadDone);
705 AssertRCReturn(rc, rc);
706
707 /*
708 * Attach the GPIO driver and get the interfaces.
709 */
710 rc = PDMDevHlpDriverAttach(pDevIns, 0 /*iLUN*/, &pThisCC->IBase, &pThisCC->pDrvBase, "GPIO");
711 if (RT_SUCCESS(rc))
712 {
713 pThisCC->pDrvGpio = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIGPIOCONNECTOR);
714 if (!pThisCC->pDrvGpio)
715 {
716 AssertLogRelMsgFailed(("Configuration error: instance %d has no GPIO interface!\n", iInstance));
717 return VERR_PDM_MISSING_INTERFACE;
718 }
719 }
720 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
721 {
722 pThisCC->pDrvBase = NULL;
723 pThisCC->pDrvGpio = NULL;
724 LogRel(("PL061#%d: no unit\n", iInstance));
725 }
726 else
727 {
728 AssertLogRelMsgFailed(("PL061#%d: Failed to attach to gpio driver. rc=%Rrc\n", iInstance, rc));
729 /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
730 return rc;
731 }
732
733 pl061R3Reset(pDevIns);
734 return VINF_SUCCESS;
735}
736
737#else /* !IN_RING3 */
738
739/**
740 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
741 */
742static DECLCALLBACK(int) pl061RZConstruct(PPDMDEVINS pDevIns)
743{
744 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
745 PDEVPL061 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL061);
746 PDEVPL061CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL061CC);
747
748 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, pl061MmioWrite, pl061MmioRead, NULL /*pvUser*/);
749 AssertRCReturn(rc, rc);
750
751 return VINF_SUCCESS;
752}
753
754#endif /* !IN_RING3 */
755
756/**
757 * The device registration structure.
758 */
759const PDMDEVREG g_DevicePl061Gpio =
760{
761 /* .u32Version = */ PDM_DEVREG_VERSION,
762 /* .uReserved0 = */ 0,
763 /* .szName = */ "arm-pl061-gpio",
764 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
765 /* .fClass = */ PDM_DEVREG_CLASS_GPIO,
766 /* .cMaxInstances = */ UINT32_MAX,
767 /* .uSharedVersion = */ 42,
768 /* .cbInstanceShared = */ sizeof(DEVPL061),
769 /* .cbInstanceCC = */ sizeof(DEVPL061CC),
770 /* .cbInstanceRC = */ sizeof(DEVPL061RC),
771 /* .cMaxPciDevices = */ 0,
772 /* .cMaxMsixVectors = */ 0,
773 /* .pszDescription = */ "ARM PL061 PrimeCell GPIO",
774#if defined(IN_RING3)
775 /* .pszRCMod = */ "VBoxDDRC.rc",
776 /* .pszR0Mod = */ "VBoxDDR0.r0",
777 /* .pfnConstruct = */ pl061R3Construct,
778 /* .pfnDestruct = */ pl061R3Destruct,
779 /* .pfnRelocate = */ NULL,
780 /* .pfnMemSetup = */ NULL,
781 /* .pfnPowerOn = */ NULL,
782 /* .pfnReset = */ pl061R3Reset,
783 /* .pfnSuspend = */ NULL,
784 /* .pfnResume = */ NULL,
785 /* .pfnAttach = */ pl061R3Attach,
786 /* .pfnDetach = */ pl061R3Detach,
787 /* .pfnQueryInterface = */ NULL,
788 /* .pfnInitComplete = */ NULL,
789 /* .pfnPowerOff = */ NULL,
790 /* .pfnSoftReset = */ NULL,
791 /* .pfnReserved0 = */ NULL,
792 /* .pfnReserved1 = */ NULL,
793 /* .pfnReserved2 = */ NULL,
794 /* .pfnReserved3 = */ NULL,
795 /* .pfnReserved4 = */ NULL,
796 /* .pfnReserved5 = */ NULL,
797 /* .pfnReserved6 = */ NULL,
798 /* .pfnReserved7 = */ NULL,
799#elif defined(IN_RING0)
800 /* .pfnEarlyConstruct = */ NULL,
801 /* .pfnConstruct = */ pl061RZConstruct,
802 /* .pfnDestruct = */ NULL,
803 /* .pfnFinalDestruct = */ NULL,
804 /* .pfnRequest = */ NULL,
805 /* .pfnReserved0 = */ NULL,
806 /* .pfnReserved1 = */ NULL,
807 /* .pfnReserved2 = */ NULL,
808 /* .pfnReserved3 = */ NULL,
809 /* .pfnReserved4 = */ NULL,
810 /* .pfnReserved5 = */ NULL,
811 /* .pfnReserved6 = */ NULL,
812 /* .pfnReserved7 = */ NULL,
813#elif defined(IN_RC)
814 /* .pfnConstruct = */ pl061RZConstruct,
815 /* .pfnReserved0 = */ NULL,
816 /* .pfnReserved1 = */ NULL,
817 /* .pfnReserved2 = */ NULL,
818 /* .pfnReserved3 = */ NULL,
819 /* .pfnReserved4 = */ NULL,
820 /* .pfnReserved5 = */ NULL,
821 /* .pfnReserved6 = */ NULL,
822 /* .pfnReserved7 = */ NULL,
823#else
824# error "Not in IN_RING3, IN_RING0 or IN_RC!"
825#endif
826 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
827};
828
829#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
830
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette