* CAN bus (will be under network maintainner)
* scsi-block opblockers (myself) * Dirty log bitmap cleanup (myself) * SDHCI improvements and tests (Philippe) * HAX support for larger guest sizese (Yu Ning) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJagwYqAAoJEL/70l94x66Dy54H+weJG15nv+ihR7diKmelsmAA HQCYGImo8xKzvSwHOqLW3pisPq0Xey6Zz/h48H/JpWVLwGQ4cIenWoSIHRY9lJzy 25+Yxa4omXCq1pDuMqfMlu+OB6/0rBTvWHolmnCeRfpuLj3vY5sQ+iEz33cSJaL6 J/NsUGJX0zy9VKM5Fpu02ZN0EMmjDFMQYaGiKKv/G1kGmJgj4VeMBJJEkqjS0qiC xYQ4coRDOSdtkAvMS4k/2oMZGjPC6r9FBay/3MbyTqAzsSKKAuBs5HuTjJ6bQF+R 9xj0dqTNEHQteXtMAijAIhSHN2uGqRc+ZvBk5wyboj6heWabYQECzY05aolulf8= =KWrC -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * CAN bus (will be under network maintainner) * scsi-block opblockers (myself) * Dirty log bitmap cleanup (myself) * SDHCI improvements and tests (Philippe) * HAX support for larger guest sizese (Yu Ning) # gpg: Signature made Tue 13 Feb 2018 15:37:14 GMT # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (48 commits) travis: use libgcc-4.8-dev (libgcc-6-dev is not available on Ubuntu 14.04) memory: unify loops to sync dirty log bitmap memory: hide memory_region_sync_dirty_bitmap behind DirtyBitmapSnapshot memory: remove memory_region_test_and_clear_dirty g364fb: switch to using DirtyBitmapSnapshot sdhci: add Spec v4.2 register definitions sdhci: add a check_capab_v3() qtest sdhci: check Spec v3 capabilities qtest hw/arm/xilinx_zynqmp: enable the UHS-I mode hw/arm/xilinx_zynqmp: fix the capabilities/spec version to match the datasheet hw/arm/fsl-imx6: implement SDHCI Spec. v3 hw/arm/bcm2835_peripherals: change maximum block size to 1kB hw/arm/bcm2835_peripherals: implement SDHCI Spec v3 sdhci: implement CMD/DAT[] fields in the Present State register sdhci: implement UHS-I voltage switch sdbus: add trace events sdhci: implement the Host Control 2 register (tuning sequence) sdhci: rename the hostctl1 register sdhci: add support for v3 capabilities hw/arm/xilinx_zynq: fix the capabilities register to match the datasheet ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
bec9c64ef7
@ -13,7 +13,7 @@ addons:
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libgcc-6-dev
|
||||
- libgcc-4.8-dev
|
||||
- libgnutls-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
|
5
Makefile
5
Makefile
@ -294,7 +294,7 @@ else
|
||||
DOCS=
|
||||
endif
|
||||
|
||||
SUBDIR_MAKEFLAGS=BUILD_DIR=$(BUILD_DIR)
|
||||
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
|
||||
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
|
||||
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
|
||||
|
||||
@ -958,4 +958,5 @@ ifdef QEMU_GA_MSI_ENABLED
|
||||
endif
|
||||
@echo ''
|
||||
endif
|
||||
@echo ' $(MAKE) V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
|
||||
@echo ' $(MAKE) [targets] (quiet build, default)'
|
||||
@echo ' $(MAKE) V=1 [targets] (verbose build)'
|
||||
|
16
configure
vendored
16
configure
vendored
@ -471,10 +471,8 @@ for opt do
|
||||
--cpu=*) cpu="$optarg"
|
||||
;;
|
||||
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
|
||||
EXTRA_CFLAGS="$optarg"
|
||||
;;
|
||||
--extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg"
|
||||
EXTRA_CXXFLAGS="$optarg"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg"
|
||||
EXTRA_LDFLAGS="$optarg"
|
||||
@ -1424,7 +1422,6 @@ case "$cpu" in
|
||||
esac
|
||||
|
||||
QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS"
|
||||
EXTRA_CFLAGS="$CPU_CFLAGS $EXTRA_CFLAGS"
|
||||
|
||||
# For user-mode emulation the host arch has to be one we explicitly
|
||||
# support, even if we're using TCI.
|
||||
@ -5309,7 +5306,15 @@ fi
|
||||
##########################################
|
||||
# checks for sanitizers
|
||||
|
||||
write_c_skeleton
|
||||
# we could use a simple skeleton for flags checks, but this also
|
||||
# detect the static linking issue of ubsan, see also:
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285
|
||||
cat > $TMPC << EOF
|
||||
#include <stdint.h>
|
||||
int main(void) {
|
||||
return INT32_MIN / -1;
|
||||
}
|
||||
EOF
|
||||
|
||||
have_asan=no
|
||||
have_ubsan=no
|
||||
@ -5877,9 +5882,6 @@ if test "$mingw32" = "no" ; then
|
||||
echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
|
||||
fi
|
||||
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
|
||||
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
|
||||
echo "extra_cxxflags=$EXTRA_CXXFLAGS" >> $config_host_mak
|
||||
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
|
||||
echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
|
||||
echo "libs_softmmu=$libs_softmmu" >> $config_host_mak
|
||||
echo "GIT=$git" >> $config_host_mak
|
||||
|
@ -31,6 +31,9 @@ CONFIG_ESP_PCI=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_SERIAL_PCI=y
|
||||
CONFIG_CAN_BUS=y
|
||||
CONFIG_CAN_SJA1000=y
|
||||
CONFIG_CAN_PCI=y
|
||||
CONFIG_IPACK=y
|
||||
CONFIG_WDT_IB6300ESB=y
|
||||
CONFIG_PCI_TESTDEV=y
|
||||
|
107
docs/can.txt
Normal file
107
docs/can.txt
Normal file
@ -0,0 +1,107 @@
|
||||
QEMU CAN bus emulation support
|
||||
==============================
|
||||
|
||||
The CAN bus emulation provides mechanism to connect multiple
|
||||
emulated CAN controller chips together by one or multiple CAN busses
|
||||
(the controller device "canbus" parameter). The individual busses
|
||||
can be connected to host system CAN API (at this time only Linux
|
||||
SocketCAN is supported).
|
||||
|
||||
The concept of busses is generic and different CAN controllers
|
||||
can be implemented for it but at this time only SJA1000 chip
|
||||
controller is implemented.
|
||||
|
||||
The PCI addon card hardware has been selected as the first CAN
|
||||
interface to implement because such device can be easily connected
|
||||
to systems with different CPU architectures (x86, PowerPC, ARM, etc.).
|
||||
|
||||
The project has been initially started in frame of RTEMS GSoC 2013
|
||||
slot by Jin Yang under our mentoring The initial idea was to provide generic
|
||||
CAN subsystem for RTEMS. But lack of common environment for code and RTEMS
|
||||
testing lead to goal change to provide environment which provides complete
|
||||
emulated environment for testing and RTEMS GSoC slot has been donated
|
||||
to work on CAN hardware emulation on QEMU.
|
||||
|
||||
Examples how to use CAN emulation
|
||||
=================================
|
||||
|
||||
When QEMU with CAN PCI support is compiled then one of the next
|
||||
CAN boards can be selected
|
||||
|
||||
(1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) boad. QEMU startup options
|
||||
-object can-bus,id=canbus0
|
||||
-device kvaser_pci,canbus=canbus0
|
||||
Add "can-host-socketcan" object to connect device to host system CAN bus
|
||||
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0
|
||||
|
||||
(2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation
|
||||
-object can-bus,id=canbus0
|
||||
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
|
||||
|
||||
another example:
|
||||
-object can-bus,id=canbus0
|
||||
-object can-bus,id=canbus1
|
||||
-device pcm3680_pci,canbus0=canbus0,canbus1=canbus1
|
||||
|
||||
(3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation
|
||||
-device mioe3680_pci,canbus0=canbus0
|
||||
|
||||
|
||||
The ''kvaser_pci'' board/device model is compatible with and has been tested with
|
||||
''kvaser_pci'' driver included in mainline Linux kernel.
|
||||
The tested setup was Linux 4.9 kernel on the host and guest side.
|
||||
Example for qemu-system-x86_64:
|
||||
|
||||
qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
|
||||
-initrd ramdisk.cpio \
|
||||
-virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
|
||||
-object can-bus,id=canbus0 \
|
||||
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
|
||||
-device kvaser_pci,canbus=canbus0 \
|
||||
-nographic -append "console=ttyS0"
|
||||
|
||||
Example for qemu-system-arm:
|
||||
|
||||
qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
|
||||
-kernel kernel-qemu-arm1176-versatilepb \
|
||||
-hda rpi-wheezy-overlay \
|
||||
-append "console=ttyAMA0 root=/dev/sda2 ro init=/sbin/init-overlay" \
|
||||
-nographic \
|
||||
-virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
|
||||
-object can-bus,id=canbus0 \
|
||||
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
|
||||
-device kvaser_pci,canbus=canbus0,host=can0 \
|
||||
|
||||
The CAN interface of the host system has to be configured for proper
|
||||
bitrate and set up. Configuration is not propagated from emulated
|
||||
devices through bus to the physical host device. Example configuration
|
||||
for 1 Mbit/s
|
||||
|
||||
ip link set can0 type can bitrate 1000000
|
||||
ip link set can0 up
|
||||
|
||||
Virtual (host local only) can interface can be used on the host
|
||||
side instead of physical interface
|
||||
|
||||
ip link add dev can0 type vcan
|
||||
|
||||
The CAN interface on the host side can be used to analyze CAN
|
||||
traffic with "candump" command which is included in "can-utils".
|
||||
|
||||
candump can0
|
||||
|
||||
Links to other resources
|
||||
========================
|
||||
|
||||
(1) Repository with development branch can-pci at Czech Technical University
|
||||
https://gitlab.fel.cvut.cz/canbus/qemu-canbus
|
||||
(2) GitHub repository with can-pci and our other changes included
|
||||
https://gitlab.fel.cvut.cz/canbus/qemu-canbus
|
||||
(3) RTEMS page describing project
|
||||
https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation
|
||||
(4) RTLWS 2015 article about the projevt and its use with CANopen emulation
|
||||
http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can.pdf
|
||||
Slides
|
||||
http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf
|
||||
(5) Linux SocketCAN utilities
|
||||
https://github.com/linux-can/can-utils/
|
@ -19,7 +19,7 @@
|
||||
#define BCM2835_VC_PERI_BASE 0x7e000000
|
||||
|
||||
/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
|
||||
#define BCM2835_SDHC_CAPAREG 0x52034b4
|
||||
#define BCM2835_SDHC_CAPAREG 0x52134b4
|
||||
|
||||
static void bcm2835_peripherals_init(Object *obj)
|
||||
{
|
||||
@ -254,14 +254,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
|
||||
|
||||
/* Extended Mass Media Controller */
|
||||
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extended Mass Media Controller
|
||||
*
|
||||
* Compatible with:
|
||||
* - SD Host Controller Specification Version 3.0 Draft 1.0
|
||||
* - SDIO Specification Version 3.0
|
||||
* - MMC Specification Version 4.4
|
||||
*
|
||||
* For the exact details please refer to the Arasan documentation:
|
||||
* SD3.0_Host_AHB_eMMC4.4_Usersguide_ver5.9_jan11_10.pdf
|
||||
*/
|
||||
object_property_set_uint(OBJECT(&s->sdhci), 3, "sd-spec-version", &err);
|
||||
object_property_set_uint(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
|
||||
&err);
|
||||
object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
|
||||
&err);
|
||||
if (err) {
|
||||
|
@ -377,8 +377,20 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
|
||||
BlockBackend *blk;
|
||||
DriveInfo *di;
|
||||
|
||||
/* Compatible with:
|
||||
* - SD Host Controller Specification Version 2.0
|
||||
* - SDIO Specification Version 2.0
|
||||
* - MMC Specification Version 4.3
|
||||
* - SDMA
|
||||
* - ADMA2
|
||||
*
|
||||
* As this part of the Exynos4210 is not publically available,
|
||||
* we used the "HS-MMC Controller S3C2416X RISC Microprocessor"
|
||||
* public datasheet which is very similar (implementing
|
||||
* MMC Specification Version 4.0 being the only difference noted)
|
||||
*/
|
||||
dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
|
||||
qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
|
||||
qdev_prop_set_uint64(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define IMX6_ESDHC_CAPABILITIES 0x057834b4
|
||||
|
||||
#define NAME_SIZE 20
|
||||
|
||||
static void fsl_imx6_init(Object *obj)
|
||||
@ -348,6 +350,11 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
|
||||
{ FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ },
|
||||
};
|
||||
|
||||
/* UHS-I SDIO3.0 SDR104 1.8V ADMA */
|
||||
object_property_set_uint(OBJECT(&s->esdhc[i]), 3, "sd-spec-version",
|
||||
&err);
|
||||
object_property_set_uint(OBJECT(&s->esdhc[i]), IMX6_ESDHC_CAPABILITIES,
|
||||
"capareg", &err);
|
||||
object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -61,6 +61,8 @@ static const int dma_irqs[8] = {
|
||||
#define SLCR_XILINX_UNLOCK_KEY 0xdf0d
|
||||
#define SLCR_XILINX_LOCK_KEY 0x767b
|
||||
|
||||
#define ZYNQ_SDHCI_CAPABILITIES 0x69ec0080 /* Datasheet: UG585 (v1.12.1) */
|
||||
|
||||
#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \
|
||||
extract32((x), 12, 4) << 16)
|
||||
|
||||
@ -165,10 +167,8 @@ static void zynq_init(MachineState *machine)
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
|
||||
DeviceState *dev, *carddev;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *busdev;
|
||||
DriveInfo *di;
|
||||
BlockBackend *blk;
|
||||
qemu_irq pic[64];
|
||||
int n;
|
||||
|
||||
@ -247,27 +247,32 @@ static void zynq_init(MachineState *machine)
|
||||
gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]);
|
||||
gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]);
|
||||
|
||||
dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]);
|
||||
for (n = 0; n < 2; n++) {
|
||||
int hci_irq = n ? 79 : 56;
|
||||
hwaddr hci_addr = n ? 0xE0101000 : 0xE0100000;
|
||||
DriveInfo *di;
|
||||
BlockBackend *blk;
|
||||
DeviceState *carddev;
|
||||
|
||||
di = drive_get_next(IF_SD);
|
||||
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||
carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
||||
/* Compatible with:
|
||||
* - SD Host Controller Specification Version 2.0 Part A2
|
||||
* - SDIO Specification Version 2.0
|
||||
* - MMC Specification Version 3.31
|
||||
*/
|
||||
dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
|
||||
qdev_prop_set_uint8(dev, "sd-spec-version", 2);
|
||||
qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
|
||||
|
||||
dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
|
||||
|
||||
di = drive_get_next(IF_SD);
|
||||
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||
carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
||||
di = drive_get_next(IF_SD);
|
||||
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||
carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(carddev), true, "realized",
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
dev = qdev_create(NULL, TYPE_ZYNQ_XADC);
|
||||
qdev_init_nofail(dev);
|
||||
|
@ -53,6 +53,8 @@
|
||||
#define IPI_ADDR 0xFF300000
|
||||
#define IPI_IRQ 64
|
||||
|
||||
#define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */
|
||||
|
||||
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
|
||||
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
|
||||
};
|
||||
@ -387,22 +389,28 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]);
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
|
||||
char *bus_name;
|
||||
char *bus_name = g_strdup_printf("sd-bus%d", i);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]);
|
||||
Object *sdhci = OBJECT(&s->sdhci[i]);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->sdhci[i]), true,
|
||||
"realized", &err);
|
||||
/* Compatible with:
|
||||
* - SD Host Controller Specification Version 3.00
|
||||
* - SDIO Specification Version 3.0
|
||||
* - eMMC Specification Version 4.51
|
||||
*/
|
||||
object_property_set_uint(sdhci, 3, "sd-spec-version", &err);
|
||||
object_property_set_uint(sdhci, SDHCI_CAPABILITIES, "capareg", &err);
|
||||
object_property_set_uint(sdhci, UHS_I, "uhs", &err);
|
||||
object_property_set_bool(sdhci, true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
|
||||
sdhci_addr[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
|
||||
gic_spi[sdhci_intr[i]]);
|
||||
sysbus_mmio_map(sbd, 0, sdhci_addr[i]);
|
||||
sysbus_connect_irq(sbd, 0, gic_spi[sdhci_intr[i]]);
|
||||
|
||||
/* Alias controller SD bus to the SoC itself */
|
||||
bus_name = g_strdup_printf("sd-bus%d", i);
|
||||
object_property_add_alias(OBJECT(s), bus_name,
|
||||
OBJECT(&s->sdhci[i]), "sd-bus",
|
||||
object_property_add_alias(OBJECT(s), bus_name, sdhci, "sd-bus",
|
||||
&error_abort);
|
||||
g_free(bus_name);
|
||||
}
|
||||
|
@ -108,7 +108,6 @@ static void cg3_update_display(void *opaque)
|
||||
data = (uint32_t *)surface_data(surface);
|
||||
|
||||
if (!s->full_update) {
|
||||
memory_region_sync_dirty_bitmap(&s->vram_mem);
|
||||
snap = memory_region_snapshot_and_clear_dirty(&s->vram_mem, 0x0,
|
||||
memory_region_size(&s->vram_mem),
|
||||
DIRTY_MEMORY_VGA);
|
||||
|
@ -1289,7 +1289,6 @@ static void exynos4210_fimd_update(void *opaque)
|
||||
scrn_width = w->virtpage_width;
|
||||
/* Total width of virtual screen page in bytes */
|
||||
inc_size = scrn_width + w->virtpage_offsize;
|
||||
memory_region_sync_dirty_bitmap(w->mem_section.mr);
|
||||
host_fb_addr = w->host_fb_addr;
|
||||
fb_line_addr = w->mem_section.offset_within_region;
|
||||
snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr,
|
||||
|
@ -83,7 +83,6 @@ void framebuffer_update_display(
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
memory_region_sync_dirty_bitmap(mem);
|
||||
|
||||
addr = mem_section->offset_within_region;
|
||||
src = memory_region_get_ram_ptr(mem) + addr;
|
||||
|
@ -62,15 +62,15 @@ typedef struct G364State {
|
||||
|
||||
#define G364_PAGE_SIZE 4096
|
||||
|
||||
static inline int check_dirty(G364State *s, ram_addr_t page)
|
||||
static inline int check_dirty(G364State *s, DirtyBitmapSnapshot *snap, ram_addr_t page)
|
||||
{
|
||||
return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
|
||||
DIRTY_MEMORY_VGA);
|
||||
return memory_region_snapshot_get_dirty(&s->mem_vram, snap, page, G364_PAGE_SIZE);
|
||||
}
|
||||
|
||||
static void g364fb_draw_graphic8(G364State *s)
|
||||
{
|
||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
||||
DirtyBitmapSnapshot *snap;
|
||||
int i, w;
|
||||
uint8_t *vram;
|
||||
uint8_t *data_display, *dd;
|
||||
@ -122,8 +122,10 @@ static void g364fb_draw_graphic8(G364State *s)
|
||||
vram = s->vram + s->top_of_screen;
|
||||
/* XXX: out of range in vram? */
|
||||
data_display = dd = surface_data(surface);
|
||||
snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0, s->vram_size,
|
||||
DIRTY_MEMORY_VGA);
|
||||
while (y < s->height) {
|
||||
if (check_dirty(s, page)) {
|
||||
if (check_dirty(s, snap, page)) {
|
||||
if (y < ymin)
|
||||
ymin = ymax = y;
|
||||
if (x < xmin)
|
||||
@ -244,7 +246,6 @@ static void g364fb_update_display(void *opaque)
|
||||
qemu_console_resize(s->con, s->width, s->height);
|
||||
}
|
||||
|
||||
memory_region_sync_dirty_bitmap(&s->mem_vram);
|
||||
if (s->ctla & CTLA_FORCE_BLANK) {
|
||||
g364fb_draw_blank(s);
|
||||
} else if (s->depth == 8) {
|
||||
|
@ -1508,7 +1508,6 @@ static void sm501_update_display(void *opaque)
|
||||
}
|
||||
|
||||
/* draw each line according to conditions */
|
||||
memory_region_sync_dirty_bitmap(&s->local_mem_region);
|
||||
snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region,
|
||||
offset, width * height * src_bpp, DIRTY_MEMORY_VGA);
|
||||
for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) {
|
||||
|
@ -236,7 +236,6 @@ static void tcx_update_display(void *opaque)
|
||||
dd = surface_stride(surface);
|
||||
ds = 1024;
|
||||
|
||||
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
||||
snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
|
||||
memory_region_size(&ts->vram_mem),
|
||||
DIRTY_MEMORY_VGA);
|
||||
@ -292,7 +291,6 @@ static void tcx24_update_display(void *opaque)
|
||||
dd = surface_stride(surface);
|
||||
ds = 1024;
|
||||
|
||||
memory_region_sync_dirty_bitmap(&ts->vram_mem);
|
||||
snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
|
||||
memory_region_size(&ts->vram_mem),
|
||||
DIRTY_MEMORY_VGA);
|
||||
|
@ -1444,11 +1444,6 @@ static bool vga_scanline_invalidated(VGACommonState *s, int y)
|
||||
return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f));
|
||||
}
|
||||
|
||||
void vga_sync_dirty_bitmap(VGACommonState *s)
|
||||
{
|
||||
memory_region_sync_dirty_bitmap(&s->vram);
|
||||
}
|
||||
|
||||
void vga_dirty_log_start(VGACommonState *s)
|
||||
{
|
||||
memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
|
||||
@ -1638,7 +1633,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
|
||||
y1 = 0;
|
||||
|
||||
if (!full_update) {
|
||||
vga_sync_dirty_bitmap(s);
|
||||
if (s->line_compare < height) {
|
||||
/* split screen mode */
|
||||
region_start = 0;
|
||||
|
@ -46,3 +46,5 @@ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
|
||||
rocker/rocker_desc.o rocker/rocker_world.o \
|
||||
rocker/rocker_of_dpa.o
|
||||
obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
|
||||
|
||||
common-obj-$(CONFIG_CAN_BUS) += can/
|
||||
|
4
hw/net/can/Makefile.objs
Normal file
4
hw/net/can/Makefile.objs
Normal file
@ -0,0 +1,4 @@
|
||||
common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
|
||||
common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
|
||||
common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
|
||||
common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
|
319
hw/net/can/can_kvaser_pci.c
Normal file
319
hw/net/can/can_kvaser_pci.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Kvaser PCI CAN device (SJA1000 based) emulation
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Partially based on educational PCIexpress APOHW hardware
|
||||
* emulator used fro class A0B36APO at CTU FEE course by
|
||||
* Rostislav Lisovy and Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "chardev/char.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#include "can_sja1000.h"
|
||||
|
||||
#define TYPE_CAN_PCI_DEV "kvaser_pci"
|
||||
|
||||
#define KVASER_PCI_DEV(obj) \
|
||||
OBJECT_CHECK(KvaserPCIState, (obj), TYPE_CAN_PCI_DEV)
|
||||
|
||||
#ifndef KVASER_PCI_VENDOR_ID1
|
||||
#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
|
||||
#endif
|
||||
|
||||
#ifndef KVASER_PCI_DEVICE_ID1
|
||||
#define KVASER_PCI_DEVICE_ID1 0x8406
|
||||
#endif
|
||||
|
||||
#define KVASER_PCI_S5920_RANGE 0x80
|
||||
#define KVASER_PCI_SJA_RANGE 0x80
|
||||
#define KVASER_PCI_XILINX_RANGE 0x8
|
||||
|
||||
#define KVASER_PCI_BYTES_PER_SJA 0x20
|
||||
|
||||
#define S5920_OMB 0x0C
|
||||
#define S5920_IMB 0x1C
|
||||
#define S5920_MBEF 0x34
|
||||
#define S5920_INTCSR 0x38
|
||||
#define S5920_RCR 0x3C
|
||||
#define S5920_PTCR 0x60
|
||||
|
||||
#define S5920_INTCSR_ADDON_INTENABLE_M 0x2000
|
||||
#define S5920_INTCSR_INTERRUPT_ASSERTED_M 0x800000
|
||||
|
||||
#define KVASER_PCI_XILINX_VERINT 7 /* Lower nibble simulate interrupts,
|
||||
high nibble version number. */
|
||||
|
||||
#define KVASER_PCI_XILINX_VERSION_NUMBER 13
|
||||
|
||||
typedef struct KvaserPCIState {
|
||||
/*< private >*/
|
||||
PCIDevice dev;
|
||||
/*< public >*/
|
||||
MemoryRegion s5920_io;
|
||||
MemoryRegion sja_io;
|
||||
MemoryRegion xilinx_io;
|
||||
|
||||
CanSJA1000State sja_state;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t s5920_intcsr;
|
||||
uint32_t s5920_irqstate;
|
||||
|
||||
CanBusState *canbus;
|
||||
} KvaserPCIState;
|
||||
|
||||
static void kvaser_pci_irq_handler(void *opaque, int irq_num, int level)
|
||||
{
|
||||
KvaserPCIState *d = (KvaserPCIState *)opaque;
|
||||
|
||||
d->s5920_irqstate = level;
|
||||
if (d->s5920_intcsr & S5920_INTCSR_ADDON_INTENABLE_M) {
|
||||
pci_set_irq(&d->dev, level);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvaser_pci_reset(DeviceState *dev)
|
||||
{
|
||||
KvaserPCIState *d = KVASER_PCI_DEV(dev);
|
||||
CanSJA1000State *s = &d->sja_state;
|
||||
|
||||
can_sja_hardware_reset(s);
|
||||
}
|
||||
|
||||
static uint64_t kvaser_pci_s5920_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
KvaserPCIState *d = opaque;
|
||||
uint64_t val;
|
||||
|
||||
switch (addr) {
|
||||
case S5920_INTCSR:
|
||||
val = d->s5920_intcsr;
|
||||
val &= ~S5920_INTCSR_INTERRUPT_ASSERTED_M;
|
||||
if (d->s5920_irqstate) {
|
||||
val |= S5920_INTCSR_INTERRUPT_ASSERTED_M;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvaser_pci_s5920_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
KvaserPCIState *d = opaque;
|
||||
|
||||
switch (addr) {
|
||||
case S5920_INTCSR:
|
||||
if (d->s5920_irqstate &&
|
||||
((d->s5920_intcsr ^ data) & S5920_INTCSR_ADDON_INTENABLE_M)) {
|
||||
pci_set_irq(&d->dev, !!(data & S5920_INTCSR_ADDON_INTENABLE_M));
|
||||
}
|
||||
d->s5920_intcsr = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t kvaser_pci_sja_io_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
KvaserPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state;
|
||||
|
||||
if (addr >= KVASER_PCI_BYTES_PER_SJA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_sja_mem_read(s, addr, size);
|
||||
}
|
||||
|
||||
static void kvaser_pci_sja_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
KvaserPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state;
|
||||
|
||||
if (addr >= KVASER_PCI_BYTES_PER_SJA) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_sja_mem_write(s, addr, data, size);
|
||||
}
|
||||
|
||||
static uint64_t kvaser_pci_xilinx_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
switch (addr) {
|
||||
case KVASER_PCI_XILINX_VERINT:
|
||||
return (KVASER_PCI_XILINX_VERSION_NUMBER << 4) | 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kvaser_pci_xilinx_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const MemoryRegionOps kvaser_pci_s5920_io_ops = {
|
||||
.read = kvaser_pci_s5920_io_read,
|
||||
.write = kvaser_pci_s5920_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps kvaser_pci_sja_io_ops = {
|
||||
.read = kvaser_pci_sja_io_read,
|
||||
.write = kvaser_pci_sja_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps kvaser_pci_xilinx_io_ops = {
|
||||
.read = kvaser_pci_xilinx_io_read,
|
||||
.write = kvaser_pci_xilinx_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void kvaser_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
|
||||
CanSJA1000State *s = &d->sja_state;
|
||||
uint8_t *pci_conf;
|
||||
|
||||
pci_conf = pci_dev->config;
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
|
||||
|
||||
d->irq = qemu_allocate_irq(kvaser_pci_irq_handler, d, 0);
|
||||
|
||||
can_sja_init(s, d->irq);
|
||||
|
||||
if (can_sja_connect_to_bus(s, d->canbus) < 0) {
|
||||
error_setg(errp, "can_sja_connect_to_bus failed");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_init_io(&d->s5920_io, OBJECT(d), &kvaser_pci_s5920_io_ops,
|
||||
d, "kvaser_pci-s5920", KVASER_PCI_S5920_RANGE);
|
||||
memory_region_init_io(&d->sja_io, OBJECT(d), &kvaser_pci_sja_io_ops,
|
||||
d, "kvaser_pci-sja", KVASER_PCI_SJA_RANGE);
|
||||
memory_region_init_io(&d->xilinx_io, OBJECT(d), &kvaser_pci_xilinx_io_ops,
|
||||
d, "kvaser_pci-xilinx", KVASER_PCI_XILINX_RANGE);
|
||||
|
||||
pci_register_bar(&d->dev, /*BAR*/ 0, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->s5920_io);
|
||||
pci_register_bar(&d->dev, /*BAR*/ 1, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->sja_io);
|
||||
pci_register_bar(&d->dev, /*BAR*/ 2, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->xilinx_io);
|
||||
}
|
||||
|
||||
static void kvaser_pci_exit(PCIDevice *pci_dev)
|
||||
{
|
||||
KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
|
||||
CanSJA1000State *s = &d->sja_state;
|
||||
|
||||
can_sja_disconnect(s);
|
||||
|
||||
qemu_free_irq(d->irq);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_kvaser_pci = {
|
||||
.name = "kvaser_pci",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, KvaserPCIState),
|
||||
/* Load this before sja_state. */
|
||||
VMSTATE_UINT32(s5920_intcsr, KvaserPCIState),
|
||||
VMSTATE_STRUCT(sja_state, KvaserPCIState, 0, vmstate_can_sja,
|
||||
CanSJA1000State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void kvaser_pci_instance_init(Object *obj)
|
||||
{
|
||||
KvaserPCIState *d = KVASER_PCI_DEV(obj);
|
||||
|
||||
object_property_add_link(obj, "canbus", TYPE_CAN_BUS,
|
||||
(Object **)&d->canbus,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
0, &error_abort);
|
||||
}
|
||||
|
||||
static void kvaser_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = kvaser_pci_realize;
|
||||
k->exit = kvaser_pci_exit;
|
||||
k->vendor_id = KVASER_PCI_VENDOR_ID1;
|
||||
k->device_id = KVASER_PCI_DEVICE_ID1;
|
||||
k->revision = 0x00;
|
||||
k->class_id = 0x00ff00;
|
||||
dc->desc = "Kvaser PCICANx";
|
||||
dc->vmsd = &vmstate_kvaser_pci;
|
||||
dc->reset = kvaser_pci_reset;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo kvaser_pci_info = {
|
||||
.name = TYPE_CAN_PCI_DEV,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(KvaserPCIState),
|
||||
.class_init = kvaser_pci_class_init,
|
||||
.instance_init = kvaser_pci_instance_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void kvaser_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&kvaser_pci_info);
|
||||
}
|
||||
|
||||
type_init(kvaser_pci_register_types)
|
262
hw/net/can/can_mioe3680_pci.c
Normal file
262
hw/net/can/can_mioe3680_pci.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* MIOe-3680 PCI CAN device (SJA1000 based) emulation
|
||||
*
|
||||
* Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
|
||||
*
|
||||
* Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
|
||||
* Jin Yang and Pavel Pisa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "chardev/char.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#include "can_sja1000.h"
|
||||
|
||||
#define TYPE_CAN_PCI_DEV "mioe3680_pci"
|
||||
|
||||
#define MIOe3680_PCI_DEV(obj) \
|
||||
OBJECT_CHECK(Mioe3680PCIState, (obj), TYPE_CAN_PCI_DEV)
|
||||
|
||||
/* the PCI device and vendor IDs */
|
||||
#ifndef MIOe3680_PCI_VENDOR_ID1
|
||||
#define MIOe3680_PCI_VENDOR_ID1 0x13fe
|
||||
#endif
|
||||
|
||||
#ifndef MIOe3680_PCI_DEVICE_ID1
|
||||
#define MIOe3680_PCI_DEVICE_ID1 0xc302
|
||||
#endif
|
||||
|
||||
#define MIOe3680_PCI_SJA_COUNT 2
|
||||
#define MIOe3680_PCI_SJA_RANGE 0x400
|
||||
|
||||
#define MIOe3680_PCI_BYTES_PER_SJA 0x80
|
||||
|
||||
typedef struct Mioe3680PCIState {
|
||||
/*< private >*/
|
||||
PCIDevice dev;
|
||||
/*< public >*/
|
||||
MemoryRegion sja_io[MIOe3680_PCI_SJA_COUNT];
|
||||
|
||||
CanSJA1000State sja_state[MIOe3680_PCI_SJA_COUNT];
|
||||
qemu_irq irq;
|
||||
|
||||
char *model; /* The model that support, only SJA1000 now. */
|
||||
CanBusState *canbus[MIOe3680_PCI_SJA_COUNT];
|
||||
} Mioe3680PCIState;
|
||||
|
||||
static void mioe3680_pci_reset(DeviceState *dev)
|
||||
{
|
||||
Mioe3680PCIState *d = MIOe3680_PCI_DEV(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
|
||||
can_sja_hardware_reset(&d->sja_state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t mioe3680_pci_sja1_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
Mioe3680PCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[0];
|
||||
|
||||
if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_sja_mem_read(s, addr >> 2, size);
|
||||
}
|
||||
|
||||
static void mioe3680_pci_sja1_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
Mioe3680PCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[0];
|
||||
|
||||
if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_sja_mem_write(s, addr >> 2, data, size);
|
||||
}
|
||||
|
||||
static uint64_t mioe3680_pci_sja2_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
Mioe3680PCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[1];
|
||||
|
||||
if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_sja_mem_read(s, addr >> 2, size);
|
||||
}
|
||||
|
||||
static void mioe3680_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
Mioe3680PCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[1];
|
||||
|
||||
if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_sja_mem_write(s, addr >> 2, data, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mioe3680_pci_sja1_io_ops = {
|
||||
.read = mioe3680_pci_sja1_io_read,
|
||||
.write = mioe3680_pci_sja1_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps mioe3680_pci_sja2_io_ops = {
|
||||
.read = mioe3680_pci_sja2_io_read,
|
||||
.write = mioe3680_pci_sja2_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void mioe3680_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
|
||||
uint8_t *pci_conf;
|
||||
int i;
|
||||
|
||||
pci_conf = pci_dev->config;
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
|
||||
|
||||
d->irq = pci_allocate_irq(&d->dev);
|
||||
|
||||
for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
|
||||
can_sja_init(&d->sja_state[i], d->irq);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
|
||||
if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
|
||||
error_setg(errp, "can_sja_connect_to_bus failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memory_region_init_io(&d->sja_io[0], OBJECT(d), &mioe3680_pci_sja1_io_ops,
|
||||
d, "mioe3680_pci-sja1", MIOe3680_PCI_SJA_RANGE);
|
||||
memory_region_init_io(&d->sja_io[1], OBJECT(d), &mioe3680_pci_sja2_io_ops,
|
||||
d, "mioe3680_pci-sja2", MIOe3680_PCI_SJA_RANGE);
|
||||
|
||||
for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
|
||||
pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->sja_io[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void mioe3680_pci_exit(PCIDevice *pci_dev)
|
||||
{
|
||||
Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
|
||||
can_sja_disconnect(&d->sja_state[i]);
|
||||
}
|
||||
|
||||
qemu_free_irq(d->irq);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_mioe3680_pci = {
|
||||
.name = "mioe3680_pci",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState),
|
||||
VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja,
|
||||
CanSJA1000State),
|
||||
VMSTATE_STRUCT(sja_state[1], Mioe3680PCIState, 0, vmstate_can_sja,
|
||||
CanSJA1000State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void mioe3680_pci_instance_init(Object *obj)
|
||||
{
|
||||
Mioe3680PCIState *d = MIOe3680_PCI_DEV(obj);
|
||||
|
||||
object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
|
||||
(Object **)&d->canbus[0],
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
0, &error_abort);
|
||||
object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
|
||||
(Object **)&d->canbus[1],
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
0, &error_abort);
|
||||
}
|
||||
|
||||
static void mioe3680_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = mioe3680_pci_realize;
|
||||
k->exit = mioe3680_pci_exit;
|
||||
k->vendor_id = MIOe3680_PCI_VENDOR_ID1;
|
||||
k->device_id = MIOe3680_PCI_DEVICE_ID1;
|
||||
k->revision = 0x00;
|
||||
k->class_id = 0x000c09;
|
||||
k->subsystem_vendor_id = MIOe3680_PCI_VENDOR_ID1;
|
||||
k->subsystem_id = MIOe3680_PCI_DEVICE_ID1;
|
||||
dc->desc = "Mioe3680 PCICANx";
|
||||
dc->vmsd = &vmstate_mioe3680_pci;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->reset = mioe3680_pci_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo mioe3680_pci_info = {
|
||||
.name = TYPE_CAN_PCI_DEV,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(Mioe3680PCIState),
|
||||
.class_init = mioe3680_pci_class_init,
|
||||
.instance_init = mioe3680_pci_instance_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void mioe3680_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&mioe3680_pci_info);
|
||||
}
|
||||
|
||||
type_init(mioe3680_pci_register_types)
|
263
hw/net/can/can_pcm3680_pci.c
Normal file
263
hw/net/can/can_pcm3680_pci.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* PCM-3680i PCI CAN device (SJA1000 based) emulation
|
||||
*
|
||||
* Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
|
||||
*
|
||||
* Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
|
||||
* Jin Yang and Pavel Pisa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "chardev/char.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#include "can_sja1000.h"
|
||||
|
||||
#define TYPE_CAN_PCI_DEV "pcm3680_pci"
|
||||
|
||||
#define PCM3680i_PCI_DEV(obj) \
|
||||
OBJECT_CHECK(Pcm3680iPCIState, (obj), TYPE_CAN_PCI_DEV)
|
||||
|
||||
/* the PCI device and vendor IDs */
|
||||
#ifndef PCM3680i_PCI_VENDOR_ID1
|
||||
#define PCM3680i_PCI_VENDOR_ID1 0x13fe
|
||||
#endif
|
||||
|
||||
#ifndef PCM3680i_PCI_DEVICE_ID1
|
||||
#define PCM3680i_PCI_DEVICE_ID1 0xc002
|
||||
#endif
|
||||
|
||||
#define PCM3680i_PCI_SJA_COUNT 2
|
||||
#define PCM3680i_PCI_SJA_RANGE 0x100
|
||||
|
||||
#define PCM3680i_PCI_BYTES_PER_SJA 0x20
|
||||
|
||||
typedef struct Pcm3680iPCIState {
|
||||
/*< private >*/
|
||||
PCIDevice dev;
|
||||
/*< public >*/
|
||||
MemoryRegion sja_io[PCM3680i_PCI_SJA_COUNT];
|
||||
|
||||
CanSJA1000State sja_state[PCM3680i_PCI_SJA_COUNT];
|
||||
qemu_irq irq;
|
||||
|
||||
char *model; /* The model that support, only SJA1000 now. */
|
||||
CanBusState *canbus[PCM3680i_PCI_SJA_COUNT];
|
||||
} Pcm3680iPCIState;
|
||||
|
||||
static void pcm3680i_pci_reset(DeviceState *dev)
|
||||
{
|
||||
Pcm3680iPCIState *d = PCM3680i_PCI_DEV(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
|
||||
can_sja_hardware_reset(&d->sja_state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pcm3680i_pci_sja1_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
Pcm3680iPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[0];
|
||||
|
||||
if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_sja_mem_read(s, addr, size);
|
||||
}
|
||||
|
||||
static void pcm3680i_pci_sja1_io_write(void *opaque, hwaddr addr,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
Pcm3680iPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[0];
|
||||
|
||||
if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_sja_mem_write(s, addr, data, size);
|
||||
}
|
||||
|
||||
static uint64_t pcm3680i_pci_sja2_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
Pcm3680iPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[1];
|
||||
|
||||
if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_sja_mem_read(s, addr, size);
|
||||
}
|
||||
|
||||
static void pcm3680i_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
Pcm3680iPCIState *d = opaque;
|
||||
CanSJA1000State *s = &d->sja_state[1];
|
||||
|
||||
if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_sja_mem_write(s, addr, data, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pcm3680i_pci_sja1_io_ops = {
|
||||
.read = pcm3680i_pci_sja1_io_read,
|
||||
.write = pcm3680i_pci_sja1_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pcm3680i_pci_sja2_io_ops = {
|
||||
.read = pcm3680i_pci_sja2_io_read,
|
||||
.write = pcm3680i_pci_sja2_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void pcm3680i_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
|
||||
uint8_t *pci_conf;
|
||||
int i;
|
||||
|
||||
pci_conf = pci_dev->config;
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
|
||||
|
||||
d->irq = pci_allocate_irq(&d->dev);
|
||||
|
||||
for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
|
||||
can_sja_init(&d->sja_state[i], d->irq);
|
||||
}
|
||||
|
||||
for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
|
||||
if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
|
||||
error_setg(errp, "can_sja_connect_to_bus failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memory_region_init_io(&d->sja_io[0], OBJECT(d), &pcm3680i_pci_sja1_io_ops,
|
||||
d, "pcm3680i_pci-sja1", PCM3680i_PCI_SJA_RANGE);
|
||||
|
||||
memory_region_init_io(&d->sja_io[1], OBJECT(d), &pcm3680i_pci_sja2_io_ops,
|
||||
d, "pcm3680i_pci-sja2", PCM3680i_PCI_SJA_RANGE);
|
||||
|
||||
for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
|
||||
pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->sja_io[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcm3680i_pci_exit(PCIDevice *pci_dev)
|
||||
{
|
||||
Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
|
||||
can_sja_disconnect(&d->sja_state[i]);
|
||||
}
|
||||
|
||||
qemu_free_irq(d->irq);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pcm3680i_pci = {
|
||||
.name = "pcm3680i_pci",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState),
|
||||
VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0,
|
||||
vmstate_can_sja, CanSJA1000State),
|
||||
VMSTATE_STRUCT(sja_state[1], Pcm3680iPCIState, 0,
|
||||
vmstate_can_sja, CanSJA1000State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void pcm3680i_pci_instance_init(Object *obj)
|
||||
{
|
||||
Pcm3680iPCIState *d = PCM3680i_PCI_DEV(obj);
|
||||
|
||||
object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
|
||||
(Object **)&d->canbus[0],
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
0, &error_abort);
|
||||
object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
|
||||
(Object **)&d->canbus[1],
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
0, &error_abort);
|
||||
}
|
||||
|
||||
static void pcm3680i_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pcm3680i_pci_realize;
|
||||
k->exit = pcm3680i_pci_exit;
|
||||
k->vendor_id = PCM3680i_PCI_VENDOR_ID1;
|
||||
k->device_id = PCM3680i_PCI_DEVICE_ID1;
|
||||
k->revision = 0x00;
|
||||
k->class_id = 0x000c09;
|
||||
k->subsystem_vendor_id = PCM3680i_PCI_VENDOR_ID1;
|
||||
k->subsystem_id = PCM3680i_PCI_DEVICE_ID1;
|
||||
dc->desc = "Pcm3680i PCICANx";
|
||||
dc->vmsd = &vmstate_pcm3680i_pci;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->reset = pcm3680i_pci_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo pcm3680i_pci_info = {
|
||||
.name = TYPE_CAN_PCI_DEV,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(Pcm3680iPCIState),
|
||||
.class_init = pcm3680i_pci_class_init,
|
||||
.instance_init = pcm3680i_pci_instance_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void pcm3680i_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&pcm3680i_pci_info);
|
||||
}
|
||||
|
||||
type_init(pcm3680i_pci_register_types)
|
953
hw/net/can/can_sja1000.c
Normal file
953
hw/net/can/can_sja1000.c
Normal file
@ -0,0 +1,953 @@
|
||||
/*
|
||||
* CAN device - SJA1000 chip emulation for QEMU
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "chardev/char.h"
|
||||
#include "hw/hw.h"
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#include "can_sja1000.h"
|
||||
|
||||
#ifndef DEBUG_FILTER
|
||||
#define DEBUG_FILTER 0
|
||||
#endif /*DEBUG_FILTER*/
|
||||
|
||||
#ifndef DEBUG_CAN
|
||||
#define DEBUG_CAN 0
|
||||
#endif /*DEBUG_CAN*/
|
||||
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { \
|
||||
if (DEBUG_CAN) { \
|
||||
qemu_log("[cansja]: " fmt , ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void can_sja_software_reset(CanSJA1000State *s)
|
||||
{
|
||||
s->mode &= ~0x31;
|
||||
s->mode |= 0x01;
|
||||
s->status_pel &= ~0x37;
|
||||
s->status_pel |= 0x34;
|
||||
|
||||
s->rxbuf_start = 0x00;
|
||||
s->rxmsg_cnt = 0x00;
|
||||
s->rx_cnt = 0x00;
|
||||
}
|
||||
|
||||
void can_sja_hardware_reset(CanSJA1000State *s)
|
||||
{
|
||||
/* Reset by hardware, p10 */
|
||||
s->mode = 0x01;
|
||||
s->status_pel = 0x3c;
|
||||
s->interrupt_pel = 0x00;
|
||||
s->clock = 0x00;
|
||||
s->rxbuf_start = 0x00;
|
||||
s->rxmsg_cnt = 0x00;
|
||||
s->rx_cnt = 0x00;
|
||||
|
||||
s->control = 0x01;
|
||||
s->status_bas = 0x0c;
|
||||
s->interrupt_bas = 0x00;
|
||||
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
|
||||
static
|
||||
void can_sja_single_filter(struct qemu_can_filter *filter,
|
||||
const uint8_t *acr, const uint8_t *amr, int extended)
|
||||
{
|
||||
if (extended) {
|
||||
filter->can_id = (uint32_t)acr[0] << 21;
|
||||
filter->can_id |= (uint32_t)acr[1] << 13;
|
||||
filter->can_id |= (uint32_t)acr[2] << 5;
|
||||
filter->can_id |= (uint32_t)acr[3] >> 3;
|
||||
if (acr[3] & 4) {
|
||||
filter->can_id |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
|
||||
filter->can_mask = (uint32_t)amr[0] << 21;
|
||||
filter->can_mask |= (uint32_t)amr[1] << 13;
|
||||
filter->can_mask |= (uint32_t)amr[2] << 5;
|
||||
filter->can_mask |= (uint32_t)amr[3] >> 3;
|
||||
filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK;
|
||||
if (!(amr[3] & 4)) {
|
||||
filter->can_mask |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
} else {
|
||||
filter->can_id = (uint32_t)acr[0] << 3;
|
||||
filter->can_id |= (uint32_t)acr[1] >> 5;
|
||||
if (acr[1] & 0x10) {
|
||||
filter->can_id |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
|
||||
filter->can_mask = (uint32_t)amr[0] << 3;
|
||||
filter->can_mask |= (uint32_t)amr[1] << 5;
|
||||
filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
|
||||
if (!(amr[1] & 0x10)) {
|
||||
filter->can_mask |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void can_sja_dual_filter(struct qemu_can_filter *filter,
|
||||
const uint8_t *acr, const uint8_t *amr, int extended)
|
||||
{
|
||||
if (extended) {
|
||||
filter->can_id = (uint32_t)acr[0] << 21;
|
||||
filter->can_id |= (uint32_t)acr[1] << 13;
|
||||
|
||||
filter->can_mask = (uint32_t)amr[0] << 21;
|
||||
filter->can_mask |= (uint32_t)amr[1] << 13;
|
||||
filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK & ~0x1fff;
|
||||
} else {
|
||||
filter->can_id = (uint32_t)acr[0] << 3;
|
||||
filter->can_id |= (uint32_t)acr[1] >> 5;
|
||||
if (acr[1] & 0x10) {
|
||||
filter->can_id |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
|
||||
filter->can_mask = (uint32_t)amr[0] << 3;
|
||||
filter->can_mask |= (uint32_t)amr[1] >> 5;
|
||||
filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
|
||||
if (!(amr[1] & 0x10)) {
|
||||
filter->can_mask |= QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Details in DS-p22, what we need to do here is to test the data. */
|
||||
static
|
||||
int can_sja_accept_filter(CanSJA1000State *s,
|
||||
const qemu_can_frame *frame)
|
||||
{
|
||||
|
||||
struct qemu_can_filter filter;
|
||||
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
if (s->mode & (1 << 3)) { /* Single mode. */
|
||||
if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
|
||||
can_sja_single_filter(&filter,
|
||||
s->code_mask + 0, s->code_mask + 4, 1);
|
||||
|
||||
if (!can_bus_filter_match(&filter, frame->can_id)) {
|
||||
return 0;
|
||||
}
|
||||
} else { /* SFF */
|
||||
can_sja_single_filter(&filter,
|
||||
s->code_mask + 0, s->code_mask + 4, 0);
|
||||
|
||||
if (!can_bus_filter_match(&filter, frame->can_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (frame->can_dlc == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((frame->data[0] & ~(s->code_mask[6])) !=
|
||||
(s->code_mask[2] & ~(s->code_mask[6]))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frame->can_dlc < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((frame->data[1] & ~(s->code_mask[7])) ==
|
||||
(s->code_mask[3] & ~(s->code_mask[7]))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else { /* Dual mode */
|
||||
if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
|
||||
can_sja_dual_filter(&filter,
|
||||
s->code_mask + 0, s->code_mask + 4, 1);
|
||||
|
||||
if (can_bus_filter_match(&filter, frame->can_id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
can_sja_dual_filter(&filter,
|
||||
s->code_mask + 2, s->code_mask + 6, 1);
|
||||
|
||||
if (can_bus_filter_match(&filter, frame->can_id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
can_sja_dual_filter(&filter,
|
||||
s->code_mask + 0, s->code_mask + 4, 0);
|
||||
|
||||
if (can_bus_filter_match(&filter, frame->can_id)) {
|
||||
uint8_t expect;
|
||||
uint8_t mask;
|
||||
expect = s->code_mask[1] << 4;
|
||||
expect |= s->code_mask[3] & 0x0f;
|
||||
|
||||
mask = s->code_mask[5] << 4;
|
||||
mask |= s->code_mask[7] & 0x0f;
|
||||
mask = ~mask & 0xff;
|
||||
|
||||
if ((frame->data[0] & mask) ==
|
||||
(expect & mask)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
can_sja_dual_filter(&filter,
|
||||
s->code_mask + 2, s->code_mask + 6, 0);
|
||||
|
||||
if (can_bus_filter_match(&filter, frame->can_id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void can_display_msg(const char *prefix, const qemu_can_frame *msg)
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_log_lock();
|
||||
qemu_log("%s%03X [%01d] %s %s",
|
||||
prefix,
|
||||
msg->can_id & QEMU_CAN_EFF_MASK,
|
||||
msg->can_dlc,
|
||||
msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
|
||||
msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");
|
||||
|
||||
for (i = 0; i < msg->can_dlc; i++) {
|
||||
qemu_log(" %02X", msg->data[i]);
|
||||
}
|
||||
qemu_log("\n");
|
||||
qemu_log_flush();
|
||||
qemu_log_unlock();
|
||||
}
|
||||
|
||||
static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
frame->can_id = 0;
|
||||
if (buff[0] & 0x40) { /* RTR */
|
||||
frame->can_id = QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
frame->can_dlc = buff[0] & 0x0f;
|
||||
|
||||
if (buff[0] & 0x80) { /* Extended */
|
||||
frame->can_id |= QEMU_CAN_EFF_FLAG;
|
||||
frame->can_id |= buff[1] << 21; /* ID.28~ID.21 */
|
||||
frame->can_id |= buff[2] << 13; /* ID.20~ID.13 */
|
||||
frame->can_id |= buff[3] << 5;
|
||||
frame->can_id |= buff[4] >> 3;
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
frame->data[i] = buff[5 + i];
|
||||
}
|
||||
for (; i < 8; i++) {
|
||||
frame->data[i] = 0;
|
||||
}
|
||||
} else {
|
||||
frame->can_id |= buff[1] << 3;
|
||||
frame->can_id |= buff[2] >> 5;
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
frame->data[i] = buff[3 + i];
|
||||
}
|
||||
for (; i < 8; i++) {
|
||||
frame->data[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void buff2frame_bas(const uint8_t *buff, qemu_can_frame *frame)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
frame->can_id = ((buff[0] << 3) & (0xff << 3)) + ((buff[1] >> 5) & 0x07);
|
||||
if (buff[1] & 0x10) { /* RTR */
|
||||
frame->can_id = QEMU_CAN_RTR_FLAG;
|
||||
}
|
||||
frame->can_dlc = buff[1] & 0x0f;
|
||||
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
frame->data[i] = buff[2 + i];
|
||||
}
|
||||
for (; i < 8; i++) {
|
||||
frame->data[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int frame2buff_pel(const qemu_can_frame *frame, uint8_t *buff)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (frame->can_id & QEMU_CAN_ERR_FLAG) { /* error frame, NOT support now. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
buff[0] = 0x0f & frame->can_dlc; /* DLC */
|
||||
if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
|
||||
buff[0] |= (1 << 6);
|
||||
}
|
||||
if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
|
||||
buff[0] |= (1 << 7);
|
||||
buff[1] = extract32(frame->can_id, 21, 8); /* ID.28~ID.21 */
|
||||
buff[2] = extract32(frame->can_id, 13, 8); /* ID.20~ID.13 */
|
||||
buff[3] = extract32(frame->can_id, 5, 8); /* ID.12~ID.05 */
|
||||
buff[4] = extract32(frame->can_id, 0, 5) << 3; /* ID.04~ID.00,xxx */
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
buff[5 + i] = frame->data[i];
|
||||
}
|
||||
return frame->can_dlc + 5;
|
||||
} else { /* SFF */
|
||||
buff[1] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
|
||||
buff[2] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
buff[3 + i] = frame->data[i];
|
||||
}
|
||||
|
||||
return frame->can_dlc + 3;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* EFF, no support for BasicMode
|
||||
* No use for Error frames now,
|
||||
* they could be used in future to update SJA1000 error state
|
||||
*/
|
||||
if ((frame->can_id & QEMU_CAN_EFF_FLAG) ||
|
||||
(frame->can_id & QEMU_CAN_ERR_FLAG)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buff[0] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
|
||||
buff[1] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
|
||||
if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
|
||||
buff[1] |= (1 << 4);
|
||||
}
|
||||
buff[1] |= frame->can_dlc & 0x0f;
|
||||
for (i = 0; i < frame->can_dlc; i++) {
|
||||
buff[2 + i] = frame->data[i];
|
||||
}
|
||||
|
||||
return frame->can_dlc + 2;
|
||||
}
|
||||
|
||||
static void can_sja_update_pel_irq(CanSJA1000State *s)
|
||||
{
|
||||
if (s->interrupt_en & s->interrupt_pel) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void can_sja_update_bas_irq(CanSJA1000State *s)
|
||||
{
|
||||
if ((s->control >> 1) & s->interrupt_bas) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
qemu_can_frame frame;
|
||||
uint32_t tmp;
|
||||
uint8_t tmp8, count;
|
||||
|
||||
|
||||
DPRINTF("write 0x%02llx addr 0x%02x\n",
|
||||
(unsigned long long)val, (unsigned int)addr);
|
||||
|
||||
if (addr > CAN_SJA_MEM_SIZE) {
|
||||
return ;
|
||||
}
|
||||
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
switch (addr) {
|
||||
case SJA_MOD: /* Mode register */
|
||||
s->mode = 0x1f & val;
|
||||
if ((s->mode & 0x01) && ((val & 0x01) == 0)) {
|
||||
/* Go to operation mode from reset mode. */
|
||||
if (s->mode & (1 << 3)) { /* Single mode. */
|
||||
/* For EFF */
|
||||
can_sja_single_filter(&s->filter[0],
|
||||
s->code_mask + 0, s->code_mask + 4, 1);
|
||||
|
||||
/* For SFF */
|
||||
can_sja_single_filter(&s->filter[1],
|
||||
s->code_mask + 0, s->code_mask + 4, 0);
|
||||
|
||||
can_bus_client_set_filters(&s->bus_client, s->filter, 2);
|
||||
} else { /* Dual mode */
|
||||
/* For EFF */
|
||||
can_sja_dual_filter(&s->filter[0],
|
||||
s->code_mask + 0, s->code_mask + 4, 1);
|
||||
|
||||
can_sja_dual_filter(&s->filter[1],
|
||||
s->code_mask + 2, s->code_mask + 6, 1);
|
||||
|
||||
/* For SFF */
|
||||
can_sja_dual_filter(&s->filter[2],
|
||||
s->code_mask + 0, s->code_mask + 4, 0);
|
||||
|
||||
can_sja_dual_filter(&s->filter[3],
|
||||
s->code_mask + 2, s->code_mask + 6, 0);
|
||||
|
||||
can_bus_client_set_filters(&s->bus_client, s->filter, 4);
|
||||
}
|
||||
|
||||
s->rxmsg_cnt = 0;
|
||||
s->rx_cnt = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case SJA_CMR: /* Command register. */
|
||||
if (0x01 & val) { /* Send transmission request. */
|
||||
buff2frame_pel(s->tx_buff, &frame);
|
||||
if (DEBUG_FILTER) {
|
||||
can_display_msg("[cansja]: Tx request " , &frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear transmission complete status,
|
||||
* and Transmit Buffer Status.
|
||||
* write to the backends.
|
||||
*/
|
||||
s->status_pel &= ~(3 << 2);
|
||||
|
||||
can_bus_client_send(&s->bus_client, &frame, 1);
|
||||
|
||||
/*
|
||||
* Set transmission complete status
|
||||
* and Transmit Buffer Status.
|
||||
*/
|
||||
s->status_pel |= (3 << 2);
|
||||
|
||||
/* Clear transmit status. */
|
||||
s->status_pel &= ~(1 << 5);
|
||||
s->interrupt_pel |= 0x02;
|
||||
can_sja_update_pel_irq(s);
|
||||
}
|
||||
if (0x04 & val) { /* Release Receive Buffer */
|
||||
if (s->rxmsg_cnt <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
tmp8 = s->rx_buff[s->rxbuf_start]; count = 0;
|
||||
if (tmp8 & (1 << 7)) { /* EFF */
|
||||
count += 2;
|
||||
}
|
||||
count += 3;
|
||||
if (!(tmp8 & (1 << 6))) { /* DATA */
|
||||
count += (tmp8 & 0x0f);
|
||||
}
|
||||
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message released from "
|
||||
"Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
|
||||
}
|
||||
|
||||
s->rxbuf_start += count;
|
||||
s->rxbuf_start %= SJA_RCV_BUF_LEN;
|
||||
|
||||
s->rx_cnt -= count;
|
||||
s->rxmsg_cnt--;
|
||||
if (s->rxmsg_cnt == 0) {
|
||||
s->status_pel &= ~(1 << 0);
|
||||
s->interrupt_pel &= ~(1 << 0);
|
||||
can_sja_update_pel_irq(s);
|
||||
}
|
||||
}
|
||||
if (0x08 & val) { /* Clear data overrun */
|
||||
s->status_pel &= ~(1 << 1);
|
||||
s->interrupt_pel &= ~(1 << 3);
|
||||
can_sja_update_pel_irq(s);
|
||||
}
|
||||
break;
|
||||
case SJA_SR: /* Status register */
|
||||
case SJA_IR: /* Interrupt register */
|
||||
break; /* Do nothing */
|
||||
case SJA_IER: /* Interrupt enable register */
|
||||
s->interrupt_en = val;
|
||||
break;
|
||||
case 16: /* RX frame information addr16-28. */
|
||||
s->status_pel |= (1 << 5); /* Set transmit status. */
|
||||
case 17 ... 28:
|
||||
if (s->mode & 0x01) { /* Reset mode */
|
||||
if (addr < 24) {
|
||||
s->code_mask[addr - 16] = val;
|
||||
}
|
||||
} else { /* Operation mode */
|
||||
s->tx_buff[addr - 16] = val; /* Store to TX buffer directly. */
|
||||
}
|
||||
break;
|
||||
case SJA_CDR:
|
||||
s->clock = val;
|
||||
break;
|
||||
}
|
||||
} else { /* Basic Mode */
|
||||
switch (addr) {
|
||||
case SJA_BCAN_CTR: /* Control register, addr 0 */
|
||||
if ((s->control & 0x01) && ((val & 0x01) == 0)) {
|
||||
/* Go to operation mode from reset mode. */
|
||||
s->filter[0].can_id = (s->code << 3) & (0xff << 3);
|
||||
tmp = (~(s->mask << 3)) & (0xff << 3);
|
||||
tmp |= QEMU_CAN_EFF_FLAG; /* Only Basic CAN Frame. */
|
||||
s->filter[0].can_mask = tmp;
|
||||
can_bus_client_set_filters(&s->bus_client, s->filter, 1);
|
||||
|
||||
s->rxmsg_cnt = 0;
|
||||
s->rx_cnt = 0;
|
||||
} else if (!(s->control & 0x01) && !(val & 0x01)) {
|
||||
can_sja_software_reset(s);
|
||||
}
|
||||
|
||||
s->control = 0x1f & val;
|
||||
break;
|
||||
case SJA_BCAN_CMR: /* Command register, addr 1 */
|
||||
if (0x01 & val) { /* Send transmission request. */
|
||||
buff2frame_bas(s->tx_buff, &frame);
|
||||
if (DEBUG_FILTER) {
|
||||
can_display_msg("[cansja]: Tx request " , &frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear transmission complete status,
|
||||
* and Transmit Buffer Status.
|
||||
*/
|
||||
s->status_bas &= ~(3 << 2);
|
||||
|
||||
/* write to the backends. */
|
||||
can_bus_client_send(&s->bus_client, &frame, 1);
|
||||
|
||||
/*
|
||||
* Set transmission complete status,
|
||||
* and Transmit Buffer Status.
|
||||
*/
|
||||
s->status_bas |= (3 << 2);
|
||||
|
||||
/* Clear transmit status. */
|
||||
s->status_bas &= ~(1 << 5);
|
||||
s->interrupt_bas |= 0x02;
|
||||
can_sja_update_bas_irq(s);
|
||||
}
|
||||
if (0x04 & val) { /* Release Receive Buffer */
|
||||
if (s->rxmsg_cnt <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
tmp8 = s->rx_buff[(s->rxbuf_start + 1) % SJA_RCV_BUF_LEN];
|
||||
count = 2 + (tmp8 & 0x0f);
|
||||
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message released from "
|
||||
"Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
|
||||
}
|
||||
|
||||
s->rxbuf_start += count;
|
||||
s->rxbuf_start %= SJA_RCV_BUF_LEN;
|
||||
s->rx_cnt -= count;
|
||||
s->rxmsg_cnt--;
|
||||
|
||||
if (s->rxmsg_cnt == 0) {
|
||||
s->status_bas &= ~(1 << 0);
|
||||
s->interrupt_bas &= ~(1 << 0);
|
||||
can_sja_update_bas_irq(s);
|
||||
}
|
||||
}
|
||||
if (0x08 & val) { /* Clear data overrun */
|
||||
s->status_bas &= ~(1 << 1);
|
||||
s->interrupt_bas &= ~(1 << 3);
|
||||
can_sja_update_bas_irq(s);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
s->code = val;
|
||||
break;
|
||||
case 5:
|
||||
s->mask = val;
|
||||
break;
|
||||
case 10:
|
||||
s->status_bas |= (1 << 5); /* Set transmit status. */
|
||||
case 11 ... 19:
|
||||
if ((s->control & 0x01) == 0) { /* Operation mode */
|
||||
s->tx_buff[addr - 10] = val; /* Store to TX buffer directly. */
|
||||
}
|
||||
break;
|
||||
case SJA_CDR:
|
||||
s->clock = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size)
|
||||
{
|
||||
uint64_t temp = 0;
|
||||
|
||||
DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
|
||||
|
||||
if (addr > CAN_SJA_MEM_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
switch (addr) {
|
||||
case SJA_MOD: /* Mode register, addr 0 */
|
||||
temp = s->mode;
|
||||
break;
|
||||
case SJA_CMR: /* Command register, addr 1 */
|
||||
temp = 0x00; /* Command register, cannot be read. */
|
||||
break;
|
||||
case SJA_SR: /* Status register, addr 2 */
|
||||
temp = s->status_pel;
|
||||
break;
|
||||
case SJA_IR: /* Interrupt register, addr 3 */
|
||||
temp = s->interrupt_pel;
|
||||
s->interrupt_pel = 0;
|
||||
if (s->rxmsg_cnt) {
|
||||
s->interrupt_pel |= (1 << 0); /* Receive interrupt. */
|
||||
}
|
||||
can_sja_update_pel_irq(s);
|
||||
break;
|
||||
case SJA_IER: /* Interrupt enable register, addr 4 */
|
||||
temp = s->interrupt_en;
|
||||
break;
|
||||
case 5: /* Reserved */
|
||||
case 6: /* Bus timing 0, hardware related, not support now. */
|
||||
case 7: /* Bus timing 1, hardware related, not support now. */
|
||||
case 8: /*
|
||||
* Output control register, hardware related,
|
||||
* not supported for now.
|
||||
*/
|
||||
case 9: /* Test. */
|
||||
case 10 ... 15: /* Reserved */
|
||||
temp = 0x00;
|
||||
break;
|
||||
|
||||
case 16 ... 28:
|
||||
if (s->mode & 0x01) { /* Reset mode */
|
||||
if (addr < 24) {
|
||||
temp = s->code_mask[addr - 16];
|
||||
} else {
|
||||
temp = 0x00;
|
||||
}
|
||||
} else { /* Operation mode */
|
||||
temp = s->rx_buff[(s->rxbuf_start + addr - 16) %
|
||||
SJA_RCV_BUF_LEN];
|
||||
}
|
||||
break;
|
||||
case SJA_CDR:
|
||||
temp = s->clock;
|
||||
break;
|
||||
default:
|
||||
temp = 0xff;
|
||||
}
|
||||
} else { /* Basic Mode */
|
||||
switch (addr) {
|
||||
case SJA_BCAN_CTR: /* Control register, addr 0 */
|
||||
temp = s->control;
|
||||
break;
|
||||
case SJA_BCAN_SR: /* Status register, addr 2 */
|
||||
temp = s->status_bas;
|
||||
break;
|
||||
case SJA_BCAN_IR: /* Interrupt register, addr 3 */
|
||||
temp = s->interrupt_bas;
|
||||
s->interrupt_bas = 0;
|
||||
if (s->rxmsg_cnt) {
|
||||
s->interrupt_bas |= (1 << 0); /* Receive interrupt. */
|
||||
}
|
||||
can_sja_update_bas_irq(s);
|
||||
break;
|
||||
case 4:
|
||||
temp = s->code;
|
||||
break;
|
||||
case 5:
|
||||
temp = s->mask;
|
||||
break;
|
||||
case 20 ... 29:
|
||||
temp = s->rx_buff[(s->rxbuf_start + addr - 20) % SJA_RCV_BUF_LEN];
|
||||
break;
|
||||
case 31:
|
||||
temp = s->clock;
|
||||
break;
|
||||
default:
|
||||
temp = 0xff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DPRINTF("read addr 0x%02x, %d bytes, content 0x%02lx\n",
|
||||
(int)addr, size, (long unsigned int)temp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
int can_sja_can_receive(CanBusClientState *client)
|
||||
{
|
||||
CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
|
||||
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
if (s->mode & 0x01) { /* reset mode. */
|
||||
return 0;
|
||||
}
|
||||
} else { /* BasicCAN mode */
|
||||
if (s->control & 0x01) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1; /* always return 1, when operation mode */
|
||||
}
|
||||
|
||||
ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames,
|
||||
size_t frames_cnt)
|
||||
{
|
||||
CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
|
||||
static uint8_t rcv[SJA_MSG_MAX_LEN];
|
||||
int i;
|
||||
int ret = -1;
|
||||
const qemu_can_frame *frame = frames;
|
||||
|
||||
if (frames_cnt <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (DEBUG_FILTER) {
|
||||
can_display_msg("[cansja]: receive ", frame);
|
||||
}
|
||||
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
|
||||
/* the CAN controller is receiving a message */
|
||||
s->status_pel |= (1 << 4);
|
||||
|
||||
if (can_sja_accept_filter(s, frame) == 0) {
|
||||
s->status_pel &= ~(1 << 4);
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: filter rejects message\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = frame2buff_pel(frame, rcv);
|
||||
if (ret < 0) {
|
||||
s->status_pel &= ~(1 << 4);
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message store failed\n");
|
||||
}
|
||||
return ret; /* maybe not support now. */
|
||||
}
|
||||
|
||||
if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
|
||||
s->status_pel |= (1 << 1); /* Overrun status */
|
||||
s->interrupt_pel |= (1 << 3);
|
||||
s->status_pel &= ~(1 << 4);
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: receive FIFO overrun\n");
|
||||
}
|
||||
can_sja_update_pel_irq(s);
|
||||
return ret;
|
||||
}
|
||||
s->rx_cnt += ret;
|
||||
s->rxmsg_cnt++;
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message stored in receive FIFO\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
|
||||
}
|
||||
s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
|
||||
|
||||
s->status_pel |= 0x01; /* Set the Receive Buffer Status. DS-p23 */
|
||||
s->interrupt_pel |= 0x01;
|
||||
s->status_pel &= ~(1 << 4);
|
||||
s->status_pel |= (1 << 0);
|
||||
can_sja_update_pel_irq(s);
|
||||
} else { /* BasicCAN mode */
|
||||
|
||||
/* the CAN controller is receiving a message */
|
||||
s->status_bas |= (1 << 4);
|
||||
|
||||
ret = frame2buff_bas(frame, rcv);
|
||||
if (ret < 0) {
|
||||
s->status_bas &= ~(1 << 4);
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message store failed\n");
|
||||
}
|
||||
return ret; /* maybe not support now. */
|
||||
}
|
||||
|
||||
if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
|
||||
s->status_bas |= (1 << 1); /* Overrun status */
|
||||
s->status_bas &= ~(1 << 4);
|
||||
s->interrupt_bas |= (1 << 3);
|
||||
can_sja_update_bas_irq(s);
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: receive FIFO overrun\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
s->rx_cnt += ret;
|
||||
s->rxmsg_cnt++;
|
||||
|
||||
if (DEBUG_FILTER) {
|
||||
qemu_log("[cansja]: message stored\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
|
||||
}
|
||||
s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
|
||||
|
||||
s->status_bas |= 0x01; /* Set the Receive Buffer Status. DS-p15 */
|
||||
s->status_bas &= ~(1 << 4);
|
||||
s->interrupt_bas |= (1 << 0);
|
||||
can_sja_update_bas_irq(s);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static CanBusClientInfo can_sja_bus_client_info = {
|
||||
.can_receive = can_sja_can_receive,
|
||||
.receive = can_sja_receive,
|
||||
};
|
||||
|
||||
|
||||
int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus)
|
||||
{
|
||||
s->bus_client.info = &can_sja_bus_client_info;
|
||||
|
||||
if (can_bus_insert_client(bus, &s->bus_client) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void can_sja_disconnect(CanSJA1000State *s)
|
||||
{
|
||||
can_bus_remove_client(&s->bus_client);
|
||||
}
|
||||
|
||||
int can_sja_init(CanSJA1000State *s, qemu_irq irq)
|
||||
{
|
||||
s->irq = irq;
|
||||
|
||||
qemu_irq_lower(s->irq);
|
||||
|
||||
can_sja_hardware_reset(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_qemu_can_filter = {
|
||||
.name = "qemu_can_filter",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(can_id, qemu_can_filter),
|
||||
VMSTATE_UINT32(can_mask, qemu_can_filter),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int can_sja_post_load(void *opaque, int version_id)
|
||||
{
|
||||
CanSJA1000State *s = opaque;
|
||||
if (s->clock & 0x80) { /* PeliCAN Mode */
|
||||
can_sja_update_pel_irq(s);
|
||||
} else {
|
||||
can_sja_update_bas_irq(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* VMState is needed for live migration of QEMU images */
|
||||
const VMStateDescription vmstate_can_sja = {
|
||||
.name = "can_sja",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.post_load = can_sja_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(mode, CanSJA1000State),
|
||||
|
||||
VMSTATE_UINT8(status_pel, CanSJA1000State),
|
||||
VMSTATE_UINT8(interrupt_pel, CanSJA1000State),
|
||||
VMSTATE_UINT8(interrupt_en, CanSJA1000State),
|
||||
VMSTATE_UINT8(rxmsg_cnt, CanSJA1000State),
|
||||
VMSTATE_UINT8(rxbuf_start, CanSJA1000State),
|
||||
VMSTATE_UINT8(clock, CanSJA1000State),
|
||||
|
||||
VMSTATE_BUFFER(code_mask, CanSJA1000State),
|
||||
VMSTATE_BUFFER(tx_buff, CanSJA1000State),
|
||||
|
||||
VMSTATE_BUFFER(rx_buff, CanSJA1000State),
|
||||
|
||||
VMSTATE_UINT32(rx_ptr, CanSJA1000State),
|
||||
VMSTATE_UINT32(rx_cnt, CanSJA1000State),
|
||||
|
||||
VMSTATE_UINT8(control, CanSJA1000State),
|
||||
|
||||
VMSTATE_UINT8(status_bas, CanSJA1000State),
|
||||
VMSTATE_UINT8(interrupt_bas, CanSJA1000State),
|
||||
VMSTATE_UINT8(code, CanSJA1000State),
|
||||
VMSTATE_UINT8(mask, CanSJA1000State),
|
||||
|
||||
VMSTATE_STRUCT_ARRAY(filter, CanSJA1000State, 4, 0,
|
||||
vmstate_qemu_can_filter, qemu_can_filter),
|
||||
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
146
hw/net/can/can_sja1000.h
Normal file
146
hw/net/can/can_sja1000.h
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* CAN device - SJA1000 chip emulation for QEMU
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef HW_CAN_SJA1000_H
|
||||
#define HW_CAN_SJA1000_H
|
||||
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#define CAN_SJA_MEM_SIZE 128
|
||||
|
||||
/* The max size for a message buffer, EFF and DLC=8, DS-p39 */
|
||||
#define SJA_MSG_MAX_LEN 13
|
||||
/* The receive buffer size. */
|
||||
#define SJA_RCV_BUF_LEN 64
|
||||
|
||||
typedef struct CanSJA1000State {
|
||||
/* PeliCAN state and registers sorted by address */
|
||||
uint8_t mode; /* 0 .. Mode register, DS-p26 */
|
||||
/* 1 .. Command register */
|
||||
uint8_t status_pel; /* 2 .. Status register, p15 */
|
||||
uint8_t interrupt_pel; /* 3 .. Interrupt register */
|
||||
uint8_t interrupt_en; /* 4 .. Interrupt Enable register */
|
||||
uint8_t rxmsg_cnt; /* 29 .. RX message counter. DS-p49 */
|
||||
uint8_t rxbuf_start; /* 30 .. RX buffer start address, DS-p49 */
|
||||
uint8_t clock; /* 31 .. Clock Divider register, DS-p55 */
|
||||
|
||||
uint8_t code_mask[8]; /* 16~23 */
|
||||
uint8_t tx_buff[13]; /* 96~108 .. transmit buffer */
|
||||
/* 10~19 .. transmit buffer for BasicCAN */
|
||||
|
||||
uint8_t rx_buff[SJA_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */
|
||||
uint32_t rx_ptr; /* Count by bytes. */
|
||||
uint32_t rx_cnt; /* Count by bytes. */
|
||||
|
||||
/* PeliCAN state and registers sorted by address */
|
||||
uint8_t control; /* 0 .. Control register */
|
||||
/* 1 .. Command register */
|
||||
uint8_t status_bas; /* 2 .. Status register */
|
||||
uint8_t interrupt_bas; /* 3 .. Interrupt register */
|
||||
uint8_t code; /* 4 .. Acceptance code register */
|
||||
uint8_t mask; /* 5 .. Acceptance mask register */
|
||||
|
||||
qemu_can_filter filter[4];
|
||||
|
||||
qemu_irq irq;
|
||||
CanBusClientState bus_client;
|
||||
} CanSJA1000State;
|
||||
|
||||
/* PeliCAN mode */
|
||||
enum SJA1000_PeliCAN_regs {
|
||||
SJA_MOD = 0x00, /* Mode control register */
|
||||
SJA_CMR = 0x01, /* Command register */
|
||||
SJA_SR = 0x02, /* Status register */
|
||||
SJA_IR = 0x03, /* Interrupt register */
|
||||
SJA_IER = 0x04, /* Interrupt Enable */
|
||||
SJA_BTR0 = 0x06, /* Bus Timing register 0 */
|
||||
SJA_BTR1 = 0x07, /* Bus Timing register 1 */
|
||||
SJA_OCR = 0x08, /* Output Control register */
|
||||
SJA_ALC = 0x0b, /* Arbitration Lost Capture */
|
||||
SJA_ECC = 0x0c, /* Error Code Capture */
|
||||
SJA_EWLR = 0x0d, /* Error Warning Limit */
|
||||
SJA_RXERR = 0x0e, /* RX Error Counter */
|
||||
SJA_TXERR0 = 0x0e, /* TX Error Counter */
|
||||
SJA_TXERR1 = 0x0f,
|
||||
SJA_RMC = 0x1d, /* Rx Message Counter
|
||||
* number of messages in RX FIFO
|
||||
*/
|
||||
SJA_RBSA = 0x1e, /* Rx Buffer Start Addr
|
||||
* address of current message
|
||||
*/
|
||||
SJA_FRM = 0x10, /* Transmit Buffer
|
||||
* write: Receive Buffer
|
||||
* read: Frame Information
|
||||
*/
|
||||
/*
|
||||
* ID bytes (11 bits in 0 and 1 for standard message or
|
||||
* 16 bits in 0,1 and 13 bits in 2,3 for extended message)
|
||||
* The most significant bit of ID is placed in MSB
|
||||
* position of ID0 register.
|
||||
*/
|
||||
SJA_ID0 = 0x11, /* ID for standard and extended frames */
|
||||
SJA_ID1 = 0x12,
|
||||
SJA_ID2 = 0x13, /* ID cont. for extended frames */
|
||||
SJA_ID3 = 0x14,
|
||||
|
||||
SJA_DATS = 0x13, /* Data start standard frame */
|
||||
SJA_DATE = 0x15, /* Data start extended frame */
|
||||
SJA_ACR0 = 0x10, /* Acceptance Code (4 bytes) in RESET mode */
|
||||
SJA_AMR0 = 0x14, /* Acceptance Mask (4 bytes) in RESET mode */
|
||||
SJA_PeliCAN_AC_LEN = 4, /* 4 bytes */
|
||||
SJA_CDR = 0x1f /* Clock Divider */
|
||||
};
|
||||
|
||||
|
||||
/* BasicCAN mode */
|
||||
enum SJA1000_BasicCAN_regs {
|
||||
SJA_BCAN_CTR = 0x00, /* Control register */
|
||||
SJA_BCAN_CMR = 0x01, /* Command register */
|
||||
SJA_BCAN_SR = 0x02, /* Status register */
|
||||
SJA_BCAN_IR = 0x03 /* Interrupt register */
|
||||
};
|
||||
|
||||
void can_sja_hardware_reset(CanSJA1000State *s);
|
||||
|
||||
void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
|
||||
unsigned size);
|
||||
|
||||
uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size);
|
||||
|
||||
int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus);
|
||||
|
||||
void can_sja_disconnect(CanSJA1000State *s);
|
||||
|
||||
int can_sja_init(CanSJA1000State *s, qemu_irq irq);
|
||||
|
||||
int can_sja_can_receive(CanBusClientState *client);
|
||||
|
||||
ssize_t can_sja_receive(CanBusClientState *client,
|
||||
const qemu_can_frame *frames, size_t frames_cnt);
|
||||
|
||||
extern const VMStateDescription vmstate_can_sja;
|
||||
|
||||
#endif
|
61
hw/sd/core.c
61
hw/sd/core.c
@ -23,6 +23,12 @@
|
||||
#include "hw/qdev-core.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/sd/sd.h"
|
||||
#include "trace.h"
|
||||
|
||||
static inline const char *sdbus_name(SDBus *sdbus)
|
||||
{
|
||||
return sdbus->qbus.name;
|
||||
}
|
||||
|
||||
static SDState *get_card(SDBus *sdbus)
|
||||
{
|
||||
@ -35,10 +41,58 @@ static SDState *get_card(SDBus *sdbus)
|
||||
return SD_CARD(kid->child);
|
||||
}
|
||||
|
||||
uint8_t sdbus_get_dat_lines(SDBus *sdbus)
|
||||
{
|
||||
SDState *slave = get_card(sdbus);
|
||||
uint8_t dat_lines = 0b1111; /* 4 bit bus width */
|
||||
|
||||
if (slave) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(slave);
|
||||
|
||||
if (sc->get_dat_lines) {
|
||||
dat_lines = sc->get_dat_lines(slave);
|
||||
}
|
||||
}
|
||||
trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines);
|
||||
|
||||
return dat_lines;
|
||||
}
|
||||
|
||||
bool sdbus_get_cmd_line(SDBus *sdbus)
|
||||
{
|
||||
SDState *slave = get_card(sdbus);
|
||||
bool cmd_line = true;
|
||||
|
||||
if (slave) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(slave);
|
||||
|
||||
if (sc->get_cmd_line) {
|
||||
cmd_line = sc->get_cmd_line(slave);
|
||||
}
|
||||
}
|
||||
trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line);
|
||||
|
||||
return cmd_line;
|
||||
}
|
||||
|
||||
void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
|
||||
trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts);
|
||||
if (card) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(card);
|
||||
|
||||
assert(sc->set_voltage);
|
||||
sc->set_voltage(card, millivolts);
|
||||
}
|
||||
}
|
||||
|
||||
int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
|
||||
trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg, req->crc);
|
||||
if (card) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(card);
|
||||
|
||||
@ -52,6 +106,7 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
|
||||
trace_sdbus_write(sdbus_name(sdbus), value);
|
||||
if (card) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(card);
|
||||
|
||||
@ -62,14 +117,16 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
|
||||
uint8_t sdbus_read_data(SDBus *sdbus)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
uint8_t value = 0;
|
||||
|
||||
if (card) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(card);
|
||||
|
||||
return sc->read_data(card);
|
||||
value = sc->read_data(card);
|
||||
}
|
||||
trace_sdbus_read(sdbus_name(sdbus), value);
|
||||
|
||||
return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
bool sdbus_data_ready(SDBus *sdbus)
|
||||
|
29
hw/sd/sd.c
29
hw/sd/sd.c
@ -126,8 +126,32 @@ struct SDState {
|
||||
BlockBackend *blk;
|
||||
|
||||
bool enable;
|
||||
uint8_t dat_lines;
|
||||
bool cmd_line;
|
||||
};
|
||||
|
||||
static uint8_t sd_get_dat_lines(SDState *sd)
|
||||
{
|
||||
return sd->enable ? sd->dat_lines : 0;
|
||||
}
|
||||
|
||||
static bool sd_get_cmd_line(SDState *sd)
|
||||
{
|
||||
return sd->enable ? sd->cmd_line : false;
|
||||
}
|
||||
|
||||
static void sd_set_voltage(SDState *sd, uint16_t millivolts)
|
||||
{
|
||||
switch (millivolts) {
|
||||
case 3001 ... 3600: /* SD_VOLTAGE_3_3V */
|
||||
case 2001 ... 3000: /* SD_VOLTAGE_3_0V */
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "SD card voltage not supported: %.3fV",
|
||||
millivolts / 1000.f);
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_set_mode(SDState *sd)
|
||||
{
|
||||
switch (sd->state) {
|
||||
@ -445,6 +469,8 @@ static void sd_reset(DeviceState *dev)
|
||||
sd->blk_len = 0x200;
|
||||
sd->pwd_len = 0;
|
||||
sd->expecting_acmd = false;
|
||||
sd->dat_lines = 0xf;
|
||||
sd->cmd_line = true;
|
||||
sd->multi_blk_cnt = 0;
|
||||
}
|
||||
|
||||
@ -1926,6 +1952,9 @@ static void sd_class_init(ObjectClass *klass, void *data)
|
||||
dc->reset = sd_reset;
|
||||
dc->bus_type = TYPE_SD_BUS;
|
||||
|
||||
sc->set_voltage = sd_set_voltage;
|
||||
sc->get_dat_lines = sd_get_dat_lines;
|
||||
sc->get_cmd_line = sd_get_cmd_line;
|
||||
sc->do_command = sd_do_command;
|
||||
sc->write_data = sd_write_data;
|
||||
sc->read_data = sd_read_data;
|
||||
|
@ -24,6 +24,8 @@
|
||||
#ifndef SDHCI_INTERNAL_H
|
||||
#define SDHCI_INTERNAL_H
|
||||
|
||||
#include "hw/registerfields.h"
|
||||
|
||||
/* R/W SDMA System Address register 0x0 */
|
||||
#define SDHC_SYSAD 0x00
|
||||
|
||||
@ -41,6 +43,7 @@
|
||||
#define SDHC_TRNS_DMA 0x0001
|
||||
#define SDHC_TRNS_BLK_CNT_EN 0x0002
|
||||
#define SDHC_TRNS_ACMD12 0x0004
|
||||
#define SDHC_TRNS_ACMD23 0x0008 /* since v3 */
|
||||
#define SDHC_TRNS_READ 0x0010
|
||||
#define SDHC_TRNS_MULTI 0x0020
|
||||
#define SDHC_TRNMOD_MASK 0x0037
|
||||
@ -79,15 +82,19 @@
|
||||
#define SDHC_CARD_PRESENT 0x00010000
|
||||
#define SDHC_CARD_DETECT 0x00040000
|
||||
#define SDHC_WRITE_PROTECT 0x00080000
|
||||
FIELD(SDHC_PRNSTS, DAT_LVL, 20, 4);
|
||||
FIELD(SDHC_PRNSTS, CMD_LVL, 24, 1);
|
||||
#define TRANSFERRING_DATA(x) \
|
||||
((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE))
|
||||
|
||||
/* R/W Host control Register 0x0 */
|
||||
#define SDHC_HOSTCTL 0x28
|
||||
#define SDHC_CTRL_LED 0x01
|
||||
#define SDHC_CTRL_DATATRANSFERWIDTH 0x02 /* SD mode only */
|
||||
#define SDHC_CTRL_HIGH_SPEED 0x04
|
||||
#define SDHC_CTRL_DMA_CHECK_MASK 0x18
|
||||
#define SDHC_CTRL_SDMA 0x00
|
||||
#define SDHC_CTRL_ADMA1_32 0x08
|
||||
#define SDHC_CTRL_ADMA1_32 0x08 /* NOT ALLOWED since v2 */
|
||||
#define SDHC_CTRL_ADMA2_32 0x10
|
||||
#define SDHC_CTRL_ADMA2_64 0x18
|
||||
#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK)
|
||||
@ -96,10 +103,10 @@
|
||||
#define SDHC_CTRL_CDTEST_INS 0x40
|
||||
#define SDHC_CTRL_CDTEST_EN 0x80
|
||||
|
||||
|
||||
/* R/W Power Control Register 0x0 */
|
||||
#define SDHC_PWRCON 0x29
|
||||
#define SDHC_POWER_ON (1 << 0)
|
||||
FIELD(SDHC_PWRCON, BUS_VOLTAGE, 1, 3);
|
||||
|
||||
/* R/W Block Gap Control Register 0x0 */
|
||||
#define SDHC_BLKGAP 0x2A
|
||||
@ -122,6 +129,7 @@
|
||||
|
||||
/* R/W Timeout Control Register 0x0 */
|
||||
#define SDHC_TIMEOUTCON 0x2E
|
||||
FIELD(SDHC_TIMEOUTCON, COUNTER, 0, 4);
|
||||
|
||||
/* R/W Software Reset Register 0x0 */
|
||||
#define SDHC_SWRST 0x2F
|
||||
@ -178,17 +186,62 @@
|
||||
|
||||
/* ROC Auto CMD12 error status register 0x0 */
|
||||
#define SDHC_ACMD12ERRSTS 0x3C
|
||||
FIELD(SDHC_ACMD12ERRSTS, TIMEOUT_ERR, 1, 1);
|
||||
FIELD(SDHC_ACMD12ERRSTS, CRC_ERR, 2, 1);
|
||||
FIELD(SDHC_ACMD12ERRSTS, INDEX_ERR, 4, 1);
|
||||
|
||||
/* Host Control Register 2 (since v3) */
|
||||
#define SDHC_HOSTCTL2 0x3E
|
||||
FIELD(SDHC_HOSTCTL2, UHS_MODE_SEL, 0, 3);
|
||||
FIELD(SDHC_HOSTCTL2, V18_ENA, 3, 1); /* UHS-I only */
|
||||
FIELD(SDHC_HOSTCTL2, DRIVER_STRENGTH, 4, 2); /* UHS-I only */
|
||||
FIELD(SDHC_HOSTCTL2, EXECUTE_TUNING, 6, 1); /* UHS-I only */
|
||||
FIELD(SDHC_HOSTCTL2, SAMPLING_CLKSEL, 7, 1); /* UHS-I only */
|
||||
FIELD(SDHC_HOSTCTL2, UHS_II_ENA, 8, 1); /* since v4 */
|
||||
FIELD(SDHC_HOSTCTL2, ADMA2_LENGTH, 10, 1); /* since v4 */
|
||||
FIELD(SDHC_HOSTCTL2, CMD23_ENA, 11, 1); /* since v4 */
|
||||
FIELD(SDHC_HOSTCTL2, VERSION4, 12, 1); /* since v4 */
|
||||
FIELD(SDHC_HOSTCTL2, ASYNC_INT, 14, 1);
|
||||
FIELD(SDHC_HOSTCTL2, PRESET_ENA, 15, 1);
|
||||
|
||||
/* HWInit Capabilities Register 0x05E80080 */
|
||||
#define SDHC_CAPAB 0x40
|
||||
#define SDHC_CAN_DO_DMA 0x00400000
|
||||
#define SDHC_CAN_DO_ADMA2 0x00080000
|
||||
#define SDHC_CAN_DO_ADMA1 0x00100000
|
||||
#define SDHC_64_BIT_BUS_SUPPORT (1 << 28)
|
||||
#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3)
|
||||
FIELD(SDHC_CAPAB, TOCLKFREQ, 0, 6);
|
||||
FIELD(SDHC_CAPAB, TOUNIT, 7, 1);
|
||||
FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8);
|
||||
FIELD(SDHC_CAPAB, MAXBLOCKLENGTH, 16, 2);
|
||||
FIELD(SDHC_CAPAB, EMBEDDED_8BIT, 18, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, ADMA2, 19, 1); /* since v2 */
|
||||
FIELD(SDHC_CAPAB, ADMA1, 20, 1); /* v1 only? */
|
||||
FIELD(SDHC_CAPAB, HIGHSPEED, 21, 1);
|
||||
FIELD(SDHC_CAPAB, SDMA, 22, 1);
|
||||
FIELD(SDHC_CAPAB, SUSPRESUME, 23, 1);
|
||||
FIELD(SDHC_CAPAB, V33, 24, 1);
|
||||
FIELD(SDHC_CAPAB, V30, 25, 1);
|
||||
FIELD(SDHC_CAPAB, V18, 26, 1);
|
||||
FIELD(SDHC_CAPAB, BUS64BIT_V4, 27, 1); /* since v4.10 */
|
||||
FIELD(SDHC_CAPAB, BUS64BIT, 28, 1); /* since v2 */
|
||||
FIELD(SDHC_CAPAB, ASYNC_INT, 29, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, SLOT_TYPE, 30, 2); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, BUS_SPEED, 32, 3); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, UHS_II, 35, 8); /* since v4.20 */
|
||||
FIELD(SDHC_CAPAB, DRIVER_STRENGTH, 36, 3); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, DRIVER_TYPE_A, 36, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, DRIVER_TYPE_C, 37, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, DRIVER_TYPE_D, 38, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, TIMER_RETUNING, 40, 4); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, SDR50_TUNING, 45, 1); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, RETUNING_MODE, 46, 2); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, CLOCK_MULT, 48, 8); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, ADMA3, 59, 1); /* since v4.20 */
|
||||
FIELD(SDHC_CAPAB, V18_VDD2, 60, 1); /* since v4.20 */
|
||||
|
||||
/* HWInit Maximum Current Capabilities Register 0x0 */
|
||||
#define SDHC_MAXCURR 0x48
|
||||
FIELD(SDHC_MAXCURR, V33_VDD1, 0, 8);
|
||||
FIELD(SDHC_MAXCURR, V30_VDD1, 8, 8);
|
||||
FIELD(SDHC_MAXCURR, V18_VDD1, 16, 8);
|
||||
FIELD(SDHC_MAXCURR, V18_VDD2, 32, 8); /* since v4.20 */
|
||||
|
||||
/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */
|
||||
#define SDHC_FEAER 0x50
|
||||
@ -216,9 +269,9 @@
|
||||
/* Slot interrupt status */
|
||||
#define SDHC_SLOT_INT_STATUS 0xFC
|
||||
|
||||
/* HWInit Host Controller Version Register 0x0401 */
|
||||
/* HWInit Host Controller Version Register */
|
||||
#define SDHC_HCVER 0xFE
|
||||
#define SD_HOST_SPECv2_VERS 0x2401
|
||||
#define SDHC_HCVER_VENDOR 0x24
|
||||
|
||||
#define SDHC_REGISTERS_MAP_SIZE 0x100
|
||||
#define SDHC_INSERTION_DELAY (NANOSECONDS_PER_SECOND)
|
||||
|
387
hw/sd/sdhci.c
387
hw/sd/sdhci.c
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
@ -33,72 +34,194 @@
|
||||
#include "hw/sd/sdhci.h"
|
||||
#include "sdhci-internal.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define TYPE_SDHCI_BUS "sdhci-bus"
|
||||
#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
|
||||
|
||||
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
||||
|
||||
/* Default SD/MMC host controller features information, which will be
|
||||
* presented in CAPABILITIES register of generic SD host controller at reset.
|
||||
* If not stated otherwise:
|
||||
* 0 - not supported, 1 - supported, other - prohibited.
|
||||
*
|
||||
* support:
|
||||
* - 3.3v and 1.8v voltages
|
||||
* - SDMA/ADMA1/ADMA2
|
||||
* - high-speed
|
||||
* max host controller R/W buffers size: 512B
|
||||
* max clock frequency for SDclock: 52 MHz
|
||||
* timeout clock frequency: 52 MHz
|
||||
*
|
||||
* does not support:
|
||||
* - 3.0v voltage
|
||||
* - 64-bit system bus
|
||||
* - suspend/resume
|
||||
*/
|
||||
#define SDHC_CAPAB_64BITBUS 0ul /* 64-bit System Bus Support */
|
||||
#define SDHC_CAPAB_18V 1ul /* Voltage support 1.8v */
|
||||
#define SDHC_CAPAB_30V 0ul /* Voltage support 3.0v */
|
||||
#define SDHC_CAPAB_33V 1ul /* Voltage support 3.3v */
|
||||
#define SDHC_CAPAB_SUSPRESUME 0ul /* Suspend/resume support */
|
||||
#define SDHC_CAPAB_SDMA 1ul /* SDMA support */
|
||||
#define SDHC_CAPAB_HIGHSPEED 1ul /* High speed support */
|
||||
#define SDHC_CAPAB_ADMA1 1ul /* ADMA1 support */
|
||||
#define SDHC_CAPAB_ADMA2 1ul /* ADMA2 support */
|
||||
/* Maximum host controller R/W buffers size
|
||||
* Possible values: 512, 1024, 2048 bytes */
|
||||
#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
|
||||
/* Maximum clock frequency for SDclock in MHz
|
||||
* value in range 10-63 MHz, 0 - not defined */
|
||||
#define SDHC_CAPAB_BASECLKFREQ 52ul
|
||||
#define SDHC_CAPAB_TOUNIT 1ul /* Timeout clock unit 0 - kHz, 1 - MHz */
|
||||
/* Timeout clock frequency 1-63, 0 - not defined */
|
||||
#define SDHC_CAPAB_TOCLKFREQ 52ul
|
||||
#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
|
||||
|
||||
/* Now check all parameters and calculate CAPABILITIES REGISTER value */
|
||||
#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 || \
|
||||
SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 || \
|
||||
SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
|
||||
SDHC_CAPAB_TOUNIT > 1
|
||||
#error Capabilities features can have value 0 or 1 only!
|
||||
#endif
|
||||
static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
|
||||
{
|
||||
return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
|
||||
}
|
||||
|
||||
#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
|
||||
#define MAX_BLOCK_LENGTH 0ul
|
||||
#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
|
||||
#define MAX_BLOCK_LENGTH 1ul
|
||||
#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
|
||||
#define MAX_BLOCK_LENGTH 2ul
|
||||
#else
|
||||
#error Max host controller block size can have value 512, 1024 or 2048 only!
|
||||
#endif
|
||||
/* return true on error */
|
||||
static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc,
|
||||
uint8_t freq, Error **errp)
|
||||
{
|
||||
if (s->sd_spec_version >= 3) {
|
||||
return false;
|
||||
}
|
||||
switch (freq) {
|
||||
case 0:
|
||||
case 10 ... 63:
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "SD %s clock frequency can have value"
|
||||
"in range 0-63 only", desc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
|
||||
SDHC_CAPAB_BASECLKFREQ > 63
|
||||
#error SDclock frequency can have value in range 0, 10-63 only!
|
||||
#endif
|
||||
static void sdhci_check_capareg(SDHCIState *s, Error **errp)
|
||||
{
|
||||
uint64_t msk = s->capareg;
|
||||
uint32_t val;
|
||||
bool y;
|
||||
|
||||
#if SDHC_CAPAB_TOCLKFREQ > 63
|
||||
#error Timeout clock frequency can have value in range 0-63 only!
|
||||
#endif
|
||||
switch (s->sd_spec_version) {
|
||||
case 4:
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT_V4);
|
||||
trace_sdhci_capareg("64-bit system bus (v4)", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT_V4, 0);
|
||||
|
||||
#define SDHC_CAPAB_REG_DEFAULT \
|
||||
((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) | \
|
||||
(SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) | \
|
||||
(SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) | \
|
||||
(SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) | \
|
||||
(SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) | \
|
||||
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
|
||||
(SDHC_CAPAB_TOCLKFREQ))
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, UHS_II);
|
||||
trace_sdhci_capareg("UHS-II", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, UHS_II, 0);
|
||||
|
||||
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA3);
|
||||
trace_sdhci_capareg("ADMA3", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA3, 0);
|
||||
|
||||
/* fallthrough */
|
||||
case 3:
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, ASYNC_INT);
|
||||
trace_sdhci_capareg("async interrupt", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, ASYNC_INT, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, SLOT_TYPE);
|
||||
if (val) {
|
||||
error_setg(errp, "slot-type not supported");
|
||||
return;
|
||||
}
|
||||
trace_sdhci_capareg("slot type", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, SLOT_TYPE, 0);
|
||||
|
||||
if (val != 2) {
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, EMBEDDED_8BIT);
|
||||
trace_sdhci_capareg("8-bit bus", val);
|
||||
}
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, EMBEDDED_8BIT, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS_SPEED);
|
||||
trace_sdhci_capareg("bus speed mask", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, BUS_SPEED, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, DRIVER_STRENGTH);
|
||||
trace_sdhci_capareg("driver strength mask", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, DRIVER_STRENGTH, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, TIMER_RETUNING);
|
||||
trace_sdhci_capareg("timer re-tuning", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, TIMER_RETUNING, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDR50_TUNING);
|
||||
trace_sdhci_capareg("use SDR50 tuning", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, SDR50_TUNING, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, RETUNING_MODE);
|
||||
trace_sdhci_capareg("re-tuning mode", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, RETUNING_MODE, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, CLOCK_MULT);
|
||||
trace_sdhci_capareg("clock multiplier", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, CLOCK_MULT, 0);
|
||||
|
||||
/* fallthrough */
|
||||
case 2: /* default version */
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA2);
|
||||
trace_sdhci_capareg("ADMA2", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA2, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA1);
|
||||
trace_sdhci_capareg("ADMA1", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA1, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT);
|
||||
trace_sdhci_capareg("64-bit system bus (v3)", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT, 0);
|
||||
|
||||
/* fallthrough */
|
||||
case 1:
|
||||
y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ);
|
||||
trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val);
|
||||
if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) {
|
||||
return;
|
||||
}
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ);
|
||||
trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val);
|
||||
if (sdhci_check_capab_freq_range(s, "base", val, errp)) {
|
||||
return;
|
||||
}
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH);
|
||||
if (val >= 3) {
|
||||
error_setg(errp, "block size can be 512, 1024 or 2048 only");
|
||||
return;
|
||||
}
|
||||
trace_sdhci_capareg("max block length", sdhci_get_fifolen(s));
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, MAXBLOCKLENGTH, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, HIGHSPEED);
|
||||
trace_sdhci_capareg("high speed", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, HIGHSPEED, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDMA);
|
||||
trace_sdhci_capareg("SDMA", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, SDMA, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, SUSPRESUME);
|
||||
trace_sdhci_capareg("suspend/resume", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, SUSPRESUME, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, V33);
|
||||
trace_sdhci_capareg("3.3v", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, V33, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, V30);
|
||||
trace_sdhci_capareg("3.0v", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, V30, 0);
|
||||
|
||||
val = FIELD_EX64(s->capareg, SDHC_CAPAB, V18);
|
||||
trace_sdhci_capareg("1.8v", val);
|
||||
msk = FIELD_DP64(msk, SDHC_CAPAB, V18, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(errp, "Unsupported spec version: %u", s->sd_spec_version);
|
||||
}
|
||||
if (msk) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"SDHCI: unknown CAPAB mask: 0x%016" PRIx64 "\n", msk);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t sdhci_slotint(SDHCIState *s)
|
||||
{
|
||||
@ -173,7 +296,8 @@ static void sdhci_reset(SDHCIState *s)
|
||||
|
||||
timer_del(s->insert_timer);
|
||||
timer_del(s->transfer_timer);
|
||||
/* Set all registers to 0. Capabilities registers are not cleared
|
||||
|
||||
/* Set all registers to 0. Capabilities/Version registers are not cleared
|
||||
* and assumed to always preserve their value, given to them during
|
||||
* initialization */
|
||||
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
|
||||
@ -292,19 +416,35 @@ static void sdhci_end_transfer(SDHCIState *s)
|
||||
/*
|
||||
* Programmed i/o data transfer
|
||||
*/
|
||||
#define BLOCK_SIZE_MASK (4 * K_BYTE - 1)
|
||||
|
||||
/* Fill host controller's read buffer with BLKSIZE bytes of data from card */
|
||||
static void sdhci_read_block_from_card(SDHCIState *s)
|
||||
{
|
||||
int index = 0;
|
||||
uint8_t data;
|
||||
const uint16_t blk_size = s->blksize & BLOCK_SIZE_MASK;
|
||||
|
||||
if ((s->trnmod & SDHC_TRNS_MULTI) &&
|
||||
(s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (index = 0; index < (s->blksize & 0x0fff); index++) {
|
||||
s->fifo_buffer[index] = sdbus_read_data(&s->sdbus);
|
||||
for (index = 0; index < blk_size; index++) {
|
||||
data = sdbus_read_data(&s->sdbus);
|
||||
if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is not in tunning */
|
||||
s->fifo_buffer[index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
|
||||
/* Device is in tunning */
|
||||
s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
|
||||
s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
|
||||
s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
|
||||
SDHC_DATA_INHIBIT);
|
||||
goto read_done;
|
||||
}
|
||||
|
||||
/* New data now available for READ through Buffer Port Register */
|
||||
@ -329,6 +469,7 @@ static void sdhci_read_block_from_card(SDHCIState *s)
|
||||
}
|
||||
}
|
||||
|
||||
read_done:
|
||||
sdhci_update_irq(s);
|
||||
}
|
||||
|
||||
@ -348,7 +489,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
|
||||
value |= s->fifo_buffer[s->data_count] << i * 8;
|
||||
s->data_count++;
|
||||
/* 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 & BLOCK_SIZE_MASK)) {
|
||||
trace_sdhci_read_dataport(s->data_count);
|
||||
s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
|
||||
s->data_count = 0; /* next buff read must start at position [0] */
|
||||
@ -395,7 +536,7 @@ static void sdhci_write_block_to_card(SDHCIState *s)
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0; index < (s->blksize & 0x0fff); index++) {
|
||||
for (index = 0; index < (s->blksize & BLOCK_SIZE_MASK); index++) {
|
||||
sdbus_write_data(&s->sdbus, s->fifo_buffer[index]);
|
||||
}
|
||||
|
||||
@ -440,7 +581,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
|
||||
s->fifo_buffer[s->data_count] = value & 0xFF;
|
||||
s->data_count++;
|
||||
value >>= 8;
|
||||
if (s->data_count >= (s->blksize & 0x0fff)) {
|
||||
if (s->data_count >= (s->blksize & BLOCK_SIZE_MASK)) {
|
||||
trace_sdhci_write_dataport(s->data_count);
|
||||
s->data_count = 0;
|
||||
s->prnsts &= ~SDHC_SPACE_AVAILABLE;
|
||||
@ -460,8 +601,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||
{
|
||||
bool page_aligned = false;
|
||||
unsigned int n, begin;
|
||||
const uint16_t block_size = s->blksize & 0x0fff;
|
||||
uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
|
||||
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
|
||||
uint32_t boundary_chk = 1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
|
||||
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
|
||||
|
||||
if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
|
||||
@ -550,7 +691,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
|
||||
{
|
||||
int n;
|
||||
uint32_t datacnt = s->blksize & 0x0fff;
|
||||
uint32_t datacnt = s->blksize & BLOCK_SIZE_MASK;
|
||||
|
||||
if (s->trnmod & SDHC_TRNS_READ) {
|
||||
for (n = 0; n < datacnt; n++) {
|
||||
@ -580,7 +721,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||
uint32_t adma1 = 0;
|
||||
uint64_t adma2 = 0;
|
||||
hwaddr entry_addr = (hwaddr)s->admasysaddr;
|
||||
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
||||
switch (SDHC_DMA_TYPE(s->hostctl1)) {
|
||||
case SDHC_CTRL_ADMA2_32:
|
||||
dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2,
|
||||
sizeof(adma2));
|
||||
@ -614,8 +755,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||
dscr->length = le16_to_cpu(dscr->length);
|
||||
dma_memory_read(s->dma_as, entry_addr + 4,
|
||||
(uint8_t *)(&dscr->addr), 8);
|
||||
dscr->attr = le64_to_cpu(dscr->attr);
|
||||
dscr->attr &= 0xfffffff8;
|
||||
dscr->addr = le64_to_cpu(dscr->addr);
|
||||
dscr->attr &= (uint8_t) ~0xC0;
|
||||
dscr->incr = 12;
|
||||
break;
|
||||
}
|
||||
@ -626,7 +767,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
|
||||
static void sdhci_do_adma(SDHCIState *s)
|
||||
{
|
||||
unsigned int n, begin, length;
|
||||
const uint16_t block_size = s->blksize & 0x0fff;
|
||||
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
|
||||
ADMADescr dscr = {};
|
||||
int i;
|
||||
|
||||
@ -769,7 +910,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||
SDHCIState *s = (SDHCIState *)opaque;
|
||||
|
||||
if (s->trnmod & SDHC_TRNS_DMA) {
|
||||
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
||||
switch (SDHC_DMA_TYPE(s->hostctl1)) {
|
||||
case SDHC_CTRL_SDMA:
|
||||
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
|
||||
sdhci_sdma_transfer_single_block(s);
|
||||
@ -779,7 +920,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||
|
||||
break;
|
||||
case SDHC_CTRL_ADMA1_32:
|
||||
if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
|
||||
if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) {
|
||||
trace_sdhci_error("ADMA1 not supported");
|
||||
break;
|
||||
}
|
||||
@ -787,7 +928,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||
sdhci_do_adma(s);
|
||||
break;
|
||||
case SDHC_CTRL_ADMA2_32:
|
||||
if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
|
||||
if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK)) {
|
||||
trace_sdhci_error("ADMA2 not supported");
|
||||
break;
|
||||
}
|
||||
@ -795,8 +936,8 @@ static void sdhci_data_transfer(void *opaque)
|
||||
sdhci_do_adma(s);
|
||||
break;
|
||||
case SDHC_CTRL_ADMA2_64:
|
||||
if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
|
||||
!(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
|
||||
if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK) ||
|
||||
!(s->capareg & R_SDHC_CAPAB_BUS64BIT_MASK)) {
|
||||
trace_sdhci_error("64 bit ADMA not supported");
|
||||
break;
|
||||
}
|
||||
@ -876,9 +1017,13 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||
break;
|
||||
case SDHC_PRNSTS:
|
||||
ret = s->prnsts;
|
||||
ret = FIELD_DP32(ret, SDHC_PRNSTS, DAT_LVL,
|
||||
sdbus_get_dat_lines(&s->sdbus));
|
||||
ret = FIELD_DP32(ret, SDHC_PRNSTS, CMD_LVL,
|
||||
sdbus_get_cmd_line(&s->sdbus));
|
||||
break;
|
||||
case SDHC_HOSTCTL:
|
||||
ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) |
|
||||
ret = s->hostctl1 | (s->pwrcon << 8) | (s->blkgap << 16) |
|
||||
(s->wakcon << 24);
|
||||
break;
|
||||
case SDHC_CLKCON:
|
||||
@ -894,7 +1039,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||
ret = s->norintsigen | (s->errintsigen << 16);
|
||||
break;
|
||||
case SDHC_ACMD12ERRSTS:
|
||||
ret = s->acmd12errsts;
|
||||
ret = s->acmd12errsts | (s->hostctl2 << 16);
|
||||
break;
|
||||
case SDHC_CAPAB:
|
||||
ret = (uint32_t)s->capareg;
|
||||
@ -918,7 +1063,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
|
||||
ret = (uint32_t)(s->admasysaddr >> 32);
|
||||
break;
|
||||
case SDHC_SLOT_INT_STATUS:
|
||||
ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
|
||||
ret = (s->version << 16) | sdhci_slotint(s);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SDHC rd_%ub @0x%02" HWADDR_PRIx " "
|
||||
@ -996,7 +1141,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
MASKED_WRITE(s->sdmasysad, mask, value);
|
||||
/* Writing to last byte of sdmasysad might trigger transfer */
|
||||
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
|
||||
s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
|
||||
s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
|
||||
if (s->trnmod & SDHC_TRNS_MULTI) {
|
||||
sdhci_sdma_transfer_multi_blocks(s);
|
||||
} else {
|
||||
@ -1026,7 +1171,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
case SDHC_TRNMOD:
|
||||
/* DMA can be enabled only if it is supported as indicated by
|
||||
* capabilities register */
|
||||
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
|
||||
if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) {
|
||||
value &= ~SDHC_TRNS_DMA;
|
||||
}
|
||||
MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK);
|
||||
@ -1048,7 +1193,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
if (!(mask & 0xFF0000)) {
|
||||
sdhci_blkgap_write(s, value >> 16);
|
||||
}
|
||||
MASKED_WRITE(s->hostctl, mask, value);
|
||||
MASKED_WRITE(s->hostctl1, mask, value);
|
||||
MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8);
|
||||
MASKED_WRITE(s->wakcon, mask >> 24, value >> 24);
|
||||
if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 ||
|
||||
@ -1128,7 +1273,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
sdhci_update_irq(s);
|
||||
break;
|
||||
case SDHC_ACMD12ERRSTS:
|
||||
MASKED_WRITE(s->acmd12errsts, mask, value);
|
||||
MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX);
|
||||
if (s->uhs_mode >= UHS_I) {
|
||||
MASKED_WRITE(s->hostctl2, mask >> 16, value >> 16);
|
||||
|
||||
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, V18_ENA)) {
|
||||
sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_1_8V);
|
||||
} else {
|
||||
sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_3_3V);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDHC_CAPAB:
|
||||
@ -1159,26 +1313,34 @@ static const MemoryRegionOps sdhci_mmio_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
|
||||
static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
|
||||
{
|
||||
switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
|
||||
case 0:
|
||||
return 512;
|
||||
case 1:
|
||||
return 1024;
|
||||
case 2:
|
||||
return 2048;
|
||||
Error *local_err = NULL;
|
||||
|
||||
switch (s->sd_spec_version) {
|
||||
case 2 ... 3:
|
||||
break;
|
||||
default:
|
||||
hw_error("SDHC: unsupported value for maximum block size\n");
|
||||
return 0;
|
||||
error_setg(errp, "Only Spec v2/v3 are supported");
|
||||
return;
|
||||
}
|
||||
s->version = (SDHC_HCVER_VENDOR << 8) | (s->sd_spec_version - 1);
|
||||
|
||||
sdhci_check_capareg(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- qdev common --- */
|
||||
|
||||
#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
|
||||
/* Capabilities registers provide information on supported features
|
||||
* of this specific host controller implementation */ \
|
||||
DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
|
||||
DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \
|
||||
\
|
||||
/* 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)
|
||||
|
||||
@ -1206,6 +1368,13 @@ static void sdhci_uninitfn(SDHCIState *s)
|
||||
|
||||
static void sdhci_common_realize(SDHCIState *s, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
sdhci_init_readonly_registers(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
s->buf_maxsz = sdhci_get_fifolen(s);
|
||||
s->fifo_buffer = g_malloc0(s->buf_maxsz);
|
||||
|
||||
@ -1255,7 +1424,7 @@ const VMStateDescription sdhci_vmstate = {
|
||||
VMSTATE_UINT16(cmdreg, SDHCIState),
|
||||
VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4),
|
||||
VMSTATE_UINT32(prnsts, SDHCIState),
|
||||
VMSTATE_UINT8(hostctl, SDHCIState),
|
||||
VMSTATE_UINT8(hostctl1, SDHCIState),
|
||||
VMSTATE_UINT8(pwrcon, SDHCIState),
|
||||
VMSTATE_UINT8(blkgap, SDHCIState),
|
||||
VMSTATE_UINT8(wakcon, SDHCIState),
|
||||
@ -1302,10 +1471,12 @@ static Property sdhci_pci_properties[] = {
|
||||
static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
SDHCIState *s = PCI_SDHCI(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
sdhci_initfn(s);
|
||||
sdhci_common_realize(s, errp);
|
||||
if (errp && *errp) {
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1383,9 +1554,11 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
|
||||
{
|
||||
SDHCIState *s = SYSBUS_SDHCI(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
sdhci_common_realize(s, errp);
|
||||
if (errp && *errp) {
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1457,7 +1630,7 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
SDHCIState *s = SYSBUS_SDHCI(opaque);
|
||||
uint32_t ret;
|
||||
uint16_t hostctl;
|
||||
uint16_t hostctl1;
|
||||
|
||||
switch (offset) {
|
||||
default:
|
||||
@ -1469,17 +1642,17 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
|
||||
* manipulation code see comments in a similar part of
|
||||
* usdhc_write()
|
||||
*/
|
||||
hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
|
||||
hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
|
||||
|
||||
if (s->hostctl & SDHC_CTRL_8BITBUS) {
|
||||
hostctl |= ESDHC_CTRL_8BITBUS;
|
||||
if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
|
||||
hostctl1 |= ESDHC_CTRL_8BITBUS;
|
||||
}
|
||||
|
||||
if (s->hostctl & SDHC_CTRL_4BITBUS) {
|
||||
hostctl |= ESDHC_CTRL_4BITBUS;
|
||||
if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
|
||||
hostctl1 |= ESDHC_CTRL_4BITBUS;
|
||||
}
|
||||
|
||||
ret = hostctl;
|
||||
ret = hostctl1;
|
||||
ret |= (uint32_t)s->blkgap << 16;
|
||||
ret |= (uint32_t)s->wakcon << 24;
|
||||
|
||||
@ -1503,7 +1676,7 @@ static void
|
||||
usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
SDHCIState *s = SYSBUS_SDHCI(opaque);
|
||||
uint8_t hostctl;
|
||||
uint8_t hostctl1;
|
||||
uint32_t value = (uint32_t)val;
|
||||
|
||||
switch (offset) {
|
||||
@ -1566,25 +1739,25 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
/*
|
||||
* First, save bits 7 6 and 0 since they are identical
|
||||
*/
|
||||
hostctl = value & (SDHC_CTRL_LED |
|
||||
SDHC_CTRL_CDTEST_INS |
|
||||
SDHC_CTRL_CDTEST_EN);
|
||||
hostctl1 = value & (SDHC_CTRL_LED |
|
||||
SDHC_CTRL_CDTEST_INS |
|
||||
SDHC_CTRL_CDTEST_EN);
|
||||
/*
|
||||
* Second, split "Data Transfer Width" from bits 2 and 1 in to
|
||||
* bits 5 and 1
|
||||
*/
|
||||
if (value & ESDHC_CTRL_8BITBUS) {
|
||||
hostctl |= SDHC_CTRL_8BITBUS;
|
||||
hostctl1 |= SDHC_CTRL_8BITBUS;
|
||||
}
|
||||
|
||||
if (value & ESDHC_CTRL_4BITBUS) {
|
||||
hostctl |= ESDHC_CTRL_4BITBUS;
|
||||
hostctl1 |= ESDHC_CTRL_4BITBUS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Third, move DMA select from bits 9 and 8 to bits 4 and 3
|
||||
*/
|
||||
hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
|
||||
hostctl1 |= SDHC_DMA_TYPE(value >> (8 - 3));
|
||||
|
||||
/*
|
||||
* Now place the corrected value into low 16-bit of the value
|
||||
@ -1595,7 +1768,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
* kernel
|
||||
*/
|
||||
value &= ~UINT16_MAX;
|
||||
value |= hostctl;
|
||||
value |= hostctl1;
|
||||
value |= (uint16_t)s->pwrcon << 8;
|
||||
|
||||
sdhci_write(opaque, offset, value, size);
|
||||
|
@ -1,5 +1,13 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# hw/sd/core.c
|
||||
sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg, uint8_t crc) "@%s CMD%02d arg 0x%08x crc 0x%02x"
|
||||
sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x"
|
||||
sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x"
|
||||
sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)"
|
||||
sdbus_get_dat_lines(const char *bus_name, uint8_t dat_lines) "@%s dat_lines: %u"
|
||||
sdbus_get_cmd_line(const char *bus_name, bool cmd_line) "@%s cmd_line: %u"
|
||||
|
||||
# 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]"
|
||||
@ -13,6 +21,7 @@ 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"
|
||||
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
|
||||
|
||||
# hw/sd/milkymist-memcard.c
|
||||
milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
|
@ -1090,33 +1090,15 @@ bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
hwaddr size);
|
||||
|
||||
/**
|
||||
* memory_region_test_and_clear_dirty: Check whether a range of bytes is dirty
|
||||
* for a specified client. It clears them.
|
||||
*
|
||||
* Checks whether a range of bytes has been written to since the last
|
||||
* call to memory_region_reset_dirty() with the same @client. Dirty logging
|
||||
* must be enabled.
|
||||
*
|
||||
* @mr: the memory region being queried.
|
||||
* @addr: the address (relative to the start of the region) being queried.
|
||||
* @size: the size of the range being queried.
|
||||
* @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
|
||||
* %DIRTY_MEMORY_VGA.
|
||||
*/
|
||||
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
hwaddr size, unsigned client);
|
||||
|
||||
/**
|
||||
* memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty
|
||||
* bitmap and clear it.
|
||||
*
|
||||
* Creates a snapshot of the dirty bitmap, clears the dirty bitmap and
|
||||
* returns the snapshot. The snapshot can then be used to query dirty
|
||||
* status, using memory_region_snapshot_get_dirty. Unlike
|
||||
* memory_region_test_and_clear_dirty this allows to query the same
|
||||
* page multiple times, which is especially useful for display updates
|
||||
* where the scanlines often are not page aligned.
|
||||
* status, using memory_region_snapshot_get_dirty. Snapshotting allows
|
||||
* querying the same page multiple times, which is especially useful for
|
||||
* display updates where the scanlines often are not page aligned.
|
||||
*
|
||||
* The dirty bitmap region which gets copyed into the snapshot (and
|
||||
* cleared afterwards) can be larger than requested. The boundaries
|
||||
@ -1153,17 +1135,6 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
|
||||
DirtyBitmapSnapshot *snap,
|
||||
hwaddr addr, hwaddr size);
|
||||
|
||||
/**
|
||||
* memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with
|
||||
* any external TLBs (e.g. kvm)
|
||||
*
|
||||
* Flushes dirty information from accelerators such as kvm and vhost-net
|
||||
* and makes it available to users of the memory API.
|
||||
*
|
||||
* @mr: the region being flushed.
|
||||
*/
|
||||
void memory_region_sync_dirty_bitmap(MemoryRegion *mr);
|
||||
|
||||
/**
|
||||
* memory_region_reset_dirty: Mark a range of pages as clean, for a specified
|
||||
* client.
|
||||
|
@ -55,6 +55,20 @@
|
||||
#define AKE_SEQ_ERROR (1 << 3)
|
||||
#define OCR_CCS_BITN 30
|
||||
|
||||
typedef enum {
|
||||
SD_VOLTAGE_0_4V = 400, /* currently not supported */
|
||||
SD_VOLTAGE_1_8V = 1800,
|
||||
SD_VOLTAGE_3_0V = 3000,
|
||||
SD_VOLTAGE_3_3V = 3300,
|
||||
} sd_voltage_mv_t;
|
||||
|
||||
typedef enum {
|
||||
UHS_NOT_SUPPORTED = 0,
|
||||
UHS_I = 1,
|
||||
UHS_II = 2, /* currently not supported */
|
||||
UHS_III = 3, /* currently not supported */
|
||||
} sd_uhs_mode_t;
|
||||
|
||||
typedef enum {
|
||||
sd_none = -1,
|
||||
sd_bc = 0, /* broadcast -- no response */
|
||||
@ -88,6 +102,9 @@ typedef struct {
|
||||
void (*write_data)(SDState *sd, uint8_t value);
|
||||
uint8_t (*read_data)(SDState *sd);
|
||||
bool (*data_ready)(SDState *sd);
|
||||
void (*set_voltage)(SDState *sd, uint16_t millivolts);
|
||||
uint8_t (*get_dat_lines)(SDState *sd);
|
||||
bool (*get_cmd_line)(SDState *sd);
|
||||
void (*enable)(SDState *sd, bool enable);
|
||||
bool (*get_inserted)(SDState *sd);
|
||||
bool (*get_readonly)(SDState *sd);
|
||||
@ -134,6 +151,9 @@ void sd_enable(SDState *sd, bool enable);
|
||||
/* Functions to be used by qdevified callers (working via
|
||||
* an SDBus rather than directly with SDState)
|
||||
*/
|
||||
void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts);
|
||||
uint8_t sdbus_get_dat_lines(SDBus *sdbus);
|
||||
bool sdbus_get_cmd_line(SDBus *sdbus);
|
||||
int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response);
|
||||
void sdbus_write_data(SDBus *sd, uint8_t value);
|
||||
uint8_t sdbus_read_data(SDBus *sd);
|
||||
|
@ -59,7 +59,7 @@ typedef struct SDHCIState {
|
||||
uint16_t cmdreg; /* Command Register */
|
||||
uint32_t rspreg[4]; /* Response Registers 0-3 */
|
||||
uint32_t prnsts; /* Present State Register */
|
||||
uint8_t hostctl; /* Host Control Register */
|
||||
uint8_t hostctl1; /* Host Control Register */
|
||||
uint8_t pwrcon; /* Power control Register */
|
||||
uint8_t blkgap; /* Block Gap Control Register */
|
||||
uint8_t wakcon; /* WakeUp Control Register */
|
||||
@ -73,11 +73,13 @@ typedef struct SDHCIState {
|
||||
uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */
|
||||
uint16_t errintsigen; /* Error Interrupt Signal Enable Register */
|
||||
uint16_t acmd12errsts; /* Auto CMD12 error status register */
|
||||
uint16_t hostctl2; /* Host Control 2 */
|
||||
uint64_t admasysaddr; /* ADMA System Address Register */
|
||||
|
||||
/* Read-only registers */
|
||||
uint64_t capareg; /* Capabilities Register */
|
||||
uint64_t maxcurr; /* Maximum Current Capabilities Register */
|
||||
uint16_t version; /* Host Controller Version Register */
|
||||
|
||||
uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
|
||||
uint32_t buf_maxsz;
|
||||
@ -93,6 +95,8 @@ typedef struct SDHCIState {
|
||||
/* Configurable properties */
|
||||
bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */
|
||||
uint32_t quirks;
|
||||
uint8_t sd_spec_version;
|
||||
uint8_t uhs_mode;
|
||||
} SDHCIState;
|
||||
|
||||
/*
|
||||
|
123
include/net/can_emu.h
Normal file
123
include/net/can_emu.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* CAN common CAN bus emulation support
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef NET_CAN_EMU_H
|
||||
#define NET_CAN_EMU_H
|
||||
|
||||
#include "qom/object.h"
|
||||
|
||||
/* NOTE: the following two structures is copied from <linux/can.h>. */
|
||||
|
||||
/*
|
||||
* Controller Area Network Identifier structure
|
||||
*
|
||||
* bit 0-28 : CAN identifier (11/29 bit)
|
||||
* bit 29 : error frame flag (0 = data frame, 1 = error frame)
|
||||
* bit 30 : remote transmission request flag (1 = rtr frame)
|
||||
* bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
|
||||
*/
|
||||
typedef uint32_t qemu_canid_t;
|
||||
|
||||
typedef struct qemu_can_frame {
|
||||
qemu_canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||
uint8_t can_dlc; /* data length code: 0 .. 8 */
|
||||
uint8_t data[8] QEMU_ALIGNED(8);
|
||||
} qemu_can_frame;
|
||||
|
||||
/* Keep defines for QEMU separate from Linux ones for now */
|
||||
|
||||
#define QEMU_CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
|
||||
#define QEMU_CAN_RTR_FLAG 0x40000000U /* remote transmission request */
|
||||
#define QEMU_CAN_ERR_FLAG 0x20000000U /* error message frame */
|
||||
|
||||
#define QEMU_CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
|
||||
#define QEMU_CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
|
||||
|
||||
/**
|
||||
* struct qemu_can_filter - CAN ID based filter in can_register().
|
||||
* @can_id: relevant bits of CAN ID which are not masked out.
|
||||
* @can_mask: CAN mask (see description)
|
||||
*
|
||||
* Description:
|
||||
* A filter matches, when
|
||||
*
|
||||
* <received_can_id> & mask == can_id & mask
|
||||
*
|
||||
* The filter can be inverted (QEMU_CAN_INV_FILTER bit set in can_id) or it can
|
||||
* filter for error message frames (QEMU_CAN_ERR_FLAG bit set in mask).
|
||||
*/
|
||||
typedef struct qemu_can_filter {
|
||||
qemu_canid_t can_id;
|
||||
qemu_canid_t can_mask;
|
||||
} qemu_can_filter;
|
||||
|
||||
/* QEMU_CAN_INV_FILTER can be set in qemu_can_filter.can_id */
|
||||
#define QEMU_CAN_INV_FILTER 0x20000000U
|
||||
|
||||
typedef struct CanBusClientState CanBusClientState;
|
||||
typedef struct CanBusState CanBusState;
|
||||
|
||||
typedef struct CanBusClientInfo {
|
||||
int (*can_receive)(CanBusClientState *);
|
||||
ssize_t (*receive)(CanBusClientState *,
|
||||
const struct qemu_can_frame *frames, size_t frames_cnt);
|
||||
} CanBusClientInfo;
|
||||
|
||||
struct CanBusClientState {
|
||||
CanBusClientInfo *info;
|
||||
CanBusState *bus;
|
||||
int link_down;
|
||||
QTAILQ_ENTRY(CanBusClientState) next;
|
||||
CanBusClientState *peer;
|
||||
char *model;
|
||||
char *name;
|
||||
void (*destructor)(CanBusClientState *);
|
||||
};
|
||||
|
||||
#define TYPE_CAN_BUS "can-bus"
|
||||
#define CAN_BUS_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(CanBusClass, (klass), TYPE_CAN_BUS)
|
||||
#define CAN_BUS_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(CanBusClass, (obj), TYPE_CAN_BUS)
|
||||
#define CAN_BUS(obj) \
|
||||
OBJECT_CHECK(CanBusState, (obj), TYPE_CAN_BUS)
|
||||
|
||||
int can_bus_filter_match(struct qemu_can_filter *filter, qemu_canid_t can_id);
|
||||
|
||||
int can_bus_insert_client(CanBusState *bus, CanBusClientState *client);
|
||||
|
||||
int can_bus_remove_client(CanBusClientState *client);
|
||||
|
||||
ssize_t can_bus_client_send(CanBusClientState *,
|
||||
const struct qemu_can_frame *frames,
|
||||
size_t frames_cnt);
|
||||
|
||||
int can_bus_client_set_filters(CanBusClientState *,
|
||||
const struct qemu_can_filter *filters,
|
||||
size_t filters_cnt);
|
||||
|
||||
#endif
|
55
include/net/can_host.h
Normal file
55
include/net/can_host.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* CAN common CAN bus emulation support
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef NET_CAN_HOST_H
|
||||
#define NET_CAN_HOST_H
|
||||
|
||||
#include "net/can_emu.h"
|
||||
|
||||
#define TYPE_CAN_HOST "can-host"
|
||||
#define CAN_HOST_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(CanHostClass, (klass), TYPE_CAN_HOST)
|
||||
#define CAN_HOST_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(CanHostClass, (obj), TYPE_CAN_HOST)
|
||||
#define CAN_HOST(obj) \
|
||||
OBJECT_CHECK(CanHostState, (obj), TYPE_CAN_HOST)
|
||||
|
||||
typedef struct CanHostState {
|
||||
ObjectClass oc;
|
||||
|
||||
CanBusState *bus;
|
||||
CanBusClientState bus_client;
|
||||
} CanHostState;
|
||||
|
||||
typedef struct CanHostClass {
|
||||
ObjectClass oc;
|
||||
|
||||
void (*connect)(CanHostState *ch, Error **errp);
|
||||
void (*disconnect)(CanHostState *ch);
|
||||
} CanHostClass;
|
||||
|
||||
#endif
|
@ -27,7 +27,7 @@
|
||||
int hax_sync_vcpus(void);
|
||||
int hax_init_vcpu(CPUState *cpu);
|
||||
int hax_smp_cpu_exec(CPUState *cpu);
|
||||
int hax_populate_ram(uint64_t va, uint32_t size);
|
||||
int hax_populate_ram(uint64_t va, uint64_t size);
|
||||
|
||||
void hax_cpu_synchronize_state(CPUState *cpu);
|
||||
void hax_cpu_synchronize_post_reset(CPUState *cpu);
|
||||
|
70
memory.c
70
memory.c
@ -1971,33 +1971,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
memory_region_get_dirty_log_mask(mr));
|
||||
}
|
||||
|
||||
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
|
||||
hwaddr size, unsigned client)
|
||||
{
|
||||
assert(mr->ram_block);
|
||||
return cpu_physical_memory_test_and_clear_dirty(
|
||||
memory_region_get_ram_addr(mr) + addr, size, client);
|
||||
}
|
||||
|
||||
DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
hwaddr size,
|
||||
unsigned client)
|
||||
{
|
||||
assert(mr->ram_block);
|
||||
return cpu_physical_memory_snapshot_and_clear_dirty(
|
||||
memory_region_get_ram_addr(mr) + addr, size, client);
|
||||
}
|
||||
|
||||
bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
|
||||
hwaddr addr, hwaddr size)
|
||||
{
|
||||
assert(mr->ram_block);
|
||||
return cpu_physical_memory_snapshot_get_dirty(snap,
|
||||
memory_region_get_ram_addr(mr) + addr, size);
|
||||
}
|
||||
|
||||
void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||
static void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||
{
|
||||
MemoryListener *listener;
|
||||
AddressSpace *as;
|
||||
@ -2016,7 +1990,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||
as = listener->address_space;
|
||||
view = address_space_get_flatview(as);
|
||||
FOR_EACH_FLAT_RANGE(fr, view) {
|
||||
if (fr->mr == mr) {
|
||||
if (fr->dirty_log_mask && (!mr || fr->mr == mr)) {
|
||||
MemoryRegionSection mrs = section_from_flat_range(fr, view);
|
||||
listener->log_sync(listener, &mrs);
|
||||
}
|
||||
@ -2025,6 +1999,25 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||
}
|
||||
}
|
||||
|
||||
DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
|
||||
hwaddr addr,
|
||||
hwaddr size,
|
||||
unsigned client)
|
||||
{
|
||||
assert(mr->ram_block);
|
||||
memory_region_sync_dirty_bitmap(mr);
|
||||
return cpu_physical_memory_snapshot_and_clear_dirty(
|
||||
memory_region_get_ram_addr(mr) + addr, size, client);
|
||||
}
|
||||
|
||||
bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
|
||||
hwaddr addr, hwaddr size)
|
||||
{
|
||||
assert(mr->ram_block);
|
||||
return cpu_physical_memory_snapshot_get_dirty(snap,
|
||||
memory_region_get_ram_addr(mr) + addr, size);
|
||||
}
|
||||
|
||||
void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
|
||||
{
|
||||
if (mr->readonly != readonly) {
|
||||
@ -2513,26 +2506,7 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr)
|
||||
|
||||
void memory_global_dirty_log_sync(void)
|
||||
{
|
||||
MemoryListener *listener;
|
||||
AddressSpace *as;
|
||||
FlatView *view;
|
||||
FlatRange *fr;
|
||||
|
||||
QTAILQ_FOREACH(listener, &memory_listeners, link) {
|
||||
if (!listener->log_sync) {
|
||||
continue;
|
||||
}
|
||||
as = listener->address_space;
|
||||
view = address_space_get_flatview(as);
|
||||
FOR_EACH_FLAT_RANGE(fr, view) {
|
||||
if (fr->dirty_log_mask) {
|
||||
MemoryRegionSection mrs = section_from_flat_range(fr, view);
|
||||
|
||||
listener->log_sync(listener, &mrs);
|
||||
}
|
||||
}
|
||||
flatview_unref(view);
|
||||
}
|
||||
memory_region_sync_dirty_bitmap(NULL);
|
||||
}
|
||||
|
||||
static VMChangeStateEntry *vmstate_change;
|
||||
|
@ -23,3 +23,5 @@ common-obj-$(CONFIG_POSIX) += tap.o $(tap-obj-y)
|
||||
common-obj-$(CONFIG_WIN32) += tap-win32.o
|
||||
|
||||
vde.o-libs = $(VDE_LIBS)
|
||||
|
||||
common-obj-$(CONFIG_CAN_BUS) += can/
|
||||
|
2
net/can/Makefile.objs
Normal file
2
net/can/Makefile.objs
Normal file
@ -0,0 +1,2 @@
|
||||
common-obj-y += can_core.o can_host.o
|
||||
common-obj-$(CONFIG_LINUX) += can_socketcan.o
|
138
net/can/can_core.c
Normal file
138
net/can/can_core.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* CAN common CAN bus emulation support
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "net/can_emu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
struct CanBusState {
|
||||
Object object;
|
||||
|
||||
QTAILQ_HEAD(, CanBusClientState) clients;
|
||||
};
|
||||
|
||||
static void can_bus_instance_init(Object *object)
|
||||
{
|
||||
CanBusState *bus = (CanBusState *)object;
|
||||
|
||||
QTAILQ_INIT(&bus->clients);
|
||||
}
|
||||
|
||||
int can_bus_insert_client(CanBusState *bus, CanBusClientState *client)
|
||||
{
|
||||
client->bus = bus;
|
||||
QTAILQ_INSERT_TAIL(&bus->clients, client, next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int can_bus_remove_client(CanBusClientState *client)
|
||||
{
|
||||
CanBusState *bus = client->bus;
|
||||
if (bus == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&bus->clients, client, next);
|
||||
client->bus = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t can_bus_client_send(CanBusClientState *client,
|
||||
const struct qemu_can_frame *frames, size_t frames_cnt)
|
||||
{
|
||||
int ret = 0;
|
||||
CanBusState *bus = client->bus;
|
||||
CanBusClientState *peer;
|
||||
if (bus == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(peer, &bus->clients, next) {
|
||||
if (peer->info->can_receive(peer)) {
|
||||
if (peer == client) {
|
||||
/* No loopback support for now */
|
||||
continue;
|
||||
}
|
||||
if (peer->info->receive(peer, frames, frames_cnt) > 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int can_bus_filter_match(struct qemu_can_filter *filter, qemu_canid_t can_id)
|
||||
{
|
||||
int m;
|
||||
if (((can_id | filter->can_mask) & QEMU_CAN_ERR_FLAG)) {
|
||||
return (filter->can_mask & QEMU_CAN_ERR_FLAG) != 0;
|
||||
}
|
||||
m = (can_id & filter->can_mask) == (filter->can_id & filter->can_mask);
|
||||
return filter->can_id & QEMU_CAN_INV_FILTER ? !m : m;
|
||||
}
|
||||
|
||||
int can_bus_client_set_filters(CanBusClientState *client,
|
||||
const struct qemu_can_filter *filters, size_t filters_cnt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool can_bus_can_be_deleted(UserCreatable *uc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void can_bus_class_init(ObjectClass *klass,
|
||||
void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
|
||||
|
||||
uc_klass->can_be_deleted = can_bus_can_be_deleted;
|
||||
}
|
||||
|
||||
static const TypeInfo can_bus_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_CAN_BUS,
|
||||
.instance_size = sizeof(CanBusState),
|
||||
.instance_init = can_bus_instance_init,
|
||||
.class_init = can_bus_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void can_bus_register_types(void)
|
||||
{
|
||||
type_register_static(&can_bus_info);
|
||||
}
|
||||
|
||||
type_init(can_bus_register_types);
|
112
net/can/can_host.c
Normal file
112
net/can/can_host.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* CAN generic CAN host connection support
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "net/can_emu.h"
|
||||
#include "net/can_host.h"
|
||||
|
||||
struct CanBusState {
|
||||
Object object;
|
||||
|
||||
QTAILQ_HEAD(, CanBusClientState) clients;
|
||||
};
|
||||
|
||||
static void can_host_disconnect(CanHostState *ch)
|
||||
{
|
||||
CanHostClass *chc = CAN_HOST_GET_CLASS(ch);
|
||||
|
||||
can_bus_remove_client(&ch->bus_client);
|
||||
chc->disconnect(ch);
|
||||
}
|
||||
|
||||
static void can_host_connect(CanHostState *ch, Error **errp)
|
||||
{
|
||||
CanHostClass *chc = CAN_HOST_GET_CLASS(ch);
|
||||
Error *local_err = NULL;
|
||||
|
||||
chc->connect(ch, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
can_bus_insert_client(ch->bus, &ch->bus_client);
|
||||
}
|
||||
|
||||
static void can_host_unparent(Object *obj)
|
||||
{
|
||||
can_host_disconnect(CAN_HOST(obj));
|
||||
}
|
||||
|
||||
static void can_host_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
can_host_connect(CAN_HOST(uc), errp);
|
||||
}
|
||||
|
||||
static void can_host_instance_init(Object *obj)
|
||||
{
|
||||
CanHostState *ch = CAN_HOST(obj);
|
||||
|
||||
object_property_add_link(obj, "canbus", TYPE_CAN_BUS,
|
||||
(Object **)&ch->bus,
|
||||
object_property_allow_set_link,
|
||||
OBJ_PROP_LINK_UNREF_ON_RELEASE,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void can_host_class_init(ObjectClass *klass,
|
||||
void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
|
||||
|
||||
klass->unparent = can_host_unparent;
|
||||
uc_klass->complete = can_host_complete;
|
||||
}
|
||||
|
||||
static const TypeInfo can_host_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_CAN_HOST,
|
||||
.instance_size = sizeof(CanHostState),
|
||||
.class_size = sizeof(CanHostClass),
|
||||
.abstract = true,
|
||||
.instance_init = can_host_instance_init,
|
||||
.class_init = can_host_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void can_host_register_types(void)
|
||||
{
|
||||
type_register_static(&can_host_info);
|
||||
}
|
||||
|
||||
type_init(can_host_register_types);
|
286
net/can/can_socketcan.c
Normal file
286
net/can/can_socketcan.c
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* CAN c support to connect to the Linux host SocketCAN interfaces
|
||||
*
|
||||
* Copyright (c) 2013-2014 Jin Yang
|
||||
* Copyright (c) 2014-2018 Pavel Pisa
|
||||
*
|
||||
* Initial development supported by Google GSoC 2013 from RTEMS project slot
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "net/can_emu.h"
|
||||
#include "net/can_host.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
|
||||
#ifndef DEBUG_CAN
|
||||
#define DEBUG_CAN 0
|
||||
#endif /*DEBUG_CAN*/
|
||||
|
||||
#define TYPE_CAN_HOST_SOCKETCAN "can-host-socketcan"
|
||||
#define CAN_HOST_SOCKETCAN(obj) \
|
||||
OBJECT_CHECK(CanHostSocketCAN, (obj), TYPE_CAN_HOST_SOCKETCAN)
|
||||
|
||||
#define CAN_READ_BUF_LEN 5
|
||||
typedef struct CanHostSocketCAN {
|
||||
CanHostState parent;
|
||||
char *ifname;
|
||||
|
||||
qemu_can_filter *rfilter;
|
||||
int rfilter_num;
|
||||
can_err_mask_t err_mask;
|
||||
|
||||
qemu_can_frame buf[CAN_READ_BUF_LEN];
|
||||
int bufcnt;
|
||||
int bufptr;
|
||||
|
||||
int fd;
|
||||
} CanHostSocketCAN;
|
||||
|
||||
/* Check that QEMU and Linux kernel flags encoding and structure matches */
|
||||
QEMU_BUILD_BUG_ON(QEMU_CAN_EFF_FLAG != CAN_EFF_FLAG);
|
||||
QEMU_BUILD_BUG_ON(QEMU_CAN_RTR_FLAG != CAN_RTR_FLAG);
|
||||
QEMU_BUILD_BUG_ON(QEMU_CAN_ERR_FLAG != CAN_ERR_FLAG);
|
||||
QEMU_BUILD_BUG_ON(QEMU_CAN_INV_FILTER != CAN_INV_FILTER);
|
||||
QEMU_BUILD_BUG_ON(offsetof(qemu_can_frame, data)
|
||||
!= offsetof(struct can_frame, data));
|
||||
|
||||
static void can_host_socketcan_display_msg(struct qemu_can_frame *msg)
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_log_lock();
|
||||
qemu_log("[cansocketcan]: %03X [%01d] %s %s",
|
||||
msg->can_id & QEMU_CAN_EFF_MASK,
|
||||
msg->can_dlc,
|
||||
msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
|
||||
msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");
|
||||
|
||||
for (i = 0; i < msg->can_dlc; i++) {
|
||||
qemu_log(" %02X", msg->data[i]);
|
||||
}
|
||||
qemu_log("\n");
|
||||
qemu_log_flush();
|
||||
qemu_log_unlock();
|
||||
}
|
||||
|
||||
static void can_host_socketcan_read(void *opaque)
|
||||
{
|
||||
CanHostSocketCAN *c = opaque;
|
||||
CanHostState *ch = CAN_HOST(c);
|
||||
|
||||
/* CAN_READ_BUF_LEN for multiple messages syscall is possible for future */
|
||||
c->bufcnt = read(c->fd, c->buf, sizeof(qemu_can_frame));
|
||||
if (c->bufcnt < 0) {
|
||||
warn_report("CAN bus host read failed (%s)", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
can_bus_client_send(&ch->bus_client, c->buf, 1);
|
||||
|
||||
if (DEBUG_CAN) {
|
||||
can_host_socketcan_display_msg(c->buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int can_host_socketcan_can_receive(CanBusClientState *client)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t can_host_socketcan_receive(CanBusClientState *client,
|
||||
const qemu_can_frame *frames, size_t frames_cnt)
|
||||
{
|
||||
CanHostState *ch = container_of(client, CanHostState, bus_client);
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
|
||||
|
||||
size_t len = sizeof(qemu_can_frame);
|
||||
int res;
|
||||
|
||||
if (c->fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = write(c->fd, frames, len);
|
||||
|
||||
if (!res) {
|
||||
warn_report("[cansocketcan]: write message to host returns zero");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res != len) {
|
||||
if (res < 0) {
|
||||
warn_report("[cansocketcan]: write to host failed (%s)",
|
||||
strerror(errno));
|
||||
} else {
|
||||
warn_report("[cansocketcan]: write to host truncated");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void can_host_socketcan_disconnect(CanHostState *ch)
|
||||
{
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
|
||||
|
||||
if (c->fd >= 0) {
|
||||
qemu_set_fd_handler(c->fd, NULL, NULL, c);
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
}
|
||||
|
||||
g_free(c->rfilter);
|
||||
c->rfilter = NULL;
|
||||
c->rfilter_num = 0;
|
||||
}
|
||||
|
||||
static CanBusClientInfo can_host_socketcan_bus_client_info = {
|
||||
.can_receive = can_host_socketcan_can_receive,
|
||||
.receive = can_host_socketcan_receive,
|
||||
};
|
||||
|
||||
static void can_host_socketcan_connect(CanHostState *ch, Error **errp)
|
||||
{
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(ch);
|
||||
int s; /* can raw socket */
|
||||
struct sockaddr_can addr;
|
||||
struct ifreq ifr;
|
||||
|
||||
/* open socket */
|
||||
s = qemu_socket(PF_CAN, SOCK_RAW, CAN_RAW);
|
||||
if (s < 0) {
|
||||
error_setg_errno(errp, errno, "failed to create CAN_RAW socket");
|
||||
return;
|
||||
}
|
||||
|
||||
addr.can_family = AF_CAN;
|
||||
memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name));
|
||||
strcpy(ifr.ifr_name, c->ifname);
|
||||
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"SocketCAN host interface %s not available", c->ifname);
|
||||
goto fail;
|
||||
}
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
|
||||
c->err_mask = 0xffffffff; /* Receive error frame. */
|
||||
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
|
||||
&c->err_mask, sizeof(c->err_mask));
|
||||
|
||||
c->rfilter_num = 1;
|
||||
c->rfilter = g_new(struct qemu_can_filter, c->rfilter_num);
|
||||
|
||||
/* Receive all data frame. If |= CAN_INV_FILTER no data. */
|
||||
c->rfilter[0].can_id = 0;
|
||||
c->rfilter[0].can_mask = 0;
|
||||
c->rfilter[0].can_mask &= ~CAN_ERR_FLAG;
|
||||
|
||||
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, c->rfilter,
|
||||
c->rfilter_num * sizeof(struct qemu_can_filter));
|
||||
|
||||
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
error_setg_errno(errp, errno, "failed to bind to host interface %s",
|
||||
c->ifname);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c->fd = s;
|
||||
ch->bus_client.info = &can_host_socketcan_bus_client_info;
|
||||
qemu_set_fd_handler(c->fd, can_host_socketcan_read, NULL, c);
|
||||
return;
|
||||
|
||||
fail:
|
||||
close(s);
|
||||
g_free(c->rfilter);
|
||||
c->rfilter = NULL;
|
||||
c->rfilter_num = 0;
|
||||
}
|
||||
|
||||
static char *can_host_socketcan_get_if(Object *obj, Error **errp)
|
||||
{
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
|
||||
|
||||
return g_strdup(c->ifname);
|
||||
}
|
||||
|
||||
static void can_host_socketcan_set_if(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
|
||||
struct ifreq ifr;
|
||||
|
||||
if (strlen(value) >= sizeof(ifr.ifr_name)) {
|
||||
error_setg(errp, "CAN interface name longer than %zd characters",
|
||||
sizeof(ifr.ifr_name) - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->fd != -1) {
|
||||
error_setg(errp, "CAN interface already connected");
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(c->ifname);
|
||||
c->ifname = g_strdup(value);
|
||||
}
|
||||
|
||||
static void can_host_socketcan_instance_init(Object *obj)
|
||||
{
|
||||
CanHostSocketCAN *c = CAN_HOST_SOCKETCAN(obj);
|
||||
|
||||
c->fd = -1;
|
||||
}
|
||||
|
||||
static void can_host_socketcan_class_init(ObjectClass *klass,
|
||||
void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
CanHostClass *chc = CAN_HOST_CLASS(klass);
|
||||
|
||||
object_class_property_add_str(klass, "if",
|
||||
can_host_socketcan_get_if,
|
||||
can_host_socketcan_set_if,
|
||||
&error_abort);
|
||||
chc->connect = can_host_socketcan_connect;
|
||||
chc->disconnect = can_host_socketcan_disconnect;
|
||||
}
|
||||
|
||||
static const TypeInfo can_host_socketcan_info = {
|
||||
.parent = TYPE_CAN_HOST,
|
||||
.name = TYPE_CAN_HOST_SOCKETCAN,
|
||||
.instance_size = sizeof(CanHostSocketCAN),
|
||||
.instance_init = can_host_socketcan_instance_init,
|
||||
.class_init = can_host_socketcan_class_init,
|
||||
};
|
||||
|
||||
static void can_host_register_types(void)
|
||||
{
|
||||
type_register_static(&can_host_socketcan_info);
|
||||
}
|
||||
|
||||
type_init(can_host_register_types);
|
@ -131,8 +131,6 @@ modules:
|
||||
# If called with only a single argument, will print nothing in quiet mode.
|
||||
quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1))
|
||||
|
||||
MAKEFLAGS += $(if $(V),,--no-print-directory --quiet)
|
||||
|
||||
# cc-option
|
||||
# Usage: CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
|
||||
|
||||
|
@ -103,6 +103,8 @@ static int hax_get_capability(struct hax_state *hax)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
hax->supports_64bit_ramblock = !!(cap->winfo & HAX_CAP_64BIT_RAMBLOCK);
|
||||
|
||||
if (cap->wstatus & HAX_CAP_MEMQUOTA) {
|
||||
if (cap->mem_quota < hax->mem_quota) {
|
||||
fprintf(stderr, "The VM memory needed exceeds the driver limit.\n");
|
||||
|
@ -28,21 +28,36 @@ hax_fd hax_mod_open(void)
|
||||
return fd;
|
||||
}
|
||||
|
||||
int hax_populate_ram(uint64_t va, uint32_t size)
|
||||
int hax_populate_ram(uint64_t va, uint64_t size)
|
||||
{
|
||||
int ret;
|
||||
struct hax_alloc_ram_info info;
|
||||
|
||||
if (!hax_global.vm || !hax_global.vm->fd) {
|
||||
fprintf(stderr, "Allocate memory before vm create?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info.size = size;
|
||||
info.va = va;
|
||||
ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info);
|
||||
if (hax_global.supports_64bit_ramblock) {
|
||||
struct hax_ramblock_info ramblock = {
|
||||
.start_va = va,
|
||||
.size = size,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ADD_RAMBLOCK, &ramblock);
|
||||
} else {
|
||||
struct hax_alloc_ram_info info = {
|
||||
.size = (uint32_t)size,
|
||||
.pad = 0,
|
||||
.va = va
|
||||
};
|
||||
|
||||
ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info);
|
||||
}
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to allocate %x memory\n", size);
|
||||
fprintf(stderr, "Failed to register RAM block: ret=%d, va=0x%" PRIx64
|
||||
", size=0x%" PRIx64 ", method=%s\n", ret, va, size,
|
||||
hax_global.supports_64bit_ramblock ? "new" : "legacy");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
|
@ -44,6 +44,7 @@ static inline void hax_close_fd(hax_fd fd)
|
||||
#define HAX_VM_IOCTL_SET_RAM _IOWR(0, 0x82, struct hax_set_ram_info)
|
||||
#define HAX_VM_IOCTL_VCPU_DESTROY _IOW(0, 0x83, uint32_t)
|
||||
#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version)
|
||||
#define HAX_VM_IOCTL_ADD_RAMBLOCK _IOW(0, 0x85, struct hax_ramblock_info)
|
||||
|
||||
#define HAX_VCPU_IOCTL_RUN _IO(0, 0xc0)
|
||||
#define HAX_VCPU_IOCTL_SET_MSRS _IOWR(0, 0xc1, struct hax_msr_data)
|
||||
|
@ -37,6 +37,7 @@ struct hax_state {
|
||||
uint32_t version;
|
||||
struct hax_vm *vm;
|
||||
uint64_t mem_quota;
|
||||
bool supports_64bit_ramblock;
|
||||
};
|
||||
|
||||
#define HAX_MAX_VCPU 0x10
|
||||
|
@ -308,6 +308,13 @@ struct hax_alloc_ram_info {
|
||||
uint32_t pad;
|
||||
uint64_t va;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct hax_ramblock_info {
|
||||
uint64_t start_va;
|
||||
uint64_t size;
|
||||
uint64_t reserved;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define HAX_RAM_INFO_ROM 0x01 /* Read-Only */
|
||||
#define HAX_RAM_INFO_INVALID 0x80 /* Unmapped, usually used for MMIO */
|
||||
struct hax_set_ram_info {
|
||||
@ -327,6 +334,7 @@ struct hax_set_ram_info {
|
||||
|
||||
#define HAX_CAP_MEMQUOTA 0x2
|
||||
#define HAX_CAP_UG 0x4
|
||||
#define HAX_CAP_64BIT_RAMBLOCK 0x8
|
||||
|
||||
struct hax_capabilityinfo {
|
||||
/* bit 0: 1 - working
|
||||
|
@ -174,6 +174,7 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
|
||||
ram_addr_t size = int128_get64(section->size);
|
||||
unsigned int delta;
|
||||
uint64_t host_va;
|
||||
uint32_t max_mapping_size;
|
||||
|
||||
/* We only care about RAM and ROM regions */
|
||||
if (!memory_region_is_ram(mr)) {
|
||||
@ -206,10 +207,23 @@ static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
|
||||
flags |= HAX_RAM_INFO_ROM;
|
||||
}
|
||||
|
||||
/* the kernel module interface uses 32-bit sizes (but we could split...) */
|
||||
g_assert(size <= UINT32_MAX);
|
||||
|
||||
hax_update_mapping(start_pa, size, host_va, flags);
|
||||
/*
|
||||
* The kernel module interface uses 32-bit sizes:
|
||||
* https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_set_ram
|
||||
*
|
||||
* If the mapping size is longer than 32 bits, we can't process it in one
|
||||
* call into the kernel. Instead, we split the mapping into smaller ones,
|
||||
* and call hax_update_mapping() on each.
|
||||
*/
|
||||
max_mapping_size = UINT32_MAX & qemu_real_host_page_mask;
|
||||
while (size > max_mapping_size) {
|
||||
hax_update_mapping(start_pa, max_mapping_size, host_va, flags);
|
||||
start_pa += max_mapping_size;
|
||||
size -= max_mapping_size;
|
||||
host_va += max_mapping_size;
|
||||
}
|
||||
/* Now size <= max_mapping_size */
|
||||
hax_update_mapping(start_pa, (uint32_t)size, host_va, flags);
|
||||
}
|
||||
|
||||
static void hax_region_add(MemoryListener *listener,
|
||||
@ -283,12 +297,16 @@ static MemoryListener hax_memory_listener = {
|
||||
static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
|
||||
{
|
||||
/*
|
||||
* In HAX, QEMU allocates the virtual address, and HAX kernel
|
||||
* populates the memory with physical memory. Currently we have no
|
||||
* paging, so user should make sure enough free memory in advance.
|
||||
* We must register each RAM block with the HAXM kernel module, or
|
||||
* hax_set_ram() will fail for any mapping into the RAM block:
|
||||
* https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_alloc_ram
|
||||
*
|
||||
* Old versions of the HAXM kernel module (< 6.2.0) used to preallocate all
|
||||
* host physical pages for the RAM block as part of this registration
|
||||
* process, hence the name hax_populate_ram().
|
||||
*/
|
||||
if (hax_populate_ram((uint64_t)(uintptr_t)host, size) < 0) {
|
||||
fprintf(stderr, "HAX failed to populate RAM");
|
||||
fprintf(stderr, "HAX failed to populate RAM\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
@ -58,10 +58,9 @@ static int hax_open_device(hax_fd *fd)
|
||||
return fd;
|
||||
}
|
||||
|
||||
int hax_populate_ram(uint64_t va, uint32_t size)
|
||||
int hax_populate_ram(uint64_t va, uint64_t size)
|
||||
{
|
||||
int ret;
|
||||
struct hax_alloc_ram_info info;
|
||||
HANDLE hDeviceVM;
|
||||
DWORD dSize = 0;
|
||||
|
||||
@ -70,18 +69,35 @@ int hax_populate_ram(uint64_t va, uint32_t size)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info.size = size;
|
||||
info.va = va;
|
||||
|
||||
hDeviceVM = hax_global.vm->fd;
|
||||
if (hax_global.supports_64bit_ramblock) {
|
||||
struct hax_ramblock_info ramblock = {
|
||||
.start_va = va,
|
||||
.size = size,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
ret = DeviceIoControl(hDeviceVM,
|
||||
HAX_VM_IOCTL_ALLOC_RAM,
|
||||
&info, sizeof(info), NULL, 0, &dSize,
|
||||
(LPOVERLAPPED) NULL);
|
||||
ret = DeviceIoControl(hDeviceVM,
|
||||
HAX_VM_IOCTL_ADD_RAMBLOCK,
|
||||
&ramblock, sizeof(ramblock), NULL, 0, &dSize,
|
||||
(LPOVERLAPPED) NULL);
|
||||
} else {
|
||||
struct hax_alloc_ram_info info = {
|
||||
.size = (uint32_t) size,
|
||||
.pad = 0,
|
||||
.va = va
|
||||
};
|
||||
|
||||
ret = DeviceIoControl(hDeviceVM,
|
||||
HAX_VM_IOCTL_ALLOC_RAM,
|
||||
&info, sizeof(info), NULL, 0, &dSize,
|
||||
(LPOVERLAPPED) NULL);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
fprintf(stderr, "Failed to allocate %x memory\n", size);
|
||||
fprintf(stderr, "Failed to register RAM block: va=0x%" PRIx64
|
||||
", size=0x%" PRIx64 ", method=%s\n", va, size,
|
||||
hax_global.supports_64bit_ramblock ? "new" : "legacy");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,8 @@ static inline int hax_invalid_fd(hax_fd fd)
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define HAX_VM_IOCTL_VCPU_DESTROY CTL_CODE(HAX_DEVICE_TYPE, 0x905, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define HAX_VM_IOCTL_ADD_RAMBLOCK CTL_CODE(HAX_DEVICE_TYPE, 0x913, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define HAX_VCPU_IOCTL_RUN CTL_CODE(HAX_DEVICE_TYPE, 0x906, \
|
||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
@ -294,6 +294,7 @@ check-qtest-i386-y += tests/migration-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
|
||||
check-qtest-i386-y += tests/numa-test$(EXESUF)
|
||||
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
||||
check-qtest-x86_64-y += tests/sdhci-test$(EXESUF)
|
||||
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
|
||||
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
|
||||
|
||||
@ -367,8 +368,10 @@ gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
|
||||
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
|
||||
gcov-files-arm-y += hw/timer/arm_mptimer.c
|
||||
check-qtest-arm-y += tests/boot-serial-test$(EXESUF)
|
||||
check-qtest-arm-y += tests/sdhci-test$(EXESUF)
|
||||
|
||||
check-qtest-aarch64-y = tests/numa-test$(EXESUF)
|
||||
check-qtest-aarch64-y += tests/sdhci-test$(EXESUF)
|
||||
|
||||
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
|
||||
|
||||
@ -822,6 +825,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
||||
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
|
||||
tests/numa-test$(EXESUF): tests/numa-test.o
|
||||
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o
|
||||
tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y)
|
||||
|
||||
tests/migration/stress$(EXESUF): tests/migration/stress.o
|
||||
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
|
||||
|
250
tests/sdhci-test.c
Normal file
250
tests/sdhci-test.c
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* QTest testcase for SDHCI controllers
|
||||
*
|
||||
* Written by Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
|
||||
#define SDHC_CAPAB 0x40
|
||||
FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */
|
||||
FIELD(SDHC_CAPAB, SDMA, 22, 1);
|
||||
FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */
|
||||
FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */
|
||||
#define SDHC_HCVER 0xFE
|
||||
|
||||
static const struct sdhci_t {
|
||||
const char *arch, *machine;
|
||||
struct {
|
||||
uintptr_t addr;
|
||||
uint8_t version;
|
||||
uint8_t baseclock;
|
||||
struct {
|
||||
bool sdma;
|
||||
uint64_t reg;
|
||||
} capab;
|
||||
} sdhci;
|
||||
struct {
|
||||
uint16_t vendor_id, device_id;
|
||||
} pci;
|
||||
} models[] = {
|
||||
/* PC via PCI */
|
||||
{ "x86_64", "pc",
|
||||
{-1, 2, 0, {1, 0x057834b4} },
|
||||
.pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } },
|
||||
|
||||
/* Exynos4210 */
|
||||
{ "arm", "smdkc210",
|
||||
{0x12510000, 2, 0, {1, 0x5e80080} } },
|
||||
|
||||
/* i.MX 6 */
|
||||
{ "arm", "sabrelite",
|
||||
{0x02190000, 3, 0, {1, 0x057834b4} } },
|
||||
|
||||
/* BCM2835 */
|
||||
{ "arm", "raspi2",
|
||||
{0x3f300000, 3, 52, {0, 0x052134b4} } },
|
||||
|
||||
/* Zynq-7000 */
|
||||
{ "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */
|
||||
{0xe0100000, 2, 0, {1, 0x69ec0080} } },
|
||||
|
||||
/* ZynqMP */
|
||||
{ "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */
|
||||
{0xff160000, 3, 0, {1, 0x280737ec6481} } },
|
||||
|
||||
};
|
||||
|
||||
typedef struct QSDHCI {
|
||||
struct {
|
||||
QPCIBus *bus;
|
||||
QPCIDevice *dev;
|
||||
} pci;
|
||||
union {
|
||||
QPCIBar mem_bar;
|
||||
uint64_t addr;
|
||||
};
|
||||
} QSDHCI;
|
||||
|
||||
static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
uint16_t val;
|
||||
|
||||
if (s->pci.dev) {
|
||||
val = qpci_io_readw(s->pci.dev, s->mem_bar, reg);
|
||||
} else {
|
||||
val = qtest_readw(global_qtest, s->addr + reg);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
if (s->pci.dev) {
|
||||
val = qpci_io_readq(s->pci.dev, s->mem_bar, reg);
|
||||
} else {
|
||||
val = qtest_readq(global_qtest, s->addr + reg);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
|
||||
{
|
||||
if (s->pci.dev) {
|
||||
qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val);
|
||||
} else {
|
||||
qtest_writeq(global_qtest, s->addr + reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_specs_version(QSDHCI *s, uint8_t version)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
v = sdhci_readw(s, SDHC_HCVER);
|
||||
v &= 0xff;
|
||||
v += 1;
|
||||
g_assert_cmpuint(v, ==, version);
|
||||
}
|
||||
|
||||
static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab)
|
||||
{
|
||||
uint64_t capab;
|
||||
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
g_assert_cmphex(capab, ==, expec_capab);
|
||||
}
|
||||
|
||||
static void check_capab_readonly(QSDHCI *s)
|
||||
{
|
||||
const uint64_t vrand = 0x123456789abcdef;
|
||||
uint64_t capab0, capab1;
|
||||
|
||||
capab0 = sdhci_readq(s, SDHC_CAPAB);
|
||||
g_assert_cmpuint(capab0, !=, vrand);
|
||||
|
||||
sdhci_writeq(s, SDHC_CAPAB, vrand);
|
||||
capab1 = sdhci_readq(s, SDHC_CAPAB);
|
||||
g_assert_cmpuint(capab1, !=, vrand);
|
||||
g_assert_cmpuint(capab1, ==, capab0);
|
||||
}
|
||||
|
||||
static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq)
|
||||
{
|
||||
uint64_t capab, capab_freq;
|
||||
|
||||
if (!expec_freq) {
|
||||
return;
|
||||
}
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ);
|
||||
g_assert_cmpuint(capab_freq, ==, expec_freq);
|
||||
}
|
||||
|
||||
static void check_capab_sdma(QSDHCI *s, bool supported)
|
||||
{
|
||||
uint64_t capab, capab_sdma;
|
||||
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA);
|
||||
g_assert_cmpuint(capab_sdma, ==, supported);
|
||||
}
|
||||
|
||||
static void check_capab_v3(QSDHCI *s, uint8_t version)
|
||||
{
|
||||
uint64_t capab, capab_v3;
|
||||
|
||||
if (version < 3) {
|
||||
/* before v3 those fields are RESERVED */
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR);
|
||||
g_assert_cmpuint(capab_v3, ==, 0);
|
||||
capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER);
|
||||
g_assert_cmpuint(capab_v3, ==, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static QSDHCI *machine_start(const struct sdhci_t *test)
|
||||
{
|
||||
QSDHCI *s = g_new0(QSDHCI, 1);
|
||||
|
||||
if (test->pci.vendor_id) {
|
||||
/* PCI */
|
||||
uint16_t vendor_id, device_id;
|
||||
uint64_t barsize;
|
||||
|
||||
global_qtest = qtest_startf("-machine %s -device sdhci-pci",
|
||||
test->machine);
|
||||
|
||||
s->pci.bus = qpci_init_pc(NULL);
|
||||
|
||||
/* Find PCI device and verify it's the right one */
|
||||
s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0));
|
||||
g_assert_nonnull(s->pci.dev);
|
||||
vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID);
|
||||
device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID);
|
||||
g_assert(vendor_id == test->pci.vendor_id);
|
||||
g_assert(device_id == test->pci.device_id);
|
||||
s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize);
|
||||
qpci_device_enable(s->pci.dev);
|
||||
} else {
|
||||
/* SysBus */
|
||||
global_qtest = qtest_startf("-machine %s", test->machine);
|
||||
s->addr = test->sdhci.addr;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void machine_stop(QSDHCI *s)
|
||||
{
|
||||
g_free(s->pci.dev);
|
||||
qtest_quit(global_qtest);
|
||||
}
|
||||
|
||||
static void test_machine(const void *data)
|
||||
{
|
||||
const struct sdhci_t *test = data;
|
||||
QSDHCI *s;
|
||||
|
||||
s = machine_start(test);
|
||||
|
||||
check_specs_version(s, test->sdhci.version);
|
||||
check_capab_capareg(s, test->sdhci.capab.reg);
|
||||
check_capab_readonly(s);
|
||||
check_capab_v3(s, test->sdhci.version);
|
||||
check_capab_sdma(s, test->sdhci.capab.sdma);
|
||||
check_capab_baseclock(s, test->sdhci.baseclock);
|
||||
|
||||
machine_stop(s);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
if (strcmp(arch, models[i].arch)) {
|
||||
continue;
|
||||
}
|
||||
name = g_strdup_printf("sdhci/%s", models[i].machine);
|
||||
qtest_add_data_func(name, &models[i], test_machine);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user