db302f8f93
Near total rewrite of this device model. It is stylistically obsolete, has numerous coverity fails and is not up to date with latest Xilinx documentation. Fix. The registers are flattened into a single array. This greatly simplifies the MMIO accessor functions. We take the oppurtunity to update the register Macro definitions to match the latest TRM. Xilinx has de-documented some regs hence there are some straight deletions. We only do this however in the case or a stock read-as-written reset-zero register. Non-zero resets are always preserved. New register definitions are added as needed. This all comes with a VMSD version break as the union layout from before was a bit strange and we are better off without it. Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com> Message-id: 3aa016167b352ed224666909217137285fd3351d.1396503037.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
461 lines
12 KiB
C
461 lines
12 KiB
C
/*
|
|
* Status and system control registers for Xilinx Zynq Platform
|
|
*
|
|
* Copyright (c) 2011 Michal Simek <monstr@monstr.eu>
|
|
* Copyright (c) 2012 PetaLogix Pty Ltd.
|
|
* Based on hw/arm_sysctl.c, written by Paul Brook
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "hw/hw.h"
|
|
#include "qemu/timer.h"
|
|
#include "hw/sysbus.h"
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#ifdef ZYNQ_ARM_SLCR_ERR_DEBUG
|
|
#define DB_PRINT(...) do { \
|
|
fprintf(stderr, ": %s: ", __func__); \
|
|
fprintf(stderr, ## __VA_ARGS__); \
|
|
} while (0);
|
|
#else
|
|
#define DB_PRINT(...)
|
|
#endif
|
|
|
|
#define XILINX_LOCK_KEY 0x767b
|
|
#define XILINX_UNLOCK_KEY 0xdf0d
|
|
|
|
#define R_PSS_RST_CTRL_SOFT_RST 0x1
|
|
|
|
enum {
|
|
SCL = 0x000 / 4,
|
|
LOCK,
|
|
UNLOCK,
|
|
LOCKSTA,
|
|
|
|
ARM_PLL_CTRL = 0x100 / 4,
|
|
DDR_PLL_CTRL,
|
|
IO_PLL_CTRL,
|
|
PLL_STATUS,
|
|
ARM_PLL_CFG,
|
|
DDR_PLL_CFG,
|
|
IO_PLL_CFG,
|
|
|
|
ARM_CLK_CTRL = 0x120 / 4,
|
|
DDR_CLK_CTRL,
|
|
DCI_CLK_CTRL,
|
|
APER_CLK_CTRL,
|
|
USB0_CLK_CTRL,
|
|
USB1_CLK_CTRL,
|
|
GEM0_RCLK_CTRL,
|
|
GEM1_RCLK_CTRL,
|
|
GEM0_CLK_CTRL,
|
|
GEM1_CLK_CTRL,
|
|
SMC_CLK_CTRL,
|
|
LQSPI_CLK_CTRL,
|
|
SDIO_CLK_CTRL,
|
|
UART_CLK_CTRL,
|
|
SPI_CLK_CTRL,
|
|
CAN_CLK_CTRL,
|
|
CAN_MIOCLK_CTRL,
|
|
DBG_CLK_CTRL,
|
|
PCAP_CLK_CTRL,
|
|
TOPSW_CLK_CTRL,
|
|
|
|
#define FPGA_CTRL_REGS(n, start) \
|
|
FPGA ## n ## _CLK_CTRL = (start) / 4, \
|
|
FPGA ## n ## _THR_CTRL, \
|
|
FPGA ## n ## _THR_CNT, \
|
|
FPGA ## n ## _THR_STA,
|
|
FPGA_CTRL_REGS(0, 0x170)
|
|
FPGA_CTRL_REGS(1, 0x180)
|
|
FPGA_CTRL_REGS(2, 0x190)
|
|
FPGA_CTRL_REGS(3, 0x1a0)
|
|
|
|
BANDGAP_TRIP = 0x1b8 / 4,
|
|
PLL_PREDIVISOR = 0x1c0 / 4,
|
|
CLK_621_TRUE,
|
|
|
|
PSS_RST_CTRL = 0x200 / 4,
|
|
DDR_RST_CTRL,
|
|
TOPSW_RESET_CTRL,
|
|
DMAC_RST_CTRL,
|
|
USB_RST_CTRL,
|
|
GEM_RST_CTRL,
|
|
SDIO_RST_CTRL,
|
|
SPI_RST_CTRL,
|
|
CAN_RST_CTRL,
|
|
I2C_RST_CTRL,
|
|
UART_RST_CTRL,
|
|
GPIO_RST_CTRL,
|
|
LQSPI_RST_CTRL,
|
|
SMC_RST_CTRL,
|
|
OCM_RST_CTRL,
|
|
FPGA_RST_CTRL = 0x240 / 4,
|
|
A9_CPU_RST_CTRL,
|
|
|
|
RS_AWDT_CTRL = 0x24c / 4,
|
|
RST_REASON,
|
|
|
|
REBOOT_STATUS = 0x258 / 4,
|
|
BOOT_MODE,
|
|
|
|
APU_CTRL = 0x300 / 4,
|
|
WDT_CLK_SEL,
|
|
|
|
TZ_DMA_NS = 0x440 / 4,
|
|
TZ_DMA_IRQ_NS,
|
|
TZ_DMA_PERIPH_NS,
|
|
|
|
PSS_IDCODE = 0x530 / 4,
|
|
|
|
DDR_URGENT = 0x600 / 4,
|
|
DDR_CAL_START = 0x60c / 4,
|
|
DDR_REF_START = 0x614 / 4,
|
|
DDR_CMD_STA,
|
|
DDR_URGENT_SEL,
|
|
DDR_DFI_STATUS,
|
|
|
|
MIO = 0x700 / 4,
|
|
#define MIO_LENGTH 54
|
|
|
|
MIO_LOOPBACK = 0x804 / 4,
|
|
MIO_MST_TRI0,
|
|
MIO_MST_TRI1,
|
|
|
|
SD0_WP_CD_SEL = 0x830 / 4,
|
|
SD1_WP_CD_SEL,
|
|
|
|
LVL_SHFTR_EN = 0x900 / 4,
|
|
OCM_CFG = 0x910 / 4,
|
|
|
|
CPU_RAM = 0xa00 / 4,
|
|
|
|
IOU = 0xa30 / 4,
|
|
|
|
DMAC_RAM = 0xa50 / 4,
|
|
|
|
AFI0 = 0xa60 / 4,
|
|
AFI1 = AFI0 + 3,
|
|
AFI2 = AFI1 + 3,
|
|
AFI3 = AFI2 + 3,
|
|
#define AFI_LENGTH 3
|
|
|
|
OCM = 0xa90 / 4,
|
|
|
|
DEVCI_RAM = 0xaa0 / 4,
|
|
|
|
CSG_RAM = 0xab0 / 4,
|
|
|
|
GPIOB_CTRL = 0xb00 / 4,
|
|
GPIOB_CFG_CMOS18,
|
|
GPIOB_CFG_CMOS25,
|
|
GPIOB_CFG_CMOS33,
|
|
GPIOB_CFG_HSTL = 0xb14 / 4,
|
|
GPIOB_DRVR_BIAS_CTRL,
|
|
|
|
DDRIOB = 0xb40 / 4,
|
|
#define DDRIOB_LENGTH 14
|
|
};
|
|
|
|
#define ZYNQ_SLCR_MMIO_SIZE 0x1000
|
|
#define ZYNQ_SLCR_NUM_REGS (ZYNQ_SLCR_MMIO_SIZE / 4)
|
|
|
|
#define TYPE_ZYNQ_SLCR "xilinx,zynq_slcr"
|
|
#define ZYNQ_SLCR(obj) OBJECT_CHECK(ZynqSLCRState, (obj), TYPE_ZYNQ_SLCR)
|
|
|
|
typedef struct ZynqSLCRState {
|
|
SysBusDevice parent_obj;
|
|
|
|
MemoryRegion iomem;
|
|
|
|
uint32_t regs[ZYNQ_SLCR_NUM_REGS];
|
|
} ZynqSLCRState;
|
|
|
|
static void zynq_slcr_reset(DeviceState *d)
|
|
{
|
|
ZynqSLCRState *s = ZYNQ_SLCR(d);
|
|
int i;
|
|
|
|
DB_PRINT("RESET\n");
|
|
|
|
s->regs[LOCKSTA] = 1;
|
|
/* 0x100 - 0x11C */
|
|
s->regs[ARM_PLL_CTRL] = 0x0001A008;
|
|
s->regs[DDR_PLL_CTRL] = 0x0001A008;
|
|
s->regs[IO_PLL_CTRL] = 0x0001A008;
|
|
s->regs[PLL_STATUS] = 0x0000003F;
|
|
s->regs[ARM_PLL_CFG] = 0x00014000;
|
|
s->regs[DDR_PLL_CFG] = 0x00014000;
|
|
s->regs[IO_PLL_CFG] = 0x00014000;
|
|
|
|
/* 0x120 - 0x16C */
|
|
s->regs[ARM_CLK_CTRL] = 0x1F000400;
|
|
s->regs[DDR_CLK_CTRL] = 0x18400003;
|
|
s->regs[DCI_CLK_CTRL] = 0x01E03201;
|
|
s->regs[APER_CLK_CTRL] = 0x01FFCCCD;
|
|
s->regs[USB0_CLK_CTRL] = s->regs[USB1_CLK_CTRL] = 0x00101941;
|
|
s->regs[GEM0_RCLK_CTRL] = s->regs[GEM1_RCLK_CTRL] = 0x00000001;
|
|
s->regs[GEM0_CLK_CTRL] = s->regs[GEM1_CLK_CTRL] = 0x00003C01;
|
|
s->regs[SMC_CLK_CTRL] = 0x00003C01;
|
|
s->regs[LQSPI_CLK_CTRL] = 0x00002821;
|
|
s->regs[SDIO_CLK_CTRL] = 0x00001E03;
|
|
s->regs[UART_CLK_CTRL] = 0x00003F03;
|
|
s->regs[SPI_CLK_CTRL] = 0x00003F03;
|
|
s->regs[CAN_CLK_CTRL] = 0x00501903;
|
|
s->regs[DBG_CLK_CTRL] = 0x00000F03;
|
|
s->regs[PCAP_CLK_CTRL] = 0x00000F01;
|
|
|
|
/* 0x170 - 0x1AC */
|
|
s->regs[FPGA0_CLK_CTRL] = s->regs[FPGA1_CLK_CTRL] = s->regs[FPGA2_CLK_CTRL]
|
|
= s->regs[FPGA3_CLK_CTRL] = 0x00101800;
|
|
s->regs[FPGA0_THR_STA] = s->regs[FPGA1_THR_STA] = s->regs[FPGA2_THR_STA]
|
|
= s->regs[FPGA3_THR_STA] = 0x00010000;
|
|
|
|
/* 0x1B0 - 0x1D8 */
|
|
s->regs[BANDGAP_TRIP] = 0x0000001F;
|
|
s->regs[PLL_PREDIVISOR] = 0x00000001;
|
|
s->regs[CLK_621_TRUE] = 0x00000001;
|
|
|
|
/* 0x200 - 0x25C */
|
|
s->regs[FPGA_RST_CTRL] = 0x01F33F0F;
|
|
s->regs[RST_REASON] = 0x00000040;
|
|
|
|
s->regs[BOOT_MODE] = 0x00000001;
|
|
|
|
/* 0x700 - 0x7D4 */
|
|
for (i = 0; i < 54; i++) {
|
|
s->regs[MIO + i] = 0x00001601;
|
|
}
|
|
for (i = 2; i <= 8; i++) {
|
|
s->regs[MIO + i] = 0x00000601;
|
|
}
|
|
|
|
s->regs[MIO_MST_TRI0] = s->regs[MIO_MST_TRI1] = 0xFFFFFFFF;
|
|
|
|
s->regs[CPU_RAM + 0] = s->regs[CPU_RAM + 1] = s->regs[CPU_RAM + 3]
|
|
= s->regs[CPU_RAM + 4] = s->regs[CPU_RAM + 7]
|
|
= 0x00010101;
|
|
s->regs[CPU_RAM + 2] = s->regs[CPU_RAM + 5] = 0x01010101;
|
|
s->regs[CPU_RAM + 6] = 0x00000001;
|
|
|
|
s->regs[IOU + 0] = s->regs[IOU + 1] = s->regs[IOU + 2] = s->regs[IOU + 3]
|
|
= 0x09090909;
|
|
s->regs[IOU + 4] = s->regs[IOU + 5] = 0x00090909;
|
|
s->regs[IOU + 6] = 0x00000909;
|
|
|
|
s->regs[DMAC_RAM] = 0x00000009;
|
|
|
|
s->regs[AFI0 + 0] = s->regs[AFI0 + 1] = 0x09090909;
|
|
s->regs[AFI1 + 0] = s->regs[AFI1 + 1] = 0x09090909;
|
|
s->regs[AFI2 + 0] = s->regs[AFI2 + 1] = 0x09090909;
|
|
s->regs[AFI3 + 0] = s->regs[AFI3 + 1] = 0x09090909;
|
|
s->regs[AFI0 + 2] = s->regs[AFI1 + 2] = s->regs[AFI2 + 2]
|
|
= s->regs[AFI3 + 2] = 0x00000909;
|
|
|
|
s->regs[OCM + 0] = 0x01010101;
|
|
s->regs[OCM + 1] = s->regs[OCM + 2] = 0x09090909;
|
|
|
|
s->regs[DEVCI_RAM] = 0x00000909;
|
|
s->regs[CSG_RAM] = 0x00000001;
|
|
|
|
s->regs[DDRIOB + 0] = s->regs[DDRIOB + 1] = s->regs[DDRIOB + 2]
|
|
= s->regs[DDRIOB + 3] = 0x00000e00;
|
|
s->regs[DDRIOB + 4] = s->regs[DDRIOB + 5] = s->regs[DDRIOB + 6]
|
|
= 0x00000e00;
|
|
s->regs[DDRIOB + 12] = 0x00000021;
|
|
}
|
|
|
|
|
|
static bool zynq_slcr_check_offset(hwaddr offset, bool rnw)
|
|
{
|
|
switch (offset) {
|
|
case LOCK:
|
|
case UNLOCK:
|
|
case DDR_CAL_START:
|
|
case DDR_REF_START:
|
|
return !rnw; /* Write only */
|
|
case LOCKSTA:
|
|
case FPGA0_THR_STA:
|
|
case FPGA1_THR_STA:
|
|
case FPGA2_THR_STA:
|
|
case FPGA3_THR_STA:
|
|
case BOOT_MODE:
|
|
case PSS_IDCODE:
|
|
case DDR_CMD_STA:
|
|
case DDR_DFI_STATUS:
|
|
case PLL_STATUS:
|
|
return rnw;/* read only */
|
|
case SCL:
|
|
case ARM_PLL_CTRL ... IO_PLL_CTRL:
|
|
case ARM_PLL_CFG ... IO_PLL_CFG:
|
|
case ARM_CLK_CTRL ... TOPSW_CLK_CTRL:
|
|
case FPGA0_CLK_CTRL ... FPGA0_THR_CNT:
|
|
case FPGA1_CLK_CTRL ... FPGA1_THR_CNT:
|
|
case FPGA2_CLK_CTRL ... FPGA2_THR_CNT:
|
|
case FPGA3_CLK_CTRL ... FPGA3_THR_CNT:
|
|
case BANDGAP_TRIP:
|
|
case PLL_PREDIVISOR:
|
|
case CLK_621_TRUE:
|
|
case PSS_RST_CTRL ... A9_CPU_RST_CTRL:
|
|
case RS_AWDT_CTRL:
|
|
case RST_REASON:
|
|
case REBOOT_STATUS:
|
|
case APU_CTRL:
|
|
case WDT_CLK_SEL:
|
|
case TZ_DMA_NS ... TZ_DMA_PERIPH_NS:
|
|
case DDR_URGENT:
|
|
case DDR_URGENT_SEL:
|
|
case MIO ... MIO + MIO_LENGTH - 1:
|
|
case MIO_LOOPBACK ... MIO_MST_TRI1:
|
|
case SD0_WP_CD_SEL:
|
|
case SD1_WP_CD_SEL:
|
|
case LVL_SHFTR_EN:
|
|
case OCM_CFG:
|
|
case CPU_RAM:
|
|
case IOU:
|
|
case DMAC_RAM:
|
|
case AFI0 ... AFI3 + AFI_LENGTH - 1:
|
|
case OCM:
|
|
case DEVCI_RAM:
|
|
case CSG_RAM:
|
|
case GPIOB_CTRL ... GPIOB_CFG_CMOS33:
|
|
case GPIOB_CFG_HSTL:
|
|
case GPIOB_DRVR_BIAS_CTRL:
|
|
case DDRIOB ... DDRIOB + DDRIOB_LENGTH - 1:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static uint64_t zynq_slcr_read(void *opaque, hwaddr offset,
|
|
unsigned size)
|
|
{
|
|
ZynqSLCRState *s = opaque;
|
|
offset /= 4;
|
|
uint32_t ret = s->regs[offset];
|
|
|
|
if (!zynq_slcr_check_offset(offset, true)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid read access to "
|
|
" addr %" HWADDR_PRIx "\n", offset * 4);
|
|
}
|
|
|
|
DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset * 4, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void zynq_slcr_write(void *opaque, hwaddr offset,
|
|
uint64_t val, unsigned size)
|
|
{
|
|
ZynqSLCRState *s = (ZynqSLCRState *)opaque;
|
|
offset /= 4;
|
|
|
|
DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx64 "\n", offset * 4, val);
|
|
|
|
if (!zynq_slcr_check_offset(offset, false)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid write access to "
|
|
"addr %" HWADDR_PRIx "\n", offset * 4);
|
|
return;
|
|
}
|
|
|
|
switch (offset) {
|
|
case SCL:
|
|
s->regs[SCL] = val & 0x1;
|
|
return;
|
|
case LOCK:
|
|
if ((val & 0xFFFF) == XILINX_LOCK_KEY) {
|
|
DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset,
|
|
(unsigned)val & 0xFFFF);
|
|
s->regs[LOCKSTA] = 1;
|
|
} else {
|
|
DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n",
|
|
(int)offset, (unsigned)val & 0xFFFF);
|
|
}
|
|
return;
|
|
case UNLOCK:
|
|
if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) {
|
|
DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset,
|
|
(unsigned)val & 0xFFFF);
|
|
s->regs[LOCKSTA] = 0;
|
|
} else {
|
|
DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n",
|
|
(int)offset, (unsigned)val & 0xFFFF);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!s->regs[LOCKSTA]) {
|
|
s->regs[offset / 4] = val;
|
|
} else {
|
|
DB_PRINT("SCLR registers are locked. Unlock them first\n");
|
|
return;
|
|
}
|
|
|
|
switch (offset) {
|
|
case PSS_RST_CTRL:
|
|
if (val & R_PSS_RST_CTRL_SOFT_RST) {
|
|
qemu_system_reset_request();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps slcr_ops = {
|
|
.read = zynq_slcr_read,
|
|
.write = zynq_slcr_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
};
|
|
|
|
static int zynq_slcr_init(SysBusDevice *dev)
|
|
{
|
|
ZynqSLCRState *s = ZYNQ_SLCR(dev);
|
|
|
|
memory_region_init_io(&s->iomem, OBJECT(s), &slcr_ops, s, "slcr",
|
|
ZYNQ_SLCR_MMIO_SIZE);
|
|
sysbus_init_mmio(dev, &s->iomem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_zynq_slcr = {
|
|
.name = "zynq_slcr",
|
|
.version_id = 2,
|
|
.minimum_version_id = 2,
|
|
.minimum_version_id_old = 2,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static void zynq_slcr_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
|
|
|
sdc->init = zynq_slcr_init;
|
|
dc->vmsd = &vmstate_zynq_slcr;
|
|
dc->reset = zynq_slcr_reset;
|
|
}
|
|
|
|
static const TypeInfo zynq_slcr_info = {
|
|
.class_init = zynq_slcr_class_init,
|
|
.name = TYPE_ZYNQ_SLCR,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(ZynqSLCRState),
|
|
};
|
|
|
|
static void zynq_slcr_register_types(void)
|
|
{
|
|
type_register_static(&zynq_slcr_info);
|
|
}
|
|
|
|
type_init(zynq_slcr_register_types)
|