2 * QEMU rocker switch emulation - Descriptor ring support
4 * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include "qemu/osdep.h"
20 #include "hw/pci/pci.h"
23 #include "rocker_hw.h"
24 #include "rocker_desc.h"
36 desc_ring_consume
*consume
;
47 uint16_t desc_buf_size(DescInfo
*info
)
49 return le16_to_cpu(info
->desc
.buf_size
);
52 uint16_t desc_tlv_size(DescInfo
*info
)
54 return le16_to_cpu(info
->desc
.tlv_size
);
57 char *desc_get_buf(DescInfo
*info
, bool read_only
)
59 PCIDevice
*dev
= PCI_DEVICE(info
->ring
->r
);
60 size_t size
= read_only
? le16_to_cpu(info
->desc
.tlv_size
) :
61 le16_to_cpu(info
->desc
.buf_size
);
63 if (size
> info
->buf_size
) {
64 info
->buf
= g_realloc(info
->buf
, size
);
65 info
->buf_size
= size
;
68 pci_dma_read(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, size
);
73 int desc_set_buf(DescInfo
*info
, size_t tlv_size
)
75 PCIDevice
*dev
= PCI_DEVICE(info
->ring
->r
);
77 if (tlv_size
> info
->buf_size
) {
78 DPRINTF("ERROR: trying to write more to desc buf than it "
79 "can hold buf_size %zu tlv_size %zu\n",
80 info
->buf_size
, tlv_size
);
81 return -ROCKER_EMSGSIZE
;
84 info
->desc
.tlv_size
= cpu_to_le16(tlv_size
);
85 pci_dma_write(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, tlv_size
);
90 DescRing
*desc_get_ring(DescInfo
*info
)
95 int desc_ring_index(DescRing
*ring
)
100 static bool desc_ring_empty(DescRing
*ring
)
102 return ring
->head
== ring
->tail
;
105 bool desc_ring_set_base_addr(DescRing
*ring
, uint64_t base_addr
)
107 if (base_addr
& 0x7) {
108 DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
109 ") not 8-byte aligned\n", ring
->index
, base_addr
);
113 ring
->base_addr
= base_addr
;
118 uint64_t desc_ring_get_base_addr(DescRing
*ring
)
120 return ring
->base_addr
;
123 bool desc_ring_set_size(DescRing
*ring
, uint32_t size
)
127 if (size
< 2 || size
> 0x10000 || (size
& (size
- 1))) {
128 DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
129 "or in range [2, 64K]\n", ring
->index
, size
);
133 for (i
= 0; i
< ring
->size
; i
++) {
134 g_free(ring
->info
[i
].buf
);
138 ring
->head
= ring
->tail
= 0;
140 ring
->info
= g_renew(DescInfo
, ring
->info
, size
);
142 memset(ring
->info
, 0, size
* sizeof(DescInfo
));
144 for (i
= 0; i
< size
; i
++) {
145 ring
->info
[i
].ring
= ring
;
151 uint32_t desc_ring_get_size(DescRing
*ring
)
156 static DescInfo
*desc_read(DescRing
*ring
, uint32_t index
)
158 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
159 DescInfo
*info
= &ring
->info
[index
];
160 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
162 pci_dma_read(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
167 static void desc_write(DescRing
*ring
, uint32_t index
)
169 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
170 DescInfo
*info
= &ring
->info
[index
];
171 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
173 pci_dma_write(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
176 static bool desc_ring_base_addr_check(DescRing
*ring
)
178 if (!ring
->base_addr
) {
179 DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
186 static DescInfo
*__desc_ring_fetch_desc(DescRing
*ring
)
188 return desc_read(ring
, ring
->tail
);
191 DescInfo
*desc_ring_fetch_desc(DescRing
*ring
)
193 if (desc_ring_empty(ring
) || !desc_ring_base_addr_check(ring
)) {
197 return desc_read(ring
, ring
->tail
);
200 static bool __desc_ring_post_desc(DescRing
*ring
, int err
)
202 uint16_t comp_err
= 0x8000 | (uint16_t)-err
;
203 DescInfo
*info
= &ring
->info
[ring
->tail
];
205 info
->desc
.comp_err
= cpu_to_le16(comp_err
);
206 desc_write(ring
, ring
->tail
);
207 ring
->tail
= (ring
->tail
+ 1) % ring
->size
;
209 /* return true if starting credit count */
211 return ring
->credits
++ == 0;
214 bool desc_ring_post_desc(DescRing
*ring
, int err
)
216 if (desc_ring_empty(ring
)) {
217 DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
222 if (!desc_ring_base_addr_check(ring
)) {
226 return __desc_ring_post_desc(ring
, err
);
229 static bool ring_pump(DescRing
*ring
)
235 /* If the ring has a consumer, call consumer for each
236 * desc starting at tail and stopping when tail reaches
237 * head (the empty ring condition).
241 while (ring
->head
!= ring
->tail
) {
242 info
= __desc_ring_fetch_desc(ring
);
243 err
= ring
->consume(ring
->r
, info
);
244 if (__desc_ring_post_desc(ring
, err
)) {
253 bool desc_ring_set_head(DescRing
*ring
, uint32_t new)
255 uint32_t tail
= ring
->tail
;
256 uint32_t head
= ring
->head
;
258 if (!desc_ring_base_addr_check(ring
)) {
262 if (new >= ring
->size
) {
263 DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
264 new, ring
->index
, ring
->size
);
268 if (((head
< tail
) && ((new >= tail
) || (new < head
))) ||
269 ((head
> tail
) && ((new >= tail
) && (new < head
)))) {
270 DPRINTF("ERROR: trying to wrap ring[%d] "
271 "(head %d, tail %d, new head %d)\n",
272 ring
->index
, head
, tail
, new);
276 if (new == ring
->head
) {
277 DPRINTF("WARNING: setting head (%d) to current head position\n", new);
282 return ring_pump(ring
);
285 uint32_t desc_ring_get_head(DescRing
*ring
)
290 uint32_t desc_ring_get_tail(DescRing
*ring
)
295 void desc_ring_set_ctrl(DescRing
*ring
, uint32_t val
)
297 if (val
& ROCKER_DMA_DESC_CTRL_RESET
) {
298 DPRINTF("ring[%d] resetting\n", ring
->index
);
299 desc_ring_reset(ring
);
303 bool desc_ring_ret_credits(DescRing
*ring
, uint32_t credits
)
305 if (credits
> ring
->credits
) {
306 DPRINTF("ERROR: trying to return more credits (%d) "
307 "than are outstanding (%d)\n", credits
, ring
->credits
);
312 ring
->credits
-= credits
;
314 /* return true if credits are still outstanding */
316 return ring
->credits
> 0;
319 uint32_t desc_ring_get_credits(DescRing
*ring
)
321 return ring
->credits
;
324 void desc_ring_set_consume(DescRing
*ring
, desc_ring_consume
*consume
,
327 ring
->consume
= consume
;
328 ring
->msix_vector
= vector
;
331 unsigned desc_ring_get_msix_vector(DescRing
*ring
)
333 return ring
->msix_vector
;
336 DescRing
*desc_ring_alloc(Rocker
*r
, int index
)
340 ring
= g_new0(DescRing
, 1);
348 void desc_ring_free(DescRing
*ring
)
354 void desc_ring_reset(DescRing
*ring
)