Revision | cbb45ff038cdbfcc8af158191405e2597f28c562 (tree) |
---|---|
Time | 2022-01-28 23:29:46 |
Author | Francisco Iglesias <francisco.iglesias@xili...> |
Commiter | Peter Maydell |
hw/ssi: Add a model of Xilinx Versal's OSPI flash memory controller
Add a model of Xilinx Versal's OSPI flash memory controller.
Signed-off-by: Francisco Iglesias <francisco.iglesias@xilinx.com>
Reviewed-by: Luc Michel <luc@lmichel.fr>
Message-id: 20220121161141.14389-7-francisco.iglesias@xilinx.com
[PMM: fixed indent]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
@@ -7,5 +7,6 @@ softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) | ||
7 | 7 | softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) |
8 | 8 | softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) |
9 | 9 | softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) |
10 | +softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) | |
10 | 11 | softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) |
11 | 12 | softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) |
@@ -0,0 +1,1853 @@ | ||
1 | +/* | |
2 | + * QEMU model of Xilinx Versal's OSPI controller. | |
3 | + * | |
4 | + * Copyright (c) 2021 Xilinx Inc. | |
5 | + * Written by Francisco Iglesias <francisco.iglesias@xilinx.com> | |
6 | + * | |
7 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | + * of this software and associated documentation files (the "Software"), to deal | |
9 | + * in the Software without restriction, including without limitation the rights | |
10 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | + * copies of the Software, and to permit persons to whom the Software is | |
12 | + * furnished to do so, subject to the following conditions: | |
13 | + * | |
14 | + * The above copyright notice and this permission notice shall be included in | |
15 | + * all copies or substantial portions of the Software. | |
16 | + * | |
17 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | + * THE SOFTWARE. | |
24 | + */ | |
25 | +#include "qemu/osdep.h" | |
26 | +#include "hw/sysbus.h" | |
27 | +#include "migration/vmstate.h" | |
28 | +#include "hw/qdev-properties.h" | |
29 | +#include "qemu/bitops.h" | |
30 | +#include "qemu/log.h" | |
31 | +#include "hw/irq.h" | |
32 | +#include "hw/ssi/xlnx-versal-ospi.h" | |
33 | + | |
34 | +#ifndef XILINX_VERSAL_OSPI_ERR_DEBUG | |
35 | +#define XILINX_VERSAL_OSPI_ERR_DEBUG 0 | |
36 | +#endif | |
37 | + | |
38 | +REG32(CONFIG_REG, 0x0) | |
39 | + FIELD(CONFIG_REG, IDLE_FLD, 31, 1) | |
40 | + FIELD(CONFIG_REG, DUAL_BYTE_OPCODE_EN_FLD, 30, 1) | |
41 | + FIELD(CONFIG_REG, CRC_ENABLE_FLD, 29, 1) | |
42 | + FIELD(CONFIG_REG, CONFIG_RESV2_FLD, 26, 3) | |
43 | + FIELD(CONFIG_REG, PIPELINE_PHY_FLD, 25, 1) | |
44 | + FIELD(CONFIG_REG, ENABLE_DTR_PROTOCOL_FLD, 24, 1) | |
45 | + FIELD(CONFIG_REG, ENABLE_AHB_DECODER_FLD, 23, 1) | |
46 | + FIELD(CONFIG_REG, MSTR_BAUD_DIV_FLD, 19, 4) | |
47 | + FIELD(CONFIG_REG, ENTER_XIP_MODE_IMM_FLD, 18, 1) | |
48 | + FIELD(CONFIG_REG, ENTER_XIP_MODE_FLD, 17, 1) | |
49 | + FIELD(CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD, 16, 1) | |
50 | + FIELD(CONFIG_REG, ENB_DMA_IF_FLD, 15, 1) | |
51 | + FIELD(CONFIG_REG, WR_PROT_FLASH_FLD, 14, 1) | |
52 | + FIELD(CONFIG_REG, PERIPH_CS_LINES_FLD, 10, 4) | |
53 | + FIELD(CONFIG_REG, PERIPH_SEL_DEC_FLD, 9, 1) | |
54 | + FIELD(CONFIG_REG, ENB_LEGACY_IP_MODE_FLD, 8, 1) | |
55 | + FIELD(CONFIG_REG, ENB_DIR_ACC_CTLR_FLD, 7, 1) | |
56 | + FIELD(CONFIG_REG, RESET_CFG_FLD, 6, 1) | |
57 | + FIELD(CONFIG_REG, RESET_PIN_FLD, 5, 1) | |
58 | + FIELD(CONFIG_REG, HOLD_PIN_FLD, 4, 1) | |
59 | + FIELD(CONFIG_REG, PHY_MODE_ENABLE_FLD, 3, 1) | |
60 | + FIELD(CONFIG_REG, SEL_CLK_PHASE_FLD, 2, 1) | |
61 | + FIELD(CONFIG_REG, SEL_CLK_POL_FLD, 1, 1) | |
62 | + FIELD(CONFIG_REG, ENB_SPI_FLD, 0, 1) | |
63 | +REG32(DEV_INSTR_RD_CONFIG_REG, 0x4) | |
64 | + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV5_FLD, 29, 3) | |
65 | + FIELD(DEV_INSTR_RD_CONFIG_REG, DUMMY_RD_CLK_CYCLES_FLD, 24, 5) | |
66 | + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV4_FLD, 21, 3) | |
67 | + FIELD(DEV_INSTR_RD_CONFIG_REG, MODE_BIT_ENABLE_FLD, 20, 1) | |
68 | + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV3_FLD, 18, 2) | |
69 | + FIELD(DEV_INSTR_RD_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2) | |
70 | + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV2_FLD, 14, 2) | |
71 | + FIELD(DEV_INSTR_RD_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2) | |
72 | + FIELD(DEV_INSTR_RD_CONFIG_REG, PRED_DIS_FLD, 11, 1) | |
73 | + FIELD(DEV_INSTR_RD_CONFIG_REG, DDR_EN_FLD, 10, 1) | |
74 | + FIELD(DEV_INSTR_RD_CONFIG_REG, INSTR_TYPE_FLD, 8, 2) | |
75 | + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD, 0, 8) | |
76 | +REG32(DEV_INSTR_WR_CONFIG_REG, 0x8) | |
77 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV4_FLD, 29, 3) | |
78 | + FIELD(DEV_INSTR_WR_CONFIG_REG, DUMMY_WR_CLK_CYCLES_FLD, 24, 5) | |
79 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV3_FLD, 18, 6) | |
80 | + FIELD(DEV_INSTR_WR_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2) | |
81 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV2_FLD, 14, 2) | |
82 | + FIELD(DEV_INSTR_WR_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2) | |
83 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV1_FLD, 9, 3) | |
84 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD, 8, 1) | |
85 | + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD, 0, 8) | |
86 | +REG32(DEV_DELAY_REG, 0xc) | |
87 | + FIELD(DEV_DELAY_REG, D_NSS_FLD, 24, 8) | |
88 | + FIELD(DEV_DELAY_REG, D_BTWN_FLD, 16, 8) | |
89 | + FIELD(DEV_DELAY_REG, D_AFTER_FLD, 8, 8) | |
90 | + FIELD(DEV_DELAY_REG, D_INIT_FLD, 0, 8) | |
91 | +REG32(RD_DATA_CAPTURE_REG, 0x10) | |
92 | + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV3_FLD, 20, 12) | |
93 | + FIELD(RD_DATA_CAPTURE_REG, DDR_READ_DELAY_FLD, 16, 4) | |
94 | + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV2_FLD, 9, 7) | |
95 | + FIELD(RD_DATA_CAPTURE_REG, DQS_ENABLE_FLD, 8, 1) | |
96 | + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV1_FLD, 6, 2) | |
97 | + FIELD(RD_DATA_CAPTURE_REG, SAMPLE_EDGE_SEL_FLD, 5, 1) | |
98 | + FIELD(RD_DATA_CAPTURE_REG, DELAY_FLD, 1, 4) | |
99 | + FIELD(RD_DATA_CAPTURE_REG, BYPASS_FLD, 0, 1) | |
100 | +REG32(DEV_SIZE_CONFIG_REG, 0x14) | |
101 | + FIELD(DEV_SIZE_CONFIG_REG, DEV_SIZE_RESV_FLD, 29, 3) | |
102 | + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS3_FLD, 27, 2) | |
103 | + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS2_FLD, 25, 2) | |
104 | + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS1_FLD, 23, 2) | |
105 | + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD, 21, 2) | |
106 | + FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_SUBSECTOR_FLD, 16, 5) | |
107 | + FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD, 4, 12) | |
108 | + FIELD(DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD, 0, 4) | |
109 | +REG32(SRAM_PARTITION_CFG_REG, 0x18) | |
110 | + FIELD(SRAM_PARTITION_CFG_REG, SRAM_PARTITION_RESV_FLD, 8, 24) | |
111 | + FIELD(SRAM_PARTITION_CFG_REG, ADDR_FLD, 0, 8) | |
112 | +REG32(IND_AHB_ADDR_TRIGGER_REG, 0x1c) | |
113 | +REG32(DMA_PERIPH_CONFIG_REG, 0x20) | |
114 | + FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV2_FLD, 12, 20) | |
115 | + FIELD(DMA_PERIPH_CONFIG_REG, NUM_BURST_REQ_BYTES_FLD, 8, 4) | |
116 | + FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV1_FLD, 4, 4) | |
117 | + FIELD(DMA_PERIPH_CONFIG_REG, NUM_SINGLE_REQ_BYTES_FLD, 0, 4) | |
118 | +REG32(REMAP_ADDR_REG, 0x24) | |
119 | +REG32(MODE_BIT_CONFIG_REG, 0x28) | |
120 | + FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_LOW_FLD, 24, 8) | |
121 | + FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_UP_FLD, 16, 8) | |
122 | + FIELD(MODE_BIT_CONFIG_REG, CRC_OUT_ENABLE_FLD, 15, 1) | |
123 | + FIELD(MODE_BIT_CONFIG_REG, MODE_BIT_RESV1_FLD, 11, 4) | |
124 | + FIELD(MODE_BIT_CONFIG_REG, CHUNK_SIZE_FLD, 8, 3) | |
125 | + FIELD(MODE_BIT_CONFIG_REG, MODE_FLD, 0, 8) | |
126 | +REG32(SRAM_FILL_REG, 0x2c) | |
127 | + FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_WRITE_FLD, 16, 16) | |
128 | + FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_READ_FLD, 0, 16) | |
129 | +REG32(TX_THRESH_REG, 0x30) | |
130 | + FIELD(TX_THRESH_REG, TX_THRESH_RESV_FLD, 5, 27) | |
131 | + FIELD(TX_THRESH_REG, LEVEL_FLD, 0, 5) | |
132 | +REG32(RX_THRESH_REG, 0x34) | |
133 | + FIELD(RX_THRESH_REG, RX_THRESH_RESV_FLD, 5, 27) | |
134 | + FIELD(RX_THRESH_REG, LEVEL_FLD, 0, 5) | |
135 | +REG32(WRITE_COMPLETION_CTRL_REG, 0x38) | |
136 | + FIELD(WRITE_COMPLETION_CTRL_REG, POLL_REP_DELAY_FLD, 24, 8) | |
137 | + FIELD(WRITE_COMPLETION_CTRL_REG, POLL_COUNT_FLD, 16, 8) | |
138 | + FIELD(WRITE_COMPLETION_CTRL_REG, ENABLE_POLLING_EXP_FLD, 15, 1) | |
139 | + FIELD(WRITE_COMPLETION_CTRL_REG, DISABLE_POLLING_FLD, 14, 1) | |
140 | + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_POLARITY_FLD, 13, 1) | |
141 | + FIELD(WRITE_COMPLETION_CTRL_REG, WR_COMP_CTRL_RESV1_FLD, 12, 1) | |
142 | + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_ADDR_EN_FLD, 11, 1) | |
143 | + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_BIT_INDEX_FLD, 8, 3) | |
144 | + FIELD(WRITE_COMPLETION_CTRL_REG, OPCODE_FLD, 0, 8) | |
145 | +REG32(NO_OF_POLLS_BEF_EXP_REG, 0x3c) | |
146 | +REG32(IRQ_STATUS_REG, 0x40) | |
147 | + FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV_FLD, 20, 12) | |
148 | + FIELD(IRQ_STATUS_REG, ECC_FAIL_FLD, 19, 1) | |
149 | + FIELD(IRQ_STATUS_REG, TX_CRC_CHUNK_BRK_FLD, 18, 1) | |
150 | + FIELD(IRQ_STATUS_REG, RX_CRC_DATA_VAL_FLD, 17, 1) | |
151 | + FIELD(IRQ_STATUS_REG, RX_CRC_DATA_ERR_FLD, 16, 1) | |
152 | + FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV1_FLD, 15, 1) | |
153 | + FIELD(IRQ_STATUS_REG, STIG_REQ_INT_FLD, 14, 1) | |
154 | + FIELD(IRQ_STATUS_REG, POLL_EXP_INT_FLD, 13, 1) | |
155 | + FIELD(IRQ_STATUS_REG, INDRD_SRAM_FULL_FLD, 12, 1) | |
156 | + FIELD(IRQ_STATUS_REG, RX_FIFO_FULL_FLD, 11, 1) | |
157 | + FIELD(IRQ_STATUS_REG, RX_FIFO_NOT_EMPTY_FLD, 10, 1) | |
158 | + FIELD(IRQ_STATUS_REG, TX_FIFO_FULL_FLD, 9, 1) | |
159 | + FIELD(IRQ_STATUS_REG, TX_FIFO_NOT_FULL_FLD, 8, 1) | |
160 | + FIELD(IRQ_STATUS_REG, RECV_OVERFLOW_FLD, 7, 1) | |
161 | + FIELD(IRQ_STATUS_REG, INDIRECT_XFER_LEVEL_BREACH_FLD, 6, 1) | |
162 | + FIELD(IRQ_STATUS_REG, ILLEGAL_ACCESS_DET_FLD, 5, 1) | |
163 | + FIELD(IRQ_STATUS_REG, PROT_WR_ATTEMPT_FLD, 4, 1) | |
164 | + FIELD(IRQ_STATUS_REG, INDIRECT_TRANSFER_REJECT_FLD, 3, 1) | |
165 | + FIELD(IRQ_STATUS_REG, INDIRECT_OP_DONE_FLD, 2, 1) | |
166 | + FIELD(IRQ_STATUS_REG, UNDERFLOW_DET_FLD, 1, 1) | |
167 | + FIELD(IRQ_STATUS_REG, MODE_M_FAIL_FLD, 0, 1) | |
168 | +REG32(IRQ_MASK_REG, 0x44) | |
169 | + FIELD(IRQ_MASK_REG, IRQ_MASK_RESV_FLD, 20, 12) | |
170 | + FIELD(IRQ_MASK_REG, ECC_FAIL_MASK_FLD, 19, 1) | |
171 | + FIELD(IRQ_MASK_REG, TX_CRC_CHUNK_BRK_MASK_FLD, 18, 1) | |
172 | + FIELD(IRQ_MASK_REG, RX_CRC_DATA_VAL_MASK_FLD, 17, 1) | |
173 | + FIELD(IRQ_MASK_REG, RX_CRC_DATA_ERR_MASK_FLD, 16, 1) | |
174 | + FIELD(IRQ_MASK_REG, IRQ_MASK_RESV1_FLD, 15, 1) | |
175 | + FIELD(IRQ_MASK_REG, STIG_REQ_MASK_FLD, 14, 1) | |
176 | + FIELD(IRQ_MASK_REG, POLL_EXP_INT_MASK_FLD, 13, 1) | |
177 | + FIELD(IRQ_MASK_REG, INDRD_SRAM_FULL_MASK_FLD, 12, 1) | |
178 | + FIELD(IRQ_MASK_REG, RX_FIFO_FULL_MASK_FLD, 11, 1) | |
179 | + FIELD(IRQ_MASK_REG, RX_FIFO_NOT_EMPTY_MASK_FLD, 10, 1) | |
180 | + FIELD(IRQ_MASK_REG, TX_FIFO_FULL_MASK_FLD, 9, 1) | |
181 | + FIELD(IRQ_MASK_REG, TX_FIFO_NOT_FULL_MASK_FLD, 8, 1) | |
182 | + FIELD(IRQ_MASK_REG, RECV_OVERFLOW_MASK_FLD, 7, 1) | |
183 | + FIELD(IRQ_MASK_REG, INDIRECT_XFER_LEVEL_BREACH_MASK_FLD, 6, 1) | |
184 | + FIELD(IRQ_MASK_REG, ILLEGAL_ACCESS_DET_MASK_FLD, 5, 1) | |
185 | + FIELD(IRQ_MASK_REG, PROT_WR_ATTEMPT_MASK_FLD, 4, 1) | |
186 | + FIELD(IRQ_MASK_REG, INDIRECT_TRANSFER_REJECT_MASK_FLD, 3, 1) | |
187 | + FIELD(IRQ_MASK_REG, INDIRECT_OP_DONE_MASK_FLD, 2, 1) | |
188 | + FIELD(IRQ_MASK_REG, UNDERFLOW_DET_MASK_FLD, 1, 1) | |
189 | + FIELD(IRQ_MASK_REG, MODE_M_FAIL_MASK_FLD, 0, 1) | |
190 | +REG32(LOWER_WR_PROT_REG, 0x50) | |
191 | +REG32(UPPER_WR_PROT_REG, 0x54) | |
192 | +REG32(WR_PROT_CTRL_REG, 0x58) | |
193 | + FIELD(WR_PROT_CTRL_REG, WR_PROT_CTRL_RESV_FLD, 2, 30) | |
194 | + FIELD(WR_PROT_CTRL_REG, ENB_FLD, 1, 1) | |
195 | + FIELD(WR_PROT_CTRL_REG, INV_FLD, 0, 1) | |
196 | +REG32(INDIRECT_READ_XFER_CTRL_REG, 0x60) | |
197 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, INDIR_RD_XFER_RESV_FLD, 8, 24) | |
198 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2) | |
199 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1) | |
200 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_QUEUED_FLD, 4, 1) | |
201 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 3, 1) | |
202 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 2, 1) | |
203 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 1, 1) | |
204 | + FIELD(INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0, 1) | |
205 | +REG32(INDIRECT_READ_XFER_WATERMARK_REG, 0x64) | |
206 | +REG32(INDIRECT_READ_XFER_START_REG, 0x68) | |
207 | +REG32(INDIRECT_READ_XFER_NUM_BYTES_REG, 0x6c) | |
208 | +REG32(INDIRECT_WRITE_XFER_CTRL_REG, 0x70) | |
209 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV2_FLD, 8, 24) | |
210 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2) | |
211 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1) | |
212 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_QUEUED_FLD, 4, 1) | |
213 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV1_FLD, 3, 1) | |
214 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 2, 1) | |
215 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 1, 1) | |
216 | + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0, 1) | |
217 | +REG32(INDIRECT_WRITE_XFER_WATERMARK_REG, 0x74) | |
218 | +REG32(INDIRECT_WRITE_XFER_START_REG, 0x78) | |
219 | +REG32(INDIRECT_WRITE_XFER_NUM_BYTES_REG, 0x7c) | |
220 | +REG32(INDIRECT_TRIGGER_ADDR_RANGE_REG, 0x80) | |
221 | + FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_RESV1_FLD, 4, 28) | |
222 | + FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_WIDTH_FLD, 0, 4) | |
223 | +REG32(FLASH_COMMAND_CTRL_MEM_REG, 0x8c) | |
224 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV1_FLD, 29, 3) | |
225 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD, 20, 9) | |
226 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV2_FLD, 19, 1) | |
227 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, NB_OF_STIG_READ_BYTES_FLD, 16, 3) | |
228 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_READ_DATA_FLD, 8, 8) | |
229 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV3_FLD, 2, 6) | |
230 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_REQ_IN_PROGRESS_FLD, 1, 1) | |
231 | + FIELD(FLASH_COMMAND_CTRL_MEM_REG, TRIGGER_MEM_BANK_REQ_FLD, 0, 1) | |
232 | +REG32(FLASH_CMD_CTRL_REG, 0x90) | |
233 | + FIELD(FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD, 24, 8) | |
234 | + FIELD(FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD, 23, 1) | |
235 | + FIELD(FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD, 20, 3) | |
236 | + FIELD(FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD, 19, 1) | |
237 | + FIELD(FLASH_CMD_CTRL_REG, ENB_MODE_BIT_FLD, 18, 1) | |
238 | + FIELD(FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD, 16, 2) | |
239 | + FIELD(FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD, 15, 1) | |
240 | + FIELD(FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD, 12, 3) | |
241 | + FIELD(FLASH_CMD_CTRL_REG, NUM_DUMMY_CYCLES_FLD, 7, 5) | |
242 | + FIELD(FLASH_CMD_CTRL_REG, FLASH_CMD_CTRL_RESV1_FLD, 3, 4) | |
243 | + FIELD(FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD, 2, 1) | |
244 | + FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_STATUS_FLD, 1, 1) | |
245 | + FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0, 1) | |
246 | +REG32(FLASH_CMD_ADDR_REG, 0x94) | |
247 | +REG32(FLASH_RD_DATA_LOWER_REG, 0xa0) | |
248 | +REG32(FLASH_RD_DATA_UPPER_REG, 0xa4) | |
249 | +REG32(FLASH_WR_DATA_LOWER_REG, 0xa8) | |
250 | +REG32(FLASH_WR_DATA_UPPER_REG, 0xac) | |
251 | +REG32(POLLING_FLASH_STATUS_REG, 0xb0) | |
252 | + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD2, 21, 11) | |
253 | + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_NB_DUMMY, 16, 5) | |
254 | + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD1, 9, 7) | |
255 | + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_VALID_FLD, 8, 1) | |
256 | + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_FLD, 0, 8) | |
257 | +REG32(PHY_CONFIGURATION_REG, 0xb4) | |
258 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESYNC_FLD, 31, 1) | |
259 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESET_FLD, 30, 1) | |
260 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_BYPASS_FLD, 29, 1) | |
261 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV2_FLD, 23, 6) | |
262 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_TX_DLL_DELAY_FLD, 16, 7) | |
263 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV1_FLD, 7, 9) | |
264 | + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_DELAY_FLD, 0, 7) | |
265 | +REG32(PHY_MASTER_CONTROL_REG, 0xb8) | |
266 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV3_FLD, 25, 7) | |
267 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_LOCK_MODE_FLD, 24, 1) | |
268 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_BYPASS_MODE_FLD, 23, 1) | |
269 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_PHASE_DETECT_SELECTOR_FLD, 20, 3) | |
270 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV2_FLD, 19, 1) | |
271 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_NB_INDICATIONS_FLD, 16, 3) | |
272 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV1_FLD, 7, 9) | |
273 | + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_INITIAL_DELAY_FLD, 0, 7) | |
274 | +REG32(DLL_OBSERVABLE_LOWER_REG, 0xbc) | |
275 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
276 | + DLL_OBSERVABLE_LOWER_DLL_LOCK_INC_FLD, 24, 8) | |
277 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
278 | + DLL_OBSERVABLE_LOWER_DLL_LOCK_DEC_FLD, 16, 8) | |
279 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
280 | + DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 15, 1) | |
281 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
282 | + DLL_OBSERVABLE_LOWER_LOCK_VALUE_FLD, 8, 7) | |
283 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
284 | + DLL_OBSERVABLE_LOWER_UNLOCK_COUNTER_FLD, 3, 5) | |
285 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
286 | + DLL_OBSERVABLE_LOWER_LOCK_MODE_FLD, 1, 2) | |
287 | + FIELD(DLL_OBSERVABLE_LOWER_REG, | |
288 | + DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 0, 1) | |
289 | +REG32(DLL_OBSERVABLE_UPPER_REG, 0xc0) | |
290 | + FIELD(DLL_OBSERVABLE_UPPER_REG, | |
291 | + DLL_OBSERVABLE_UPPER_RESV2_FLD, 23, 9) | |
292 | + FIELD(DLL_OBSERVABLE_UPPER_REG, | |
293 | + DLL_OBSERVABLE_UPPER_TX_DECODER_OUTPUT_FLD, 16, 7) | |
294 | + FIELD(DLL_OBSERVABLE_UPPER_REG, | |
295 | + DLL_OBSERVABLE_UPPER_RESV1_FLD, 7, 9) | |
296 | + FIELD(DLL_OBSERVABLE_UPPER_REG, | |
297 | + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD, 0, 7) | |
298 | +REG32(OPCODE_EXT_LOWER_REG, 0xe0) | |
299 | + FIELD(OPCODE_EXT_LOWER_REG, EXT_READ_OPCODE_FLD, 24, 8) | |
300 | + FIELD(OPCODE_EXT_LOWER_REG, EXT_WRITE_OPCODE_FLD, 16, 8) | |
301 | + FIELD(OPCODE_EXT_LOWER_REG, EXT_POLL_OPCODE_FLD, 8, 8) | |
302 | + FIELD(OPCODE_EXT_LOWER_REG, EXT_STIG_OPCODE_FLD, 0, 8) | |
303 | +REG32(OPCODE_EXT_UPPER_REG, 0xe4) | |
304 | + FIELD(OPCODE_EXT_UPPER_REG, WEL_OPCODE_FLD, 24, 8) | |
305 | + FIELD(OPCODE_EXT_UPPER_REG, EXT_WEL_OPCODE_FLD, 16, 8) | |
306 | + FIELD(OPCODE_EXT_UPPER_REG, OPCODE_EXT_UPPER_RESV1_FLD, 0, 16) | |
307 | +REG32(MODULE_ID_REG, 0xfc) | |
308 | + FIELD(MODULE_ID_REG, FIX_PATCH_FLD, 24, 8) | |
309 | + FIELD(MODULE_ID_REG, MODULE_ID_FLD, 8, 16) | |
310 | + FIELD(MODULE_ID_REG, MODULE_ID_RESV_FLD, 2, 6) | |
311 | + FIELD(MODULE_ID_REG, CONF_FLD, 0, 2) | |
312 | + | |
313 | +#define RXFF_SZ 1024 | |
314 | +#define TXFF_SZ 1024 | |
315 | + | |
316 | +#define MAX_RX_DEC_OUT 8 | |
317 | + | |
318 | +#define SZ_512MBIT (512 * 1024 * 1024) | |
319 | +#define SZ_1GBIT (1024 * 1024 * 1024) | |
320 | +#define SZ_2GBIT (2ULL * SZ_1GBIT) | |
321 | +#define SZ_4GBIT (4ULL * SZ_1GBIT) | |
322 | + | |
323 | +#define IS_IND_DMA_START(op) (op->done_bytes == 0) | |
324 | +/* | |
325 | + * Bit field size of R_INDIRECT_WRITE_XFER_CTRL_REG_NUM_IND_OPS_DONE_FLD | |
326 | + * is 2 bits, which can record max of 3 indac operations. | |
327 | + */ | |
328 | +#define IND_OPS_DONE_MAX 3 | |
329 | + | |
330 | +typedef enum { | |
331 | + WREN = 0x6, | |
332 | +} FlashCMD; | |
333 | + | |
334 | +static unsigned int ospi_stig_addr_len(XlnxVersalOspi *s) | |
335 | +{ | |
336 | + /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */ | |
337 | + return ARRAY_FIELD_EX32(s->regs, | |
338 | + FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD) + 1; | |
339 | +} | |
340 | + | |
341 | +static unsigned int ospi_stig_wr_data_len(XlnxVersalOspi *s) | |
342 | +{ | |
343 | + /* Num write data bytes is NUM_WR_DATA_BYTES_FLD + 1 */ | |
344 | + return ARRAY_FIELD_EX32(s->regs, | |
345 | + FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD) + 1; | |
346 | +} | |
347 | + | |
348 | +static unsigned int ospi_stig_rd_data_len(XlnxVersalOspi *s) | |
349 | +{ | |
350 | + /* Num read data bytes is NUM_RD_DATA_BYTES_FLD + 1 */ | |
351 | + return ARRAY_FIELD_EX32(s->regs, | |
352 | + FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD) + 1; | |
353 | +} | |
354 | + | |
355 | +/* | |
356 | + * Status bits in R_IRQ_STATUS_REG are set when the event occurs and the | |
357 | + * interrupt is enabled in the mask register ([1] Section 2.3.17) | |
358 | + */ | |
359 | +static void set_irq(XlnxVersalOspi *s, uint32_t set_mask) | |
360 | +{ | |
361 | + s->regs[R_IRQ_STATUS_REG] |= s->regs[R_IRQ_MASK_REG] & set_mask; | |
362 | +} | |
363 | + | |
364 | +static void ospi_update_irq_line(XlnxVersalOspi *s) | |
365 | +{ | |
366 | + qemu_set_irq(s->irq, !!(s->regs[R_IRQ_STATUS_REG] & | |
367 | + s->regs[R_IRQ_MASK_REG])); | |
368 | +} | |
369 | + | |
370 | +static uint8_t ospi_get_wr_opcode(XlnxVersalOspi *s) | |
371 | +{ | |
372 | + return ARRAY_FIELD_EX32(s->regs, | |
373 | + DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD); | |
374 | +} | |
375 | + | |
376 | +static uint8_t ospi_get_rd_opcode(XlnxVersalOspi *s) | |
377 | +{ | |
378 | + return ARRAY_FIELD_EX32(s->regs, | |
379 | + DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD); | |
380 | +} | |
381 | + | |
382 | +static uint32_t ospi_get_num_addr_bytes(XlnxVersalOspi *s) | |
383 | +{ | |
384 | + /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */ | |
385 | + return ARRAY_FIELD_EX32(s->regs, | |
386 | + DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD) + 1; | |
387 | +} | |
388 | + | |
389 | +static void ospi_stig_membank_req(XlnxVersalOspi *s) | |
390 | +{ | |
391 | + int idx = ARRAY_FIELD_EX32(s->regs, | |
392 | + FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD); | |
393 | + | |
394 | + ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, | |
395 | + MEM_BANK_READ_DATA_FLD, s->stig_membank[idx]); | |
396 | +} | |
397 | + | |
398 | +static int ospi_stig_membank_rd_bytes(XlnxVersalOspi *s) | |
399 | +{ | |
400 | + int rd_data_fld = ARRAY_FIELD_EX32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, | |
401 | + NB_OF_STIG_READ_BYTES_FLD); | |
402 | + static const int sizes[6] = { 16, 32, 64, 128, 256, 512 }; | |
403 | + return (rd_data_fld < 6) ? sizes[rd_data_fld] : 0; | |
404 | +} | |
405 | + | |
406 | +static uint32_t ospi_get_page_sz(XlnxVersalOspi *s) | |
407 | +{ | |
408 | + return ARRAY_FIELD_EX32(s->regs, | |
409 | + DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD); | |
410 | +} | |
411 | + | |
412 | +static bool ospi_ind_rd_watermark_enabled(XlnxVersalOspi *s) | |
413 | +{ | |
414 | + return s->regs[R_INDIRECT_READ_XFER_WATERMARK_REG]; | |
415 | +} | |
416 | + | |
417 | +static void ind_op_advance(IndOp *op, unsigned int len) | |
418 | +{ | |
419 | + op->done_bytes += len; | |
420 | + assert(op->done_bytes <= op->num_bytes); | |
421 | + if (op->done_bytes == op->num_bytes) { | |
422 | + op->completed = true; | |
423 | + } | |
424 | +} | |
425 | + | |
426 | +static uint32_t ind_op_next_byte(IndOp *op) | |
427 | +{ | |
428 | + return op->flash_addr + op->done_bytes; | |
429 | +} | |
430 | + | |
431 | +static uint32_t ind_op_end_byte(IndOp *op) | |
432 | +{ | |
433 | + return op->flash_addr + op->num_bytes; | |
434 | +} | |
435 | + | |
436 | +static void ospi_ind_op_next(IndOp *op) | |
437 | +{ | |
438 | + op[0] = op[1]; | |
439 | + op[1].completed = true; | |
440 | +} | |
441 | + | |
442 | +static void ind_op_setup(IndOp *op, uint32_t flash_addr, uint32_t num_bytes) | |
443 | +{ | |
444 | + if (num_bytes & 0x3) { | |
445 | + qemu_log_mask(LOG_GUEST_ERROR, | |
446 | + "OSPI indirect op num bytes not word aligned\n"); | |
447 | + } | |
448 | + op->flash_addr = flash_addr; | |
449 | + op->num_bytes = num_bytes; | |
450 | + op->done_bytes = 0; | |
451 | + op->completed = false; | |
452 | +} | |
453 | + | |
454 | +static bool ospi_ind_op_completed(IndOp *op) | |
455 | +{ | |
456 | + return op->completed; | |
457 | +} | |
458 | + | |
459 | +static bool ospi_ind_op_all_completed(XlnxVersalOspi *s) | |
460 | +{ | |
461 | + return s->rd_ind_op[0].completed && s->wr_ind_op[0].completed; | |
462 | +} | |
463 | + | |
464 | +static void ospi_ind_op_cancel(IndOp *op) | |
465 | +{ | |
466 | + op[0].completed = true; | |
467 | + op[1].completed = true; | |
468 | +} | |
469 | + | |
470 | +static bool ospi_ind_op_add(IndOp *op, Fifo8 *fifo, | |
471 | + uint32_t flash_addr, uint32_t num_bytes) | |
472 | +{ | |
473 | + /* Check if first indirect op has been completed */ | |
474 | + if (op->completed) { | |
475 | + fifo8_reset(fifo); | |
476 | + ind_op_setup(op, flash_addr, num_bytes); | |
477 | + return false; | |
478 | + } | |
479 | + | |
480 | + /* Check if second indirect op has been completed */ | |
481 | + op++; | |
482 | + if (op->completed) { | |
483 | + ind_op_setup(op, flash_addr, num_bytes); | |
484 | + return false; | |
485 | + } | |
486 | + return true; | |
487 | +} | |
488 | + | |
489 | +static void ospi_ind_op_queue_up_rd(XlnxVersalOspi *s) | |
490 | +{ | |
491 | + uint32_t num_bytes = s->regs[R_INDIRECT_READ_XFER_NUM_BYTES_REG]; | |
492 | + uint32_t flash_addr = s->regs[R_INDIRECT_READ_XFER_START_REG]; | |
493 | + bool failed; | |
494 | + | |
495 | + failed = ospi_ind_op_add(s->rd_ind_op, &s->rx_sram, flash_addr, num_bytes); | |
496 | + /* If two already queued set rd reject interrupt */ | |
497 | + if (failed) { | |
498 | + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK); | |
499 | + } | |
500 | +} | |
501 | + | |
502 | +static void ospi_ind_op_queue_up_wr(XlnxVersalOspi *s) | |
503 | +{ | |
504 | + uint32_t num_bytes = s->regs[R_INDIRECT_WRITE_XFER_NUM_BYTES_REG]; | |
505 | + uint32_t flash_addr = s->regs[R_INDIRECT_WRITE_XFER_START_REG]; | |
506 | + bool failed; | |
507 | + | |
508 | + failed = ospi_ind_op_add(s->wr_ind_op, &s->tx_sram, flash_addr, num_bytes); | |
509 | + /* If two already queued set rd reject interrupt */ | |
510 | + if (failed) { | |
511 | + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK); | |
512 | + } | |
513 | +} | |
514 | + | |
515 | +static uint64_t flash_sz(XlnxVersalOspi *s, unsigned int cs) | |
516 | +{ | |
517 | + /* Flash sizes in MB */ | |
518 | + static const uint64_t sizes[4] = { SZ_512MBIT / 8, SZ_1GBIT / 8, | |
519 | + SZ_2GBIT / 8, SZ_4GBIT / 8 }; | |
520 | + uint32_t v = s->regs[R_DEV_SIZE_CONFIG_REG]; | |
521 | + | |
522 | + v >>= cs * R_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS0_FLD_LENGTH; | |
523 | + return sizes[FIELD_EX32(v, DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD)]; | |
524 | +} | |
525 | + | |
526 | +static unsigned int ospi_get_block_sz(XlnxVersalOspi *s) | |
527 | +{ | |
528 | + unsigned int block_fld = ARRAY_FIELD_EX32(s->regs, | |
529 | + DEV_SIZE_CONFIG_REG, | |
530 | + BYTES_PER_SUBSECTOR_FLD); | |
531 | + return 1 << block_fld; | |
532 | +} | |
533 | + | |
534 | +static unsigned int flash_blocks(XlnxVersalOspi *s, unsigned int cs) | |
535 | +{ | |
536 | + unsigned int b_sz = ospi_get_block_sz(s); | |
537 | + unsigned int f_sz = flash_sz(s, cs); | |
538 | + | |
539 | + return f_sz / b_sz; | |
540 | +} | |
541 | + | |
542 | +static int ospi_ahb_decoder_cs(XlnxVersalOspi *s, hwaddr addr) | |
543 | +{ | |
544 | + uint64_t end_addr = 0; | |
545 | + int cs; | |
546 | + | |
547 | + for (cs = 0; cs < s->num_cs; cs++) { | |
548 | + end_addr += flash_sz(s, cs); | |
549 | + if (addr < end_addr) { | |
550 | + break; | |
551 | + } | |
552 | + } | |
553 | + | |
554 | + if (cs == s->num_cs) { | |
555 | + /* Address is out of range */ | |
556 | + qemu_log_mask(LOG_GUEST_ERROR, | |
557 | + "OSPI flash address does not fit in configuration\n"); | |
558 | + return -1; | |
559 | + } | |
560 | + return cs; | |
561 | +} | |
562 | + | |
563 | +static void ospi_ahb_decoder_enable_cs(XlnxVersalOspi *s, hwaddr addr) | |
564 | +{ | |
565 | + int cs = ospi_ahb_decoder_cs(s, addr); | |
566 | + | |
567 | + if (cs >= 0) { | |
568 | + for (int i = 0; i < s->num_cs; i++) { | |
569 | + qemu_set_irq(s->cs_lines[i], cs != i); | |
570 | + } | |
571 | + } | |
572 | +} | |
573 | + | |
574 | +static unsigned int single_cs(XlnxVersalOspi *s) | |
575 | +{ | |
576 | + unsigned int field = ARRAY_FIELD_EX32(s->regs, | |
577 | + CONFIG_REG, PERIPH_CS_LINES_FLD); | |
578 | + | |
579 | + /* | |
580 | + * Below one liner is a trick that finds the rightmost zero and makes sure | |
581 | + * all other bits are turned to 1. It is a variant of the 'Isolate the | |
582 | + * rightmost 0-bit' trick found below at the time of writing: | |
583 | + * | |
584 | + * https://emre.me/computer-science/bit-manipulation-tricks/ | |
585 | + * | |
586 | + * 4'bXXX0 -> 4'b1110 | |
587 | + * 4'bXX01 -> 4'b1101 | |
588 | + * 4'bX011 -> 4'b1011 | |
589 | + * 4'b0111 -> 4'b0111 | |
590 | + * 4'b1111 -> 4'b1111 | |
591 | + */ | |
592 | + return (field | ~(field + 1)) & 0xf; | |
593 | +} | |
594 | + | |
595 | +static void ospi_update_cs_lines(XlnxVersalOspi *s) | |
596 | +{ | |
597 | + unsigned int all_cs; | |
598 | + int i; | |
599 | + | |
600 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_SEL_DEC_FLD)) { | |
601 | + all_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_CS_LINES_FLD); | |
602 | + } else { | |
603 | + all_cs = single_cs(s); | |
604 | + } | |
605 | + | |
606 | + for (i = 0; i < s->num_cs; i++) { | |
607 | + bool cs = (all_cs >> i) & 1; | |
608 | + | |
609 | + qemu_set_irq(s->cs_lines[i], cs); | |
610 | + } | |
611 | +} | |
612 | + | |
613 | +static void ospi_dac_cs(XlnxVersalOspi *s, hwaddr addr) | |
614 | +{ | |
615 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENABLE_AHB_DECODER_FLD)) { | |
616 | + ospi_ahb_decoder_enable_cs(s, addr); | |
617 | + } else { | |
618 | + ospi_update_cs_lines(s); | |
619 | + } | |
620 | +} | |
621 | + | |
622 | +static void ospi_disable_cs(XlnxVersalOspi *s) | |
623 | +{ | |
624 | + int i; | |
625 | + | |
626 | + for (i = 0; i < s->num_cs; i++) { | |
627 | + qemu_set_irq(s->cs_lines[i], 1); | |
628 | + } | |
629 | +} | |
630 | + | |
631 | +static void ospi_flush_txfifo(XlnxVersalOspi *s) | |
632 | +{ | |
633 | + while (!fifo8_is_empty(&s->tx_fifo)) { | |
634 | + uint32_t tx_rx = fifo8_pop(&s->tx_fifo); | |
635 | + | |
636 | + tx_rx = ssi_transfer(s->spi, tx_rx); | |
637 | + fifo8_push(&s->rx_fifo, tx_rx); | |
638 | + } | |
639 | +} | |
640 | + | |
641 | +static void ospi_tx_fifo_push_address_raw(XlnxVersalOspi *s, | |
642 | + uint32_t flash_addr, | |
643 | + unsigned int addr_bytes) | |
644 | +{ | |
645 | + /* Push write address */ | |
646 | + if (addr_bytes == 4) { | |
647 | + fifo8_push(&s->tx_fifo, flash_addr >> 24); | |
648 | + } | |
649 | + if (addr_bytes >= 3) { | |
650 | + fifo8_push(&s->tx_fifo, flash_addr >> 16); | |
651 | + } | |
652 | + if (addr_bytes >= 2) { | |
653 | + fifo8_push(&s->tx_fifo, flash_addr >> 8); | |
654 | + } | |
655 | + fifo8_push(&s->tx_fifo, flash_addr); | |
656 | +} | |
657 | + | |
658 | +static void ospi_tx_fifo_push_address(XlnxVersalOspi *s, uint32_t flash_addr) | |
659 | +{ | |
660 | + /* Push write address */ | |
661 | + int addr_bytes = ospi_get_num_addr_bytes(s); | |
662 | + | |
663 | + ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes); | |
664 | +} | |
665 | + | |
666 | +static void ospi_tx_fifo_push_stig_addr(XlnxVersalOspi *s) | |
667 | +{ | |
668 | + uint32_t flash_addr = s->regs[R_FLASH_CMD_ADDR_REG]; | |
669 | + unsigned int addr_bytes = ospi_stig_addr_len(s); | |
670 | + | |
671 | + ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes); | |
672 | +} | |
673 | + | |
674 | +static void ospi_tx_fifo_push_rd_op_addr(XlnxVersalOspi *s, uint32_t flash_addr) | |
675 | +{ | |
676 | + uint8_t inst_code = ospi_get_rd_opcode(s); | |
677 | + | |
678 | + fifo8_reset(&s->tx_fifo); | |
679 | + | |
680 | + /* Push read opcode */ | |
681 | + fifo8_push(&s->tx_fifo, inst_code); | |
682 | + | |
683 | + /* Push read address */ | |
684 | + ospi_tx_fifo_push_address(s, flash_addr); | |
685 | +} | |
686 | + | |
687 | +static void ospi_tx_fifo_push_stig_wr_data(XlnxVersalOspi *s) | |
688 | +{ | |
689 | + uint64_t data = s->regs[R_FLASH_WR_DATA_LOWER_REG]; | |
690 | + int wr_data_len = ospi_stig_wr_data_len(s); | |
691 | + int i; | |
692 | + | |
693 | + data |= (uint64_t) s->regs[R_FLASH_WR_DATA_UPPER_REG] << 32; | |
694 | + for (i = 0; i < wr_data_len; i++) { | |
695 | + int shift = i * 8; | |
696 | + fifo8_push(&s->tx_fifo, data >> shift); | |
697 | + } | |
698 | +} | |
699 | + | |
700 | +static void ospi_tx_fifo_push_stig_rd_data(XlnxVersalOspi *s) | |
701 | +{ | |
702 | + int rd_data_len; | |
703 | + int i; | |
704 | + | |
705 | + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) { | |
706 | + rd_data_len = ospi_stig_membank_rd_bytes(s); | |
707 | + } else { | |
708 | + rd_data_len = ospi_stig_rd_data_len(s); | |
709 | + } | |
710 | + | |
711 | + /* transmit second part (data) */ | |
712 | + for (i = 0; i < rd_data_len; ++i) { | |
713 | + fifo8_push(&s->tx_fifo, 0); | |
714 | + } | |
715 | +} | |
716 | + | |
717 | +static void ospi_rx_fifo_pop_stig_rd_data(XlnxVersalOspi *s) | |
718 | +{ | |
719 | + int size = ospi_stig_rd_data_len(s); | |
720 | + uint8_t bytes[8] = {}; | |
721 | + int i; | |
722 | + | |
723 | + size = MIN(fifo8_num_used(&s->rx_fifo), size); | |
724 | + | |
725 | + assert(size <= 8); | |
726 | + | |
727 | + for (i = 0; i < size; i++) { | |
728 | + bytes[i] = fifo8_pop(&s->rx_fifo); | |
729 | + } | |
730 | + | |
731 | + s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(bytes); | |
732 | + s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(bytes + 4); | |
733 | +} | |
734 | + | |
735 | +static void ospi_ind_read(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len) | |
736 | +{ | |
737 | + int i; | |
738 | + | |
739 | + /* Create first section of read cmd */ | |
740 | + ospi_tx_fifo_push_rd_op_addr(s, flash_addr); | |
741 | + | |
742 | + /* transmit first part */ | |
743 | + ospi_update_cs_lines(s); | |
744 | + ospi_flush_txfifo(s); | |
745 | + | |
746 | + fifo8_reset(&s->rx_fifo); | |
747 | + | |
748 | + /* transmit second part (data) */ | |
749 | + for (i = 0; i < len; ++i) { | |
750 | + fifo8_push(&s->tx_fifo, 0); | |
751 | + } | |
752 | + ospi_flush_txfifo(s); | |
753 | + | |
754 | + for (i = 0; i < len; ++i) { | |
755 | + fifo8_push(&s->rx_sram, fifo8_pop(&s->rx_fifo)); | |
756 | + } | |
757 | + | |
758 | + /* done */ | |
759 | + ospi_disable_cs(s); | |
760 | +} | |
761 | + | |
762 | +static unsigned int ospi_dma_burst_size(XlnxVersalOspi *s) | |
763 | +{ | |
764 | + return 1 << ARRAY_FIELD_EX32(s->regs, | |
765 | + DMA_PERIPH_CONFIG_REG, | |
766 | + NUM_BURST_REQ_BYTES_FLD); | |
767 | +} | |
768 | + | |
769 | +static unsigned int ospi_dma_single_size(XlnxVersalOspi *s) | |
770 | +{ | |
771 | + return 1 << ARRAY_FIELD_EX32(s->regs, | |
772 | + DMA_PERIPH_CONFIG_REG, | |
773 | + NUM_SINGLE_REQ_BYTES_FLD); | |
774 | +} | |
775 | + | |
776 | +static void ind_rd_inc_num_done(XlnxVersalOspi *s) | |
777 | +{ | |
778 | + unsigned int done = ARRAY_FIELD_EX32(s->regs, | |
779 | + INDIRECT_READ_XFER_CTRL_REG, | |
780 | + NUM_IND_OPS_DONE_FLD); | |
781 | + if (done < IND_OPS_DONE_MAX) { | |
782 | + done++; | |
783 | + } | |
784 | + done &= 0x3; | |
785 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, | |
786 | + NUM_IND_OPS_DONE_FLD, done); | |
787 | +} | |
788 | + | |
789 | +static void ospi_ind_rd_completed(XlnxVersalOspi *s) | |
790 | +{ | |
791 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, | |
792 | + IND_OPS_DONE_STATUS_FLD, 1); | |
793 | + | |
794 | + ind_rd_inc_num_done(s); | |
795 | + ospi_ind_op_next(s->rd_ind_op); | |
796 | + if (ospi_ind_op_all_completed(s)) { | |
797 | + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK); | |
798 | + } | |
799 | +} | |
800 | + | |
801 | +static void ospi_dma_read(XlnxVersalOspi *s) | |
802 | +{ | |
803 | + IndOp *op = s->rd_ind_op; | |
804 | + uint32_t dma_len = op->num_bytes; | |
805 | + uint32_t burst_sz = ospi_dma_burst_size(s); | |
806 | + uint32_t single_sz = ospi_dma_single_size(s); | |
807 | + uint32_t ind_trig_range; | |
808 | + uint32_t remainder; | |
809 | + XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_GET_CLASS(s->dma_src); | |
810 | + | |
811 | + ind_trig_range = (1 << ARRAY_FIELD_EX32(s->regs, | |
812 | + INDIRECT_TRIGGER_ADDR_RANGE_REG, | |
813 | + IND_RANGE_WIDTH_FLD)); | |
814 | + remainder = dma_len % burst_sz; | |
815 | + remainder = remainder % single_sz; | |
816 | + if (burst_sz > ind_trig_range || single_sz > ind_trig_range || | |
817 | + remainder != 0) { | |
818 | + qemu_log_mask(LOG_GUEST_ERROR, | |
819 | + "OSPI DMA burst size / single size config error\n"); | |
820 | + } | |
821 | + | |
822 | + s->src_dma_inprog = true; | |
823 | + if (xcdc->read(s->dma_src, 0, dma_len) != MEMTX_OK) { | |
824 | + qemu_log_mask(LOG_GUEST_ERROR, "OSPI DMA configuration error\n"); | |
825 | + } | |
826 | + s->src_dma_inprog = false; | |
827 | +} | |
828 | + | |
829 | +static void ospi_do_ind_read(XlnxVersalOspi *s) | |
830 | +{ | |
831 | + IndOp *op = s->rd_ind_op; | |
832 | + uint32_t next_b; | |
833 | + uint32_t end_b; | |
834 | + uint32_t len; | |
835 | + bool start_dma = IS_IND_DMA_START(op) && !s->src_dma_inprog; | |
836 | + | |
837 | + /* Continue to read flash until we run out of space in sram */ | |
838 | + while (!ospi_ind_op_completed(op) && | |
839 | + !fifo8_is_full(&s->rx_sram)) { | |
840 | + /* Read reqested number of bytes, max bytes limited to size of sram */ | |
841 | + next_b = ind_op_next_byte(op); | |
842 | + end_b = next_b + fifo8_num_free(&s->rx_sram); | |
843 | + end_b = MIN(end_b, ind_op_end_byte(op)); | |
844 | + | |
845 | + len = end_b - next_b; | |
846 | + ospi_ind_read(s, next_b, len); | |
847 | + ind_op_advance(op, len); | |
848 | + | |
849 | + if (ospi_ind_rd_watermark_enabled(s)) { | |
850 | + ARRAY_FIELD_DP32(s->regs, IRQ_STATUS_REG, | |
851 | + INDIRECT_XFER_LEVEL_BREACH_FLD, 1); | |
852 | + set_irq(s, | |
853 | + R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK); | |
854 | + } | |
855 | + | |
856 | + if (!s->src_dma_inprog && | |
857 | + ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) { | |
858 | + ospi_dma_read(s); | |
859 | + } | |
860 | + } | |
861 | + | |
862 | + /* Set sram full */ | |
863 | + if (fifo8_num_used(&s->rx_sram) == RXFF_SZ) { | |
864 | + ARRAY_FIELD_DP32(s->regs, | |
865 | + INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 1); | |
866 | + set_irq(s, R_IRQ_STATUS_REG_INDRD_SRAM_FULL_FLD_MASK); | |
867 | + } | |
868 | + | |
869 | + /* Signal completion if done, unless inside recursion via ospi_dma_read */ | |
870 | + if (!ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD) || start_dma) { | |
871 | + if (ospi_ind_op_completed(op)) { | |
872 | + ospi_ind_rd_completed(s); | |
873 | + } | |
874 | + } | |
875 | +} | |
876 | + | |
877 | +/* Transmit write enable instruction */ | |
878 | +static void ospi_transmit_wel(XlnxVersalOspi *s, bool ahb_decoder_cs, | |
879 | + hwaddr addr) | |
880 | +{ | |
881 | + fifo8_reset(&s->tx_fifo); | |
882 | + fifo8_push(&s->tx_fifo, WREN); | |
883 | + | |
884 | + if (ahb_decoder_cs) { | |
885 | + ospi_ahb_decoder_enable_cs(s, addr); | |
886 | + } else { | |
887 | + ospi_update_cs_lines(s); | |
888 | + } | |
889 | + | |
890 | + ospi_flush_txfifo(s); | |
891 | + ospi_disable_cs(s); | |
892 | + | |
893 | + fifo8_reset(&s->rx_fifo); | |
894 | +} | |
895 | + | |
896 | +static void ospi_ind_write(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len) | |
897 | +{ | |
898 | + bool ahb_decoder_cs = false; | |
899 | + uint8_t inst_code; | |
900 | + int i; | |
901 | + | |
902 | + assert(fifo8_num_used(&s->tx_sram) >= len); | |
903 | + | |
904 | + if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) { | |
905 | + ospi_transmit_wel(s, ahb_decoder_cs, 0); | |
906 | + } | |
907 | + | |
908 | + /* reset fifos */ | |
909 | + fifo8_reset(&s->tx_fifo); | |
910 | + fifo8_reset(&s->rx_fifo); | |
911 | + | |
912 | + /* Push write opcode */ | |
913 | + inst_code = ospi_get_wr_opcode(s); | |
914 | + fifo8_push(&s->tx_fifo, inst_code); | |
915 | + | |
916 | + /* Push write address */ | |
917 | + ospi_tx_fifo_push_address(s, flash_addr); | |
918 | + | |
919 | + /* data */ | |
920 | + for (i = 0; i < len; i++) { | |
921 | + fifo8_push(&s->tx_fifo, fifo8_pop(&s->tx_sram)); | |
922 | + } | |
923 | + | |
924 | + /* transmit */ | |
925 | + ospi_update_cs_lines(s); | |
926 | + ospi_flush_txfifo(s); | |
927 | + | |
928 | + /* done */ | |
929 | + ospi_disable_cs(s); | |
930 | + fifo8_reset(&s->rx_fifo); | |
931 | +} | |
932 | + | |
933 | +static void ind_wr_inc_num_done(XlnxVersalOspi *s) | |
934 | +{ | |
935 | + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, | |
936 | + NUM_IND_OPS_DONE_FLD); | |
937 | + if (done < IND_OPS_DONE_MAX) { | |
938 | + done++; | |
939 | + } | |
940 | + done &= 0x3; | |
941 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, | |
942 | + NUM_IND_OPS_DONE_FLD, done); | |
943 | +} | |
944 | + | |
945 | +static void ospi_ind_wr_completed(XlnxVersalOspi *s) | |
946 | +{ | |
947 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, | |
948 | + IND_OPS_DONE_STATUS_FLD, 1); | |
949 | + ind_wr_inc_num_done(s); | |
950 | + ospi_ind_op_next(s->wr_ind_op); | |
951 | + /* Set indirect op done interrupt if enabled */ | |
952 | + if (ospi_ind_op_all_completed(s)) { | |
953 | + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK); | |
954 | + } | |
955 | +} | |
956 | + | |
957 | +static void ospi_do_indirect_write(XlnxVersalOspi *s) | |
958 | +{ | |
959 | + uint32_t write_watermark = s->regs[R_INDIRECT_WRITE_XFER_WATERMARK_REG]; | |
960 | + uint32_t pagesz = ospi_get_page_sz(s); | |
961 | + uint32_t page_mask = ~(pagesz - 1); | |
962 | + IndOp *op = s->wr_ind_op; | |
963 | + uint32_t next_b; | |
964 | + uint32_t end_b; | |
965 | + uint32_t len; | |
966 | + | |
967 | + /* Write out tx_fifo in maximum page sz chunks */ | |
968 | + while (!ospi_ind_op_completed(op) && fifo8_num_used(&s->tx_sram) > 0) { | |
969 | + next_b = ind_op_next_byte(op); | |
970 | + end_b = next_b + MIN(fifo8_num_used(&s->tx_sram), pagesz); | |
971 | + | |
972 | + /* Dont cross page boundary */ | |
973 | + if ((end_b & page_mask) > next_b) { | |
974 | + end_b &= page_mask; | |
975 | + } | |
976 | + | |
977 | + len = end_b - next_b; | |
978 | + len = MIN(len, op->num_bytes - op->done_bytes); | |
979 | + ospi_ind_write(s, next_b, len); | |
980 | + ind_op_advance(op, len); | |
981 | + } | |
982 | + | |
983 | + /* | |
984 | + * Always set indirect transfer level breached interrupt if enabled | |
985 | + * (write watermark > 0) since the tx_sram always will be emptied | |
986 | + */ | |
987 | + if (write_watermark > 0) { | |
988 | + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK); | |
989 | + } | |
990 | + | |
991 | + /* Signal completions if done */ | |
992 | + if (ospi_ind_op_completed(op)) { | |
993 | + ospi_ind_wr_completed(s); | |
994 | + } | |
995 | +} | |
996 | + | |
997 | +static void ospi_stig_fill_membank(XlnxVersalOspi *s) | |
998 | +{ | |
999 | + int num_rd_bytes = ospi_stig_membank_rd_bytes(s); | |
1000 | + int idx = num_rd_bytes - 8; /* first of last 8 */ | |
1001 | + int i; | |
1002 | + | |
1003 | + for (i = 0; i < num_rd_bytes; i++) { | |
1004 | + s->stig_membank[i] = fifo8_pop(&s->rx_fifo); | |
1005 | + } | |
1006 | + | |
1007 | + g_assert((idx + 4) < ARRAY_SIZE(s->stig_membank)); | |
1008 | + | |
1009 | + /* Fill in lower upper regs */ | |
1010 | + s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(&s->stig_membank[idx]); | |
1011 | + s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(&s->stig_membank[idx + 4]); | |
1012 | +} | |
1013 | + | |
1014 | +static void ospi_stig_cmd_exec(XlnxVersalOspi *s) | |
1015 | +{ | |
1016 | + uint8_t inst_code; | |
1017 | + | |
1018 | + /* Reset fifos */ | |
1019 | + fifo8_reset(&s->tx_fifo); | |
1020 | + fifo8_reset(&s->rx_fifo); | |
1021 | + | |
1022 | + /* Push write opcode */ | |
1023 | + inst_code = ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD); | |
1024 | + fifo8_push(&s->tx_fifo, inst_code); | |
1025 | + | |
1026 | + /* Push address if enabled */ | |
1027 | + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD)) { | |
1028 | + ospi_tx_fifo_push_stig_addr(s); | |
1029 | + } | |
1030 | + | |
1031 | + /* Enable cs */ | |
1032 | + ospi_update_cs_lines(s); | |
1033 | + | |
1034 | + /* Data */ | |
1035 | + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD)) { | |
1036 | + ospi_tx_fifo_push_stig_wr_data(s); | |
1037 | + } else if (ARRAY_FIELD_EX32(s->regs, | |
1038 | + FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) { | |
1039 | + /* transmit first part */ | |
1040 | + ospi_flush_txfifo(s); | |
1041 | + fifo8_reset(&s->rx_fifo); | |
1042 | + ospi_tx_fifo_push_stig_rd_data(s); | |
1043 | + } | |
1044 | + | |
1045 | + /* Transmit */ | |
1046 | + ospi_flush_txfifo(s); | |
1047 | + ospi_disable_cs(s); | |
1048 | + | |
1049 | + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) { | |
1050 | + if (ARRAY_FIELD_EX32(s->regs, | |
1051 | + FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) { | |
1052 | + ospi_stig_fill_membank(s); | |
1053 | + } else { | |
1054 | + ospi_rx_fifo_pop_stig_rd_data(s); | |
1055 | + } | |
1056 | + } | |
1057 | +} | |
1058 | + | |
1059 | +static uint32_t ospi_block_address(XlnxVersalOspi *s, unsigned int block) | |
1060 | +{ | |
1061 | + unsigned int block_sz = ospi_get_block_sz(s); | |
1062 | + unsigned int cs = 0; | |
1063 | + uint32_t addr = 0; | |
1064 | + | |
1065 | + while (cs < s->num_cs && block >= flash_blocks(s, cs)) { | |
1066 | + block -= flash_blocks(s, 0); | |
1067 | + addr += flash_sz(s, cs); | |
1068 | + } | |
1069 | + addr += block * block_sz; | |
1070 | + return addr; | |
1071 | +} | |
1072 | + | |
1073 | +static uint32_t ospi_get_wr_prot_addr_low(XlnxVersalOspi *s) | |
1074 | +{ | |
1075 | + unsigned int block = s->regs[R_LOWER_WR_PROT_REG]; | |
1076 | + | |
1077 | + return ospi_block_address(s, block); | |
1078 | +} | |
1079 | + | |
1080 | +static uint32_t ospi_get_wr_prot_addr_upper(XlnxVersalOspi *s) | |
1081 | +{ | |
1082 | + unsigned int block = s->regs[R_UPPER_WR_PROT_REG]; | |
1083 | + | |
1084 | + /* Get address of first block out of defined range */ | |
1085 | + return ospi_block_address(s, block + 1); | |
1086 | +} | |
1087 | + | |
1088 | +static bool ospi_is_write_protected(XlnxVersalOspi *s, hwaddr addr) | |
1089 | +{ | |
1090 | + uint32_t wr_prot_addr_upper = ospi_get_wr_prot_addr_upper(s); | |
1091 | + uint32_t wr_prot_addr_low = ospi_get_wr_prot_addr_low(s); | |
1092 | + bool in_range = false; | |
1093 | + | |
1094 | + if (addr >= wr_prot_addr_low && addr < wr_prot_addr_upper) { | |
1095 | + in_range = true; | |
1096 | + } | |
1097 | + | |
1098 | + if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, INV_FLD)) { | |
1099 | + in_range = !in_range; | |
1100 | + } | |
1101 | + return in_range; | |
1102 | +} | |
1103 | + | |
1104 | +static uint64_t ospi_rx_sram_read(XlnxVersalOspi *s, unsigned int size) | |
1105 | +{ | |
1106 | + uint8_t bytes[8] = {}; | |
1107 | + int i; | |
1108 | + | |
1109 | + if (size < 4 && fifo8_num_used(&s->rx_sram) >= 4) { | |
1110 | + qemu_log_mask(LOG_GUEST_ERROR, | |
1111 | + "OSPI only last read of internal " | |
1112 | + "sram is allowed to be < 32 bits\n"); | |
1113 | + } | |
1114 | + | |
1115 | + size = MIN(fifo8_num_used(&s->rx_sram), size); | |
1116 | + | |
1117 | + assert(size <= 8); | |
1118 | + | |
1119 | + for (i = 0; i < size; i++) { | |
1120 | + bytes[i] = fifo8_pop(&s->rx_sram); | |
1121 | + } | |
1122 | + | |
1123 | + return ldq_le_p(bytes); | |
1124 | +} | |
1125 | + | |
1126 | +static void ospi_tx_sram_write(XlnxVersalOspi *s, uint64_t value, | |
1127 | + unsigned int size) | |
1128 | +{ | |
1129 | + int i; | |
1130 | + for (i = 0; i < size && !fifo8_is_full(&s->tx_sram); i++) { | |
1131 | + fifo8_push(&s->tx_sram, value >> 8 * i); | |
1132 | + } | |
1133 | +} | |
1134 | + | |
1135 | +static uint64_t ospi_do_dac_read(void *opaque, hwaddr addr, unsigned int size) | |
1136 | +{ | |
1137 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1138 | + uint8_t bytes[8] = {}; | |
1139 | + int i; | |
1140 | + | |
1141 | + /* Create first section of read cmd */ | |
1142 | + ospi_tx_fifo_push_rd_op_addr(s, (uint32_t) addr); | |
1143 | + | |
1144 | + /* Enable cs and transmit first part */ | |
1145 | + ospi_dac_cs(s, addr); | |
1146 | + ospi_flush_txfifo(s); | |
1147 | + | |
1148 | + fifo8_reset(&s->rx_fifo); | |
1149 | + | |
1150 | + /* transmit second part (data) */ | |
1151 | + for (i = 0; i < size; ++i) { | |
1152 | + fifo8_push(&s->tx_fifo, 0); | |
1153 | + } | |
1154 | + ospi_flush_txfifo(s); | |
1155 | + | |
1156 | + /* fill in result */ | |
1157 | + size = MIN(fifo8_num_used(&s->rx_fifo), size); | |
1158 | + | |
1159 | + assert(size <= 8); | |
1160 | + | |
1161 | + for (i = 0; i < size; i++) { | |
1162 | + bytes[i] = fifo8_pop(&s->rx_fifo); | |
1163 | + } | |
1164 | + | |
1165 | + /* done */ | |
1166 | + ospi_disable_cs(s); | |
1167 | + | |
1168 | + return ldq_le_p(bytes); | |
1169 | +} | |
1170 | + | |
1171 | +static void ospi_do_dac_write(void *opaque, | |
1172 | + hwaddr addr, | |
1173 | + uint64_t value, | |
1174 | + unsigned int size) | |
1175 | +{ | |
1176 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1177 | + bool ahb_decoder_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG, | |
1178 | + ENABLE_AHB_DECODER_FLD); | |
1179 | + uint8_t inst_code; | |
1180 | + unsigned int i; | |
1181 | + | |
1182 | + if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) { | |
1183 | + ospi_transmit_wel(s, ahb_decoder_cs, addr); | |
1184 | + } | |
1185 | + | |
1186 | + /* reset fifos */ | |
1187 | + fifo8_reset(&s->tx_fifo); | |
1188 | + fifo8_reset(&s->rx_fifo); | |
1189 | + | |
1190 | + /* Push write opcode */ | |
1191 | + inst_code = ospi_get_wr_opcode(s); | |
1192 | + fifo8_push(&s->tx_fifo, inst_code); | |
1193 | + | |
1194 | + /* Push write address */ | |
1195 | + ospi_tx_fifo_push_address(s, addr); | |
1196 | + | |
1197 | + /* data */ | |
1198 | + for (i = 0; i < size; i++) { | |
1199 | + fifo8_push(&s->tx_fifo, value >> 8 * i); | |
1200 | + } | |
1201 | + | |
1202 | + /* Enable cs and transmit */ | |
1203 | + ospi_dac_cs(s, addr); | |
1204 | + ospi_flush_txfifo(s); | |
1205 | + ospi_disable_cs(s); | |
1206 | + | |
1207 | + fifo8_reset(&s->rx_fifo); | |
1208 | +} | |
1209 | + | |
1210 | +static void flash_cmd_ctrl_mem_reg_post_write(RegisterInfo *reg, | |
1211 | + uint64_t val) | |
1212 | +{ | |
1213 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1214 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { | |
1215 | + if (ARRAY_FIELD_EX32(s->regs, | |
1216 | + FLASH_COMMAND_CTRL_MEM_REG, | |
1217 | + TRIGGER_MEM_BANK_REQ_FLD)) { | |
1218 | + ospi_stig_membank_req(s); | |
1219 | + ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, | |
1220 | + TRIGGER_MEM_BANK_REQ_FLD, 0); | |
1221 | + } | |
1222 | + } | |
1223 | +} | |
1224 | + | |
1225 | +static void flash_cmd_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) | |
1226 | +{ | |
1227 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1228 | + | |
1229 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD) && | |
1230 | + ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD)) { | |
1231 | + ospi_stig_cmd_exec(s); | |
1232 | + set_irq(s, R_IRQ_STATUS_REG_STIG_REQ_INT_FLD_MASK); | |
1233 | + ARRAY_FIELD_DP32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0); | |
1234 | + } | |
1235 | +} | |
1236 | + | |
1237 | +static uint64_t ind_wr_dec_num_done(XlnxVersalOspi *s, uint64_t val) | |
1238 | +{ | |
1239 | + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, | |
1240 | + NUM_IND_OPS_DONE_FLD); | |
1241 | + done--; | |
1242 | + done &= 0x3; | |
1243 | + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, | |
1244 | + NUM_IND_OPS_DONE_FLD, done); | |
1245 | + return val; | |
1246 | +} | |
1247 | + | |
1248 | +static bool ind_wr_clearing_op_done(XlnxVersalOspi *s, uint64_t new_val) | |
1249 | +{ | |
1250 | + bool set_in_reg = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, | |
1251 | + IND_OPS_DONE_STATUS_FLD); | |
1252 | + bool set_in_new_val = FIELD_EX32(new_val, INDIRECT_WRITE_XFER_CTRL_REG, | |
1253 | + IND_OPS_DONE_STATUS_FLD); | |
1254 | + /* return true if clearing bit */ | |
1255 | + return set_in_reg && !set_in_new_val; | |
1256 | +} | |
1257 | + | |
1258 | +static uint64_t ind_wr_xfer_ctrl_reg_pre_write(RegisterInfo *reg, | |
1259 | + uint64_t val) | |
1260 | +{ | |
1261 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1262 | + | |
1263 | + if (ind_wr_clearing_op_done(s, val)) { | |
1264 | + val = ind_wr_dec_num_done(s, val); | |
1265 | + } | |
1266 | + return val; | |
1267 | +} | |
1268 | + | |
1269 | +static void ind_wr_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) | |
1270 | +{ | |
1271 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1272 | + | |
1273 | + if (s->ind_write_disabled) { | |
1274 | + return; | |
1275 | + } | |
1276 | + | |
1277 | + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD)) { | |
1278 | + ospi_ind_op_queue_up_wr(s); | |
1279 | + ospi_do_indirect_write(s); | |
1280 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0); | |
1281 | + } | |
1282 | + | |
1283 | + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD)) { | |
1284 | + ospi_ind_op_cancel(s->wr_ind_op); | |
1285 | + fifo8_reset(&s->tx_sram); | |
1286 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 0); | |
1287 | + } | |
1288 | +} | |
1289 | + | |
1290 | +static uint64_t ind_wr_xfer_ctrl_reg_post_read(RegisterInfo *reg, | |
1291 | + uint64_t val) | |
1292 | +{ | |
1293 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1294 | + IndOp *op = s->wr_ind_op; | |
1295 | + | |
1296 | + /* Check if ind ops is ongoing */ | |
1297 | + if (!ospi_ind_op_completed(&op[0])) { | |
1298 | + /* Check if two ind ops are queued */ | |
1299 | + if (!ospi_ind_op_completed(&op[1])) { | |
1300 | + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, | |
1301 | + WR_QUEUED_FLD, 1); | |
1302 | + } | |
1303 | + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 1); | |
1304 | + } | |
1305 | + return val; | |
1306 | +} | |
1307 | + | |
1308 | +static uint64_t ind_rd_dec_num_done(XlnxVersalOspi *s, uint64_t val) | |
1309 | +{ | |
1310 | + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, | |
1311 | + NUM_IND_OPS_DONE_FLD); | |
1312 | + done--; | |
1313 | + done &= 0x3; | |
1314 | + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, | |
1315 | + NUM_IND_OPS_DONE_FLD, done); | |
1316 | + return val; | |
1317 | +} | |
1318 | + | |
1319 | +static uint64_t ind_rd_xfer_ctrl_reg_pre_write(RegisterInfo *reg, | |
1320 | + uint64_t val) | |
1321 | +{ | |
1322 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1323 | + | |
1324 | + if (FIELD_EX32(val, INDIRECT_READ_XFER_CTRL_REG, | |
1325 | + IND_OPS_DONE_STATUS_FLD)) { | |
1326 | + val = ind_rd_dec_num_done(s, val); | |
1327 | + val &= ~R_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_FLD_MASK; | |
1328 | + } | |
1329 | + return val; | |
1330 | +} | |
1331 | + | |
1332 | +static void ind_rd_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) | |
1333 | +{ | |
1334 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1335 | + | |
1336 | + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD)) { | |
1337 | + ospi_ind_op_queue_up_rd(s); | |
1338 | + ospi_do_ind_read(s); | |
1339 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0); | |
1340 | + } | |
1341 | + | |
1342 | + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD)) { | |
1343 | + ospi_ind_op_cancel(s->rd_ind_op); | |
1344 | + fifo8_reset(&s->rx_sram); | |
1345 | + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 0); | |
1346 | + } | |
1347 | +} | |
1348 | + | |
1349 | +static uint64_t ind_rd_xfer_ctrl_reg_post_read(RegisterInfo *reg, | |
1350 | + uint64_t val) | |
1351 | +{ | |
1352 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1353 | + IndOp *op = s->rd_ind_op; | |
1354 | + | |
1355 | + /* Check if ind ops is ongoing */ | |
1356 | + if (!ospi_ind_op_completed(&op[0])) { | |
1357 | + /* Check if two ind ops are queued */ | |
1358 | + if (!ospi_ind_op_completed(&op[1])) { | |
1359 | + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, | |
1360 | + RD_QUEUED_FLD, 1); | |
1361 | + } | |
1362 | + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 1); | |
1363 | + } | |
1364 | + return val; | |
1365 | +} | |
1366 | + | |
1367 | +static uint64_t sram_fill_reg_post_read(RegisterInfo *reg, uint64_t val) | |
1368 | +{ | |
1369 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1370 | + val = ((fifo8_num_used(&s->tx_sram) & 0xFFFF) << 16) | | |
1371 | + (fifo8_num_used(&s->rx_sram) & 0xFFFF); | |
1372 | + return val; | |
1373 | +} | |
1374 | + | |
1375 | +static uint64_t dll_obs_upper_reg_post_read(RegisterInfo *reg, uint64_t val) | |
1376 | +{ | |
1377 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); | |
1378 | + uint32_t rx_dec_out; | |
1379 | + | |
1380 | + rx_dec_out = FIELD_EX32(val, DLL_OBSERVABLE_UPPER_REG, | |
1381 | + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD); | |
1382 | + | |
1383 | + if (rx_dec_out < MAX_RX_DEC_OUT) { | |
1384 | + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_UPPER_REG, | |
1385 | + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD, | |
1386 | + rx_dec_out + 1); | |
1387 | + } | |
1388 | + | |
1389 | + return val; | |
1390 | +} | |
1391 | + | |
1392 | + | |
1393 | +static void xlnx_versal_ospi_reset(DeviceState *dev) | |
1394 | +{ | |
1395 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev); | |
1396 | + unsigned int i; | |
1397 | + | |
1398 | + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { | |
1399 | + register_reset(&s->regs_info[i]); | |
1400 | + } | |
1401 | + | |
1402 | + fifo8_reset(&s->rx_fifo); | |
1403 | + fifo8_reset(&s->tx_fifo); | |
1404 | + fifo8_reset(&s->rx_sram); | |
1405 | + fifo8_reset(&s->tx_sram); | |
1406 | + | |
1407 | + s->rd_ind_op[0].completed = true; | |
1408 | + s->rd_ind_op[1].completed = true; | |
1409 | + s->wr_ind_op[0].completed = true; | |
1410 | + s->wr_ind_op[1].completed = true; | |
1411 | + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG, | |
1412 | + DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 1); | |
1413 | + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG, | |
1414 | + DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 1); | |
1415 | +} | |
1416 | + | |
1417 | +static RegisterAccessInfo ospi_regs_info[] = { | |
1418 | + { .name = "CONFIG_REG", | |
1419 | + .addr = A_CONFIG_REG, | |
1420 | + .reset = 0x80780081, | |
1421 | + .ro = 0x9c000000, | |
1422 | + },{ .name = "DEV_INSTR_RD_CONFIG_REG", | |
1423 | + .addr = A_DEV_INSTR_RD_CONFIG_REG, | |
1424 | + .reset = 0x3, | |
1425 | + .ro = 0xe0ecc800, | |
1426 | + },{ .name = "DEV_INSTR_WR_CONFIG_REG", | |
1427 | + .addr = A_DEV_INSTR_WR_CONFIG_REG, | |
1428 | + .reset = 0x2, | |
1429 | + .ro = 0xe0fcce00, | |
1430 | + },{ .name = "DEV_DELAY_REG", | |
1431 | + .addr = A_DEV_DELAY_REG, | |
1432 | + },{ .name = "RD_DATA_CAPTURE_REG", | |
1433 | + .addr = A_RD_DATA_CAPTURE_REG, | |
1434 | + .reset = 0x1, | |
1435 | + .ro = 0xfff0fec0, | |
1436 | + },{ .name = "DEV_SIZE_CONFIG_REG", | |
1437 | + .addr = A_DEV_SIZE_CONFIG_REG, | |
1438 | + .reset = 0x101002, | |
1439 | + .ro = 0xe0000000, | |
1440 | + },{ .name = "SRAM_PARTITION_CFG_REG", | |
1441 | + .addr = A_SRAM_PARTITION_CFG_REG, | |
1442 | + .reset = 0x80, | |
1443 | + .ro = 0xffffff00, | |
1444 | + },{ .name = "IND_AHB_ADDR_TRIGGER_REG", | |
1445 | + .addr = A_IND_AHB_ADDR_TRIGGER_REG, | |
1446 | + },{ .name = "DMA_PERIPH_CONFIG_REG", | |
1447 | + .addr = A_DMA_PERIPH_CONFIG_REG, | |
1448 | + .ro = 0xfffff0f0, | |
1449 | + },{ .name = "REMAP_ADDR_REG", | |
1450 | + .addr = A_REMAP_ADDR_REG, | |
1451 | + },{ .name = "MODE_BIT_CONFIG_REG", | |
1452 | + .addr = A_MODE_BIT_CONFIG_REG, | |
1453 | + .reset = 0x200, | |
1454 | + .ro = 0xffff7800, | |
1455 | + },{ .name = "SRAM_FILL_REG", | |
1456 | + .addr = A_SRAM_FILL_REG, | |
1457 | + .ro = 0xffffffff, | |
1458 | + .post_read = sram_fill_reg_post_read, | |
1459 | + },{ .name = "TX_THRESH_REG", | |
1460 | + .addr = A_TX_THRESH_REG, | |
1461 | + .reset = 0x1, | |
1462 | + .ro = 0xffffffe0, | |
1463 | + },{ .name = "RX_THRESH_REG", | |
1464 | + .addr = A_RX_THRESH_REG, | |
1465 | + .reset = 0x1, | |
1466 | + .ro = 0xffffffe0, | |
1467 | + },{ .name = "WRITE_COMPLETION_CTRL_REG", | |
1468 | + .addr = A_WRITE_COMPLETION_CTRL_REG, | |
1469 | + .reset = 0x10005, | |
1470 | + .ro = 0x1800, | |
1471 | + },{ .name = "NO_OF_POLLS_BEF_EXP_REG", | |
1472 | + .addr = A_NO_OF_POLLS_BEF_EXP_REG, | |
1473 | + .reset = 0xffffffff, | |
1474 | + },{ .name = "IRQ_STATUS_REG", | |
1475 | + .addr = A_IRQ_STATUS_REG, | |
1476 | + .ro = 0xfff08000, | |
1477 | + .w1c = 0xf7fff, | |
1478 | + },{ .name = "IRQ_MASK_REG", | |
1479 | + .addr = A_IRQ_MASK_REG, | |
1480 | + .ro = 0xfff08000, | |
1481 | + },{ .name = "LOWER_WR_PROT_REG", | |
1482 | + .addr = A_LOWER_WR_PROT_REG, | |
1483 | + },{ .name = "UPPER_WR_PROT_REG", | |
1484 | + .addr = A_UPPER_WR_PROT_REG, | |
1485 | + },{ .name = "WR_PROT_CTRL_REG", | |
1486 | + .addr = A_WR_PROT_CTRL_REG, | |
1487 | + .ro = 0xfffffffc, | |
1488 | + },{ .name = "INDIRECT_READ_XFER_CTRL_REG", | |
1489 | + .addr = A_INDIRECT_READ_XFER_CTRL_REG, | |
1490 | + .ro = 0xffffffd4, | |
1491 | + .w1c = 0x08, | |
1492 | + .pre_write = ind_rd_xfer_ctrl_reg_pre_write, | |
1493 | + .post_write = ind_rd_xfer_ctrl_reg_post_write, | |
1494 | + .post_read = ind_rd_xfer_ctrl_reg_post_read, | |
1495 | + },{ .name = "INDIRECT_READ_XFER_WATERMARK_REG", | |
1496 | + .addr = A_INDIRECT_READ_XFER_WATERMARK_REG, | |
1497 | + },{ .name = "INDIRECT_READ_XFER_START_REG", | |
1498 | + .addr = A_INDIRECT_READ_XFER_START_REG, | |
1499 | + },{ .name = "INDIRECT_READ_XFER_NUM_BYTES_REG", | |
1500 | + .addr = A_INDIRECT_READ_XFER_NUM_BYTES_REG, | |
1501 | + },{ .name = "INDIRECT_WRITE_XFER_CTRL_REG", | |
1502 | + .addr = A_INDIRECT_WRITE_XFER_CTRL_REG, | |
1503 | + .ro = 0xffffffdc, | |
1504 | + .w1c = 0x20, | |
1505 | + .pre_write = ind_wr_xfer_ctrl_reg_pre_write, | |
1506 | + .post_write = ind_wr_xfer_ctrl_reg_post_write, | |
1507 | + .post_read = ind_wr_xfer_ctrl_reg_post_read, | |
1508 | + },{ .name = "INDIRECT_WRITE_XFER_WATERMARK_REG", | |
1509 | + .addr = A_INDIRECT_WRITE_XFER_WATERMARK_REG, | |
1510 | + .reset = 0xffffffff, | |
1511 | + },{ .name = "INDIRECT_WRITE_XFER_START_REG", | |
1512 | + .addr = A_INDIRECT_WRITE_XFER_START_REG, | |
1513 | + },{ .name = "INDIRECT_WRITE_XFER_NUM_BYTES_REG", | |
1514 | + .addr = A_INDIRECT_WRITE_XFER_NUM_BYTES_REG, | |
1515 | + },{ .name = "INDIRECT_TRIGGER_ADDR_RANGE_REG", | |
1516 | + .addr = A_INDIRECT_TRIGGER_ADDR_RANGE_REG, | |
1517 | + .reset = 0x4, | |
1518 | + .ro = 0xfffffff0, | |
1519 | + },{ .name = "FLASH_COMMAND_CTRL_MEM_REG", | |
1520 | + .addr = A_FLASH_COMMAND_CTRL_MEM_REG, | |
1521 | + .ro = 0xe008fffe, | |
1522 | + .post_write = flash_cmd_ctrl_mem_reg_post_write, | |
1523 | + },{ .name = "FLASH_CMD_CTRL_REG", | |
1524 | + .addr = A_FLASH_CMD_CTRL_REG, | |
1525 | + .ro = 0x7a, | |
1526 | + .post_write = flash_cmd_ctrl_reg_post_write, | |
1527 | + },{ .name = "FLASH_CMD_ADDR_REG", | |
1528 | + .addr = A_FLASH_CMD_ADDR_REG, | |
1529 | + },{ .name = "FLASH_RD_DATA_LOWER_REG", | |
1530 | + .addr = A_FLASH_RD_DATA_LOWER_REG, | |
1531 | + .ro = 0xffffffff, | |
1532 | + },{ .name = "FLASH_RD_DATA_UPPER_REG", | |
1533 | + .addr = A_FLASH_RD_DATA_UPPER_REG, | |
1534 | + .ro = 0xffffffff, | |
1535 | + },{ .name = "FLASH_WR_DATA_LOWER_REG", | |
1536 | + .addr = A_FLASH_WR_DATA_LOWER_REG, | |
1537 | + },{ .name = "FLASH_WR_DATA_UPPER_REG", | |
1538 | + .addr = A_FLASH_WR_DATA_UPPER_REG, | |
1539 | + },{ .name = "POLLING_FLASH_STATUS_REG", | |
1540 | + .addr = A_POLLING_FLASH_STATUS_REG, | |
1541 | + .ro = 0xfff0ffff, | |
1542 | + },{ .name = "PHY_CONFIGURATION_REG", | |
1543 | + .addr = A_PHY_CONFIGURATION_REG, | |
1544 | + .reset = 0x40000000, | |
1545 | + .ro = 0x1f80ff80, | |
1546 | + },{ .name = "PHY_MASTER_CONTROL_REG", | |
1547 | + .addr = A_PHY_MASTER_CONTROL_REG, | |
1548 | + .reset = 0x800000, | |
1549 | + .ro = 0xfe08ff80, | |
1550 | + },{ .name = "DLL_OBSERVABLE_LOWER_REG", | |
1551 | + .addr = A_DLL_OBSERVABLE_LOWER_REG, | |
1552 | + .ro = 0xffffffff, | |
1553 | + },{ .name = "DLL_OBSERVABLE_UPPER_REG", | |
1554 | + .addr = A_DLL_OBSERVABLE_UPPER_REG, | |
1555 | + .ro = 0xffffffff, | |
1556 | + .post_read = dll_obs_upper_reg_post_read, | |
1557 | + },{ .name = "OPCODE_EXT_LOWER_REG", | |
1558 | + .addr = A_OPCODE_EXT_LOWER_REG, | |
1559 | + .reset = 0x13edfa00, | |
1560 | + },{ .name = "OPCODE_EXT_UPPER_REG", | |
1561 | + .addr = A_OPCODE_EXT_UPPER_REG, | |
1562 | + .reset = 0x6f90000, | |
1563 | + .ro = 0xffff, | |
1564 | + },{ .name = "MODULE_ID_REG", | |
1565 | + .addr = A_MODULE_ID_REG, | |
1566 | + .reset = 0x300, | |
1567 | + .ro = 0xffffffff, | |
1568 | + } | |
1569 | +}; | |
1570 | + | |
1571 | +/* Return dev-obj from reg-region created by register_init_block32 */ | |
1572 | +static XlnxVersalOspi *xilinx_ospi_of_mr(void *mr_accessor) | |
1573 | +{ | |
1574 | + RegisterInfoArray *reg_array = mr_accessor; | |
1575 | + Object *dev; | |
1576 | + | |
1577 | + dev = reg_array->mem.owner; | |
1578 | + assert(dev); | |
1579 | + | |
1580 | + return XILINX_VERSAL_OSPI(dev); | |
1581 | +} | |
1582 | + | |
1583 | +static void ospi_write(void *opaque, hwaddr addr, uint64_t value, | |
1584 | + unsigned int size) | |
1585 | +{ | |
1586 | + XlnxVersalOspi *s = xilinx_ospi_of_mr(opaque); | |
1587 | + | |
1588 | + register_write_memory(opaque, addr, value, size); | |
1589 | + ospi_update_irq_line(s); | |
1590 | +} | |
1591 | + | |
1592 | +static const MemoryRegionOps ospi_ops = { | |
1593 | + .read = register_read_memory, | |
1594 | + .write = ospi_write, | |
1595 | + .endianness = DEVICE_LITTLE_ENDIAN, | |
1596 | + .valid = { | |
1597 | + .min_access_size = 4, | |
1598 | + .max_access_size = 4, | |
1599 | + }, | |
1600 | +}; | |
1601 | + | |
1602 | +static uint64_t ospi_indac_read(void *opaque, unsigned int size) | |
1603 | +{ | |
1604 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1605 | + uint64_t ret = ospi_rx_sram_read(s, size); | |
1606 | + | |
1607 | + if (!ospi_ind_op_completed(s->rd_ind_op)) { | |
1608 | + ospi_do_ind_read(s); | |
1609 | + } | |
1610 | + return ret; | |
1611 | +} | |
1612 | + | |
1613 | +static void ospi_indac_write(void *opaque, uint64_t value, unsigned int size) | |
1614 | +{ | |
1615 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1616 | + | |
1617 | + g_assert(!s->ind_write_disabled); | |
1618 | + | |
1619 | + if (!ospi_ind_op_completed(s->wr_ind_op)) { | |
1620 | + ospi_tx_sram_write(s, value, size); | |
1621 | + ospi_do_indirect_write(s); | |
1622 | + } else { | |
1623 | + qemu_log_mask(LOG_GUEST_ERROR, | |
1624 | + "OSPI wr into indac area while no ongoing indac wr\n"); | |
1625 | + } | |
1626 | +} | |
1627 | + | |
1628 | +static bool is_inside_indac_range(XlnxVersalOspi *s, hwaddr addr) | |
1629 | +{ | |
1630 | + uint32_t range_start; | |
1631 | + uint32_t range_end; | |
1632 | + | |
1633 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) { | |
1634 | + return true; | |
1635 | + } | |
1636 | + | |
1637 | + range_start = s->regs[R_IND_AHB_ADDR_TRIGGER_REG]; | |
1638 | + range_end = range_start + | |
1639 | + (1 << ARRAY_FIELD_EX32(s->regs, | |
1640 | + INDIRECT_TRIGGER_ADDR_RANGE_REG, | |
1641 | + IND_RANGE_WIDTH_FLD)); | |
1642 | + | |
1643 | + addr += s->regs[R_IND_AHB_ADDR_TRIGGER_REG] & 0xF0000000; | |
1644 | + | |
1645 | + return addr >= range_start && addr < range_end; | |
1646 | +} | |
1647 | + | |
1648 | +static bool ospi_is_indac_active(XlnxVersalOspi *s) | |
1649 | +{ | |
1650 | + /* | |
1651 | + * When dac and indac cannot be active at the same time, | |
1652 | + * return true when dac is disabled. | |
1653 | + */ | |
1654 | + return s->dac_with_indac || !s->dac_enable; | |
1655 | +} | |
1656 | + | |
1657 | +static uint64_t ospi_dac_read(void *opaque, hwaddr addr, unsigned int size) | |
1658 | +{ | |
1659 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1660 | + | |
1661 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { | |
1662 | + if (ospi_is_indac_active(s) && | |
1663 | + is_inside_indac_range(s, addr)) { | |
1664 | + return ospi_indac_read(s, size); | |
1665 | + } | |
1666 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD) | |
1667 | + && s->dac_enable) { | |
1668 | + if (ARRAY_FIELD_EX32(s->regs, | |
1669 | + CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) { | |
1670 | + addr += s->regs[R_REMAP_ADDR_REG]; | |
1671 | + } | |
1672 | + return ospi_do_dac_read(opaque, addr, size); | |
1673 | + } else { | |
1674 | + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while DAC disabled\n"); | |
1675 | + } | |
1676 | + } else { | |
1677 | + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while OSPI disabled\n"); | |
1678 | + } | |
1679 | + | |
1680 | + return 0; | |
1681 | +} | |
1682 | + | |
1683 | +static void ospi_dac_write(void *opaque, hwaddr addr, uint64_t value, | |
1684 | + unsigned int size) | |
1685 | +{ | |
1686 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1687 | + | |
1688 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { | |
1689 | + if (ospi_is_indac_active(s) && | |
1690 | + !s->ind_write_disabled && | |
1691 | + is_inside_indac_range(s, addr)) { | |
1692 | + return ospi_indac_write(s, value, size); | |
1693 | + } | |
1694 | + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD) && | |
1695 | + s->dac_enable) { | |
1696 | + if (ARRAY_FIELD_EX32(s->regs, | |
1697 | + CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) { | |
1698 | + addr += s->regs[R_REMAP_ADDR_REG]; | |
1699 | + } | |
1700 | + /* Check if addr is write protected */ | |
1701 | + if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, ENB_FLD) && | |
1702 | + ospi_is_write_protected(s, addr)) { | |
1703 | + set_irq(s, R_IRQ_STATUS_REG_PROT_WR_ATTEMPT_FLD_MASK); | |
1704 | + ospi_update_irq_line(s); | |
1705 | + qemu_log_mask(LOG_GUEST_ERROR, | |
1706 | + "OSPI writing into write protected area\n"); | |
1707 | + return; | |
1708 | + } | |
1709 | + ospi_do_dac_write(opaque, addr, value, size); | |
1710 | + } else { | |
1711 | + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while DAC disabled\n"); | |
1712 | + } | |
1713 | + } else { | |
1714 | + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while OSPI disabled\n"); | |
1715 | + } | |
1716 | +} | |
1717 | + | |
1718 | +static const MemoryRegionOps ospi_dac_ops = { | |
1719 | + .read = ospi_dac_read, | |
1720 | + .write = ospi_dac_write, | |
1721 | + .endianness = DEVICE_LITTLE_ENDIAN, | |
1722 | + .valid = { | |
1723 | + .min_access_size = 4, | |
1724 | + .max_access_size = 4, | |
1725 | + }, | |
1726 | +}; | |
1727 | + | |
1728 | +static void ospi_update_dac_status(void *opaque, int n, int level) | |
1729 | +{ | |
1730 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); | |
1731 | + | |
1732 | + s->dac_enable = level; | |
1733 | +} | |
1734 | + | |
1735 | +static void xlnx_versal_ospi_realize(DeviceState *dev, Error **errp) | |
1736 | +{ | |
1737 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev); | |
1738 | + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
1739 | + | |
1740 | + s->num_cs = 4; | |
1741 | + s->spi = ssi_create_bus(dev, "spi0"); | |
1742 | + s->cs_lines = g_new0(qemu_irq, s->num_cs); | |
1743 | + for (int i = 0; i < s->num_cs; ++i) { | |
1744 | + sysbus_init_irq(sbd, &s->cs_lines[i]); | |
1745 | + } | |
1746 | + | |
1747 | + fifo8_create(&s->rx_fifo, RXFF_SZ); | |
1748 | + fifo8_create(&s->tx_fifo, TXFF_SZ); | |
1749 | + fifo8_create(&s->rx_sram, RXFF_SZ); | |
1750 | + fifo8_create(&s->tx_sram, TXFF_SZ); | |
1751 | +} | |
1752 | + | |
1753 | +static void xlnx_versal_ospi_init(Object *obj) | |
1754 | +{ | |
1755 | + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(obj); | |
1756 | + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
1757 | + DeviceState *dev = DEVICE(obj); | |
1758 | + RegisterInfoArray *reg_array; | |
1759 | + | |
1760 | + memory_region_init(&s->iomem, obj, TYPE_XILINX_VERSAL_OSPI, | |
1761 | + XILINX_VERSAL_OSPI_R_MAX * 4); | |
1762 | + reg_array = | |
1763 | + register_init_block32(DEVICE(obj), ospi_regs_info, | |
1764 | + ARRAY_SIZE(ospi_regs_info), | |
1765 | + s->regs_info, s->regs, | |
1766 | + &ospi_ops, | |
1767 | + XILINX_VERSAL_OSPI_ERR_DEBUG, | |
1768 | + XILINX_VERSAL_OSPI_R_MAX * 4); | |
1769 | + memory_region_add_subregion(&s->iomem, 0x0, ®_array->mem); | |
1770 | + sysbus_init_mmio(sbd, &s->iomem); | |
1771 | + | |
1772 | + memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s, | |
1773 | + TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000); | |
1774 | + sysbus_init_mmio(sbd, &s->iomem_dac); | |
1775 | + | |
1776 | + sysbus_init_irq(sbd, &s->irq); | |
1777 | + | |
1778 | + object_property_add_link(obj, "dma-src", TYPE_XLNX_CSU_DMA, | |
1779 | + (Object **)&s->dma_src, | |
1780 | + object_property_allow_set_link, | |
1781 | + OBJ_PROP_LINK_STRONG); | |
1782 | + | |
1783 | + qdev_init_gpio_in_named(dev, ospi_update_dac_status, "ospi-mux-sel", 1); | |
1784 | +} | |
1785 | + | |
1786 | +static const VMStateDescription vmstate_ind_op = { | |
1787 | + .name = "OSPIIndOp", | |
1788 | + .version_id = 1, | |
1789 | + .minimum_version_id = 1, | |
1790 | + .fields = (VMStateField[]) { | |
1791 | + VMSTATE_UINT32(flash_addr, IndOp), | |
1792 | + VMSTATE_UINT32(num_bytes, IndOp), | |
1793 | + VMSTATE_UINT32(done_bytes, IndOp), | |
1794 | + VMSTATE_BOOL(completed, IndOp), | |
1795 | + VMSTATE_END_OF_LIST() | |
1796 | + } | |
1797 | +}; | |
1798 | + | |
1799 | +static const VMStateDescription vmstate_xlnx_versal_ospi = { | |
1800 | + .name = TYPE_XILINX_VERSAL_OSPI, | |
1801 | + .version_id = 1, | |
1802 | + .minimum_version_id = 1, | |
1803 | + .minimum_version_id_old = 1, | |
1804 | + .fields = (VMStateField[]) { | |
1805 | + VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi), | |
1806 | + VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi), | |
1807 | + VMSTATE_FIFO8(rx_sram, XlnxVersalOspi), | |
1808 | + VMSTATE_FIFO8(tx_sram, XlnxVersalOspi), | |
1809 | + VMSTATE_BOOL(ind_write_disabled, XlnxVersalOspi), | |
1810 | + VMSTATE_BOOL(dac_with_indac, XlnxVersalOspi), | |
1811 | + VMSTATE_BOOL(dac_enable, XlnxVersalOspi), | |
1812 | + VMSTATE_BOOL(src_dma_inprog, XlnxVersalOspi), | |
1813 | + VMSTATE_STRUCT_ARRAY(rd_ind_op, XlnxVersalOspi, 2, 1, | |
1814 | + vmstate_ind_op, IndOp), | |
1815 | + VMSTATE_STRUCT_ARRAY(wr_ind_op, XlnxVersalOspi, 2, 1, | |
1816 | + vmstate_ind_op, IndOp), | |
1817 | + VMSTATE_UINT32_ARRAY(regs, XlnxVersalOspi, XILINX_VERSAL_OSPI_R_MAX), | |
1818 | + VMSTATE_UINT8_ARRAY(stig_membank, XlnxVersalOspi, 512), | |
1819 | + VMSTATE_END_OF_LIST(), | |
1820 | + } | |
1821 | +}; | |
1822 | + | |
1823 | +static Property xlnx_versal_ospi_properties[] = { | |
1824 | + DEFINE_PROP_BOOL("dac-with-indac", XlnxVersalOspi, dac_with_indac, false), | |
1825 | + DEFINE_PROP_BOOL("indac-write-disabled", XlnxVersalOspi, | |
1826 | + ind_write_disabled, false), | |
1827 | + DEFINE_PROP_END_OF_LIST(), | |
1828 | +}; | |
1829 | + | |
1830 | +static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data) | |
1831 | +{ | |
1832 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
1833 | + | |
1834 | + dc->reset = xlnx_versal_ospi_reset; | |
1835 | + dc->realize = xlnx_versal_ospi_realize; | |
1836 | + dc->vmsd = &vmstate_xlnx_versal_ospi; | |
1837 | + device_class_set_props(dc, xlnx_versal_ospi_properties); | |
1838 | +} | |
1839 | + | |
1840 | +static const TypeInfo xlnx_versal_ospi_info = { | |
1841 | + .name = TYPE_XILINX_VERSAL_OSPI, | |
1842 | + .parent = TYPE_SYS_BUS_DEVICE, | |
1843 | + .instance_size = sizeof(XlnxVersalOspi), | |
1844 | + .class_init = xlnx_versal_ospi_class_init, | |
1845 | + .instance_init = xlnx_versal_ospi_init, | |
1846 | +}; | |
1847 | + | |
1848 | +static void xlnx_versal_ospi_register_types(void) | |
1849 | +{ | |
1850 | + type_register_static(&xlnx_versal_ospi_info); | |
1851 | +} | |
1852 | + | |
1853 | +type_init(xlnx_versal_ospi_register_types) |
@@ -0,0 +1,111 @@ | ||
1 | +/* | |
2 | + * Header file for the Xilinx Versal's OSPI controller | |
3 | + * | |
4 | + * Copyright (C) 2021 Xilinx Inc | |
5 | + * Written by Francisco Iglesias <francisco.iglesias@xilinx.com> | |
6 | + * | |
7 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | + * of this software and associated documentation files (the "Software"), to deal | |
9 | + * in the Software without restriction, including without limitation the rights | |
10 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | + * copies of the Software, and to permit persons to whom the Software is | |
12 | + * furnished to do so, subject to the following conditions: | |
13 | + * | |
14 | + * The above copyright notice and this permission notice shall be included in | |
15 | + * all copies or substantial portions of the Software. | |
16 | + * | |
17 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | + * THE SOFTWARE. | |
24 | + */ | |
25 | + | |
26 | +/* | |
27 | + * This is a model of Xilinx Versal's Octal SPI flash memory controller | |
28 | + * documented in Versal's Technical Reference manual [1] and the Versal ACAP | |
29 | + * Register reference [2]. | |
30 | + * | |
31 | + * References: | |
32 | + * | |
33 | + * [1] Versal ACAP Technical Reference Manual, | |
34 | + * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf | |
35 | + * | |
36 | + * [2] Versal ACAP Register Reference, | |
37 | + * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___ospi.html | |
38 | + * | |
39 | + * | |
40 | + * QEMU interface: | |
41 | + * + sysbus MMIO region 0: MemoryRegion for the device's registers | |
42 | + * + sysbus MMIO region 1: MemoryRegion for flash memory linear address space | |
43 | + * (data transfer). | |
44 | + * + sysbus IRQ 0: Device interrupt. | |
45 | + * + Named GPIO input "ospi-mux-sel": 0: enables indirect access mode | |
46 | + * and 1: enables direct access mode. | |
47 | + * + Property "dac-with-indac": Allow both direct accesses and indirect | |
48 | + * accesses simultaneously. | |
49 | + * + Property "indac-write-disabled": Disable indirect access writes. | |
50 | + */ | |
51 | + | |
52 | +#ifndef XILINX_VERSAL_OSPI_H | |
53 | +#define XILINX_VERSAL_OSPI_H | |
54 | + | |
55 | +#include "hw/register.h" | |
56 | +#include "hw/ssi/ssi.h" | |
57 | +#include "qemu/fifo8.h" | |
58 | +#include "hw/dma/xlnx_csu_dma.h" | |
59 | + | |
60 | +#define TYPE_XILINX_VERSAL_OSPI "xlnx.versal-ospi" | |
61 | + | |
62 | +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalOspi, XILINX_VERSAL_OSPI) | |
63 | + | |
64 | +#define XILINX_VERSAL_OSPI_R_MAX (0xfc / 4 + 1) | |
65 | + | |
66 | +/* | |
67 | + * Indirect operations | |
68 | + */ | |
69 | +typedef struct IndOp { | |
70 | + uint32_t flash_addr; | |
71 | + uint32_t num_bytes; | |
72 | + uint32_t done_bytes; | |
73 | + bool completed; | |
74 | +} IndOp; | |
75 | + | |
76 | +struct XlnxVersalOspi { | |
77 | + SysBusDevice parent_obj; | |
78 | + | |
79 | + MemoryRegion iomem; | |
80 | + MemoryRegion iomem_dac; | |
81 | + | |
82 | + uint8_t num_cs; | |
83 | + qemu_irq *cs_lines; | |
84 | + | |
85 | + SSIBus *spi; | |
86 | + | |
87 | + Fifo8 rx_fifo; | |
88 | + Fifo8 tx_fifo; | |
89 | + | |
90 | + Fifo8 rx_sram; | |
91 | + Fifo8 tx_sram; | |
92 | + | |
93 | + qemu_irq irq; | |
94 | + | |
95 | + XlnxCSUDMA *dma_src; | |
96 | + bool ind_write_disabled; | |
97 | + bool dac_with_indac; | |
98 | + bool dac_enable; | |
99 | + bool src_dma_inprog; | |
100 | + | |
101 | + IndOp rd_ind_op[2]; | |
102 | + IndOp wr_ind_op[2]; | |
103 | + | |
104 | + uint32_t regs[XILINX_VERSAL_OSPI_R_MAX]; | |
105 | + RegisterInfo regs_info[XILINX_VERSAL_OSPI_R_MAX]; | |
106 | + | |
107 | + /* Maximum inferred membank size is 512 bytes */ | |
108 | + uint8_t stig_membank[512]; | |
109 | +}; | |
110 | + | |
111 | +#endif /* XILINX_VERSAL_OSPI_H */ |