2020-09-14 11:13:40 +03:00
|
|
|
/*
|
|
|
|
* CTU CAN FD PCI device emulation
|
|
|
|
* http://canbus.pages.fel.cvut.cz/
|
|
|
|
*
|
|
|
|
* Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.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/log.h"
|
|
|
|
#include "chardev/char.h"
|
|
|
|
#include "hw/irq.h"
|
|
|
|
#include "migration/vmstate.h"
|
|
|
|
#include "net/can_emu.h"
|
|
|
|
|
|
|
|
#include "ctucan_core.h"
|
|
|
|
|
|
|
|
#ifndef DEBUG_CAN
|
|
|
|
#define DEBUG_CAN 0
|
|
|
|
#endif /*DEBUG_CAN*/
|
|
|
|
|
|
|
|
#define DPRINTF(fmt, ...) \
|
|
|
|
do { \
|
|
|
|
if (DEBUG_CAN) { \
|
|
|
|
qemu_log("[ctucan]: " fmt , ## __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static void ctucan_buff2frame(const uint8_t *buff, qemu_can_frame *frame)
|
|
|
|
{
|
|
|
|
frame->can_id = 0;
|
|
|
|
frame->can_dlc = 0;
|
|
|
|
frame->flags = 0;
|
|
|
|
|
|
|
|
if (buff == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
union ctu_can_fd_frame_form_w frame_form_w;
|
|
|
|
union ctu_can_fd_identifier_w identifier_w;
|
|
|
|
unsigned int ide;
|
|
|
|
uint32_t w;
|
|
|
|
|
|
|
|
w = le32_to_cpu(*(uint32_t *)buff);
|
|
|
|
frame_form_w = (union ctu_can_fd_frame_form_w)w;
|
|
|
|
frame->can_dlc = can_dlc2len(frame_form_w.s.dlc);
|
|
|
|
|
|
|
|
w = le32_to_cpu(*(uint32_t *)(buff + 4));
|
|
|
|
identifier_w = (union ctu_can_fd_identifier_w)w;
|
|
|
|
|
|
|
|
ide = frame_form_w.s.ide;
|
|
|
|
if (ide) {
|
|
|
|
frame->can_id = (identifier_w.s.identifier_base << 18) |
|
|
|
|
identifier_w.s.identifier_ext;
|
|
|
|
frame->can_id |= QEMU_CAN_EFF_FLAG;
|
|
|
|
} else {
|
|
|
|
frame->can_id = identifier_w.s.identifier_base;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_form_w.s.esi_rsv) {
|
|
|
|
frame->flags |= QEMU_CAN_FRMF_ESI;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_form_w.s.rtr) {
|
|
|
|
frame->can_id |= QEMU_CAN_RTR_FLAG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_form_w.s.fdf) { /*CAN FD*/
|
|
|
|
frame->flags |= QEMU_CAN_FRMF_TYPE_FD;
|
|
|
|
if (frame_form_w.s.brs) {
|
|
|
|
frame->flags |= QEMU_CAN_FRMF_BRS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(frame->data, buff + 0x10, 0x40);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ctucan_frame2buff(const qemu_can_frame *frame, uint8_t *buff)
|
|
|
|
{
|
|
|
|
unsigned int bytes_cnt = -1;
|
|
|
|
memset(buff, 0, CTUCAN_MSG_MAX_LEN * sizeof(*buff));
|
|
|
|
|
|
|
|
if (frame == NULL) {
|
|
|
|
return bytes_cnt;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
union ctu_can_fd_frame_form_w frame_form_w;
|
|
|
|
union ctu_can_fd_identifier_w identifier_w;
|
|
|
|
|
|
|
|
frame_form_w.u32 = 0;
|
|
|
|
identifier_w.u32 = 0;
|
|
|
|
|
|
|
|
bytes_cnt = frame->can_dlc;
|
|
|
|
bytes_cnt = (bytes_cnt + 3) & ~3;
|
|
|
|
bytes_cnt += 16;
|
|
|
|
frame_form_w.s.rwcnt = (bytes_cnt >> 2) - 1;
|
|
|
|
|
|
|
|
frame_form_w.s.dlc = can_len2dlc(frame->can_dlc);
|
|
|
|
|
|
|
|
if (frame->can_id & QEMU_CAN_EFF_FLAG) {
|
|
|
|
frame_form_w.s.ide = 1;
|
|
|
|
identifier_w.s.identifier_base =
|
|
|
|
(frame->can_id & 0x1FFC0000) >> 18;
|
|
|
|
identifier_w.s.identifier_ext = frame->can_id & 0x3FFFF;
|
|
|
|
} else {
|
|
|
|
identifier_w.s.identifier_base = frame->can_id & 0x7FF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame->flags & QEMU_CAN_FRMF_ESI) {
|
|
|
|
frame_form_w.s.esi_rsv = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame->can_id & QEMU_CAN_RTR_FLAG) {
|
|
|
|
frame_form_w.s.rtr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) { /*CAN FD*/
|
|
|
|
frame_form_w.s.fdf = 1;
|
|
|
|
if (frame->flags & QEMU_CAN_FRMF_BRS) {
|
|
|
|
frame_form_w.s.brs = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*(uint32_t *)buff = cpu_to_le32(frame_form_w.u32);
|
|
|
|
*(uint32_t *)(buff + 4) = cpu_to_le32(identifier_w.u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buff + 0x10, frame->data, 0x40);
|
|
|
|
|
|
|
|
return bytes_cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ctucan_update_irq(CtuCanCoreState *s)
|
|
|
|
{
|
|
|
|
union ctu_can_fd_int_stat int_rq;
|
|
|
|
|
|
|
|
int_rq.u32 = 0;
|
|
|
|
|
|
|
|
if (s->rx_status_rx_settings.s.rxfrc) {
|
|
|
|
int_rq.s.rbnei = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int_rq.u32 &= ~s->int_mask.u32;
|
|
|
|
s->int_stat.u32 |= int_rq.u32;
|
|
|
|
if (s->int_stat.u32 & s->int_ena.u32) {
|
|
|
|
qemu_irq_raise(s->irq);
|
|
|
|
} else {
|
|
|
|
qemu_irq_lower(s->irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ctucan_update_txnf(CtuCanCoreState *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int txnf;
|
|
|
|
unsigned int buff_st;
|
|
|
|
|
|
|
|
txnf = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
|
|
|
|
buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
|
|
|
|
if (buff_st == TXT_ETY) {
|
|
|
|
txnf = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->status.s.txnf = txnf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ctucan_hardware_reset(CtuCanCoreState *s)
|
|
|
|
{
|
|
|
|
DPRINTF("Hardware reset in progress!!!\n");
|
|
|
|
int i;
|
|
|
|
unsigned int buff_st;
|
|
|
|
uint32_t buff_st_mask;
|
|
|
|
|
|
|
|
s->tx_status.u32 = 0;
|
|
|
|
for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
|
|
|
|
buff_st_mask = 0xf << (i * 4);
|
|
|
|
buff_st = TXT_ETY;
|
|
|
|
s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
|
|
|
|
(buff_st << (i * 4));
|
|
|
|
}
|
|
|
|
s->status.s.idle = 1;
|
|
|
|
|
|
|
|
ctucan_update_txnf(s);
|
|
|
|
|
|
|
|
s->rx_status_rx_settings.u32 = 0;
|
|
|
|
s->rx_tail_pos = 0;
|
|
|
|
s->rx_cnt = 0;
|
|
|
|
s->rx_frame_rem = 0;
|
|
|
|
|
|
|
|
/* Flush RX buffer */
|
|
|
|
s->rx_tail_pos = 0;
|
|
|
|
s->rx_cnt = 0;
|
|
|
|
s->rx_frame_rem = 0;
|
|
|
|
|
|
|
|
/* Set on progdokum reset value */
|
|
|
|
s->mode_settings.u32 = 0;
|
|
|
|
s->mode_settings.s.fde = 1;
|
|
|
|
|
|
|
|
s->int_stat.u32 = 0;
|
|
|
|
s->int_ena.u32 = 0;
|
|
|
|
s->int_mask.u32 = 0;
|
|
|
|
|
|
|
|
s->rx_status_rx_settings.u32 = 0;
|
|
|
|
s->rx_status_rx_settings.s.rxe = 0;
|
|
|
|
|
|
|
|
s->rx_fr_ctr.u32 = 0;
|
|
|
|
s->tx_fr_ctr.u32 = 0;
|
|
|
|
|
|
|
|
s->yolo_reg.s.yolo_val = 3735928559;
|
|
|
|
|
|
|
|
qemu_irq_lower(s->irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ctucan_send_ready_buffers(CtuCanCoreState *s)
|
|
|
|
{
|
|
|
|
qemu_can_frame frame;
|
|
|
|
uint8_t *pf;
|
|
|
|
int buff2tx_idx;
|
|
|
|
uint32_t tx_prio_max;
|
|
|
|
|
|
|
|
if (!s->mode_settings.s.ena) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
union ctu_can_fd_int_stat int_stat;
|
|
|
|
int i;
|
|
|
|
buff2tx_idx = -1;
|
|
|
|
tx_prio_max = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
|
|
|
|
uint32_t prio;
|
|
|
|
|
2020-11-11 00:52:48 +03:00
|
|
|
if (extract32(s->tx_status.u32, i * 4, 4) != TXT_RDY) {
|
2020-09-14 11:13:40 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
prio = (s->tx_priority.u32 >> (i * 4)) & 0x7;
|
|
|
|
if (tx_prio_max < prio) {
|
|
|
|
tx_prio_max = prio;
|
|
|
|
buff2tx_idx = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (buff2tx_idx == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
int_stat.u32 = 0;
|
|
|
|
pf = s->tx_buffer[buff2tx_idx].data;
|
|
|
|
ctucan_buff2frame(pf, &frame);
|
|
|
|
s->status.s.idle = 0;
|
|
|
|
s->status.s.txs = 1;
|
|
|
|
can_bus_client_send(&s->bus_client, &frame, 1);
|
|
|
|
s->status.s.idle = 1;
|
|
|
|
s->status.s.txs = 0;
|
|
|
|
s->tx_fr_ctr.s.tx_fr_ctr_val++;
|
|
|
|
int_stat.s.txi = 1;
|
|
|
|
int_stat.s.txbhci = 1;
|
|
|
|
s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
|
2020-11-11 00:52:48 +03:00
|
|
|
s->tx_status.u32 = deposit32(s->tx_status.u32,
|
|
|
|
buff2tx_idx * 4, 4, TXT_TOK);
|
2020-09-14 11:13:40 +03:00
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CTUCAN_CORE_TXBUFF_SPAN \
|
|
|
|
(CTU_CAN_FD_TXTB2_DATA_1 - CTU_CAN_FD_TXTB1_DATA_1)
|
|
|
|
|
|
|
|
void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
|
|
|
|
unsigned size)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTF("write 0x%02llx addr 0x%02x\n",
|
|
|
|
(unsigned long long)val, (unsigned int)addr);
|
|
|
|
|
2020-11-11 00:52:47 +03:00
|
|
|
if (addr >= CTUCAN_CORE_MEM_SIZE) {
|
2020-09-14 11:13:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr >= CTU_CAN_FD_TXTB1_DATA_1) {
|
|
|
|
int buff_num;
|
|
|
|
addr -= CTU_CAN_FD_TXTB1_DATA_1;
|
|
|
|
buff_num = addr / CTUCAN_CORE_TXBUFF_SPAN;
|
|
|
|
addr %= CTUCAN_CORE_TXBUFF_SPAN;
|
2020-11-11 00:52:47 +03:00
|
|
|
if ((buff_num < CTUCAN_CORE_TXBUF_NUM) &&
|
2020-11-11 00:52:50 +03:00
|
|
|
((addr + size) <= sizeof(s->tx_buffer[buff_num].data))) {
|
|
|
|
stn_le_p(s->tx_buffer[buff_num].data + addr, size, val);
|
2020-09-14 11:13:40 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (addr & ~3) {
|
|
|
|
case CTU_CAN_FD_MODE:
|
|
|
|
s->mode_settings.u32 = (uint32_t)val;
|
|
|
|
if (s->mode_settings.s.rst) {
|
|
|
|
ctucan_hardware_reset(s);
|
|
|
|
s->mode_settings.s.rst = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_COMMAND:
|
|
|
|
{
|
|
|
|
union ctu_can_fd_command command;
|
|
|
|
command.u32 = (uint32_t)val;
|
|
|
|
if (command.s.cdo) {
|
|
|
|
s->status.s.dor = 0;
|
|
|
|
}
|
|
|
|
if (command.s.rrb) {
|
|
|
|
s->rx_tail_pos = 0;
|
|
|
|
s->rx_cnt = 0;
|
|
|
|
s->rx_frame_rem = 0;
|
|
|
|
s->rx_status_rx_settings.s.rxfrc = 0;
|
|
|
|
}
|
|
|
|
if (command.s.txfcrst) {
|
|
|
|
s->tx_fr_ctr.s.tx_fr_ctr_val = 0;
|
|
|
|
}
|
|
|
|
if (command.s.rxfcrst) {
|
|
|
|
s->rx_fr_ctr.s.rx_fr_ctr_val = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CTU_CAN_FD_INT_STAT:
|
|
|
|
s->int_stat.u32 &= ~(uint32_t)val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_ENA_SET:
|
|
|
|
s->int_ena.u32 |= (uint32_t)val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_ENA_CLR:
|
|
|
|
s->int_ena.u32 &= ~(uint32_t)val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_MASK_SET:
|
|
|
|
s->int_mask.u32 |= (uint32_t)val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_MASK_CLR:
|
|
|
|
s->int_mask.u32 &= ~(uint32_t)val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_TX_COMMAND:
|
|
|
|
if (s->mode_settings.s.ena) {
|
|
|
|
union ctu_can_fd_tx_command tx_command;
|
|
|
|
union ctu_can_fd_tx_command mask;
|
|
|
|
unsigned int buff_st;
|
|
|
|
uint32_t buff_st_mask;
|
|
|
|
|
|
|
|
tx_command.u32 = (uint32_t)val;
|
|
|
|
mask.u32 = 0;
|
|
|
|
mask.s.txb1 = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
|
|
|
|
if (!(tx_command.u32 & (mask.u32 << i))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
buff_st_mask = 0xf << (i * 4);
|
|
|
|
buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
|
|
|
|
if (tx_command.s.txca) {
|
|
|
|
if (buff_st == TXT_RDY) {
|
|
|
|
buff_st = TXT_ABT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tx_command.s.txcr) {
|
|
|
|
if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
|
|
|
|
(buff_st == TXT_ABT) || (buff_st == TXT_ETY))
|
|
|
|
buff_st = TXT_RDY;
|
|
|
|
}
|
|
|
|
if (tx_command.s.txce) {
|
|
|
|
if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
|
|
|
|
(buff_st == TXT_ABT))
|
|
|
|
buff_st = TXT_ETY;
|
|
|
|
}
|
|
|
|
s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
|
|
|
|
(buff_st << (i * 4));
|
|
|
|
}
|
|
|
|
|
|
|
|
ctucan_send_ready_buffers(s);
|
|
|
|
ctucan_update_txnf(s);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_TX_PRIORITY:
|
|
|
|
s->tx_priority.u32 = (uint32_t)val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctucan_update_irq(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size)
|
|
|
|
{
|
|
|
|
uint32_t val = 0;
|
|
|
|
|
|
|
|
DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
|
|
|
|
|
|
|
|
if (addr > CTUCAN_CORE_MEM_SIZE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (addr & ~3) {
|
|
|
|
case CTU_CAN_FD_DEVICE_ID:
|
|
|
|
{
|
|
|
|
union ctu_can_fd_device_id_version idver;
|
|
|
|
idver.u32 = 0;
|
|
|
|
idver.s.device_id = CTU_CAN_FD_ID;
|
|
|
|
idver.s.ver_major = 2;
|
|
|
|
idver.s.ver_minor = 2;
|
|
|
|
val = idver.u32;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_MODE:
|
|
|
|
val = s->mode_settings.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_STATUS:
|
|
|
|
val = s->status.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_STAT:
|
|
|
|
val = s->int_stat.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_ENA_SET:
|
|
|
|
case CTU_CAN_FD_INT_ENA_CLR:
|
|
|
|
val = s->int_ena.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_INT_MASK_SET:
|
|
|
|
case CTU_CAN_FD_INT_MASK_CLR:
|
|
|
|
val = s->int_mask.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_RX_MEM_INFO:
|
|
|
|
s->rx_mem_info.u32 = 0;
|
|
|
|
s->rx_mem_info.s.rx_buff_size = CTUCAN_RCV_BUF_LEN >> 2;
|
|
|
|
s->rx_mem_info.s.rx_mem_free = (CTUCAN_RCV_BUF_LEN -
|
|
|
|
s->rx_cnt) >> 2;
|
|
|
|
val = s->rx_mem_info.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_RX_POINTERS:
|
|
|
|
{
|
|
|
|
uint32_t rx_head_pos = s->rx_tail_pos + s->rx_cnt;
|
|
|
|
rx_head_pos %= CTUCAN_RCV_BUF_LEN;
|
|
|
|
s->rx_pointers.s.rx_wpp = rx_head_pos;
|
|
|
|
s->rx_pointers.s.rx_rpp = s->rx_tail_pos;
|
|
|
|
val = s->rx_pointers.u32;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CTU_CAN_FD_RX_STATUS:
|
|
|
|
case CTU_CAN_FD_RX_SETTINGS:
|
|
|
|
if (!s->rx_status_rx_settings.s.rxfrc) {
|
|
|
|
s->rx_status_rx_settings.s.rxe = 1;
|
|
|
|
} else {
|
|
|
|
s->rx_status_rx_settings.s.rxe = 0;
|
|
|
|
}
|
|
|
|
if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
|
|
|
|
s->rx_status_rx_settings.s.rxf = 1;
|
|
|
|
} else {
|
|
|
|
s->rx_status_rx_settings.s.rxf = 0;
|
|
|
|
}
|
|
|
|
val = s->rx_status_rx_settings.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_RX_DATA:
|
|
|
|
if (s->rx_cnt) {
|
|
|
|
memcpy(&val, s->rx_buff + s->rx_tail_pos, 4);
|
|
|
|
val = le32_to_cpu(val);
|
|
|
|
if (!s->rx_frame_rem) {
|
|
|
|
union ctu_can_fd_frame_form_w frame_form_w;
|
|
|
|
frame_form_w.u32 = val;
|
|
|
|
s->rx_frame_rem = frame_form_w.s.rwcnt * 4 + 4;
|
|
|
|
}
|
|
|
|
s->rx_cnt -= 4;
|
|
|
|
s->rx_frame_rem -= 4;
|
|
|
|
if (!s->rx_frame_rem) {
|
|
|
|
s->rx_status_rx_settings.s.rxfrc--;
|
|
|
|
if (!s->rx_status_rx_settings.s.rxfrc) {
|
|
|
|
s->status.s.rxne = 0;
|
|
|
|
s->status.s.idle = 1;
|
|
|
|
s->status.s.rxs = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->rx_tail_pos = (s->rx_tail_pos + 4) % CTUCAN_RCV_BUF_LEN;
|
|
|
|
} else {
|
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_TX_STATUS:
|
|
|
|
val = s->tx_status.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_TX_PRIORITY:
|
|
|
|
val = s->tx_priority.u32;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_RX_FR_CTR:
|
|
|
|
val = s->rx_fr_ctr.s.rx_fr_ctr_val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_TX_FR_CTR:
|
|
|
|
val = s->tx_fr_ctr.s.tx_fr_ctr_val;
|
|
|
|
break;
|
|
|
|
case CTU_CAN_FD_YOLO_REG:
|
|
|
|
val = s->yolo_reg.s.yolo_val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
val >>= ((addr & 3) << 3);
|
|
|
|
if (size < 8) {
|
|
|
|
val &= ((uint64_t)1 << (size << 3)) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ctucan_can_receive(CanBusClientState *client)
|
|
|
|
{
|
|
|
|
CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
|
|
|
|
|
|
|
|
if (!s->mode_settings.s.ena) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true; /* always return true, when operation mode */
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t ctucan_receive(CanBusClientState *client, const qemu_can_frame *frames,
|
|
|
|
size_t frames_cnt)
|
|
|
|
{
|
|
|
|
CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
|
|
|
|
static uint8_t rcv[CTUCAN_MSG_MAX_LEN];
|
|
|
|
int i;
|
|
|
|
int ret = -1;
|
|
|
|
const qemu_can_frame *frame = frames;
|
|
|
|
union ctu_can_fd_int_stat int_stat;
|
|
|
|
int_stat.u32 = 0;
|
|
|
|
|
|
|
|
if (frames_cnt <= 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ctucan_frame2buff(frame, rcv);
|
|
|
|
|
|
|
|
if (s->rx_cnt + ret > CTUCAN_RCV_BUF_LEN) { /* Data overrun. */
|
|
|
|
s->status.s.dor = 1;
|
|
|
|
int_stat.s.doi = 1;
|
|
|
|
s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
|
|
|
|
ctucan_update_irq(s);
|
|
|
|
DPRINTF("Receive FIFO overrun\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
s->status.s.idle = 0;
|
|
|
|
s->status.s.rxs = 1;
|
|
|
|
int_stat.s.rxi = 1;
|
|
|
|
if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
|
|
|
|
int_stat.s.rxfi = 1;
|
|
|
|
}
|
|
|
|
s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
|
|
|
|
s->rx_fr_ctr.s.rx_fr_ctr_val++;
|
|
|
|
s->rx_status_rx_settings.s.rxfrc++;
|
|
|
|
for (i = 0; i < ret; i++) {
|
|
|
|
s->rx_buff[(s->rx_tail_pos + s->rx_cnt) % CTUCAN_RCV_BUF_LEN] = rcv[i];
|
|
|
|
s->rx_cnt++;
|
|
|
|
}
|
|
|
|
s->status.s.rxne = 1;
|
|
|
|
|
|
|
|
ctucan_update_irq(s);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CanBusClientInfo ctucan_bus_client_info = {
|
|
|
|
.can_receive = ctucan_can_receive,
|
|
|
|
.receive = ctucan_receive,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus)
|
|
|
|
{
|
|
|
|
s->bus_client.info = &ctucan_bus_client_info;
|
|
|
|
|
|
|
|
if (!bus) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (can_bus_insert_client(bus, &s->bus_client) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ctucan_disconnect(CtuCanCoreState *s)
|
|
|
|
{
|
|
|
|
can_bus_remove_client(&s->bus_client);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ctucan_init(CtuCanCoreState *s, qemu_irq irq)
|
|
|
|
{
|
|
|
|
s->irq = irq;
|
|
|
|
|
|
|
|
qemu_irq_lower(s->irq);
|
|
|
|
|
|
|
|
ctucan_hardware_reset(s);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const VMStateDescription vmstate_qemu_ctucan_tx_buffer = {
|
|
|
|
.name = "qemu_ctucan_tx_buffer",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2023-12-21 06:16:22 +03:00
|
|
|
.fields = (const VMStateField[]) {
|
2020-09-14 11:13:40 +03:00
|
|
|
VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ctucan_post_load(void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
CtuCanCoreState *s = opaque;
|
|
|
|
ctucan_update_irq(s);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* VMState is needed for live migration of QEMU images */
|
|
|
|
const VMStateDescription vmstate_ctucan = {
|
|
|
|
.name = "ctucan",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.post_load = ctucan_post_load,
|
2023-12-21 06:16:22 +03:00
|
|
|
.fields = (const VMStateField[]) {
|
2020-09-14 11:13:40 +03:00
|
|
|
VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(status.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(int_stat.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(int_ena.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(int_mask.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(brt.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(brt_fd.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(ewl_erp_fault_state.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rec_tec.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(err_norm_err_fd.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(ctr_pres.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_a_mask.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_a_val.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_b_mask.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_b_val.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_c_mask.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_c_val.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_ran_low.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_ran_high.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(filter_control_filter_status.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_mem_info.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_pointers.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_status_rx_settings.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(tx_status.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(tx_priority.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(err_capt_alc.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(trv_delay_ssp_cfg.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_fr_ctr.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(tx_fr_ctr.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(debug_register.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(yolo_reg.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(timestamp_low.u32, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(timestamp_high.u32, CtuCanCoreState),
|
|
|
|
|
|
|
|
VMSTATE_STRUCT_ARRAY(tx_buffer, CtuCanCoreState,
|
|
|
|
CTUCAN_CORE_TXBUF_NUM, 0, vmstate_qemu_ctucan_tx_buffer,
|
|
|
|
CtuCanCoreMsgBuffer),
|
|
|
|
|
|
|
|
VMSTATE_BUFFER(rx_buff, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_tail_pos, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_cnt, CtuCanCoreState),
|
|
|
|
VMSTATE_UINT32(rx_frame_rem, CtuCanCoreState),
|
|
|
|
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|