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"
19 #include "hw/pci/pci.h"
22 #include "rocker_hw.h"
23 #include "rocker_desc.h"
35 desc_ring_consume
*consume
;
46 uint16_t desc_buf_size(DescInfo
*info
)
48 return le16_to_cpu(info
->desc
.buf_size
);
51 uint16_t desc_tlv_size(DescInfo
*info
)
53 return le16_to_cpu(info
->desc
.tlv_size
);
56 char *desc_get_buf(DescInfo
*info
, bool read_only
)
58 PCIDevice
*dev
= PCI_DEVICE(info
->ring
->r
);
59 size_t size
= read_only
? le16_to_cpu(info
->desc
.tlv_size
) :
60 le16_to_cpu(info
->desc
.buf_size
);
62 if (size
> info
->buf_size
) {
63 info
->buf
= g_realloc(info
->buf
, size
);
64 info
->buf_size
= size
;
67 pci_dma_read(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, size
);
72 int desc_set_buf(DescInfo
*info
, size_t tlv_size
)
74 PCIDevice
*dev
= PCI_DEVICE(info
->ring
->r
);
76 if (tlv_size
> info
->buf_size
) {
77 DPRINTF("ERROR: trying to write more to desc buf than it "
78 "can hold buf_size %zu tlv_size %zu\n",
79 info
->buf_size
, tlv_size
);
80 return -ROCKER_EMSGSIZE
;
83 info
->desc
.tlv_size
= cpu_to_le16(tlv_size
);
84 pci_dma_write(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, tlv_size
);
89 DescRing
*desc_get_ring(DescInfo
*info
)
94 int desc_ring_index(DescRing
*ring
)
99 static bool desc_ring_empty(DescRing
*ring
)
101 return ring
->head
== ring
->tail
;
104 bool desc_ring_set_base_addr(DescRing
*ring
, uint64_t base_addr
)
106 if (base_addr
& 0x7) {
107 DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
108 ") not 8-byte aligned\n", ring
->index
, base_addr
);
112 ring
->base_addr
= base_addr
;
117 uint64_t desc_ring_get_base_addr(DescRing
*ring
)
119 return ring
->base_addr
;
122 bool desc_ring_set_size(DescRing
*ring
, uint32_t size
)
126 if (size
< 2 || size
> 0x10000 || (size
& (size
- 1))) {
127 DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
128 "or in range [2, 64K]\n", ring
->index
, size
);
132 for (i
= 0; i
< ring
->size
; i
++) {
133 g_free(ring
->info
[i
].buf
);
137 ring
->head
= ring
->tail
= 0;
139 ring
->info
= g_renew(DescInfo
, ring
->info
, size
);
141 memset(ring
->info
, 0, size
* sizeof(DescInfo
));
143 for (i
= 0; i
< size
; i
++) {
144 ring
->info
[i
].ring
= ring
;
150 uint32_t desc_ring_get_size(DescRing
*ring
)
155 static DescInfo
*desc_read(DescRing
*ring
, uint32_t index
)
157 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
158 DescInfo
*info
= &ring
->info
[index
];
159 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
161 pci_dma_read(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
166 static void desc_write(DescRing
*ring
, uint32_t index
)
168 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
169 DescInfo
*info
= &ring
->info
[index
];
170 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
172 pci_dma_write(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
175 static bool desc_ring_base_addr_check(DescRing
*ring
)
177 if (!ring
->base_addr
) {
178 DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
185 static DescInfo
*__desc_ring_fetch_desc(DescRing
*ring
)
187 return desc_read(ring
, ring
->tail
);
190 DescInfo
*desc_ring_fetch_desc(DescRing
*ring
)
192 if (desc_ring_empty(ring
) || !desc_ring_base_addr_check(ring
)) {
196 return desc_read(ring
, ring
->tail
);
199 static bool __desc_ring_post_desc(DescRing
*ring
, int err
)
201 uint16_t comp_err
= 0x8000 | (uint16_t)-err
;
202 DescInfo
*info
= &ring
->info
[ring
->tail
];
204 info
->desc
.comp_err
= cpu_to_le16(comp_err
);
205 desc_write(ring
, ring
->tail
);
206 ring
->tail
= (ring
->tail
+ 1) % ring
->size
;
208 /* return true if starting credit count */
210 return ring
->credits
++ == 0;
213 bool desc_ring_post_desc(DescRing
*ring
, int err
)
215 if (desc_ring_empty(ring
)) {
216 DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
221 if (!desc_ring_base_addr_check(ring
)) {
225 return __desc_ring_post_desc(ring
, err
);
228 static bool ring_pump(DescRing
*ring
)
234 /* If the ring has a consumer, call consumer for each
235 * desc starting at tail and stopping when tail reaches
236 * head (the empty ring condition).
240 while (ring
->head
!= ring
->tail
) {
241 info
= __desc_ring_fetch_desc(ring
);
242 err
= ring
->consume(ring
->r
, info
);
243 if (__desc_ring_post_desc(ring
, err
)) {
252 bool desc_ring_set_head(DescRing
*ring
, uint32_t new)
254 uint32_t tail
= ring
->tail
;
255 uint32_t head
= ring
->head
;
257 if (!desc_ring_base_addr_check(ring
)) {
261 if (new >= ring
->size
) {
262 DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
263 new, ring
->index
, ring
->size
);
267 if (((head
< tail
) && ((new >= tail
) || (new < head
))) ||
268 ((head
> tail
) && ((new >= tail
) && (new < head
)))) {
269 DPRINTF("ERROR: trying to wrap ring[%d] "
270 "(head %d, tail %d, new head %d)\n",
271 ring
->index
, head
, tail
, new);
275 if (new == ring
->head
) {
276 DPRINTF("WARNING: setting head (%d) to current head position\n", new);
281 return ring_pump(ring
);
284 uint32_t desc_ring_get_head(DescRing
*ring
)
289 uint32_t desc_ring_get_tail(DescRing
*ring
)
294 void desc_ring_set_ctrl(DescRing
*ring
, uint32_t val
)
296 if (val
& ROCKER_DMA_DESC_CTRL_RESET
) {
297 DPRINTF("ring[%d] resetting\n", ring
->index
);
298 desc_ring_reset(ring
);
302 bool desc_ring_ret_credits(DescRing
*ring
, uint32_t credits
)
304 if (credits
> ring
->credits
) {
305 DPRINTF("ERROR: trying to return more credits (%d) "
306 "than are outstanding (%d)\n", credits
, ring
->credits
);
311 ring
->credits
-= credits
;
313 /* return true if credits are still outstanding */
315 return ring
->credits
> 0;
318 uint32_t desc_ring_get_credits(DescRing
*ring
)
320 return ring
->credits
;
323 void desc_ring_set_consume(DescRing
*ring
, desc_ring_consume
*consume
,
326 ring
->consume
= consume
;
327 ring
->msix_vector
= vector
;
330 unsigned desc_ring_get_msix_vector(DescRing
*ring
)
332 return ring
->msix_vector
;
335 DescRing
*desc_ring_alloc(Rocker
*r
, int index
)
339 ring
= g_new0(DescRing
, 1);
347 void desc_ring_free(DescRing
*ring
)
353 void desc_ring_reset(DescRing
*ring
)