From 733210e75465aecf99cac641e6408cee6ace9037 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Sat, 27 Jan 2018 17:43:21 +0100 Subject: hw/net/can: SJA1000 chip register level emulation for QEMU The core SJA1000 support is independent of following patches which map SJA1000 chip to PCI boards. The work is based on Jin Yang GSoC 2013 work funded by Google and mentored in frame of RTEMS project GSoC slot donated to QEMU. Rewritten for QEMU-2.0+ versions and architecture cleanup by Pavel Pisa (Czech Technical University in Prague). Signed-off-by: Pavel Pisa Signed-off-by: Paolo Bonzini --- hw/net/Makefile.objs | 2 + hw/net/can/Makefile.objs | 1 + hw/net/can/can_sja1000.c | 949 +++++++++++++++++++++++++++++++++++++++++++++++ hw/net/can/can_sja1000.h | 146 ++++++++ 4 files changed, 1098 insertions(+) create mode 100644 hw/net/can/Makefile.objs create mode 100644 hw/net/can/can_sja1000.c create mode 100644 hw/net/can/can_sja1000.h (limited to 'hw/net') diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 4171af0b5d..ab22968641 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -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/ diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs new file mode 100644 index 0000000000..c299f83c3d --- /dev/null +++ b/hw/net/can/Makefile.objs @@ -0,0 +1 @@ +common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c new file mode 100644 index 0000000000..503fbcfb23 --- /dev/null +++ b/hw/net/can/can_sja1000.c @@ -0,0 +1,949 @@ +/* + * 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; +} + +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; + if (s->interrupt_en & 0x02) { + qemu_irq_raise(s->irq); + } + } + 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); + } + if ((s->interrupt_en & 0x01) && (s->interrupt_pel == 0)) { + /* no other interrupts. */ + qemu_irq_lower(s->irq); + } + } + if (0x08 & val) { /* Clear data overrun */ + s->status_pel &= ~(1 << 1); + s->interrupt_pel &= ~(1 << 3); + if ((s->interrupt_en & 0x80) && (s->interrupt_pel == 0)) { + /* no other interrupts. */ + qemu_irq_lower(s->irq); + } + } + 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; + if (s->control & 0x04) { + qemu_irq_raise(s->irq); + } + } + 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); + } + if ((s->control & 0x02) && (s->interrupt_bas == 0)) { + /* no other interrupts. */ + qemu_irq_lower(s->irq); + } + } + if (0x08 & val) { /* Clear data overrun */ + s->status_bas &= ~(1 << 1); + s->interrupt_bas &= ~(1 << 3); + if ((s->control & 0x10) && (s->interrupt_bas == 0)) { + /* no other interrupts. */ + qemu_irq_lower(s->irq); + } + } + 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. */ + break; + } + qemu_irq_lower(s->irq); + 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. */ + break; + } + qemu_irq_lower(s->irq); + 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); + if (s->interrupt_en & (1 << 3)) { /* Overrun interrupt enable */ + qemu_irq_raise(s->irq); + } + s->status_pel &= ~(1 << 4); + 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 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); + if (s->interrupt_en & 0x01) { /* Receive Interrupt enable. */ + qemu_irq_raise(s->irq); + } + } 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); + if (s->control & (1 << 4)) { /* Overrun interrupt enable */ + qemu_irq_raise(s->irq); + } + 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 |= 0x01; + if (s->control & 0x02) { /* Receive Interrupt enable. */ + qemu_irq_raise(s->irq); + } + } + 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() + } +}; + +/* 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, + .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() + } +}; diff --git a/hw/net/can/can_sja1000.h b/hw/net/can/can_sja1000.h new file mode 100644 index 0000000000..4731cbbd2a --- /dev/null +++ b/hw/net/can/can_sja1000.h @@ -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 -- cgit v1.2.3 From 321af2f5b8285d547468c505fc8ed66b9c1b1907 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Sat, 27 Jan 2018 18:06:57 +0100 Subject: hw/net/can: Kvaser PCI CAN-S (single SJA1000 channel) emulation Signed-off-by: Pavel Pisa Signed-off-by: Paolo Bonzini --- hw/net/can/Makefile.objs | 1 + hw/net/can/can_kvaser_pci.c | 319 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 hw/net/can/can_kvaser_pci.c (limited to 'hw/net') diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs index c299f83c3d..9d4a5152a3 100644 --- a/hw/net/can/Makefile.objs +++ b/hw/net/can/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o +common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c new file mode 100644 index 0000000000..2d77ef2938 --- /dev/null +++ b/hw/net/can/can_kvaser_pci.c @@ -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), + VMSTATE_STRUCT(sja_state, KvaserPCIState, 0, vmstate_can_sja, + CanSJA1000State), + VMSTATE_UINT32(s5920_intcsr, KvaserPCIState), + VMSTATE_UINT32(s5920_irqstate, KvaserPCIState), + 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) -- cgit v1.2.3 From cfae1ba3b68566fe781c9468eca9f77bc301c174 Mon Sep 17 00:00:00 2001 From: Deniz Eren Date: Mon, 29 Jan 2018 22:17:19 +0100 Subject: hw/net/can: PCM-3680I PCI (dual SJA1000 channel) emulation Signed-off-by: Deniz Eren Signed-off-by: Pavel Pisa Signed-off-by: Paolo Bonzini --- hw/net/can/Makefile.objs | 1 + hw/net/can/can_pcm3680_pci.c | 263 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 hw/net/can/can_pcm3680_pci.c (limited to 'hw/net') diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs index 9d4a5152a3..97c4295943 100644 --- a/hw/net/can/Makefile.objs +++ b/hw/net/can/Makefile.objs @@ -1,2 +1,3 @@ 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 diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c new file mode 100644 index 0000000000..23f7ff45a3 --- /dev/null +++ b/hw/net/can/can_pcm3680_pci.c @@ -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) -- cgit v1.2.3 From db3e63798d035fca693457db3138c93f8fabbb6a Mon Sep 17 00:00:00 2001 From: Deniz Eren Date: Sat, 27 Jan 2018 18:11:01 +0100 Subject: hw/net/can: MIOe-3680 PCI (dual SJA1000 channel) emulation Signed-off-by: Deniz Eren Signed-off-by: Pavel Pisa Signed-off-by: Paolo Bonzini --- hw/net/can/Makefile.objs | 1 + hw/net/can/can_mioe3680_pci.c | 262 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 hw/net/can/can_mioe3680_pci.c (limited to 'hw/net') diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs index 97c4295943..9f0c4ee332 100644 --- a/hw/net/can/Makefile.objs +++ b/hw/net/can/Makefile.objs @@ -1,3 +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 diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c new file mode 100644 index 0000000000..fd20b88955 --- /dev/null +++ b/hw/net/can/can_mioe3680_pci.c @@ -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) -- cgit v1.2.3 From a62ed5d10600762eef20e2d925ce9e3adf56b9f7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 29 Jan 2018 23:30:17 +0100 Subject: hw/net/can: interrupt cleanup Define two functions to update the interrupt state, and call them on loadvm. This removes the need to migrate the state as part of vmstate_kvaser_pci. Signed-off-by: Paolo Bonzini --- hw/net/can/can_kvaser_pci.c | 4 +-- hw/net/can/can_sja1000.c | 82 ++++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 41 deletions(-) (limited to 'hw/net') diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 2d77ef2938..5f82f4359a 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -264,10 +264,10 @@ static const VMStateDescription vmstate_kvaser_pci = { .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_UINT32(s5920_intcsr, KvaserPCIState), - VMSTATE_UINT32(s5920_irqstate, KvaserPCIState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 503fbcfb23..629323312c 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -378,6 +378,24 @@ static int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff) 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) { @@ -457,9 +475,7 @@ void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, /* Clear transmit status. */ s->status_pel &= ~(1 << 5); s->interrupt_pel |= 0x02; - if (s->interrupt_en & 0x02) { - qemu_irq_raise(s->irq); - } + can_sja_update_pel_irq(s); } if (0x04 & val) { /* Release Receive Buffer */ if (s->rxmsg_cnt <= 0) { @@ -488,19 +504,13 @@ void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, if (s->rxmsg_cnt == 0) { s->status_pel &= ~(1 << 0); s->interrupt_pel &= ~(1 << 0); - } - if ((s->interrupt_en & 0x01) && (s->interrupt_pel == 0)) { - /* no other interrupts. */ - qemu_irq_lower(s->irq); + can_sja_update_pel_irq(s); } } if (0x08 & val) { /* Clear data overrun */ s->status_pel &= ~(1 << 1); s->interrupt_pel &= ~(1 << 3); - if ((s->interrupt_en & 0x80) && (s->interrupt_pel == 0)) { - /* no other interrupts. */ - qemu_irq_lower(s->irq); - } + can_sja_update_pel_irq(s); } break; case SJA_SR: /* Status register */ @@ -568,9 +578,7 @@ void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, /* Clear transmit status. */ s->status_bas &= ~(1 << 5); s->interrupt_bas |= 0x02; - if (s->control & 0x04) { - qemu_irq_raise(s->irq); - } + can_sja_update_bas_irq(s); } if (0x04 & val) { /* Release Receive Buffer */ if (s->rxmsg_cnt <= 0) { @@ -593,19 +601,13 @@ void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, if (s->rxmsg_cnt == 0) { s->status_bas &= ~(1 << 0); s->interrupt_bas &= ~(1 << 0); - } - if ((s->control & 0x02) && (s->interrupt_bas == 0)) { - /* no other interrupts. */ - qemu_irq_lower(s->irq); + can_sja_update_bas_irq(s); } } if (0x08 & val) { /* Clear data overrun */ s->status_bas &= ~(1 << 1); s->interrupt_bas &= ~(1 << 3); - if ((s->control & 0x10) && (s->interrupt_bas == 0)) { - /* no other interrupts. */ - qemu_irq_lower(s->irq); - } + can_sja_update_bas_irq(s); } break; case 4: @@ -654,9 +656,8 @@ uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size) s->interrupt_pel = 0; if (s->rxmsg_cnt) { s->interrupt_pel |= (1 << 0); /* Receive interrupt. */ - break; } - qemu_irq_lower(s->irq); + can_sja_update_pel_irq(s); break; case SJA_IER: /* Interrupt enable register, addr 4 */ temp = s->interrupt_en; @@ -704,9 +705,8 @@ uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size) s->interrupt_bas = 0; if (s->rxmsg_cnt) { s->interrupt_bas |= (1 << 0); /* Receive interrupt. */ - break; } - qemu_irq_lower(s->irq); + can_sja_update_bas_irq(s); break; case 4: temp = s->code; @@ -789,13 +789,11 @@ ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */ s->status_pel |= (1 << 1); /* Overrun status */ s->interrupt_pel |= (1 << 3); - if (s->interrupt_en & (1 << 3)) { /* Overrun interrupt enable */ - qemu_irq_raise(s->irq); - } 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; @@ -813,9 +811,7 @@ ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, s->interrupt_pel |= 0x01; s->status_pel &= ~(1 << 4); s->status_pel |= (1 << 0); - if (s->interrupt_en & 0x01) { /* Receive Interrupt enable. */ - qemu_irq_raise(s->irq); - } + can_sja_update_pel_irq(s); } else { /* BasicCAN mode */ /* the CAN controller is receiving a message */ @@ -834,9 +830,7 @@ ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, s->status_bas |= (1 << 1); /* Overrun status */ s->status_bas &= ~(1 << 4); s->interrupt_bas |= (1 << 3); - if (s->control & (1 << 4)) { /* Overrun interrupt enable */ - qemu_irq_raise(s->irq); - } + can_sja_update_bas_irq(s); if (DEBUG_FILTER) { qemu_log("[cansja]: receive FIFO overrun\n"); } @@ -856,10 +850,8 @@ ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, s->status_bas |= 0x01; /* Set the Receive Buffer Status. DS-p15 */ s->status_bas &= ~(1 << 4); - s->interrupt_bas |= 0x01; - if (s->control & 0x02) { /* Receive Interrupt enable. */ - qemu_irq_raise(s->irq); - } + s->interrupt_bas |= (1 << 0); + can_sja_update_bas_irq(s); } return 1; } @@ -909,12 +901,24 @@ const VMStateDescription vmstate_qemu_can_filter = { } }; +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), -- cgit v1.2.3