| /* | 
 |  * QEMU model of the Xilinx ZynqMP CAN controller. | 
 |  * This implementation is based on the following datasheet: | 
 |  * https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf | 
 |  * | 
 |  * Copyright (c) 2020 Xilinx Inc. | 
 |  * | 
 |  * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com> | 
 |  * | 
 |  * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren 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 "hw/sysbus.h" | 
 | #include "hw/register.h" | 
 | #include "hw/irq.h" | 
 | #include "qapi/error.h" | 
 | #include "qemu/bitops.h" | 
 | #include "qemu/log.h" | 
 | #include "qemu/cutils.h" | 
 | #include "migration/vmstate.h" | 
 | #include "hw/qdev-properties.h" | 
 | #include "net/can_emu.h" | 
 | #include "net/can_host.h" | 
 | #include "qemu/event_notifier.h" | 
 | #include "qom/object_interfaces.h" | 
 | #include "hw/net/xlnx-zynqmp-can.h" | 
 | #include "trace.h" | 
 |  | 
 | #ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG | 
 | #define XLNX_ZYNQMP_CAN_ERR_DEBUG 0 | 
 | #endif | 
 |  | 
 | #define MAX_DLC            8 | 
 | #undef ERROR | 
 |  | 
 | REG32(SOFTWARE_RESET_REGISTER, 0x0) | 
 |     FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1) | 
 |     FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1) | 
 | REG32(MODE_SELECT_REGISTER, 0x4) | 
 |     FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1) | 
 |     FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1) | 
 |     FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1) | 
 | REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8) | 
 |     FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8) | 
 | REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc) | 
 |     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 7, 2) | 
 |     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 4, 3) | 
 |     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 4) | 
 | REG32(ERROR_COUNTER_REGISTER, 0x10) | 
 |     FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8) | 
 |     FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8) | 
 | REG32(ERROR_STATUS_REGISTER, 0x14) | 
 |     FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1) | 
 |     FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1) | 
 |     FIELD(ERROR_STATUS_REGISTER, STER, 2, 1) | 
 |     FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1) | 
 |     FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1) | 
 | REG32(STATUS_REGISTER, 0x18) | 
 |     FIELD(STATUS_REGISTER, SNOOP, 12, 1) | 
 |     FIELD(STATUS_REGISTER, ACFBSY, 11, 1) | 
 |     FIELD(STATUS_REGISTER, TXFLL, 10, 1) | 
 |     FIELD(STATUS_REGISTER, TXBFLL, 9, 1) | 
 |     FIELD(STATUS_REGISTER, ESTAT, 7, 2) | 
 |     FIELD(STATUS_REGISTER, ERRWRN, 6, 1) | 
 |     FIELD(STATUS_REGISTER, BBSY, 5, 1) | 
 |     FIELD(STATUS_REGISTER, BIDLE, 4, 1) | 
 |     FIELD(STATUS_REGISTER, NORMAL, 3, 1) | 
 |     FIELD(STATUS_REGISTER, SLEEP, 2, 1) | 
 |     FIELD(STATUS_REGISTER, LBACK, 1, 1) | 
 |     FIELD(STATUS_REGISTER, CONFIG, 0, 1) | 
 | REG32(INTERRUPT_STATUS_REGISTER, 0x1c) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, TXFEMP, 14, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, TXFWMEMP, 13, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, ERROR, 8, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, RXNEMP, 7, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, RXOFLW, 6, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, RXUFLW, 5, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, TXBFLL, 3, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, TXFLL, 2, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1) | 
 |     FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1) | 
 | REG32(INTERRUPT_ENABLE_REGISTER, 0x20) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFEMP, 14, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFWMEMP, 13, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ERXNEMP, 7, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ERXOFLW, 6, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ERXUFLW, 5, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ETXBFLL, 3, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFLL, 2, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1) | 
 |     FIELD(INTERRUPT_ENABLE_REGISTER, EARBLST, 0, 1) | 
 | REG32(INTERRUPT_CLEAR_REGISTER, 0x24) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFEMP, 14, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFWMEMP, 13, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CRXNEMP, 7, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CRXOFLW, 6, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CRXUFLW, 5, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CTXBFLL, 3, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFLL, 2, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1) | 
 |     FIELD(INTERRUPT_CLEAR_REGISTER, CARBLST, 0, 1) | 
 | REG32(TIMESTAMP_REGISTER, 0x28) | 
 |     FIELD(TIMESTAMP_REGISTER, CTS, 0, 1) | 
 | REG32(WIR, 0x2c) | 
 |     FIELD(WIR, EW, 8, 8) | 
 |     FIELD(WIR, FW, 0, 8) | 
 | REG32(TXFIFO_ID, 0x30) | 
 |     FIELD(TXFIFO_ID, IDH, 21, 11) | 
 |     FIELD(TXFIFO_ID, SRRRTR, 20, 1) | 
 |     FIELD(TXFIFO_ID, IDE, 19, 1) | 
 |     FIELD(TXFIFO_ID, IDL, 1, 18) | 
 |     FIELD(TXFIFO_ID, RTR, 0, 1) | 
 | REG32(TXFIFO_DLC, 0x34) | 
 |     FIELD(TXFIFO_DLC, DLC, 28, 4) | 
 | REG32(TXFIFO_DATA1, 0x38) | 
 |     FIELD(TXFIFO_DATA1, DB0, 24, 8) | 
 |     FIELD(TXFIFO_DATA1, DB1, 16, 8) | 
 |     FIELD(TXFIFO_DATA1, DB2, 8, 8) | 
 |     FIELD(TXFIFO_DATA1, DB3, 0, 8) | 
 | REG32(TXFIFO_DATA2, 0x3c) | 
 |     FIELD(TXFIFO_DATA2, DB4, 24, 8) | 
 |     FIELD(TXFIFO_DATA2, DB5, 16, 8) | 
 |     FIELD(TXFIFO_DATA2, DB6, 8, 8) | 
 |     FIELD(TXFIFO_DATA2, DB7, 0, 8) | 
 | REG32(TXHPB_ID, 0x40) | 
 |     FIELD(TXHPB_ID, IDH, 21, 11) | 
 |     FIELD(TXHPB_ID, SRRRTR, 20, 1) | 
 |     FIELD(TXHPB_ID, IDE, 19, 1) | 
 |     FIELD(TXHPB_ID, IDL, 1, 18) | 
 |     FIELD(TXHPB_ID, RTR, 0, 1) | 
 | REG32(TXHPB_DLC, 0x44) | 
 |     FIELD(TXHPB_DLC, DLC, 28, 4) | 
 | REG32(TXHPB_DATA1, 0x48) | 
 |     FIELD(TXHPB_DATA1, DB0, 24, 8) | 
 |     FIELD(TXHPB_DATA1, DB1, 16, 8) | 
 |     FIELD(TXHPB_DATA1, DB2, 8, 8) | 
 |     FIELD(TXHPB_DATA1, DB3, 0, 8) | 
 | REG32(TXHPB_DATA2, 0x4c) | 
 |     FIELD(TXHPB_DATA2, DB4, 24, 8) | 
 |     FIELD(TXHPB_DATA2, DB5, 16, 8) | 
 |     FIELD(TXHPB_DATA2, DB6, 8, 8) | 
 |     FIELD(TXHPB_DATA2, DB7, 0, 8) | 
 | REG32(RXFIFO_ID, 0x50) | 
 |     FIELD(RXFIFO_ID, IDH, 21, 11) | 
 |     FIELD(RXFIFO_ID, SRRRTR, 20, 1) | 
 |     FIELD(RXFIFO_ID, IDE, 19, 1) | 
 |     FIELD(RXFIFO_ID, IDL, 1, 18) | 
 |     FIELD(RXFIFO_ID, RTR, 0, 1) | 
 | REG32(RXFIFO_DLC, 0x54) | 
 |     FIELD(RXFIFO_DLC, DLC, 28, 4) | 
 |     FIELD(RXFIFO_DLC, RXT, 0, 16) | 
 | REG32(RXFIFO_DATA1, 0x58) | 
 |     FIELD(RXFIFO_DATA1, DB0, 24, 8) | 
 |     FIELD(RXFIFO_DATA1, DB1, 16, 8) | 
 |     FIELD(RXFIFO_DATA1, DB2, 8, 8) | 
 |     FIELD(RXFIFO_DATA1, DB3, 0, 8) | 
 | REG32(RXFIFO_DATA2, 0x5c) | 
 |     FIELD(RXFIFO_DATA2, DB4, 24, 8) | 
 |     FIELD(RXFIFO_DATA2, DB5, 16, 8) | 
 |     FIELD(RXFIFO_DATA2, DB6, 8, 8) | 
 |     FIELD(RXFIFO_DATA2, DB7, 0, 8) | 
 | REG32(AFR, 0x60) | 
 |     FIELD(AFR, UAF4, 3, 1) | 
 |     FIELD(AFR, UAF3, 2, 1) | 
 |     FIELD(AFR, UAF2, 1, 1) | 
 |     FIELD(AFR, UAF1, 0, 1) | 
 | REG32(AFMR1, 0x64) | 
 |     FIELD(AFMR1, AMIDH, 21, 11) | 
 |     FIELD(AFMR1, AMSRR, 20, 1) | 
 |     FIELD(AFMR1, AMIDE, 19, 1) | 
 |     FIELD(AFMR1, AMIDL, 1, 18) | 
 |     FIELD(AFMR1, AMRTR, 0, 1) | 
 | REG32(AFIR1, 0x68) | 
 |     FIELD(AFIR1, AIIDH, 21, 11) | 
 |     FIELD(AFIR1, AISRR, 20, 1) | 
 |     FIELD(AFIR1, AIIDE, 19, 1) | 
 |     FIELD(AFIR1, AIIDL, 1, 18) | 
 |     FIELD(AFIR1, AIRTR, 0, 1) | 
 | REG32(AFMR2, 0x6c) | 
 |     FIELD(AFMR2, AMIDH, 21, 11) | 
 |     FIELD(AFMR2, AMSRR, 20, 1) | 
 |     FIELD(AFMR2, AMIDE, 19, 1) | 
 |     FIELD(AFMR2, AMIDL, 1, 18) | 
 |     FIELD(AFMR2, AMRTR, 0, 1) | 
 | REG32(AFIR2, 0x70) | 
 |     FIELD(AFIR2, AIIDH, 21, 11) | 
 |     FIELD(AFIR2, AISRR, 20, 1) | 
 |     FIELD(AFIR2, AIIDE, 19, 1) | 
 |     FIELD(AFIR2, AIIDL, 1, 18) | 
 |     FIELD(AFIR2, AIRTR, 0, 1) | 
 | REG32(AFMR3, 0x74) | 
 |     FIELD(AFMR3, AMIDH, 21, 11) | 
 |     FIELD(AFMR3, AMSRR, 20, 1) | 
 |     FIELD(AFMR3, AMIDE, 19, 1) | 
 |     FIELD(AFMR3, AMIDL, 1, 18) | 
 |     FIELD(AFMR3, AMRTR, 0, 1) | 
 | REG32(AFIR3, 0x78) | 
 |     FIELD(AFIR3, AIIDH, 21, 11) | 
 |     FIELD(AFIR3, AISRR, 20, 1) | 
 |     FIELD(AFIR3, AIIDE, 19, 1) | 
 |     FIELD(AFIR3, AIIDL, 1, 18) | 
 |     FIELD(AFIR3, AIRTR, 0, 1) | 
 | REG32(AFMR4, 0x7c) | 
 |     FIELD(AFMR4, AMIDH, 21, 11) | 
 |     FIELD(AFMR4, AMSRR, 20, 1) | 
 |     FIELD(AFMR4, AMIDE, 19, 1) | 
 |     FIELD(AFMR4, AMIDL, 1, 18) | 
 |     FIELD(AFMR4, AMRTR, 0, 1) | 
 | REG32(AFIR4, 0x80) | 
 |     FIELD(AFIR4, AIIDH, 21, 11) | 
 |     FIELD(AFIR4, AISRR, 20, 1) | 
 |     FIELD(AFIR4, AIIDE, 19, 1) | 
 |     FIELD(AFIR4, AIIDL, 1, 18) | 
 |     FIELD(AFIR4, AIRTR, 0, 1) | 
 |  | 
 | static void can_update_irq(XlnxZynqMPCANState *s) | 
 | { | 
 |     uint32_t irq; | 
 |  | 
 |     /* Watermark register interrupts. */ | 
 |     if ((fifo32_num_free(&s->tx_fifo) / CAN_FRAME_SIZE) > | 
 |             ARRAY_FIELD_EX32(s->regs, WIR, EW)) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFWMEMP, 1); | 
 |     } | 
 |  | 
 |     if ((fifo32_num_used(&s->rx_fifo) / CAN_FRAME_SIZE) > | 
 |             ARRAY_FIELD_EX32(s->regs, WIR, FW)) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1); | 
 |     } | 
 |  | 
 |     /* RX Interrupts. */ | 
 |     if (fifo32_num_used(&s->rx_fifo) >= CAN_FRAME_SIZE) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXNEMP, 1); | 
 |     } | 
 |  | 
 |     /* TX interrupts. */ | 
 |     if (fifo32_is_empty(&s->tx_fifo)) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFEMP, 1); | 
 |     } | 
 |  | 
 |     if (fifo32_is_full(&s->tx_fifo)) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFLL, 1); | 
 |     } | 
 |  | 
 |     if (fifo32_is_full(&s->txhpb_fifo)) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXBFLL, 1); | 
 |     } | 
 |  | 
 |     irq = s->regs[R_INTERRUPT_STATUS_REGISTER]; | 
 |     irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER]; | 
 |  | 
 |     trace_xlnx_can_update_irq(s->regs[R_INTERRUPT_STATUS_REGISTER], | 
 |                               s->regs[R_INTERRUPT_ENABLE_REGISTER], irq); | 
 |     qemu_set_irq(s->irq, irq); | 
 | } | 
 |  | 
 | static void can_ier_post_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     can_update_irq(s); | 
 | } | 
 |  | 
 | static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val; | 
 |     can_update_irq(s); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static void can_config_reset(XlnxZynqMPCANState *s) | 
 | { | 
 |     /* Reset all the configuration registers. */ | 
 |     register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]); | 
 |     register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]); | 
 |     register_reset( | 
 |               &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER]); | 
 |     register_reset(&s->reg_info[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]); | 
 |     register_reset(&s->reg_info[R_STATUS_REGISTER]); | 
 |     register_reset(&s->reg_info[R_INTERRUPT_STATUS_REGISTER]); | 
 |     register_reset(&s->reg_info[R_INTERRUPT_ENABLE_REGISTER]); | 
 |     register_reset(&s->reg_info[R_INTERRUPT_CLEAR_REGISTER]); | 
 |     register_reset(&s->reg_info[R_WIR]); | 
 | } | 
 |  | 
 | static void can_config_mode(XlnxZynqMPCANState *s) | 
 | { | 
 |     register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]); | 
 |     register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]); | 
 |  | 
 |     /* Put XlnxZynqMPCAN in configuration mode. */ | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0); | 
 |  | 
 |     can_update_irq(s); | 
 | } | 
 |  | 
 | static void update_status_register_mode_bits(XlnxZynqMPCANState *s) | 
 | { | 
 |     bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP); | 
 |     bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP); | 
 |     /* Wake up interrupt bit. */ | 
 |     bool wakeup_irq_val = sleep_status && (sleep_mode == 0); | 
 |     /* Sleep interrupt bit. */ | 
 |     bool sleep_irq_val = sleep_mode && (sleep_status == 0); | 
 |  | 
 |     /* Clear previous core mode status bits. */ | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0); | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0); | 
 |  | 
 |     /* set current mode bit and generate irqs accordingly. */ | 
 |     if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) { | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1); | 
 |     } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) { | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1); | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, | 
 |                          sleep_irq_val); | 
 |     } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1); | 
 |     } else { | 
 |         /* | 
 |          * If all bits are zero then XlnxZynqMPCAN is set in normal mode. | 
 |          */ | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1); | 
 |         /* Set wakeup interrupt bit. */ | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, | 
 |                          wakeup_irq_val); | 
 |     } | 
 |  | 
 |     can_update_irq(s); | 
 | } | 
 |  | 
 | static void can_exit_sleep_mode(XlnxZynqMPCANState *s) | 
 | { | 
 |     ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0); | 
 |     update_status_register_mode_bits(s); | 
 | } | 
 |  | 
 | static void generate_frame(qemu_can_frame *frame, uint32_t *data) | 
 | { | 
 |     frame->can_id = data[0]; | 
 |     frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC); | 
 |  | 
 |     frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3); | 
 |     frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2); | 
 |     frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1); | 
 |     frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0); | 
 |  | 
 |     frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7); | 
 |     frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6); | 
 |     frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5); | 
 |     frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4); | 
 | } | 
 |  | 
 | static bool tx_ready_check(XlnxZynqMPCANState *s) | 
 | { | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" | 
 |                       " data while controller is in reset mode.\n", | 
 |                       path); | 
 |         return false; | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer" | 
 |                       " data while controller is in configuration mode. Reset" | 
 |                       " the core so operations can start fresh.\n", | 
 |                       path); | 
 |         return false; | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer" | 
 |                       " data while controller is in SNOOP MODE.\n", | 
 |                       path); | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | static void read_tx_frame(XlnxZynqMPCANState *s, Fifo32 *fifo, uint32_t *data) | 
 | { | 
 |     unsigned used = fifo32_num_used(fifo); | 
 |     bool is_txhpb = fifo == &s->txhpb_fifo; | 
 |  | 
 |     assert(used > 0); | 
 |     used %= CAN_FRAME_SIZE; | 
 |  | 
 |     /* | 
 |      * Frame Message Format | 
 |      * | 
 |      * Each frame includes four words (16 bytes). Software must read and write | 
 |      * all four words regardless of the actual number of data bytes and valid | 
 |      * fields in the message. | 
 |      * If software misbehave (not writing all four words), we use the previous | 
 |      * registers content to initialize each missing word. | 
 |      * | 
 |      * If used is 1 then ID, DLC and DATA1 are missing. | 
 |      * if used is 2 then ID and DLC are missing. | 
 |      * if used is 3 then only ID is missing. | 
 |      */ | 
 |      if (used > 0) { | 
 |         data[0] = s->regs[is_txhpb ? R_TXHPB_ID : R_TXFIFO_ID]; | 
 |     } else { | 
 |         data[0] = fifo32_pop(fifo); | 
 |     } | 
 |     if (used == 1 || used == 2) { | 
 |         data[1] = s->regs[is_txhpb ? R_TXHPB_DLC : R_TXFIFO_DLC]; | 
 |     } else { | 
 |         data[1] = fifo32_pop(fifo); | 
 |     } | 
 |     if (used == 1) { | 
 |         data[2] = s->regs[is_txhpb ? R_TXHPB_DATA1 : R_TXFIFO_DATA1]; | 
 |     } else { | 
 |         data[2] = fifo32_pop(fifo); | 
 |     } | 
 |     /* DATA2 triggered the transfer thus is always available */ | 
 |     data[3] = fifo32_pop(fifo); | 
 |  | 
 |     if (used) { | 
 |         qemu_log_mask(LOG_GUEST_ERROR, | 
 |                       "%s: Incomplete CAN frame (only %u/%u slots used)\n", | 
 |                       TYPE_XLNX_ZYNQMP_CAN, used, CAN_FRAME_SIZE); | 
 |     } | 
 | } | 
 |  | 
 | static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) | 
 | { | 
 |     qemu_can_frame frame; | 
 |     uint32_t data[CAN_FRAME_SIZE]; | 
 |     int i; | 
 |     bool can_tx = tx_ready_check(s); | 
 |  | 
 |     if (!can_tx) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is not enabled for data" | 
 |                       " transfer.\n", path); | 
 |         can_update_irq(s); | 
 |         return; | 
 |     } | 
 |  | 
 |     while (!fifo32_is_empty(fifo)) { | 
 |         read_tx_frame(s, fifo, data); | 
 |  | 
 |         if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { | 
 |             /* | 
 |              * Controller is in loopback. In Loopback mode, the CAN core | 
 |              * transmits a recessive bitstream on to the XlnxZynqMPCAN Bus. | 
 |              * Any message transmitted is looped back to the RX line and | 
 |              * acknowledged. The XlnxZynqMPCAN core receives any message | 
 |              * that it transmits. | 
 |              */ | 
 |             if (fifo32_is_full(&s->rx_fifo)) { | 
 |                 ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1); | 
 |             } else { | 
 |                 for (i = 0; i < CAN_FRAME_SIZE; i++) { | 
 |                     fifo32_push(&s->rx_fifo, data[i]); | 
 |                 } | 
 |  | 
 |                 ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); | 
 |             } | 
 |         } else { | 
 |             /* Normal mode Tx. */ | 
 |             generate_frame(&frame, data); | 
 |  | 
 |             trace_xlnx_can_tx_data(frame.can_id, frame.can_dlc, | 
 |                                    frame.data[0], frame.data[1], | 
 |                                    frame.data[2], frame.data[3], | 
 |                                    frame.data[4], frame.data[5], | 
 |                                    frame.data[6], frame.data[7]); | 
 |             can_bus_client_send(&s->bus_client, &frame, 1); | 
 |         } | 
 |     } | 
 |  | 
 |     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1); | 
 |     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, TXBFLL, 0); | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) { | 
 |         can_exit_sleep_mode(s); | 
 |     } | 
 |  | 
 |     can_update_irq(s); | 
 | } | 
 |  | 
 | static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN, | 
 |                      FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN)); | 
 |  | 
 |     if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) { | 
 |         trace_xlnx_can_reset(val); | 
 |  | 
 |         /* First, core will do software reset then will enter in config mode. */ | 
 |         can_config_reset(s); | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | 
 |         can_config_mode(s); | 
 |     } else { | 
 |         /* | 
 |          * Leave config mode. Now XlnxZynqMPCAN core will enter normal, | 
 |          * sleep, snoop or loopback mode depending upon LBACK, SLEEP, SNOOP | 
 |          * register states. | 
 |          */ | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0); | 
 |  | 
 |         ptimer_transaction_begin(s->can_timer); | 
 |         ptimer_set_count(s->can_timer, 0); | 
 |         ptimer_transaction_commit(s->can_timer); | 
 |  | 
 |         /* XlnxZynqMPCAN is out of config mode. It will send pending data. */ | 
 |         transfer_fifo(s, &s->txhpb_fifo); | 
 |         transfer_fifo(s, &s->tx_fifo); | 
 |     } | 
 |  | 
 |     update_status_register_mode_bits(s); | 
 |  | 
 |     return s->regs[R_SOFTWARE_RESET_REGISTER]; | 
 | } | 
 |  | 
 | static uint64_t can_msr_pre_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |     uint8_t multi_mode; | 
 |  | 
 |     /* | 
 |      * Multiple mode set check. This is done to make sure user doesn't set | 
 |      * multiple modes. | 
 |      */ | 
 |     multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) + | 
 |                  FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) + | 
 |                  FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP); | 
 |  | 
 |     if (multi_mode > 1) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to config" | 
 |                       " several modes simultaneously. One mode will be selected" | 
 |                       " according to their priority: LBACK > SLEEP > SNOOP.\n", | 
 |                       path); | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | 
 |         /* We are in configuration mode, any mode can be selected. */ | 
 |         s->regs[R_MODE_SELECT_REGISTER] = val; | 
 |     } else { | 
 |         bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP); | 
 |  | 
 |         ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit); | 
 |  | 
 |         if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) { | 
 |             g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |             qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set" | 
 |                           " LBACK mode without setting CEN bit as 0.\n", | 
 |                           path); | 
 |         } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) { | 
 |             g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |             qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set" | 
 |                           " SNOOP mode without setting CEN bit as 0.\n", | 
 |                           path); | 
 |         } | 
 |  | 
 |         update_status_register_mode_bits(s); | 
 |     } | 
 |  | 
 |     return s->regs[R_MODE_SELECT_REGISTER]; | 
 | } | 
 |  | 
 | static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     /* Only allow writes when in config mode. */ | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | 
 |         return s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER]; | 
 |     } | 
 |  | 
 |     return val; | 
 | } | 
 |  | 
 | static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     /* Only allow writes when in config mode. */ | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | 
 |         return s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]; | 
 |     } | 
 |  | 
 |     return val; | 
 | } | 
 |  | 
 | static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) { | 
 |         ptimer_transaction_begin(s->can_timer); | 
 |         ptimer_set_count(s->can_timer, 0); | 
 |         ptimer_transaction_commit(s->can_timer); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame) | 
 | { | 
 |     bool filter_pass = false; | 
 |     uint16_t timestamp = 0; | 
 |  | 
 |     /* If no filter is enabled. Message will be stored in FIFO. */ | 
 |     if (!((ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) | | 
 |        (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) | | 
 |        (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) | | 
 |        (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)))) { | 
 |         filter_pass = true; | 
 |     } | 
 |  | 
 |     /* | 
 |      * Messages that pass any of the acceptance filters will be stored in | 
 |      * the RX FIFO. | 
 |      */ | 
 |     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) { | 
 |         uint32_t id_masked = s->regs[R_AFMR1] & frame->can_id; | 
 |         uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1]; | 
 |  | 
 |         if (filter_id_masked == id_masked) { | 
 |             filter_pass = true; | 
 |         } | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) { | 
 |         uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id; | 
 |         uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2]; | 
 |  | 
 |         if (filter_id_masked == id_masked) { | 
 |             filter_pass = true; | 
 |         } | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) { | 
 |         uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id; | 
 |         uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3]; | 
 |  | 
 |         if (filter_id_masked == id_masked) { | 
 |             filter_pass = true; | 
 |         } | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) { | 
 |         uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id; | 
 |         uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4]; | 
 |  | 
 |         if (filter_id_masked == id_masked) { | 
 |             filter_pass = true; | 
 |         } | 
 |     } | 
 |  | 
 |     if (!filter_pass) { | 
 |         trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc); | 
 |         return; | 
 |     } | 
 |  | 
 |     /* Store the message in fifo if it passed through any of the filters. */ | 
 |     if (filter_pass && frame->can_dlc <= MAX_DLC) { | 
 |  | 
 |         if (fifo32_is_full(&s->rx_fifo)) { | 
 |             ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1); | 
 |         } else { | 
 |             timestamp = CAN_TIMER_MAX - ptimer_get_count(s->can_timer); | 
 |  | 
 |             fifo32_push(&s->rx_fifo, frame->can_id); | 
 |  | 
 |             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT, | 
 |                                                R_RXFIFO_DLC_DLC_LENGTH, | 
 |                                                frame->can_dlc) | | 
 |                                      deposit32(0, R_RXFIFO_DLC_RXT_SHIFT, | 
 |                                                R_RXFIFO_DLC_RXT_LENGTH, | 
 |                                                timestamp)); | 
 |  | 
 |             /* First 32 bit of the data. */ | 
 |             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA1_DB3_SHIFT, | 
 |                                                R_RXFIFO_DATA1_DB3_LENGTH, | 
 |                                                frame->data[0]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA1_DB2_SHIFT, | 
 |                                                R_RXFIFO_DATA1_DB2_LENGTH, | 
 |                                                frame->data[1]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA1_DB1_SHIFT, | 
 |                                                R_RXFIFO_DATA1_DB1_LENGTH, | 
 |                                                frame->data[2]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA1_DB0_SHIFT, | 
 |                                                R_RXFIFO_DATA1_DB0_LENGTH, | 
 |                                                frame->data[3])); | 
 |             /* Last 32 bit of the data. */ | 
 |             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA2_DB7_SHIFT, | 
 |                                                R_RXFIFO_DATA2_DB7_LENGTH, | 
 |                                                frame->data[4]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA2_DB6_SHIFT, | 
 |                                                R_RXFIFO_DATA2_DB6_LENGTH, | 
 |                                                frame->data[5]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA2_DB5_SHIFT, | 
 |                                                R_RXFIFO_DATA2_DB5_LENGTH, | 
 |                                                frame->data[6]) | | 
 |                                      deposit32(0, R_RXFIFO_DATA2_DB4_SHIFT, | 
 |                                                R_RXFIFO_DATA2_DB4_LENGTH, | 
 |                                                frame->data[7])); | 
 |  | 
 |             ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); | 
 |             trace_xlnx_can_rx_data(frame->can_id, frame->can_dlc, | 
 |                                    frame->data[0], frame->data[1], | 
 |                                    frame->data[2], frame->data[3], | 
 |                                    frame->data[4], frame->data[5], | 
 |                                    frame->data[6], frame->data[7]); | 
 |         } | 
 |  | 
 |         can_update_irq(s); | 
 |     } | 
 | } | 
 |  | 
 | static uint64_t can_rxfifo_post_read_id(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |     unsigned used = fifo32_num_used(&s->rx_fifo); | 
 |  | 
 |     if (used < CAN_FRAME_SIZE) { | 
 |         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1); | 
 |     } else { | 
 |         val = s->regs[R_RXFIFO_ID] = fifo32_pop(&s->rx_fifo); | 
 |         s->regs[R_RXFIFO_DLC] = fifo32_pop(&s->rx_fifo); | 
 |         s->regs[R_RXFIFO_DATA1] = fifo32_pop(&s->rx_fifo); | 
 |         s->regs[R_RXFIFO_DATA2] = fifo32_pop(&s->rx_fifo); | 
 |     } | 
 |  | 
 |     can_update_irq(s); | 
 |     return val; | 
 | } | 
 |  | 
 | static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1) && | 
 |         ARRAY_FIELD_EX32(s->regs, AFR, UAF2) && | 
 |         ARRAY_FIELD_EX32(s->regs, AFR, UAF3) && | 
 |         ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) { | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 1); | 
 |     } else { | 
 |         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0); | 
 |     } | 
 | } | 
 |  | 
 | static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |     uint32_t reg_idx = (reg->access->addr) / 4; | 
 |     uint32_t filter_number = (reg_idx - R_AFMR1) / 2; | 
 |  | 
 |     /* modify an acceptance filter, the corresponding UAF bit should be '0'. */ | 
 |     if (!(s->regs[R_AFR] & (1 << filter_number))) { | 
 |         s->regs[reg_idx] = val; | 
 |  | 
 |         trace_xlnx_can_filter_mask_pre_write(filter_number, s->regs[reg_idx]); | 
 |     } else { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d" | 
 |                       " mask is not set as corresponding UAF bit is not 0.\n", | 
 |                       path, filter_number + 1); | 
 |     } | 
 |  | 
 |     return s->regs[reg_idx]; | 
 | } | 
 |  | 
 | static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |     uint32_t reg_idx = (reg->access->addr) / 4; | 
 |     uint32_t filter_number = (reg_idx - R_AFIR1) / 2; | 
 |  | 
 |     if (!(s->regs[R_AFR] & (1 << filter_number))) { | 
 |         s->regs[reg_idx] = val; | 
 |  | 
 |         trace_xlnx_can_filter_id_pre_write(filter_number, s->regs[reg_idx]); | 
 |     } else { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d" | 
 |                       " id is not set as corresponding UAF bit is not 0.\n", | 
 |                       path, filter_number + 1); | 
 |     } | 
 |  | 
 |     return s->regs[reg_idx]; | 
 | } | 
 |  | 
 | static void can_tx_post_write(RegisterInfo *reg, uint64_t val) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | 
 |  | 
 |     bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2; | 
 |  | 
 |     bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) || | 
 |                              (reg->access->addr == A_TXHPB_DATA2); | 
 |  | 
 |     Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo; | 
 |  | 
 |     if (!fifo32_is_full(f)) { | 
 |         fifo32_push(f, val); | 
 |     } else { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: TX FIFO is full.\n", path); | 
 |     } | 
 |  | 
 |     /* Initiate the message send if TX register is written. */ | 
 |     if (initiate_transfer && | 
 |         ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | 
 |         transfer_fifo(s, f); | 
 |     } | 
 |  | 
 |     can_update_irq(s); | 
 | } | 
 |  | 
 | static const RegisterAccessInfo can_regs_info[] = { | 
 |     {   .name = "SOFTWARE_RESET_REGISTER", | 
 |         .addr = A_SOFTWARE_RESET_REGISTER, | 
 |         .rsvd = 0xfffffffc, | 
 |         .pre_write = can_srr_pre_write, | 
 |     },{ .name = "MODE_SELECT_REGISTER", | 
 |         .addr = A_MODE_SELECT_REGISTER, | 
 |         .rsvd = 0xfffffff8, | 
 |         .pre_write = can_msr_pre_write, | 
 |     },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER", | 
 |         .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, | 
 |         .rsvd = 0xffffff00, | 
 |         .pre_write = can_brpr_pre_write, | 
 |     },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER", | 
 |         .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER, | 
 |         .rsvd = 0xfffffe00, | 
 |         .pre_write = can_btr_pre_write, | 
 |     },{ .name = "ERROR_COUNTER_REGISTER", | 
 |         .addr = A_ERROR_COUNTER_REGISTER, | 
 |         .rsvd = 0xffff0000, | 
 |         .ro = 0xffffffff, | 
 |     },{ .name = "ERROR_STATUS_REGISTER", | 
 |         .addr = A_ERROR_STATUS_REGISTER, | 
 |         .rsvd = 0xffffffe0, | 
 |         .w1c = 0x1f, | 
 |     },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER, | 
 |         .reset = 0x1, | 
 |         .rsvd = 0xffffe000, | 
 |         .ro = 0x1fff, | 
 |     },{ .name = "INTERRUPT_STATUS_REGISTER", | 
 |         .addr = A_INTERRUPT_STATUS_REGISTER, | 
 |         .reset = 0x6000, | 
 |         .rsvd = 0xffff8000, | 
 |         .ro = 0x7fff, | 
 |     },{ .name = "INTERRUPT_ENABLE_REGISTER", | 
 |         .addr = A_INTERRUPT_ENABLE_REGISTER, | 
 |         .rsvd = 0xffff8000, | 
 |         .post_write = can_ier_post_write, | 
 |     },{ .name = "INTERRUPT_CLEAR_REGISTER", | 
 |         .addr = A_INTERRUPT_CLEAR_REGISTER, | 
 |         .rsvd = 0xffff8000, | 
 |         .pre_write = can_icr_pre_write, | 
 |     },{ .name = "TIMESTAMP_REGISTER", | 
 |         .addr = A_TIMESTAMP_REGISTER, | 
 |         .rsvd = 0xfffffffe, | 
 |         .pre_write = can_tcr_pre_write, | 
 |     },{ .name = "WIR",  .addr = A_WIR, | 
 |         .reset = 0x3f3f, | 
 |         .rsvd = 0xffff0000, | 
 |     },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC, | 
 |         .rsvd = 0xfffffff, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC, | 
 |         .rsvd = 0xfffffff, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2, | 
 |         .post_write = can_tx_post_write, | 
 |     },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID, | 
 |         .ro = 0xffffffff, | 
 |         .post_read = can_rxfifo_post_read_id, | 
 |     },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC, | 
 |         .rsvd = 0xfff0000, | 
 |     },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1, | 
 |     },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2, | 
 |     },{ .name = "AFR",  .addr = A_AFR, | 
 |         .rsvd = 0xfffffff0, | 
 |         .post_write = can_filter_enable_post_write, | 
 |     },{ .name = "AFMR1",  .addr = A_AFMR1, | 
 |         .pre_write = can_filter_mask_pre_write, | 
 |     },{ .name = "AFIR1",  .addr = A_AFIR1, | 
 |         .pre_write = can_filter_id_pre_write, | 
 |     },{ .name = "AFMR2",  .addr = A_AFMR2, | 
 |         .pre_write = can_filter_mask_pre_write, | 
 |     },{ .name = "AFIR2",  .addr = A_AFIR2, | 
 |         .pre_write = can_filter_id_pre_write, | 
 |     },{ .name = "AFMR3",  .addr = A_AFMR3, | 
 |         .pre_write = can_filter_mask_pre_write, | 
 |     },{ .name = "AFIR3",  .addr = A_AFIR3, | 
 |         .pre_write = can_filter_id_pre_write, | 
 |     },{ .name = "AFMR4",  .addr = A_AFMR4, | 
 |         .pre_write = can_filter_mask_pre_write, | 
 |     },{ .name = "AFIR4",  .addr = A_AFIR4, | 
 |         .pre_write = can_filter_id_pre_write, | 
 |     } | 
 | }; | 
 |  | 
 | static void xlnx_zynqmp_can_ptimer_cb(void *opaque) | 
 | { | 
 |     /* No action required on the timer rollover. */ | 
 | } | 
 |  | 
 | static const MemoryRegionOps can_ops = { | 
 |     .read = register_read_memory, | 
 |     .write = register_write_memory, | 
 |     .endianness = DEVICE_LITTLE_ENDIAN, | 
 |     .valid = { | 
 |         .min_access_size = 4, | 
 |         .max_access_size = 4, | 
 |     }, | 
 | }; | 
 |  | 
 | static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | 
 |     unsigned int i; | 
 |  | 
 |     for (i = R_RXFIFO_ID; i < ARRAY_SIZE(s->reg_info); ++i) { | 
 |         register_reset(&s->reg_info[i]); | 
 |     } | 
 |  | 
 |     ptimer_transaction_begin(s->can_timer); | 
 |     ptimer_set_count(s->can_timer, 0); | 
 |     ptimer_transaction_commit(s->can_timer); | 
 | } | 
 |  | 
 | static void xlnx_zynqmp_can_reset_hold(Object *obj, ResetType type) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | 
 |     unsigned int i; | 
 |  | 
 |     for (i = 0; i < R_RXFIFO_ID; ++i) { | 
 |         register_reset(&s->reg_info[i]); | 
 |     } | 
 |  | 
 |     /* | 
 |      * Reset FIFOs when CAN model is reset. This will clear the fifo writes | 
 |      * done by post_write which gets called from register_reset function, | 
 |      * post_write handle will not be able to trigger tx because CAN will be | 
 |      * disabled when software_reset_register is cleared first. | 
 |      */ | 
 |     fifo32_reset(&s->rx_fifo); | 
 |     fifo32_reset(&s->tx_fifo); | 
 |     fifo32_reset(&s->txhpb_fifo); | 
 | } | 
 |  | 
 | static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client) | 
 | { | 
 |     XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState, | 
 |                                          bus_client); | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in reset state.\n", | 
 |                       path); | 
 |         return false; | 
 |     } | 
 |  | 
 |     if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is disabled. Incoming" | 
 |                       " messages will be discarded.\n", path); | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client, | 
 |                                const qemu_can_frame *buf, size_t buf_size) { | 
 |     XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState, | 
 |                                          bus_client); | 
 |     const qemu_can_frame *frame = buf; | 
 |  | 
 |     if (buf_size <= 0) { | 
 |         g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |         qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n", | 
 |                       path); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { | 
 |         /* Snoop Mode: Just keep the data. no response back. */ | 
 |         update_rx_fifo(s, frame); | 
 |     } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) { | 
 |         /* | 
 |          * XlnxZynqMPCAN is in sleep mode. Any data on bus will bring it to wake | 
 |          * up state. | 
 |          */ | 
 |         can_exit_sleep_mode(s); | 
 |         update_rx_fifo(s, frame); | 
 |     } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) { | 
 |         update_rx_fifo(s, frame); | 
 |     } else { | 
 |         /* | 
 |          * XlnxZynqMPCAN will not participate in normal bus communication | 
 |          * and will not receive any messages transmitted by other CAN nodes. | 
 |          */ | 
 |         trace_xlnx_can_rx_discard(s->regs[R_STATUS_REGISTER]); | 
 |     } | 
 |  | 
 |     return 1; | 
 | } | 
 |  | 
 | static CanBusClientInfo can_xilinx_bus_client_info = { | 
 |     .can_receive = xlnx_zynqmp_can_can_receive, | 
 |     .receive = xlnx_zynqmp_can_receive, | 
 | }; | 
 |  | 
 | static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCANState *s, | 
 |                                           CanBusState *bus) | 
 | { | 
 |     s->bus_client.info = &can_xilinx_bus_client_info; | 
 |  | 
 |     if (can_bus_insert_client(bus, &s->bus_client) < 0) { | 
 |         return -1; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static void xlnx_zynqmp_can_realize(DeviceState *dev, Error **errp) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev); | 
 |  | 
 |     if (s->canbus) { | 
 |         if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus) < 0) { | 
 |             g_autofree char *path = object_get_canonical_path(OBJECT(s)); | 
 |  | 
 |             error_setg(errp, "%s: xlnx_zynqmp_can_connect_to_bus" | 
 |                        " failed.", path); | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 |     /* Create RX FIFO, TXFIFO, TXHPB storage. */ | 
 |     fifo32_create(&s->rx_fifo, RXFIFO_SIZE); | 
 |     fifo32_create(&s->tx_fifo, RXFIFO_SIZE); | 
 |     fifo32_create(&s->txhpb_fifo, CAN_FRAME_SIZE); | 
 |  | 
 |     /* Allocate a new timer. */ | 
 |     s->can_timer = ptimer_init(xlnx_zynqmp_can_ptimer_cb, s, | 
 |                                PTIMER_POLICY_LEGACY); | 
 |  | 
 |     ptimer_transaction_begin(s->can_timer); | 
 |  | 
 |     ptimer_set_freq(s->can_timer, s->cfg.ext_clk_freq); | 
 |     ptimer_set_limit(s->can_timer, CAN_TIMER_MAX, 1); | 
 |     ptimer_run(s->can_timer, 0); | 
 |     ptimer_transaction_commit(s->can_timer); | 
 | } | 
 |  | 
 | static void xlnx_zynqmp_can_init(Object *obj) | 
 | { | 
 |     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | 
 |     SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | 
 |  | 
 |     RegisterInfoArray *reg_array; | 
 |  | 
 |     memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_CAN, | 
 |                         XLNX_ZYNQMP_CAN_R_MAX * 4); | 
 |     reg_array = register_init_block32(DEVICE(obj), can_regs_info, | 
 |                                ARRAY_SIZE(can_regs_info), | 
 |                                s->reg_info, s->regs, | 
 |                                &can_ops, | 
 |                                XLNX_ZYNQMP_CAN_ERR_DEBUG, | 
 |                                XLNX_ZYNQMP_CAN_R_MAX * 4); | 
 |  | 
 |     memory_region_add_subregion(&s->iomem, 0x00, ®_array->mem); | 
 |     sysbus_init_mmio(sbd, &s->iomem); | 
 |     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); | 
 | } | 
 |  | 
 | static const VMStateDescription vmstate_can = { | 
 |     .name = TYPE_XLNX_ZYNQMP_CAN, | 
 |     .version_id = 1, | 
 |     .minimum_version_id = 1, | 
 |     .fields = (const VMStateField[]) { | 
 |         VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCANState), | 
 |         VMSTATE_FIFO32(tx_fifo, XlnxZynqMPCANState), | 
 |         VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState), | 
 |         VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX), | 
 |         VMSTATE_PTIMER(can_timer, XlnxZynqMPCANState), | 
 |         VMSTATE_END_OF_LIST(), | 
 |     } | 
 | }; | 
 |  | 
 | static Property xlnx_zynqmp_can_properties[] = { | 
 |     DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq, | 
 |                        CAN_DEFAULT_CLOCK), | 
 |     DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS, | 
 |                      CanBusState *), | 
 |     DEFINE_PROP_END_OF_LIST(), | 
 | }; | 
 |  | 
 | static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data) | 
 | { | 
 |     DeviceClass *dc = DEVICE_CLASS(klass); | 
 |     ResettableClass *rc = RESETTABLE_CLASS(klass); | 
 |  | 
 |     rc->phases.enter = xlnx_zynqmp_can_reset_init; | 
 |     rc->phases.hold = xlnx_zynqmp_can_reset_hold; | 
 |     dc->realize = xlnx_zynqmp_can_realize; | 
 |     device_class_set_props(dc, xlnx_zynqmp_can_properties); | 
 |     dc->vmsd = &vmstate_can; | 
 | } | 
 |  | 
 | static const TypeInfo can_info = { | 
 |     .name          = TYPE_XLNX_ZYNQMP_CAN, | 
 |     .parent        = TYPE_SYS_BUS_DEVICE, | 
 |     .instance_size = sizeof(XlnxZynqMPCANState), | 
 |     .class_init    = xlnx_zynqmp_can_class_init, | 
 |     .instance_init = xlnx_zynqmp_can_init, | 
 | }; | 
 |  | 
 | static void can_register_types(void) | 
 | { | 
 |     type_register_static(&can_info); | 
 | } | 
 |  | 
 | type_init(can_register_types) |