xref: /openbmc/qemu/hw/net/rocker/rocker_desc.c (revision 701ed34833f53880ba38bde09b0846d01fc16d66)
1dc488f88SScott Feldman /*
2dc488f88SScott Feldman  * QEMU rocker switch emulation - Descriptor ring support
3dc488f88SScott Feldman  *
4dc488f88SScott Feldman  * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
5dc488f88SScott Feldman  *
6dc488f88SScott Feldman  * This program is free software; you can redistribute it and/or modify
7dc488f88SScott Feldman  * it under the terms of the GNU General Public License as published by
8dc488f88SScott Feldman  * the Free Software Foundation; either version 2 of the License, or
9dc488f88SScott Feldman  * (at your option) any later version.
10dc488f88SScott Feldman  *
11dc488f88SScott Feldman  * This program is distributed in the hope that it will be useful,
12dc488f88SScott Feldman  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13dc488f88SScott Feldman  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14dc488f88SScott Feldman  * GNU General Public License for more details.
15dc488f88SScott Feldman  */
16dc488f88SScott Feldman 
17e8d40465SPeter Maydell #include "qemu/osdep.h"
18dc488f88SScott Feldman #include "net/net.h"
19edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h"
20dc488f88SScott Feldman 
21dc488f88SScott Feldman #include "rocker.h"
22dc488f88SScott Feldman #include "rocker_hw.h"
23dc488f88SScott Feldman #include "rocker_desc.h"
24dc488f88SScott Feldman 
25dc488f88SScott Feldman struct desc_ring {
26dc488f88SScott Feldman     hwaddr base_addr;
27dc488f88SScott Feldman     uint32_t size;
28dc488f88SScott Feldman     uint32_t head;
29dc488f88SScott Feldman     uint32_t tail;
30dc488f88SScott Feldman     uint32_t ctrl;
31dc488f88SScott Feldman     uint32_t credits;
32dc488f88SScott Feldman     Rocker *r;
33dc488f88SScott Feldman     DescInfo *info;
34dc488f88SScott Feldman     int index;
35dc488f88SScott Feldman     desc_ring_consume *consume;
36dc488f88SScott Feldman     unsigned msix_vector;
37dc488f88SScott Feldman };
38dc488f88SScott Feldman 
39dc488f88SScott Feldman struct desc_info {
40dc488f88SScott Feldman     DescRing *ring;
41dc488f88SScott Feldman     RockerDesc desc;
42dc488f88SScott Feldman     char *buf;
43dc488f88SScott Feldman     size_t buf_size;
44dc488f88SScott Feldman };
45dc488f88SScott Feldman 
desc_buf_size(DescInfo * info)46dc488f88SScott Feldman uint16_t desc_buf_size(DescInfo *info)
47dc488f88SScott Feldman {
48dc488f88SScott Feldman     return le16_to_cpu(info->desc.buf_size);
49dc488f88SScott Feldman }
50dc488f88SScott Feldman 
desc_tlv_size(DescInfo * info)51dc488f88SScott Feldman uint16_t desc_tlv_size(DescInfo *info)
52dc488f88SScott Feldman {
53dc488f88SScott Feldman     return le16_to_cpu(info->desc.tlv_size);
54dc488f88SScott Feldman }
55dc488f88SScott Feldman 
desc_get_buf(DescInfo * info,bool read_only)56dc488f88SScott Feldman char *desc_get_buf(DescInfo *info, bool read_only)
57dc488f88SScott Feldman {
58dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(info->ring->r);
59dc488f88SScott Feldman     size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) :
60dc488f88SScott Feldman                               le16_to_cpu(info->desc.buf_size);
61dc488f88SScott Feldman 
62dc488f88SScott Feldman     if (size > info->buf_size) {
63dc488f88SScott Feldman         info->buf = g_realloc(info->buf, size);
64dc488f88SScott Feldman         info->buf_size = size;
65dc488f88SScott Feldman     }
66dc488f88SScott Feldman 
674cee3cf3SMao Zhongyi     pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size);
68dc488f88SScott Feldman 
69dc488f88SScott Feldman     return info->buf;
70dc488f88SScott Feldman }
71dc488f88SScott Feldman 
desc_set_buf(DescInfo * info,size_t tlv_size)72dc488f88SScott Feldman int desc_set_buf(DescInfo *info, size_t tlv_size)
73dc488f88SScott Feldman {
74dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(info->ring->r);
75dc488f88SScott Feldman 
76dc488f88SScott Feldman     if (tlv_size > info->buf_size) {
77dc488f88SScott Feldman         DPRINTF("ERROR: trying to write more to desc buf than it "
78dc488f88SScott Feldman                 "can hold buf_size %zu tlv_size %zu\n",
79dc488f88SScott Feldman                 info->buf_size, tlv_size);
80dc488f88SScott Feldman         return -ROCKER_EMSGSIZE;
81dc488f88SScott Feldman     }
82dc488f88SScott Feldman 
83dc488f88SScott Feldman     info->desc.tlv_size = cpu_to_le16(tlv_size);
84dc488f88SScott Feldman     pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size);
85dc488f88SScott Feldman 
86dc488f88SScott Feldman     return ROCKER_OK;
87dc488f88SScott Feldman }
88dc488f88SScott Feldman 
desc_get_ring(DescInfo * info)89dc488f88SScott Feldman DescRing *desc_get_ring(DescInfo *info)
90dc488f88SScott Feldman {
91dc488f88SScott Feldman     return info->ring;
92dc488f88SScott Feldman }
93dc488f88SScott Feldman 
desc_ring_index(DescRing * ring)94dc488f88SScott Feldman int desc_ring_index(DescRing *ring)
95dc488f88SScott Feldman {
96dc488f88SScott Feldman     return ring->index;
97dc488f88SScott Feldman }
98dc488f88SScott Feldman 
desc_ring_empty(DescRing * ring)99dc488f88SScott Feldman static bool desc_ring_empty(DescRing *ring)
100dc488f88SScott Feldman {
101dc488f88SScott Feldman     return ring->head == ring->tail;
102dc488f88SScott Feldman }
103dc488f88SScott Feldman 
desc_ring_set_base_addr(DescRing * ring,uint64_t base_addr)104dc488f88SScott Feldman bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr)
105dc488f88SScott Feldman {
106dc488f88SScott Feldman     if (base_addr & 0x7) {
107*883f2c59SPhilippe Mathieu-Daudé         DPRINTF("ERROR: ring[%d] desc base addr (0x" HWADDR_FMT_plx
108dc488f88SScott Feldman                 ") not 8-byte aligned\n", ring->index, base_addr);
109dc488f88SScott Feldman         return false;
110dc488f88SScott Feldman     }
111dc488f88SScott Feldman 
112dc488f88SScott Feldman     ring->base_addr = base_addr;
113dc488f88SScott Feldman 
114dc488f88SScott Feldman     return true;
115dc488f88SScott Feldman }
116dc488f88SScott Feldman 
desc_ring_get_base_addr(DescRing * ring)117dc488f88SScott Feldman uint64_t desc_ring_get_base_addr(DescRing *ring)
118dc488f88SScott Feldman {
119dc488f88SScott Feldman     return ring->base_addr;
120dc488f88SScott Feldman }
121dc488f88SScott Feldman 
desc_ring_set_size(DescRing * ring,uint32_t size)122dc488f88SScott Feldman bool desc_ring_set_size(DescRing *ring, uint32_t size)
123dc488f88SScott Feldman {
124dc488f88SScott Feldman     int i;
125dc488f88SScott Feldman 
126dc488f88SScott Feldman     if (size < 2 || size > 0x10000 || (size & (size - 1))) {
127dc488f88SScott Feldman         DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
128dc488f88SScott Feldman                 "or in range [2, 64K]\n", ring->index, size);
129dc488f88SScott Feldman         return false;
130dc488f88SScott Feldman     }
131dc488f88SScott Feldman 
132dc488f88SScott Feldman     for (i = 0; i < ring->size; i++) {
133dc488f88SScott Feldman         g_free(ring->info[i].buf);
134dc488f88SScott Feldman     }
135dc488f88SScott Feldman 
136dc488f88SScott Feldman     ring->size = size;
137dc488f88SScott Feldman     ring->head = ring->tail = 0;
138dc488f88SScott Feldman 
139778358d0SMarkus Armbruster     ring->info = g_renew(DescInfo, ring->info, size);
140dc488f88SScott Feldman 
141dc488f88SScott Feldman     memset(ring->info, 0, size * sizeof(DescInfo));
142dc488f88SScott Feldman 
143dc488f88SScott Feldman     for (i = 0; i < size; i++) {
144dc488f88SScott Feldman         ring->info[i].ring = ring;
145dc488f88SScott Feldman     }
146dc488f88SScott Feldman 
147dc488f88SScott Feldman     return true;
148dc488f88SScott Feldman }
149dc488f88SScott Feldman 
desc_ring_get_size(DescRing * ring)150dc488f88SScott Feldman uint32_t desc_ring_get_size(DescRing *ring)
151dc488f88SScott Feldman {
152dc488f88SScott Feldman     return ring->size;
153dc488f88SScott Feldman }
154dc488f88SScott Feldman 
desc_read(DescRing * ring,uint32_t index)155dc488f88SScott Feldman static DescInfo *desc_read(DescRing *ring, uint32_t index)
156dc488f88SScott Feldman {
157dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(ring->r);
158dc488f88SScott Feldman     DescInfo *info = &ring->info[index];
159dc488f88SScott Feldman     hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
160dc488f88SScott Feldman 
161dc488f88SScott Feldman     pci_dma_read(dev, addr, &info->desc, sizeof(info->desc));
162dc488f88SScott Feldman 
163dc488f88SScott Feldman     return info;
164dc488f88SScott Feldman }
165dc488f88SScott Feldman 
desc_write(DescRing * ring,uint32_t index)166dc488f88SScott Feldman static void desc_write(DescRing *ring, uint32_t index)
167dc488f88SScott Feldman {
168dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(ring->r);
169dc488f88SScott Feldman     DescInfo *info = &ring->info[index];
170dc488f88SScott Feldman     hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
171dc488f88SScott Feldman 
172dc488f88SScott Feldman     pci_dma_write(dev, addr, &info->desc, sizeof(info->desc));
173dc488f88SScott Feldman }
174dc488f88SScott Feldman 
desc_ring_base_addr_check(DescRing * ring)175dc488f88SScott Feldman static bool desc_ring_base_addr_check(DescRing *ring)
176dc488f88SScott Feldman {
177dc488f88SScott Feldman     if (!ring->base_addr) {
178dc488f88SScott Feldman         DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
179dc488f88SScott Feldman                 ring->index);
180dc488f88SScott Feldman         return false;
181dc488f88SScott Feldman     }
182dc488f88SScott Feldman     return true;
183dc488f88SScott Feldman }
184dc488f88SScott Feldman 
__desc_ring_fetch_desc(DescRing * ring)185dc488f88SScott Feldman static DescInfo *__desc_ring_fetch_desc(DescRing *ring)
186dc488f88SScott Feldman {
187dc488f88SScott Feldman     return desc_read(ring, ring->tail);
188dc488f88SScott Feldman }
189dc488f88SScott Feldman 
desc_ring_fetch_desc(DescRing * ring)190dc488f88SScott Feldman DescInfo *desc_ring_fetch_desc(DescRing *ring)
191dc488f88SScott Feldman {
192dc488f88SScott Feldman     if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) {
193dc488f88SScott Feldman         return NULL;
194dc488f88SScott Feldman     }
195dc488f88SScott Feldman 
196dc488f88SScott Feldman     return desc_read(ring, ring->tail);
197dc488f88SScott Feldman }
198dc488f88SScott Feldman 
__desc_ring_post_desc(DescRing * ring,int err)199dc488f88SScott Feldman static bool __desc_ring_post_desc(DescRing *ring, int err)
200dc488f88SScott Feldman {
201dc488f88SScott Feldman     uint16_t comp_err = 0x8000 | (uint16_t)-err;
202dc488f88SScott Feldman     DescInfo *info = &ring->info[ring->tail];
203dc488f88SScott Feldman 
204dc488f88SScott Feldman     info->desc.comp_err = cpu_to_le16(comp_err);
205dc488f88SScott Feldman     desc_write(ring, ring->tail);
206dc488f88SScott Feldman     ring->tail = (ring->tail + 1) % ring->size;
207dc488f88SScott Feldman 
208dc488f88SScott Feldman     /* return true if starting credit count */
209dc488f88SScott Feldman 
210dc488f88SScott Feldman     return ring->credits++ == 0;
211dc488f88SScott Feldman }
212dc488f88SScott Feldman 
desc_ring_post_desc(DescRing * ring,int err)213dc488f88SScott Feldman bool desc_ring_post_desc(DescRing *ring, int err)
214dc488f88SScott Feldman {
215dc488f88SScott Feldman     if (desc_ring_empty(ring)) {
216dc488f88SScott Feldman         DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
217dc488f88SScott Feldman                 ring->index);
218dc488f88SScott Feldman         return false;
219dc488f88SScott Feldman     }
220dc488f88SScott Feldman 
221dc488f88SScott Feldman     if (!desc_ring_base_addr_check(ring)) {
222dc488f88SScott Feldman         return false;
223dc488f88SScott Feldman     }
224dc488f88SScott Feldman 
225dc488f88SScott Feldman     return __desc_ring_post_desc(ring, err);
226dc488f88SScott Feldman }
227dc488f88SScott Feldman 
ring_pump(DescRing * ring)228dc488f88SScott Feldman static bool ring_pump(DescRing *ring)
229dc488f88SScott Feldman {
230dc488f88SScott Feldman     DescInfo *info;
231dc488f88SScott Feldman     bool primed = false;
232dc488f88SScott Feldman     int err;
233dc488f88SScott Feldman 
234dc488f88SScott Feldman     /* If the ring has a consumer, call consumer for each
235dc488f88SScott Feldman      * desc starting at tail and stopping when tail reaches
236dc488f88SScott Feldman      * head (the empty ring condition).
237dc488f88SScott Feldman      */
238dc488f88SScott Feldman 
239dc488f88SScott Feldman     if (ring->consume) {
240dc488f88SScott Feldman         while (ring->head != ring->tail) {
241dc488f88SScott Feldman             info = __desc_ring_fetch_desc(ring);
242dc488f88SScott Feldman             err = ring->consume(ring->r, info);
243dc488f88SScott Feldman             if (__desc_ring_post_desc(ring, err)) {
244dc488f88SScott Feldman                 primed = true;
245dc488f88SScott Feldman             }
246dc488f88SScott Feldman         }
247dc488f88SScott Feldman     }
248dc488f88SScott Feldman 
249dc488f88SScott Feldman     return primed;
250dc488f88SScott Feldman }
251dc488f88SScott Feldman 
desc_ring_set_head(DescRing * ring,uint32_t new)252dc488f88SScott Feldman bool desc_ring_set_head(DescRing *ring, uint32_t new)
253dc488f88SScott Feldman {
254dc488f88SScott Feldman     uint32_t tail = ring->tail;
255dc488f88SScott Feldman     uint32_t head = ring->head;
256dc488f88SScott Feldman 
257dc488f88SScott Feldman     if (!desc_ring_base_addr_check(ring)) {
258dc488f88SScott Feldman         return false;
259dc488f88SScott Feldman     }
260dc488f88SScott Feldman 
261dc488f88SScott Feldman     if (new >= ring->size) {
262dc488f88SScott Feldman         DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
263dc488f88SScott Feldman                 new, ring->index, ring->size);
264dc488f88SScott Feldman         return false;
265dc488f88SScott Feldman     }
266dc488f88SScott Feldman 
267dc488f88SScott Feldman     if (((head < tail) && ((new >= tail) || (new < head))) ||
268dc488f88SScott Feldman         ((head > tail) && ((new >= tail) && (new < head)))) {
269dc488f88SScott Feldman         DPRINTF("ERROR: trying to wrap ring[%d] "
270dc488f88SScott Feldman                 "(head %d, tail %d, new head %d)\n",
271dc488f88SScott Feldman                 ring->index, head, tail, new);
272dc488f88SScott Feldman         return false;
273dc488f88SScott Feldman     }
274dc488f88SScott Feldman 
275dc488f88SScott Feldman     if (new == ring->head) {
276dc488f88SScott Feldman         DPRINTF("WARNING: setting head (%d) to current head position\n", new);
277dc488f88SScott Feldman     }
278dc488f88SScott Feldman 
279dc488f88SScott Feldman     ring->head = new;
280dc488f88SScott Feldman 
281dc488f88SScott Feldman     return ring_pump(ring);
282dc488f88SScott Feldman }
283dc488f88SScott Feldman 
desc_ring_get_head(DescRing * ring)284dc488f88SScott Feldman uint32_t desc_ring_get_head(DescRing *ring)
285dc488f88SScott Feldman {
286dc488f88SScott Feldman     return ring->head;
287dc488f88SScott Feldman }
288dc488f88SScott Feldman 
desc_ring_get_tail(DescRing * ring)289dc488f88SScott Feldman uint32_t desc_ring_get_tail(DescRing *ring)
290dc488f88SScott Feldman {
291dc488f88SScott Feldman     return ring->tail;
292dc488f88SScott Feldman }
293dc488f88SScott Feldman 
desc_ring_set_ctrl(DescRing * ring,uint32_t val)294dc488f88SScott Feldman void desc_ring_set_ctrl(DescRing *ring, uint32_t val)
295dc488f88SScott Feldman {
296dc488f88SScott Feldman     if (val & ROCKER_DMA_DESC_CTRL_RESET) {
297dc488f88SScott Feldman         DPRINTF("ring[%d] resetting\n", ring->index);
298dc488f88SScott Feldman         desc_ring_reset(ring);
299dc488f88SScott Feldman     }
300dc488f88SScott Feldman }
301dc488f88SScott Feldman 
desc_ring_ret_credits(DescRing * ring,uint32_t credits)302dc488f88SScott Feldman bool desc_ring_ret_credits(DescRing *ring, uint32_t credits)
303dc488f88SScott Feldman {
304dc488f88SScott Feldman     if (credits > ring->credits) {
305dc488f88SScott Feldman         DPRINTF("ERROR: trying to return more credits (%d) "
306dc488f88SScott Feldman                 "than are outstanding (%d)\n", credits, ring->credits);
307dc488f88SScott Feldman         ring->credits = 0;
308dc488f88SScott Feldman         return false;
309dc488f88SScott Feldman     }
310dc488f88SScott Feldman 
311dc488f88SScott Feldman     ring->credits -= credits;
312dc488f88SScott Feldman 
313dc488f88SScott Feldman     /* return true if credits are still outstanding */
314dc488f88SScott Feldman 
315dc488f88SScott Feldman     return ring->credits > 0;
316dc488f88SScott Feldman }
317dc488f88SScott Feldman 
desc_ring_get_credits(DescRing * ring)318dc488f88SScott Feldman uint32_t desc_ring_get_credits(DescRing *ring)
319dc488f88SScott Feldman {
320dc488f88SScott Feldman     return ring->credits;
321dc488f88SScott Feldman }
322dc488f88SScott Feldman 
desc_ring_set_consume(DescRing * ring,desc_ring_consume * consume,unsigned vector)323dc488f88SScott Feldman void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
324dc488f88SScott Feldman                            unsigned vector)
325dc488f88SScott Feldman {
326dc488f88SScott Feldman     ring->consume = consume;
327dc488f88SScott Feldman     ring->msix_vector = vector;
328dc488f88SScott Feldman }
329dc488f88SScott Feldman 
desc_ring_get_msix_vector(DescRing * ring)330dc488f88SScott Feldman unsigned desc_ring_get_msix_vector(DescRing *ring)
331dc488f88SScott Feldman {
332dc488f88SScott Feldman     return ring->msix_vector;
333dc488f88SScott Feldman }
334dc488f88SScott Feldman 
desc_ring_alloc(Rocker * r,int index)335dc488f88SScott Feldman DescRing *desc_ring_alloc(Rocker *r, int index)
336dc488f88SScott Feldman {
337dc488f88SScott Feldman     DescRing *ring;
338dc488f88SScott Feldman 
339778358d0SMarkus Armbruster     ring = g_new0(DescRing, 1);
340dc488f88SScott Feldman 
341dc488f88SScott Feldman     ring->r = r;
342dc488f88SScott Feldman     ring->index = index;
343dc488f88SScott Feldman 
344dc488f88SScott Feldman     return ring;
345dc488f88SScott Feldman }
346dc488f88SScott Feldman 
desc_ring_free(DescRing * ring)347dc488f88SScott Feldman void desc_ring_free(DescRing *ring)
348dc488f88SScott Feldman {
349dc488f88SScott Feldman     g_free(ring->info);
350dc488f88SScott Feldman     g_free(ring);
351dc488f88SScott Feldman }
352dc488f88SScott Feldman 
desc_ring_reset(DescRing * ring)353dc488f88SScott Feldman void desc_ring_reset(DescRing *ring)
354dc488f88SScott Feldman {
355dc488f88SScott Feldman     ring->base_addr = 0;
356dc488f88SScott Feldman     ring->size = 0;
357dc488f88SScott Feldman     ring->head = 0;
358dc488f88SScott Feldman     ring->tail = 0;
359dc488f88SScott Feldman     ring->ctrl = 0;
360dc488f88SScott Feldman     ring->credits = 0;
361dc488f88SScott Feldman }
362