xref: /openbmc/qemu/hw/dma/xlnx_dpdma.c (revision ba06fe8add5b788956a7317246c6280dfc157040)
1d3c6369aSKONRAD Frederic /*
2d3c6369aSKONRAD Frederic  * xlnx_dpdma.c
3d3c6369aSKONRAD Frederic  *
4d3c6369aSKONRAD Frederic  *  Copyright (C) 2015 : GreenSocs Ltd
5d3c6369aSKONRAD Frederic  *      http://www.greensocs.com/ , email: info@greensocs.com
6d3c6369aSKONRAD Frederic  *
7d3c6369aSKONRAD Frederic  *  Developed by :
8d3c6369aSKONRAD Frederic  *  Frederic Konrad   <fred.konrad@greensocs.com>
9d3c6369aSKONRAD Frederic  *
10d3c6369aSKONRAD Frederic  * This program is free software; you can redistribute it and/or modify
11d3c6369aSKONRAD Frederic  * it under the terms of the GNU General Public License as published by
12d3c6369aSKONRAD Frederic  * the Free Software Foundation, either version 2 of the License, or
13d3c6369aSKONRAD Frederic  * (at your option) any later version.
14d3c6369aSKONRAD Frederic  *
15d3c6369aSKONRAD Frederic  * This program is distributed in the hope that it will be useful,
16d3c6369aSKONRAD Frederic  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17d3c6369aSKONRAD Frederic  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18d3c6369aSKONRAD Frederic  * GNU General Public License for more details.
19d3c6369aSKONRAD Frederic  *
20d3c6369aSKONRAD Frederic  * You should have received a copy of the GNU General Public License along
21d3c6369aSKONRAD Frederic  * with this program; if not, see <http://www.gnu.org/licenses/>.
22d3c6369aSKONRAD Frederic  *
23d3c6369aSKONRAD Frederic  */
24d3c6369aSKONRAD Frederic 
25d3c6369aSKONRAD Frederic #include "qemu/osdep.h"
26a8d25326SMarkus Armbruster #include "qemu-common.h"
27d3c6369aSKONRAD Frederic #include "qemu/log.h"
280b8fa32fSMarkus Armbruster #include "qemu/module.h"
29d3c6369aSKONRAD Frederic #include "hw/dma/xlnx_dpdma.h"
3064552b6bSMarkus Armbruster #include "hw/irq.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
32d3c6369aSKONRAD Frederic 
33d3c6369aSKONRAD Frederic #ifndef DEBUG_DPDMA
34d3c6369aSKONRAD Frederic #define DEBUG_DPDMA 0
35d3c6369aSKONRAD Frederic #endif
36d3c6369aSKONRAD Frederic 
37d3c6369aSKONRAD Frederic #define DPRINTF(fmt, ...) do {                                                 \
38d3c6369aSKONRAD Frederic     if (DEBUG_DPDMA) {                                                         \
39d3c6369aSKONRAD Frederic         qemu_log("xlnx_dpdma: " fmt , ## __VA_ARGS__);                         \
40d3c6369aSKONRAD Frederic     }                                                                          \
412562755eSEric Blake } while (0)
42d3c6369aSKONRAD Frederic 
43d3c6369aSKONRAD Frederic /*
44d3c6369aSKONRAD Frederic  * Registers offset for DPDMA.
45d3c6369aSKONRAD Frederic  */
46d3c6369aSKONRAD Frederic #define DPDMA_ERR_CTRL                        (0x0000)
47d3c6369aSKONRAD Frederic #define DPDMA_ISR                             (0x0004 >> 2)
48d3c6369aSKONRAD Frederic #define DPDMA_IMR                             (0x0008 >> 2)
49d3c6369aSKONRAD Frederic #define DPDMA_IEN                             (0x000C >> 2)
50d3c6369aSKONRAD Frederic #define DPDMA_IDS                             (0x0010 >> 2)
51d3c6369aSKONRAD Frederic #define DPDMA_EISR                            (0x0014 >> 2)
52d3c6369aSKONRAD Frederic #define DPDMA_EIMR                            (0x0018 >> 2)
53d3c6369aSKONRAD Frederic #define DPDMA_EIEN                            (0x001C >> 2)
54d3c6369aSKONRAD Frederic #define DPDMA_EIDS                            (0x0020 >> 2)
55d3c6369aSKONRAD Frederic #define DPDMA_CNTL                            (0x0100 >> 2)
56d3c6369aSKONRAD Frederic 
57d3c6369aSKONRAD Frederic #define DPDMA_GBL                             (0x0104 >> 2)
58d3c6369aSKONRAD Frederic #define DPDMA_GBL_TRG_CH(n)                   (1 << n)
59d3c6369aSKONRAD Frederic #define DPDMA_GBL_RTRG_CH(n)                  (1 << 6 << n)
60d3c6369aSKONRAD Frederic 
61d3c6369aSKONRAD Frederic #define DPDMA_ALC0_CNTL                       (0x0108 >> 2)
62d3c6369aSKONRAD Frederic #define DPDMA_ALC0_STATUS                     (0x010C >> 2)
63d3c6369aSKONRAD Frederic #define DPDMA_ALC0_MAX                        (0x0110 >> 2)
64d3c6369aSKONRAD Frederic #define DPDMA_ALC0_MIN                        (0x0114 >> 2)
65d3c6369aSKONRAD Frederic #define DPDMA_ALC0_ACC                        (0x0118 >> 2)
66d3c6369aSKONRAD Frederic #define DPDMA_ALC0_ACC_TRAN                   (0x011C >> 2)
67d3c6369aSKONRAD Frederic #define DPDMA_ALC1_CNTL                       (0x0120 >> 2)
68d3c6369aSKONRAD Frederic #define DPDMA_ALC1_STATUS                     (0x0124 >> 2)
69d3c6369aSKONRAD Frederic #define DPDMA_ALC1_MAX                        (0x0128 >> 2)
70d3c6369aSKONRAD Frederic #define DPDMA_ALC1_MIN                        (0x012C >> 2)
71d3c6369aSKONRAD Frederic #define DPDMA_ALC1_ACC                        (0x0130 >> 2)
72d3c6369aSKONRAD Frederic #define DPDMA_ALC1_ACC_TRAN                   (0x0134 >> 2)
73d3c6369aSKONRAD Frederic 
74d3c6369aSKONRAD Frederic #define DPDMA_DSCR_STRT_ADDRE_CH(n)           ((0x0200 + n * 0x100) >> 2)
75d3c6369aSKONRAD Frederic #define DPDMA_DSCR_STRT_ADDR_CH(n)            ((0x0204 + n * 0x100) >> 2)
76d3c6369aSKONRAD Frederic #define DPDMA_DSCR_NEXT_ADDRE_CH(n)           ((0x0208 + n * 0x100) >> 2)
77d3c6369aSKONRAD Frederic #define DPDMA_DSCR_NEXT_ADDR_CH(n)            ((0x020C + n * 0x100) >> 2)
78d3c6369aSKONRAD Frederic #define DPDMA_PYLD_CUR_ADDRE_CH(n)            ((0x0210 + n * 0x100) >> 2)
79d3c6369aSKONRAD Frederic #define DPDMA_PYLD_CUR_ADDR_CH(n)             ((0x0214 + n * 0x100) >> 2)
80d3c6369aSKONRAD Frederic 
81d3c6369aSKONRAD Frederic #define DPDMA_CNTL_CH(n)                      ((0x0218 + n * 0x100) >> 2)
82d3c6369aSKONRAD Frederic #define DPDMA_CNTL_CH_EN                      (1)
83d3c6369aSKONRAD Frederic #define DPDMA_CNTL_CH_PAUSED                  (1 << 1)
84d3c6369aSKONRAD Frederic 
85d3c6369aSKONRAD Frederic #define DPDMA_STATUS_CH(n)                    ((0x021C + n * 0x100) >> 2)
86d3c6369aSKONRAD Frederic #define DPDMA_STATUS_BURST_TYPE               (1 << 4)
87d3c6369aSKONRAD Frederic #define DPDMA_STATUS_MODE                     (1 << 5)
88d3c6369aSKONRAD Frederic #define DPDMA_STATUS_EN_CRC                   (1 << 6)
89d3c6369aSKONRAD Frederic #define DPDMA_STATUS_LAST_DSCR                (1 << 7)
90d3c6369aSKONRAD Frederic #define DPDMA_STATUS_LDSCR_FRAME              (1 << 8)
91d3c6369aSKONRAD Frederic #define DPDMA_STATUS_IGNR_DONE                (1 << 9)
92d3c6369aSKONRAD Frederic #define DPDMA_STATUS_DSCR_DONE                (1 << 10)
93d3c6369aSKONRAD Frederic #define DPDMA_STATUS_EN_DSCR_UP               (1 << 11)
94d3c6369aSKONRAD Frederic #define DPDMA_STATUS_EN_DSCR_INTR             (1 << 12)
95d3c6369aSKONRAD Frederic #define DPDMA_STATUS_PREAMBLE_OFF             (13)
96d3c6369aSKONRAD Frederic 
97d3c6369aSKONRAD Frederic #define DPDMA_VDO_CH(n)                       ((0x0220 + n * 0x100) >> 2)
98d3c6369aSKONRAD Frederic #define DPDMA_PYLD_SZ_CH(n)                   ((0x0224 + n * 0x100) >> 2)
99d3c6369aSKONRAD Frederic #define DPDMA_DSCR_ID_CH(n)                   ((0x0228 + n * 0x100) >> 2)
100d3c6369aSKONRAD Frederic 
101d3c6369aSKONRAD Frederic /*
102d3c6369aSKONRAD Frederic  * Descriptor control field.
103d3c6369aSKONRAD Frederic  */
104d3c6369aSKONRAD Frederic #define CONTROL_PREAMBLE_VALUE                0xA5
105d3c6369aSKONRAD Frederic 
106d3c6369aSKONRAD Frederic #define DSCR_CTRL_PREAMBLE                    0xFF
107d3c6369aSKONRAD Frederic #define DSCR_CTRL_EN_DSCR_DONE_INTR           (1 << 8)
108d3c6369aSKONRAD Frederic #define DSCR_CTRL_EN_DSCR_UPDATE              (1 << 9)
109d3c6369aSKONRAD Frederic #define DSCR_CTRL_IGNORE_DONE                 (1 << 10)
110d3c6369aSKONRAD Frederic #define DSCR_CTRL_AXI_BURST_TYPE              (1 << 11)
111d3c6369aSKONRAD Frederic #define DSCR_CTRL_AXCACHE                     (0x0F << 12)
112d3c6369aSKONRAD Frederic #define DSCR_CTRL_AXPROT                      (0x2 << 16)
113d3c6369aSKONRAD Frederic #define DSCR_CTRL_DESCRIPTOR_MODE             (1 << 18)
114d3c6369aSKONRAD Frederic #define DSCR_CTRL_LAST_DESCRIPTOR             (1 << 19)
115d3c6369aSKONRAD Frederic #define DSCR_CTRL_ENABLE_CRC                  (1 << 20)
116d3c6369aSKONRAD Frederic #define DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME    (1 << 21)
117d3c6369aSKONRAD Frederic 
118d3c6369aSKONRAD Frederic /*
119d3c6369aSKONRAD Frederic  * Descriptor timestamp field.
120d3c6369aSKONRAD Frederic  */
121d3c6369aSKONRAD Frederic #define STATUS_DONE                           (1 << 31)
122d3c6369aSKONRAD Frederic 
123d3c6369aSKONRAD Frederic #define DPDMA_FRAG_MAX_SZ                     (4096)
124d3c6369aSKONRAD Frederic 
125d3c6369aSKONRAD Frederic enum DPDMABurstType {
126d3c6369aSKONRAD Frederic     DPDMA_INCR = 0,
127d3c6369aSKONRAD Frederic     DPDMA_FIXED = 1
128d3c6369aSKONRAD Frederic };
129d3c6369aSKONRAD Frederic 
130d3c6369aSKONRAD Frederic enum DPDMAMode {
131d3c6369aSKONRAD Frederic     DPDMA_CONTIGOUS = 0,
132d3c6369aSKONRAD Frederic     DPDMA_FRAGMENTED = 1
133d3c6369aSKONRAD Frederic };
134d3c6369aSKONRAD Frederic 
135d3c6369aSKONRAD Frederic struct DPDMADescriptor {
136d3c6369aSKONRAD Frederic     uint32_t control;
137d3c6369aSKONRAD Frederic     uint32_t descriptor_id;
138d3c6369aSKONRAD Frederic     /* transfer size in byte. */
139d3c6369aSKONRAD Frederic     uint32_t xfer_size;
140d3c6369aSKONRAD Frederic     uint32_t line_size_stride;
141d3c6369aSKONRAD Frederic     uint32_t timestamp_lsb;
142d3c6369aSKONRAD Frederic     uint32_t timestamp_msb;
143d3c6369aSKONRAD Frederic     /* contains extension for both descriptor and source. */
144d3c6369aSKONRAD Frederic     uint32_t address_extension;
145d3c6369aSKONRAD Frederic     uint32_t next_descriptor;
146d3c6369aSKONRAD Frederic     uint32_t source_address;
147d3c6369aSKONRAD Frederic     uint32_t address_extension_23;
148d3c6369aSKONRAD Frederic     uint32_t address_extension_45;
149d3c6369aSKONRAD Frederic     uint32_t source_address2;
150d3c6369aSKONRAD Frederic     uint32_t source_address3;
151d3c6369aSKONRAD Frederic     uint32_t source_address4;
152d3c6369aSKONRAD Frederic     uint32_t source_address5;
153d3c6369aSKONRAD Frederic     uint32_t crc;
154d3c6369aSKONRAD Frederic };
155d3c6369aSKONRAD Frederic 
156d3c6369aSKONRAD Frederic typedef enum DPDMABurstType DPDMABurstType;
157d3c6369aSKONRAD Frederic typedef enum DPDMAMode DPDMAMode;
158d3c6369aSKONRAD Frederic typedef struct DPDMADescriptor DPDMADescriptor;
159d3c6369aSKONRAD Frederic 
160d3c6369aSKONRAD Frederic static bool xlnx_dpdma_desc_is_last(DPDMADescriptor *desc)
161d3c6369aSKONRAD Frederic {
162d3c6369aSKONRAD Frederic     return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0);
163d3c6369aSKONRAD Frederic }
164d3c6369aSKONRAD Frederic 
165d3c6369aSKONRAD Frederic static bool xlnx_dpdma_desc_is_last_of_frame(DPDMADescriptor *desc)
166d3c6369aSKONRAD Frederic {
167d3c6369aSKONRAD Frederic     return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0);
168d3c6369aSKONRAD Frederic }
169d3c6369aSKONRAD Frederic 
170d3c6369aSKONRAD Frederic static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc,
171d3c6369aSKONRAD Frederic                                                      uint8_t frag)
172d3c6369aSKONRAD Frederic {
173d3c6369aSKONRAD Frederic     uint64_t addr = 0;
174d3c6369aSKONRAD Frederic     assert(frag < 5);
175d3c6369aSKONRAD Frederic 
176d3c6369aSKONRAD Frederic     switch (frag) {
177d3c6369aSKONRAD Frederic     case 0:
178d3c6369aSKONRAD Frederic         addr = desc->source_address
179d3c6369aSKONRAD Frederic             + (extract32(desc->address_extension, 16, 12) << 20);
180d3c6369aSKONRAD Frederic         break;
181d3c6369aSKONRAD Frederic     case 1:
182d3c6369aSKONRAD Frederic         addr = desc->source_address2
183d3c6369aSKONRAD Frederic             + (extract32(desc->address_extension_23, 0, 12) << 8);
184d3c6369aSKONRAD Frederic         break;
185d3c6369aSKONRAD Frederic     case 2:
186d3c6369aSKONRAD Frederic         addr = desc->source_address3
187d3c6369aSKONRAD Frederic             + (extract32(desc->address_extension_23, 16, 12) << 20);
188d3c6369aSKONRAD Frederic         break;
189d3c6369aSKONRAD Frederic     case 3:
190d3c6369aSKONRAD Frederic         addr = desc->source_address4
191d3c6369aSKONRAD Frederic             + (extract32(desc->address_extension_45, 0, 12) << 8);
192d3c6369aSKONRAD Frederic         break;
193d3c6369aSKONRAD Frederic     case 4:
194d3c6369aSKONRAD Frederic         addr = desc->source_address5
195d3c6369aSKONRAD Frederic             + (extract32(desc->address_extension_45, 16, 12) << 20);
196d3c6369aSKONRAD Frederic         break;
197d3c6369aSKONRAD Frederic     default:
198d3c6369aSKONRAD Frederic         addr = 0;
199d3c6369aSKONRAD Frederic         break;
200d3c6369aSKONRAD Frederic     }
201d3c6369aSKONRAD Frederic 
202d3c6369aSKONRAD Frederic     return addr;
203d3c6369aSKONRAD Frederic }
204d3c6369aSKONRAD Frederic 
205d3c6369aSKONRAD Frederic static uint32_t xlnx_dpdma_desc_get_transfer_size(DPDMADescriptor *desc)
206d3c6369aSKONRAD Frederic {
207d3c6369aSKONRAD Frederic     return desc->xfer_size;
208d3c6369aSKONRAD Frederic }
209d3c6369aSKONRAD Frederic 
210d3c6369aSKONRAD Frederic static uint32_t xlnx_dpdma_desc_get_line_size(DPDMADescriptor *desc)
211d3c6369aSKONRAD Frederic {
212d3c6369aSKONRAD Frederic     return extract32(desc->line_size_stride, 0, 18);
213d3c6369aSKONRAD Frederic }
214d3c6369aSKONRAD Frederic 
215d3c6369aSKONRAD Frederic static uint32_t xlnx_dpdma_desc_get_line_stride(DPDMADescriptor *desc)
216d3c6369aSKONRAD Frederic {
217d3c6369aSKONRAD Frederic     return extract32(desc->line_size_stride, 18, 14) * 16;
218d3c6369aSKONRAD Frederic }
219d3c6369aSKONRAD Frederic 
220d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_crc_enabled(DPDMADescriptor *desc)
221d3c6369aSKONRAD Frederic {
222d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_ENABLE_CRC) != 0;
223d3c6369aSKONRAD Frederic }
224d3c6369aSKONRAD Frederic 
225d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_check_crc(DPDMADescriptor *desc)
226d3c6369aSKONRAD Frederic {
227d3c6369aSKONRAD Frederic     uint32_t *p = (uint32_t *)desc;
228d3c6369aSKONRAD Frederic     uint32_t crc = 0;
229d3c6369aSKONRAD Frederic     uint8_t i;
230d3c6369aSKONRAD Frederic 
231d3c6369aSKONRAD Frederic     /*
232d3c6369aSKONRAD Frederic      * CRC is calculated on the whole descriptor except the last 32bits word
233d3c6369aSKONRAD Frederic      * using 32bits addition.
234d3c6369aSKONRAD Frederic      */
235d3c6369aSKONRAD Frederic     for (i = 0; i < 15; i++) {
236d3c6369aSKONRAD Frederic         crc += p[i];
237d3c6369aSKONRAD Frederic     }
238d3c6369aSKONRAD Frederic 
239d3c6369aSKONRAD Frederic     return crc == desc->crc;
240d3c6369aSKONRAD Frederic }
241d3c6369aSKONRAD Frederic 
242d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_completion_interrupt(DPDMADescriptor *desc)
243d3c6369aSKONRAD Frederic {
244d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0;
245d3c6369aSKONRAD Frederic }
246d3c6369aSKONRAD Frederic 
247d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_is_valid(DPDMADescriptor *desc)
248d3c6369aSKONRAD Frederic {
249d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_PREAMBLE) == CONTROL_PREAMBLE_VALUE;
250d3c6369aSKONRAD Frederic }
251d3c6369aSKONRAD Frederic 
252d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_is_contiguous(DPDMADescriptor *desc)
253d3c6369aSKONRAD Frederic {
254d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_DESCRIPTOR_MODE) == 0;
255d3c6369aSKONRAD Frederic }
256d3c6369aSKONRAD Frederic 
257d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_update_enabled(DPDMADescriptor *desc)
258d3c6369aSKONRAD Frederic {
259d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0;
260d3c6369aSKONRAD Frederic }
261d3c6369aSKONRAD Frederic 
262d3c6369aSKONRAD Frederic static inline void xlnx_dpdma_desc_set_done(DPDMADescriptor *desc)
263d3c6369aSKONRAD Frederic {
264d3c6369aSKONRAD Frederic     desc->timestamp_msb |= STATUS_DONE;
265d3c6369aSKONRAD Frederic }
266d3c6369aSKONRAD Frederic 
267d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_is_already_done(DPDMADescriptor *desc)
268d3c6369aSKONRAD Frederic {
269d3c6369aSKONRAD Frederic     return (desc->timestamp_msb & STATUS_DONE) != 0;
270d3c6369aSKONRAD Frederic }
271d3c6369aSKONRAD Frederic 
272d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc)
273d3c6369aSKONRAD Frederic {
274d3c6369aSKONRAD Frederic     return (desc->control & DSCR_CTRL_IGNORE_DONE) != 0;
275d3c6369aSKONRAD Frederic }
276d3c6369aSKONRAD Frederic 
277d3c6369aSKONRAD Frederic static const VMStateDescription vmstate_xlnx_dpdma = {
278d3c6369aSKONRAD Frederic     .name = TYPE_XLNX_DPDMA,
279d3c6369aSKONRAD Frederic     .version_id = 1,
280d3c6369aSKONRAD Frederic     .fields = (VMStateField[]) {
281d3c6369aSKONRAD Frederic         VMSTATE_UINT32_ARRAY(registers, XlnxDPDMAState,
282d3c6369aSKONRAD Frederic                              XLNX_DPDMA_REG_ARRAY_SIZE),
283d3c6369aSKONRAD Frederic         VMSTATE_BOOL_ARRAY(operation_finished, XlnxDPDMAState, 6),
284d3c6369aSKONRAD Frederic         VMSTATE_END_OF_LIST()
285d3c6369aSKONRAD Frederic     }
286d3c6369aSKONRAD Frederic };
287d3c6369aSKONRAD Frederic 
288d3c6369aSKONRAD Frederic static void xlnx_dpdma_update_irq(XlnxDPDMAState *s)
289d3c6369aSKONRAD Frederic {
290d3c6369aSKONRAD Frederic     bool flags;
291d3c6369aSKONRAD Frederic 
292d3c6369aSKONRAD Frederic     flags = ((s->registers[DPDMA_ISR] & (~s->registers[DPDMA_IMR]))
293d3c6369aSKONRAD Frederic           || (s->registers[DPDMA_EISR] & (~s->registers[DPDMA_EIMR])));
294d3c6369aSKONRAD Frederic     qemu_set_irq(s->irq, flags);
295d3c6369aSKONRAD Frederic }
296d3c6369aSKONRAD Frederic 
297d3c6369aSKONRAD Frederic static uint64_t xlnx_dpdma_descriptor_start_address(XlnxDPDMAState *s,
298d3c6369aSKONRAD Frederic                                                       uint8_t channel)
299d3c6369aSKONRAD Frederic {
300d3c6369aSKONRAD Frederic     return (s->registers[DPDMA_DSCR_STRT_ADDRE_CH(channel)] << 16)
301d3c6369aSKONRAD Frederic           + s->registers[DPDMA_DSCR_STRT_ADDR_CH(channel)];
302d3c6369aSKONRAD Frederic }
303d3c6369aSKONRAD Frederic 
304d3c6369aSKONRAD Frederic static uint64_t xlnx_dpdma_descriptor_next_address(XlnxDPDMAState *s,
305d3c6369aSKONRAD Frederic                                                      uint8_t channel)
306d3c6369aSKONRAD Frederic {
307d3c6369aSKONRAD Frederic     return ((uint64_t)s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] << 32)
308d3c6369aSKONRAD Frederic            + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)];
309d3c6369aSKONRAD Frederic }
310d3c6369aSKONRAD Frederic 
311d3c6369aSKONRAD Frederic static bool xlnx_dpdma_is_channel_enabled(XlnxDPDMAState *s,
312d3c6369aSKONRAD Frederic                                             uint8_t channel)
313d3c6369aSKONRAD Frederic {
314d3c6369aSKONRAD Frederic     return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_EN) != 0;
315d3c6369aSKONRAD Frederic }
316d3c6369aSKONRAD Frederic 
317d3c6369aSKONRAD Frederic static bool xlnx_dpdma_is_channel_paused(XlnxDPDMAState *s,
318d3c6369aSKONRAD Frederic                                            uint8_t channel)
319d3c6369aSKONRAD Frederic {
320d3c6369aSKONRAD Frederic     return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_PAUSED) != 0;
321d3c6369aSKONRAD Frederic }
322d3c6369aSKONRAD Frederic 
323d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_is_channel_retriggered(XlnxDPDMAState *s,
324d3c6369aSKONRAD Frederic                                                        uint8_t channel)
325d3c6369aSKONRAD Frederic {
326d3c6369aSKONRAD Frederic     /* Clear the retriggered bit after reading it. */
327d3c6369aSKONRAD Frederic     bool channel_is_retriggered = s->registers[DPDMA_GBL]
328d3c6369aSKONRAD Frederic                                 & DPDMA_GBL_RTRG_CH(channel);
329d3c6369aSKONRAD Frederic     s->registers[DPDMA_GBL] &= ~DPDMA_GBL_RTRG_CH(channel);
330d3c6369aSKONRAD Frederic     return channel_is_retriggered;
331d3c6369aSKONRAD Frederic }
332d3c6369aSKONRAD Frederic 
333d3c6369aSKONRAD Frederic static inline bool xlnx_dpdma_is_channel_triggered(XlnxDPDMAState *s,
334d3c6369aSKONRAD Frederic                                                      uint8_t channel)
335d3c6369aSKONRAD Frederic {
336d3c6369aSKONRAD Frederic     return s->registers[DPDMA_GBL] & DPDMA_GBL_TRG_CH(channel);
337d3c6369aSKONRAD Frederic }
338d3c6369aSKONRAD Frederic 
339d3c6369aSKONRAD Frederic static void xlnx_dpdma_update_desc_info(XlnxDPDMAState *s, uint8_t channel,
340d3c6369aSKONRAD Frederic                                           DPDMADescriptor *desc)
341d3c6369aSKONRAD Frederic {
342d3c6369aSKONRAD Frederic     s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] =
343d3c6369aSKONRAD Frederic                                 extract32(desc->address_extension, 0, 16);
344d3c6369aSKONRAD Frederic     s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = desc->next_descriptor;
345d3c6369aSKONRAD Frederic     s->registers[DPDMA_PYLD_CUR_ADDRE_CH(channel)] =
346d3c6369aSKONRAD Frederic                                 extract32(desc->address_extension, 16, 16);
347d3c6369aSKONRAD Frederic     s->registers[DPDMA_PYLD_CUR_ADDR_CH(channel)] = desc->source_address;
348d3c6369aSKONRAD Frederic     s->registers[DPDMA_VDO_CH(channel)] =
349d3c6369aSKONRAD Frederic                                 extract32(desc->line_size_stride, 18, 14)
350d3c6369aSKONRAD Frederic                                 + (extract32(desc->line_size_stride, 0, 18)
351d3c6369aSKONRAD Frederic                                   << 14);
352d3c6369aSKONRAD Frederic     s->registers[DPDMA_PYLD_SZ_CH(channel)] = desc->xfer_size;
353d3c6369aSKONRAD Frederic     s->registers[DPDMA_DSCR_ID_CH(channel)] = desc->descriptor_id;
354d3c6369aSKONRAD Frederic 
355d3c6369aSKONRAD Frederic     /* Compute the status register with the descriptor information. */
356d3c6369aSKONRAD Frederic     s->registers[DPDMA_STATUS_CH(channel)] =
357d3c6369aSKONRAD Frederic                                 extract32(desc->control, 0, 8) << 13;
358d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0) {
359d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_INTR;
360d3c6369aSKONRAD Frederic     }
361d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0) {
362d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_UP;
363d3c6369aSKONRAD Frederic     }
364d3c6369aSKONRAD Frederic     if ((desc->timestamp_msb & STATUS_DONE) != 0) {
365d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_DSCR_DONE;
366d3c6369aSKONRAD Frederic     }
367d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_IGNORE_DONE) != 0) {
368d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_IGNR_DONE;
369d3c6369aSKONRAD Frederic     }
370d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0) {
371d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LDSCR_FRAME;
372d3c6369aSKONRAD Frederic     }
373d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0) {
374d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LAST_DSCR;
375d3c6369aSKONRAD Frederic     }
376d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_ENABLE_CRC) != 0) {
377d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_CRC;
378d3c6369aSKONRAD Frederic     }
379d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_DESCRIPTOR_MODE) != 0) {
380d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_MODE;
381d3c6369aSKONRAD Frederic     }
382d3c6369aSKONRAD Frederic     if ((desc->control & DSCR_CTRL_AXI_BURST_TYPE) != 0) {
383d3c6369aSKONRAD Frederic         s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_BURST_TYPE;
384d3c6369aSKONRAD Frederic     }
385d3c6369aSKONRAD Frederic }
386d3c6369aSKONRAD Frederic 
387d3c6369aSKONRAD Frederic static void xlnx_dpdma_dump_descriptor(DPDMADescriptor *desc)
388d3c6369aSKONRAD Frederic {
389d3c6369aSKONRAD Frederic     if (DEBUG_DPDMA) {
390d3c6369aSKONRAD Frederic         qemu_log("DUMP DESCRIPTOR:\n");
391b42581f5SPhilippe Mathieu-Daudé         qemu_hexdump(stdout, "", desc, sizeof(DPDMADescriptor));
392d3c6369aSKONRAD Frederic     }
393d3c6369aSKONRAD Frederic }
394d3c6369aSKONRAD Frederic 
395d3c6369aSKONRAD Frederic static uint64_t xlnx_dpdma_read(void *opaque, hwaddr offset,
396d3c6369aSKONRAD Frederic                                 unsigned size)
397d3c6369aSKONRAD Frederic {
398d3c6369aSKONRAD Frederic     XlnxDPDMAState *s = XLNX_DPDMA(opaque);
399d3c6369aSKONRAD Frederic 
400d3c6369aSKONRAD Frederic     DPRINTF("read @%" HWADDR_PRIx "\n", offset);
401d3c6369aSKONRAD Frederic     offset = offset >> 2;
402d3c6369aSKONRAD Frederic 
403d3c6369aSKONRAD Frederic     switch (offset) {
404d3c6369aSKONRAD Frederic     /*
405d3c6369aSKONRAD Frederic      * Trying to read a write only register.
406d3c6369aSKONRAD Frederic      */
407d3c6369aSKONRAD Frederic     case DPDMA_GBL:
408d3c6369aSKONRAD Frederic         return 0;
409d3c6369aSKONRAD Frederic     default:
410d3c6369aSKONRAD Frederic         assert(offset <= (0xFFC >> 2));
411d3c6369aSKONRAD Frederic         return s->registers[offset];
412d3c6369aSKONRAD Frederic     }
413d3c6369aSKONRAD Frederic     return 0;
414d3c6369aSKONRAD Frederic }
415d3c6369aSKONRAD Frederic 
416d3c6369aSKONRAD Frederic static void xlnx_dpdma_write(void *opaque, hwaddr offset,
417d3c6369aSKONRAD Frederic                                uint64_t value, unsigned size)
418d3c6369aSKONRAD Frederic {
419d3c6369aSKONRAD Frederic     XlnxDPDMAState *s = XLNX_DPDMA(opaque);
420d3c6369aSKONRAD Frederic 
421d3c6369aSKONRAD Frederic     DPRINTF("write @%" HWADDR_PRIx " = %" PRIx64 "\n", offset, value);
422d3c6369aSKONRAD Frederic     offset = offset >> 2;
423d3c6369aSKONRAD Frederic 
424d3c6369aSKONRAD Frederic     switch (offset) {
425d3c6369aSKONRAD Frederic     case DPDMA_ISR:
426d3c6369aSKONRAD Frederic         s->registers[DPDMA_ISR] &= ~value;
427d3c6369aSKONRAD Frederic         xlnx_dpdma_update_irq(s);
428d3c6369aSKONRAD Frederic         break;
429d3c6369aSKONRAD Frederic     case DPDMA_IEN:
430d3c6369aSKONRAD Frederic         s->registers[DPDMA_IMR] &= ~value;
431d3c6369aSKONRAD Frederic         break;
432d3c6369aSKONRAD Frederic     case DPDMA_IDS:
433d3c6369aSKONRAD Frederic         s->registers[DPDMA_IMR] |= value;
434d3c6369aSKONRAD Frederic         break;
435d3c6369aSKONRAD Frederic     case DPDMA_EISR:
436d3c6369aSKONRAD Frederic         s->registers[DPDMA_EISR] &= ~value;
437d3c6369aSKONRAD Frederic         xlnx_dpdma_update_irq(s);
438d3c6369aSKONRAD Frederic         break;
439d3c6369aSKONRAD Frederic     case DPDMA_EIEN:
440d3c6369aSKONRAD Frederic         s->registers[DPDMA_EIMR] &= ~value;
441d3c6369aSKONRAD Frederic         break;
442d3c6369aSKONRAD Frederic     case DPDMA_EIDS:
443d3c6369aSKONRAD Frederic         s->registers[DPDMA_EIMR] |= value;
444d3c6369aSKONRAD Frederic         break;
445d3c6369aSKONRAD Frederic     case DPDMA_IMR:
446d3c6369aSKONRAD Frederic     case DPDMA_EIMR:
447d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(0):
448d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(1):
449d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(2):
450d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(3):
451d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(4):
452d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDRE_CH(5):
453d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(0):
454d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(1):
455d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(2):
456d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(3):
457d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(4):
458d3c6369aSKONRAD Frederic     case DPDMA_DSCR_NEXT_ADDR_CH(5):
459d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(0):
460d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(1):
461d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(2):
462d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(3):
463d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(4):
464d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDRE_CH(5):
465d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(0):
466d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(1):
467d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(2):
468d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(3):
469d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(4):
470d3c6369aSKONRAD Frederic     case DPDMA_PYLD_CUR_ADDR_CH(5):
471d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(0):
472d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(1):
473d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(2):
474d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(3):
475d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(4):
476d3c6369aSKONRAD Frederic     case DPDMA_STATUS_CH(5):
477d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(0):
478d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(1):
479d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(2):
480d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(3):
481d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(4):
482d3c6369aSKONRAD Frederic     case DPDMA_VDO_CH(5):
483d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(0):
484d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(1):
485d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(2):
486d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(3):
487d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(4):
488d3c6369aSKONRAD Frederic     case DPDMA_PYLD_SZ_CH(5):
489d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(0):
490d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(1):
491d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(2):
492d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(3):
493d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(4):
494d3c6369aSKONRAD Frederic     case DPDMA_DSCR_ID_CH(5):
495d3c6369aSKONRAD Frederic         /*
496d3c6369aSKONRAD Frederic          * Trying to write to a read only register..
497d3c6369aSKONRAD Frederic          */
498d3c6369aSKONRAD Frederic         break;
499d3c6369aSKONRAD Frederic     case DPDMA_GBL:
500d3c6369aSKONRAD Frederic         /*
501d3c6369aSKONRAD Frederic          * This is a write only register so it's read as zero in the read
502d3c6369aSKONRAD Frederic          * callback.
503d3c6369aSKONRAD Frederic          * We store the value anyway so we can know if the channel is
504d3c6369aSKONRAD Frederic          * enabled.
505d3c6369aSKONRAD Frederic          */
506d3c6369aSKONRAD Frederic         s->registers[offset] |= value & 0x00000FFF;
507d3c6369aSKONRAD Frederic         break;
508d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(0):
509d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(1):
510d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(2):
511d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(3):
512d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(4):
513d3c6369aSKONRAD Frederic     case DPDMA_DSCR_STRT_ADDRE_CH(5):
514d3c6369aSKONRAD Frederic         value &= 0x0000FFFF;
515d3c6369aSKONRAD Frederic         s->registers[offset] = value;
516d3c6369aSKONRAD Frederic         break;
517d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(0):
518d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(0);
519d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
520d3c6369aSKONRAD Frederic         s->registers[offset] = value;
521d3c6369aSKONRAD Frederic         break;
522d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(1):
523d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(1);
524d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
525d3c6369aSKONRAD Frederic         s->registers[offset] = value;
526d3c6369aSKONRAD Frederic         break;
527d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(2):
528d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(2);
529d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
530d3c6369aSKONRAD Frederic         s->registers[offset] = value;
531d3c6369aSKONRAD Frederic         break;
532d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(3):
533d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(3);
534d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
535d3c6369aSKONRAD Frederic         s->registers[offset] = value;
536d3c6369aSKONRAD Frederic         break;
537d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(4):
538d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(4);
539d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
540d3c6369aSKONRAD Frederic         s->registers[offset] = value;
541d3c6369aSKONRAD Frederic         break;
542d3c6369aSKONRAD Frederic     case DPDMA_CNTL_CH(5):
543d3c6369aSKONRAD Frederic         s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(5);
544d3c6369aSKONRAD Frederic         value &= 0x3FFFFFFF;
545d3c6369aSKONRAD Frederic         s->registers[offset] = value;
546d3c6369aSKONRAD Frederic         break;
547d3c6369aSKONRAD Frederic     default:
548d3c6369aSKONRAD Frederic         assert(offset <= (0xFFC >> 2));
549d3c6369aSKONRAD Frederic         s->registers[offset] = value;
550d3c6369aSKONRAD Frederic         break;
551d3c6369aSKONRAD Frederic     }
552d3c6369aSKONRAD Frederic }
553d3c6369aSKONRAD Frederic 
554d3c6369aSKONRAD Frederic static const MemoryRegionOps dma_ops = {
555d3c6369aSKONRAD Frederic     .read = xlnx_dpdma_read,
556d3c6369aSKONRAD Frederic     .write = xlnx_dpdma_write,
557d3c6369aSKONRAD Frederic     .endianness = DEVICE_NATIVE_ENDIAN,
558d3c6369aSKONRAD Frederic     .valid = {
559d3c6369aSKONRAD Frederic         .min_access_size = 4,
560d3c6369aSKONRAD Frederic         .max_access_size = 4,
561d3c6369aSKONRAD Frederic     },
562d3c6369aSKONRAD Frederic     .impl = {
563d3c6369aSKONRAD Frederic         .min_access_size = 4,
564d3c6369aSKONRAD Frederic         .max_access_size = 4,
565d3c6369aSKONRAD Frederic     },
566d3c6369aSKONRAD Frederic };
567d3c6369aSKONRAD Frederic 
568d3c6369aSKONRAD Frederic static void xlnx_dpdma_init(Object *obj)
569d3c6369aSKONRAD Frederic {
570d3c6369aSKONRAD Frederic     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
571d3c6369aSKONRAD Frederic     XlnxDPDMAState *s = XLNX_DPDMA(obj);
572d3c6369aSKONRAD Frederic 
573d3c6369aSKONRAD Frederic     memory_region_init_io(&s->iomem, obj, &dma_ops, s,
574d3c6369aSKONRAD Frederic                           TYPE_XLNX_DPDMA, 0x1000);
575d3c6369aSKONRAD Frederic     sysbus_init_mmio(sbd, &s->iomem);
576d3c6369aSKONRAD Frederic     sysbus_init_irq(sbd, &s->irq);
577d3c6369aSKONRAD Frederic }
578d3c6369aSKONRAD Frederic 
579d3c6369aSKONRAD Frederic static void xlnx_dpdma_reset(DeviceState *dev)
580d3c6369aSKONRAD Frederic {
581d3c6369aSKONRAD Frederic     XlnxDPDMAState *s = XLNX_DPDMA(dev);
582d3c6369aSKONRAD Frederic     size_t i;
583d3c6369aSKONRAD Frederic 
584d3c6369aSKONRAD Frederic     memset(s->registers, 0, sizeof(s->registers));
585d3c6369aSKONRAD Frederic     s->registers[DPDMA_IMR] =  0x07FFFFFF;
586d3c6369aSKONRAD Frederic     s->registers[DPDMA_EIMR] = 0xFFFFFFFF;
587d3c6369aSKONRAD Frederic     s->registers[DPDMA_ALC0_MIN] = 0x0000FFFF;
588d3c6369aSKONRAD Frederic     s->registers[DPDMA_ALC1_MIN] = 0x0000FFFF;
589d3c6369aSKONRAD Frederic 
590d3c6369aSKONRAD Frederic     for (i = 0; i < 6; i++) {
591d3c6369aSKONRAD Frederic         s->data[i] = NULL;
592d3c6369aSKONRAD Frederic         s->operation_finished[i] = true;
593d3c6369aSKONRAD Frederic     }
594d3c6369aSKONRAD Frederic }
595d3c6369aSKONRAD Frederic 
596d3c6369aSKONRAD Frederic static void xlnx_dpdma_class_init(ObjectClass *oc, void *data)
597d3c6369aSKONRAD Frederic {
598d3c6369aSKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(oc);
599d3c6369aSKONRAD Frederic 
600d3c6369aSKONRAD Frederic     dc->vmsd = &vmstate_xlnx_dpdma;
601d3c6369aSKONRAD Frederic     dc->reset = xlnx_dpdma_reset;
602d3c6369aSKONRAD Frederic }
603d3c6369aSKONRAD Frederic 
604d3c6369aSKONRAD Frederic static const TypeInfo xlnx_dpdma_info = {
605d3c6369aSKONRAD Frederic     .name          = TYPE_XLNX_DPDMA,
606d3c6369aSKONRAD Frederic     .parent        = TYPE_SYS_BUS_DEVICE,
607d3c6369aSKONRAD Frederic     .instance_size = sizeof(XlnxDPDMAState),
608d3c6369aSKONRAD Frederic     .instance_init = xlnx_dpdma_init,
609d3c6369aSKONRAD Frederic     .class_init    = xlnx_dpdma_class_init,
610d3c6369aSKONRAD Frederic };
611d3c6369aSKONRAD Frederic 
612d3c6369aSKONRAD Frederic static void xlnx_dpdma_register_types(void)
613d3c6369aSKONRAD Frederic {
614d3c6369aSKONRAD Frederic     type_register_static(&xlnx_dpdma_info);
615d3c6369aSKONRAD Frederic }
616d3c6369aSKONRAD Frederic 
617d3c6369aSKONRAD Frederic size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
618d3c6369aSKONRAD Frederic                                     bool one_desc)
619d3c6369aSKONRAD Frederic {
620d3c6369aSKONRAD Frederic     uint64_t desc_addr;
621d3c6369aSKONRAD Frederic     uint64_t source_addr[6];
622d3c6369aSKONRAD Frederic     DPDMADescriptor desc;
623d3c6369aSKONRAD Frederic     bool done = false;
624d3c6369aSKONRAD Frederic     size_t ptr = 0;
625d3c6369aSKONRAD Frederic 
626d3c6369aSKONRAD Frederic     assert(channel <= 5);
627d3c6369aSKONRAD Frederic 
628d3c6369aSKONRAD Frederic     DPRINTF("start dpdma channel 0x%" PRIX8 "\n", channel);
629d3c6369aSKONRAD Frederic 
630d3c6369aSKONRAD Frederic     if (!xlnx_dpdma_is_channel_triggered(s, channel)) {
631d3c6369aSKONRAD Frederic         DPRINTF("Channel isn't triggered..\n");
632d3c6369aSKONRAD Frederic         return 0;
633d3c6369aSKONRAD Frederic     }
634d3c6369aSKONRAD Frederic 
635d3c6369aSKONRAD Frederic     if (!xlnx_dpdma_is_channel_enabled(s, channel)) {
636d3c6369aSKONRAD Frederic         DPRINTF("Channel isn't enabled..\n");
637d3c6369aSKONRAD Frederic         return 0;
638d3c6369aSKONRAD Frederic     }
639d3c6369aSKONRAD Frederic 
640d3c6369aSKONRAD Frederic     if (xlnx_dpdma_is_channel_paused(s, channel)) {
641d3c6369aSKONRAD Frederic         DPRINTF("Channel is paused..\n");
642d3c6369aSKONRAD Frederic         return 0;
643d3c6369aSKONRAD Frederic     }
644d3c6369aSKONRAD Frederic 
645d3c6369aSKONRAD Frederic     do {
646d3c6369aSKONRAD Frederic         if ((s->operation_finished[channel])
647d3c6369aSKONRAD Frederic           || xlnx_dpdma_is_channel_retriggered(s, channel)) {
648d3c6369aSKONRAD Frederic             desc_addr = xlnx_dpdma_descriptor_start_address(s, channel);
649d3c6369aSKONRAD Frederic             s->operation_finished[channel] = false;
650d3c6369aSKONRAD Frederic         } else {
651d3c6369aSKONRAD Frederic             desc_addr = xlnx_dpdma_descriptor_next_address(s, channel);
652d3c6369aSKONRAD Frederic         }
653d3c6369aSKONRAD Frederic 
654d3c6369aSKONRAD Frederic         if (dma_memory_read(&address_space_memory, desc_addr, &desc,
655*ba06fe8aSPhilippe Mathieu-Daudé                             sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED)) {
656d3c6369aSKONRAD Frederic             s->registers[DPDMA_EISR] |= ((1 << 1) << channel);
657d3c6369aSKONRAD Frederic             xlnx_dpdma_update_irq(s);
658d3c6369aSKONRAD Frederic             s->operation_finished[channel] = true;
659d3c6369aSKONRAD Frederic             DPRINTF("Can't get the descriptor.\n");
660d3c6369aSKONRAD Frederic             break;
661d3c6369aSKONRAD Frederic         }
662d3c6369aSKONRAD Frederic 
663d3c6369aSKONRAD Frederic         xlnx_dpdma_update_desc_info(s, channel, &desc);
664d3c6369aSKONRAD Frederic 
665d3c6369aSKONRAD Frederic #ifdef DEBUG_DPDMA
666d3c6369aSKONRAD Frederic         xlnx_dpdma_dump_descriptor(&desc);
667d3c6369aSKONRAD Frederic #endif
668d3c6369aSKONRAD Frederic 
669d3c6369aSKONRAD Frederic         DPRINTF("location of the descriptor: %" PRIx64 "\n", desc_addr);
670d3c6369aSKONRAD Frederic         if (!xlnx_dpdma_desc_is_valid(&desc)) {
671d3c6369aSKONRAD Frederic             s->registers[DPDMA_EISR] |= ((1 << 7) << channel);
672d3c6369aSKONRAD Frederic             xlnx_dpdma_update_irq(s);
673d3c6369aSKONRAD Frederic             s->operation_finished[channel] = true;
674d3c6369aSKONRAD Frederic             DPRINTF("Invalid descriptor..\n");
675d3c6369aSKONRAD Frederic             break;
676d3c6369aSKONRAD Frederic         }
677d3c6369aSKONRAD Frederic 
678d3c6369aSKONRAD Frederic         if (xlnx_dpdma_desc_crc_enabled(&desc)
679d3c6369aSKONRAD Frederic             && !xlnx_dpdma_desc_check_crc(&desc)) {
680d3c6369aSKONRAD Frederic             s->registers[DPDMA_EISR] |= ((1 << 13) << channel);
681d3c6369aSKONRAD Frederic             xlnx_dpdma_update_irq(s);
682d3c6369aSKONRAD Frederic             s->operation_finished[channel] = true;
683d3c6369aSKONRAD Frederic             DPRINTF("Bad CRC for descriptor..\n");
684d3c6369aSKONRAD Frederic             break;
685d3c6369aSKONRAD Frederic         }
686d3c6369aSKONRAD Frederic 
687d3c6369aSKONRAD Frederic         if (xlnx_dpdma_desc_is_already_done(&desc)
688d3c6369aSKONRAD Frederic             && !xlnx_dpdma_desc_ignore_done_bit(&desc)) {
689d3c6369aSKONRAD Frederic             /* We are trying to process an already processed descriptor. */
690d3c6369aSKONRAD Frederic             s->registers[DPDMA_EISR] |= ((1 << 25) << channel);
691d3c6369aSKONRAD Frederic             xlnx_dpdma_update_irq(s);
692d3c6369aSKONRAD Frederic             s->operation_finished[channel] = true;
693d3c6369aSKONRAD Frederic             DPRINTF("Already processed descriptor..\n");
694d3c6369aSKONRAD Frederic             break;
695d3c6369aSKONRAD Frederic         }
696d3c6369aSKONRAD Frederic 
697d3c6369aSKONRAD Frederic         done = xlnx_dpdma_desc_is_last(&desc)
698d3c6369aSKONRAD Frederic              || xlnx_dpdma_desc_is_last_of_frame(&desc);
699d3c6369aSKONRAD Frederic 
700d3c6369aSKONRAD Frederic         s->operation_finished[channel] = done;
701d3c6369aSKONRAD Frederic         if (s->data[channel]) {
702d3c6369aSKONRAD Frederic             int64_t transfer_len = xlnx_dpdma_desc_get_transfer_size(&desc);
703d3c6369aSKONRAD Frederic             uint32_t line_size = xlnx_dpdma_desc_get_line_size(&desc);
704d3c6369aSKONRAD Frederic             uint32_t line_stride = xlnx_dpdma_desc_get_line_stride(&desc);
705d3c6369aSKONRAD Frederic             if (xlnx_dpdma_desc_is_contiguous(&desc)) {
706d3c6369aSKONRAD Frederic                 source_addr[0] = xlnx_dpdma_desc_get_source_address(&desc, 0);
707d3c6369aSKONRAD Frederic                 while (transfer_len != 0) {
708d3c6369aSKONRAD Frederic                     if (dma_memory_read(&address_space_memory,
709d3c6369aSKONRAD Frederic                                         source_addr[0],
710d3c6369aSKONRAD Frederic                                         &s->data[channel][ptr],
711*ba06fe8aSPhilippe Mathieu-Daudé                                         line_size,
712*ba06fe8aSPhilippe Mathieu-Daudé                                         MEMTXATTRS_UNSPECIFIED)) {
713d3c6369aSKONRAD Frederic                         s->registers[DPDMA_ISR] |= ((1 << 12) << channel);
714d3c6369aSKONRAD Frederic                         xlnx_dpdma_update_irq(s);
715d3c6369aSKONRAD Frederic                         DPRINTF("Can't get data.\n");
716d3c6369aSKONRAD Frederic                         break;
717d3c6369aSKONRAD Frederic                     }
718d3c6369aSKONRAD Frederic                     ptr += line_size;
719d3c6369aSKONRAD Frederic                     transfer_len -= line_size;
720d3c6369aSKONRAD Frederic                     source_addr[0] += line_stride;
721d3c6369aSKONRAD Frederic                 }
722d3c6369aSKONRAD Frederic             } else {
723d3c6369aSKONRAD Frederic                 DPRINTF("Source address:\n");
724d3c6369aSKONRAD Frederic                 int frag;
725d3c6369aSKONRAD Frederic                 for (frag = 0; frag < 5; frag++) {
726d3c6369aSKONRAD Frederic                     source_addr[frag] =
727d3c6369aSKONRAD Frederic                           xlnx_dpdma_desc_get_source_address(&desc, frag);
728d3c6369aSKONRAD Frederic                     DPRINTF("Fragment %u: %" PRIx64 "\n", frag + 1,
729d3c6369aSKONRAD Frederic                             source_addr[frag]);
730d3c6369aSKONRAD Frederic                 }
731d3c6369aSKONRAD Frederic 
732d3c6369aSKONRAD Frederic                 frag = 0;
733d3c6369aSKONRAD Frederic                 while ((transfer_len < 0) && (frag < 5)) {
734d3c6369aSKONRAD Frederic                     size_t fragment_len = DPDMA_FRAG_MAX_SZ
735d3c6369aSKONRAD Frederic                                     - (source_addr[frag] % DPDMA_FRAG_MAX_SZ);
736d3c6369aSKONRAD Frederic 
737d3c6369aSKONRAD Frederic                     if (dma_memory_read(&address_space_memory,
738d3c6369aSKONRAD Frederic                                         source_addr[frag],
739d3c6369aSKONRAD Frederic                                         &(s->data[channel][ptr]),
740*ba06fe8aSPhilippe Mathieu-Daudé                                         fragment_len,
741*ba06fe8aSPhilippe Mathieu-Daudé                                         MEMTXATTRS_UNSPECIFIED)) {
742d3c6369aSKONRAD Frederic                         s->registers[DPDMA_ISR] |= ((1 << 12) << channel);
743d3c6369aSKONRAD Frederic                         xlnx_dpdma_update_irq(s);
744d3c6369aSKONRAD Frederic                         DPRINTF("Can't get data.\n");
745d3c6369aSKONRAD Frederic                         break;
746d3c6369aSKONRAD Frederic                     }
747d3c6369aSKONRAD Frederic                     ptr += fragment_len;
748d3c6369aSKONRAD Frederic                     transfer_len -= fragment_len;
749d3c6369aSKONRAD Frederic                     frag += 1;
750d3c6369aSKONRAD Frederic                 }
751d3c6369aSKONRAD Frederic             }
752d3c6369aSKONRAD Frederic         }
753d3c6369aSKONRAD Frederic 
754d3c6369aSKONRAD Frederic         if (xlnx_dpdma_desc_update_enabled(&desc)) {
755d3c6369aSKONRAD Frederic             /* The descriptor need to be updated when it's completed. */
756d3c6369aSKONRAD Frederic             DPRINTF("update the descriptor with the done flag set.\n");
757d3c6369aSKONRAD Frederic             xlnx_dpdma_desc_set_done(&desc);
758d3c6369aSKONRAD Frederic             dma_memory_write(&address_space_memory, desc_addr, &desc,
759*ba06fe8aSPhilippe Mathieu-Daudé                              sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED);
760d3c6369aSKONRAD Frederic         }
761d3c6369aSKONRAD Frederic 
762d3c6369aSKONRAD Frederic         if (xlnx_dpdma_desc_completion_interrupt(&desc)) {
763d3c6369aSKONRAD Frederic             DPRINTF("completion interrupt enabled!\n");
764d3c6369aSKONRAD Frederic             s->registers[DPDMA_ISR] |= (1 << channel);
765d3c6369aSKONRAD Frederic             xlnx_dpdma_update_irq(s);
766d3c6369aSKONRAD Frederic         }
767d3c6369aSKONRAD Frederic 
768d3c6369aSKONRAD Frederic     } while (!done && !one_desc);
769d3c6369aSKONRAD Frederic 
770d3c6369aSKONRAD Frederic     return ptr;
771d3c6369aSKONRAD Frederic }
772d3c6369aSKONRAD Frederic 
773d3c6369aSKONRAD Frederic void xlnx_dpdma_set_host_data_location(XlnxDPDMAState *s, uint8_t channel,
774d3c6369aSKONRAD Frederic                                          void *p)
775d3c6369aSKONRAD Frederic {
776d3c6369aSKONRAD Frederic     if (!s) {
777d3c6369aSKONRAD Frederic         qemu_log_mask(LOG_UNIMP, "DPDMA client not attached to valid DPDMA"
778d3c6369aSKONRAD Frederic                       " instance\n");
779d3c6369aSKONRAD Frederic         return;
780d3c6369aSKONRAD Frederic     }
781d3c6369aSKONRAD Frederic 
782d3c6369aSKONRAD Frederic     assert(channel <= 5);
783d3c6369aSKONRAD Frederic     s->data[channel] = p;
784d3c6369aSKONRAD Frederic }
785d3c6369aSKONRAD Frederic 
786d3c6369aSKONRAD Frederic void xlnx_dpdma_trigger_vsync_irq(XlnxDPDMAState *s)
787d3c6369aSKONRAD Frederic {
788d3c6369aSKONRAD Frederic     s->registers[DPDMA_ISR] |= (1 << 27);
789d3c6369aSKONRAD Frederic     xlnx_dpdma_update_irq(s);
790d3c6369aSKONRAD Frederic }
791d3c6369aSKONRAD Frederic 
792d3c6369aSKONRAD Frederic type_init(xlnx_dpdma_register_types)
793