mirror of https://gitee.com/openkylin/qemu.git
target-arm queue:
* SDHCI: cleanups and minor bug fixes * target/arm: minor refactor preparatory to fp16 support * omap_ssd, ssi-sd, pl181, milkymist-memcard: reset the SD card on controller reset (fixes migration failures) * target/arm: Handle page table walk load failures correctly * hw/arm/virt: Add virt-2.12 machine type * get_phys_addr_pmsav7: Support AP=0b111 for v7M * hw/intc/armv7m: Support byte and halfword accesses to CFSR -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJaXf8rAAoJEDwlJe0UNgzeoi4QALHpLEXjOmIrIxjcbEoN2T6B tGdR+ZyNfdKI2lgleG6c5bmdzPaFooDoqOyCIML77AvqVlRjbDGw6KIfNmLfXQ2g 16bh0D11Qyl5Yv/YBN3Lv7nDPou4/sop6TzP0MSxvDucqksEVvQARuY147bxHkmy uz5jhdSkWdePFJRtmIJtm9G1PpOhS6IjCVKR/upWDJkpBqUdIxtPNx8fEgrcK7mb MdSU9LVl0p4J4zYjzFCUF8qMIrtbiqlAAVEt894BeZJk+tDYbq245S3oj1psc3XP sGRriXNxQnsczkrkyEAq2BHjswyLLd12jG+vtWkj0A0tIlB4jHgB26juZJbqk8Ui GzJoJQ7kCA4qsQxm4goONUkUJdmBtgCCQekWc0a87ELhdzd9pus8bDe9ivFmxCOW 1+i/ZX773GfLyDp5b1HwN5TU6u8J2bg+C4604hGIJ/h8r4Wu+iqH5/LfEum304rS Oqx5XQhU3a1dw+4w897CLbBscfVjNsPmgrCOXq4SYGZZ3NjTwyBMOedtB5HF279i mI5NZqVBiLoKC+oDjDe7k7oW6FdeEKfuDsOsxeLpIIe/66YwSpgk1bAsbvipcbRG 4sUIqMEk6/2TQmVY9lnYg5msQK3QGFtPDSzrFnz3T+DswaCto/SFa89yeocAOXqv qEcQXDkndUFrEuo6S4/+ =G4xE -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180116' into staging target-arm queue: * SDHCI: cleanups and minor bug fixes * target/arm: minor refactor preparatory to fp16 support * omap_ssd, ssi-sd, pl181, milkymist-memcard: reset the SD card on controller reset (fixes migration failures) * target/arm: Handle page table walk load failures correctly * hw/arm/virt: Add virt-2.12 machine type * get_phys_addr_pmsav7: Support AP=0b111 for v7M * hw/intc/armv7m: Support byte and halfword accesses to CFSR # gpg: Signature made Tue 16 Jan 2018 13:33:31 GMT # gpg: using RSA key 0x3C2525ED14360CDE # 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-20180116: (24 commits) sdhci: add a 'dma' property to the sysbus devices sdhci: fix the PCI device, using the PCI address space for DMA sdhci: Implement write method of ACMD12ERRSTS register sdhci: fix CAPAB/MAXCURR registers, both are 64bit and read-only sdhci: rename the SDHC_CAPAB register sdhci: move MASK_TRNMOD with other SDHC_TRN* defines in "sd-internal.h" sdhci: convert the DPRINT() calls into trace events sdhci: use qemu_log_mask(UNIMP) instead of fprintf() sdhci: refactor common sysbus/pci unrealize() into sdhci_common_unrealize() sdhci: refactor common sysbus/pci realize() into sdhci_common_realize() sdhci: refactor common sysbus/pci class_init() into sdhci_common_class_init() sdhci: use DEFINE_SDHCI_COMMON_PROPERTIES() for common sysbus/pci properties sdhci: remove dead code sdhci: clean up includes target/arm: Add fp16 support to vfp_expand_imm target/arm: Split out vfp_expand_imm hw/sd/omap_mmc: Reset SD card on controller reset hw/sd/ssi-sd: Reset SD card on controller reset hw/sd/milkymist-memcard: Reset SD card on controller reset hw/sd/pl181: Reset SD card on controller reset ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
aae39d24a3
|
@ -1618,7 +1618,7 @@ static void machvirt_machine_init(void)
|
||||||
}
|
}
|
||||||
type_init(machvirt_machine_init);
|
type_init(machvirt_machine_init);
|
||||||
|
|
||||||
static void virt_2_11_instance_init(Object *obj)
|
static void virt_2_12_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
VirtMachineState *vms = VIRT_MACHINE(obj);
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||||
|
@ -1678,10 +1678,25 @@ static void virt_2_11_instance_init(Object *obj)
|
||||||
vms->irqmap = a15irqmap;
|
vms->irqmap = a15irqmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virt_machine_2_11_options(MachineClass *mc)
|
static void virt_machine_2_12_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
DEFINE_VIRT_MACHINE_AS_LATEST(2, 11)
|
DEFINE_VIRT_MACHINE_AS_LATEST(2, 12)
|
||||||
|
|
||||||
|
#define VIRT_COMPAT_2_11 \
|
||||||
|
HW_COMPAT_2_11
|
||||||
|
|
||||||
|
static void virt_2_11_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
virt_2_12_instance_init(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virt_machine_2_11_options(MachineClass *mc)
|
||||||
|
{
|
||||||
|
virt_machine_2_12_options(mc);
|
||||||
|
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_11);
|
||||||
|
}
|
||||||
|
DEFINE_VIRT_MACHINE(2, 11)
|
||||||
|
|
||||||
#define VIRT_COMPAT_2_10 \
|
#define VIRT_COMPAT_2_10 \
|
||||||
HW_COMPAT_2_10
|
HW_COMPAT_2_10
|
||||||
|
|
|
@ -896,13 +896,6 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
|
||||||
val |= (1 << 8);
|
val |= (1 << 8);
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
case 0xd28: /* Configurable Fault Status. */
|
|
||||||
/* The BFSR bits [15:8] are shared between security states
|
|
||||||
* and we store them in the NS copy
|
|
||||||
*/
|
|
||||||
val = cpu->env.v7m.cfsr[attrs.secure];
|
|
||||||
val |= cpu->env.v7m.cfsr[M_REG_NS] & R_V7M_CFSR_BFSR_MASK;
|
|
||||||
return val;
|
|
||||||
case 0xd2c: /* Hard Fault Status. */
|
case 0xd2c: /* Hard Fault Status. */
|
||||||
return cpu->env.v7m.hfsr;
|
return cpu->env.v7m.hfsr;
|
||||||
case 0xd30: /* Debug Fault Status. */
|
case 0xd30: /* Debug Fault Status. */
|
||||||
|
@ -1280,15 +1273,6 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
|
||||||
s->vectors[ARMV7M_EXCP_DEBUG].active = (value & (1 << 8)) != 0;
|
s->vectors[ARMV7M_EXCP_DEBUG].active = (value & (1 << 8)) != 0;
|
||||||
nvic_irq_update(s);
|
nvic_irq_update(s);
|
||||||
break;
|
break;
|
||||||
case 0xd28: /* Configurable Fault Status. */
|
|
||||||
cpu->env.v7m.cfsr[attrs.secure] &= ~value; /* W1C */
|
|
||||||
if (attrs.secure) {
|
|
||||||
/* The BFSR bits [15:8] are shared between security states
|
|
||||||
* and we store them in the NS copy.
|
|
||||||
*/
|
|
||||||
cpu->env.v7m.cfsr[M_REG_NS] &= ~(value & R_V7M_CFSR_BFSR_MASK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0xd2c: /* Hard Fault Status. */
|
case 0xd2c: /* Hard Fault Status. */
|
||||||
cpu->env.v7m.hfsr &= ~value; /* W1C */
|
cpu->env.v7m.hfsr &= ~value; /* W1C */
|
||||||
break;
|
break;
|
||||||
|
@ -1667,6 +1651,14 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
|
||||||
val = deposit32(val, i * 8, 8, get_prio(s, hdlidx, sbank));
|
val = deposit32(val, i * 8, 8, get_prio(s, hdlidx, sbank));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0xd28 ... 0xd2b: /* Configurable Fault Status (CFSR) */
|
||||||
|
/* The BFSR bits [15:8] are shared between security states
|
||||||
|
* and we store them in the NS copy
|
||||||
|
*/
|
||||||
|
val = s->cpu->env.v7m.cfsr[attrs.secure];
|
||||||
|
val |= s->cpu->env.v7m.cfsr[M_REG_NS] & R_V7M_CFSR_BFSR_MASK;
|
||||||
|
val = extract32(val, (offset - 0xd28) * 8, size * 8);
|
||||||
|
break;
|
||||||
case 0xfe0 ... 0xfff: /* ID. */
|
case 0xfe0 ... 0xfff: /* ID. */
|
||||||
if (offset & 3) {
|
if (offset & 3) {
|
||||||
val = 0;
|
val = 0;
|
||||||
|
@ -1765,6 +1757,20 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
||||||
}
|
}
|
||||||
nvic_irq_update(s);
|
nvic_irq_update(s);
|
||||||
return MEMTX_OK;
|
return MEMTX_OK;
|
||||||
|
case 0xd28 ... 0xd2b: /* Configurable Fault Status (CFSR) */
|
||||||
|
/* All bits are W1C, so construct 32 bit value with 0s in
|
||||||
|
* the parts not written by the access size
|
||||||
|
*/
|
||||||
|
value <<= ((offset - 0xd28) * 8);
|
||||||
|
|
||||||
|
s->cpu->env.v7m.cfsr[attrs.secure] &= ~value;
|
||||||
|
if (attrs.secure) {
|
||||||
|
/* The BFSR bits [15:8] are shared between security states
|
||||||
|
* and we store them in the NS copy.
|
||||||
|
*/
|
||||||
|
s->cpu->env.v7m.cfsr[M_REG_NS] &= ~(value & R_V7M_CFSR_BFSR_MASK);
|
||||||
|
}
|
||||||
|
return MEMTX_OK;
|
||||||
}
|
}
|
||||||
if (size == 4) {
|
if (size == 4) {
|
||||||
nvic_writel(s, offset, value, attrs);
|
nvic_writel(s, offset, value, attrs);
|
||||||
|
|
|
@ -248,6 +248,10 @@ static void milkymist_memcard_reset(DeviceState *d)
|
||||||
for (i = 0; i < R_MAX; i++) {
|
for (i = 0; i < R_MAX; i++) {
|
||||||
s->regs[i] = 0;
|
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 int milkymist_memcard_init(SysBusDevice *dev)
|
||||||
|
|
|
@ -305,6 +305,12 @@ void omap_mmc_reset(struct omap_mmc_s *host)
|
||||||
host->cdet_enable = 0;
|
host->cdet_enable = 0;
|
||||||
qemu_set_irq(host->coverswitch, host->cdet_state);
|
qemu_set_irq(host->coverswitch, host->cdet_state);
|
||||||
host->clkdiv = 0;
|
host->clkdiv = 0;
|
||||||
|
|
||||||
|
/* Since we're still using the legacy SD API the card is not plugged
|
||||||
|
* into any bus, and we must reset it manually. When omap_mmc is
|
||||||
|
* QOMified this must move into the QOM reset function.
|
||||||
|
*/
|
||||||
|
device_reset(DEVICE(host->card));
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t omap_mmc_read(void *opaque, hwaddr offset,
|
static uint64_t omap_mmc_read(void *opaque, hwaddr offset,
|
||||||
|
@ -587,8 +593,6 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
|
||||||
s->lines = 1; /* TODO: needs to be settable per-board */
|
s->lines = 1; /* TODO: needs to be settable per-board */
|
||||||
s->rev = 1;
|
s->rev = 1;
|
||||||
|
|
||||||
omap_mmc_reset(s);
|
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800);
|
memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800);
|
||||||
memory_region_add_subregion(sysmem, base, &s->iomem);
|
memory_region_add_subregion(sysmem, base, &s->iomem);
|
||||||
|
|
||||||
|
@ -598,6 +602,8 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
omap_mmc_reset(s);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,8 +619,6 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
|
||||||
s->lines = 4;
|
s->lines = 4;
|
||||||
s->rev = 2;
|
s->rev = 2;
|
||||||
|
|
||||||
omap_mmc_reset(s);
|
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc",
|
memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc",
|
||||||
omap_l4_region_size(ta, 0));
|
omap_l4_region_size(ta, 0));
|
||||||
omap_l4_attach(ta, 0, &s->iomem);
|
omap_l4_attach(ta, 0, &s->iomem);
|
||||||
|
@ -628,6 +632,8 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
|
||||||
s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0);
|
s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0);
|
||||||
sd_set_cb(s->card, NULL, s->cdet);
|
sd_set_cb(s->card, NULL, s->cdet);
|
||||||
|
|
||||||
|
omap_mmc_reset(s);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -480,6 +480,10 @@ static void pl181_reset(DeviceState *d)
|
||||||
|
|
||||||
/* We can assume our GPIO outputs have been wired up now */
|
/* We can assume our GPIO outputs have been wired up now */
|
||||||
sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
|
sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
|
||||||
|
/* 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 void pl181_init(Object *obj)
|
static void pl181_init(Object *obj)
|
||||||
|
|
|
@ -24,8 +24,6 @@
|
||||||
#ifndef SDHCI_INTERNAL_H
|
#ifndef SDHCI_INTERNAL_H
|
||||||
#define SDHCI_INTERNAL_H
|
#define SDHCI_INTERNAL_H
|
||||||
|
|
||||||
#include "hw/sd/sdhci.h"
|
|
||||||
|
|
||||||
/* R/W SDMA System Address register 0x0 */
|
/* R/W SDMA System Address register 0x0 */
|
||||||
#define SDHC_SYSAD 0x00
|
#define SDHC_SYSAD 0x00
|
||||||
|
|
||||||
|
@ -45,6 +43,7 @@
|
||||||
#define SDHC_TRNS_ACMD12 0x0004
|
#define SDHC_TRNS_ACMD12 0x0004
|
||||||
#define SDHC_TRNS_READ 0x0010
|
#define SDHC_TRNS_READ 0x0010
|
||||||
#define SDHC_TRNS_MULTI 0x0020
|
#define SDHC_TRNS_MULTI 0x0020
|
||||||
|
#define SDHC_TRNMOD_MASK 0x0037
|
||||||
|
|
||||||
/* R/W Command Register 0x0 */
|
/* R/W Command Register 0x0 */
|
||||||
#define SDHC_CMDREG 0x0E
|
#define SDHC_CMDREG 0x0E
|
||||||
|
@ -175,7 +174,7 @@
|
||||||
#define SDHC_ACMD12ERRSTS 0x3C
|
#define SDHC_ACMD12ERRSTS 0x3C
|
||||||
|
|
||||||
/* HWInit Capabilities Register 0x05E80080 */
|
/* HWInit Capabilities Register 0x05E80080 */
|
||||||
#define SDHC_CAPAREG 0x40
|
#define SDHC_CAPAB 0x40
|
||||||
#define SDHC_CAN_DO_DMA 0x00400000
|
#define SDHC_CAN_DO_DMA 0x00400000
|
||||||
#define SDHC_CAN_DO_ADMA2 0x00080000
|
#define SDHC_CAN_DO_ADMA2 0x00080000
|
||||||
#define SDHC_CAN_DO_ADMA1 0x00100000
|
#define SDHC_CAN_DO_ADMA1 0x00100000
|
||||||
|
@ -227,6 +226,4 @@ enum {
|
||||||
sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */
|
sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const VMStateDescription sdhci_vmstate;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
266
hw/sd/sdhci.c
266
hw/sd/sdhci.c
|
@ -23,38 +23,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
#include "hw/sd/sdhci.h"
|
||||||
#include "sdhci-internal.h"
|
#include "sdhci-internal.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
|
#include "trace.h"
|
||||||
/* host controller debug messages */
|
|
||||||
#ifndef SDHC_DEBUG
|
|
||||||
#define SDHC_DEBUG 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DPRINT_L1(fmt, args...) \
|
|
||||||
do { \
|
|
||||||
if (SDHC_DEBUG) { \
|
|
||||||
fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#define DPRINT_L2(fmt, args...) \
|
|
||||||
do { \
|
|
||||||
if (SDHC_DEBUG > 1) { \
|
|
||||||
fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#define ERRPRINT(fmt, args...) \
|
|
||||||
do { \
|
|
||||||
if (SDHC_DEBUG) { \
|
|
||||||
fprintf(stderr, "QEMU SDHC ERROR: " fmt, ## args); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TYPE_SDHCI_BUS "sdhci-bus"
|
#define TYPE_SDHCI_BUS "sdhci-bus"
|
||||||
#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
|
#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
|
||||||
|
@ -119,7 +99,6 @@
|
||||||
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
|
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
|
||||||
(SDHC_CAPAB_TOCLKFREQ))
|
(SDHC_CAPAB_TOCLKFREQ))
|
||||||
|
|
||||||
#define MASK_TRNMOD 0x0037
|
|
||||||
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
||||||
|
|
||||||
static uint8_t sdhci_slotint(SDHCIState *s)
|
static uint8_t sdhci_slotint(SDHCIState *s)
|
||||||
|
@ -153,8 +132,8 @@ static void sdhci_raise_insertion_irq(void *opaque)
|
||||||
static void sdhci_set_inserted(DeviceState *dev, bool level)
|
static void sdhci_set_inserted(DeviceState *dev, bool level)
|
||||||
{
|
{
|
||||||
SDHCIState *s = (SDHCIState *)dev;
|
SDHCIState *s = (SDHCIState *)dev;
|
||||||
DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject");
|
|
||||||
|
|
||||||
|
trace_sdhci_set_inserted(level ? "insert" : "eject");
|
||||||
if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
|
if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
|
||||||
/* Give target some time to notice card ejection */
|
/* Give target some time to notice card ejection */
|
||||||
timer_mod(s->insert_timer,
|
timer_mod(s->insert_timer,
|
||||||
|
@ -236,7 +215,8 @@ static void sdhci_send_command(SDHCIState *s)
|
||||||
s->acmd12errsts = 0;
|
s->acmd12errsts = 0;
|
||||||
request.cmd = s->cmdreg >> 8;
|
request.cmd = s->cmdreg >> 8;
|
||||||
request.arg = s->argument;
|
request.arg = s->argument;
|
||||||
DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg);
|
|
||||||
|
trace_sdhci_send_command(request.cmd, request.arg);
|
||||||
rlen = sdbus_do_command(&s->sdbus, &request, response);
|
rlen = sdbus_do_command(&s->sdbus, &request, response);
|
||||||
|
|
||||||
if (s->cmdreg & SDHC_CMD_RESPONSE) {
|
if (s->cmdreg & SDHC_CMD_RESPONSE) {
|
||||||
|
@ -244,7 +224,7 @@ static void sdhci_send_command(SDHCIState *s)
|
||||||
s->rspreg[0] = (response[0] << 24) | (response[1] << 16) |
|
s->rspreg[0] = (response[0] << 24) | (response[1] << 16) |
|
||||||
(response[2] << 8) | response[3];
|
(response[2] << 8) | response[3];
|
||||||
s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0;
|
s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0;
|
||||||
DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]);
|
trace_sdhci_response4(s->rspreg[0]);
|
||||||
} else if (rlen == 16) {
|
} else if (rlen == 16) {
|
||||||
s->rspreg[0] = (response[11] << 24) | (response[12] << 16) |
|
s->rspreg[0] = (response[11] << 24) | (response[12] << 16) |
|
||||||
(response[13] << 8) | response[14];
|
(response[13] << 8) | response[14];
|
||||||
|
@ -254,11 +234,10 @@ static void sdhci_send_command(SDHCIState *s)
|
||||||
(response[5] << 8) | response[6];
|
(response[5] << 8) | response[6];
|
||||||
s->rspreg[3] = (response[0] << 16) | (response[1] << 8) |
|
s->rspreg[3] = (response[0] << 16) | (response[1] << 8) |
|
||||||
response[2];
|
response[2];
|
||||||
DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.."
|
trace_sdhci_response16(s->rspreg[3], s->rspreg[2],
|
||||||
"64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n",
|
s->rspreg[1], s->rspreg[0]);
|
||||||
s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]);
|
|
||||||
} else {
|
} else {
|
||||||
ERRPRINT("Timeout waiting for command response\n");
|
trace_sdhci_error("timeout waiting for command response");
|
||||||
if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
|
if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
|
||||||
s->errintsts |= SDHC_EIS_CMDTIMEOUT;
|
s->errintsts |= SDHC_EIS_CMDTIMEOUT;
|
||||||
s->norintsts |= SDHC_NIS_ERR;
|
s->norintsts |= SDHC_NIS_ERR;
|
||||||
|
@ -292,7 +271,7 @@ static void sdhci_end_transfer(SDHCIState *s)
|
||||||
|
|
||||||
request.cmd = 0x0C;
|
request.cmd = 0x0C;
|
||||||
request.arg = 0;
|
request.arg = 0;
|
||||||
DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg);
|
trace_sdhci_end_transfer(request.cmd, request.arg);
|
||||||
sdbus_do_command(&s->sdbus, &request, response);
|
sdbus_do_command(&s->sdbus, &request, response);
|
||||||
/* Auto CMD12 response goes to the upper Response register */
|
/* Auto CMD12 response goes to the upper Response register */
|
||||||
s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
|
s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
|
||||||
|
@ -361,7 +340,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
|
||||||
|
|
||||||
/* first check that a valid data exists in host controller input buffer */
|
/* first check that a valid data exists in host controller input buffer */
|
||||||
if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) {
|
if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) {
|
||||||
ERRPRINT("Trying to read from empty buffer\n");
|
trace_sdhci_error("read from empty buffer");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,8 +349,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
|
||||||
s->data_count++;
|
s->data_count++;
|
||||||
/* check if we've read all valid data (blksize bytes) from buffer */
|
/* check if we've read all valid data (blksize bytes) from buffer */
|
||||||
if ((s->data_count) >= (s->blksize & 0x0fff)) {
|
if ((s->data_count) >= (s->blksize & 0x0fff)) {
|
||||||
DPRINT_L2("All %u bytes of data have been read from input buffer\n",
|
trace_sdhci_read_dataport(s->data_count);
|
||||||
s->data_count);
|
|
||||||
s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
|
s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
|
||||||
s->data_count = 0; /* next buff read must start at position [0] */
|
s->data_count = 0; /* next buff read must start at position [0] */
|
||||||
|
|
||||||
|
@ -454,7 +432,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
|
||||||
|
|
||||||
/* Check that there is free space left in a buffer */
|
/* Check that there is free space left in a buffer */
|
||||||
if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) {
|
if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) {
|
||||||
ERRPRINT("Can't write to data buffer: buffer full\n");
|
trace_sdhci_error("Can't write to data buffer: buffer full");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,8 +441,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
|
||||||
s->data_count++;
|
s->data_count++;
|
||||||
value >>= 8;
|
value >>= 8;
|
||||||
if (s->data_count >= (s->blksize & 0x0fff)) {
|
if (s->data_count >= (s->blksize & 0x0fff)) {
|
||||||
DPRINT_L2("write buffer filled with %u bytes of data\n",
|
trace_sdhci_write_dataport(s->data_count);
|
||||||
s->data_count);
|
|
||||||
s->data_count = 0;
|
s->data_count = 0;
|
||||||
s->prnsts &= ~SDHC_SPACE_AVAILABLE;
|
s->prnsts &= ~SDHC_SPACE_AVAILABLE;
|
||||||
if (s->prnsts & SDHC_DOING_WRITE) {
|
if (s->prnsts & SDHC_DOING_WRITE) {
|
||||||
|
@ -519,7 +496,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||||
s->blkcnt--;
|
s->blkcnt--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dma_memory_write(&address_space_memory, s->sdmasysad,
|
dma_memory_write(s->dma_as, s->sdmasysad,
|
||||||
&s->fifo_buffer[begin], s->data_count - begin);
|
&s->fifo_buffer[begin], s->data_count - begin);
|
||||||
s->sdmasysad += s->data_count - begin;
|
s->sdmasysad += s->data_count - begin;
|
||||||
if (s->data_count == block_size) {
|
if (s->data_count == block_size) {
|
||||||
|
@ -541,7 +518,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||||
s->data_count = block_size;
|
s->data_count = block_size;
|
||||||
boundary_count -= block_size - begin;
|
boundary_count -= block_size - begin;
|
||||||
}
|
}
|
||||||
dma_memory_read(&address_space_memory, s->sdmasysad,
|
dma_memory_read(s->dma_as, s->sdmasysad,
|
||||||
&s->fifo_buffer[begin], s->data_count - begin);
|
&s->fifo_buffer[begin], s->data_count - begin);
|
||||||
s->sdmasysad += s->data_count - begin;
|
s->sdmasysad += s->data_count - begin;
|
||||||
if (s->data_count == block_size) {
|
if (s->data_count == block_size) {
|
||||||
|
@ -579,11 +556,9 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
|
||||||
for (n = 0; n < datacnt; n++) {
|
for (n = 0; n < datacnt; n++) {
|
||||||
s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
|
s->fifo_buffer[n] = sdbus_read_data(&s->sdbus);
|
||||||
}
|
}
|
||||||
dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer,
|
dma_memory_write(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
|
||||||
datacnt);
|
|
||||||
} else {
|
} else {
|
||||||
dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer,
|
dma_memory_read(s->dma_as, s->sdmasysad, s->fifo_buffer, datacnt);
|
||||||
datacnt);
|
|
||||||
for (n = 0; n < datacnt; n++) {
|
for (n = 0; n < datacnt; n++) {
|
||||||
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
|
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
|
||||||
}
|
}
|
||||||
|
@ -607,7 +582,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||||
hwaddr entry_addr = (hwaddr)s->admasysaddr;
|
hwaddr entry_addr = (hwaddr)s->admasysaddr;
|
||||||
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
||||||
case SDHC_CTRL_ADMA2_32:
|
case SDHC_CTRL_ADMA2_32:
|
||||||
dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma2,
|
dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2,
|
||||||
sizeof(adma2));
|
sizeof(adma2));
|
||||||
adma2 = le64_to_cpu(adma2);
|
adma2 = le64_to_cpu(adma2);
|
||||||
/* The spec does not specify endianness of descriptor table.
|
/* The spec does not specify endianness of descriptor table.
|
||||||
|
@ -619,7 +594,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||||
dscr->incr = 8;
|
dscr->incr = 8;
|
||||||
break;
|
break;
|
||||||
case SDHC_CTRL_ADMA1_32:
|
case SDHC_CTRL_ADMA1_32:
|
||||||
dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma1,
|
dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma1,
|
||||||
sizeof(adma1));
|
sizeof(adma1));
|
||||||
adma1 = le32_to_cpu(adma1);
|
adma1 = le32_to_cpu(adma1);
|
||||||
dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
|
dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
|
||||||
|
@ -632,12 +607,12 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDHC_CTRL_ADMA2_64:
|
case SDHC_CTRL_ADMA2_64:
|
||||||
dma_memory_read(&address_space_memory, entry_addr,
|
dma_memory_read(s->dma_as, entry_addr,
|
||||||
(uint8_t *)(&dscr->attr), 1);
|
(uint8_t *)(&dscr->attr), 1);
|
||||||
dma_memory_read(&address_space_memory, entry_addr + 2,
|
dma_memory_read(s->dma_as, entry_addr + 2,
|
||||||
(uint8_t *)(&dscr->length), 2);
|
(uint8_t *)(&dscr->length), 2);
|
||||||
dscr->length = le16_to_cpu(dscr->length);
|
dscr->length = le16_to_cpu(dscr->length);
|
||||||
dma_memory_read(&address_space_memory, entry_addr + 4,
|
dma_memory_read(s->dma_as, entry_addr + 4,
|
||||||
(uint8_t *)(&dscr->addr), 8);
|
(uint8_t *)(&dscr->addr), 8);
|
||||||
dscr->attr = le64_to_cpu(dscr->attr);
|
dscr->attr = le64_to_cpu(dscr->attr);
|
||||||
dscr->attr &= 0xfffffff8;
|
dscr->attr &= 0xfffffff8;
|
||||||
|
@ -652,15 +627,14 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
{
|
{
|
||||||
unsigned int n, begin, length;
|
unsigned int n, begin, length;
|
||||||
const uint16_t block_size = s->blksize & 0x0fff;
|
const uint16_t block_size = s->blksize & 0x0fff;
|
||||||
ADMADescr dscr;
|
ADMADescr dscr = {};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) {
|
for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) {
|
||||||
s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH;
|
s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH;
|
||||||
|
|
||||||
get_adma_description(s, &dscr);
|
get_adma_description(s, &dscr);
|
||||||
DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n",
|
trace_sdhci_adma_loop(dscr.addr, dscr.length, dscr.attr);
|
||||||
dscr.addr, dscr.length, dscr.attr);
|
|
||||||
|
|
||||||
if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) {
|
if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) {
|
||||||
/* Indicate that error occurred in ST_FDS state */
|
/* Indicate that error occurred in ST_FDS state */
|
||||||
|
@ -697,7 +671,7 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
s->data_count = block_size;
|
s->data_count = block_size;
|
||||||
length -= block_size - begin;
|
length -= block_size - begin;
|
||||||
}
|
}
|
||||||
dma_memory_write(&address_space_memory, dscr.addr,
|
dma_memory_write(s->dma_as, dscr.addr,
|
||||||
&s->fifo_buffer[begin],
|
&s->fifo_buffer[begin],
|
||||||
s->data_count - begin);
|
s->data_count - begin);
|
||||||
dscr.addr += s->data_count - begin;
|
dscr.addr += s->data_count - begin;
|
||||||
|
@ -721,7 +695,7 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
s->data_count = block_size;
|
s->data_count = block_size;
|
||||||
length -= block_size - begin;
|
length -= block_size - begin;
|
||||||
}
|
}
|
||||||
dma_memory_read(&address_space_memory, dscr.addr,
|
dma_memory_read(s->dma_as, dscr.addr,
|
||||||
&s->fifo_buffer[begin],
|
&s->fifo_buffer[begin],
|
||||||
s->data_count - begin);
|
s->data_count - begin);
|
||||||
dscr.addr += s->data_count - begin;
|
dscr.addr += s->data_count - begin;
|
||||||
|
@ -743,8 +717,7 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
break;
|
break;
|
||||||
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
|
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
|
||||||
s->admasysaddr = dscr.addr;
|
s->admasysaddr = dscr.addr;
|
||||||
DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n",
|
trace_sdhci_adma("link", s->admasysaddr);
|
||||||
s->admasysaddr);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s->admasysaddr += dscr.incr;
|
s->admasysaddr += dscr.incr;
|
||||||
|
@ -752,8 +725,7 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dscr.attr & SDHC_ADMA_ATTR_INT) {
|
if (dscr.attr & SDHC_ADMA_ATTR_INT) {
|
||||||
DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n",
|
trace_sdhci_adma("interrupt", s->admasysaddr);
|
||||||
s->admasysaddr);
|
|
||||||
if (s->norintstsen & SDHC_NISEN_DMA) {
|
if (s->norintstsen & SDHC_NISEN_DMA) {
|
||||||
s->norintsts |= SDHC_NIS_DMA;
|
s->norintsts |= SDHC_NIS_DMA;
|
||||||
}
|
}
|
||||||
|
@ -764,15 +736,15 @@ static void sdhci_do_adma(SDHCIState *s)
|
||||||
/* ADMA transfer terminates if blkcnt == 0 or by END attribute */
|
/* ADMA transfer terminates if blkcnt == 0 or by END attribute */
|
||||||
if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
|
if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
|
||||||
(s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) {
|
(s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) {
|
||||||
DPRINT_L2("ADMA transfer completed\n");
|
trace_sdhci_adma_transfer_completed();
|
||||||
if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) &&
|
if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) &&
|
||||||
(s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
|
(s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
|
||||||
s->blkcnt != 0)) {
|
s->blkcnt != 0)) {
|
||||||
ERRPRINT("SD/MMC host ADMA length mismatch\n");
|
trace_sdhci_error("SD/MMC host ADMA length mismatch");
|
||||||
s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
|
s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
|
||||||
SDHC_ADMAERR_STATE_ST_TFR;
|
SDHC_ADMAERR_STATE_ST_TFR;
|
||||||
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
|
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
|
||||||
ERRPRINT("Set ADMA error flag\n");
|
trace_sdhci_error("Set ADMA error flag");
|
||||||
s->errintsts |= SDHC_EIS_ADMAERR;
|
s->errintsts |= SDHC_EIS_ADMAERR;
|
||||||
s->norintsts |= SDHC_NIS_ERR;
|
s->norintsts |= SDHC_NIS_ERR;
|
||||||
}
|
}
|
||||||
|
@ -808,7 +780,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||||
break;
|
break;
|
||||||
case SDHC_CTRL_ADMA1_32:
|
case SDHC_CTRL_ADMA1_32:
|
||||||
if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
|
if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
|
||||||
ERRPRINT("ADMA1 not supported\n");
|
trace_sdhci_error("ADMA1 not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,7 +788,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||||
break;
|
break;
|
||||||
case SDHC_CTRL_ADMA2_32:
|
case SDHC_CTRL_ADMA2_32:
|
||||||
if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
|
if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
|
||||||
ERRPRINT("ADMA2 not supported\n");
|
trace_sdhci_error("ADMA2 not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,14 +797,14 @@ static void sdhci_data_transfer(void *opaque)
|
||||||
case SDHC_CTRL_ADMA2_64:
|
case SDHC_CTRL_ADMA2_64:
|
||||||
if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
|
if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
|
||||||
!(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
|
!(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
|
||||||
ERRPRINT("64 bit ADMA not supported\n");
|
trace_sdhci_error("64 bit ADMA not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sdhci_do_adma(s);
|
sdhci_do_adma(s);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERRPRINT("Unsupported DMA type\n");
|
trace_sdhci_error("Unsupported DMA type");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -867,8 +839,8 @@ static inline bool
|
||||||
sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
|
sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
|
||||||
{
|
{
|
||||||
if ((s->data_count & 0x3) != byte_num) {
|
if ((s->data_count & 0x3) != byte_num) {
|
||||||
ERRPRINT("Non-sequential access to Buffer Data Port register"
|
trace_sdhci_error("Non-sequential access to Buffer Data Port register"
|
||||||
"is prohibited\n");
|
"is prohibited\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -898,8 +870,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
case SDHC_BDATA:
|
case SDHC_BDATA:
|
||||||
if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
|
if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
|
||||||
ret = sdhci_read_dataport(s, size);
|
ret = sdhci_read_dataport(s, size);
|
||||||
DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset,
|
trace_sdhci_access("rd", size << 3, offset, "->", ret, ret);
|
||||||
ret, ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -925,11 +896,17 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
case SDHC_ACMD12ERRSTS:
|
case SDHC_ACMD12ERRSTS:
|
||||||
ret = s->acmd12errsts;
|
ret = s->acmd12errsts;
|
||||||
break;
|
break;
|
||||||
case SDHC_CAPAREG:
|
case SDHC_CAPAB:
|
||||||
ret = s->capareg;
|
ret = (uint32_t)s->capareg;
|
||||||
|
break;
|
||||||
|
case SDHC_CAPAB + 4:
|
||||||
|
ret = (uint32_t)(s->capareg >> 32);
|
||||||
break;
|
break;
|
||||||
case SDHC_MAXCURR:
|
case SDHC_MAXCURR:
|
||||||
ret = s->maxcurr;
|
ret = (uint32_t)s->maxcurr;
|
||||||
|
break;
|
||||||
|
case SDHC_MAXCURR + 4:
|
||||||
|
ret = (uint32_t)(s->maxcurr >> 32);
|
||||||
break;
|
break;
|
||||||
case SDHC_ADMAERR:
|
case SDHC_ADMAERR:
|
||||||
ret = s->admaerr;
|
ret = s->admaerr;
|
||||||
|
@ -944,13 +921,14 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
|
ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERRPRINT("bad %ub read: addr[0x%04x]\n", size, (int)offset);
|
qemu_log_mask(LOG_UNIMP, "SDHC rd_%ub @0x%02" HWADDR_PRIx " "
|
||||||
|
"not implemented\n", size, offset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret >>= (offset & 0x3) * 8;
|
ret >>= (offset & 0x3) * 8;
|
||||||
ret &= (1ULL << (size * 8)) - 1;
|
ret &= (1ULL << (size * 8)) - 1;
|
||||||
DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, ret, ret);
|
trace_sdhci_access("rd", size << 3, offset, "->", ret, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,7 +1029,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||||
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
|
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
|
||||||
value &= ~SDHC_TRNS_DMA;
|
value &= ~SDHC_TRNS_DMA;
|
||||||
}
|
}
|
||||||
MASKED_WRITE(s->trnmod, mask, value & MASK_TRNMOD);
|
MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK);
|
||||||
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
|
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
|
||||||
|
|
||||||
/* Writing to the upper byte of CMDREG triggers SD command generation */
|
/* Writing to the upper byte of CMDREG triggers SD command generation */
|
||||||
|
@ -1149,13 +1127,25 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||||
}
|
}
|
||||||
sdhci_update_irq(s);
|
sdhci_update_irq(s);
|
||||||
break;
|
break;
|
||||||
|
case SDHC_ACMD12ERRSTS:
|
||||||
|
MASKED_WRITE(s->acmd12errsts, mask, value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDHC_CAPAB:
|
||||||
|
case SDHC_CAPAB + 4:
|
||||||
|
case SDHC_MAXCURR:
|
||||||
|
case SDHC_MAXCURR + 4:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "SDHC wr_%ub @0x%02" HWADDR_PRIx
|
||||||
|
" <- 0x%08x read-only\n", size, offset, value >> shift);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n",
|
qemu_log_mask(LOG_UNIMP, "SDHC wr_%ub @0x%02" HWADDR_PRIx " <- 0x%08x "
|
||||||
size, (int)offset, value >> shift, value >> shift);
|
"not implemented\n", size, offset, value >> shift);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n",
|
trace_sdhci_access("wr", size << 3, offset, "<-",
|
||||||
size, (int)offset, value >> shift, value >> shift);
|
value >> shift, value >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps sdhci_mmio_ops = {
|
static const MemoryRegionOps sdhci_mmio_ops = {
|
||||||
|
@ -1184,6 +1174,14 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- qdev common --- */
|
||||||
|
|
||||||
|
#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
|
||||||
|
/* Capabilities registers provide information on supported features
|
||||||
|
* of this specific host controller implementation */ \
|
||||||
|
DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
|
||||||
|
DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
|
||||||
|
|
||||||
static void sdhci_initfn(SDHCIState *s)
|
static void sdhci_initfn(SDHCIState *s)
|
||||||
{
|
{
|
||||||
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
|
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
|
||||||
|
@ -1199,13 +1197,31 @@ static void sdhci_uninitfn(SDHCIState *s)
|
||||||
timer_free(s->insert_timer);
|
timer_free(s->insert_timer);
|
||||||
timer_del(s->transfer_timer);
|
timer_del(s->transfer_timer);
|
||||||
timer_free(s->transfer_timer);
|
timer_free(s->transfer_timer);
|
||||||
qemu_free_irq(s->eject_cb);
|
|
||||||
qemu_free_irq(s->ro_cb);
|
|
||||||
|
|
||||||
g_free(s->fifo_buffer);
|
g_free(s->fifo_buffer);
|
||||||
s->fifo_buffer = NULL;
|
s->fifo_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_common_realize(SDHCIState *s, Error **errp)
|
||||||
|
{
|
||||||
|
s->buf_maxsz = sdhci_get_fifolen(s);
|
||||||
|
s->fifo_buffer = g_malloc0(s->buf_maxsz);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
|
||||||
|
SDHC_REGISTERS_MAP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_common_unrealize(SDHCIState *s, Error **errp)
|
||||||
|
{
|
||||||
|
/* This function is expected to be called only once for each class:
|
||||||
|
* - SysBus: via DeviceClass->unrealize(),
|
||||||
|
* - PCI: via PCIDeviceClass->exit().
|
||||||
|
* However to avoid double-free and/or use-after-free we still nullify
|
||||||
|
* this variable (better safe than sorry!). */
|
||||||
|
g_free(s->fifo_buffer);
|
||||||
|
s->fifo_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static bool sdhci_pending_insert_vmstate_needed(void *opaque)
|
static bool sdhci_pending_insert_vmstate_needed(void *opaque)
|
||||||
{
|
{
|
||||||
SDHCIState *s = opaque;
|
SDHCIState *s = opaque;
|
||||||
|
@ -1265,32 +1281,44 @@ const VMStateDescription sdhci_vmstate = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Capabilities registers provide information on supported features of this
|
static void sdhci_common_class_init(ObjectClass *klass, void *data)
|
||||||
* specific host controller implementation */
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
|
dc->vmsd = &sdhci_vmstate;
|
||||||
|
dc->reset = sdhci_poweron_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- qdev PCI --- */
|
||||||
|
|
||||||
static Property sdhci_pci_properties[] = {
|
static Property sdhci_pci_properties[] = {
|
||||||
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
|
DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
|
||||||
SDHC_CAPAB_REG_DEFAULT),
|
|
||||||
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
|
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
|
||||||
{
|
{
|
||||||
SDHCIState *s = PCI_SDHCI(dev);
|
SDHCIState *s = PCI_SDHCI(dev);
|
||||||
|
|
||||||
|
sdhci_initfn(s);
|
||||||
|
sdhci_common_realize(s, errp);
|
||||||
|
if (errp && *errp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
|
dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
|
||||||
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
|
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
|
||||||
sdhci_initfn(s);
|
|
||||||
s->buf_maxsz = sdhci_get_fifolen(s);
|
|
||||||
s->fifo_buffer = g_malloc0(s->buf_maxsz);
|
|
||||||
s->irq = pci_allocate_irq(dev);
|
s->irq = pci_allocate_irq(dev);
|
||||||
memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
|
s->dma_as = pci_get_address_space(dev);
|
||||||
SDHC_REGISTERS_MAP_SIZE);
|
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem);
|
||||||
pci_register_bar(dev, 0, 0, &s->iomem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_pci_exit(PCIDevice *dev)
|
static void sdhci_pci_exit(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
SDHCIState *s = PCI_SDHCI(dev);
|
SDHCIState *s = PCI_SDHCI(dev);
|
||||||
|
|
||||||
|
sdhci_common_unrealize(s, &error_abort);
|
||||||
sdhci_uninitfn(s);
|
sdhci_uninitfn(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,10 +1332,9 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data)
|
||||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||||
k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI;
|
k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI;
|
||||||
k->class_id = PCI_CLASS_SYSTEM_SDHCI;
|
k->class_id = PCI_CLASS_SYSTEM_SDHCI;
|
||||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
|
||||||
dc->vmsd = &sdhci_vmstate;
|
|
||||||
dc->props = sdhci_pci_properties;
|
dc->props = sdhci_pci_properties;
|
||||||
dc->reset = sdhci_poweron_reset;
|
|
||||||
|
sdhci_common_class_init(klass, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo sdhci_pci_info = {
|
static const TypeInfo sdhci_pci_info = {
|
||||||
|
@ -1321,12 +1348,14 @@ static const TypeInfo sdhci_pci_info = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --- qdev SysBus --- */
|
||||||
|
|
||||||
static Property sdhci_sysbus_properties[] = {
|
static Property sdhci_sysbus_properties[] = {
|
||||||
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
|
DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState),
|
||||||
SDHC_CAPAB_REG_DEFAULT),
|
|
||||||
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
|
|
||||||
DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk,
|
DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk,
|
||||||
false),
|
false),
|
||||||
|
DEFINE_PROP_LINK("dma", SDHCIState,
|
||||||
|
dma_mr, TYPE_MEMORY_REGION, MemoryRegion *),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1340,6 +1369,11 @@ static void sdhci_sysbus_init(Object *obj)
|
||||||
static void sdhci_sysbus_finalize(Object *obj)
|
static void sdhci_sysbus_finalize(Object *obj)
|
||||||
{
|
{
|
||||||
SDHCIState *s = SYSBUS_SDHCI(obj);
|
SDHCIState *s = SYSBUS_SDHCI(obj);
|
||||||
|
|
||||||
|
if (s->dma_mr) {
|
||||||
|
object_unparent(OBJECT(s->dma_mr));
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_uninitfn(s);
|
sdhci_uninitfn(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1348,22 +1382,42 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
|
||||||
SDHCIState *s = SYSBUS_SDHCI(dev);
|
SDHCIState *s = SYSBUS_SDHCI(dev);
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
|
|
||||||
s->buf_maxsz = sdhci_get_fifolen(s);
|
sdhci_common_realize(s, errp);
|
||||||
s->fifo_buffer = g_malloc0(s->buf_maxsz);
|
if (errp && *errp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->dma_mr) {
|
||||||
|
address_space_init(s->dma_as, s->dma_mr, "sdhci-dma");
|
||||||
|
} else {
|
||||||
|
/* use system_memory() if property "dma" not set */
|
||||||
|
s->dma_as = &address_space_memory;
|
||||||
|
}
|
||||||
|
|
||||||
sysbus_init_irq(sbd, &s->irq);
|
sysbus_init_irq(sbd, &s->irq);
|
||||||
memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
|
|
||||||
SDHC_REGISTERS_MAP_SIZE);
|
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_sysbus_unrealize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
SDHCIState *s = SYSBUS_SDHCI(dev);
|
||||||
|
|
||||||
|
sdhci_common_unrealize(s, &error_abort);
|
||||||
|
|
||||||
|
if (s->dma_mr) {
|
||||||
|
address_space_destroy(s->dma_as);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
|
static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
dc->vmsd = &sdhci_vmstate;
|
|
||||||
dc->props = sdhci_sysbus_properties;
|
dc->props = sdhci_sysbus_properties;
|
||||||
dc->realize = sdhci_sysbus_realize;
|
dc->realize = sdhci_sysbus_realize;
|
||||||
dc->reset = sdhci_poweron_reset;
|
dc->unrealize = sdhci_sysbus_unrealize;
|
||||||
|
|
||||||
|
sdhci_common_class_init(klass, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo sdhci_sysbus_info = {
|
static const TypeInfo sdhci_sysbus_info = {
|
||||||
|
@ -1375,6 +1429,8 @@ static const TypeInfo sdhci_sysbus_info = {
|
||||||
.class_init = sdhci_sysbus_class_init,
|
.class_init = sdhci_sysbus_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --- qdev bus master --- */
|
||||||
|
|
||||||
static void sdhci_bus_class_init(ObjectClass *klass, void *data)
|
static void sdhci_bus_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
SDBusClass *sbc = SD_BUS_CLASS(klass);
|
SDBusClass *sbc = SD_BUS_CLASS(klass);
|
||||||
|
|
|
@ -50,6 +50,9 @@ typedef struct {
|
||||||
SDState *sd;
|
SDState *sd;
|
||||||
} ssi_sd_state;
|
} ssi_sd_state;
|
||||||
|
|
||||||
|
#define TYPE_SSI_SD "ssi-sd"
|
||||||
|
#define SSI_SD(obj) OBJECT_CHECK(ssi_sd_state, (obj), TYPE_SSI_SD)
|
||||||
|
|
||||||
/* State word bits. */
|
/* State word bits. */
|
||||||
#define SSI_SDR_LOCKED 0x0001
|
#define SSI_SDR_LOCKED 0x0001
|
||||||
#define SSI_SDR_WP_ERASE 0x0002
|
#define SSI_SDR_WP_ERASE 0x0002
|
||||||
|
@ -241,7 +244,6 @@ static void ssi_sd_realize(SSISlave *d, Error **errp)
|
||||||
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d);
|
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d);
|
||||||
DriveInfo *dinfo;
|
DriveInfo *dinfo;
|
||||||
|
|
||||||
s->mode = SSI_SD_CMD;
|
|
||||||
/* FIXME use a qdev drive property instead of drive_get_next() */
|
/* FIXME use a qdev drive property instead of drive_get_next() */
|
||||||
dinfo = drive_get_next(IF_SD);
|
dinfo = drive_get_next(IF_SD);
|
||||||
s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
|
s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
|
||||||
|
@ -251,6 +253,24 @@ static void ssi_sd_realize(SSISlave *d, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ssi_sd_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
ssi_sd_state *s = SSI_SD(dev);
|
||||||
|
|
||||||
|
s->mode = SSI_SD_CMD;
|
||||||
|
s->cmd = 0;
|
||||||
|
memset(s->cmdarg, 0, sizeof(s->cmdarg));
|
||||||
|
memset(s->response, 0, sizeof(s->response));
|
||||||
|
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)
|
static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
@ -260,10 +280,11 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
||||||
k->transfer = ssi_sd_transfer;
|
k->transfer = ssi_sd_transfer;
|
||||||
k->cs_polarity = SSI_CS_LOW;
|
k->cs_polarity = SSI_CS_LOW;
|
||||||
dc->vmsd = &vmstate_ssi_sd;
|
dc->vmsd = &vmstate_ssi_sd;
|
||||||
|
dc->reset = ssi_sd_reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo ssi_sd_info = {
|
static const TypeInfo ssi_sd_info = {
|
||||||
.name = "ssi-sd",
|
.name = TYPE_SSI_SD,
|
||||||
.parent = TYPE_SSI_SLAVE,
|
.parent = TYPE_SSI_SLAVE,
|
||||||
.instance_size = sizeof(ssi_sd_state),
|
.instance_size = sizeof(ssi_sd_state),
|
||||||
.class_init = ssi_sd_class_init,
|
.class_init = ssi_sd_class_init,
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
# See docs/devel/tracing.txt for syntax documentation.
|
# See docs/devel/tracing.txt for syntax documentation.
|
||||||
|
|
||||||
|
# hw/sd/sdhci.c
|
||||||
|
sdhci_set_inserted(const char *level) "card state changed: %s"
|
||||||
|
sdhci_send_command(uint8_t cmd, uint32_t arg) "CMD%02u ARG[0x%08x]"
|
||||||
|
sdhci_error(const char *msg) "%s"
|
||||||
|
sdhci_response4(uint32_t r0) "RSPREG[31..0]=0x%08x"
|
||||||
|
sdhci_response16(uint32_t r3, uint32_t r2, uint32_t r1, uint32_t r0) "RSPREG[127..96]=0x%08x, RSPREG[95..64]=0x%08x, RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x"
|
||||||
|
sdhci_end_transfer(uint8_t cmd, uint32_t arg) "Automatically issue CMD%02u 0x%08x"
|
||||||
|
sdhci_adma(const char *desc, uint32_t sysad) "%s: admasysaddr=0x%" PRIx32
|
||||||
|
sdhci_adma_loop(uint64_t addr, uint16_t length, uint8_t attr) "addr=0x%08" PRIx64 ", len=%d, attr=0x%x"
|
||||||
|
sdhci_adma_transfer_completed(void) ""
|
||||||
|
sdhci_access(const char *access, unsigned int size, uint64_t offset, const char *dir, uint64_t val, uint64_t val2) "%s%u: addr[0x%04" PRIx64 "] %s 0x%08" PRIx64 " (%" PRIu64 ")"
|
||||||
|
sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read from input buffer"
|
||||||
|
sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data"
|
||||||
|
|
||||||
# hw/sd/milkymist-memcard.c
|
# hw/sd/milkymist-memcard.c
|
||||||
milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
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"
|
milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||||
|
|
|
@ -26,26 +26,29 @@
|
||||||
#define SDHCI_H
|
#define SDHCI_H
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "hw/block/block.h"
|
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/sd/sd.h"
|
#include "hw/sd/sd.h"
|
||||||
|
|
||||||
/* SD/MMC host controller state */
|
/* SD/MMC host controller state */
|
||||||
typedef struct SDHCIState {
|
typedef struct SDHCIState {
|
||||||
|
/*< private >*/
|
||||||
union {
|
union {
|
||||||
PCIDevice pcidev;
|
PCIDevice pcidev;
|
||||||
SysBusDevice busdev;
|
SysBusDevice busdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
SDBus sdbus;
|
SDBus sdbus;
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
|
AddressSpace *dma_as;
|
||||||
|
MemoryRegion *dma_mr;
|
||||||
|
|
||||||
QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
|
QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
|
||||||
QEMUTimer *transfer_timer;
|
QEMUTimer *transfer_timer;
|
||||||
qemu_irq eject_cb;
|
|
||||||
qemu_irq ro_cb;
|
|
||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
|
|
||||||
|
/* Registers cleared on reset */
|
||||||
uint32_t sdmasysad; /* SDMA System Address register */
|
uint32_t sdmasysad; /* SDMA System Address register */
|
||||||
uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */
|
uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */
|
||||||
uint16_t blkcnt; /* Blocks count for current transfer */
|
uint16_t blkcnt; /* Blocks count for current transfer */
|
||||||
|
@ -70,19 +73,23 @@ typedef struct SDHCIState {
|
||||||
uint16_t acmd12errsts; /* Auto CMD12 error status register */
|
uint16_t acmd12errsts; /* Auto CMD12 error status register */
|
||||||
uint64_t admasysaddr; /* ADMA System Address Register */
|
uint64_t admasysaddr; /* ADMA System Address Register */
|
||||||
|
|
||||||
uint32_t capareg; /* Capabilities Register */
|
/* Read-only registers */
|
||||||
uint32_t maxcurr; /* Maximum Current Capabilities Register */
|
uint64_t capareg; /* Capabilities Register */
|
||||||
|
uint64_t maxcurr; /* Maximum Current Capabilities Register */
|
||||||
|
|
||||||
uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
|
uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
|
||||||
uint32_t buf_maxsz;
|
uint32_t buf_maxsz;
|
||||||
uint16_t data_count; /* current element in FIFO buffer */
|
uint16_t data_count; /* current element in FIFO buffer */
|
||||||
uint8_t stopped_state;/* Current SDHC state */
|
uint8_t stopped_state;/* Current SDHC state */
|
||||||
bool pending_insert_quirk;/* Quirk for Raspberry Pi card insert int */
|
|
||||||
bool pending_insert_state;
|
bool pending_insert_state;
|
||||||
/* Buffer Data Port Register - virtual access point to R and W buffers */
|
/* Buffer Data Port Register - virtual access point to R and W buffers */
|
||||||
/* Software Reset Register - always reads as 0 */
|
/* Software Reset Register - always reads as 0 */
|
||||||
/* Force Event Auto CMD12 Error Interrupt Reg - write only */
|
/* Force Event Auto CMD12 Error Interrupt Reg - write only */
|
||||||
/* Force Event Error Interrupt Register- write only */
|
/* Force Event Error Interrupt Register- write only */
|
||||||
/* RO Host Controller Version Register always reads as 0x2401 */
|
/* RO Host Controller Version Register always reads as 0x2401 */
|
||||||
|
|
||||||
|
/* Configurable properties */
|
||||||
|
bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */
|
||||||
} SDHCIState;
|
} SDHCIState;
|
||||||
|
|
||||||
#define TYPE_PCI_SDHCI "sdhci-pci"
|
#define TYPE_PCI_SDHCI "sdhci-pci"
|
||||||
|
|
|
@ -8305,6 +8305,7 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||||
ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa,
|
ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa,
|
||||||
&txattrs, &s2prot, &s2size, fi, NULL);
|
&txattrs, &s2prot, &s2size, fi, NULL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
assert(fi->type != ARMFault_None);
|
||||||
fi->s2addr = addr;
|
fi->s2addr = addr;
|
||||||
fi->stage2 = true;
|
fi->stage2 = true;
|
||||||
fi->s1ptw = true;
|
fi->s1ptw = true;
|
||||||
|
@ -8328,7 +8329,9 @@ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
MemTxAttrs attrs = {};
|
MemTxAttrs attrs = {};
|
||||||
|
MemTxResult result = MEMTX_OK;
|
||||||
AddressSpace *as;
|
AddressSpace *as;
|
||||||
|
uint32_t data;
|
||||||
|
|
||||||
attrs.secure = is_secure;
|
attrs.secure = is_secure;
|
||||||
as = arm_addressspace(cs, attrs);
|
as = arm_addressspace(cs, attrs);
|
||||||
|
@ -8337,10 +8340,16 @@ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (regime_translation_big_endian(env, mmu_idx)) {
|
if (regime_translation_big_endian(env, mmu_idx)) {
|
||||||
return address_space_ldl_be(as, addr, attrs, NULL);
|
data = address_space_ldl_be(as, addr, attrs, &result);
|
||||||
} else {
|
} else {
|
||||||
return address_space_ldl_le(as, addr, attrs, NULL);
|
data = address_space_ldl_le(as, addr, attrs, &result);
|
||||||
}
|
}
|
||||||
|
if (result == MEMTX_OK) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
fi->type = ARMFault_SyncExternalOnWalk;
|
||||||
|
fi->ea = arm_extabort_type(result);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
|
@ -8349,7 +8358,9 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
MemTxAttrs attrs = {};
|
MemTxAttrs attrs = {};
|
||||||
|
MemTxResult result = MEMTX_OK;
|
||||||
AddressSpace *as;
|
AddressSpace *as;
|
||||||
|
uint32_t data;
|
||||||
|
|
||||||
attrs.secure = is_secure;
|
attrs.secure = is_secure;
|
||||||
as = arm_addressspace(cs, attrs);
|
as = arm_addressspace(cs, attrs);
|
||||||
|
@ -8358,10 +8369,16 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (regime_translation_big_endian(env, mmu_idx)) {
|
if (regime_translation_big_endian(env, mmu_idx)) {
|
||||||
return address_space_ldq_be(as, addr, attrs, NULL);
|
data = address_space_ldq_be(as, addr, attrs, &result);
|
||||||
} else {
|
} else {
|
||||||
return address_space_ldq_le(as, addr, attrs, NULL);
|
data = address_space_ldq_le(as, addr, attrs, &result);
|
||||||
}
|
}
|
||||||
|
if (result == MEMTX_OK) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
fi->type = ARMFault_SyncExternalOnWalk;
|
||||||
|
fi->ea = arm_extabort_type(result);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
||||||
|
@ -8390,6 +8407,9 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
||||||
}
|
}
|
||||||
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
||||||
mmu_idx, fi);
|
mmu_idx, fi);
|
||||||
|
if (fi->type != ARMFault_None) {
|
||||||
|
goto do_fault;
|
||||||
|
}
|
||||||
type = (desc & 3);
|
type = (desc & 3);
|
||||||
domain = (desc >> 5) & 0x0f;
|
domain = (desc >> 5) & 0x0f;
|
||||||
if (regime_el(env, mmu_idx) == 1) {
|
if (regime_el(env, mmu_idx) == 1) {
|
||||||
|
@ -8426,6 +8446,9 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
||||||
}
|
}
|
||||||
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
||||||
mmu_idx, fi);
|
mmu_idx, fi);
|
||||||
|
if (fi->type != ARMFault_None) {
|
||||||
|
goto do_fault;
|
||||||
|
}
|
||||||
switch (desc & 3) {
|
switch (desc & 3) {
|
||||||
case 0: /* Page translation fault. */
|
case 0: /* Page translation fault. */
|
||||||
fi->type = ARMFault_Translation;
|
fi->type = ARMFault_Translation;
|
||||||
|
@ -8508,6 +8531,9 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address,
|
||||||
}
|
}
|
||||||
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
||||||
mmu_idx, fi);
|
mmu_idx, fi);
|
||||||
|
if (fi->type != ARMFault_None) {
|
||||||
|
goto do_fault;
|
||||||
|
}
|
||||||
type = (desc & 3);
|
type = (desc & 3);
|
||||||
if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) {
|
if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) {
|
||||||
/* Section translation fault, or attempt to use the encoding
|
/* Section translation fault, or attempt to use the encoding
|
||||||
|
@ -8559,6 +8585,9 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address,
|
||||||
table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
|
table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
|
||||||
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
desc = arm_ldl_ptw(cs, table, regime_is_secure(env, mmu_idx),
|
||||||
mmu_idx, fi);
|
mmu_idx, fi);
|
||||||
|
if (fi->type != ARMFault_None) {
|
||||||
|
goto do_fault;
|
||||||
|
}
|
||||||
ap = ((desc >> 4) & 3) | ((desc >> 7) & 4);
|
ap = ((desc >> 4) & 3) | ((desc >> 7) & 4);
|
||||||
switch (desc & 3) {
|
switch (desc & 3) {
|
||||||
case 0: /* Page translation fault. */
|
case 0: /* Page translation fault. */
|
||||||
|
@ -8964,7 +8993,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
||||||
descaddr &= ~7ULL;
|
descaddr &= ~7ULL;
|
||||||
nstable = extract32(tableattrs, 4, 1);
|
nstable = extract32(tableattrs, 4, 1);
|
||||||
descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fi);
|
descriptor = arm_ldq_ptw(cs, descaddr, !nstable, mmu_idx, fi);
|
||||||
if (fi->s1ptw) {
|
if (fi->type != ARMFault_None) {
|
||||||
goto do_fault;
|
goto do_fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9272,6 +9301,13 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||||
case 6:
|
case 6:
|
||||||
*prot |= PAGE_READ | PAGE_EXEC;
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
/* for v7M, same as 6; for R profile a reserved value */
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"DRACR[%d]: Bad value for AP bits: 0x%"
|
"DRACR[%d]: Bad value for AP bits: 0x%"
|
||||||
|
@ -9290,6 +9326,13 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||||
case 6:
|
case 6:
|
||||||
*prot |= PAGE_READ | PAGE_EXEC;
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
/* for v7M, same as 6; for R profile a reserved value */
|
||||||
|
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||||
|
*prot |= PAGE_READ | PAGE_EXEC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"DRACR[%d]: Bad value for AP bits: 0x%"
|
"DRACR[%d]: Bad value for AP bits: 0x%"
|
||||||
|
|
|
@ -687,6 +687,16 @@ static inline uint32_t arm_fi_to_lfsc(ARMMMUFaultInfo *fi)
|
||||||
return fsc;
|
return fsc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool arm_extabort_type(MemTxResult result)
|
||||||
|
{
|
||||||
|
/* The EA bit in syndromes and fault status registers is an
|
||||||
|
* IMPDEF classification of external aborts. ARM implementations
|
||||||
|
* usually use this to indicate AXI bus Decode error (0) or
|
||||||
|
* Slave error (1); in QEMU we follow that.
|
||||||
|
*/
|
||||||
|
return result != MEMTX_DECODE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/* Do a page table walk and add page to TLB if possible */
|
/* Do a page table walk and add page to TLB if possible */
|
||||||
bool arm_tlb_fill(CPUState *cpu, vaddr address,
|
bool arm_tlb_fill(CPUState *cpu, vaddr address,
|
||||||
MMUAccessType access_type, int mmu_idx,
|
MMUAccessType access_type, int mmu_idx,
|
||||||
|
|
|
@ -220,12 +220,7 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
|
||||||
/* now we have a real cpu fault */
|
/* now we have a real cpu fault */
|
||||||
cpu_restore_state(cs, retaddr);
|
cpu_restore_state(cs, retaddr);
|
||||||
|
|
||||||
/* The EA bit in syndromes and fault status registers is an
|
fi.ea = arm_extabort_type(response);
|
||||||
* IMPDEF classification of external aborts. ARM implementations
|
|
||||||
* usually use this to indicate AXI bus Decode error (0) or
|
|
||||||
* Slave error (1); in QEMU we follow that.
|
|
||||||
*/
|
|
||||||
fi.ea = (response != MEMTX_DECODE_ERROR);
|
|
||||||
fi.type = ARMFault_SyncExternal;
|
fi.type = ARMFault_SyncExternal;
|
||||||
deliver_fault(cpu, addr, access_type, mmu_idx, &fi);
|
deliver_fault(cpu, addr, access_type, mmu_idx, &fi);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4985,6 +4985,38 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The imm8 encodes the sign bit, enough bits to represent an exponent in
|
||||||
|
* the range 01....1xx to 10....0xx, and the most significant 4 bits of
|
||||||
|
* the mantissa; see VFPExpandImm() in the v8 ARM ARM.
|
||||||
|
*/
|
||||||
|
static uint64_t vfp_expand_imm(int size, uint8_t imm8)
|
||||||
|
{
|
||||||
|
uint64_t imm;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case MO_64:
|
||||||
|
imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
|
||||||
|
(extract32(imm8, 6, 1) ? 0x3fc0 : 0x4000) |
|
||||||
|
extract32(imm8, 0, 6);
|
||||||
|
imm <<= 48;
|
||||||
|
break;
|
||||||
|
case MO_32:
|
||||||
|
imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
|
||||||
|
(extract32(imm8, 6, 1) ? 0x3e00 : 0x4000) |
|
||||||
|
(extract32(imm8, 0, 6) << 3);
|
||||||
|
imm <<= 16;
|
||||||
|
break;
|
||||||
|
case MO_16:
|
||||||
|
imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
|
||||||
|
(extract32(imm8, 6, 1) ? 0x3000 : 0x4000) |
|
||||||
|
(extract32(imm8, 0, 6) << 6);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
return imm;
|
||||||
|
}
|
||||||
|
|
||||||
/* Floating point immediate
|
/* Floating point immediate
|
||||||
* 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0
|
* 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0
|
||||||
* +---+---+---+-----------+------+---+------------+-------+------+------+
|
* +---+---+---+-----------+------+---+------------+-------+------+------+
|
||||||
|
@ -5008,22 +5040,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The imm8 encodes the sign bit, enough bits to represent
|
imm = vfp_expand_imm(MO_32 + is_double, imm8);
|
||||||
* an exponent in the range 01....1xx to 10....0xx,
|
|
||||||
* and the most significant 4 bits of the mantissa; see
|
|
||||||
* VFPExpandImm() in the v8 ARM ARM.
|
|
||||||
*/
|
|
||||||
if (is_double) {
|
|
||||||
imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
|
|
||||||
(extract32(imm8, 6, 1) ? 0x3fc0 : 0x4000) |
|
|
||||||
extract32(imm8, 0, 6);
|
|
||||||
imm <<= 48;
|
|
||||||
} else {
|
|
||||||
imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
|
|
||||||
(extract32(imm8, 6, 1) ? 0x3e00 : 0x4000) |
|
|
||||||
(extract32(imm8, 0, 6) << 3);
|
|
||||||
imm <<= 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcg_res = tcg_const_i64(imm);
|
tcg_res = tcg_const_i64(imm);
|
||||||
write_fp_dreg(s, rd, tcg_res);
|
write_fp_dreg(s, rd, tcg_res);
|
||||||
|
|
Loading…
Reference in New Issue