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.
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
;
71 if (pci_dma_read(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, size
)) {
78 int desc_set_buf(DescInfo
*info
, size_t tlv_size
)
80 PCIDevice
*dev
= PCI_DEVICE(info
->ring
->r
);
82 if (tlv_size
> info
->buf_size
) {
83 DPRINTF("ERROR: trying to write more to desc buf than it "
84 "can hold buf_size %zu tlv_size %zu\n",
85 info
->buf_size
, tlv_size
);
86 return -ROCKER_EMSGSIZE
;
89 info
->desc
.tlv_size
= cpu_to_le16(tlv_size
);
90 pci_dma_write(dev
, le64_to_cpu(info
->desc
.buf_addr
), info
->buf
, tlv_size
);
95 DescRing
*desc_get_ring(DescInfo
*info
)
100 int desc_ring_index(DescRing
*ring
)
105 static bool desc_ring_empty(DescRing
*ring
)
107 return ring
->head
== ring
->tail
;
110 bool desc_ring_set_base_addr(DescRing
*ring
, uint64_t base_addr
)
112 if (base_addr
& 0x7) {
113 DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
114 ") not 8-byte aligned\n", ring
->index
, base_addr
);
118 ring
->base_addr
= base_addr
;
123 uint64_t desc_ring_get_base_addr(DescRing
*ring
)
125 return ring
->base_addr
;
128 bool desc_ring_set_size(DescRing
*ring
, uint32_t size
)
132 if (size
< 2 || size
> 0x10000 || (size
& (size
- 1))) {
133 DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
134 "or in range [2, 64K]\n", ring
->index
, size
);
138 for (i
= 0; i
< ring
->size
; i
++) {
139 g_free(ring
->info
[i
].buf
);
143 ring
->head
= ring
->tail
= 0;
145 ring
->info
= g_realloc(ring
->info
, size
* sizeof(DescInfo
));
150 memset(ring
->info
, 0, size
* sizeof(DescInfo
));
152 for (i
= 0; i
< size
; i
++) {
153 ring
->info
[i
].ring
= ring
;
159 uint32_t desc_ring_get_size(DescRing
*ring
)
164 static DescInfo
*desc_read(DescRing
*ring
, uint32_t index
)
166 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
167 DescInfo
*info
= &ring
->info
[index
];
168 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
170 pci_dma_read(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
175 static void desc_write(DescRing
*ring
, uint32_t index
)
177 PCIDevice
*dev
= PCI_DEVICE(ring
->r
);
178 DescInfo
*info
= &ring
->info
[index
];
179 hwaddr addr
= ring
->base_addr
+ (sizeof(RockerDesc
) * index
);
181 pci_dma_write(dev
, addr
, &info
->desc
, sizeof(info
->desc
));
184 static bool desc_ring_base_addr_check(DescRing
*ring
)
186 if (!ring
->base_addr
) {
187 DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
194 static DescInfo
*__desc_ring_fetch_desc(DescRing
*ring
)
196 return desc_read(ring
, ring
->tail
);
199 DescInfo
*desc_ring_fetch_desc(DescRing
*ring
)
201 if (desc_ring_empty(ring
) || !desc_ring_base_addr_check(ring
)) {
205 return desc_read(ring
, ring
->tail
);
208 static bool __desc_ring_post_desc(DescRing
*ring
, int err
)
210 uint16_t comp_err
= 0x8000 | (uint16_t)-err
;
211 DescInfo
*info
= &ring
->info
[ring
->tail
];
213 info
->desc
.comp_err
= cpu_to_le16(comp_err
);
214 desc_write(ring
, ring
->tail
);
215 ring
->tail
= (ring
->tail
+ 1) % ring
->size
;
217 /* return true if starting credit count */
219 return ring
->credits
++ == 0;
222 bool desc_ring_post_desc(DescRing
*ring
, int err
)
224 if (desc_ring_empty(ring
)) {
225 DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
230 if (!desc_ring_base_addr_check(ring
)) {
234 return __desc_ring_post_desc(ring
, err
);
237 static bool ring_pump(DescRing
*ring
)
243 /* If the ring has a consumer, call consumer for each
244 * desc starting at tail and stopping when tail reaches
245 * head (the empty ring condition).
249 while (ring
->head
!= ring
->tail
) {
250 info
= __desc_ring_fetch_desc(ring
);
251 err
= ring
->consume(ring
->r
, info
);
252 if (__desc_ring_post_desc(ring
, err
)) {
261 bool desc_ring_set_head(DescRing
*ring
, uint32_t new)
263 uint32_t tail
= ring
->tail
;
264 uint32_t head
= ring
->head
;
266 if (!desc_ring_base_addr_check(ring
)) {
270 if (new >= ring
->size
) {
271 DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
272 new, ring
->index
, ring
->size
);
276 if (((head
< tail
) && ((new >= tail
) || (new < head
))) ||
277 ((head
> tail
) && ((new >= tail
) && (new < head
)))) {
278 DPRINTF("ERROR: trying to wrap ring[%d] "
279 "(head %d, tail %d, new head %d)\n",
280 ring
->index
, head
, tail
, new);
284 if (new == ring
->head
) {
285 DPRINTF("WARNING: setting head (%d) to current head position\n", new);
290 return ring_pump(ring
);
293 uint32_t desc_ring_get_head(DescRing
*ring
)
298 uint32_t desc_ring_get_tail(DescRing
*ring
)
303 void desc_ring_set_ctrl(DescRing
*ring
, uint32_t val
)
305 if (val
& ROCKER_DMA_DESC_CTRL_RESET
) {
306 DPRINTF("ring[%d] resetting\n", ring
->index
);
307 desc_ring_reset(ring
);
311 bool desc_ring_ret_credits(DescRing
*ring
, uint32_t credits
)
313 if (credits
> ring
->credits
) {
314 DPRINTF("ERROR: trying to return more credits (%d) "
315 "than are outstanding (%d)\n", credits
, ring
->credits
);
320 ring
->credits
-= credits
;
322 /* return true if credits are still outstanding */
324 return ring
->credits
> 0;
327 uint32_t desc_ring_get_credits(DescRing
*ring
)
329 return ring
->credits
;
332 void desc_ring_set_consume(DescRing
*ring
, desc_ring_consume
*consume
,
335 ring
->consume
= consume
;
336 ring
->msix_vector
= vector
;
339 unsigned desc_ring_get_msix_vector(DescRing
*ring
)
341 return ring
->msix_vector
;
344 DescRing
*desc_ring_alloc(Rocker
*r
, int index
)
348 ring
= g_malloc0(sizeof(DescRing
));
359 void desc_ring_free(DescRing
*ring
)
365 void desc_ring_reset(DescRing
*ring
)