Browse Source
* raspi2: implement RNG module * raspi2: implement new SD card controller (but don't wire it up) * sdhci: bugfixes for block transfers * virt: fix cpu object reference leak * Add missing fp_access_check() to aarch64 crypto instructions * cputlb: Don't assume do_unassigned_access() never returns * virt: Add a user option to disallow ITS instantiation * i.MX timers: fix reset handling * ARMv7M NVIC: rewrite to fix broken priority handling and masking * exynos: Fix proper mapping of CPUs by providing real cluster ID * exynos: Fix Linux kernel division by zero for PLLs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJYtW/TAAoJEDwlJe0UNgzezv4P/3j+WOVgVlNL8AQ3RFEzzzz4 IszdrQIFcZ5ICT3MDgH/JMjkpj/C13eGo9eiIFlOvVjtsLlneW10frEB6SGP4ype KpFDHji0cm9MT7gdbgbWbextGU8w7xWV43JmSmEuOxkF/r64u/Ap3CXudB58A+Rv NvbJMHkkR5Q0MIDA4EkOCLn/Ihh78sd99p8+EV3Gu89KiiB4xRf9D3k/O+Sdh58L yvPNat0tjJolzZkAUf6RieFN1F7oBXazR13+E8fDy5OTr25K+S7mehBwSJtQ7dGo VjhR7eMJdyyzi+l+OezQFCUmZI9pENcDdhspSl2mOkPRrQi4gwjEszPcmcNhCNGQ mguQjk7f5KHtLDDzL1HFr+4sKZdoptXZC18JupjN9oCHJvMq4MDHJaUH0bwrHals GhE7cM3aNg8ItJu694ruMLY13Z0+B+TmSLFktRYrjJe3qJEfOQE4EKWXXUZaEe5j L13HPP4nInAUU7kvpuepiYHiR4zBTTgEqRBVdQ/qCkLSuO/EH2TbT9u6pifAtI1S OkBidnbatWflUwLMMa6jt7ZUx+yDsH7y7C1WxmytnPzKudMMOZ5MxI54yLgEEFTs SoelwzfSZb2PlOw3h3UwyRDz3CehkDMUMqzIoqF7Wn/UVb6GHvldq/eVpKOOxtG7 nVTTYBFuSil0LV/LST4X =3qLp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170228' into staging target-arm queue: * raspi2: implement RNG module * raspi2: implement new SD card controller (but don't wire it up) * sdhci: bugfixes for block transfers * virt: fix cpu object reference leak * Add missing fp_access_check() to aarch64 crypto instructions * cputlb: Don't assume do_unassigned_access() never returns * virt: Add a user option to disallow ITS instantiation * i.MX timers: fix reset handling * ARMv7M NVIC: rewrite to fix broken priority handling and masking * exynos: Fix proper mapping of CPUs by providing real cluster ID * exynos: Fix Linux kernel division by zero for PLLs # gpg: Signature made Tue 28 Feb 2017 12:40:51 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-20170228: (27 commits) hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID hw/arm/exynos: Fix Linux kernel division by zero for PLLs bcm2835_sdhost: add bcm2835 sdhost controller armv7m: Allow SHCSR writes to change pending and active bits armv7m: Raise correct kind of UsageFault for attempts to execute ARM code armv7m: Check exception return consistency armv7m: Extract "exception taken" code into functions armv7m: VECTCLRACTIVE and VECTRESET are UNPREDICTABLE armv7m: Simpler and faster exception start armv7m: Remove unused armv7m_nvic_acknowledge_irq() return value armv7m: Escalate exceptions to HardFault if necessary arm: gic: Remove references to NVIC armv7m: Fix condition check for taking exceptions armv7m: Rewrite NVIC to not use any GIC code armv7m: Implement reading and writing of PRIGROUP armv7m: Rename nvic_state to NVICState ARM i.MX timers: fix reset handling hw/arm/virt: Add a user option to disallow ITS instantiation cputlb: Don't assume do_unassigned_access() never returns Add missing fp_access_check() to aarch64 crypto instructions ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>pull/42/merge
26 changed files with 1897 additions and 331 deletions
File diff suppressed because it is too large
@ -0,0 +1,149 @@ |
|||
/*
|
|||
* BCM2835 Random Number Generator emulation |
|||
* |
|||
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com> |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "qemu/log.h" |
|||
#include "qapi/error.h" |
|||
#include "crypto/random.h" |
|||
#include "hw/misc/bcm2835_rng.h" |
|||
|
|||
static uint32_t get_random_bytes(void) |
|||
{ |
|||
uint32_t res; |
|||
Error *err = NULL; |
|||
|
|||
if (qcrypto_random_bytes((uint8_t *)&res, sizeof(res), &err) < 0) { |
|||
/* On failure we don't want to return the guest a non-random
|
|||
* value in case they're really using it for cryptographic |
|||
* purposes, so the best we can do is die here. |
|||
* This shouldn't happen unless something's broken. |
|||
* In theory we could implement this device's full FIFO |
|||
* and interrupt semantics and then just stop filling the |
|||
* FIFO. That's a lot of work, though, so we assume any |
|||
* errors are systematic problems and trust that if we didn't |
|||
* fail as the guest inited then we won't fail later on |
|||
* mid-run. |
|||
*/ |
|||
error_report_err(err); |
|||
exit(1); |
|||
} |
|||
return res; |
|||
} |
|||
|
|||
static uint64_t bcm2835_rng_read(void *opaque, hwaddr offset, |
|||
unsigned size) |
|||
{ |
|||
BCM2835RngState *s = (BCM2835RngState *)opaque; |
|||
uint32_t res = 0; |
|||
|
|||
assert(size == 4); |
|||
|
|||
switch (offset) { |
|||
case 0x0: /* rng_ctrl */ |
|||
res = s->rng_ctrl; |
|||
break; |
|||
case 0x4: /* rng_status */ |
|||
res = s->rng_status | (1 << 24); |
|||
break; |
|||
case 0x8: /* rng_data */ |
|||
res = get_random_bytes(); |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, |
|||
"bcm2835_rng_read: Bad offset %x\n", |
|||
(int)offset); |
|||
res = 0; |
|||
break; |
|||
} |
|||
|
|||
return res; |
|||
} |
|||
|
|||
static void bcm2835_rng_write(void *opaque, hwaddr offset, |
|||
uint64_t value, unsigned size) |
|||
{ |
|||
BCM2835RngState *s = (BCM2835RngState *)opaque; |
|||
|
|||
assert(size == 4); |
|||
|
|||
switch (offset) { |
|||
case 0x0: /* rng_ctrl */ |
|||
s->rng_ctrl = value; |
|||
break; |
|||
case 0x4: /* rng_status */ |
|||
/* we shouldn't let the guest write to bits [31..20] */ |
|||
s->rng_status &= ~0xFFFFF; /* clear 20 lower bits */ |
|||
s->rng_status |= value & 0xFFFFF; /* set them to new value */ |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, |
|||
"bcm2835_rng_write: Bad offset %x\n", |
|||
(int)offset); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static const MemoryRegionOps bcm2835_rng_ops = { |
|||
.read = bcm2835_rng_read, |
|||
.write = bcm2835_rng_write, |
|||
.endianness = DEVICE_NATIVE_ENDIAN, |
|||
}; |
|||
|
|||
static const VMStateDescription vmstate_bcm2835_rng = { |
|||
.name = TYPE_BCM2835_RNG, |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_UINT32(rng_ctrl, BCM2835RngState), |
|||
VMSTATE_UINT32(rng_status, BCM2835RngState), |
|||
VMSTATE_END_OF_LIST() |
|||
} |
|||
}; |
|||
|
|||
static void bcm2835_rng_init(Object *obj) |
|||
{ |
|||
BCM2835RngState *s = BCM2835_RNG(obj); |
|||
|
|||
memory_region_init_io(&s->iomem, obj, &bcm2835_rng_ops, s, |
|||
TYPE_BCM2835_RNG, 0x10); |
|||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); |
|||
} |
|||
|
|||
static void bcm2835_rng_reset(DeviceState *dev) |
|||
{ |
|||
BCM2835RngState *s = BCM2835_RNG(dev); |
|||
|
|||
s->rng_ctrl = 0; |
|||
s->rng_status = 0; |
|||
} |
|||
|
|||
static void bcm2835_rng_class_init(ObjectClass *klass, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->reset = bcm2835_rng_reset; |
|||
dc->vmsd = &vmstate_bcm2835_rng; |
|||
} |
|||
|
|||
static TypeInfo bcm2835_rng_info = { |
|||
.name = TYPE_BCM2835_RNG, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.instance_size = sizeof(BCM2835RngState), |
|||
.class_init = bcm2835_rng_class_init, |
|||
.instance_init = bcm2835_rng_init, |
|||
}; |
|||
|
|||
static void bcm2835_rng_register_types(void) |
|||
{ |
|||
type_register_static(&bcm2835_rng_info); |
|||
} |
|||
|
|||
type_init(bcm2835_rng_register_types) |
|||
@ -0,0 +1,164 @@ |
|||
/*
|
|||
* Exynos4210 Clock Controller Emulation |
|||
* |
|||
* Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org> |
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation; either version 2 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but WITHOUT |
|||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|||
* for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "hw/sysbus.h" |
|||
#include "qemu/log.h" |
|||
|
|||
#define TYPE_EXYNOS4210_CLK "exynos4210.clk" |
|||
#define EXYNOS4210_CLK(obj) \ |
|||
OBJECT_CHECK(Exynos4210ClkState, (obj), TYPE_EXYNOS4210_CLK) |
|||
|
|||
#define CLK_PLL_LOCKED BIT(29) |
|||
|
|||
#define EXYNOS4210_CLK_REGS_MEM_SIZE 0x15104 |
|||
|
|||
typedef struct Exynos4210Reg { |
|||
const char *name; /* for debug only */ |
|||
uint32_t offset; |
|||
uint32_t reset_value; |
|||
} Exynos4210Reg; |
|||
|
|||
/* Clock controller register base: 0x10030000 */ |
|||
static const Exynos4210Reg exynos4210_clk_regs[] = { |
|||
{"EPLL_LOCK", 0xc010, 0x00000fff}, |
|||
{"VPLL_LOCK", 0xc020, 0x00000fff}, |
|||
{"EPLL_CON0", 0xc110, 0x00300301 | CLK_PLL_LOCKED}, |
|||
{"EPLL_CON1", 0xc114, 0x00000000}, |
|||
{"VPLL_CON0", 0xc120, 0x00240201 | CLK_PLL_LOCKED}, |
|||
{"VPLL_CON1", 0xc124, 0x66010464}, |
|||
{"APLL_LOCK", 0x14000, 0x00000fff}, |
|||
{"MPLL_LOCK", 0x14004, 0x00000fff}, |
|||
{"APLL_CON0", 0x14100, 0x00c80601 | CLK_PLL_LOCKED}, |
|||
{"APLL_CON1", 0x14104, 0x0000001c}, |
|||
{"MPLL_CON0", 0x14108, 0x00c80601 | CLK_PLL_LOCKED}, |
|||
{"MPLL_CON1", 0x1410c, 0x0000001c}, |
|||
}; |
|||
|
|||
#define EXYNOS4210_REGS_NUM ARRAY_SIZE(exynos4210_clk_regs) |
|||
|
|||
typedef struct Exynos4210ClkState { |
|||
SysBusDevice parent_obj; |
|||
|
|||
MemoryRegion iomem; |
|||
uint32_t reg[EXYNOS4210_REGS_NUM]; |
|||
} Exynos4210ClkState; |
|||
|
|||
static uint64_t exynos4210_clk_read(void *opaque, hwaddr offset, |
|||
unsigned size) |
|||
{ |
|||
const Exynos4210ClkState *s = (Exynos4210ClkState *)opaque; |
|||
const Exynos4210Reg *regs = exynos4210_clk_regs; |
|||
unsigned int i; |
|||
|
|||
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) { |
|||
if (regs->offset == offset) { |
|||
return s->reg[i]; |
|||
} |
|||
regs++; |
|||
} |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n", |
|||
__func__, (uint32_t)offset); |
|||
return 0; |
|||
} |
|||
|
|||
static void exynos4210_clk_write(void *opaque, hwaddr offset, |
|||
uint64_t val, unsigned size) |
|||
{ |
|||
Exynos4210ClkState *s = (Exynos4210ClkState *)opaque; |
|||
const Exynos4210Reg *regs = exynos4210_clk_regs; |
|||
unsigned int i; |
|||
|
|||
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) { |
|||
if (regs->offset == offset) { |
|||
s->reg[i] = val; |
|||
return; |
|||
} |
|||
regs++; |
|||
} |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n", |
|||
__func__, (uint32_t)offset); |
|||
} |
|||
|
|||
static const MemoryRegionOps exynos4210_clk_ops = { |
|||
.read = exynos4210_clk_read, |
|||
.write = exynos4210_clk_write, |
|||
.endianness = DEVICE_NATIVE_ENDIAN, |
|||
.valid = { |
|||
.min_access_size = 4, |
|||
.max_access_size = 4, |
|||
.unaligned = false |
|||
} |
|||
}; |
|||
|
|||
static void exynos4210_clk_reset(DeviceState *dev) |
|||
{ |
|||
Exynos4210ClkState *s = EXYNOS4210_CLK(dev); |
|||
unsigned int i; |
|||
|
|||
/* Set default values for registers */ |
|||
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) { |
|||
s->reg[i] = exynos4210_clk_regs[i].reset_value; |
|||
} |
|||
} |
|||
|
|||
static void exynos4210_clk_init(Object *obj) |
|||
{ |
|||
Exynos4210ClkState *s = EXYNOS4210_CLK(obj); |
|||
SysBusDevice *dev = SYS_BUS_DEVICE(obj); |
|||
|
|||
/* memory mapping */ |
|||
memory_region_init_io(&s->iomem, obj, &exynos4210_clk_ops, s, |
|||
TYPE_EXYNOS4210_CLK, EXYNOS4210_CLK_REGS_MEM_SIZE); |
|||
sysbus_init_mmio(dev, &s->iomem); |
|||
} |
|||
|
|||
static const VMStateDescription exynos4210_clk_vmstate = { |
|||
.name = TYPE_EXYNOS4210_CLK, |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_UINT32_ARRAY(reg, Exynos4210ClkState, EXYNOS4210_REGS_NUM), |
|||
VMSTATE_END_OF_LIST() |
|||
} |
|||
}; |
|||
|
|||
static void exynos4210_clk_class_init(ObjectClass *klass, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->reset = exynos4210_clk_reset; |
|||
dc->vmsd = &exynos4210_clk_vmstate; |
|||
} |
|||
|
|||
static const TypeInfo exynos4210_clk_info = { |
|||
.name = TYPE_EXYNOS4210_CLK, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.instance_size = sizeof(Exynos4210ClkState), |
|||
.instance_init = exynos4210_clk_init, |
|||
.class_init = exynos4210_clk_class_init, |
|||
}; |
|||
|
|||
static void exynos4210_clk_register(void) |
|||
{ |
|||
qemu_log_mask(LOG_GUEST_ERROR, "Clock init\n"); |
|||
type_register_static(&exynos4210_clk_info); |
|||
} |
|||
|
|||
type_init(exynos4210_clk_register) |
|||
@ -0,0 +1,429 @@ |
|||
/*
|
|||
* Raspberry Pi (BCM2835) SD Host Controller |
|||
* |
|||
* Copyright (c) 2017 Antfield SAS |
|||
* |
|||
* Authors: |
|||
* Clement Deschamps <clement.deschamps@antfield.fr> |
|||
* Luc Michel <luc.michel@antfield.fr> |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "qemu/log.h" |
|||
#include "sysemu/blockdev.h" |
|||
#include "hw/sd/bcm2835_sdhost.h" |
|||
|
|||
#define TYPE_BCM2835_SDHOST_BUS "bcm2835-sdhost-bus" |
|||
#define BCM2835_SDHOST_BUS(obj) \ |
|||
OBJECT_CHECK(SDBus, (obj), TYPE_BCM2835_SDHOST_BUS) |
|||
|
|||
#define SDCMD 0x00 /* Command to SD card - 16 R/W */ |
|||
#define SDARG 0x04 /* Argument to SD card - 32 R/W */ |
|||
#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */ |
|||
#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */ |
|||
#define SDRSP0 0x10 /* SD card rsp (31:0) - 32 R */ |
|||
#define SDRSP1 0x14 /* SD card rsp (63:32) - 32 R */ |
|||
#define SDRSP2 0x18 /* SD card rsp (95:64) - 32 R */ |
|||
#define SDRSP3 0x1c /* SD card rsp (127:96) - 32 R */ |
|||
#define SDHSTS 0x20 /* SD host status - 11 R */ |
|||
#define SDVDD 0x30 /* SD card power control - 1 R/W */ |
|||
#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */ |
|||
#define SDHCFG 0x38 /* Host configuration - 2 R/W */ |
|||
#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */ |
|||
#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */ |
|||
#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */ |
|||
|
|||
#define SDCMD_NEW_FLAG 0x8000 |
|||
#define SDCMD_FAIL_FLAG 0x4000 |
|||
#define SDCMD_BUSYWAIT 0x800 |
|||
#define SDCMD_NO_RESPONSE 0x400 |
|||
#define SDCMD_LONG_RESPONSE 0x200 |
|||
#define SDCMD_WRITE_CMD 0x80 |
|||
#define SDCMD_READ_CMD 0x40 |
|||
#define SDCMD_CMD_MASK 0x3f |
|||
|
|||
#define SDCDIV_MAX_CDIV 0x7ff |
|||
|
|||
#define SDHSTS_BUSY_IRPT 0x400 |
|||
#define SDHSTS_BLOCK_IRPT 0x200 |
|||
#define SDHSTS_SDIO_IRPT 0x100 |
|||
#define SDHSTS_REW_TIME_OUT 0x80 |
|||
#define SDHSTS_CMD_TIME_OUT 0x40 |
|||
#define SDHSTS_CRC16_ERROR 0x20 |
|||
#define SDHSTS_CRC7_ERROR 0x10 |
|||
#define SDHSTS_FIFO_ERROR 0x08 |
|||
/* Reserved */ |
|||
/* Reserved */ |
|||
#define SDHSTS_DATA_FLAG 0x01 |
|||
|
|||
#define SDHCFG_BUSY_IRPT_EN (1 << 10) |
|||
#define SDHCFG_BLOCK_IRPT_EN (1 << 8) |
|||
#define SDHCFG_SDIO_IRPT_EN (1 << 5) |
|||
#define SDHCFG_DATA_IRPT_EN (1 << 4) |
|||
#define SDHCFG_SLOW_CARD (1 << 3) |
|||
#define SDHCFG_WIDE_EXT_BUS (1 << 2) |
|||
#define SDHCFG_WIDE_INT_BUS (1 << 1) |
|||
#define SDHCFG_REL_CMD_LINE (1 << 0) |
|||
|
|||
#define SDEDM_FORCE_DATA_MODE (1 << 19) |
|||
#define SDEDM_CLOCK_PULSE (1 << 20) |
|||
#define SDEDM_BYPASS (1 << 21) |
|||
|
|||
#define SDEDM_WRITE_THRESHOLD_SHIFT 9 |
|||
#define SDEDM_READ_THRESHOLD_SHIFT 14 |
|||
#define SDEDM_THRESHOLD_MASK 0x1f |
|||
|
|||
#define SDEDM_FSM_MASK 0xf |
|||
#define SDEDM_FSM_IDENTMODE 0x0 |
|||
#define SDEDM_FSM_DATAMODE 0x1 |
|||
#define SDEDM_FSM_READDATA 0x2 |
|||
#define SDEDM_FSM_WRITEDATA 0x3 |
|||
#define SDEDM_FSM_READWAIT 0x4 |
|||
#define SDEDM_FSM_READCRC 0x5 |
|||
#define SDEDM_FSM_WRITECRC 0x6 |
|||
#define SDEDM_FSM_WRITEWAIT1 0x7 |
|||
#define SDEDM_FSM_POWERDOWN 0x8 |
|||
#define SDEDM_FSM_POWERUP 0x9 |
|||
#define SDEDM_FSM_WRITESTART1 0xa |
|||
#define SDEDM_FSM_WRITESTART2 0xb |
|||
#define SDEDM_FSM_GENPULSES 0xc |
|||
#define SDEDM_FSM_WRITEWAIT2 0xd |
|||
#define SDEDM_FSM_STARTPOWDOWN 0xf |
|||
|
|||
#define SDDATA_FIFO_WORDS 16 |
|||
|
|||
static void bcm2835_sdhost_update_irq(BCM2835SDHostState *s) |
|||
{ |
|||
uint32_t irq = s->status & |
|||
(SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT | SDHSTS_SDIO_IRPT); |
|||
qemu_set_irq(s->irq, !!irq); |
|||
} |
|||
|
|||
static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) |
|||
{ |
|||
SDRequest request; |
|||
uint8_t rsp[16]; |
|||
int rlen; |
|||
|
|||
request.cmd = s->cmd & SDCMD_CMD_MASK; |
|||
request.arg = s->cmdarg; |
|||
|
|||
rlen = sdbus_do_command(&s->sdbus, &request, rsp); |
|||
if (rlen < 0) { |
|||
goto error; |
|||
} |
|||
if (!(s->cmd & SDCMD_NO_RESPONSE)) { |
|||
#define RWORD(n) (((uint32_t)rsp[n] << 24) | (rsp[n + 1] << 16) \ |
|||
| (rsp[n + 2] << 8) | rsp[n + 3]) |
|||
if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { |
|||
goto error; |
|||
} |
|||
if (rlen != 4 && rlen != 16) { |
|||
goto error; |
|||
} |
|||
if (rlen == 4) { |
|||
s->rsp[0] = RWORD(0); |
|||
s->rsp[1] = s->rsp[2] = s->rsp[3] = 0; |
|||
} else { |
|||
s->rsp[0] = RWORD(12); |
|||
s->rsp[1] = RWORD(8); |
|||
s->rsp[2] = RWORD(4); |
|||
s->rsp[3] = RWORD(0); |
|||
} |
|||
#undef RWORD |
|||
} |
|||
return; |
|||
|
|||
error: |
|||
s->cmd |= SDCMD_FAIL_FLAG; |
|||
s->status |= SDHSTS_CMD_TIME_OUT; |
|||
} |
|||
|
|||
static void bcm2835_sdhost_fifo_push(BCM2835SDHostState *s, uint32_t value) |
|||
{ |
|||
int n; |
|||
|
|||
if (s->fifo_len == BCM2835_SDHOST_FIFO_LEN) { |
|||
/* FIFO overflow */ |
|||
return; |
|||
} |
|||
n = (s->fifo_pos + s->fifo_len) & (BCM2835_SDHOST_FIFO_LEN - 1); |
|||
s->fifo_len++; |
|||
s->fifo[n] = value; |
|||
} |
|||
|
|||
static uint32_t bcm2835_sdhost_fifo_pop(BCM2835SDHostState *s) |
|||
{ |
|||
uint32_t value; |
|||
|
|||
if (s->fifo_len == 0) { |
|||
/* FIFO underflow */ |
|||
return 0; |
|||
} |
|||
value = s->fifo[s->fifo_pos]; |
|||
s->fifo_len--; |
|||
s->fifo_pos = (s->fifo_pos + 1) & (BCM2835_SDHOST_FIFO_LEN - 1); |
|||
return value; |
|||
} |
|||
|
|||
static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) |
|||
{ |
|||
uint32_t value = 0; |
|||
int n; |
|||
int is_read; |
|||
|
|||
is_read = (s->cmd & SDCMD_READ_CMD) != 0; |
|||
if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))) { |
|||
if (is_read) { |
|||
n = 0; |
|||
while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) { |
|||
value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8); |
|||
s->datacnt--; |
|||
n++; |
|||
if (n == 4) { |
|||
bcm2835_sdhost_fifo_push(s, value); |
|||
n = 0; |
|||
value = 0; |
|||
} |
|||
} |
|||
if (n != 0) { |
|||
bcm2835_sdhost_fifo_push(s, value); |
|||
} |
|||
} else { /* write */ |
|||
n = 0; |
|||
while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { |
|||
if (n == 0) { |
|||
value = bcm2835_sdhost_fifo_pop(s); |
|||
n = 4; |
|||
} |
|||
n--; |
|||
s->datacnt--; |
|||
sdbus_write_data(&s->sdbus, value & 0xff); |
|||
value >>= 8; |
|||
} |
|||
} |
|||
} |
|||
if (s->datacnt == 0) { |
|||
s->status |= SDHSTS_DATA_FLAG; |
|||
|
|||
s->edm &= ~0xf; |
|||
s->edm |= SDEDM_FSM_DATAMODE; |
|||
|
|||
if (s->config & SDHCFG_DATA_IRPT_EN) { |
|||
s->status |= SDHSTS_SDIO_IRPT; |
|||
} |
|||
|
|||
if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) { |
|||
s->status |= SDHSTS_BUSY_IRPT; |
|||
} |
|||
|
|||
if ((s->cmd & SDCMD_WRITE_CMD) && (s->config & SDHCFG_BLOCK_IRPT_EN)) { |
|||
s->status |= SDHSTS_BLOCK_IRPT; |
|||
} |
|||
|
|||
bcm2835_sdhost_update_irq(s); |
|||
} |
|||
|
|||
s->edm &= ~(0x1f << 4); |
|||
s->edm |= ((s->fifo_len & 0x1f) << 4); |
|||
} |
|||
|
|||
static uint64_t bcm2835_sdhost_read(void *opaque, hwaddr offset, |
|||
unsigned size) |
|||
{ |
|||
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque; |
|||
uint32_t res = 0; |
|||
|
|||
switch (offset) { |
|||
case SDCMD: |
|||
res = s->cmd; |
|||
break; |
|||
case SDHSTS: |
|||
res = s->status; |
|||
break; |
|||
case SDRSP0: |
|||
res = s->rsp[0]; |
|||
break; |
|||
case SDRSP1: |
|||
res = s->rsp[1]; |
|||
break; |
|||
case SDRSP2: |
|||
res = s->rsp[2]; |
|||
break; |
|||
case SDRSP3: |
|||
res = s->rsp[3]; |
|||
break; |
|||
case SDEDM: |
|||
res = s->edm; |
|||
break; |
|||
case SDVDD: |
|||
res = s->vdd; |
|||
break; |
|||
case SDDATA: |
|||
res = bcm2835_sdhost_fifo_pop(s); |
|||
bcm2835_sdhost_fifo_run(s); |
|||
break; |
|||
case SDHBCT: |
|||
res = s->hbct; |
|||
break; |
|||
case SDHBLC: |
|||
res = s->hblc; |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", |
|||
__func__, offset); |
|||
res = 0; |
|||
break; |
|||
} |
|||
|
|||
return res; |
|||
} |
|||
|
|||
static void bcm2835_sdhost_write(void *opaque, hwaddr offset, |
|||
uint64_t value, unsigned size) |
|||
{ |
|||
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque; |
|||
|
|||
switch (offset) { |
|||
case SDCMD: |
|||
s->cmd = value; |
|||
if (value & SDCMD_NEW_FLAG) { |
|||
bcm2835_sdhost_send_command(s); |
|||
bcm2835_sdhost_fifo_run(s); |
|||
s->cmd &= ~SDCMD_NEW_FLAG; |
|||
} |
|||
break; |
|||
case SDTOUT: |
|||
break; |
|||
case SDCDIV: |
|||
break; |
|||
case SDHSTS: |
|||
s->status &= ~value; |
|||
bcm2835_sdhost_update_irq(s); |
|||
break; |
|||
case SDARG: |
|||
s->cmdarg = value; |
|||
break; |
|||
case SDEDM: |
|||
if ((value & 0xf) == 0xf) { |
|||
/* power down */ |
|||
value &= ~0xf; |
|||
} |
|||
s->edm = value; |
|||
break; |
|||
case SDHCFG: |
|||
s->config = value; |
|||
bcm2835_sdhost_fifo_run(s); |
|||
break; |
|||
case SDVDD: |
|||
s->vdd = value; |
|||
break; |
|||
case SDDATA: |
|||
bcm2835_sdhost_fifo_push(s, value); |
|||
bcm2835_sdhost_fifo_run(s); |
|||
break; |
|||
case SDHBCT: |
|||
s->hbct = value; |
|||
break; |
|||
case SDHBLC: |
|||
s->hblc = value; |
|||
s->datacnt = s->hblc * s->hbct; |
|||
bcm2835_sdhost_fifo_run(s); |
|||
break; |
|||
|
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", |
|||
__func__, offset); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static const MemoryRegionOps bcm2835_sdhost_ops = { |
|||
.read = bcm2835_sdhost_read, |
|||
.write = bcm2835_sdhost_write, |
|||
.endianness = DEVICE_NATIVE_ENDIAN, |
|||
}; |
|||
|
|||
static const VMStateDescription vmstate_bcm2835_sdhost = { |
|||
.name = TYPE_BCM2835_SDHOST, |
|||
.version_id = 1, |
|||
.minimum_version_id = 1, |
|||
.fields = (VMStateField[]) { |
|||
VMSTATE_UINT32(cmd, BCM2835SDHostState), |
|||
VMSTATE_UINT32(cmdarg, BCM2835SDHostState), |
|||
VMSTATE_UINT32(status, BCM2835SDHostState), |
|||
VMSTATE_UINT32_ARRAY(rsp, BCM2835SDHostState, 4), |
|||
VMSTATE_UINT32(config, BCM2835SDHostState), |
|||
VMSTATE_UINT32(edm, BCM2835SDHostState), |
|||
VMSTATE_UINT32(vdd, BCM2835SDHostState), |
|||
VMSTATE_UINT32(hbct, BCM2835SDHostState), |
|||
VMSTATE_UINT32(hblc, BCM2835SDHostState), |
|||
VMSTATE_INT32(fifo_pos, BCM2835SDHostState), |
|||
VMSTATE_INT32(fifo_len, BCM2835SDHostState), |
|||
VMSTATE_UINT32_ARRAY(fifo, BCM2835SDHostState, BCM2835_SDHOST_FIFO_LEN), |
|||
VMSTATE_UINT32(datacnt, BCM2835SDHostState), |
|||
VMSTATE_END_OF_LIST() |
|||
} |
|||
}; |
|||
|
|||
static void bcm2835_sdhost_init(Object *obj) |
|||
{ |
|||
BCM2835SDHostState *s = BCM2835_SDHOST(obj); |
|||
|
|||
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), |
|||
TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus"); |
|||
|
|||
memory_region_init_io(&s->iomem, obj, &bcm2835_sdhost_ops, s, |
|||
TYPE_BCM2835_SDHOST, 0x1000); |
|||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); |
|||
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); |
|||
} |
|||
|
|||
static void bcm2835_sdhost_reset(DeviceState *dev) |
|||
{ |
|||
BCM2835SDHostState *s = BCM2835_SDHOST(dev); |
|||
|
|||
s->cmd = 0; |
|||
s->cmdarg = 0; |
|||
s->edm = 0x0000c60f; |
|||
s->config = 0; |
|||
s->hbct = 0; |
|||
s->hblc = 0; |
|||
s->datacnt = 0; |
|||
s->fifo_pos = 0; |
|||
s->fifo_len = 0; |
|||
} |
|||
|
|||
static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->reset = bcm2835_sdhost_reset; |
|||
dc->vmsd = &vmstate_bcm2835_sdhost; |
|||
} |
|||
|
|||
static TypeInfo bcm2835_sdhost_info = { |
|||
.name = TYPE_BCM2835_SDHOST, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.instance_size = sizeof(BCM2835SDHostState), |
|||
.class_init = bcm2835_sdhost_class_init, |
|||
.instance_init = bcm2835_sdhost_init, |
|||
}; |
|||
|
|||
static const TypeInfo bcm2835_sdhost_bus_info = { |
|||
.name = TYPE_BCM2835_SDHOST_BUS, |
|||
.parent = TYPE_SD_BUS, |
|||
.instance_size = sizeof(SDBus), |
|||
}; |
|||
|
|||
static void bcm2835_sdhost_register_types(void) |
|||
{ |
|||
type_register_static(&bcm2835_sdhost_info); |
|||
type_register_static(&bcm2835_sdhost_bus_info); |
|||
} |
|||
|
|||
type_init(bcm2835_sdhost_register_types) |
|||
@ -0,0 +1,27 @@ |
|||
/*
|
|||
* BCM2835 Random Number Generator emulation |
|||
* |
|||
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com> |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#ifndef BCM2835_RNG_H |
|||
#define BCM2835_RNG_H |
|||
|
|||
#include "hw/sysbus.h" |
|||
|
|||
#define TYPE_BCM2835_RNG "bcm2835-rng" |
|||
#define BCM2835_RNG(obj) \ |
|||
OBJECT_CHECK(BCM2835RngState, (obj), TYPE_BCM2835_RNG) |
|||
|
|||
typedef struct { |
|||
SysBusDevice busdev; |
|||
MemoryRegion iomem; |
|||
|
|||
uint32_t rng_ctrl; |
|||
uint32_t rng_status; |
|||
} BCM2835RngState; |
|||
|
|||
#endif |
|||
@ -0,0 +1,48 @@ |
|||
/*
|
|||
* Raspberry Pi (BCM2835) SD Host Controller |
|||
* |
|||
* Copyright (c) 2017 Antfield SAS |
|||
* |
|||
* Authors: |
|||
* Clement Deschamps <clement.deschamps@antfield.fr> |
|||
* Luc Michel <luc.michel@antfield.fr> |
|||
* |
|||
* This work is licensed under the terms of the GNU GPL, version 2 or later. |
|||
* See the COPYING file in the top-level directory. |
|||
*/ |
|||
|
|||
#ifndef BCM2835_SDHOST_H |
|||
#define BCM2835_SDHOST_H |
|||
|
|||
#include "hw/sysbus.h" |
|||
#include "hw/sd/sd.h" |
|||
|
|||
#define TYPE_BCM2835_SDHOST "bcm2835-sdhost" |
|||
#define BCM2835_SDHOST(obj) \ |
|||
OBJECT_CHECK(BCM2835SDHostState, (obj), TYPE_BCM2835_SDHOST) |
|||
|
|||
#define BCM2835_SDHOST_FIFO_LEN 16 |
|||
|
|||
typedef struct { |
|||
SysBusDevice busdev; |
|||
SDBus sdbus; |
|||
MemoryRegion iomem; |
|||
|
|||
uint32_t cmd; |
|||
uint32_t cmdarg; |
|||
uint32_t status; |
|||
uint32_t rsp[4]; |
|||
uint32_t config; |
|||
uint32_t edm; |
|||
uint32_t vdd; |
|||
uint32_t hbct; |
|||
uint32_t hblc; |
|||
int32_t fifo_pos; |
|||
int32_t fifo_len; |
|||
uint32_t fifo[BCM2835_SDHOST_FIFO_LEN]; |
|||
uint32_t datacnt; |
|||
|
|||
qemu_irq irq; |
|||
} BCM2835SDHostState; |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue