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, ®_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