mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-09 10:34:58 -06:00
* 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
52 changed files with 3706 additions and 295 deletions
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue