mirror of https://github.com/xqemu/xqemu.git
* New "raspi3" machine emulating RaspberryPi 3
* Fix bad register definitions for VMIDR and VMPIDR (which caused assertions for 64-bit guest CPUs with EL2 on big-endian hosts) * hw/char/stm32f2xx_usart: fix TXE/TC bit handling * Fix ast2500 protection register emulation * Lots of SD card emulation cleanups and bugfixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJajt9dAAoJEDwlJe0UNgze4goQALGl4FY6Hoif9jiMPSC93kk/ mDGP8Unv2tQKpgbIt1AqNkFzPo7yb3ne6dvg0kN4Hz2XIhT+rRlrD4fg6To6nJpn mI4xLfuWiUSsohjZT6ZoaFlsVUuDOh3eLc75OVqaUWY7jeg2E4sKmgw1/vz7bpJW uZC722IeK3sdCGlOvz9vpebN8E57i0WPha3zu6UoT18/8onNCdcRyrl4sl2peJJ3 0+8BmYKRJ8EjyVY1/NfqmAoAwAp0s+rAoqJnQeaL3ROUesJziAAa2kMCwP9C1ayD FD+e3n7v77CyUEQiHA3FVUkQMAzZdRBNMz+WU+KcxEoMw0fvK0kwLvJNVqfHK6DB jf/QuWTlo4TAJxloJ231sTPY4gSCjWnRQMHBbNrMvBonGKQ10ZWOBHDD+SJTSTob 5pnKmTsNpAeGRS7SpJ7Q7PJU43SgIiaSNHXTrRP9YhikYrq+or7RTMWp8VTM+VYd BUPG0flQia5X+OixSJ+Vf0M4esm2EMv2TTQFfQKP8ENeTHM+Iw1dn7E0Xeimpjzu GKmabtGqOHVlYoxJi7KVNjPea5a4HoNzRwqu0KCQpp1a/QWQI0i0Gs9sswMUP/E6 LY6KlnQA52QqN/hTnUSHifPFNZXQPJq06uK8XVWy3HJcY8fxJOp2J8/eKIRvlG9m rlB8/eCAMiONx0KfsQHN =EtGK -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180222' into staging * New "raspi3" machine emulating RaspberryPi 3 * Fix bad register definitions for VMIDR and VMPIDR (which caused assertions for 64-bit guest CPUs with EL2 on big-endian hosts) * hw/char/stm32f2xx_usart: fix TXE/TC bit handling * Fix ast2500 protection register emulation * Lots of SD card emulation cleanups and bugfixes # gpg: Signature made Thu 22 Feb 2018 15:18:53 GMT # gpg: using RSA key 3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20180222: (32 commits) sdcard: simplify SD_SEND_OP_COND (ACMD41) sdcard: simplify SEND_IF_COND (CMD8) sdcard: warn if host uses an incorrect address for APP CMD (CMD55) sdcard: check the card is in correct state for APP CMD (CMD55) sdcard: handles more commands in SPI mode sdcard: use a more descriptive label 'unimplemented_spi_cmd' sdcard: handle the Security Specification commands sdcard: handle CMD54 (SDIO) sdcard: use the registerfields API for the CARD_STATUS register masks sdcard: use the correct masked OCR in the R3 reply sdcard: simplify using the ldst API sdcard: remove commands from unsupported old MMC specification sdcard: clean the SCR register and add few comments sdcard: fix the 'maximum data transfer rate' to 25MHz sdcard: update the CSD CRC register regardless the CSD structure version sdcard: Don't always set the high capacity bit sdcard: use the registerfields API to access the OCR register sdcard: use G_BYTE from cutils sdcard: define SDMMC_CMD_MAX instead of using the magic '64' sdcard: add more trace events ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
205e3e78d2
|
@ -187,3 +187,26 @@ static void raspi2_machine_init(MachineClass *mc)
|
|||
mc->ignore_memory_transaction_failures = true;
|
||||
};
|
||||
DEFINE_MACHINE("raspi2", raspi2_machine_init)
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
static void raspi3_init(MachineState *machine)
|
||||
{
|
||||
raspi_init(machine, 3);
|
||||
}
|
||||
|
||||
static void raspi3_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "Raspberry Pi 3";
|
||||
mc->init = raspi3_init;
|
||||
mc->block_default_type = IF_SD;
|
||||
mc->no_parallel = 1;
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
|
||||
mc->max_cpus = BCM2836_NCPUS;
|
||||
mc->min_cpus = BCM2836_NCPUS;
|
||||
mc->default_cpus = BCM2836_NCPUS;
|
||||
mc->default_ram_size = 1024 * 1024 * 1024;
|
||||
}
|
||||
DEFINE_MACHINE("raspi3", raspi3_machine_init)
|
||||
#endif
|
||||
|
|
|
@ -96,12 +96,10 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
|
|||
switch (addr) {
|
||||
case USART_SR:
|
||||
retvalue = s->usart_sr;
|
||||
s->usart_sr &= ~USART_SR_TC;
|
||||
qemu_chr_fe_accept_input(&s->chr);
|
||||
return retvalue;
|
||||
case USART_DR:
|
||||
DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
|
||||
s->usart_sr |= USART_SR_TXE;
|
||||
s->usart_sr &= ~USART_SR_RXNE;
|
||||
qemu_chr_fe_accept_input(&s->chr);
|
||||
qemu_set_irq(s->irq, 0);
|
||||
|
@ -137,7 +135,9 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
|
|||
switch (addr) {
|
||||
case USART_SR:
|
||||
if (value <= 0x3FF) {
|
||||
s->usart_sr = value;
|
||||
/* I/O being synchronous, TXE is always set. In addition, it may
|
||||
only be set by hardware, so keep it set here. */
|
||||
s->usart_sr = value | USART_SR_TXE;
|
||||
} else {
|
||||
s->usart_sr &= value;
|
||||
}
|
||||
|
@ -151,8 +151,12 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
|
|||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
qemu_chr_fe_write_all(&s->chr, &ch, 1);
|
||||
/* XXX I/O are currently synchronous, making it impossible for
|
||||
software to observe transient states where TXE or TC aren't
|
||||
set. Unlike TXE however, which is read-only, software may
|
||||
clear TC by writing 0 to the SR register, so set it again
|
||||
on each write. */
|
||||
s->usart_sr |= USART_SR_TC;
|
||||
s->usart_sr &= ~USART_SR_TXE;
|
||||
}
|
||||
return;
|
||||
case USART_BRR:
|
||||
|
|
|
@ -191,7 +191,7 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data,
|
|||
}
|
||||
|
||||
if (reg > PROT_KEY && reg < CPU2_BASE_SEG1 &&
|
||||
s->regs[PROT_KEY] != ASPEED_SCU_PROT_KEY) {
|
||||
!s->regs[PROT_KEY]) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
@ -199,6 +199,10 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data,
|
|||
trace_aspeed_scu_write(offset, size, data);
|
||||
|
||||
switch (reg) {
|
||||
case PROT_KEY:
|
||||
s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0;
|
||||
return;
|
||||
|
||||
case FREQ_CNTR_EVAL:
|
||||
case VGA_SCRATCH1 ... VGA_SCRATCH8:
|
||||
case RNG_DATA:
|
||||
|
|
|
@ -110,7 +110,12 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data,
|
|||
return;
|
||||
}
|
||||
|
||||
if (addr != R_PROT && s->regs[R_PROT] != PROT_KEY_UNLOCK) {
|
||||
if (addr == R_PROT) {
|
||||
s->regs[addr] = (data == PROT_KEY_UNLOCK) ? 1 : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s->regs[R_PROT]) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: SDMC is locked!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
@ -123,6 +128,7 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data,
|
|||
data &= ~ASPEED_SDMC_READONLY_MASK;
|
||||
break;
|
||||
case AST2500_A0_SILICON_REV:
|
||||
case AST2500_A1_SILICON_REV:
|
||||
data &= ~ASPEED_SDMC_AST2500_READONLY_MASK;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -22,11 +22,12 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "include/qapi/error.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/sd/sd.h"
|
||||
|
@ -68,7 +69,7 @@ struct MilkymistMemcardState {
|
|||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion regs_region;
|
||||
SDState *card;
|
||||
SDBus sdbus;
|
||||
|
||||
int command_write_ptr;
|
||||
int response_read_ptr;
|
||||
|
@ -104,7 +105,7 @@ static void memcard_sd_command(MilkymistMemcardState *s)
|
|||
req.crc = s->command[5];
|
||||
|
||||
s->response[0] = req.cmd;
|
||||
s->response_len = sd_do_command(s->card, &req, s->response+1);
|
||||
s->response_len = sdbus_do_command(&s->sdbus, &req, s->response + 1);
|
||||
s->response_read_ptr = 0;
|
||||
|
||||
if (s->response_len == 16) {
|
||||
|
@ -138,8 +139,8 @@ static uint64_t memcard_read(void *opaque, hwaddr addr,
|
|||
} else {
|
||||
r = s->response[s->response_read_ptr++];
|
||||
if (s->response_read_ptr > s->response_len) {
|
||||
error_report("milkymist_memcard: "
|
||||
"read more cmd bytes than available. Clipping.");
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "milkymist_memcard: "
|
||||
"read more cmd bytes than available. Clipping.");
|
||||
s->response_read_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
@ -149,10 +150,10 @@ static uint64_t memcard_read(void *opaque, hwaddr addr,
|
|||
r = 0xffffffff;
|
||||
} else {
|
||||
r = 0;
|
||||
r |= sd_read_data(s->card) << 24;
|
||||
r |= sd_read_data(s->card) << 16;
|
||||
r |= sd_read_data(s->card) << 8;
|
||||
r |= sd_read_data(s->card);
|
||||
r |= sdbus_read_data(&s->sdbus) << 24;
|
||||
r |= sdbus_read_data(&s->sdbus) << 16;
|
||||
r |= sdbus_read_data(&s->sdbus) << 8;
|
||||
r |= sdbus_read_data(&s->sdbus);
|
||||
}
|
||||
break;
|
||||
case R_CLK2XDIV:
|
||||
|
@ -163,8 +164,9 @@ static uint64_t memcard_read(void *opaque, hwaddr addr,
|
|||
break;
|
||||
|
||||
default:
|
||||
error_report("milkymist_memcard: read access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
qemu_log_mask(LOG_UNIMP, "milkymist_memcard: "
|
||||
"read access to unknown register 0x%" HWADDR_PRIx "\n",
|
||||
addr << 2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -205,10 +207,10 @@ static void memcard_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
if (!s->enabled) {
|
||||
break;
|
||||
}
|
||||
sd_write_data(s->card, (value >> 24) & 0xff);
|
||||
sd_write_data(s->card, (value >> 16) & 0xff);
|
||||
sd_write_data(s->card, (value >> 8) & 0xff);
|
||||
sd_write_data(s->card, value & 0xff);
|
||||
sdbus_write_data(&s->sdbus, (value >> 24) & 0xff);
|
||||
sdbus_write_data(&s->sdbus, (value >> 16) & 0xff);
|
||||
sdbus_write_data(&s->sdbus, (value >> 8) & 0xff);
|
||||
sdbus_write_data(&s->sdbus, value & 0xff);
|
||||
break;
|
||||
case R_ENABLE:
|
||||
s->regs[addr] = value;
|
||||
|
@ -220,8 +222,9 @@ static void memcard_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
break;
|
||||
|
||||
default:
|
||||
error_report("milkymist_memcard: write access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
qemu_log_mask(LOG_UNIMP, "milkymist_memcard: "
|
||||
"write access to unknown register 0x%" HWADDR_PRIx " "
|
||||
"(value 0x%" PRIx64 ")\n", addr << 2, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -248,33 +251,41 @@ static void milkymist_memcard_reset(DeviceState *d)
|
|||
for (i = 0; i < R_MAX; i++) {
|
||||
s->regs[i] = 0;
|
||||
}
|
||||
/* Since we're still using the legacy SD API the card is not plugged
|
||||
* into any bus, and we must reset it manually.
|
||||
*/
|
||||
device_reset(DEVICE(s->card));
|
||||
}
|
||||
|
||||
static int milkymist_memcard_init(SysBusDevice *dev)
|
||||
static void milkymist_memcard_init(Object *obj)
|
||||
{
|
||||
MilkymistMemcardState *s = MILKYMIST_MEMCARD(dev);
|
||||
DriveInfo *dinfo;
|
||||
BlockBackend *blk;
|
||||
|
||||
/* FIXME use a qdev drive property instead of drive_get_next() */
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
|
||||
s->card = sd_init(blk, false);
|
||||
if (s->card == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->enabled = blk && blk_is_inserted(blk);
|
||||
MilkymistMemcardState *s = MILKYMIST_MEMCARD(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s,
|
||||
"milkymist-memcard", R_MAX * 4);
|
||||
sysbus_init_mmio(dev, &s->regs_region);
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void milkymist_memcard_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MilkymistMemcardState *s = MILKYMIST_MEMCARD(dev);
|
||||
DeviceState *carddev;
|
||||
BlockBackend *blk;
|
||||
DriveInfo *dinfo;
|
||||
Error *err = NULL;
|
||||
|
||||
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS,
|
||||
dev, "sd-bus");
|
||||
|
||||
/* Create and plug in the sd card */
|
||||
/* FIXME use a qdev drive property instead of drive_get_next() */
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
|
||||
carddev = qdev_create(&s->sdbus.qbus, TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &err);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized", &err);
|
||||
if (err) {
|
||||
error_setg(errp, "failed to init SD card: %s", error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
s->enabled = blk && blk_is_inserted(blk);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_milkymist_memcard = {
|
||||
|
@ -297,9 +308,8 @@ static const VMStateDescription vmstate_milkymist_memcard = {
|
|||
static void milkymist_memcard_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = milkymist_memcard_init;
|
||||
dc->realize = milkymist_memcard_realize;
|
||||
dc->reset = milkymist_memcard_reset;
|
||||
dc->vmsd = &vmstate_milkymist_memcard;
|
||||
/* Reason: init() method uses drive_get_next() */
|
||||
|
@ -310,6 +320,7 @@ static const TypeInfo milkymist_memcard_info = {
|
|||
.name = TYPE_MILKYMIST_MEMCARD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(MilkymistMemcardState),
|
||||
.instance_init = milkymist_memcard_init,
|
||||
.class_init = milkymist_memcard_class_init,
|
||||
};
|
||||
|
||||
|
|
467
hw/sd/sd.c
467
hw/sd/sd.c
|
@ -32,28 +32,21 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/sd/sd.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sdmmc-internal.h"
|
||||
#include "trace.h"
|
||||
|
||||
//#define DEBUG_SD 1
|
||||
|
||||
#ifdef DEBUG_SD
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define ACMD41_ENQUIRY_MASK 0x00ffffff
|
||||
#define OCR_POWER_UP 0x80000000
|
||||
#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */
|
||||
|
||||
typedef enum {
|
||||
sd_r0 = 0, /* no response */
|
||||
sd_r1, /* normal response command */
|
||||
|
@ -88,16 +81,21 @@ enum SDCardStates {
|
|||
struct SDState {
|
||||
DeviceState parent_obj;
|
||||
|
||||
uint32_t mode; /* current card mode, one of SDCardModes */
|
||||
int32_t state; /* current card state, one of SDCardStates */
|
||||
/* SD Memory Card Registers */
|
||||
uint32_t ocr;
|
||||
QEMUTimer *ocr_power_timer;
|
||||
uint8_t scr[8];
|
||||
uint8_t cid[16];
|
||||
uint8_t csd[16];
|
||||
uint16_t rca;
|
||||
uint32_t card_status;
|
||||
uint8_t sd_status[64];
|
||||
|
||||
/* Configurable properties */
|
||||
BlockBackend *blk;
|
||||
bool spi;
|
||||
|
||||
uint32_t mode; /* current card mode, one of SDCardModes */
|
||||
int32_t state; /* current card state, one of SDCardStates */
|
||||
uint32_t vhs;
|
||||
bool wp_switch;
|
||||
unsigned long *wp_groups;
|
||||
|
@ -110,8 +108,6 @@ struct SDState {
|
|||
uint8_t pwd[16];
|
||||
uint32_t pwd_len;
|
||||
uint8_t function_group[6];
|
||||
|
||||
bool spi;
|
||||
uint8_t current_cmd;
|
||||
/* True if we will handle the next command as an ACMD. Note that this does
|
||||
* *not* track the APP_CMD status bit!
|
||||
|
@ -123,13 +119,53 @@ struct SDState {
|
|||
uint8_t data[512];
|
||||
qemu_irq readonly_cb;
|
||||
qemu_irq inserted_cb;
|
||||
BlockBackend *blk;
|
||||
|
||||
QEMUTimer *ocr_power_timer;
|
||||
bool enable;
|
||||
uint8_t dat_lines;
|
||||
bool cmd_line;
|
||||
};
|
||||
|
||||
static const char *sd_state_name(enum SDCardStates state)
|
||||
{
|
||||
static const char *state_name[] = {
|
||||
[sd_idle_state] = "idle",
|
||||
[sd_ready_state] = "ready",
|
||||
[sd_identification_state] = "identification",
|
||||
[sd_standby_state] = "standby",
|
||||
[sd_transfer_state] = "transfer",
|
||||
[sd_sendingdata_state] = "sendingdata",
|
||||
[sd_receivingdata_state] = "receivingdata",
|
||||
[sd_programming_state] = "programming",
|
||||
[sd_disconnect_state] = "disconnect",
|
||||
};
|
||||
if (state == sd_inactive_state) {
|
||||
return "inactive";
|
||||
}
|
||||
assert(state <= ARRAY_SIZE(state_name));
|
||||
return state_name[state];
|
||||
}
|
||||
|
||||
static const char *sd_response_name(sd_rsp_type_t rsp)
|
||||
{
|
||||
static const char *response_name[] = {
|
||||
[sd_r0] = "RESP#0 (no response)",
|
||||
[sd_r1] = "RESP#1 (normal cmd)",
|
||||
[sd_r2_i] = "RESP#2 (CID reg)",
|
||||
[sd_r2_s] = "RESP#2 (CSD reg)",
|
||||
[sd_r3] = "RESP#3 (OCR reg)",
|
||||
[sd_r6] = "RESP#6 (RCA)",
|
||||
[sd_r7] = "RESP#7 (operating voltage)",
|
||||
};
|
||||
if (rsp == sd_illegal) {
|
||||
return "ILLEGAL RESP";
|
||||
}
|
||||
if (rsp == sd_r1b) {
|
||||
rsp = sd_r1;
|
||||
}
|
||||
assert(rsp <= ARRAY_SIZE(response_name));
|
||||
return response_name[rsp];
|
||||
}
|
||||
|
||||
static uint8_t sd_get_dat_lines(SDState *sd)
|
||||
{
|
||||
return sd->enable ? sd->dat_lines : 0;
|
||||
|
@ -142,6 +178,8 @@ static bool sd_get_cmd_line(SDState *sd)
|
|||
|
||||
static void sd_set_voltage(SDState *sd, uint16_t millivolts)
|
||||
{
|
||||
trace_sdcard_set_voltage(millivolts);
|
||||
|
||||
switch (millivolts) {
|
||||
case 3001 ... 3600: /* SD_VOLTAGE_3_3V */
|
||||
case 2001 ... 3000: /* SD_VOLTAGE_3_0V */
|
||||
|
@ -176,18 +214,21 @@ static void sd_set_mode(SDState *sd)
|
|||
}
|
||||
}
|
||||
|
||||
static const sd_cmd_type_t sd_cmd_type[64] = {
|
||||
static const sd_cmd_type_t sd_cmd_type[SDMMC_CMD_MAX] = {
|
||||
sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac,
|
||||
sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac,
|
||||
/* 16 */
|
||||
sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
|
||||
sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none,
|
||||
/* 32 */
|
||||
sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none,
|
||||
sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none,
|
||||
/* 48 */
|
||||
sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
|
||||
sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
|
||||
};
|
||||
|
||||
static const int sd_cmd_class[64] = {
|
||||
static const int sd_cmd_class[SDMMC_CMD_MAX] = {
|
||||
0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6,
|
||||
5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7,
|
||||
|
@ -227,27 +268,54 @@ static uint16_t sd_crc16(void *message, size_t width)
|
|||
return shift_reg;
|
||||
}
|
||||
|
||||
#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */
|
||||
|
||||
FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24)
|
||||
FIELD(OCR, VDD_VOLTAGE_WIN_LO, 0, 8)
|
||||
FIELD(OCR, DUAL_VOLTAGE_CARD, 7, 1)
|
||||
FIELD(OCR, VDD_VOLTAGE_WIN_HI, 8, 16)
|
||||
FIELD(OCR, ACCEPT_SWITCH_1V8, 24, 1) /* Only UHS-I */
|
||||
FIELD(OCR, UHS_II_CARD, 29, 1) /* Only UHS-II */
|
||||
FIELD(OCR, CARD_CAPACITY, 30, 1) /* 0:SDSC, 1:SDHC/SDXC */
|
||||
FIELD(OCR, CARD_POWER_UP, 31, 1)
|
||||
|
||||
#define ACMD41_ENQUIRY_MASK 0x00ffffff
|
||||
#define ACMD41_R3_MASK (R_OCR_VDD_VOLTAGE_WIN_HI_MASK \
|
||||
| R_OCR_ACCEPT_SWITCH_1V8_MASK \
|
||||
| R_OCR_UHS_II_CARD_MASK \
|
||||
| R_OCR_CARD_CAPACITY_MASK \
|
||||
| R_OCR_CARD_POWER_UP_MASK)
|
||||
|
||||
static void sd_set_ocr(SDState *sd)
|
||||
{
|
||||
/* All voltages OK, Standard Capacity SD Memory Card, not yet powered up */
|
||||
sd->ocr = 0x00ffff00;
|
||||
/* All voltages OK */
|
||||
sd->ocr = R_OCR_VDD_VOLTAGE_WIN_HI_MASK;
|
||||
}
|
||||
|
||||
static void sd_ocr_powerup(void *opaque)
|
||||
{
|
||||
SDState *sd = opaque;
|
||||
|
||||
/* Set powered up bit in OCR */
|
||||
assert(!(sd->ocr & OCR_POWER_UP));
|
||||
sd->ocr |= OCR_POWER_UP;
|
||||
trace_sdcard_powerup();
|
||||
assert(!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP));
|
||||
|
||||
/* card power-up OK */
|
||||
sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_POWER_UP, 1);
|
||||
|
||||
if (sd->size > 1 * G_BYTE) {
|
||||
sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_set_scr(SDState *sd)
|
||||
{
|
||||
sd->scr[0] = 0x00; /* SCR Structure */
|
||||
sd->scr[1] = 0x2f; /* SD Security Support */
|
||||
sd->scr[2] = 0x00;
|
||||
sd->scr[0] = (0 << 4) /* SCR version 1.0 */
|
||||
| 0; /* Spec Versions 1.0 and 1.01 */
|
||||
sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */
|
||||
| 0b0101; /* 1-bit or 4-bit width bus modes */
|
||||
sd->scr[2] = 0x00; /* Extended Security is not supported. */
|
||||
sd->scr[3] = 0x00;
|
||||
/* reserved for manufacturer usage */
|
||||
sd->scr[4] = 0x00;
|
||||
sd->scr[5] = 0x00;
|
||||
sd->scr[6] = 0x00;
|
||||
|
@ -299,11 +367,11 @@ static void sd_set_csd(SDState *sd, uint64_t size)
|
|||
uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
|
||||
uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
|
||||
|
||||
if (size <= 0x40000000) { /* Standard Capacity SD */
|
||||
if (size <= 1 * G_BYTE) { /* Standard Capacity SD */
|
||||
sd->csd[0] = 0x00; /* CSD structure */
|
||||
sd->csd[1] = 0x26; /* Data read access-time-1 */
|
||||
sd->csd[2] = 0x00; /* Data read access-time-2 */
|
||||
sd->csd[3] = 0x5a; /* Max. data transfer rate */
|
||||
sd->csd[3] = 0x32; /* Max. data transfer rate: 25 MHz */
|
||||
sd->csd[4] = 0x5f; /* Card Command Classes */
|
||||
sd->csd[5] = 0x50 | /* Max. read data block length */
|
||||
HWBLOCK_SHIFT;
|
||||
|
@ -324,7 +392,6 @@ static void sd_set_csd(SDState *sd, uint64_t size)
|
|||
sd->csd[13] = 0x20 | /* Max. write data block length */
|
||||
((HWBLOCK_SHIFT << 6) & 0xc0);
|
||||
sd->csd[14] = 0x00; /* File format group */
|
||||
sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
|
||||
} else { /* SDHC */
|
||||
size /= 512 * 1024;
|
||||
size -= 1;
|
||||
|
@ -343,9 +410,8 @@ static void sd_set_csd(SDState *sd, uint64_t size)
|
|||
sd->csd[12] = 0x0a;
|
||||
sd->csd[13] = 0x40;
|
||||
sd->csd[14] = 0x00;
|
||||
sd->csd[15] = 0x00;
|
||||
sd->ocr |= 1 << 30; /* High Capacity SD Memory Card */
|
||||
}
|
||||
sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
|
||||
}
|
||||
|
||||
static void sd_set_rca(SDState *sd)
|
||||
|
@ -353,14 +419,56 @@ static void sd_set_rca(SDState *sd)
|
|||
sd->rca += 0x4567;
|
||||
}
|
||||
|
||||
FIELD(CSR, AKE_SEQ_ERROR, 3, 1)
|
||||
FIELD(CSR, APP_CMD, 5, 1)
|
||||
FIELD(CSR, FX_EVENT, 6, 1)
|
||||
FIELD(CSR, READY_FOR_DATA, 8, 1)
|
||||
FIELD(CSR, CURRENT_STATE, 9, 4)
|
||||
FIELD(CSR, ERASE_RESET, 13, 1)
|
||||
FIELD(CSR, CARD_ECC_DISABLED, 14, 1)
|
||||
FIELD(CSR, WP_ERASE_SKIP, 15, 1)
|
||||
FIELD(CSR, CSD_OVERWRITE, 16, 1)
|
||||
FIELD(CSR, DEFERRED_RESPONSE, 17, 1)
|
||||
FIELD(CSR, ERROR, 19, 1)
|
||||
FIELD(CSR, CC_ERROR, 20, 1)
|
||||
FIELD(CSR, CARD_ECC_FAILED, 21, 1)
|
||||
FIELD(CSR, ILLEGAL_COMMAND, 22, 1)
|
||||
FIELD(CSR, COM_CRC_ERROR, 23, 1)
|
||||
FIELD(CSR, LOCK_UNLOCK_FAILED, 24, 1)
|
||||
FIELD(CSR, CARD_IS_LOCKED, 25, 1)
|
||||
FIELD(CSR, WP_VIOLATION, 26, 1)
|
||||
FIELD(CSR, ERASE_PARAM, 27, 1)
|
||||
FIELD(CSR, ERASE_SEQ_ERROR, 28, 1)
|
||||
FIELD(CSR, BLOCK_LEN_ERROR, 29, 1)
|
||||
FIELD(CSR, ADDRESS_ERROR, 30, 1)
|
||||
FIELD(CSR, OUT_OF_RANGE, 31, 1)
|
||||
|
||||
/* Card status bits, split by clear condition:
|
||||
* A : According to the card current state
|
||||
* B : Always related to the previous command
|
||||
* C : Cleared by read
|
||||
*/
|
||||
#define CARD_STATUS_A 0x02004100
|
||||
#define CARD_STATUS_B 0x00c01e00
|
||||
#define CARD_STATUS_C 0xfd39a028
|
||||
#define CARD_STATUS_A (R_CSR_READY_FOR_DATA_MASK \
|
||||
| R_CSR_CARD_ECC_DISABLED_MASK \
|
||||
| R_CSR_CARD_IS_LOCKED_MASK)
|
||||
#define CARD_STATUS_B (R_CSR_CURRENT_STATE_MASK \
|
||||
| R_CSR_ILLEGAL_COMMAND_MASK \
|
||||
| R_CSR_COM_CRC_ERROR_MASK)
|
||||
#define CARD_STATUS_C (R_CSR_AKE_SEQ_ERROR_MASK \
|
||||
| R_CSR_APP_CMD_MASK \
|
||||
| R_CSR_ERASE_RESET_MASK \
|
||||
| R_CSR_WP_ERASE_SKIP_MASK \
|
||||
| R_CSR_CSD_OVERWRITE_MASK \
|
||||
| R_CSR_ERROR_MASK \
|
||||
| R_CSR_CC_ERROR_MASK \
|
||||
| R_CSR_CARD_ECC_FAILED_MASK \
|
||||
| R_CSR_LOCK_UNLOCK_FAILED_MASK \
|
||||
| R_CSR_WP_VIOLATION_MASK \
|
||||
| R_CSR_ERASE_PARAM_MASK \
|
||||
| R_CSR_ERASE_SEQ_ERROR_MASK \
|
||||
| R_CSR_BLOCK_LEN_ERROR_MASK \
|
||||
| R_CSR_ADDRESS_ERROR_MASK \
|
||||
| R_CSR_OUT_OF_RANGE_MASK)
|
||||
|
||||
static void sd_set_cardstatus(SDState *sd)
|
||||
{
|
||||
|
@ -376,57 +484,39 @@ static int sd_req_crc_validate(SDRequest *req)
|
|||
{
|
||||
uint8_t buffer[5];
|
||||
buffer[0] = 0x40 | req->cmd;
|
||||
buffer[1] = (req->arg >> 24) & 0xff;
|
||||
buffer[2] = (req->arg >> 16) & 0xff;
|
||||
buffer[3] = (req->arg >> 8) & 0xff;
|
||||
buffer[4] = (req->arg >> 0) & 0xff;
|
||||
stl_be_p(&buffer[1], req->arg);
|
||||
return 0;
|
||||
return sd_crc7(buffer, 5) != req->crc; /* TODO */
|
||||
}
|
||||
|
||||
static void sd_response_r1_make(SDState *sd, uint8_t *response)
|
||||
{
|
||||
uint32_t status = sd->card_status;
|
||||
stl_be_p(response, sd->card_status);
|
||||
|
||||
/* Clear the "clear on read" status bits */
|
||||
sd->card_status &= ~CARD_STATUS_C;
|
||||
|
||||
response[0] = (status >> 24) & 0xff;
|
||||
response[1] = (status >> 16) & 0xff;
|
||||
response[2] = (status >> 8) & 0xff;
|
||||
response[3] = (status >> 0) & 0xff;
|
||||
}
|
||||
|
||||
static void sd_response_r3_make(SDState *sd, uint8_t *response)
|
||||
{
|
||||
response[0] = (sd->ocr >> 24) & 0xff;
|
||||
response[1] = (sd->ocr >> 16) & 0xff;
|
||||
response[2] = (sd->ocr >> 8) & 0xff;
|
||||
response[3] = (sd->ocr >> 0) & 0xff;
|
||||
stl_be_p(response, sd->ocr & ACMD41_R3_MASK);
|
||||
}
|
||||
|
||||
static void sd_response_r6_make(SDState *sd, uint8_t *response)
|
||||
{
|
||||
uint16_t arg;
|
||||
uint16_t status;
|
||||
|
||||
arg = sd->rca;
|
||||
status = ((sd->card_status >> 8) & 0xc000) |
|
||||
((sd->card_status >> 6) & 0x2000) |
|
||||
(sd->card_status & 0x1fff);
|
||||
sd->card_status &= ~(CARD_STATUS_C & 0xc81fff);
|
||||
|
||||
response[0] = (arg >> 8) & 0xff;
|
||||
response[1] = arg & 0xff;
|
||||
response[2] = (status >> 8) & 0xff;
|
||||
response[3] = status & 0xff;
|
||||
stw_be_p(response + 0, sd->rca);
|
||||
stw_be_p(response + 2, status);
|
||||
}
|
||||
|
||||
static void sd_response_r7_make(SDState *sd, uint8_t *response)
|
||||
{
|
||||
response[0] = (sd->vhs >> 24) & 0xff;
|
||||
response[1] = (sd->vhs >> 16) & 0xff;
|
||||
response[2] = (sd->vhs >> 8) & 0xff;
|
||||
response[3] = (sd->vhs >> 0) & 0xff;
|
||||
stl_be_p(response, sd->vhs);
|
||||
}
|
||||
|
||||
static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
|
||||
|
@ -440,6 +530,7 @@ static void sd_reset(DeviceState *dev)
|
|||
uint64_t size;
|
||||
uint64_t sect;
|
||||
|
||||
trace_sdcard_reset();
|
||||
if (sd->blk) {
|
||||
blk_get_geometry(sd->blk, §);
|
||||
} else {
|
||||
|
@ -493,7 +584,10 @@ static void sd_cardchange(void *opaque, bool load, Error **errp)
|
|||
bool readonly = sd_get_readonly(sd);
|
||||
|
||||
if (inserted) {
|
||||
trace_sdcard_inserted(readonly);
|
||||
sd_reset(dev);
|
||||
} else {
|
||||
trace_sdcard_ejected();
|
||||
}
|
||||
|
||||
/* The IRQ notification is for legacy non-QOM SD controller devices;
|
||||
|
@ -521,7 +615,7 @@ static bool sd_ocr_vmstate_needed(void *opaque)
|
|||
SDState *sd = opaque;
|
||||
|
||||
/* Include the OCR state (and timer) if it is not yet powered up */
|
||||
return !(sd->ocr & OCR_POWER_UP);
|
||||
return !FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP);
|
||||
}
|
||||
|
||||
static const VMStateDescription sd_ocr_vmstate = {
|
||||
|
@ -625,12 +719,13 @@ static void sd_erase(SDState *sd)
|
|||
uint64_t erase_start = sd->erase_start;
|
||||
uint64_t erase_end = sd->erase_end;
|
||||
|
||||
trace_sdcard_erase();
|
||||
if (!sd->erase_start || !sd->erase_end) {
|
||||
sd->card_status |= ERASE_SEQ_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (extract32(sd->ocr, OCR_CCS_BITN, 1)) {
|
||||
if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
|
||||
/* High capacity memory card: erase units are 512 byte blocks */
|
||||
erase_start *= 512;
|
||||
erase_end *= 512;
|
||||
|
@ -667,7 +762,7 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
|
|||
|
||||
static void sd_function_switch(SDState *sd, uint32_t arg)
|
||||
{
|
||||
int i, mode, new_func, crc;
|
||||
int i, mode, new_func;
|
||||
mode = !!(arg & 0x80000000);
|
||||
|
||||
sd->data[0] = 0x00; /* Maximum current consumption */
|
||||
|
@ -691,9 +786,7 @@ static void sd_function_switch(SDState *sd, uint32_t arg)
|
|||
sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
|
||||
}
|
||||
memset(&sd->data[17], 0, 47);
|
||||
crc = sd_crc16(sd->data, 64);
|
||||
sd->data[65] = crc >> 8;
|
||||
sd->data[66] = crc & 0xff;
|
||||
stw_be_p(sd->data + 65, sd_crc16(sd->data, 64));
|
||||
}
|
||||
|
||||
static inline bool sd_wp_addr(SDState *sd, uint64_t addr)
|
||||
|
@ -714,6 +807,11 @@ static void sd_lock_command(SDState *sd)
|
|||
else
|
||||
pwd_len = 0;
|
||||
|
||||
if (lock) {
|
||||
trace_sdcard_lock();
|
||||
} else {
|
||||
trace_sdcard_unlock();
|
||||
}
|
||||
if (erase) {
|
||||
if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 ||
|
||||
set_pwd || clr_pwd || lock || sd->wp_switch ||
|
||||
|
@ -774,11 +872,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
uint32_t rca = 0x0000;
|
||||
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
|
||||
|
||||
trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
|
||||
|
||||
/* Not interpreting this as an app command */
|
||||
sd->card_status &= ~APP_CMD;
|
||||
|
||||
if (sd_cmd_type[req.cmd & 0x3F] == sd_ac
|
||||
|| sd_cmd_type[req.cmd & 0x3F] == sd_adtc) {
|
||||
if (sd_cmd_type[req.cmd] == sd_ac
|
||||
|| sd_cmd_type[req.cmd] == sd_adtc) {
|
||||
rca = req.arg >> 16;
|
||||
}
|
||||
|
||||
|
@ -788,7 +888,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
sd->multi_blk_cnt = 0;
|
||||
}
|
||||
|
||||
DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state);
|
||||
switch (req.cmd) {
|
||||
/* Basic commands (Class 0 and Class 1) */
|
||||
case 0: /* CMD0: GO_IDLE_STATE */
|
||||
|
@ -909,23 +1008,19 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
|
||||
case 8: /* CMD8: SEND_IF_COND */
|
||||
/* Physical Layer Specification Version 2.00 command */
|
||||
switch (sd->state) {
|
||||
case sd_idle_state:
|
||||
sd->vhs = 0;
|
||||
|
||||
/* No response if not exactly one VHS bit is set. */
|
||||
if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
|
||||
return sd->spi ? sd_r7 : sd_r0;
|
||||
}
|
||||
|
||||
/* Accept. */
|
||||
sd->vhs = req.arg;
|
||||
return sd_r7;
|
||||
|
||||
default:
|
||||
if (sd->state != sd_idle_state) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
sd->vhs = 0;
|
||||
|
||||
/* No response if not exactly one VHS bit is set. */
|
||||
if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
|
||||
return sd->spi ? sd_r7 : sd_r0;
|
||||
}
|
||||
|
||||
/* Accept. */
|
||||
sd->vhs = req.arg;
|
||||
return sd_r7;
|
||||
|
||||
case 9: /* CMD9: SEND_CSD */
|
||||
switch (sd->state) {
|
||||
|
@ -971,24 +1066,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
}
|
||||
break;
|
||||
|
||||
case 11: /* CMD11: READ_DAT_UNTIL_STOP */
|
||||
if (sd->spi)
|
||||
goto bad_cmd;
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->state = sd_sendingdata_state;
|
||||
sd->data_start = req.arg;
|
||||
sd->data_offset = 0;
|
||||
|
||||
if (sd->data_start + sd->blk_len > sd->size)
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
return sd_r0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 12: /* CMD12: STOP_TRANSMISSION */
|
||||
switch (sd->state) {
|
||||
case sd_sendingdata_state:
|
||||
|
@ -1039,10 +1116,12 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
case 16: /* CMD16: SET_BLOCKLEN */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
if (req.arg > (1 << HWBLOCK_SHIFT))
|
||||
if (req.arg > (1 << HWBLOCK_SHIFT)) {
|
||||
sd->card_status |= BLOCK_LEN_ERROR;
|
||||
else
|
||||
} else {
|
||||
trace_sdcard_set_blocklen(req.arg);
|
||||
sd->blk_len = req.arg;
|
||||
}
|
||||
|
||||
return sd_r1;
|
||||
|
||||
|
@ -1096,8 +1175,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
|
||||
/* Block write commands (Class 4) */
|
||||
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
|
||||
if (sd->spi)
|
||||
goto unimplemented_cmd;
|
||||
if (sd->spi) {
|
||||
goto unimplemented_spi_cmd;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
/* Writing in SPI mode not implemented. */
|
||||
|
@ -1122,8 +1202,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
break;
|
||||
|
||||
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
|
||||
if (sd->spi)
|
||||
goto unimplemented_cmd;
|
||||
if (sd->spi) {
|
||||
goto unimplemented_spi_cmd;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
/* Writing in SPI mode not implemented. */
|
||||
|
@ -1163,8 +1244,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
break;
|
||||
|
||||
case 27: /* CMD27: PROGRAM_CSD */
|
||||
if (sd->spi)
|
||||
goto unimplemented_cmd;
|
||||
if (sd->spi) {
|
||||
goto unimplemented_spi_cmd;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->state = sd_receivingdata_state;
|
||||
|
@ -1274,8 +1356,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
|
||||
/* Lock card commands (Class 7) */
|
||||
case 42: /* CMD42: LOCK_UNLOCK */
|
||||
if (sd->spi)
|
||||
goto unimplemented_cmd;
|
||||
if (sd->spi) {
|
||||
goto unimplemented_spi_cmd;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->state = sd_receivingdata_state;
|
||||
|
@ -1288,9 +1371,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
}
|
||||
break;
|
||||
|
||||
case 52:
|
||||
case 53:
|
||||
/* CMD52, CMD53: reserved for SDIO cards
|
||||
case 52 ... 54:
|
||||
/* CMD52, CMD53, CMD54: reserved for SDIO cards
|
||||
* (see the SDIO Simplified Specification V2.0)
|
||||
* Handle as illegal command but do not complain
|
||||
* on stderr, as some OSes may use these in their
|
||||
|
@ -1300,16 +1382,29 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
|
||||
/* Application specific commands (Class 8) */
|
||||
case 55: /* CMD55: APP_CMD */
|
||||
if (sd->rca != rca)
|
||||
return sd_r0;
|
||||
|
||||
switch (sd->state) {
|
||||
case sd_ready_state:
|
||||
case sd_identification_state:
|
||||
case sd_inactive_state:
|
||||
return sd_illegal;
|
||||
case sd_idle_state:
|
||||
if (rca) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!sd->spi) {
|
||||
if (sd->rca != rca) {
|
||||
return sd_r0;
|
||||
}
|
||||
}
|
||||
sd->expecting_acmd = true;
|
||||
sd->card_status |= APP_CMD;
|
||||
return sd_r1;
|
||||
|
||||
case 56: /* CMD56: GEN_CMD */
|
||||
fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
|
||||
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->data_offset = 0;
|
||||
|
@ -1324,12 +1419,24 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
}
|
||||
break;
|
||||
|
||||
case 58: /* CMD58: READ_OCR (SPI) */
|
||||
if (!sd->spi) {
|
||||
goto bad_cmd;
|
||||
}
|
||||
return sd_r3;
|
||||
|
||||
case 59: /* CMD59: CRC_ON_OFF (SPI) */
|
||||
if (!sd->spi) {
|
||||
goto bad_cmd;
|
||||
}
|
||||
goto unimplemented_spi_cmd;
|
||||
|
||||
default:
|
||||
bad_cmd:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd);
|
||||
return sd_illegal;
|
||||
|
||||
unimplemented_cmd:
|
||||
unimplemented_spi_cmd:
|
||||
/* Commands that are recognised but not yet implemented in SPI mode. */
|
||||
qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n",
|
||||
req.cmd);
|
||||
|
@ -1343,10 +1450,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
|
|||
static sd_rsp_type_t sd_app_command(SDState *sd,
|
||||
SDRequest req)
|
||||
{
|
||||
DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
|
||||
trace_sdcard_app_command(req.cmd, req.arg);
|
||||
sd->card_status |= APP_CMD;
|
||||
switch (req.cmd) {
|
||||
case 6: /* ACMD6: SET_BUS_WIDTH */
|
||||
if (sd->spi) {
|
||||
goto unimplemented_spi_cmd;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->sd_status[0] &= 0x3f;
|
||||
|
@ -1402,42 +1512,41 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
|
|||
sd->state = sd_transfer_state;
|
||||
return sd_r1;
|
||||
}
|
||||
switch (sd->state) {
|
||||
case sd_idle_state:
|
||||
/* If it's the first ACMD41 since reset, we need to decide
|
||||
* whether to power up. If this is not an enquiry ACMD41,
|
||||
* we immediately report power on and proceed below to the
|
||||
* ready state, but if it is, we set a timer to model a
|
||||
* delay for power up. This works around a bug in EDK2
|
||||
* UEFI, which sends an initial enquiry ACMD41, but
|
||||
* assumes that the card is in ready state as soon as it
|
||||
* sees the power up bit set. */
|
||||
if (!(sd->ocr & OCR_POWER_UP)) {
|
||||
if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) {
|
||||
timer_del(sd->ocr_power_timer);
|
||||
sd_ocr_powerup(sd);
|
||||
} else if (!timer_pending(sd->ocr_power_timer)) {
|
||||
if (sd->state != sd_idle_state) {
|
||||
break;
|
||||
}
|
||||
/* If it's the first ACMD41 since reset, we need to decide
|
||||
* whether to power up. If this is not an enquiry ACMD41,
|
||||
* we immediately report power on and proceed below to the
|
||||
* ready state, but if it is, we set a timer to model a
|
||||
* delay for power up. This works around a bug in EDK2
|
||||
* UEFI, which sends an initial enquiry ACMD41, but
|
||||
* assumes that the card is in ready state as soon as it
|
||||
* sees the power up bit set. */
|
||||
if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) {
|
||||
if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) {
|
||||
timer_del(sd->ocr_power_timer);
|
||||
sd_ocr_powerup(sd);
|
||||
} else {
|
||||
trace_sdcard_inquiry_cmd41();
|
||||
if (!timer_pending(sd->ocr_power_timer)) {
|
||||
timer_mod_ns(sd->ocr_power_timer,
|
||||
(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
|
||||
+ OCR_POWER_DELAY_NS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) {
|
||||
/* We accept any voltage. 10000 V is nothing.
|
||||
*
|
||||
* Once we're powered up, we advance straight to ready state
|
||||
* unless it's an enquiry ACMD41 (bits 23:0 == 0).
|
||||
*/
|
||||
if (req.arg & ACMD41_ENQUIRY_MASK) {
|
||||
sd->state = sd_ready_state;
|
||||
}
|
||||
|
||||
return sd_r3;
|
||||
|
||||
default:
|
||||
break;
|
||||
sd->state = sd_ready_state;
|
||||
}
|
||||
break;
|
||||
|
||||
return sd_r3;
|
||||
|
||||
case 42: /* ACMD42: SET_CLR_CARD_DETECT */
|
||||
switch (sd->state) {
|
||||
|
@ -1463,9 +1572,27 @@ static sd_rsp_type_t sd_app_command(SDState *sd,
|
|||
}
|
||||
break;
|
||||
|
||||
case 18: /* Reserved for SD security applications */
|
||||
case 25:
|
||||
case 26:
|
||||
case 38:
|
||||
case 43 ... 49:
|
||||
/* Refer to the "SD Specifications Part3 Security Specification" for
|
||||
* information about the SD Security Features.
|
||||
*/
|
||||
qemu_log_mask(LOG_UNIMP, "SD: CMD%i Security not implemented\n",
|
||||
req.cmd);
|
||||
return sd_illegal;
|
||||
|
||||
default:
|
||||
/* Fall back to standard commands. */
|
||||
return sd_normal_command(sd, req);
|
||||
|
||||
unimplemented_spi_cmd:
|
||||
/* Commands that are recognised but not yet implemented in SPI mode. */
|
||||
qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n",
|
||||
req.cmd);
|
||||
return sd_illegal;
|
||||
}
|
||||
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd);
|
||||
|
@ -1488,8 +1615,8 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req)
|
|||
if (req->cmd == 16 || req->cmd == 55) {
|
||||
return 1;
|
||||
}
|
||||
return sd_cmd_class[req->cmd & 0x3F] == 0
|
||||
|| sd_cmd_class[req->cmd & 0x3F] == 7;
|
||||
return sd_cmd_class[req->cmd] == 0
|
||||
|| sd_cmd_class[req->cmd] == 7;
|
||||
}
|
||||
|
||||
int sd_do_command(SDState *sd, SDRequest *req,
|
||||
|
@ -1508,6 +1635,12 @@ int sd_do_command(SDState *sd, SDRequest *req,
|
|||
goto send_response;
|
||||
}
|
||||
|
||||
if (req->cmd >= SDMMC_CMD_MAX) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "SD: incorrect command 0x%02x\n",
|
||||
req->cmd);
|
||||
req->cmd &= 0x3f;
|
||||
}
|
||||
|
||||
if (sd->card_status & CARD_IS_LOCKED) {
|
||||
if (!cmd_valid_while_locked(sd, req)) {
|
||||
sd->card_status |= ILLEGAL_COMMAND;
|
||||
|
@ -1574,10 +1707,12 @@ send_response:
|
|||
|
||||
case sd_r0:
|
||||
case sd_illegal:
|
||||
default:
|
||||
rsplen = 0;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
trace_sdcard_response(sd_response_name(rtype), rsplen);
|
||||
|
||||
if (rtype != sd_illegal) {
|
||||
/* Clear the "clear on valid command" status bits now we've
|
||||
|
@ -1587,16 +1722,7 @@ send_response:
|
|||
}
|
||||
|
||||
#ifdef DEBUG_SD
|
||||
if (rsplen) {
|
||||
int i;
|
||||
DPRINTF("Response:");
|
||||
for (i = 0; i < rsplen; i++) {
|
||||
DPRINTF(" %02x", response[i]);
|
||||
}
|
||||
DPRINTF(" state %d\n", sd->state);
|
||||
} else {
|
||||
DPRINTF("No response %d\n", sd->state);
|
||||
}
|
||||
qemu_hexdump((const char *)response, stderr, "Response", rsplen);
|
||||
#endif
|
||||
|
||||
return rsplen;
|
||||
|
@ -1604,8 +1730,7 @@ send_response:
|
|||
|
||||
static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
|
||||
(unsigned long long) addr, len);
|
||||
trace_sdcard_read_block(addr, len);
|
||||
if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) {
|
||||
fprintf(stderr, "sd_blk_read: read error on host side\n");
|
||||
}
|
||||
|
@ -1613,6 +1738,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
|
|||
|
||||
static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
trace_sdcard_write_block(addr, len);
|
||||
if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) {
|
||||
fprintf(stderr, "sd_blk_write: write error on host side\n");
|
||||
}
|
||||
|
@ -1639,6 +1765,7 @@ void sd_write_data(SDState *sd, uint8_t value)
|
|||
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
|
||||
return;
|
||||
|
||||
trace_sdcard_write_data(sd->current_cmd, value);
|
||||
switch (sd->current_cmd) {
|
||||
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
|
||||
sd->data[sd->data_offset ++] = value;
|
||||
|
@ -1776,6 +1903,7 @@ uint8_t sd_read_data(SDState *sd)
|
|||
|
||||
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
|
||||
|
||||
trace_sdcard_read_data(sd->current_cmd, io_len);
|
||||
switch (sd->current_cmd) {
|
||||
case 6: /* CMD6: SWITCH_FUNCTION */
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
|
@ -1792,21 +1920,6 @@ uint8_t sd_read_data(SDState *sd)
|
|||
sd->state = sd_transfer_state;
|
||||
break;
|
||||
|
||||
case 11: /* CMD11: READ_DAT_UNTIL_STOP */
|
||||
if (sd->data_offset == 0)
|
||||
BLK_READ_BLOCK(sd->data_start, io_len);
|
||||
ret = sd->data[sd->data_offset ++];
|
||||
|
||||
if (sd->data_offset >= io_len) {
|
||||
sd->data_start += io_len;
|
||||
sd->data_offset = 0;
|
||||
if (sd->data_start + io_len > sd->size) {
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 13: /* ACMD13: SD_STATUS */
|
||||
ret = sd->sd_status[sd->data_offset ++];
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* SD/MMC cards common
|
||||
*
|
||||
* Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef SD_INTERNAL_H
|
||||
#define SD_INTERNAL_H
|
||||
|
||||
#define SDMMC_CMD_MAX 64
|
||||
|
||||
#endif
|
|
@ -47,7 +47,7 @@ typedef struct {
|
|||
int32_t arglen;
|
||||
int32_t response_pos;
|
||||
int32_t stopping;
|
||||
SDState *sd;
|
||||
SDBus sdbus;
|
||||
} ssi_sd_state;
|
||||
|
||||
#define TYPE_SSI_SD "ssi-sd"
|
||||
|
@ -100,7 +100,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
|
|||
request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16)
|
||||
| (s->cmdarg[2] << 8) | s->cmdarg[3];
|
||||
DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg);
|
||||
s->arglen = sd_do_command(s->sd, &request, longresp);
|
||||
s->arglen = sdbus_do_command(&s->sdbus, &request, longresp);
|
||||
if (s->arglen <= 0) {
|
||||
s->arglen = 1;
|
||||
s->response[0] = 4;
|
||||
|
@ -177,7 +177,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
|
|||
DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
|
||||
return s->response[s->response_pos++];
|
||||
}
|
||||
if (sd_data_ready(s->sd)) {
|
||||
if (sdbus_data_ready(&s->sdbus)) {
|
||||
DPRINTF("Data read\n");
|
||||
s->mode = SSI_SD_DATA_START;
|
||||
} else {
|
||||
|
@ -190,8 +190,8 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
|
|||
s->mode = SSI_SD_DATA_READ;
|
||||
return 0xfe;
|
||||
case SSI_SD_DATA_READ:
|
||||
val = sd_read_data(s->sd);
|
||||
if (!sd_data_ready(s->sd)) {
|
||||
val = sdbus_read_data(&s->sdbus);
|
||||
if (!sdbus_data_ready(&s->sdbus)) {
|
||||
DPRINTF("Data read end\n");
|
||||
s->mode = SSI_SD_CMD;
|
||||
}
|
||||
|
@ -242,13 +242,24 @@ static const VMStateDescription vmstate_ssi_sd = {
|
|||
static void ssi_sd_realize(SSISlave *d, Error **errp)
|
||||
{
|
||||
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d);
|
||||
DeviceState *carddev;
|
||||
DriveInfo *dinfo;
|
||||
Error *err = NULL;
|
||||
|
||||
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS,
|
||||
DEVICE(d), "sd-bus");
|
||||
|
||||
/* Create and plug in the sd card */
|
||||
/* FIXME use a qdev drive property instead of drive_get_next() */
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
|
||||
if (s->sd == NULL) {
|
||||
error_setg(errp, "Device initialization failed.");
|
||||
carddev = qdev_create(&s->sdbus.qbus, TYPE_SD_CARD);
|
||||
if (dinfo) {
|
||||
qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err);
|
||||
}
|
||||
object_property_set_bool(OBJECT(carddev), true, "spi", &err);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized", &err);
|
||||
if (err) {
|
||||
error_setg(errp, "failed to init SD card: %s", error_get_pretty(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -264,11 +275,6 @@ static void ssi_sd_reset(DeviceState *dev)
|
|||
s->arglen = 0;
|
||||
s->response_pos = 0;
|
||||
s->stopping = 0;
|
||||
|
||||
/* Since we're still using the legacy SD API the card is not plugged
|
||||
* into any bus, and we must reset it manually.
|
||||
*/
|
||||
device_reset(DEVICE(s->sd));
|
||||
}
|
||||
|
||||
static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
||||
|
|
|
@ -23,6 +23,26 @@ sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read fr
|
|||
sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data"
|
||||
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
|
||||
|
||||
# hw/sd/sd.c
|
||||
sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
|
||||
sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
|
||||
sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
|
||||
sdcard_powerup(void) ""
|
||||
sdcard_inquiry_cmd41(void) ""
|
||||
sdcard_set_enable(bool current_state, bool new_state) "%u -> %u"
|
||||
sdcard_reset(void) ""
|
||||
sdcard_set_blocklen(uint16_t length) "0x%04x"
|
||||
sdcard_inserted(bool readonly) "read_only: %u"
|
||||
sdcard_ejected(void) ""
|
||||
sdcard_erase(void) ""
|
||||
sdcard_lock(void) ""
|
||||
sdcard_unlock(void) ""
|
||||
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
|
||||
sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
|
||||
sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
|
||||
sdcard_set_voltage(uint16_t millivolts) "%u mV"
|
||||
|
||||
# hw/sd/milkymist-memcard.c
|
||||
milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
|
|
|
@ -37,7 +37,12 @@
|
|||
#define USART_CR3 0x14
|
||||
#define USART_GTPR 0x18
|
||||
|
||||
#define USART_SR_RESET 0x00C00000
|
||||
/*
|
||||
* NB: The reset value mentioned in "24.6.1 Status register" seems bogus.
|
||||
* Looking at "Table 98 USART register map and reset values", it seems it
|
||||
* should be 0xc0, and that's how real hardware behaves.
|
||||
*/
|
||||
#define USART_SR_RESET (USART_SR_TXE | USART_SR_TC)
|
||||
|
||||
#define USART_SR_TXE (1 << 7)
|
||||
#define USART_SR_TC (1 << 6)
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#define READY_FOR_DATA (1 << 8)
|
||||
#define APP_CMD (1 << 5)
|
||||
#define AKE_SEQ_ERROR (1 << 3)
|
||||
#define OCR_CCS_BITN 30
|
||||
|
||||
typedef enum {
|
||||
SD_VOLTAGE_0_4V = 400, /* currently not supported */
|
||||
|
|
|
@ -5069,8 +5069,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
{ .name = "VPIDR", .state = ARM_CP_STATE_AA32,
|
||||
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .accessfn = access_el3_aa32ns,
|
||||
.resetvalue = cpu->midr,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) },
|
||||
.resetvalue = cpu->midr, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetoflow32(CPUARMState, cp15.vpidr_el2) },
|
||||
{ .name = "VPIDR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
|
||||
.access = PL2_RW, .resetvalue = cpu->midr,
|
||||
|
@ -5078,8 +5078,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
{ .name = "VMPIDR", .state = ARM_CP_STATE_AA32,
|
||||
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
|
||||
.access = PL2_RW, .accessfn = access_el3_aa32ns,
|
||||
.resetvalue = vmpidr_def,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) },
|
||||
.resetvalue = vmpidr_def, .type = ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetoflow32(CPUARMState, cp15.vmpidr_el2) },
|
||||
{ .name = "VMPIDR_EL2", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
|
||||
.access = PL2_RW,
|
||||
|
|
Loading…
Reference in New Issue