xref: /openbmc/qemu/hw/net/can/xlnx-zynqmp-can.c (revision 83baec642a13a69398a2643a1f905606c13cd363)
198e5d7a2SVikram Garhwal /*
298e5d7a2SVikram Garhwal  * QEMU model of the Xilinx ZynqMP CAN controller.
398e5d7a2SVikram Garhwal  * This implementation is based on the following datasheet:
498e5d7a2SVikram Garhwal  * https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf
598e5d7a2SVikram Garhwal  *
698e5d7a2SVikram Garhwal  * Copyright (c) 2020 Xilinx Inc.
798e5d7a2SVikram Garhwal  *
898e5d7a2SVikram Garhwal  * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
998e5d7a2SVikram Garhwal  *
1098e5d7a2SVikram Garhwal  * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
1198e5d7a2SVikram Garhwal  * Pavel Pisa
1298e5d7a2SVikram Garhwal  *
1398e5d7a2SVikram Garhwal  * Permission is hereby granted, free of charge, to any person obtaining a copy
1498e5d7a2SVikram Garhwal  * of this software and associated documentation files (the "Software"), to deal
1598e5d7a2SVikram Garhwal  * in the Software without restriction, including without limitation the rights
1698e5d7a2SVikram Garhwal  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1798e5d7a2SVikram Garhwal  * copies of the Software, and to permit persons to whom the Software is
1898e5d7a2SVikram Garhwal  * furnished to do so, subject to the following conditions:
1998e5d7a2SVikram Garhwal  *
2098e5d7a2SVikram Garhwal  * The above copyright notice and this permission notice shall be included in
2198e5d7a2SVikram Garhwal  * all copies or substantial portions of the Software.
2298e5d7a2SVikram Garhwal  *
2398e5d7a2SVikram Garhwal  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2498e5d7a2SVikram Garhwal  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2598e5d7a2SVikram Garhwal  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2698e5d7a2SVikram Garhwal  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2798e5d7a2SVikram Garhwal  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2898e5d7a2SVikram Garhwal  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2998e5d7a2SVikram Garhwal  * THE SOFTWARE.
3098e5d7a2SVikram Garhwal  */
3198e5d7a2SVikram Garhwal 
3298e5d7a2SVikram Garhwal #include "qemu/osdep.h"
3398e5d7a2SVikram Garhwal #include "hw/sysbus.h"
3498e5d7a2SVikram Garhwal #include "hw/register.h"
3598e5d7a2SVikram Garhwal #include "hw/irq.h"
3698e5d7a2SVikram Garhwal #include "qapi/error.h"
3798e5d7a2SVikram Garhwal #include "qemu/bitops.h"
3898e5d7a2SVikram Garhwal #include "qemu/log.h"
3998e5d7a2SVikram Garhwal #include "qemu/cutils.h"
4098e5d7a2SVikram Garhwal #include "migration/vmstate.h"
4198e5d7a2SVikram Garhwal #include "hw/qdev-properties.h"
4298e5d7a2SVikram Garhwal #include "net/can_emu.h"
4398e5d7a2SVikram Garhwal #include "net/can_host.h"
4498e5d7a2SVikram Garhwal #include "qemu/event_notifier.h"
4598e5d7a2SVikram Garhwal #include "qom/object_interfaces.h"
4698e5d7a2SVikram Garhwal #include "hw/net/xlnx-zynqmp-can.h"
4798e5d7a2SVikram Garhwal #include "trace.h"
4898e5d7a2SVikram Garhwal 
4998e5d7a2SVikram Garhwal #ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
5098e5d7a2SVikram Garhwal #define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
5198e5d7a2SVikram Garhwal #endif
5298e5d7a2SVikram Garhwal 
5398e5d7a2SVikram Garhwal #define MAX_DLC            8
5498e5d7a2SVikram Garhwal #undef ERROR
5598e5d7a2SVikram Garhwal 
5698e5d7a2SVikram Garhwal REG32(SOFTWARE_RESET_REGISTER, 0x0)
5798e5d7a2SVikram Garhwal     FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1)
5898e5d7a2SVikram Garhwal     FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1)
5998e5d7a2SVikram Garhwal REG32(MODE_SELECT_REGISTER, 0x4)
6098e5d7a2SVikram Garhwal     FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1)
6198e5d7a2SVikram Garhwal     FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1)
6298e5d7a2SVikram Garhwal     FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1)
6398e5d7a2SVikram Garhwal REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8)
6498e5d7a2SVikram Garhwal     FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8)
6598e5d7a2SVikram Garhwal REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc)
6698e5d7a2SVikram Garhwal     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 7, 2)
6798e5d7a2SVikram Garhwal     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 4, 3)
6898e5d7a2SVikram Garhwal     FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 4)
6998e5d7a2SVikram Garhwal REG32(ERROR_COUNTER_REGISTER, 0x10)
7098e5d7a2SVikram Garhwal     FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8)
7198e5d7a2SVikram Garhwal     FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8)
7298e5d7a2SVikram Garhwal REG32(ERROR_STATUS_REGISTER, 0x14)
7398e5d7a2SVikram Garhwal     FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1)
7498e5d7a2SVikram Garhwal     FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1)
7598e5d7a2SVikram Garhwal     FIELD(ERROR_STATUS_REGISTER, STER, 2, 1)
7698e5d7a2SVikram Garhwal     FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1)
7798e5d7a2SVikram Garhwal     FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1)
7898e5d7a2SVikram Garhwal REG32(STATUS_REGISTER, 0x18)
7998e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, SNOOP, 12, 1)
8098e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, ACFBSY, 11, 1)
8198e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, TXFLL, 10, 1)
8298e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, TXBFLL, 9, 1)
8398e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, ESTAT, 7, 2)
8498e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, ERRWRN, 6, 1)
8598e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, BBSY, 5, 1)
8698e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, BIDLE, 4, 1)
8798e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, NORMAL, 3, 1)
8898e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, SLEEP, 2, 1)
8998e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, LBACK, 1, 1)
9098e5d7a2SVikram Garhwal     FIELD(STATUS_REGISTER, CONFIG, 0, 1)
9198e5d7a2SVikram Garhwal REG32(INTERRUPT_STATUS_REGISTER, 0x1c)
9298e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, TXFEMP, 14, 1)
9398e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, TXFWMEMP, 13, 1)
9498e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1)
9598e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1)
9698e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1)
9798e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1)
9898e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, ERROR, 8, 1)
9998e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, RXNEMP, 7, 1)
10098e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, RXOFLW, 6, 1)
10198e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, RXUFLW, 5, 1)
10298e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1)
10398e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, TXBFLL, 3, 1)
10498e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, TXFLL, 2, 1)
10598e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1)
10698e5d7a2SVikram Garhwal     FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1)
10798e5d7a2SVikram Garhwal REG32(INTERRUPT_ENABLE_REGISTER, 0x20)
10898e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFEMP, 14, 1)
10998e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFWMEMP, 13, 1)
11098e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1)
11198e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1)
11298e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1)
11398e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1)
11498e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1)
11598e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ERXNEMP, 7, 1)
11698e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ERXOFLW, 6, 1)
11798e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ERXUFLW, 5, 1)
11898e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1)
11998e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ETXBFLL, 3, 1)
12098e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ETXFLL, 2, 1)
12198e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1)
12298e5d7a2SVikram Garhwal     FIELD(INTERRUPT_ENABLE_REGISTER, EARBLST, 0, 1)
12398e5d7a2SVikram Garhwal REG32(INTERRUPT_CLEAR_REGISTER, 0x24)
12498e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFEMP, 14, 1)
12598e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFWMEMP, 13, 1)
12698e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1)
12798e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1)
12898e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1)
12998e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1)
13098e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1)
13198e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CRXNEMP, 7, 1)
13298e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CRXOFLW, 6, 1)
13398e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CRXUFLW, 5, 1)
13498e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1)
13598e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CTXBFLL, 3, 1)
13698e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CTXFLL, 2, 1)
13798e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1)
13898e5d7a2SVikram Garhwal     FIELD(INTERRUPT_CLEAR_REGISTER, CARBLST, 0, 1)
13998e5d7a2SVikram Garhwal REG32(TIMESTAMP_REGISTER, 0x28)
14098e5d7a2SVikram Garhwal     FIELD(TIMESTAMP_REGISTER, CTS, 0, 1)
14198e5d7a2SVikram Garhwal REG32(WIR, 0x2c)
14298e5d7a2SVikram Garhwal     FIELD(WIR, EW, 8, 8)
14398e5d7a2SVikram Garhwal     FIELD(WIR, FW, 0, 8)
14498e5d7a2SVikram Garhwal REG32(TXFIFO_ID, 0x30)
14598e5d7a2SVikram Garhwal     FIELD(TXFIFO_ID, IDH, 21, 11)
14698e5d7a2SVikram Garhwal     FIELD(TXFIFO_ID, SRRRTR, 20, 1)
14798e5d7a2SVikram Garhwal     FIELD(TXFIFO_ID, IDE, 19, 1)
14898e5d7a2SVikram Garhwal     FIELD(TXFIFO_ID, IDL, 1, 18)
14998e5d7a2SVikram Garhwal     FIELD(TXFIFO_ID, RTR, 0, 1)
15098e5d7a2SVikram Garhwal REG32(TXFIFO_DLC, 0x34)
15198e5d7a2SVikram Garhwal     FIELD(TXFIFO_DLC, DLC, 28, 4)
15298e5d7a2SVikram Garhwal REG32(TXFIFO_DATA1, 0x38)
15398e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA1, DB0, 24, 8)
15498e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA1, DB1, 16, 8)
15598e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA1, DB2, 8, 8)
15698e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA1, DB3, 0, 8)
15798e5d7a2SVikram Garhwal REG32(TXFIFO_DATA2, 0x3c)
15898e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA2, DB4, 24, 8)
15998e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA2, DB5, 16, 8)
16098e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA2, DB6, 8, 8)
16198e5d7a2SVikram Garhwal     FIELD(TXFIFO_DATA2, DB7, 0, 8)
16298e5d7a2SVikram Garhwal REG32(TXHPB_ID, 0x40)
16398e5d7a2SVikram Garhwal     FIELD(TXHPB_ID, IDH, 21, 11)
16498e5d7a2SVikram Garhwal     FIELD(TXHPB_ID, SRRRTR, 20, 1)
16598e5d7a2SVikram Garhwal     FIELD(TXHPB_ID, IDE, 19, 1)
16698e5d7a2SVikram Garhwal     FIELD(TXHPB_ID, IDL, 1, 18)
16798e5d7a2SVikram Garhwal     FIELD(TXHPB_ID, RTR, 0, 1)
16898e5d7a2SVikram Garhwal REG32(TXHPB_DLC, 0x44)
16998e5d7a2SVikram Garhwal     FIELD(TXHPB_DLC, DLC, 28, 4)
17098e5d7a2SVikram Garhwal REG32(TXHPB_DATA1, 0x48)
17198e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA1, DB0, 24, 8)
17298e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA1, DB1, 16, 8)
17398e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA1, DB2, 8, 8)
17498e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA1, DB3, 0, 8)
17598e5d7a2SVikram Garhwal REG32(TXHPB_DATA2, 0x4c)
17698e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA2, DB4, 24, 8)
17798e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA2, DB5, 16, 8)
17898e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA2, DB6, 8, 8)
17998e5d7a2SVikram Garhwal     FIELD(TXHPB_DATA2, DB7, 0, 8)
18098e5d7a2SVikram Garhwal REG32(RXFIFO_ID, 0x50)
18198e5d7a2SVikram Garhwal     FIELD(RXFIFO_ID, IDH, 21, 11)
18298e5d7a2SVikram Garhwal     FIELD(RXFIFO_ID, SRRRTR, 20, 1)
18398e5d7a2SVikram Garhwal     FIELD(RXFIFO_ID, IDE, 19, 1)
18498e5d7a2SVikram Garhwal     FIELD(RXFIFO_ID, IDL, 1, 18)
18598e5d7a2SVikram Garhwal     FIELD(RXFIFO_ID, RTR, 0, 1)
18698e5d7a2SVikram Garhwal REG32(RXFIFO_DLC, 0x54)
18798e5d7a2SVikram Garhwal     FIELD(RXFIFO_DLC, DLC, 28, 4)
18898e5d7a2SVikram Garhwal     FIELD(RXFIFO_DLC, RXT, 0, 16)
18998e5d7a2SVikram Garhwal REG32(RXFIFO_DATA1, 0x58)
19098e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA1, DB0, 24, 8)
19198e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA1, DB1, 16, 8)
19298e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA1, DB2, 8, 8)
19398e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA1, DB3, 0, 8)
19498e5d7a2SVikram Garhwal REG32(RXFIFO_DATA2, 0x5c)
19598e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA2, DB4, 24, 8)
19698e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA2, DB5, 16, 8)
19798e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA2, DB6, 8, 8)
19898e5d7a2SVikram Garhwal     FIELD(RXFIFO_DATA2, DB7, 0, 8)
19998e5d7a2SVikram Garhwal REG32(AFR, 0x60)
20098e5d7a2SVikram Garhwal     FIELD(AFR, UAF4, 3, 1)
20198e5d7a2SVikram Garhwal     FIELD(AFR, UAF3, 2, 1)
20298e5d7a2SVikram Garhwal     FIELD(AFR, UAF2, 1, 1)
20398e5d7a2SVikram Garhwal     FIELD(AFR, UAF1, 0, 1)
20498e5d7a2SVikram Garhwal REG32(AFMR1, 0x64)
20598e5d7a2SVikram Garhwal     FIELD(AFMR1, AMIDH, 21, 11)
20698e5d7a2SVikram Garhwal     FIELD(AFMR1, AMSRR, 20, 1)
20798e5d7a2SVikram Garhwal     FIELD(AFMR1, AMIDE, 19, 1)
20898e5d7a2SVikram Garhwal     FIELD(AFMR1, AMIDL, 1, 18)
20998e5d7a2SVikram Garhwal     FIELD(AFMR1, AMRTR, 0, 1)
21098e5d7a2SVikram Garhwal REG32(AFIR1, 0x68)
21198e5d7a2SVikram Garhwal     FIELD(AFIR1, AIIDH, 21, 11)
21298e5d7a2SVikram Garhwal     FIELD(AFIR1, AISRR, 20, 1)
21398e5d7a2SVikram Garhwal     FIELD(AFIR1, AIIDE, 19, 1)
21498e5d7a2SVikram Garhwal     FIELD(AFIR1, AIIDL, 1, 18)
21598e5d7a2SVikram Garhwal     FIELD(AFIR1, AIRTR, 0, 1)
21698e5d7a2SVikram Garhwal REG32(AFMR2, 0x6c)
21798e5d7a2SVikram Garhwal     FIELD(AFMR2, AMIDH, 21, 11)
21898e5d7a2SVikram Garhwal     FIELD(AFMR2, AMSRR, 20, 1)
21998e5d7a2SVikram Garhwal     FIELD(AFMR2, AMIDE, 19, 1)
22098e5d7a2SVikram Garhwal     FIELD(AFMR2, AMIDL, 1, 18)
22198e5d7a2SVikram Garhwal     FIELD(AFMR2, AMRTR, 0, 1)
22298e5d7a2SVikram Garhwal REG32(AFIR2, 0x70)
22398e5d7a2SVikram Garhwal     FIELD(AFIR2, AIIDH, 21, 11)
22498e5d7a2SVikram Garhwal     FIELD(AFIR2, AISRR, 20, 1)
22598e5d7a2SVikram Garhwal     FIELD(AFIR2, AIIDE, 19, 1)
22698e5d7a2SVikram Garhwal     FIELD(AFIR2, AIIDL, 1, 18)
22798e5d7a2SVikram Garhwal     FIELD(AFIR2, AIRTR, 0, 1)
22898e5d7a2SVikram Garhwal REG32(AFMR3, 0x74)
22998e5d7a2SVikram Garhwal     FIELD(AFMR3, AMIDH, 21, 11)
23098e5d7a2SVikram Garhwal     FIELD(AFMR3, AMSRR, 20, 1)
23198e5d7a2SVikram Garhwal     FIELD(AFMR3, AMIDE, 19, 1)
23298e5d7a2SVikram Garhwal     FIELD(AFMR3, AMIDL, 1, 18)
23398e5d7a2SVikram Garhwal     FIELD(AFMR3, AMRTR, 0, 1)
23498e5d7a2SVikram Garhwal REG32(AFIR3, 0x78)
23598e5d7a2SVikram Garhwal     FIELD(AFIR3, AIIDH, 21, 11)
23698e5d7a2SVikram Garhwal     FIELD(AFIR3, AISRR, 20, 1)
23798e5d7a2SVikram Garhwal     FIELD(AFIR3, AIIDE, 19, 1)
23898e5d7a2SVikram Garhwal     FIELD(AFIR3, AIIDL, 1, 18)
23998e5d7a2SVikram Garhwal     FIELD(AFIR3, AIRTR, 0, 1)
24098e5d7a2SVikram Garhwal REG32(AFMR4, 0x7c)
24198e5d7a2SVikram Garhwal     FIELD(AFMR4, AMIDH, 21, 11)
24298e5d7a2SVikram Garhwal     FIELD(AFMR4, AMSRR, 20, 1)
24398e5d7a2SVikram Garhwal     FIELD(AFMR4, AMIDE, 19, 1)
24498e5d7a2SVikram Garhwal     FIELD(AFMR4, AMIDL, 1, 18)
24598e5d7a2SVikram Garhwal     FIELD(AFMR4, AMRTR, 0, 1)
24698e5d7a2SVikram Garhwal REG32(AFIR4, 0x80)
24798e5d7a2SVikram Garhwal     FIELD(AFIR4, AIIDH, 21, 11)
24898e5d7a2SVikram Garhwal     FIELD(AFIR4, AISRR, 20, 1)
24998e5d7a2SVikram Garhwal     FIELD(AFIR4, AIIDE, 19, 1)
25098e5d7a2SVikram Garhwal     FIELD(AFIR4, AIIDL, 1, 18)
25198e5d7a2SVikram Garhwal     FIELD(AFIR4, AIRTR, 0, 1)
25298e5d7a2SVikram Garhwal 
can_update_irq(XlnxZynqMPCANState * s)25398e5d7a2SVikram Garhwal static void can_update_irq(XlnxZynqMPCANState *s)
25498e5d7a2SVikram Garhwal {
25598e5d7a2SVikram Garhwal     uint32_t irq;
25698e5d7a2SVikram Garhwal 
25798e5d7a2SVikram Garhwal     /* Watermark register interrupts. */
25898e5d7a2SVikram Garhwal     if ((fifo32_num_free(&s->tx_fifo) / CAN_FRAME_SIZE) >
25998e5d7a2SVikram Garhwal             ARRAY_FIELD_EX32(s->regs, WIR, EW)) {
26098e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFWMEMP, 1);
26198e5d7a2SVikram Garhwal     }
26298e5d7a2SVikram Garhwal 
26398e5d7a2SVikram Garhwal     if ((fifo32_num_used(&s->rx_fifo) / CAN_FRAME_SIZE) >
26498e5d7a2SVikram Garhwal             ARRAY_FIELD_EX32(s->regs, WIR, FW)) {
26598e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1);
26698e5d7a2SVikram Garhwal     }
26798e5d7a2SVikram Garhwal 
26898e5d7a2SVikram Garhwal     /* RX Interrupts. */
26998e5d7a2SVikram Garhwal     if (fifo32_num_used(&s->rx_fifo) >= CAN_FRAME_SIZE) {
27098e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXNEMP, 1);
27198e5d7a2SVikram Garhwal     }
27298e5d7a2SVikram Garhwal 
27398e5d7a2SVikram Garhwal     /* TX interrupts. */
27498e5d7a2SVikram Garhwal     if (fifo32_is_empty(&s->tx_fifo)) {
27598e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFEMP, 1);
27698e5d7a2SVikram Garhwal     }
27798e5d7a2SVikram Garhwal 
27898e5d7a2SVikram Garhwal     if (fifo32_is_full(&s->tx_fifo)) {
27998e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFLL, 1);
28098e5d7a2SVikram Garhwal     }
28198e5d7a2SVikram Garhwal 
28298e5d7a2SVikram Garhwal     if (fifo32_is_full(&s->txhpb_fifo)) {
28398e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXBFLL, 1);
28498e5d7a2SVikram Garhwal     }
28598e5d7a2SVikram Garhwal 
28698e5d7a2SVikram Garhwal     irq = s->regs[R_INTERRUPT_STATUS_REGISTER];
28798e5d7a2SVikram Garhwal     irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER];
28898e5d7a2SVikram Garhwal 
28998e5d7a2SVikram Garhwal     trace_xlnx_can_update_irq(s->regs[R_INTERRUPT_STATUS_REGISTER],
29098e5d7a2SVikram Garhwal                               s->regs[R_INTERRUPT_ENABLE_REGISTER], irq);
29198e5d7a2SVikram Garhwal     qemu_set_irq(s->irq, irq);
29298e5d7a2SVikram Garhwal }
29398e5d7a2SVikram Garhwal 
can_ier_post_write(RegisterInfo * reg,uint64_t val)29498e5d7a2SVikram Garhwal static void can_ier_post_write(RegisterInfo *reg, uint64_t val)
29598e5d7a2SVikram Garhwal {
29698e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
29798e5d7a2SVikram Garhwal 
29898e5d7a2SVikram Garhwal     can_update_irq(s);
29998e5d7a2SVikram Garhwal }
30098e5d7a2SVikram Garhwal 
can_icr_pre_write(RegisterInfo * reg,uint64_t val)30198e5d7a2SVikram Garhwal static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val)
30298e5d7a2SVikram Garhwal {
30398e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
30498e5d7a2SVikram Garhwal 
30598e5d7a2SVikram Garhwal     s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val;
30698e5d7a2SVikram Garhwal     can_update_irq(s);
30798e5d7a2SVikram Garhwal 
30898e5d7a2SVikram Garhwal     return 0;
30998e5d7a2SVikram Garhwal }
31098e5d7a2SVikram Garhwal 
can_config_reset(XlnxZynqMPCANState * s)31198e5d7a2SVikram Garhwal static void can_config_reset(XlnxZynqMPCANState *s)
31298e5d7a2SVikram Garhwal {
31398e5d7a2SVikram Garhwal     /* Reset all the configuration registers. */
31498e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
31598e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
31698e5d7a2SVikram Garhwal     register_reset(
31798e5d7a2SVikram Garhwal               &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER]);
31898e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]);
31998e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_STATUS_REGISTER]);
32098e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_INTERRUPT_STATUS_REGISTER]);
32198e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_INTERRUPT_ENABLE_REGISTER]);
32298e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_INTERRUPT_CLEAR_REGISTER]);
32398e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_WIR]);
32498e5d7a2SVikram Garhwal }
32598e5d7a2SVikram Garhwal 
can_config_mode(XlnxZynqMPCANState * s)32698e5d7a2SVikram Garhwal static void can_config_mode(XlnxZynqMPCANState *s)
32798e5d7a2SVikram Garhwal {
32898e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
32998e5d7a2SVikram Garhwal     register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
33098e5d7a2SVikram Garhwal 
33198e5d7a2SVikram Garhwal     /* Put XlnxZynqMPCAN in configuration mode. */
33298e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
33398e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
33498e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
33598e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
33698e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
33798e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
33898e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
33998e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
34098e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
34198e5d7a2SVikram Garhwal 
34298e5d7a2SVikram Garhwal     can_update_irq(s);
34398e5d7a2SVikram Garhwal }
34498e5d7a2SVikram Garhwal 
update_status_register_mode_bits(XlnxZynqMPCANState * s)34598e5d7a2SVikram Garhwal static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
34698e5d7a2SVikram Garhwal {
34798e5d7a2SVikram Garhwal     bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP);
34898e5d7a2SVikram Garhwal     bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP);
34998e5d7a2SVikram Garhwal     /* Wake up interrupt bit. */
35098e5d7a2SVikram Garhwal     bool wakeup_irq_val = sleep_status && (sleep_mode == 0);
35198e5d7a2SVikram Garhwal     /* Sleep interrupt bit. */
35298e5d7a2SVikram Garhwal     bool sleep_irq_val = sleep_mode && (sleep_status == 0);
35398e5d7a2SVikram Garhwal 
35498e5d7a2SVikram Garhwal     /* Clear previous core mode status bits. */
35598e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0);
35698e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0);
35798e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0);
35898e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0);
35998e5d7a2SVikram Garhwal 
36098e5d7a2SVikram Garhwal     /* set current mode bit and generate irqs accordingly. */
36198e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) {
36298e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1);
36398e5d7a2SVikram Garhwal     } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) {
36498e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1);
36598e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP,
36698e5d7a2SVikram Garhwal                          sleep_irq_val);
36798e5d7a2SVikram Garhwal     } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) {
36898e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1);
36998e5d7a2SVikram Garhwal     } else {
37098e5d7a2SVikram Garhwal         /*
37198e5d7a2SVikram Garhwal          * If all bits are zero then XlnxZynqMPCAN is set in normal mode.
37298e5d7a2SVikram Garhwal          */
37398e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
37498e5d7a2SVikram Garhwal         /* Set wakeup interrupt bit. */
37598e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
37698e5d7a2SVikram Garhwal                          wakeup_irq_val);
37798e5d7a2SVikram Garhwal     }
37898e5d7a2SVikram Garhwal 
37998e5d7a2SVikram Garhwal     can_update_irq(s);
38098e5d7a2SVikram Garhwal }
38198e5d7a2SVikram Garhwal 
can_exit_sleep_mode(XlnxZynqMPCANState * s)38298e5d7a2SVikram Garhwal static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
38398e5d7a2SVikram Garhwal {
38498e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
38598e5d7a2SVikram Garhwal     update_status_register_mode_bits(s);
38698e5d7a2SVikram Garhwal }
38798e5d7a2SVikram Garhwal 
generate_frame(qemu_can_frame * frame,uint32_t * data)38898e5d7a2SVikram Garhwal static void generate_frame(qemu_can_frame *frame, uint32_t *data)
38998e5d7a2SVikram Garhwal {
39098e5d7a2SVikram Garhwal     frame->can_id = data[0];
39198e5d7a2SVikram Garhwal     frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC);
39298e5d7a2SVikram Garhwal 
39398e5d7a2SVikram Garhwal     frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
39498e5d7a2SVikram Garhwal     frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
39598e5d7a2SVikram Garhwal     frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
39698e5d7a2SVikram Garhwal     frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
39798e5d7a2SVikram Garhwal 
39898e5d7a2SVikram Garhwal     frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
39998e5d7a2SVikram Garhwal     frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
40098e5d7a2SVikram Garhwal     frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
40198e5d7a2SVikram Garhwal     frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
40298e5d7a2SVikram Garhwal }
40398e5d7a2SVikram Garhwal 
tx_ready_check(XlnxZynqMPCANState * s)40498e5d7a2SVikram Garhwal static bool tx_ready_check(XlnxZynqMPCANState *s)
40598e5d7a2SVikram Garhwal {
40698e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
40798e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
40898e5d7a2SVikram Garhwal 
40998e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while"
41098e5d7a2SVikram Garhwal                       " data while controller is in reset mode.\n",
41198e5d7a2SVikram Garhwal                       path);
41298e5d7a2SVikram Garhwal         return false;
41398e5d7a2SVikram Garhwal     }
41498e5d7a2SVikram Garhwal 
41598e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
41698e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
41798e5d7a2SVikram Garhwal 
41898e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
41998e5d7a2SVikram Garhwal                       " data while controller is in configuration mode. Reset"
42098e5d7a2SVikram Garhwal                       " the core so operations can start fresh.\n",
42198e5d7a2SVikram Garhwal                       path);
42298e5d7a2SVikram Garhwal         return false;
42398e5d7a2SVikram Garhwal     }
42498e5d7a2SVikram Garhwal 
42598e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
42698e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
42798e5d7a2SVikram Garhwal 
42898e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
42998e5d7a2SVikram Garhwal                       " data while controller is in SNOOP MODE.\n",
43098e5d7a2SVikram Garhwal                       path);
43198e5d7a2SVikram Garhwal         return false;
43298e5d7a2SVikram Garhwal     }
43398e5d7a2SVikram Garhwal 
43498e5d7a2SVikram Garhwal     return true;
43598e5d7a2SVikram Garhwal }
43698e5d7a2SVikram Garhwal 
read_tx_frame(XlnxZynqMPCANState * s,Fifo32 * fifo,uint32_t * data)43775d0e6b5SPhilippe Mathieu-Daudé static void read_tx_frame(XlnxZynqMPCANState *s, Fifo32 *fifo, uint32_t *data)
43875d0e6b5SPhilippe Mathieu-Daudé {
43975d0e6b5SPhilippe Mathieu-Daudé     unsigned used = fifo32_num_used(fifo);
44075d0e6b5SPhilippe Mathieu-Daudé     bool is_txhpb = fifo == &s->txhpb_fifo;
44175d0e6b5SPhilippe Mathieu-Daudé 
44275d0e6b5SPhilippe Mathieu-Daudé     assert(used > 0);
44375d0e6b5SPhilippe Mathieu-Daudé     used %= CAN_FRAME_SIZE;
44475d0e6b5SPhilippe Mathieu-Daudé 
44575d0e6b5SPhilippe Mathieu-Daudé     /*
44675d0e6b5SPhilippe Mathieu-Daudé      * Frame Message Format
44775d0e6b5SPhilippe Mathieu-Daudé      *
44875d0e6b5SPhilippe Mathieu-Daudé      * Each frame includes four words (16 bytes). Software must read and write
44975d0e6b5SPhilippe Mathieu-Daudé      * all four words regardless of the actual number of data bytes and valid
45075d0e6b5SPhilippe Mathieu-Daudé      * fields in the message.
45175d0e6b5SPhilippe Mathieu-Daudé      * If software misbehave (not writing all four words), we use the previous
45275d0e6b5SPhilippe Mathieu-Daudé      * registers content to initialize each missing word.
45375d0e6b5SPhilippe Mathieu-Daudé      *
45475d0e6b5SPhilippe Mathieu-Daudé      * If used is 1 then ID, DLC and DATA1 are missing.
45575d0e6b5SPhilippe Mathieu-Daudé      * if used is 2 then ID and DLC are missing.
45675d0e6b5SPhilippe Mathieu-Daudé      * if used is 3 then only ID is missing.
45775d0e6b5SPhilippe Mathieu-Daudé      */
45875d0e6b5SPhilippe Mathieu-Daudé      if (used > 0) {
45975d0e6b5SPhilippe Mathieu-Daudé         data[0] = s->regs[is_txhpb ? R_TXHPB_ID : R_TXFIFO_ID];
46075d0e6b5SPhilippe Mathieu-Daudé     } else {
46175d0e6b5SPhilippe Mathieu-Daudé         data[0] = fifo32_pop(fifo);
46275d0e6b5SPhilippe Mathieu-Daudé     }
46375d0e6b5SPhilippe Mathieu-Daudé     if (used == 1 || used == 2) {
46475d0e6b5SPhilippe Mathieu-Daudé         data[1] = s->regs[is_txhpb ? R_TXHPB_DLC : R_TXFIFO_DLC];
46575d0e6b5SPhilippe Mathieu-Daudé     } else {
46675d0e6b5SPhilippe Mathieu-Daudé         data[1] = fifo32_pop(fifo);
46775d0e6b5SPhilippe Mathieu-Daudé     }
46875d0e6b5SPhilippe Mathieu-Daudé     if (used == 1) {
46975d0e6b5SPhilippe Mathieu-Daudé         data[2] = s->regs[is_txhpb ? R_TXHPB_DATA1 : R_TXFIFO_DATA1];
47075d0e6b5SPhilippe Mathieu-Daudé     } else {
47175d0e6b5SPhilippe Mathieu-Daudé         data[2] = fifo32_pop(fifo);
47275d0e6b5SPhilippe Mathieu-Daudé     }
47375d0e6b5SPhilippe Mathieu-Daudé     /* DATA2 triggered the transfer thus is always available */
47475d0e6b5SPhilippe Mathieu-Daudé     data[3] = fifo32_pop(fifo);
47575d0e6b5SPhilippe Mathieu-Daudé 
47675d0e6b5SPhilippe Mathieu-Daudé     if (used) {
47775d0e6b5SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR,
47875d0e6b5SPhilippe Mathieu-Daudé                       "%s: Incomplete CAN frame (only %u/%u slots used)\n",
47975d0e6b5SPhilippe Mathieu-Daudé                       TYPE_XLNX_ZYNQMP_CAN, used, CAN_FRAME_SIZE);
48075d0e6b5SPhilippe Mathieu-Daudé     }
48175d0e6b5SPhilippe Mathieu-Daudé }
48275d0e6b5SPhilippe Mathieu-Daudé 
transfer_fifo(XlnxZynqMPCANState * s,Fifo32 * fifo)48398e5d7a2SVikram Garhwal static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
48498e5d7a2SVikram Garhwal {
48598e5d7a2SVikram Garhwal     qemu_can_frame frame;
48698e5d7a2SVikram Garhwal     uint32_t data[CAN_FRAME_SIZE];
48798e5d7a2SVikram Garhwal     int i;
48898e5d7a2SVikram Garhwal     bool can_tx = tx_ready_check(s);
48998e5d7a2SVikram Garhwal 
49098e5d7a2SVikram Garhwal     if (!can_tx) {
49198e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
49298e5d7a2SVikram Garhwal 
49398e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is not enabled for data"
49498e5d7a2SVikram Garhwal                       " transfer.\n", path);
49598e5d7a2SVikram Garhwal         can_update_irq(s);
49698e5d7a2SVikram Garhwal         return;
49798e5d7a2SVikram Garhwal     }
49898e5d7a2SVikram Garhwal 
49998e5d7a2SVikram Garhwal     while (!fifo32_is_empty(fifo)) {
50075d0e6b5SPhilippe Mathieu-Daudé         read_tx_frame(s, fifo, data);
50198e5d7a2SVikram Garhwal 
50298e5d7a2SVikram Garhwal         if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
50398e5d7a2SVikram Garhwal             /*
50498e5d7a2SVikram Garhwal              * Controller is in loopback. In Loopback mode, the CAN core
50598e5d7a2SVikram Garhwal              * transmits a recessive bitstream on to the XlnxZynqMPCAN Bus.
50698e5d7a2SVikram Garhwal              * Any message transmitted is looped back to the RX line and
50798e5d7a2SVikram Garhwal              * acknowledged. The XlnxZynqMPCAN core receives any message
50898e5d7a2SVikram Garhwal              * that it transmits.
50998e5d7a2SVikram Garhwal              */
51098e5d7a2SVikram Garhwal             if (fifo32_is_full(&s->rx_fifo)) {
51198e5d7a2SVikram Garhwal                 ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
51298e5d7a2SVikram Garhwal             } else {
51398e5d7a2SVikram Garhwal                 for (i = 0; i < CAN_FRAME_SIZE; i++) {
51498e5d7a2SVikram Garhwal                     fifo32_push(&s->rx_fifo, data[i]);
51598e5d7a2SVikram Garhwal                 }
51698e5d7a2SVikram Garhwal 
51798e5d7a2SVikram Garhwal                 ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1);
51898e5d7a2SVikram Garhwal             }
51998e5d7a2SVikram Garhwal         } else {
52098e5d7a2SVikram Garhwal             /* Normal mode Tx. */
52198e5d7a2SVikram Garhwal             generate_frame(&frame, data);
52298e5d7a2SVikram Garhwal 
52398e5d7a2SVikram Garhwal             trace_xlnx_can_tx_data(frame.can_id, frame.can_dlc,
52498e5d7a2SVikram Garhwal                                    frame.data[0], frame.data[1],
52598e5d7a2SVikram Garhwal                                    frame.data[2], frame.data[3],
52698e5d7a2SVikram Garhwal                                    frame.data[4], frame.data[5],
52798e5d7a2SVikram Garhwal                                    frame.data[6], frame.data[7]);
52898e5d7a2SVikram Garhwal             can_bus_client_send(&s->bus_client, &frame, 1);
52998e5d7a2SVikram Garhwal         }
53098e5d7a2SVikram Garhwal     }
53198e5d7a2SVikram Garhwal 
53298e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1);
53398e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, TXBFLL, 0);
53498e5d7a2SVikram Garhwal 
53598e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) {
53698e5d7a2SVikram Garhwal         can_exit_sleep_mode(s);
53798e5d7a2SVikram Garhwal     }
53898e5d7a2SVikram Garhwal 
53998e5d7a2SVikram Garhwal     can_update_irq(s);
54098e5d7a2SVikram Garhwal }
54198e5d7a2SVikram Garhwal 
can_srr_pre_write(RegisterInfo * reg,uint64_t val)54298e5d7a2SVikram Garhwal static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val)
54398e5d7a2SVikram Garhwal {
54498e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
54598e5d7a2SVikram Garhwal 
54698e5d7a2SVikram Garhwal     ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
54798e5d7a2SVikram Garhwal                      FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
54898e5d7a2SVikram Garhwal 
54998e5d7a2SVikram Garhwal     if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
55098e5d7a2SVikram Garhwal         trace_xlnx_can_reset(val);
55198e5d7a2SVikram Garhwal 
55298e5d7a2SVikram Garhwal         /* First, core will do software reset then will enter in config mode. */
55398e5d7a2SVikram Garhwal         can_config_reset(s);
55498e5d7a2SVikram Garhwal     }
55598e5d7a2SVikram Garhwal 
55698e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
55798e5d7a2SVikram Garhwal         can_config_mode(s);
55898e5d7a2SVikram Garhwal     } else {
55998e5d7a2SVikram Garhwal         /*
56098e5d7a2SVikram Garhwal          * Leave config mode. Now XlnxZynqMPCAN core will enter normal,
56198e5d7a2SVikram Garhwal          * sleep, snoop or loopback mode depending upon LBACK, SLEEP, SNOOP
56298e5d7a2SVikram Garhwal          * register states.
56398e5d7a2SVikram Garhwal          */
56498e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
56598e5d7a2SVikram Garhwal 
56698e5d7a2SVikram Garhwal         ptimer_transaction_begin(s->can_timer);
56798e5d7a2SVikram Garhwal         ptimer_set_count(s->can_timer, 0);
56898e5d7a2SVikram Garhwal         ptimer_transaction_commit(s->can_timer);
56998e5d7a2SVikram Garhwal 
57098e5d7a2SVikram Garhwal         /* XlnxZynqMPCAN is out of config mode. It will send pending data. */
57198e5d7a2SVikram Garhwal         transfer_fifo(s, &s->txhpb_fifo);
57298e5d7a2SVikram Garhwal         transfer_fifo(s, &s->tx_fifo);
57398e5d7a2SVikram Garhwal     }
57498e5d7a2SVikram Garhwal 
57598e5d7a2SVikram Garhwal     update_status_register_mode_bits(s);
57698e5d7a2SVikram Garhwal 
57798e5d7a2SVikram Garhwal     return s->regs[R_SOFTWARE_RESET_REGISTER];
57898e5d7a2SVikram Garhwal }
57998e5d7a2SVikram Garhwal 
can_msr_pre_write(RegisterInfo * reg,uint64_t val)58098e5d7a2SVikram Garhwal static uint64_t can_msr_pre_write(RegisterInfo *reg, uint64_t val)
58198e5d7a2SVikram Garhwal {
58298e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
58398e5d7a2SVikram Garhwal     uint8_t multi_mode;
58498e5d7a2SVikram Garhwal 
58598e5d7a2SVikram Garhwal     /*
58698e5d7a2SVikram Garhwal      * Multiple mode set check. This is done to make sure user doesn't set
58798e5d7a2SVikram Garhwal      * multiple modes.
58898e5d7a2SVikram Garhwal      */
58998e5d7a2SVikram Garhwal     multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) +
59098e5d7a2SVikram Garhwal                  FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) +
59198e5d7a2SVikram Garhwal                  FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP);
59298e5d7a2SVikram Garhwal 
59398e5d7a2SVikram Garhwal     if (multi_mode > 1) {
59498e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
59598e5d7a2SVikram Garhwal 
59698e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to config"
59798e5d7a2SVikram Garhwal                       " several modes simultaneously. One mode will be selected"
59898e5d7a2SVikram Garhwal                       " according to their priority: LBACK > SLEEP > SNOOP.\n",
59998e5d7a2SVikram Garhwal                       path);
60098e5d7a2SVikram Garhwal     }
60198e5d7a2SVikram Garhwal 
60298e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
60398e5d7a2SVikram Garhwal         /* We are in configuration mode, any mode can be selected. */
60498e5d7a2SVikram Garhwal         s->regs[R_MODE_SELECT_REGISTER] = val;
60598e5d7a2SVikram Garhwal     } else {
60698e5d7a2SVikram Garhwal         bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP);
60798e5d7a2SVikram Garhwal 
60898e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit);
60998e5d7a2SVikram Garhwal 
61098e5d7a2SVikram Garhwal         if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
61198e5d7a2SVikram Garhwal             g_autofree char *path = object_get_canonical_path(OBJECT(s));
61298e5d7a2SVikram Garhwal 
61398e5d7a2SVikram Garhwal             qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
61498e5d7a2SVikram Garhwal                           " LBACK mode without setting CEN bit as 0.\n",
61598e5d7a2SVikram Garhwal                           path);
61698e5d7a2SVikram Garhwal         } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
61798e5d7a2SVikram Garhwal             g_autofree char *path = object_get_canonical_path(OBJECT(s));
61898e5d7a2SVikram Garhwal 
61998e5d7a2SVikram Garhwal             qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
62098e5d7a2SVikram Garhwal                           " SNOOP mode without setting CEN bit as 0.\n",
62198e5d7a2SVikram Garhwal                           path);
62298e5d7a2SVikram Garhwal         }
62398e5d7a2SVikram Garhwal 
62498e5d7a2SVikram Garhwal         update_status_register_mode_bits(s);
62598e5d7a2SVikram Garhwal     }
62698e5d7a2SVikram Garhwal 
62798e5d7a2SVikram Garhwal     return s->regs[R_MODE_SELECT_REGISTER];
62898e5d7a2SVikram Garhwal }
62998e5d7a2SVikram Garhwal 
can_brpr_pre_write(RegisterInfo * reg,uint64_t val)63098e5d7a2SVikram Garhwal static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val)
63198e5d7a2SVikram Garhwal {
63298e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
63398e5d7a2SVikram Garhwal 
63498e5d7a2SVikram Garhwal     /* Only allow writes when in config mode. */
63598e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
63698e5d7a2SVikram Garhwal         return s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
63798e5d7a2SVikram Garhwal     }
63898e5d7a2SVikram Garhwal 
63998e5d7a2SVikram Garhwal     return val;
64098e5d7a2SVikram Garhwal }
64198e5d7a2SVikram Garhwal 
can_btr_pre_write(RegisterInfo * reg,uint64_t val)64298e5d7a2SVikram Garhwal static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val)
64398e5d7a2SVikram Garhwal {
64498e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
64598e5d7a2SVikram Garhwal 
64698e5d7a2SVikram Garhwal     /* Only allow writes when in config mode. */
64798e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
64898e5d7a2SVikram Garhwal         return s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
64998e5d7a2SVikram Garhwal     }
65098e5d7a2SVikram Garhwal 
65198e5d7a2SVikram Garhwal     return val;
65298e5d7a2SVikram Garhwal }
65398e5d7a2SVikram Garhwal 
can_tcr_pre_write(RegisterInfo * reg,uint64_t val)65498e5d7a2SVikram Garhwal static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val)
65598e5d7a2SVikram Garhwal {
65698e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
65798e5d7a2SVikram Garhwal 
65898e5d7a2SVikram Garhwal     if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
65998e5d7a2SVikram Garhwal         ptimer_transaction_begin(s->can_timer);
66098e5d7a2SVikram Garhwal         ptimer_set_count(s->can_timer, 0);
66198e5d7a2SVikram Garhwal         ptimer_transaction_commit(s->can_timer);
66298e5d7a2SVikram Garhwal     }
66398e5d7a2SVikram Garhwal 
66498e5d7a2SVikram Garhwal     return 0;
66598e5d7a2SVikram Garhwal }
66698e5d7a2SVikram Garhwal 
update_rx_fifo(XlnxZynqMPCANState * s,const qemu_can_frame * frame)66798e5d7a2SVikram Garhwal static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame)
66898e5d7a2SVikram Garhwal {
66998e5d7a2SVikram Garhwal     bool filter_pass = false;
67098e5d7a2SVikram Garhwal     uint16_t timestamp = 0;
67198e5d7a2SVikram Garhwal 
67298e5d7a2SVikram Garhwal     /* If no filter is enabled. Message will be stored in FIFO. */
67398e5d7a2SVikram Garhwal     if (!((ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) |
67498e5d7a2SVikram Garhwal        (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) |
67598e5d7a2SVikram Garhwal        (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) |
67698e5d7a2SVikram Garhwal        (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)))) {
67798e5d7a2SVikram Garhwal         filter_pass = true;
67898e5d7a2SVikram Garhwal     }
67998e5d7a2SVikram Garhwal 
68098e5d7a2SVikram Garhwal     /*
68198e5d7a2SVikram Garhwal      * Messages that pass any of the acceptance filters will be stored in
68298e5d7a2SVikram Garhwal      * the RX FIFO.
68398e5d7a2SVikram Garhwal      */
68498e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) {
68598e5d7a2SVikram Garhwal         uint32_t id_masked = s->regs[R_AFMR1] & frame->can_id;
68698e5d7a2SVikram Garhwal         uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];
68798e5d7a2SVikram Garhwal 
68898e5d7a2SVikram Garhwal         if (filter_id_masked == id_masked) {
68998e5d7a2SVikram Garhwal             filter_pass = true;
69098e5d7a2SVikram Garhwal         }
69198e5d7a2SVikram Garhwal     }
69298e5d7a2SVikram Garhwal 
69398e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
69498e5d7a2SVikram Garhwal         uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
69598e5d7a2SVikram Garhwal         uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];
69698e5d7a2SVikram Garhwal 
69798e5d7a2SVikram Garhwal         if (filter_id_masked == id_masked) {
69898e5d7a2SVikram Garhwal             filter_pass = true;
69998e5d7a2SVikram Garhwal         }
70098e5d7a2SVikram Garhwal     }
70198e5d7a2SVikram Garhwal 
70298e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
70398e5d7a2SVikram Garhwal         uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
70498e5d7a2SVikram Garhwal         uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];
70598e5d7a2SVikram Garhwal 
70698e5d7a2SVikram Garhwal         if (filter_id_masked == id_masked) {
70798e5d7a2SVikram Garhwal             filter_pass = true;
70898e5d7a2SVikram Garhwal         }
70998e5d7a2SVikram Garhwal     }
71098e5d7a2SVikram Garhwal 
71198e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
71298e5d7a2SVikram Garhwal         uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
71398e5d7a2SVikram Garhwal         uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];
71498e5d7a2SVikram Garhwal 
71598e5d7a2SVikram Garhwal         if (filter_id_masked == id_masked) {
71698e5d7a2SVikram Garhwal             filter_pass = true;
71798e5d7a2SVikram Garhwal         }
71898e5d7a2SVikram Garhwal     }
71998e5d7a2SVikram Garhwal 
72098e5d7a2SVikram Garhwal     if (!filter_pass) {
72198e5d7a2SVikram Garhwal         trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc);
72298e5d7a2SVikram Garhwal         return;
72398e5d7a2SVikram Garhwal     }
72498e5d7a2SVikram Garhwal 
72598e5d7a2SVikram Garhwal     /* Store the message in fifo if it passed through any of the filters. */
72698e5d7a2SVikram Garhwal     if (filter_pass && frame->can_dlc <= MAX_DLC) {
72798e5d7a2SVikram Garhwal 
72898e5d7a2SVikram Garhwal         if (fifo32_is_full(&s->rx_fifo)) {
72998e5d7a2SVikram Garhwal             ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
73098e5d7a2SVikram Garhwal         } else {
73198e5d7a2SVikram Garhwal             timestamp = CAN_TIMER_MAX - ptimer_get_count(s->can_timer);
73298e5d7a2SVikram Garhwal 
73398e5d7a2SVikram Garhwal             fifo32_push(&s->rx_fifo, frame->can_id);
73498e5d7a2SVikram Garhwal 
73598e5d7a2SVikram Garhwal             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT,
73698e5d7a2SVikram Garhwal                                                R_RXFIFO_DLC_DLC_LENGTH,
73798e5d7a2SVikram Garhwal                                                frame->can_dlc) |
73898e5d7a2SVikram Garhwal                                      deposit32(0, R_RXFIFO_DLC_RXT_SHIFT,
73998e5d7a2SVikram Garhwal                                                R_RXFIFO_DLC_RXT_LENGTH,
74098e5d7a2SVikram Garhwal                                                timestamp));
74198e5d7a2SVikram Garhwal 
74298e5d7a2SVikram Garhwal             /* First 32 bit of the data. */
743fb96d131SAnton Kochkov             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA1_DB3_SHIFT,
744fb96d131SAnton Kochkov                                                R_RXFIFO_DATA1_DB3_LENGTH,
74598e5d7a2SVikram Garhwal                                                frame->data[0]) |
746fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA1_DB2_SHIFT,
747fb96d131SAnton Kochkov                                                R_RXFIFO_DATA1_DB2_LENGTH,
74898e5d7a2SVikram Garhwal                                                frame->data[1]) |
749fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA1_DB1_SHIFT,
750fb96d131SAnton Kochkov                                                R_RXFIFO_DATA1_DB1_LENGTH,
75198e5d7a2SVikram Garhwal                                                frame->data[2]) |
752fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA1_DB0_SHIFT,
753fb96d131SAnton Kochkov                                                R_RXFIFO_DATA1_DB0_LENGTH,
75498e5d7a2SVikram Garhwal                                                frame->data[3]));
75598e5d7a2SVikram Garhwal             /* Last 32 bit of the data. */
756fb96d131SAnton Kochkov             fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA2_DB7_SHIFT,
757fb96d131SAnton Kochkov                                                R_RXFIFO_DATA2_DB7_LENGTH,
75898e5d7a2SVikram Garhwal                                                frame->data[4]) |
759fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA2_DB6_SHIFT,
760fb96d131SAnton Kochkov                                                R_RXFIFO_DATA2_DB6_LENGTH,
76198e5d7a2SVikram Garhwal                                                frame->data[5]) |
762fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA2_DB5_SHIFT,
763fb96d131SAnton Kochkov                                                R_RXFIFO_DATA2_DB5_LENGTH,
76498e5d7a2SVikram Garhwal                                                frame->data[6]) |
765fb96d131SAnton Kochkov                                      deposit32(0, R_RXFIFO_DATA2_DB4_SHIFT,
766fb96d131SAnton Kochkov                                                R_RXFIFO_DATA2_DB4_LENGTH,
76798e5d7a2SVikram Garhwal                                                frame->data[7]));
76898e5d7a2SVikram Garhwal 
76998e5d7a2SVikram Garhwal             ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1);
77098e5d7a2SVikram Garhwal             trace_xlnx_can_rx_data(frame->can_id, frame->can_dlc,
77198e5d7a2SVikram Garhwal                                    frame->data[0], frame->data[1],
77298e5d7a2SVikram Garhwal                                    frame->data[2], frame->data[3],
77398e5d7a2SVikram Garhwal                                    frame->data[4], frame->data[5],
77498e5d7a2SVikram Garhwal                                    frame->data[6], frame->data[7]);
77598e5d7a2SVikram Garhwal         }
77698e5d7a2SVikram Garhwal 
77798e5d7a2SVikram Garhwal         can_update_irq(s);
77898e5d7a2SVikram Garhwal     }
77998e5d7a2SVikram Garhwal }
78098e5d7a2SVikram Garhwal 
can_rxfifo_post_read_id(RegisterInfo * reg,uint64_t val)7818729856cSPhilippe Mathieu-Daudé static uint64_t can_rxfifo_post_read_id(RegisterInfo *reg, uint64_t val)
78298e5d7a2SVikram Garhwal {
78398e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
7848729856cSPhilippe Mathieu-Daudé     unsigned used = fifo32_num_used(&s->rx_fifo);
78598e5d7a2SVikram Garhwal 
7868729856cSPhilippe Mathieu-Daudé     if (used < CAN_FRAME_SIZE) {
78798e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1);
7888729856cSPhilippe Mathieu-Daudé     } else {
7898729856cSPhilippe Mathieu-Daudé         val = s->regs[R_RXFIFO_ID] = fifo32_pop(&s->rx_fifo);
7908729856cSPhilippe Mathieu-Daudé         s->regs[R_RXFIFO_DLC] = fifo32_pop(&s->rx_fifo);
7918729856cSPhilippe Mathieu-Daudé         s->regs[R_RXFIFO_DATA1] = fifo32_pop(&s->rx_fifo);
7928729856cSPhilippe Mathieu-Daudé         s->regs[R_RXFIFO_DATA2] = fifo32_pop(&s->rx_fifo);
79398e5d7a2SVikram Garhwal     }
79498e5d7a2SVikram Garhwal 
79598e5d7a2SVikram Garhwal     can_update_irq(s);
79698e5d7a2SVikram Garhwal     return val;
79798e5d7a2SVikram Garhwal }
79898e5d7a2SVikram Garhwal 
can_filter_enable_post_write(RegisterInfo * reg,uint64_t val)79998e5d7a2SVikram Garhwal static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val)
80098e5d7a2SVikram Garhwal {
80198e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
80298e5d7a2SVikram Garhwal 
80398e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1) &&
80498e5d7a2SVikram Garhwal         ARRAY_FIELD_EX32(s->regs, AFR, UAF2) &&
80598e5d7a2SVikram Garhwal         ARRAY_FIELD_EX32(s->regs, AFR, UAF3) &&
80698e5d7a2SVikram Garhwal         ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
80798e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 1);
80898e5d7a2SVikram Garhwal     } else {
80998e5d7a2SVikram Garhwal         ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
81098e5d7a2SVikram Garhwal     }
81198e5d7a2SVikram Garhwal }
81298e5d7a2SVikram Garhwal 
can_filter_mask_pre_write(RegisterInfo * reg,uint64_t val)81398e5d7a2SVikram Garhwal static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val)
81498e5d7a2SVikram Garhwal {
81598e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
81698e5d7a2SVikram Garhwal     uint32_t reg_idx = (reg->access->addr) / 4;
81798e5d7a2SVikram Garhwal     uint32_t filter_number = (reg_idx - R_AFMR1) / 2;
81898e5d7a2SVikram Garhwal 
81998e5d7a2SVikram Garhwal     /* modify an acceptance filter, the corresponding UAF bit should be '0'. */
82098e5d7a2SVikram Garhwal     if (!(s->regs[R_AFR] & (1 << filter_number))) {
82198e5d7a2SVikram Garhwal         s->regs[reg_idx] = val;
82298e5d7a2SVikram Garhwal 
82398e5d7a2SVikram Garhwal         trace_xlnx_can_filter_mask_pre_write(filter_number, s->regs[reg_idx]);
82498e5d7a2SVikram Garhwal     } else {
82598e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
82698e5d7a2SVikram Garhwal 
82798e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
82898e5d7a2SVikram Garhwal                       " mask is not set as corresponding UAF bit is not 0.\n",
82998e5d7a2SVikram Garhwal                       path, filter_number + 1);
83098e5d7a2SVikram Garhwal     }
83198e5d7a2SVikram Garhwal 
83298e5d7a2SVikram Garhwal     return s->regs[reg_idx];
83398e5d7a2SVikram Garhwal }
83498e5d7a2SVikram Garhwal 
can_filter_id_pre_write(RegisterInfo * reg,uint64_t val)83598e5d7a2SVikram Garhwal static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val)
83698e5d7a2SVikram Garhwal {
83798e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
83898e5d7a2SVikram Garhwal     uint32_t reg_idx = (reg->access->addr) / 4;
83998e5d7a2SVikram Garhwal     uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
84098e5d7a2SVikram Garhwal 
84198e5d7a2SVikram Garhwal     if (!(s->regs[R_AFR] & (1 << filter_number))) {
84298e5d7a2SVikram Garhwal         s->regs[reg_idx] = val;
84398e5d7a2SVikram Garhwal 
84498e5d7a2SVikram Garhwal         trace_xlnx_can_filter_id_pre_write(filter_number, s->regs[reg_idx]);
84598e5d7a2SVikram Garhwal     } else {
84698e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
84798e5d7a2SVikram Garhwal 
84898e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
84998e5d7a2SVikram Garhwal                       " id is not set as corresponding UAF bit is not 0.\n",
85098e5d7a2SVikram Garhwal                       path, filter_number + 1);
85198e5d7a2SVikram Garhwal     }
85298e5d7a2SVikram Garhwal 
85398e5d7a2SVikram Garhwal     return s->regs[reg_idx];
85498e5d7a2SVikram Garhwal }
85598e5d7a2SVikram Garhwal 
can_tx_post_write(RegisterInfo * reg,uint64_t val)85698e5d7a2SVikram Garhwal static void can_tx_post_write(RegisterInfo *reg, uint64_t val)
85798e5d7a2SVikram Garhwal {
85898e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
85998e5d7a2SVikram Garhwal 
86098e5d7a2SVikram Garhwal     bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2;
86198e5d7a2SVikram Garhwal 
86298e5d7a2SVikram Garhwal     bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) ||
86398e5d7a2SVikram Garhwal                              (reg->access->addr == A_TXHPB_DATA2);
86498e5d7a2SVikram Garhwal 
86598e5d7a2SVikram Garhwal     Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
86698e5d7a2SVikram Garhwal 
86798e5d7a2SVikram Garhwal     if (!fifo32_is_full(f)) {
86898e5d7a2SVikram Garhwal         fifo32_push(f, val);
86998e5d7a2SVikram Garhwal     } else {
87098e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
87198e5d7a2SVikram Garhwal 
87298e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: TX FIFO is full.\n", path);
87398e5d7a2SVikram Garhwal     }
87498e5d7a2SVikram Garhwal 
87598e5d7a2SVikram Garhwal     /* Initiate the message send if TX register is written. */
87698e5d7a2SVikram Garhwal     if (initiate_transfer &&
87798e5d7a2SVikram Garhwal         ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
87898e5d7a2SVikram Garhwal         transfer_fifo(s, f);
87998e5d7a2SVikram Garhwal     }
88098e5d7a2SVikram Garhwal 
88198e5d7a2SVikram Garhwal     can_update_irq(s);
88298e5d7a2SVikram Garhwal }
88398e5d7a2SVikram Garhwal 
88498e5d7a2SVikram Garhwal static const RegisterAccessInfo can_regs_info[] = {
88598e5d7a2SVikram Garhwal     {   .name = "SOFTWARE_RESET_REGISTER",
88698e5d7a2SVikram Garhwal         .addr = A_SOFTWARE_RESET_REGISTER,
88798e5d7a2SVikram Garhwal         .rsvd = 0xfffffffc,
88898e5d7a2SVikram Garhwal         .pre_write = can_srr_pre_write,
88998e5d7a2SVikram Garhwal     },{ .name = "MODE_SELECT_REGISTER",
89098e5d7a2SVikram Garhwal         .addr = A_MODE_SELECT_REGISTER,
89198e5d7a2SVikram Garhwal         .rsvd = 0xfffffff8,
89298e5d7a2SVikram Garhwal         .pre_write = can_msr_pre_write,
89398e5d7a2SVikram Garhwal     },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
89498e5d7a2SVikram Garhwal         .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
89598e5d7a2SVikram Garhwal         .rsvd = 0xffffff00,
89698e5d7a2SVikram Garhwal         .pre_write = can_brpr_pre_write,
89798e5d7a2SVikram Garhwal     },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
89898e5d7a2SVikram Garhwal         .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
89998e5d7a2SVikram Garhwal         .rsvd = 0xfffffe00,
90098e5d7a2SVikram Garhwal         .pre_write = can_btr_pre_write,
90198e5d7a2SVikram Garhwal     },{ .name = "ERROR_COUNTER_REGISTER",
90298e5d7a2SVikram Garhwal         .addr = A_ERROR_COUNTER_REGISTER,
90398e5d7a2SVikram Garhwal         .rsvd = 0xffff0000,
90498e5d7a2SVikram Garhwal         .ro = 0xffffffff,
90598e5d7a2SVikram Garhwal     },{ .name = "ERROR_STATUS_REGISTER",
90698e5d7a2SVikram Garhwal         .addr = A_ERROR_STATUS_REGISTER,
90798e5d7a2SVikram Garhwal         .rsvd = 0xffffffe0,
90898e5d7a2SVikram Garhwal         .w1c = 0x1f,
90998e5d7a2SVikram Garhwal     },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER,
91098e5d7a2SVikram Garhwal         .reset = 0x1,
91198e5d7a2SVikram Garhwal         .rsvd = 0xffffe000,
91298e5d7a2SVikram Garhwal         .ro = 0x1fff,
91398e5d7a2SVikram Garhwal     },{ .name = "INTERRUPT_STATUS_REGISTER",
91498e5d7a2SVikram Garhwal         .addr = A_INTERRUPT_STATUS_REGISTER,
91598e5d7a2SVikram Garhwal         .reset = 0x6000,
91698e5d7a2SVikram Garhwal         .rsvd = 0xffff8000,
91798e5d7a2SVikram Garhwal         .ro = 0x7fff,
91898e5d7a2SVikram Garhwal     },{ .name = "INTERRUPT_ENABLE_REGISTER",
91998e5d7a2SVikram Garhwal         .addr = A_INTERRUPT_ENABLE_REGISTER,
92098e5d7a2SVikram Garhwal         .rsvd = 0xffff8000,
92198e5d7a2SVikram Garhwal         .post_write = can_ier_post_write,
92298e5d7a2SVikram Garhwal     },{ .name = "INTERRUPT_CLEAR_REGISTER",
92398e5d7a2SVikram Garhwal         .addr = A_INTERRUPT_CLEAR_REGISTER,
92498e5d7a2SVikram Garhwal         .rsvd = 0xffff8000,
92598e5d7a2SVikram Garhwal         .pre_write = can_icr_pre_write,
92698e5d7a2SVikram Garhwal     },{ .name = "TIMESTAMP_REGISTER",
92798e5d7a2SVikram Garhwal         .addr = A_TIMESTAMP_REGISTER,
92898e5d7a2SVikram Garhwal         .rsvd = 0xfffffffe,
92998e5d7a2SVikram Garhwal         .pre_write = can_tcr_pre_write,
93098e5d7a2SVikram Garhwal     },{ .name = "WIR",  .addr = A_WIR,
93198e5d7a2SVikram Garhwal         .reset = 0x3f3f,
93298e5d7a2SVikram Garhwal         .rsvd = 0xffff0000,
93398e5d7a2SVikram Garhwal     },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID,
93498e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
93598e5d7a2SVikram Garhwal     },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC,
93698e5d7a2SVikram Garhwal         .rsvd = 0xfffffff,
93798e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
93898e5d7a2SVikram Garhwal     },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1,
93998e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
94098e5d7a2SVikram Garhwal     },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2,
94198e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
94298e5d7a2SVikram Garhwal     },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID,
94398e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
94498e5d7a2SVikram Garhwal     },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC,
94598e5d7a2SVikram Garhwal         .rsvd = 0xfffffff,
94698e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
94798e5d7a2SVikram Garhwal     },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1,
94898e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
94998e5d7a2SVikram Garhwal     },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2,
95098e5d7a2SVikram Garhwal         .post_write = can_tx_post_write,
95198e5d7a2SVikram Garhwal     },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID,
95298e5d7a2SVikram Garhwal         .ro = 0xffffffff,
9538729856cSPhilippe Mathieu-Daudé         .post_read = can_rxfifo_post_read_id,
95498e5d7a2SVikram Garhwal     },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC,
95598e5d7a2SVikram Garhwal         .rsvd = 0xfff0000,
95698e5d7a2SVikram Garhwal     },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1,
95798e5d7a2SVikram Garhwal     },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2,
95898e5d7a2SVikram Garhwal     },{ .name = "AFR",  .addr = A_AFR,
95998e5d7a2SVikram Garhwal         .rsvd = 0xfffffff0,
96098e5d7a2SVikram Garhwal         .post_write = can_filter_enable_post_write,
96198e5d7a2SVikram Garhwal     },{ .name = "AFMR1",  .addr = A_AFMR1,
96298e5d7a2SVikram Garhwal         .pre_write = can_filter_mask_pre_write,
96398e5d7a2SVikram Garhwal     },{ .name = "AFIR1",  .addr = A_AFIR1,
96498e5d7a2SVikram Garhwal         .pre_write = can_filter_id_pre_write,
96598e5d7a2SVikram Garhwal     },{ .name = "AFMR2",  .addr = A_AFMR2,
96698e5d7a2SVikram Garhwal         .pre_write = can_filter_mask_pre_write,
96798e5d7a2SVikram Garhwal     },{ .name = "AFIR2",  .addr = A_AFIR2,
96898e5d7a2SVikram Garhwal         .pre_write = can_filter_id_pre_write,
96998e5d7a2SVikram Garhwal     },{ .name = "AFMR3",  .addr = A_AFMR3,
97098e5d7a2SVikram Garhwal         .pre_write = can_filter_mask_pre_write,
97198e5d7a2SVikram Garhwal     },{ .name = "AFIR3",  .addr = A_AFIR3,
97298e5d7a2SVikram Garhwal         .pre_write = can_filter_id_pre_write,
97398e5d7a2SVikram Garhwal     },{ .name = "AFMR4",  .addr = A_AFMR4,
97498e5d7a2SVikram Garhwal         .pre_write = can_filter_mask_pre_write,
97598e5d7a2SVikram Garhwal     },{ .name = "AFIR4",  .addr = A_AFIR4,
97698e5d7a2SVikram Garhwal         .pre_write = can_filter_id_pre_write,
97798e5d7a2SVikram Garhwal     }
97898e5d7a2SVikram Garhwal };
97998e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_ptimer_cb(void * opaque)98098e5d7a2SVikram Garhwal static void xlnx_zynqmp_can_ptimer_cb(void *opaque)
98198e5d7a2SVikram Garhwal {
98298e5d7a2SVikram Garhwal     /* No action required on the timer rollover. */
98398e5d7a2SVikram Garhwal }
98498e5d7a2SVikram Garhwal 
98598e5d7a2SVikram Garhwal static const MemoryRegionOps can_ops = {
98698e5d7a2SVikram Garhwal     .read = register_read_memory,
98798e5d7a2SVikram Garhwal     .write = register_write_memory,
98898e5d7a2SVikram Garhwal     .endianness = DEVICE_LITTLE_ENDIAN,
98998e5d7a2SVikram Garhwal     .valid = {
99098e5d7a2SVikram Garhwal         .min_access_size = 4,
99198e5d7a2SVikram Garhwal         .max_access_size = 4,
99298e5d7a2SVikram Garhwal     },
99398e5d7a2SVikram Garhwal };
99498e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_reset_init(Object * obj,ResetType type)99598e5d7a2SVikram Garhwal static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type)
99698e5d7a2SVikram Garhwal {
99798e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj);
99898e5d7a2SVikram Garhwal     unsigned int i;
99998e5d7a2SVikram Garhwal 
100098e5d7a2SVikram Garhwal     for (i = R_RXFIFO_ID; i < ARRAY_SIZE(s->reg_info); ++i) {
100198e5d7a2SVikram Garhwal         register_reset(&s->reg_info[i]);
100298e5d7a2SVikram Garhwal     }
100398e5d7a2SVikram Garhwal 
100498e5d7a2SVikram Garhwal     ptimer_transaction_begin(s->can_timer);
100598e5d7a2SVikram Garhwal     ptimer_set_count(s->can_timer, 0);
100698e5d7a2SVikram Garhwal     ptimer_transaction_commit(s->can_timer);
100798e5d7a2SVikram Garhwal }
100898e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_reset_hold(Object * obj,ResetType type)1009*ad80e367SPeter Maydell static void xlnx_zynqmp_can_reset_hold(Object *obj, ResetType type)
101098e5d7a2SVikram Garhwal {
101198e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj);
101298e5d7a2SVikram Garhwal     unsigned int i;
101398e5d7a2SVikram Garhwal 
101498e5d7a2SVikram Garhwal     for (i = 0; i < R_RXFIFO_ID; ++i) {
101598e5d7a2SVikram Garhwal         register_reset(&s->reg_info[i]);
101698e5d7a2SVikram Garhwal     }
101798e5d7a2SVikram Garhwal 
101898e5d7a2SVikram Garhwal     /*
101998e5d7a2SVikram Garhwal      * Reset FIFOs when CAN model is reset. This will clear the fifo writes
102098e5d7a2SVikram Garhwal      * done by post_write which gets called from register_reset function,
102198e5d7a2SVikram Garhwal      * post_write handle will not be able to trigger tx because CAN will be
102298e5d7a2SVikram Garhwal      * disabled when software_reset_register is cleared first.
102398e5d7a2SVikram Garhwal      */
102498e5d7a2SVikram Garhwal     fifo32_reset(&s->rx_fifo);
102598e5d7a2SVikram Garhwal     fifo32_reset(&s->tx_fifo);
102698e5d7a2SVikram Garhwal     fifo32_reset(&s->txhpb_fifo);
102798e5d7a2SVikram Garhwal }
102898e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_can_receive(CanBusClientState * client)102998e5d7a2SVikram Garhwal static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
103098e5d7a2SVikram Garhwal {
103198e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
103298e5d7a2SVikram Garhwal                                          bus_client);
103398e5d7a2SVikram Garhwal 
103498e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
103598e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
103698e5d7a2SVikram Garhwal 
103798e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in reset state.\n",
103898e5d7a2SVikram Garhwal                       path);
103998e5d7a2SVikram Garhwal         return false;
104098e5d7a2SVikram Garhwal     }
104198e5d7a2SVikram Garhwal 
104298e5d7a2SVikram Garhwal     if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
104398e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
104498e5d7a2SVikram Garhwal 
104598e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is disabled. Incoming"
104698e5d7a2SVikram Garhwal                       " messages will be discarded.\n", path);
104798e5d7a2SVikram Garhwal         return false;
104898e5d7a2SVikram Garhwal     }
104998e5d7a2SVikram Garhwal 
105098e5d7a2SVikram Garhwal     return true;
105198e5d7a2SVikram Garhwal }
105298e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_receive(CanBusClientState * client,const qemu_can_frame * buf,size_t buf_size)105398e5d7a2SVikram Garhwal static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
105498e5d7a2SVikram Garhwal                                const qemu_can_frame *buf, size_t buf_size) {
105598e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
105698e5d7a2SVikram Garhwal                                          bus_client);
105798e5d7a2SVikram Garhwal     const qemu_can_frame *frame = buf;
105898e5d7a2SVikram Garhwal 
105998e5d7a2SVikram Garhwal     if (buf_size <= 0) {
106098e5d7a2SVikram Garhwal         g_autofree char *path = object_get_canonical_path(OBJECT(s));
106198e5d7a2SVikram Garhwal 
106298e5d7a2SVikram Garhwal         qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n",
106398e5d7a2SVikram Garhwal                       path);
106498e5d7a2SVikram Garhwal         return 0;
106598e5d7a2SVikram Garhwal     }
106698e5d7a2SVikram Garhwal 
106798e5d7a2SVikram Garhwal     if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
106898e5d7a2SVikram Garhwal         /* Snoop Mode: Just keep the data. no response back. */
106998e5d7a2SVikram Garhwal         update_rx_fifo(s, frame);
107098e5d7a2SVikram Garhwal     } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) {
107198e5d7a2SVikram Garhwal         /*
107298e5d7a2SVikram Garhwal          * XlnxZynqMPCAN is in sleep mode. Any data on bus will bring it to wake
107398e5d7a2SVikram Garhwal          * up state.
107498e5d7a2SVikram Garhwal          */
107598e5d7a2SVikram Garhwal         can_exit_sleep_mode(s);
107698e5d7a2SVikram Garhwal         update_rx_fifo(s, frame);
107798e5d7a2SVikram Garhwal     } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
107898e5d7a2SVikram Garhwal         update_rx_fifo(s, frame);
107998e5d7a2SVikram Garhwal     } else {
108098e5d7a2SVikram Garhwal         /*
108198e5d7a2SVikram Garhwal          * XlnxZynqMPCAN will not participate in normal bus communication
108298e5d7a2SVikram Garhwal          * and will not receive any messages transmitted by other CAN nodes.
108398e5d7a2SVikram Garhwal          */
108498e5d7a2SVikram Garhwal         trace_xlnx_can_rx_discard(s->regs[R_STATUS_REGISTER]);
108598e5d7a2SVikram Garhwal     }
108698e5d7a2SVikram Garhwal 
108798e5d7a2SVikram Garhwal     return 1;
108898e5d7a2SVikram Garhwal }
108998e5d7a2SVikram Garhwal 
109098e5d7a2SVikram Garhwal static CanBusClientInfo can_xilinx_bus_client_info = {
109198e5d7a2SVikram Garhwal     .can_receive = xlnx_zynqmp_can_can_receive,
109298e5d7a2SVikram Garhwal     .receive = xlnx_zynqmp_can_receive,
109398e5d7a2SVikram Garhwal };
109498e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCANState * s,CanBusState * bus)109598e5d7a2SVikram Garhwal static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCANState *s,
109698e5d7a2SVikram Garhwal                                           CanBusState *bus)
109798e5d7a2SVikram Garhwal {
109898e5d7a2SVikram Garhwal     s->bus_client.info = &can_xilinx_bus_client_info;
109998e5d7a2SVikram Garhwal 
110098e5d7a2SVikram Garhwal     if (can_bus_insert_client(bus, &s->bus_client) < 0) {
110198e5d7a2SVikram Garhwal         return -1;
110298e5d7a2SVikram Garhwal     }
110398e5d7a2SVikram Garhwal     return 0;
110498e5d7a2SVikram Garhwal }
110598e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_realize(DeviceState * dev,Error ** errp)110698e5d7a2SVikram Garhwal static void xlnx_zynqmp_can_realize(DeviceState *dev, Error **errp)
110798e5d7a2SVikram Garhwal {
110898e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
110998e5d7a2SVikram Garhwal 
111098e5d7a2SVikram Garhwal     if (s->canbus) {
111198e5d7a2SVikram Garhwal         if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus) < 0) {
111298e5d7a2SVikram Garhwal             g_autofree char *path = object_get_canonical_path(OBJECT(s));
111398e5d7a2SVikram Garhwal 
111498e5d7a2SVikram Garhwal             error_setg(errp, "%s: xlnx_zynqmp_can_connect_to_bus"
111598e5d7a2SVikram Garhwal                        " failed.", path);
111698e5d7a2SVikram Garhwal             return;
111798e5d7a2SVikram Garhwal         }
111898e5d7a2SVikram Garhwal     }
111998e5d7a2SVikram Garhwal 
112098e5d7a2SVikram Garhwal     /* Create RX FIFO, TXFIFO, TXHPB storage. */
112198e5d7a2SVikram Garhwal     fifo32_create(&s->rx_fifo, RXFIFO_SIZE);
112298e5d7a2SVikram Garhwal     fifo32_create(&s->tx_fifo, RXFIFO_SIZE);
112398e5d7a2SVikram Garhwal     fifo32_create(&s->txhpb_fifo, CAN_FRAME_SIZE);
112498e5d7a2SVikram Garhwal 
112598e5d7a2SVikram Garhwal     /* Allocate a new timer. */
112698e5d7a2SVikram Garhwal     s->can_timer = ptimer_init(xlnx_zynqmp_can_ptimer_cb, s,
11279598c1bbSPeter Maydell                                PTIMER_POLICY_LEGACY);
112898e5d7a2SVikram Garhwal 
112998e5d7a2SVikram Garhwal     ptimer_transaction_begin(s->can_timer);
113098e5d7a2SVikram Garhwal 
113198e5d7a2SVikram Garhwal     ptimer_set_freq(s->can_timer, s->cfg.ext_clk_freq);
113298e5d7a2SVikram Garhwal     ptimer_set_limit(s->can_timer, CAN_TIMER_MAX, 1);
113398e5d7a2SVikram Garhwal     ptimer_run(s->can_timer, 0);
113498e5d7a2SVikram Garhwal     ptimer_transaction_commit(s->can_timer);
113598e5d7a2SVikram Garhwal }
113698e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_init(Object * obj)113798e5d7a2SVikram Garhwal static void xlnx_zynqmp_can_init(Object *obj)
113898e5d7a2SVikram Garhwal {
113998e5d7a2SVikram Garhwal     XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj);
114098e5d7a2SVikram Garhwal     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
114198e5d7a2SVikram Garhwal 
114298e5d7a2SVikram Garhwal     RegisterInfoArray *reg_array;
114398e5d7a2SVikram Garhwal 
114498e5d7a2SVikram Garhwal     memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_CAN,
114598e5d7a2SVikram Garhwal                         XLNX_ZYNQMP_CAN_R_MAX * 4);
114698e5d7a2SVikram Garhwal     reg_array = register_init_block32(DEVICE(obj), can_regs_info,
114798e5d7a2SVikram Garhwal                                ARRAY_SIZE(can_regs_info),
114898e5d7a2SVikram Garhwal                                s->reg_info, s->regs,
114998e5d7a2SVikram Garhwal                                &can_ops,
115098e5d7a2SVikram Garhwal                                XLNX_ZYNQMP_CAN_ERR_DEBUG,
115198e5d7a2SVikram Garhwal                                XLNX_ZYNQMP_CAN_R_MAX * 4);
115298e5d7a2SVikram Garhwal 
115398e5d7a2SVikram Garhwal     memory_region_add_subregion(&s->iomem, 0x00, &reg_array->mem);
115498e5d7a2SVikram Garhwal     sysbus_init_mmio(sbd, &s->iomem);
115598e5d7a2SVikram Garhwal     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
115698e5d7a2SVikram Garhwal }
115798e5d7a2SVikram Garhwal 
115898e5d7a2SVikram Garhwal static const VMStateDescription vmstate_can = {
115998e5d7a2SVikram Garhwal     .name = TYPE_XLNX_ZYNQMP_CAN,
116098e5d7a2SVikram Garhwal     .version_id = 1,
116198e5d7a2SVikram Garhwal     .minimum_version_id = 1,
11621de81b42SRichard Henderson     .fields = (const VMStateField[]) {
116398e5d7a2SVikram Garhwal         VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCANState),
116498e5d7a2SVikram Garhwal         VMSTATE_FIFO32(tx_fifo, XlnxZynqMPCANState),
116598e5d7a2SVikram Garhwal         VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState),
116698e5d7a2SVikram Garhwal         VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX),
116798e5d7a2SVikram Garhwal         VMSTATE_PTIMER(can_timer, XlnxZynqMPCANState),
116898e5d7a2SVikram Garhwal         VMSTATE_END_OF_LIST(),
116998e5d7a2SVikram Garhwal     }
117098e5d7a2SVikram Garhwal };
117198e5d7a2SVikram Garhwal 
117298e5d7a2SVikram Garhwal static Property xlnx_zynqmp_can_properties[] = {
117398e5d7a2SVikram Garhwal     DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq,
117498e5d7a2SVikram Garhwal                        CAN_DEFAULT_CLOCK),
117598e5d7a2SVikram Garhwal     DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS,
117698e5d7a2SVikram Garhwal                      CanBusState *),
117798e5d7a2SVikram Garhwal     DEFINE_PROP_END_OF_LIST(),
117898e5d7a2SVikram Garhwal };
117998e5d7a2SVikram Garhwal 
xlnx_zynqmp_can_class_init(ObjectClass * klass,void * data)118098e5d7a2SVikram Garhwal static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
118198e5d7a2SVikram Garhwal {
118298e5d7a2SVikram Garhwal     DeviceClass *dc = DEVICE_CLASS(klass);
118398e5d7a2SVikram Garhwal     ResettableClass *rc = RESETTABLE_CLASS(klass);
118498e5d7a2SVikram Garhwal 
118598e5d7a2SVikram Garhwal     rc->phases.enter = xlnx_zynqmp_can_reset_init;
118698e5d7a2SVikram Garhwal     rc->phases.hold = xlnx_zynqmp_can_reset_hold;
118798e5d7a2SVikram Garhwal     dc->realize = xlnx_zynqmp_can_realize;
118898e5d7a2SVikram Garhwal     device_class_set_props(dc, xlnx_zynqmp_can_properties);
118998e5d7a2SVikram Garhwal     dc->vmsd = &vmstate_can;
119098e5d7a2SVikram Garhwal }
119198e5d7a2SVikram Garhwal 
119298e5d7a2SVikram Garhwal static const TypeInfo can_info = {
119398e5d7a2SVikram Garhwal     .name          = TYPE_XLNX_ZYNQMP_CAN,
119498e5d7a2SVikram Garhwal     .parent        = TYPE_SYS_BUS_DEVICE,
119598e5d7a2SVikram Garhwal     .instance_size = sizeof(XlnxZynqMPCANState),
119698e5d7a2SVikram Garhwal     .class_init    = xlnx_zynqmp_can_class_init,
119798e5d7a2SVikram Garhwal     .instance_init = xlnx_zynqmp_can_init,
119898e5d7a2SVikram Garhwal };
119998e5d7a2SVikram Garhwal 
can_register_types(void)120098e5d7a2SVikram Garhwal static void can_register_types(void)
120198e5d7a2SVikram Garhwal {
120298e5d7a2SVikram Garhwal     type_register_static(&can_info);
120398e5d7a2SVikram Garhwal }
120498e5d7a2SVikram Garhwal 
120598e5d7a2SVikram Garhwal type_init(can_register_types)
1206