xref: /openbmc/qemu/hw/net/rocker/rocker.c (revision a3fb4e93a3a7cf2be355c41cd550bef856f5ffe4)
1dc488f88SScott Feldman /*
2dc488f88SScott Feldman  * QEMU rocker switch emulation - PCI device
3dc488f88SScott Feldman  *
4dc488f88SScott Feldman  * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
5dc488f88SScott Feldman  * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
6dc488f88SScott Feldman  *
7dc488f88SScott Feldman  * This program is free software; you can redistribute it and/or modify
8dc488f88SScott Feldman  * it under the terms of the GNU General Public License as published by
9dc488f88SScott Feldman  * the Free Software Foundation; either version 2 of the License, or
10dc488f88SScott Feldman  * (at your option) any later version.
11dc488f88SScott Feldman  *
12dc488f88SScott Feldman  * This program is distributed in the hope that it will be useful,
13dc488f88SScott Feldman  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14dc488f88SScott Feldman  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15dc488f88SScott Feldman  * GNU General Public License for more details.
16dc488f88SScott Feldman  */
17dc488f88SScott Feldman 
18e8d40465SPeter Maydell #include "qemu/osdep.h"
19edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h"
20a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
21ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
22d6454270SMarkus Armbruster #include "migration/vmstate.h"
23dc488f88SScott Feldman #include "hw/pci/msix.h"
24dc488f88SScott Feldman #include "net/net.h"
25dc488f88SScott Feldman #include "net/eth.h"
26e688df6bSMarkus Armbruster #include "qapi/error.h"
279af23989SMarkus Armbruster #include "qapi/qapi-commands-rocker.h"
28dc488f88SScott Feldman #include "qemu/iov.h"
290b8fa32fSMarkus Armbruster #include "qemu/module.h"
30dc488f88SScott Feldman #include "qemu/bitops.h"
31b18ee6a2SPhilippe Mathieu-Daudé #include "qemu/log.h"
32dc488f88SScott Feldman 
33dc488f88SScott Feldman #include "rocker.h"
34dc488f88SScott Feldman #include "rocker_hw.h"
35dc488f88SScott Feldman #include "rocker_fp.h"
36dc488f88SScott Feldman #include "rocker_desc.h"
37dc488f88SScott Feldman #include "rocker_tlv.h"
38dc488f88SScott Feldman #include "rocker_world.h"
39dc488f88SScott Feldman #include "rocker_of_dpa.h"
40dc488f88SScott Feldman 
41dc488f88SScott Feldman struct rocker {
42dc488f88SScott Feldman     /* private */
43dc488f88SScott Feldman     PCIDevice parent_obj;
44dc488f88SScott Feldman     /* public */
45dc488f88SScott Feldman 
46dc488f88SScott Feldman     MemoryRegion mmio;
47dc488f88SScott Feldman     MemoryRegion msix_bar;
48dc488f88SScott Feldman 
49dc488f88SScott Feldman     /* switch configuration */
50dc488f88SScott Feldman     char *name;                  /* switch name */
519fe7101fSJiri Pirko     char *world_name;            /* world name */
52dc488f88SScott Feldman     uint32_t fp_ports;           /* front-panel port count */
53dc488f88SScott Feldman     NICPeers *fp_ports_peers;
54dc488f88SScott Feldman     MACAddr fp_start_macaddr;    /* front-panel port 0 mac addr */
55dc488f88SScott Feldman     uint64_t switch_id;          /* switch id */
56dc488f88SScott Feldman 
57dc488f88SScott Feldman     /* front-panel ports */
58dc488f88SScott Feldman     FpPort *fp_port[ROCKER_FP_PORTS_MAX];
59dc488f88SScott Feldman 
60dc488f88SScott Feldman     /* register backings */
61dc488f88SScott Feldman     uint32_t test_reg;
62dc488f88SScott Feldman     uint64_t test_reg64;
63dc488f88SScott Feldman     dma_addr_t test_dma_addr;
64dc488f88SScott Feldman     uint32_t test_dma_size;
65dc488f88SScott Feldman     uint64_t lower32;            /* lower 32-bit val in 2-part 64-bit access */
66dc488f88SScott Feldman 
67dc488f88SScott Feldman     /* desc rings */
68dc488f88SScott Feldman     DescRing **rings;
69dc488f88SScott Feldman 
70dc488f88SScott Feldman     /* switch worlds */
71dc488f88SScott Feldman     World *worlds[ROCKER_WORLD_TYPE_MAX];
72dc488f88SScott Feldman     World *world_dflt;
73dc488f88SScott Feldman 
74dc488f88SScott Feldman     QLIST_ENTRY(rocker) next;
75dc488f88SScott Feldman };
76dc488f88SScott Feldman 
77dc488f88SScott Feldman static QLIST_HEAD(, rocker) rockers;
78dc488f88SScott Feldman 
rocker_find(const char * name)79dc488f88SScott Feldman Rocker *rocker_find(const char *name)
80dc488f88SScott Feldman {
81dc488f88SScott Feldman     Rocker *r;
82dc488f88SScott Feldman 
83dc488f88SScott Feldman     QLIST_FOREACH(r, &rockers, next)
84dc488f88SScott Feldman         if (strcmp(r->name, name) == 0) {
85dc488f88SScott Feldman             return r;
86dc488f88SScott Feldman         }
87dc488f88SScott Feldman 
88dc488f88SScott Feldman     return NULL;
89dc488f88SScott Feldman }
90dc488f88SScott Feldman 
rocker_get_world(Rocker * r,enum rocker_world_type type)91dc488f88SScott Feldman World *rocker_get_world(Rocker *r, enum rocker_world_type type)
92dc488f88SScott Feldman {
93dc488f88SScott Feldman     if (type < ROCKER_WORLD_TYPE_MAX) {
94dc488f88SScott Feldman         return r->worlds[type];
95dc488f88SScott Feldman     }
96dc488f88SScott Feldman     return NULL;
97dc488f88SScott Feldman }
98dc488f88SScott Feldman 
qmp_query_rocker(const char * name,Error ** errp)99fafa4d50SScott Feldman RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
100fafa4d50SScott Feldman {
101ec50dd46SGonglei     RockerSwitch *rocker;
102fafa4d50SScott Feldman     Rocker *r;
103fafa4d50SScott Feldman 
104fafa4d50SScott Feldman     r = rocker_find(name);
105fafa4d50SScott Feldman     if (!r) {
106455b0fdeSEric Blake         error_setg(errp, "rocker %s not found", name);
107fafa4d50SScott Feldman         return NULL;
108fafa4d50SScott Feldman     }
109fafa4d50SScott Feldman 
110ec50dd46SGonglei     rocker = g_new0(RockerSwitch, 1);
111fafa4d50SScott Feldman     rocker->name = g_strdup(r->name);
112fafa4d50SScott Feldman     rocker->id = r->switch_id;
113fafa4d50SScott Feldman     rocker->ports = r->fp_ports;
114fafa4d50SScott Feldman 
115fafa4d50SScott Feldman     return rocker;
116fafa4d50SScott Feldman }
117fafa4d50SScott Feldman 
qmp_query_rocker_ports(const char * name,Error ** errp)118fafa4d50SScott Feldman RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
119fafa4d50SScott Feldman {
120fafa4d50SScott Feldman     RockerPortList *list = NULL;
121fafa4d50SScott Feldman     Rocker *r;
122fafa4d50SScott Feldman     int i;
123fafa4d50SScott Feldman 
124fafa4d50SScott Feldman     r = rocker_find(name);
125fafa4d50SScott Feldman     if (!r) {
126455b0fdeSEric Blake         error_setg(errp, "rocker %s not found", name);
127fafa4d50SScott Feldman         return NULL;
128fafa4d50SScott Feldman     }
129fafa4d50SScott Feldman 
130fafa4d50SScott Feldman     for (i = r->fp_ports - 1; i >= 0; i--) {
131fe4d7e33SEric Blake         QAPI_LIST_PREPEND(list, fp_port_get_info(r->fp_port[i]));
132fafa4d50SScott Feldman     }
133fafa4d50SScott Feldman 
134fafa4d50SScott Feldman     return list;
135fafa4d50SScott Feldman }
136fafa4d50SScott Feldman 
rocker_get_pport_by_tx_ring(Rocker * r,DescRing * ring)137dc488f88SScott Feldman static uint32_t rocker_get_pport_by_tx_ring(Rocker *r,
138dc488f88SScott Feldman                                             DescRing *ring)
139dc488f88SScott Feldman {
140dc488f88SScott Feldman     return (desc_ring_index(ring) - 2) / 2 + 1;
141dc488f88SScott Feldman }
142dc488f88SScott Feldman 
tx_consume(Rocker * r,DescInfo * info)143dc488f88SScott Feldman static int tx_consume(Rocker *r, DescInfo *info)
144dc488f88SScott Feldman {
145dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
146dc488f88SScott Feldman     char *buf = desc_get_buf(info, true);
147dc488f88SScott Feldman     RockerTlv *tlv_frag;
148dc488f88SScott Feldman     RockerTlv *tlvs[ROCKER_TLV_TX_MAX + 1];
149dc488f88SScott Feldman     struct iovec iov[ROCKER_TX_FRAGS_MAX] = { { 0, }, };
150dc488f88SScott Feldman     uint32_t pport;
151dc488f88SScott Feldman     uint32_t port;
152dc488f88SScott Feldman     uint16_t tx_offload = ROCKER_TX_OFFLOAD_NONE;
153dc488f88SScott Feldman     uint16_t tx_l3_csum_off = 0;
154dc488f88SScott Feldman     uint16_t tx_tso_mss = 0;
155dc488f88SScott Feldman     uint16_t tx_tso_hdr_len = 0;
156dc488f88SScott Feldman     int iovcnt = 0;
157dc488f88SScott Feldman     int err = ROCKER_OK;
158dc488f88SScott Feldman     int rem;
159dc488f88SScott Feldman     int i;
160dc488f88SScott Feldman 
161dc488f88SScott Feldman     if (!buf) {
162dc488f88SScott Feldman         return -ROCKER_ENXIO;
163dc488f88SScott Feldman     }
164dc488f88SScott Feldman 
165dc488f88SScott Feldman     rocker_tlv_parse(tlvs, ROCKER_TLV_TX_MAX, buf, desc_tlv_size(info));
166dc488f88SScott Feldman 
167dc488f88SScott Feldman     if (!tlvs[ROCKER_TLV_TX_FRAGS]) {
168dc488f88SScott Feldman         return -ROCKER_EINVAL;
169dc488f88SScott Feldman     }
170dc488f88SScott Feldman 
171dc488f88SScott Feldman     pport = rocker_get_pport_by_tx_ring(r, desc_get_ring(info));
172dc488f88SScott Feldman     if (!fp_port_from_pport(pport, &port)) {
173dc488f88SScott Feldman         return -ROCKER_EINVAL;
174dc488f88SScott Feldman     }
175dc488f88SScott Feldman 
176dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_TX_OFFLOAD]) {
177dc488f88SScott Feldman         tx_offload = rocker_tlv_get_u8(tlvs[ROCKER_TLV_TX_OFFLOAD]);
178dc488f88SScott Feldman     }
179dc488f88SScott Feldman 
180dc488f88SScott Feldman     switch (tx_offload) {
181dc488f88SScott Feldman     case ROCKER_TX_OFFLOAD_L3_CSUM:
182dc488f88SScott Feldman         if (!tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
183dc488f88SScott Feldman             return -ROCKER_EINVAL;
184dc488f88SScott Feldman         }
185f211fcd7SScott Feldman         break;
186dc488f88SScott Feldman     case ROCKER_TX_OFFLOAD_TSO:
187dc488f88SScott Feldman         if (!tlvs[ROCKER_TLV_TX_TSO_MSS] ||
188dc488f88SScott Feldman             !tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
189dc488f88SScott Feldman             return -ROCKER_EINVAL;
190dc488f88SScott Feldman         }
191f211fcd7SScott Feldman         break;
192dc488f88SScott Feldman     }
193dc488f88SScott Feldman 
194dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
195dc488f88SScott Feldman         tx_l3_csum_off = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]);
196b18ee6a2SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_UNIMP, "rocker %s: L3 not implemented"
197b18ee6a2SPhilippe Mathieu-Daudé                                  " (cksum off: %u)\n",
198b18ee6a2SPhilippe Mathieu-Daudé                       __func__, tx_l3_csum_off);
199dc488f88SScott Feldman     }
200dc488f88SScott Feldman 
201dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_TX_TSO_MSS]) {
202dc488f88SScott Feldman         tx_tso_mss = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_MSS]);
203b18ee6a2SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented (MSS: %u)\n",
204b18ee6a2SPhilippe Mathieu-Daudé                       __func__, tx_tso_mss);
205dc488f88SScott Feldman     }
206dc488f88SScott Feldman 
207dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
208dc488f88SScott Feldman         tx_tso_hdr_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]);
209b18ee6a2SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented"
210b18ee6a2SPhilippe Mathieu-Daudé                                  " (hdr length: %u)\n",
211b18ee6a2SPhilippe Mathieu-Daudé                       __func__, tx_tso_hdr_len);
212dc488f88SScott Feldman     }
213dc488f88SScott Feldman 
214dc488f88SScott Feldman     rocker_tlv_for_each_nested(tlv_frag, tlvs[ROCKER_TLV_TX_FRAGS], rem) {
215dc488f88SScott Feldman         hwaddr frag_addr;
216dc488f88SScott Feldman         uint16_t frag_len;
217dc488f88SScott Feldman 
218dc488f88SScott Feldman         if (rocker_tlv_type(tlv_frag) != ROCKER_TLV_TX_FRAG) {
219dc488f88SScott Feldman             err = -ROCKER_EINVAL;
220dc488f88SScott Feldman             goto err_bad_attr;
221dc488f88SScott Feldman         }
222dc488f88SScott Feldman 
223dc488f88SScott Feldman         rocker_tlv_parse_nested(tlvs, ROCKER_TLV_TX_FRAG_ATTR_MAX, tlv_frag);
224dc488f88SScott Feldman 
225dc488f88SScott Feldman         if (!tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
226dc488f88SScott Feldman             !tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) {
227dc488f88SScott Feldman             err = -ROCKER_EINVAL;
228dc488f88SScott Feldman             goto err_bad_attr;
229dc488f88SScott Feldman         }
230dc488f88SScott Feldman 
231dc488f88SScott Feldman         frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
232dc488f88SScott Feldman         frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
233dc488f88SScott Feldman 
234007cd223SPrasad J Pandit         if (iovcnt >= ROCKER_TX_FRAGS_MAX) {
235007cd223SPrasad J Pandit             goto err_too_many_frags;
236007cd223SPrasad J Pandit         }
237dc488f88SScott Feldman         iov[iovcnt].iov_len = frag_len;
238dc488f88SScott Feldman         iov[iovcnt].iov_base = g_malloc(frag_len);
239dc488f88SScott Feldman 
2404cee3cf3SMao Zhongyi         pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base,
2414cee3cf3SMao Zhongyi                      iov[iovcnt].iov_len);
2424cee3cf3SMao Zhongyi 
243007cd223SPrasad J Pandit         iovcnt++;
244dc488f88SScott Feldman     }
245dc488f88SScott Feldman 
246dc488f88SScott Feldman     err = fp_port_eg(r->fp_port[port], iov, iovcnt);
247dc488f88SScott Feldman 
248dc488f88SScott Feldman err_too_many_frags:
249dc488f88SScott Feldman err_bad_attr:
250dc488f88SScott Feldman     for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) {
251dc488f88SScott Feldman         g_free(iov[i].iov_base);
252dc488f88SScott Feldman     }
253dc488f88SScott Feldman 
254dc488f88SScott Feldman     return err;
255dc488f88SScott Feldman }
256dc488f88SScott Feldman 
cmd_get_port_settings(Rocker * r,DescInfo * info,char * buf,RockerTlv * cmd_info_tlv)257dc488f88SScott Feldman static int cmd_get_port_settings(Rocker *r,
258dc488f88SScott Feldman                                  DescInfo *info, char *buf,
259dc488f88SScott Feldman                                  RockerTlv *cmd_info_tlv)
260dc488f88SScott Feldman {
261dc488f88SScott Feldman     RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
262dc488f88SScott Feldman     RockerTlv *nest;
263dc488f88SScott Feldman     FpPort *fp_port;
264dc488f88SScott Feldman     uint32_t pport;
265dc488f88SScott Feldman     uint32_t port;
266dc488f88SScott Feldman     uint32_t speed;
267dc488f88SScott Feldman     uint8_t duplex;
268dc488f88SScott Feldman     uint8_t autoneg;
269dc488f88SScott Feldman     uint8_t learning;
27077349536SDavid Ahern     char *phys_name;
271dc488f88SScott Feldman     MACAddr macaddr;
272dc488f88SScott Feldman     enum rocker_world_type mode;
273dc488f88SScott Feldman     size_t tlv_size;
274dc488f88SScott Feldman     int pos;
275dc488f88SScott Feldman     int err;
276dc488f88SScott Feldman 
277dc488f88SScott Feldman     rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
278dc488f88SScott Feldman                             cmd_info_tlv);
279dc488f88SScott Feldman 
280dc488f88SScott Feldman     if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
281dc488f88SScott Feldman         return -ROCKER_EINVAL;
282dc488f88SScott Feldman     }
283dc488f88SScott Feldman 
284dc488f88SScott Feldman     pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
285dc488f88SScott Feldman     if (!fp_port_from_pport(pport, &port)) {
286dc488f88SScott Feldman         return -ROCKER_EINVAL;
287dc488f88SScott Feldman     }
288dc488f88SScott Feldman     fp_port = r->fp_port[port];
289dc488f88SScott Feldman 
290dc488f88SScott Feldman     err = fp_port_get_settings(fp_port, &speed, &duplex, &autoneg);
291dc488f88SScott Feldman     if (err) {
292dc488f88SScott Feldman         return err;
293dc488f88SScott Feldman     }
294dc488f88SScott Feldman 
295dc488f88SScott Feldman     fp_port_get_macaddr(fp_port, &macaddr);
296dc488f88SScott Feldman     mode = world_type(fp_port_get_world(fp_port));
297dc488f88SScott Feldman     learning = fp_port_get_learning(fp_port);
29877349536SDavid Ahern     phys_name = fp_port_get_name(fp_port);
299dc488f88SScott Feldman 
300dc488f88SScott Feldman     tlv_size = rocker_tlv_total_size(0) +                 /* nest */
301dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
302dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint32_t)) +  /*   speed */
303dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint8_t)) +   /*   duplex */
304dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint8_t)) +   /*   autoneg */
305dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(macaddr.a)) + /*   macaddr */
306dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint8_t)) +   /*   mode */
30777349536SDavid Ahern                rocker_tlv_total_size(sizeof(uint8_t)) +   /*   learning */
30877349536SDavid Ahern                rocker_tlv_total_size(strlen(phys_name));
309dc488f88SScott Feldman 
310dc488f88SScott Feldman     if (tlv_size > desc_buf_size(info)) {
311dc488f88SScott Feldman         return -ROCKER_EMSGSIZE;
312dc488f88SScott Feldman     }
313dc488f88SScott Feldman 
314dc488f88SScott Feldman     pos = 0;
315dc488f88SScott Feldman     nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_CMD_INFO);
316dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, pport);
317dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, speed);
318dc488f88SScott Feldman     rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, duplex);
319dc488f88SScott Feldman     rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, autoneg);
320dc488f88SScott Feldman     rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
321dc488f88SScott Feldman                    sizeof(macaddr.a), macaddr.a);
322dc488f88SScott Feldman     rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode);
323dc488f88SScott Feldman     rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
324dc488f88SScott Feldman                       learning);
32577349536SDavid Ahern     rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME,
32677349536SDavid Ahern                    strlen(phys_name), phys_name);
327dc488f88SScott Feldman     rocker_tlv_nest_end(buf, &pos, nest);
328dc488f88SScott Feldman 
329dc488f88SScott Feldman     return desc_set_buf(info, tlv_size);
330dc488f88SScott Feldman }
331dc488f88SScott Feldman 
cmd_set_port_settings(Rocker * r,RockerTlv * cmd_info_tlv)332dc488f88SScott Feldman static int cmd_set_port_settings(Rocker *r,
333dc488f88SScott Feldman                                  RockerTlv *cmd_info_tlv)
334dc488f88SScott Feldman {
335dc488f88SScott Feldman     RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
336dc488f88SScott Feldman     FpPort *fp_port;
337dc488f88SScott Feldman     uint32_t pport;
338dc488f88SScott Feldman     uint32_t port;
339dc488f88SScott Feldman     uint32_t speed;
340dc488f88SScott Feldman     uint8_t duplex;
341dc488f88SScott Feldman     uint8_t autoneg;
342dc488f88SScott Feldman     uint8_t learning;
343dc488f88SScott Feldman     MACAddr macaddr;
344dc488f88SScott Feldman     enum rocker_world_type mode;
345dc488f88SScott Feldman     int err;
346dc488f88SScott Feldman 
347dc488f88SScott Feldman     rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
348dc488f88SScott Feldman                             cmd_info_tlv);
349dc488f88SScott Feldman 
350dc488f88SScott Feldman     if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
351dc488f88SScott Feldman         return -ROCKER_EINVAL;
352dc488f88SScott Feldman     }
353dc488f88SScott Feldman 
354dc488f88SScott Feldman     pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
355dc488f88SScott Feldman     if (!fp_port_from_pport(pport, &port)) {
356dc488f88SScott Feldman         return -ROCKER_EINVAL;
357dc488f88SScott Feldman     }
358dc488f88SScott Feldman     fp_port = r->fp_port[port];
359dc488f88SScott Feldman 
360dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] &&
361dc488f88SScott Feldman         tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] &&
362dc488f88SScott Feldman         tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) {
363dc488f88SScott Feldman 
364dc488f88SScott Feldman         speed = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
365dc488f88SScott Feldman         duplex = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
366dc488f88SScott Feldman         autoneg = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
367dc488f88SScott Feldman 
368dc488f88SScott Feldman         err = fp_port_set_settings(fp_port, speed, duplex, autoneg);
369dc488f88SScott Feldman         if (err) {
370dc488f88SScott Feldman             return err;
371dc488f88SScott Feldman         }
372dc488f88SScott Feldman     }
373dc488f88SScott Feldman 
374dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) {
375dc488f88SScott Feldman         if (rocker_tlv_len(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) !=
376dc488f88SScott Feldman             sizeof(macaddr.a)) {
377dc488f88SScott Feldman             return -ROCKER_EINVAL;
378dc488f88SScott Feldman         }
379dc488f88SScott Feldman         memcpy(macaddr.a,
380dc488f88SScott Feldman                rocker_tlv_data(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]),
381dc488f88SScott Feldman                sizeof(macaddr.a));
382dc488f88SScott Feldman         fp_port_set_macaddr(fp_port, &macaddr);
383dc488f88SScott Feldman     }
384dc488f88SScott Feldman 
385dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) {
386dc488f88SScott Feldman         mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
3870ab9cd9aSJiri Pirko         if (mode >= ROCKER_WORLD_TYPE_MAX) {
3880ab9cd9aSJiri Pirko             return -ROCKER_EINVAL;
3890ab9cd9aSJiri Pirko         }
3900ab9cd9aSJiri Pirko         /* We don't support world change. */
3910ab9cd9aSJiri Pirko         if (!fp_port_check_world(fp_port, r->worlds[mode])) {
3920ab9cd9aSJiri Pirko             return -ROCKER_EINVAL;
3930ab9cd9aSJiri Pirko         }
394dc488f88SScott Feldman     }
395dc488f88SScott Feldman 
396dc488f88SScott Feldman     if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) {
397dc488f88SScott Feldman         learning =
398dc488f88SScott Feldman             rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]);
399dc488f88SScott Feldman         fp_port_set_learning(fp_port, learning);
400dc488f88SScott Feldman     }
401dc488f88SScott Feldman 
402dc488f88SScott Feldman     return ROCKER_OK;
403dc488f88SScott Feldman }
404dc488f88SScott Feldman 
cmd_consume(Rocker * r,DescInfo * info)405dc488f88SScott Feldman static int cmd_consume(Rocker *r, DescInfo *info)
406dc488f88SScott Feldman {
407dc488f88SScott Feldman     char *buf = desc_get_buf(info, false);
408dc488f88SScott Feldman     RockerTlv *tlvs[ROCKER_TLV_CMD_MAX + 1];
409dc488f88SScott Feldman     RockerTlv *info_tlv;
410dc488f88SScott Feldman     World *world;
411dc488f88SScott Feldman     uint16_t cmd;
412dc488f88SScott Feldman     int err;
413dc488f88SScott Feldman 
414dc488f88SScott Feldman     if (!buf) {
415dc488f88SScott Feldman         return -ROCKER_ENXIO;
416dc488f88SScott Feldman     }
417dc488f88SScott Feldman 
418dc488f88SScott Feldman     rocker_tlv_parse(tlvs, ROCKER_TLV_CMD_MAX, buf, desc_tlv_size(info));
419dc488f88SScott Feldman 
420dc488f88SScott Feldman     if (!tlvs[ROCKER_TLV_CMD_TYPE] || !tlvs[ROCKER_TLV_CMD_INFO]) {
421dc488f88SScott Feldman         return -ROCKER_EINVAL;
422dc488f88SScott Feldman     }
423dc488f88SScott Feldman 
424dc488f88SScott Feldman     cmd = rocker_tlv_get_le16(tlvs[ROCKER_TLV_CMD_TYPE]);
425dc488f88SScott Feldman     info_tlv = tlvs[ROCKER_TLV_CMD_INFO];
426dc488f88SScott Feldman 
427dc488f88SScott Feldman     /* This might be reworked to something like this:
428dc488f88SScott Feldman      * Every world will have an array of command handlers from
429dc488f88SScott Feldman      * ROCKER_TLV_CMD_TYPE_UNSPEC to ROCKER_TLV_CMD_TYPE_MAX. There is
430dc488f88SScott Feldman      * up to each world to implement whatever command it want.
431dc488f88SScott Feldman      * It can reference "generic" commands as cmd_set_port_settings or
432dc488f88SScott Feldman      * cmd_get_port_settings
433dc488f88SScott Feldman      */
434dc488f88SScott Feldman 
435dc488f88SScott Feldman     switch (cmd) {
436dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
437dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
438dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
439dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
440dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
441dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
442dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
443dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
444dc488f88SScott Feldman         world = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
445dc488f88SScott Feldman         err = world_do_cmd(world, info, buf, cmd, info_tlv);
446dc488f88SScott Feldman         break;
447dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS:
448dc488f88SScott Feldman         err = cmd_get_port_settings(r, info, buf, info_tlv);
449dc488f88SScott Feldman         break;
450dc488f88SScott Feldman     case ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS:
451dc488f88SScott Feldman         err = cmd_set_port_settings(r, info_tlv);
452dc488f88SScott Feldman         break;
453dc488f88SScott Feldman     default:
454dc488f88SScott Feldman         err = -ROCKER_EINVAL;
455dc488f88SScott Feldman         break;
456dc488f88SScott Feldman     }
457dc488f88SScott Feldman 
458dc488f88SScott Feldman     return err;
459dc488f88SScott Feldman }
460dc488f88SScott Feldman 
rocker_msix_irq(Rocker * r,unsigned vector)461dc488f88SScott Feldman static void rocker_msix_irq(Rocker *r, unsigned vector)
462dc488f88SScott Feldman {
463dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
464dc488f88SScott Feldman 
465dc488f88SScott Feldman     DPRINTF("MSI-X notify request for vector %d\n", vector);
466dc488f88SScott Feldman     if (vector >= ROCKER_MSIX_VEC_COUNT(r->fp_ports)) {
467dc488f88SScott Feldman         DPRINTF("incorrect vector %d\n", vector);
468dc488f88SScott Feldman         return;
469dc488f88SScott Feldman     }
470dc488f88SScott Feldman     msix_notify(dev, vector);
471dc488f88SScott Feldman }
472dc488f88SScott Feldman 
rocker_event_link_changed(Rocker * r,uint32_t pport,bool link_up)473dc488f88SScott Feldman int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up)
474dc488f88SScott Feldman {
475dc488f88SScott Feldman     DescRing *ring = r->rings[ROCKER_RING_EVENT];
476dc488f88SScott Feldman     DescInfo *info = desc_ring_fetch_desc(ring);
477dc488f88SScott Feldman     RockerTlv *nest;
478dc488f88SScott Feldman     char *buf;
479dc488f88SScott Feldman     size_t tlv_size;
480dc488f88SScott Feldman     int pos;
481dc488f88SScott Feldman     int err;
482dc488f88SScott Feldman 
483dc488f88SScott Feldman     if (!info) {
484dc488f88SScott Feldman         return -ROCKER_ENOBUFS;
485dc488f88SScott Feldman     }
486dc488f88SScott Feldman 
487dc488f88SScott Feldman     tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) +  /* event type */
488dc488f88SScott Feldman                rocker_tlv_total_size(0) +                 /* nest */
489dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
490dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint8_t));    /*   link up */
491dc488f88SScott Feldman 
492dc488f88SScott Feldman     if (tlv_size > desc_buf_size(info)) {
493dc488f88SScott Feldman         err = -ROCKER_EMSGSIZE;
494dc488f88SScott Feldman         goto err_too_big;
495dc488f88SScott Feldman     }
496dc488f88SScott Feldman 
497dc488f88SScott Feldman     buf = desc_get_buf(info, false);
498dc488f88SScott Feldman     if (!buf) {
499dc488f88SScott Feldman         err = -ROCKER_ENOMEM;
500dc488f88SScott Feldman         goto err_no_mem;
501dc488f88SScott Feldman     }
502dc488f88SScott Feldman 
503dc488f88SScott Feldman     pos = 0;
504dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
505dc488f88SScott Feldman                         ROCKER_TLV_EVENT_TYPE_LINK_CHANGED);
506dc488f88SScott Feldman     nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
507dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, pport);
508dc488f88SScott Feldman     rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP,
509dc488f88SScott Feldman                       link_up ? 1 : 0);
510dc488f88SScott Feldman     rocker_tlv_nest_end(buf, &pos, nest);
511dc488f88SScott Feldman 
512dc488f88SScott Feldman     err = desc_set_buf(info, tlv_size);
513dc488f88SScott Feldman 
514dc488f88SScott Feldman err_too_big:
515dc488f88SScott Feldman err_no_mem:
516dc488f88SScott Feldman     if (desc_ring_post_desc(ring, err)) {
517dc488f88SScott Feldman         rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
518dc488f88SScott Feldman     }
519dc488f88SScott Feldman 
520dc488f88SScott Feldman     return err;
521dc488f88SScott Feldman }
522dc488f88SScott Feldman 
rocker_event_mac_vlan_seen(Rocker * r,uint32_t pport,uint8_t * addr,uint16_t vlan_id)523dc488f88SScott Feldman int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr,
524dc488f88SScott Feldman                                uint16_t vlan_id)
525dc488f88SScott Feldman {
526dc488f88SScott Feldman     DescRing *ring = r->rings[ROCKER_RING_EVENT];
527dc488f88SScott Feldman     DescInfo *info;
528dc488f88SScott Feldman     FpPort *fp_port;
529dc488f88SScott Feldman     uint32_t port;
530dc488f88SScott Feldman     RockerTlv *nest;
531dc488f88SScott Feldman     char *buf;
532dc488f88SScott Feldman     size_t tlv_size;
533dc488f88SScott Feldman     int pos;
534dc488f88SScott Feldman     int err;
535dc488f88SScott Feldman 
536dc488f88SScott Feldman     if (!fp_port_from_pport(pport, &port)) {
537dc488f88SScott Feldman         return -ROCKER_EINVAL;
538dc488f88SScott Feldman     }
539dc488f88SScott Feldman     fp_port = r->fp_port[port];
540dc488f88SScott Feldman     if (!fp_port_get_learning(fp_port)) {
541dc488f88SScott Feldman         return ROCKER_OK;
542dc488f88SScott Feldman     }
543dc488f88SScott Feldman 
544dc488f88SScott Feldman     info = desc_ring_fetch_desc(ring);
545dc488f88SScott Feldman     if (!info) {
546dc488f88SScott Feldman         return -ROCKER_ENOBUFS;
547dc488f88SScott Feldman     }
548dc488f88SScott Feldman 
549dc488f88SScott Feldman     tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) +  /* event type */
550dc488f88SScott Feldman                rocker_tlv_total_size(0) +                 /* nest */
551dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint32_t)) +  /*   pport */
552dc488f88SScott Feldman                rocker_tlv_total_size(ETH_ALEN) +          /*   mac addr */
553dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint16_t));   /*   vlan_id */
554dc488f88SScott Feldman 
555dc488f88SScott Feldman     if (tlv_size > desc_buf_size(info)) {
556dc488f88SScott Feldman         err = -ROCKER_EMSGSIZE;
557dc488f88SScott Feldman         goto err_too_big;
558dc488f88SScott Feldman     }
559dc488f88SScott Feldman 
560dc488f88SScott Feldman     buf = desc_get_buf(info, false);
561dc488f88SScott Feldman     if (!buf) {
562dc488f88SScott Feldman         err = -ROCKER_ENOMEM;
563dc488f88SScott Feldman         goto err_no_mem;
564dc488f88SScott Feldman     }
565dc488f88SScott Feldman 
566dc488f88SScott Feldman     pos = 0;
567dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
568dc488f88SScott Feldman                         ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN);
569dc488f88SScott Feldman     nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
570dc488f88SScott Feldman     rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_PPORT, pport);
571dc488f88SScott Feldman     rocker_tlv_put(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_MAC, ETH_ALEN, addr);
572dc488f88SScott Feldman     rocker_tlv_put_u16(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, vlan_id);
573dc488f88SScott Feldman     rocker_tlv_nest_end(buf, &pos, nest);
574dc488f88SScott Feldman 
575dc488f88SScott Feldman     err = desc_set_buf(info, tlv_size);
576dc488f88SScott Feldman 
577dc488f88SScott Feldman err_too_big:
578dc488f88SScott Feldman err_no_mem:
579dc488f88SScott Feldman     if (desc_ring_post_desc(ring, err)) {
580dc488f88SScott Feldman         rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
581dc488f88SScott Feldman     }
582dc488f88SScott Feldman 
583dc488f88SScott Feldman     return err;
584dc488f88SScott Feldman }
585dc488f88SScott Feldman 
rocker_get_rx_ring_by_pport(Rocker * r,uint32_t pport)586dc488f88SScott Feldman static DescRing *rocker_get_rx_ring_by_pport(Rocker *r,
587dc488f88SScott Feldman                                                      uint32_t pport)
588dc488f88SScott Feldman {
589dc488f88SScott Feldman     return r->rings[(pport - 1) * 2 + 3];
590dc488f88SScott Feldman }
591dc488f88SScott Feldman 
rx_produce(World * world,uint32_t pport,const struct iovec * iov,int iovcnt,uint8_t copy_to_cpu)592dc488f88SScott Feldman int rx_produce(World *world, uint32_t pport,
593d0d25558SScott Feldman                const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu)
594dc488f88SScott Feldman {
595dc488f88SScott Feldman     Rocker *r = world_rocker(world);
596dc488f88SScott Feldman     PCIDevice *dev = (PCIDevice *)r;
597dc488f88SScott Feldman     DescRing *ring = rocker_get_rx_ring_by_pport(r, pport);
598dc488f88SScott Feldman     DescInfo *info = desc_ring_fetch_desc(ring);
599dc488f88SScott Feldman     char *data;
600dc488f88SScott Feldman     size_t data_size = iov_size(iov, iovcnt);
601dc488f88SScott Feldman     char *buf;
602dc488f88SScott Feldman     uint16_t rx_flags = 0;
603dc488f88SScott Feldman     uint16_t rx_csum = 0;
604dc488f88SScott Feldman     size_t tlv_size;
605dc488f88SScott Feldman     RockerTlv *tlvs[ROCKER_TLV_RX_MAX + 1];
606dc488f88SScott Feldman     hwaddr frag_addr;
607dc488f88SScott Feldman     uint16_t frag_max_len;
608dc488f88SScott Feldman     int pos;
609dc488f88SScott Feldman     int err;
610dc488f88SScott Feldman 
611dc488f88SScott Feldman     if (!info) {
612dc488f88SScott Feldman         return -ROCKER_ENOBUFS;
613dc488f88SScott Feldman     }
614dc488f88SScott Feldman 
615dc488f88SScott Feldman     buf = desc_get_buf(info, false);
616dc488f88SScott Feldman     if (!buf) {
617dc488f88SScott Feldman         err = -ROCKER_ENXIO;
618dc488f88SScott Feldman         goto out;
619dc488f88SScott Feldman     }
620dc488f88SScott Feldman     rocker_tlv_parse(tlvs, ROCKER_TLV_RX_MAX, buf, desc_tlv_size(info));
621dc488f88SScott Feldman 
622dc488f88SScott Feldman     if (!tlvs[ROCKER_TLV_RX_FRAG_ADDR] ||
623dc488f88SScott Feldman         !tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]) {
624dc488f88SScott Feldman         err = -ROCKER_EINVAL;
625dc488f88SScott Feldman         goto out;
626dc488f88SScott Feldman     }
627dc488f88SScott Feldman 
628dc488f88SScott Feldman     frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_RX_FRAG_ADDR]);
629dc488f88SScott Feldman     frag_max_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
630dc488f88SScott Feldman 
631dc488f88SScott Feldman     if (data_size > frag_max_len) {
632dc488f88SScott Feldman         err = -ROCKER_EMSGSIZE;
633dc488f88SScott Feldman         goto out;
634dc488f88SScott Feldman     }
635dc488f88SScott Feldman 
636d0d25558SScott Feldman     if (copy_to_cpu) {
637d0d25558SScott Feldman         rx_flags |= ROCKER_RX_FLAGS_FWD_OFFLOAD;
638d0d25558SScott Feldman     }
639d0d25558SScott Feldman 
640dc488f88SScott Feldman     /* XXX calc rx flags/csum */
641dc488f88SScott Feldman 
642dc488f88SScott Feldman     tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* flags */
643dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint16_t)) + /* scum */
644dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint64_t)) + /* frag addr */
645dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint16_t)) + /* frag max len */
646dc488f88SScott Feldman                rocker_tlv_total_size(sizeof(uint16_t));  /* frag len */
647dc488f88SScott Feldman 
648dc488f88SScott Feldman     if (tlv_size > desc_buf_size(info)) {
649dc488f88SScott Feldman         err = -ROCKER_EMSGSIZE;
650dc488f88SScott Feldman         goto out;
651dc488f88SScott Feldman     }
652dc488f88SScott Feldman 
653dc488f88SScott Feldman     /* TODO:
654dc488f88SScott Feldman      * iov dma write can be optimized in similar way e1000 does it in
655dc488f88SScott Feldman      * e1000_receive_iov. But maybe if would make sense to introduce
656dc488f88SScott Feldman      * generic helper iov_dma_write.
657dc488f88SScott Feldman      */
658dc488f88SScott Feldman 
659dc488f88SScott Feldman     data = g_malloc(data_size);
660107e4b35SMao Zhongyi 
661dc488f88SScott Feldman     iov_to_buf(iov, iovcnt, 0, data, data_size);
662dc488f88SScott Feldman     pci_dma_write(dev, frag_addr, data, data_size);
663dc488f88SScott Feldman     g_free(data);
664dc488f88SScott Feldman 
665dc488f88SScott Feldman     pos = 0;
666dc488f88SScott Feldman     rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FLAGS, rx_flags);
667dc488f88SScott Feldman     rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_CSUM, rx_csum);
668dc488f88SScott Feldman     rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_RX_FRAG_ADDR, frag_addr);
669dc488f88SScott Feldman     rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_MAX_LEN, frag_max_len);
670dc488f88SScott Feldman     rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_LEN, data_size);
671dc488f88SScott Feldman 
672dc488f88SScott Feldman     err = desc_set_buf(info, tlv_size);
673dc488f88SScott Feldman 
674dc488f88SScott Feldman out:
675dc488f88SScott Feldman     if (desc_ring_post_desc(ring, err)) {
676dc488f88SScott Feldman         rocker_msix_irq(r, ROCKER_MSIX_VEC_RX(pport - 1));
677dc488f88SScott Feldman     }
678dc488f88SScott Feldman 
679dc488f88SScott Feldman     return err;
680dc488f88SScott Feldman }
681dc488f88SScott Feldman 
rocker_port_eg(Rocker * r,uint32_t pport,const struct iovec * iov,int iovcnt)682dc488f88SScott Feldman int rocker_port_eg(Rocker *r, uint32_t pport,
683dc488f88SScott Feldman                    const struct iovec *iov, int iovcnt)
684dc488f88SScott Feldman {
685dc488f88SScott Feldman     FpPort *fp_port;
686dc488f88SScott Feldman     uint32_t port;
687dc488f88SScott Feldman 
688dc488f88SScott Feldman     if (!fp_port_from_pport(pport, &port)) {
689dc488f88SScott Feldman         return -ROCKER_EINVAL;
690dc488f88SScott Feldman     }
691dc488f88SScott Feldman 
692dc488f88SScott Feldman     fp_port = r->fp_port[port];
693dc488f88SScott Feldman 
694dc488f88SScott Feldman     return fp_port_eg(fp_port, iov, iovcnt);
695dc488f88SScott Feldman }
696dc488f88SScott Feldman 
rocker_test_dma_ctrl(Rocker * r,uint32_t val)697dc488f88SScott Feldman static void rocker_test_dma_ctrl(Rocker *r, uint32_t val)
698dc488f88SScott Feldman {
699dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
700dc488f88SScott Feldman     char *buf;
701dc488f88SScott Feldman     int i;
702dc488f88SScott Feldman 
703dc488f88SScott Feldman     buf = g_malloc(r->test_dma_size);
704dc488f88SScott Feldman 
705dc488f88SScott Feldman     switch (val) {
706dc488f88SScott Feldman     case ROCKER_TEST_DMA_CTRL_CLEAR:
707dc488f88SScott Feldman         memset(buf, 0, r->test_dma_size);
708dc488f88SScott Feldman         break;
709dc488f88SScott Feldman     case ROCKER_TEST_DMA_CTRL_FILL:
710dc488f88SScott Feldman         memset(buf, 0x96, r->test_dma_size);
711dc488f88SScott Feldman         break;
712dc488f88SScott Feldman     case ROCKER_TEST_DMA_CTRL_INVERT:
713dc488f88SScott Feldman         pci_dma_read(dev, r->test_dma_addr, buf, r->test_dma_size);
714dc488f88SScott Feldman         for (i = 0; i < r->test_dma_size; i++) {
715dc488f88SScott Feldman             buf[i] = ~buf[i];
716dc488f88SScott Feldman         }
717dc488f88SScott Feldman         break;
718dc488f88SScott Feldman     default:
719dc488f88SScott Feldman         DPRINTF("not test dma control val=0x%08x\n", val);
720dc488f88SScott Feldman         goto err_out;
721dc488f88SScott Feldman     }
722dc488f88SScott Feldman     pci_dma_write(dev, r->test_dma_addr, buf, r->test_dma_size);
723dc488f88SScott Feldman 
724dc488f88SScott Feldman     rocker_msix_irq(r, ROCKER_MSIX_VEC_TEST);
725dc488f88SScott Feldman 
726dc488f88SScott Feldman err_out:
727dc488f88SScott Feldman     g_free(buf);
728dc488f88SScott Feldman }
729dc488f88SScott Feldman 
730dc488f88SScott Feldman static void rocker_reset(DeviceState *dev);
731dc488f88SScott Feldman 
rocker_control(Rocker * r,uint32_t val)732dc488f88SScott Feldman static void rocker_control(Rocker *r, uint32_t val)
733dc488f88SScott Feldman {
734dc488f88SScott Feldman     if (val & ROCKER_CONTROL_RESET) {
735dc488f88SScott Feldman         rocker_reset(DEVICE(r));
736dc488f88SScott Feldman     }
737dc488f88SScott Feldman }
738dc488f88SScott Feldman 
rocker_pci_ring_count(Rocker * r)739dc488f88SScott Feldman static int rocker_pci_ring_count(Rocker *r)
740dc488f88SScott Feldman {
741dc488f88SScott Feldman     /* There are:
742dc488f88SScott Feldman      * - command ring
743dc488f88SScott Feldman      * - event ring
744dc488f88SScott Feldman      * - tx and rx ring per each port
745dc488f88SScott Feldman      */
746dc488f88SScott Feldman     return 2 + (2 * r->fp_ports);
747dc488f88SScott Feldman }
748dc488f88SScott Feldman 
rocker_addr_is_desc_reg(Rocker * r,hwaddr addr)749dc488f88SScott Feldman static bool rocker_addr_is_desc_reg(Rocker *r, hwaddr addr)
750dc488f88SScott Feldman {
751dc488f88SScott Feldman     hwaddr start = ROCKER_DMA_DESC_BASE;
752dc488f88SScott Feldman     hwaddr end = start + (ROCKER_DMA_DESC_SIZE * rocker_pci_ring_count(r));
753dc488f88SScott Feldman 
754dc488f88SScott Feldman     return addr >= start && addr < end;
755dc488f88SScott Feldman }
756dc488f88SScott Feldman 
rocker_port_phys_enable_write(Rocker * r,uint64_t new)757dc488f88SScott Feldman static void rocker_port_phys_enable_write(Rocker *r, uint64_t new)
758dc488f88SScott Feldman {
759dc488f88SScott Feldman     int i;
760dc488f88SScott Feldman     bool old_enabled;
761dc488f88SScott Feldman     bool new_enabled;
762dc488f88SScott Feldman     FpPort *fp_port;
763dc488f88SScott Feldman 
764dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
765dc488f88SScott Feldman         fp_port = r->fp_port[i];
766dc488f88SScott Feldman         old_enabled = fp_port_enabled(fp_port);
767dc488f88SScott Feldman         new_enabled = (new >> (i + 1)) & 0x1;
768dc488f88SScott Feldman         if (new_enabled == old_enabled) {
769dc488f88SScott Feldman             continue;
770dc488f88SScott Feldman         }
771dc488f88SScott Feldman         if (new_enabled) {
772dc488f88SScott Feldman             fp_port_enable(r->fp_port[i]);
773dc488f88SScott Feldman         } else {
774dc488f88SScott Feldman             fp_port_disable(r->fp_port[i]);
775dc488f88SScott Feldman         }
776dc488f88SScott Feldman     }
777dc488f88SScott Feldman }
778dc488f88SScott Feldman 
rocker_io_writel(void * opaque,hwaddr addr,uint32_t val)779dc488f88SScott Feldman static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val)
780dc488f88SScott Feldman {
781dc488f88SScott Feldman     Rocker *r = opaque;
782dc488f88SScott Feldman 
783dc488f88SScott Feldman     if (rocker_addr_is_desc_reg(r, addr)) {
784dc488f88SScott Feldman         unsigned index = ROCKER_RING_INDEX(addr);
785dc488f88SScott Feldman         unsigned offset = addr & ROCKER_DMA_DESC_MASK;
786dc488f88SScott Feldman 
787dc488f88SScott Feldman         switch (offset) {
788dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET:
789dc488f88SScott Feldman             r->lower32 = (uint64_t)val;
790dc488f88SScott Feldman             break;
791dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
792dc488f88SScott Feldman             desc_ring_set_base_addr(r->rings[index],
793dc488f88SScott Feldman                                     ((uint64_t)val) << 32 | r->lower32);
794dc488f88SScott Feldman             r->lower32 = 0;
795dc488f88SScott Feldman             break;
796dc488f88SScott Feldman         case ROCKER_DMA_DESC_SIZE_OFFSET:
797dc488f88SScott Feldman             desc_ring_set_size(r->rings[index], val);
798dc488f88SScott Feldman             break;
799dc488f88SScott Feldman         case ROCKER_DMA_DESC_HEAD_OFFSET:
800dc488f88SScott Feldman             if (desc_ring_set_head(r->rings[index], val)) {
801dc488f88SScott Feldman                 rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
802dc488f88SScott Feldman             }
803dc488f88SScott Feldman             break;
804dc488f88SScott Feldman         case ROCKER_DMA_DESC_CTRL_OFFSET:
805dc488f88SScott Feldman             desc_ring_set_ctrl(r->rings[index], val);
806dc488f88SScott Feldman             break;
807dc488f88SScott Feldman         case ROCKER_DMA_DESC_CREDITS_OFFSET:
808dc488f88SScott Feldman             if (desc_ring_ret_credits(r->rings[index], val)) {
809dc488f88SScott Feldman                 rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
810dc488f88SScott Feldman             }
811dc488f88SScott Feldman             break;
812dc488f88SScott Feldman         default:
813883f2c59SPhilippe Mathieu-Daudé             DPRINTF("not implemented dma reg write(l) addr=0x" HWADDR_FMT_plx
814dc488f88SScott Feldman                     " val=0x%08x (ring %d, addr=0x%02x)\n",
815dc488f88SScott Feldman                     addr, val, index, offset);
816dc488f88SScott Feldman             break;
817dc488f88SScott Feldman         }
818dc488f88SScott Feldman         return;
819dc488f88SScott Feldman     }
820dc488f88SScott Feldman 
821dc488f88SScott Feldman     switch (addr) {
822dc488f88SScott Feldman     case ROCKER_TEST_REG:
823dc488f88SScott Feldman         r->test_reg = val;
824dc488f88SScott Feldman         break;
825dc488f88SScott Feldman     case ROCKER_TEST_REG64:
826dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR:
827dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE:
828dc488f88SScott Feldman         r->lower32 = (uint64_t)val;
829dc488f88SScott Feldman         break;
830dc488f88SScott Feldman     case ROCKER_TEST_REG64 + 4:
831dc488f88SScott Feldman         r->test_reg64 = ((uint64_t)val) << 32 | r->lower32;
832dc488f88SScott Feldman         r->lower32 = 0;
833dc488f88SScott Feldman         break;
834dc488f88SScott Feldman     case ROCKER_TEST_IRQ:
835dc488f88SScott Feldman         rocker_msix_irq(r, val);
836dc488f88SScott Feldman         break;
837dc488f88SScott Feldman     case ROCKER_TEST_DMA_SIZE:
8388caed3d5SPrasad J Pandit         r->test_dma_size = val & 0xFFFF;
839dc488f88SScott Feldman         break;
840dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR + 4:
841dc488f88SScott Feldman         r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32;
842dc488f88SScott Feldman         r->lower32 = 0;
843dc488f88SScott Feldman         break;
844dc488f88SScott Feldman     case ROCKER_TEST_DMA_CTRL:
845dc488f88SScott Feldman         rocker_test_dma_ctrl(r, val);
846dc488f88SScott Feldman         break;
847dc488f88SScott Feldman     case ROCKER_CONTROL:
848dc488f88SScott Feldman         rocker_control(r, val);
849dc488f88SScott Feldman         break;
850dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE + 4:
851dc488f88SScott Feldman         rocker_port_phys_enable_write(r, ((uint64_t)val) << 32 | r->lower32);
852dc488f88SScott Feldman         r->lower32 = 0;
853dc488f88SScott Feldman         break;
854dc488f88SScott Feldman     default:
855883f2c59SPhilippe Mathieu-Daudé         DPRINTF("not implemented write(l) addr=0x" HWADDR_FMT_plx
856dc488f88SScott Feldman                 " val=0x%08x\n", addr, val);
857dc488f88SScott Feldman         break;
858dc488f88SScott Feldman     }
859dc488f88SScott Feldman }
860dc488f88SScott Feldman 
rocker_io_writeq(void * opaque,hwaddr addr,uint64_t val)861dc488f88SScott Feldman static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val)
862dc488f88SScott Feldman {
863dc488f88SScott Feldman     Rocker *r = opaque;
864dc488f88SScott Feldman 
865dc488f88SScott Feldman     if (rocker_addr_is_desc_reg(r, addr)) {
866dc488f88SScott Feldman         unsigned index = ROCKER_RING_INDEX(addr);
867dc488f88SScott Feldman         unsigned offset = addr & ROCKER_DMA_DESC_MASK;
868dc488f88SScott Feldman 
869dc488f88SScott Feldman         switch (offset) {
870dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET:
871dc488f88SScott Feldman             desc_ring_set_base_addr(r->rings[index], val);
872dc488f88SScott Feldman             break;
873dc488f88SScott Feldman         default:
874883f2c59SPhilippe Mathieu-Daudé             DPRINTF("not implemented dma reg write(q) addr=0x" HWADDR_FMT_plx
875883f2c59SPhilippe Mathieu-Daudé                     " val=0x" HWADDR_FMT_plx " (ring %d, offset=0x%02x)\n",
876dc488f88SScott Feldman                     addr, val, index, offset);
877dc488f88SScott Feldman             break;
878dc488f88SScott Feldman         }
879dc488f88SScott Feldman         return;
880dc488f88SScott Feldman     }
881dc488f88SScott Feldman 
882dc488f88SScott Feldman     switch (addr) {
883dc488f88SScott Feldman     case ROCKER_TEST_REG64:
884dc488f88SScott Feldman         r->test_reg64 = val;
885dc488f88SScott Feldman         break;
886dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR:
887dc488f88SScott Feldman         r->test_dma_addr = val;
888dc488f88SScott Feldman         break;
889dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE:
890dc488f88SScott Feldman         rocker_port_phys_enable_write(r, val);
891dc488f88SScott Feldman         break;
892dc488f88SScott Feldman     default:
893883f2c59SPhilippe Mathieu-Daudé         DPRINTF("not implemented write(q) addr=0x" HWADDR_FMT_plx
894883f2c59SPhilippe Mathieu-Daudé                 " val=0x" HWADDR_FMT_plx "\n", addr, val);
895dc488f88SScott Feldman         break;
896dc488f88SScott Feldman     }
897dc488f88SScott Feldman }
898dc488f88SScott Feldman 
899dc488f88SScott Feldman #ifdef DEBUG_ROCKER
900dc488f88SScott Feldman #define regname(reg) case (reg): return #reg
rocker_reg_name(void * opaque,hwaddr addr)901dc488f88SScott Feldman static const char *rocker_reg_name(void *opaque, hwaddr addr)
902dc488f88SScott Feldman {
903dc488f88SScott Feldman     Rocker *r = opaque;
904dc488f88SScott Feldman 
905dc488f88SScott Feldman     if (rocker_addr_is_desc_reg(r, addr)) {
906dc488f88SScott Feldman         unsigned index = ROCKER_RING_INDEX(addr);
907dc488f88SScott Feldman         unsigned offset = addr & ROCKER_DMA_DESC_MASK;
908dc488f88SScott Feldman         static char buf[100];
909dc488f88SScott Feldman         char ring_name[10];
910dc488f88SScott Feldman 
911dc488f88SScott Feldman         switch (index) {
912dc488f88SScott Feldman         case 0:
913dc488f88SScott Feldman             sprintf(ring_name, "cmd");
914dc488f88SScott Feldman             break;
915dc488f88SScott Feldman         case 1:
916dc488f88SScott Feldman             sprintf(ring_name, "event");
917dc488f88SScott Feldman             break;
918dc488f88SScott Feldman         default:
919dc488f88SScott Feldman             sprintf(ring_name, "%s-%d", index % 2 ? "rx" : "tx",
920dc488f88SScott Feldman                     (index - 2) / 2);
921dc488f88SScott Feldman         }
922dc488f88SScott Feldman 
923dc488f88SScott Feldman         switch (offset) {
924dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET:
925dc488f88SScott Feldman             sprintf(buf, "Ring[%s] ADDR", ring_name);
926dc488f88SScott Feldman             return buf;
927dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET+4:
928dc488f88SScott Feldman             sprintf(buf, "Ring[%s] ADDR+4", ring_name);
929dc488f88SScott Feldman             return buf;
930dc488f88SScott Feldman         case ROCKER_DMA_DESC_SIZE_OFFSET:
931dc488f88SScott Feldman             sprintf(buf, "Ring[%s] SIZE", ring_name);
932dc488f88SScott Feldman             return buf;
933dc488f88SScott Feldman         case ROCKER_DMA_DESC_HEAD_OFFSET:
934dc488f88SScott Feldman             sprintf(buf, "Ring[%s] HEAD", ring_name);
935dc488f88SScott Feldman             return buf;
936dc488f88SScott Feldman         case ROCKER_DMA_DESC_TAIL_OFFSET:
937dc488f88SScott Feldman             sprintf(buf, "Ring[%s] TAIL", ring_name);
938dc488f88SScott Feldman             return buf;
939dc488f88SScott Feldman         case ROCKER_DMA_DESC_CTRL_OFFSET:
940dc488f88SScott Feldman             sprintf(buf, "Ring[%s] CTRL", ring_name);
941dc488f88SScott Feldman             return buf;
942dc488f88SScott Feldman         case ROCKER_DMA_DESC_CREDITS_OFFSET:
943dc488f88SScott Feldman             sprintf(buf, "Ring[%s] CREDITS", ring_name);
944dc488f88SScott Feldman             return buf;
945dc488f88SScott Feldman         default:
946dc488f88SScott Feldman             sprintf(buf, "Ring[%s] ???", ring_name);
947dc488f88SScott Feldman             return buf;
948dc488f88SScott Feldman         }
949dc488f88SScott Feldman     } else {
950dc488f88SScott Feldman         switch (addr) {
951dc488f88SScott Feldman             regname(ROCKER_BOGUS_REG0);
952dc488f88SScott Feldman             regname(ROCKER_BOGUS_REG1);
953dc488f88SScott Feldman             regname(ROCKER_BOGUS_REG2);
954dc488f88SScott Feldman             regname(ROCKER_BOGUS_REG3);
955dc488f88SScott Feldman             regname(ROCKER_TEST_REG);
956dc488f88SScott Feldman             regname(ROCKER_TEST_REG64);
957dc488f88SScott Feldman             regname(ROCKER_TEST_REG64+4);
958dc488f88SScott Feldman             regname(ROCKER_TEST_IRQ);
959dc488f88SScott Feldman             regname(ROCKER_TEST_DMA_ADDR);
960dc488f88SScott Feldman             regname(ROCKER_TEST_DMA_ADDR+4);
961dc488f88SScott Feldman             regname(ROCKER_TEST_DMA_SIZE);
962dc488f88SScott Feldman             regname(ROCKER_TEST_DMA_CTRL);
963dc488f88SScott Feldman             regname(ROCKER_CONTROL);
964dc488f88SScott Feldman             regname(ROCKER_PORT_PHYS_COUNT);
965dc488f88SScott Feldman             regname(ROCKER_PORT_PHYS_LINK_STATUS);
966dc488f88SScott Feldman             regname(ROCKER_PORT_PHYS_LINK_STATUS+4);
967dc488f88SScott Feldman             regname(ROCKER_PORT_PHYS_ENABLE);
968dc488f88SScott Feldman             regname(ROCKER_PORT_PHYS_ENABLE+4);
969dc488f88SScott Feldman             regname(ROCKER_SWITCH_ID);
970dc488f88SScott Feldman             regname(ROCKER_SWITCH_ID+4);
971dc488f88SScott Feldman         }
972dc488f88SScott Feldman     }
973dc488f88SScott Feldman     return "???";
974dc488f88SScott Feldman }
975dc488f88SScott Feldman #else
rocker_reg_name(void * opaque,hwaddr addr)976dc488f88SScott Feldman static const char *rocker_reg_name(void *opaque, hwaddr addr)
977dc488f88SScott Feldman {
978dc488f88SScott Feldman     return NULL;
979dc488f88SScott Feldman }
980dc488f88SScott Feldman #endif
981dc488f88SScott Feldman 
rocker_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)982dc488f88SScott Feldman static void rocker_mmio_write(void *opaque, hwaddr addr, uint64_t val,
983dc488f88SScott Feldman                               unsigned size)
984dc488f88SScott Feldman {
985883f2c59SPhilippe Mathieu-Daudé     DPRINTF("Write %s addr " HWADDR_FMT_plx
986883f2c59SPhilippe Mathieu-Daudé             ", size %u, val " HWADDR_FMT_plx "\n",
987dc488f88SScott Feldman             rocker_reg_name(opaque, addr), addr, size, val);
988dc488f88SScott Feldman 
989dc488f88SScott Feldman     switch (size) {
990dc488f88SScott Feldman     case 4:
991dc488f88SScott Feldman         rocker_io_writel(opaque, addr, val);
992dc488f88SScott Feldman         break;
993dc488f88SScott Feldman     case 8:
994dc488f88SScott Feldman         rocker_io_writeq(opaque, addr, val);
995dc488f88SScott Feldman         break;
996dc488f88SScott Feldman     }
997dc488f88SScott Feldman }
998dc488f88SScott Feldman 
rocker_port_phys_link_status(Rocker * r)999dc488f88SScott Feldman static uint64_t rocker_port_phys_link_status(Rocker *r)
1000dc488f88SScott Feldman {
1001dc488f88SScott Feldman     int i;
1002dc488f88SScott Feldman     uint64_t status = 0;
1003dc488f88SScott Feldman 
1004dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
1005dc488f88SScott Feldman         FpPort *port = r->fp_port[i];
1006dc488f88SScott Feldman 
1007dc488f88SScott Feldman         if (fp_port_get_link_up(port)) {
10087cf745ddSPeter Maydell             status |= 1ULL << (i + 1);
1009dc488f88SScott Feldman         }
1010dc488f88SScott Feldman     }
1011dc488f88SScott Feldman     return status;
1012dc488f88SScott Feldman }
1013dc488f88SScott Feldman 
rocker_port_phys_enable_read(Rocker * r)1014dc488f88SScott Feldman static uint64_t rocker_port_phys_enable_read(Rocker *r)
1015dc488f88SScott Feldman {
1016dc488f88SScott Feldman     int i;
1017dc488f88SScott Feldman     uint64_t ret = 0;
1018dc488f88SScott Feldman 
1019dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
1020dc488f88SScott Feldman         FpPort *port = r->fp_port[i];
1021dc488f88SScott Feldman 
1022dc488f88SScott Feldman         if (fp_port_enabled(port)) {
10237cf745ddSPeter Maydell             ret |= 1ULL << (i + 1);
1024dc488f88SScott Feldman         }
1025dc488f88SScott Feldman     }
1026dc488f88SScott Feldman     return ret;
1027dc488f88SScott Feldman }
1028dc488f88SScott Feldman 
rocker_io_readl(void * opaque,hwaddr addr)1029dc488f88SScott Feldman static uint32_t rocker_io_readl(void *opaque, hwaddr addr)
1030dc488f88SScott Feldman {
1031dc488f88SScott Feldman     Rocker *r = opaque;
1032dc488f88SScott Feldman     uint32_t ret;
1033dc488f88SScott Feldman 
1034dc488f88SScott Feldman     if (rocker_addr_is_desc_reg(r, addr)) {
1035dc488f88SScott Feldman         unsigned index = ROCKER_RING_INDEX(addr);
1036dc488f88SScott Feldman         unsigned offset = addr & ROCKER_DMA_DESC_MASK;
1037dc488f88SScott Feldman 
1038dc488f88SScott Feldman         switch (offset) {
1039dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET:
1040dc488f88SScott Feldman             ret = (uint32_t)desc_ring_get_base_addr(r->rings[index]);
1041dc488f88SScott Feldman             break;
1042dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
1043dc488f88SScott Feldman             ret = (uint32_t)(desc_ring_get_base_addr(r->rings[index]) >> 32);
1044dc488f88SScott Feldman             break;
1045dc488f88SScott Feldman         case ROCKER_DMA_DESC_SIZE_OFFSET:
1046dc488f88SScott Feldman             ret = desc_ring_get_size(r->rings[index]);
1047dc488f88SScott Feldman             break;
1048dc488f88SScott Feldman         case ROCKER_DMA_DESC_HEAD_OFFSET:
1049dc488f88SScott Feldman             ret = desc_ring_get_head(r->rings[index]);
1050dc488f88SScott Feldman             break;
1051dc488f88SScott Feldman         case ROCKER_DMA_DESC_TAIL_OFFSET:
1052dc488f88SScott Feldman             ret = desc_ring_get_tail(r->rings[index]);
1053dc488f88SScott Feldman             break;
1054dc488f88SScott Feldman         case ROCKER_DMA_DESC_CREDITS_OFFSET:
1055dc488f88SScott Feldman             ret = desc_ring_get_credits(r->rings[index]);
1056dc488f88SScott Feldman             break;
1057dc488f88SScott Feldman         default:
1058883f2c59SPhilippe Mathieu-Daudé             DPRINTF("not implemented dma reg read(l) addr=0x" HWADDR_FMT_plx
1059dc488f88SScott Feldman                     " (ring %d, addr=0x%02x)\n", addr, index, offset);
1060dc488f88SScott Feldman             ret = 0;
1061dc488f88SScott Feldman             break;
1062dc488f88SScott Feldman         }
1063dc488f88SScott Feldman         return ret;
1064dc488f88SScott Feldman     }
1065dc488f88SScott Feldman 
1066dc488f88SScott Feldman     switch (addr) {
1067dc488f88SScott Feldman     case ROCKER_BOGUS_REG0:
1068dc488f88SScott Feldman     case ROCKER_BOGUS_REG1:
1069dc488f88SScott Feldman     case ROCKER_BOGUS_REG2:
1070dc488f88SScott Feldman     case ROCKER_BOGUS_REG3:
1071dc488f88SScott Feldman         ret = 0xDEADBABE;
1072dc488f88SScott Feldman         break;
1073dc488f88SScott Feldman     case ROCKER_TEST_REG:
1074dc488f88SScott Feldman         ret = r->test_reg * 2;
1075dc488f88SScott Feldman         break;
1076dc488f88SScott Feldman     case ROCKER_TEST_REG64:
1077dc488f88SScott Feldman         ret = (uint32_t)(r->test_reg64 * 2);
1078dc488f88SScott Feldman         break;
1079dc488f88SScott Feldman     case ROCKER_TEST_REG64 + 4:
1080dc488f88SScott Feldman         ret = (uint32_t)((r->test_reg64 * 2) >> 32);
1081dc488f88SScott Feldman         break;
1082dc488f88SScott Feldman     case ROCKER_TEST_DMA_SIZE:
1083dc488f88SScott Feldman         ret = r->test_dma_size;
1084dc488f88SScott Feldman         break;
1085dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR:
1086dc488f88SScott Feldman         ret = (uint32_t)r->test_dma_addr;
1087dc488f88SScott Feldman         break;
1088dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR + 4:
1089dc488f88SScott Feldman         ret = (uint32_t)(r->test_dma_addr >> 32);
1090dc488f88SScott Feldman         break;
1091dc488f88SScott Feldman     case ROCKER_PORT_PHYS_COUNT:
1092dc488f88SScott Feldman         ret = r->fp_ports;
1093dc488f88SScott Feldman         break;
1094dc488f88SScott Feldman     case ROCKER_PORT_PHYS_LINK_STATUS:
1095dc488f88SScott Feldman         ret = (uint32_t)rocker_port_phys_link_status(r);
1096dc488f88SScott Feldman         break;
1097dc488f88SScott Feldman     case ROCKER_PORT_PHYS_LINK_STATUS + 4:
1098dc488f88SScott Feldman         ret = (uint32_t)(rocker_port_phys_link_status(r) >> 32);
1099dc488f88SScott Feldman         break;
1100dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE:
1101dc488f88SScott Feldman         ret = (uint32_t)rocker_port_phys_enable_read(r);
1102dc488f88SScott Feldman         break;
1103dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE + 4:
1104dc488f88SScott Feldman         ret = (uint32_t)(rocker_port_phys_enable_read(r) >> 32);
1105dc488f88SScott Feldman         break;
1106dc488f88SScott Feldman     case ROCKER_SWITCH_ID:
1107dc488f88SScott Feldman         ret = (uint32_t)r->switch_id;
1108dc488f88SScott Feldman         break;
1109dc488f88SScott Feldman     case ROCKER_SWITCH_ID + 4:
1110dc488f88SScott Feldman         ret = (uint32_t)(r->switch_id >> 32);
1111dc488f88SScott Feldman         break;
1112dc488f88SScott Feldman     default:
1113883f2c59SPhilippe Mathieu-Daudé         DPRINTF("not implemented read(l) addr=0x" HWADDR_FMT_plx "\n", addr);
1114dc488f88SScott Feldman         ret = 0;
1115dc488f88SScott Feldman         break;
1116dc488f88SScott Feldman     }
1117dc488f88SScott Feldman     return ret;
1118dc488f88SScott Feldman }
1119dc488f88SScott Feldman 
rocker_io_readq(void * opaque,hwaddr addr)1120dc488f88SScott Feldman static uint64_t rocker_io_readq(void *opaque, hwaddr addr)
1121dc488f88SScott Feldman {
1122dc488f88SScott Feldman     Rocker *r = opaque;
1123dc488f88SScott Feldman     uint64_t ret;
1124dc488f88SScott Feldman 
1125dc488f88SScott Feldman     if (rocker_addr_is_desc_reg(r, addr)) {
1126dc488f88SScott Feldman         unsigned index = ROCKER_RING_INDEX(addr);
1127dc488f88SScott Feldman         unsigned offset = addr & ROCKER_DMA_DESC_MASK;
1128dc488f88SScott Feldman 
1129dc488f88SScott Feldman         switch (addr & ROCKER_DMA_DESC_MASK) {
1130dc488f88SScott Feldman         case ROCKER_DMA_DESC_ADDR_OFFSET:
1131dc488f88SScott Feldman             ret = desc_ring_get_base_addr(r->rings[index]);
1132dc488f88SScott Feldman             break;
1133dc488f88SScott Feldman         default:
1134883f2c59SPhilippe Mathieu-Daudé             DPRINTF("not implemented dma reg read(q) addr=0x" HWADDR_FMT_plx
1135dc488f88SScott Feldman                     " (ring %d, addr=0x%02x)\n", addr, index, offset);
1136dc488f88SScott Feldman             ret = 0;
1137dc488f88SScott Feldman             break;
1138dc488f88SScott Feldman         }
1139dc488f88SScott Feldman         return ret;
1140dc488f88SScott Feldman     }
1141dc488f88SScott Feldman 
1142dc488f88SScott Feldman     switch (addr) {
1143dc488f88SScott Feldman     case ROCKER_BOGUS_REG0:
1144dc488f88SScott Feldman     case ROCKER_BOGUS_REG2:
1145dc488f88SScott Feldman         ret = 0xDEADBABEDEADBABEULL;
1146dc488f88SScott Feldman         break;
1147dc488f88SScott Feldman     case ROCKER_TEST_REG64:
1148dc488f88SScott Feldman         ret = r->test_reg64 * 2;
1149dc488f88SScott Feldman         break;
1150dc488f88SScott Feldman     case ROCKER_TEST_DMA_ADDR:
1151dc488f88SScott Feldman         ret = r->test_dma_addr;
1152dc488f88SScott Feldman         break;
1153dc488f88SScott Feldman     case ROCKER_PORT_PHYS_LINK_STATUS:
1154dc488f88SScott Feldman         ret = rocker_port_phys_link_status(r);
1155dc488f88SScott Feldman         break;
1156dc488f88SScott Feldman     case ROCKER_PORT_PHYS_ENABLE:
1157dc488f88SScott Feldman         ret = rocker_port_phys_enable_read(r);
1158dc488f88SScott Feldman         break;
1159dc488f88SScott Feldman     case ROCKER_SWITCH_ID:
1160dc488f88SScott Feldman         ret = r->switch_id;
1161dc488f88SScott Feldman         break;
1162dc488f88SScott Feldman     default:
1163883f2c59SPhilippe Mathieu-Daudé         DPRINTF("not implemented read(q) addr=0x" HWADDR_FMT_plx "\n", addr);
1164dc488f88SScott Feldman         ret = 0;
1165dc488f88SScott Feldman         break;
1166dc488f88SScott Feldman     }
1167dc488f88SScott Feldman     return ret;
1168dc488f88SScott Feldman }
1169dc488f88SScott Feldman 
rocker_mmio_read(void * opaque,hwaddr addr,unsigned size)1170dc488f88SScott Feldman static uint64_t rocker_mmio_read(void *opaque, hwaddr addr, unsigned size)
1171dc488f88SScott Feldman {
1172883f2c59SPhilippe Mathieu-Daudé     DPRINTF("Read %s addr " HWADDR_FMT_plx ", size %u\n",
1173dc488f88SScott Feldman             rocker_reg_name(opaque, addr), addr, size);
1174dc488f88SScott Feldman 
1175dc488f88SScott Feldman     switch (size) {
1176dc488f88SScott Feldman     case 4:
1177dc488f88SScott Feldman         return rocker_io_readl(opaque, addr);
1178dc488f88SScott Feldman     case 8:
1179dc488f88SScott Feldman         return rocker_io_readq(opaque, addr);
1180dc488f88SScott Feldman     }
1181dc488f88SScott Feldman 
1182dc488f88SScott Feldman     return -1;
1183dc488f88SScott Feldman }
1184dc488f88SScott Feldman 
1185dc488f88SScott Feldman static const MemoryRegionOps rocker_mmio_ops = {
1186dc488f88SScott Feldman     .read = rocker_mmio_read,
1187dc488f88SScott Feldman     .write = rocker_mmio_write,
1188dc488f88SScott Feldman     .endianness = DEVICE_LITTLE_ENDIAN,
1189dc488f88SScott Feldman     .valid = {
1190dc488f88SScott Feldman         .min_access_size = 4,
1191dc488f88SScott Feldman         .max_access_size = 8,
1192dc488f88SScott Feldman     },
1193dc488f88SScott Feldman     .impl = {
1194dc488f88SScott Feldman         .min_access_size = 4,
1195dc488f88SScott Feldman         .max_access_size = 8,
1196dc488f88SScott Feldman     },
1197dc488f88SScott Feldman };
1198dc488f88SScott Feldman 
rocker_msix_vectors_unuse(Rocker * r,unsigned int num_vectors)1199dc488f88SScott Feldman static void rocker_msix_vectors_unuse(Rocker *r,
1200dc488f88SScott Feldman                                       unsigned int num_vectors)
1201dc488f88SScott Feldman {
1202dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
1203dc488f88SScott Feldman     int i;
1204dc488f88SScott Feldman 
1205dc488f88SScott Feldman     for (i = 0; i < num_vectors; i++) {
1206dc488f88SScott Feldman         msix_vector_unuse(dev, i);
1207dc488f88SScott Feldman     }
1208dc488f88SScott Feldman }
1209dc488f88SScott Feldman 
rocker_msix_vectors_use(Rocker * r,unsigned int num_vectors)121015377f6eSAkihiko Odaki static void rocker_msix_vectors_use(Rocker *r, unsigned int num_vectors)
1211dc488f88SScott Feldman {
1212dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
1213dc488f88SScott Feldman     int i;
1214dc488f88SScott Feldman 
1215dc488f88SScott Feldman     for (i = 0; i < num_vectors; i++) {
121615377f6eSAkihiko Odaki         msix_vector_use(dev, i);
1217dc488f88SScott Feldman     }
1218dc488f88SScott Feldman }
1219dc488f88SScott Feldman 
rocker_msix_init(Rocker * r,Error ** errp)12200c8f86eaSMao Zhongyi static int rocker_msix_init(Rocker *r, Error **errp)
1221dc488f88SScott Feldman {
1222dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
1223dc488f88SScott Feldman     int err;
1224dc488f88SScott Feldman 
1225dc488f88SScott Feldman     err = msix_init(dev, ROCKER_MSIX_VEC_COUNT(r->fp_ports),
1226dc488f88SScott Feldman                     &r->msix_bar,
1227dc488f88SScott Feldman                     ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_TABLE_OFFSET,
1228dc488f88SScott Feldman                     &r->msix_bar,
1229dc488f88SScott Feldman                     ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_PBA_OFFSET,
12300c8f86eaSMao Zhongyi                     0, errp);
1231dc488f88SScott Feldman     if (err) {
1232dc488f88SScott Feldman         return err;
1233dc488f88SScott Feldman     }
1234dc488f88SScott Feldman 
123515377f6eSAkihiko Odaki     rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
1236dc488f88SScott Feldman 
1237dc488f88SScott Feldman     return 0;
1238dc488f88SScott Feldman }
1239dc488f88SScott Feldman 
rocker_msix_uninit(Rocker * r)1240dc488f88SScott Feldman static void rocker_msix_uninit(Rocker *r)
1241dc488f88SScott Feldman {
1242dc488f88SScott Feldman     PCIDevice *dev = PCI_DEVICE(r);
1243dc488f88SScott Feldman 
1244dc488f88SScott Feldman     msix_uninit(dev, &r->msix_bar, &r->msix_bar);
1245dc488f88SScott Feldman     rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
1246dc488f88SScott Feldman }
1247dc488f88SScott Feldman 
rocker_world_type_by_name(Rocker * r,const char * name)12489fe7101fSJiri Pirko static World *rocker_world_type_by_name(Rocker *r, const char *name)
12499fe7101fSJiri Pirko {
12509fe7101fSJiri Pirko     int i;
12519fe7101fSJiri Pirko 
12529fe7101fSJiri Pirko     for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
12539fe7101fSJiri Pirko         if (strcmp(name, world_name(r->worlds[i])) == 0) {
12549fe7101fSJiri Pirko             return r->worlds[i];
12559fe7101fSJiri Pirko         }
12569fe7101fSJiri Pirko     }
12579fe7101fSJiri Pirko     return NULL;
12589fe7101fSJiri Pirko }
12599fe7101fSJiri Pirko 
pci_rocker_realize(PCIDevice * dev,Error ** errp)12600c8f86eaSMao Zhongyi static void pci_rocker_realize(PCIDevice *dev, Error **errp)
1261dc488f88SScott Feldman {
1262ac7bc031SMao Zhongyi     Rocker *r = ROCKER(dev);
1263dc488f88SScott Feldman     const MACAddr zero = { .a = { 0, 0, 0, 0, 0, 0 } };
1264dc488f88SScott Feldman     const MACAddr dflt = { .a = { 0x52, 0x54, 0x00, 0x12, 0x35, 0x01 } };
1265dc488f88SScott Feldman     static int sw_index;
1266dc488f88SScott Feldman     int i, err = 0;
1267dc488f88SScott Feldman 
1268dc488f88SScott Feldman     /* allocate worlds */
1269dc488f88SScott Feldman 
1270dc488f88SScott Feldman     r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r);
1271dc488f88SScott Feldman 
12729fe7101fSJiri Pirko     if (!r->world_name) {
12739fe7101fSJiri Pirko         r->world_name = g_strdup(world_name(r->worlds[ROCKER_WORLD_TYPE_OF_DPA]));
12749fe7101fSJiri Pirko     }
12759fe7101fSJiri Pirko 
12769fe7101fSJiri Pirko     r->world_dflt = rocker_world_type_by_name(r, r->world_name);
12779fe7101fSJiri Pirko     if (!r->world_dflt) {
12780c8f86eaSMao Zhongyi         error_setg(errp,
12790c8f86eaSMao Zhongyi                 "invalid argument requested world %s does not exist",
12809fe7101fSJiri Pirko                 r->world_name);
12819fe7101fSJiri Pirko         goto err_world_type_by_name;
12829fe7101fSJiri Pirko     }
12839fe7101fSJiri Pirko 
1284dc488f88SScott Feldman     /* set up memory-mapped region at BAR0 */
1285dc488f88SScott Feldman 
1286dc488f88SScott Feldman     memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r,
1287dc488f88SScott Feldman                           "rocker-mmio", ROCKER_PCI_BAR0_SIZE);
1288dc488f88SScott Feldman     pci_register_bar(dev, ROCKER_PCI_BAR0_IDX,
1289dc488f88SScott Feldman                      PCI_BASE_ADDRESS_SPACE_MEMORY, &r->mmio);
1290dc488f88SScott Feldman 
1291dc488f88SScott Feldman     /* set up memory-mapped region for MSI-X */
1292dc488f88SScott Feldman 
1293dc488f88SScott Feldman     memory_region_init(&r->msix_bar, OBJECT(r), "rocker-msix-bar",
1294dc488f88SScott Feldman                        ROCKER_PCI_MSIX_BAR_SIZE);
1295dc488f88SScott Feldman     pci_register_bar(dev, ROCKER_PCI_MSIX_BAR_IDX,
1296dc488f88SScott Feldman                      PCI_BASE_ADDRESS_SPACE_MEMORY, &r->msix_bar);
1297dc488f88SScott Feldman 
1298dc488f88SScott Feldman     /* MSI-X init */
1299dc488f88SScott Feldman 
13000c8f86eaSMao Zhongyi     err = rocker_msix_init(r, errp);
1301dc488f88SScott Feldman     if (err) {
1302dc488f88SScott Feldman         goto err_msix_init;
1303dc488f88SScott Feldman     }
1304dc488f88SScott Feldman 
1305dc488f88SScott Feldman     /* validate switch properties */
1306dc488f88SScott Feldman 
1307dc488f88SScott Feldman     if (!r->name) {
1308ac7bc031SMao Zhongyi         r->name = g_strdup(TYPE_ROCKER);
1309dc488f88SScott Feldman     }
1310dc488f88SScott Feldman 
1311dc488f88SScott Feldman     if (rocker_find(r->name)) {
13120c8f86eaSMao Zhongyi         error_setg(errp, "%s already exists", r->name);
1313dc488f88SScott Feldman         goto err_duplicate;
1314dc488f88SScott Feldman     }
1315dc488f88SScott Feldman 
131677349536SDavid Ahern     /* Rocker name is passed in port name requests to OS with the intention
131777349536SDavid Ahern      * that the name is used in interface names. Limit the length of the
131877349536SDavid Ahern      * rocker name to avoid naming problems in the OS. Also, adding the
131977349536SDavid Ahern      * port number as p# and unganged breakout b#, where # is at most 2
132077349536SDavid Ahern      * digits, so leave room for it too (-1 for string terminator, -3 for
132177349536SDavid Ahern      * p# and -3 for b#)
132277349536SDavid Ahern      */
132377349536SDavid Ahern #define ROCKER_IFNAMSIZ 16
132477349536SDavid Ahern #define MAX_ROCKER_NAME_LEN  (ROCKER_IFNAMSIZ - 1 - 3 - 3)
132577349536SDavid Ahern     if (strlen(r->name) > MAX_ROCKER_NAME_LEN) {
13260c8f86eaSMao Zhongyi         error_setg(errp,
13270c8f86eaSMao Zhongyi                 "name too long; please shorten to at most %d chars",
132877349536SDavid Ahern                 MAX_ROCKER_NAME_LEN);
13291343a107SMao Zhongyi         goto err_name_too_long;
133077349536SDavid Ahern     }
133177349536SDavid Ahern 
1332dc488f88SScott Feldman     if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) {
1333dc488f88SScott Feldman         memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt));
1334dc488f88SScott Feldman         r->fp_start_macaddr.a[4] += (sw_index++);
1335dc488f88SScott Feldman     }
1336dc488f88SScott Feldman 
1337dc488f88SScott Feldman     if (!r->switch_id) {
1338dc488f88SScott Feldman         memcpy(&r->switch_id, &r->fp_start_macaddr,
1339dc488f88SScott Feldman                sizeof(r->fp_start_macaddr));
1340dc488f88SScott Feldman     }
1341dc488f88SScott Feldman 
1342dc488f88SScott Feldman     if (r->fp_ports > ROCKER_FP_PORTS_MAX) {
1343dc488f88SScott Feldman         r->fp_ports = ROCKER_FP_PORTS_MAX;
1344dc488f88SScott Feldman     }
1345dc488f88SScott Feldman 
1346778358d0SMarkus Armbruster     r->rings = g_new(DescRing *, rocker_pci_ring_count(r));
1347dc488f88SScott Feldman 
1348dc488f88SScott Feldman     /* Rings are ordered like this:
1349dc488f88SScott Feldman      * - command ring
1350dc488f88SScott Feldman      * - event ring
1351dc488f88SScott Feldman      * - port0 tx ring
1352dc488f88SScott Feldman      * - port0 rx ring
1353dc488f88SScott Feldman      * - port1 tx ring
1354dc488f88SScott Feldman      * - port1 rx ring
1355dc488f88SScott Feldman      * .....
1356dc488f88SScott Feldman      */
1357dc488f88SScott Feldman 
1358dc488f88SScott Feldman     for (i = 0; i < rocker_pci_ring_count(r); i++) {
1359dc488f88SScott Feldman         DescRing *ring = desc_ring_alloc(r, i);
1360dc488f88SScott Feldman 
1361dc488f88SScott Feldman         if (i == ROCKER_RING_CMD) {
1362dc488f88SScott Feldman             desc_ring_set_consume(ring, cmd_consume, ROCKER_MSIX_VEC_CMD);
1363dc488f88SScott Feldman         } else if (i == ROCKER_RING_EVENT) {
1364dc488f88SScott Feldman             desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_EVENT);
1365dc488f88SScott Feldman         } else if (i % 2 == 0) {
1366dc488f88SScott Feldman             desc_ring_set_consume(ring, tx_consume,
1367dc488f88SScott Feldman                                   ROCKER_MSIX_VEC_TX((i - 2) / 2));
1368dc488f88SScott Feldman         } else if (i % 2 == 1) {
1369dc488f88SScott Feldman             desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_RX((i - 3) / 2));
1370dc488f88SScott Feldman         }
1371dc488f88SScott Feldman 
1372dc488f88SScott Feldman         r->rings[i] = ring;
1373dc488f88SScott Feldman     }
1374dc488f88SScott Feldman 
1375dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
1376dc488f88SScott Feldman         FpPort *port =
1377dc488f88SScott Feldman             fp_port_alloc(r, r->name, &r->fp_start_macaddr,
1378dc488f88SScott Feldman                           i, &r->fp_ports_peers[i]);
1379dc488f88SScott Feldman 
1380dc488f88SScott Feldman         r->fp_port[i] = port;
1381dc488f88SScott Feldman         fp_port_set_world(port, r->world_dflt);
1382dc488f88SScott Feldman     }
1383dc488f88SScott Feldman 
1384dc488f88SScott Feldman     QLIST_INSERT_HEAD(&rockers, r, next);
1385dc488f88SScott Feldman 
13860c8f86eaSMao Zhongyi     return;
1387dc488f88SScott Feldman 
13881343a107SMao Zhongyi err_name_too_long:
1389dc488f88SScott Feldman err_duplicate:
1390dc488f88SScott Feldman     rocker_msix_uninit(r);
1391dc488f88SScott Feldman err_msix_init:
1392dc488f88SScott Feldman     object_unparent(OBJECT(&r->msix_bar));
1393dc488f88SScott Feldman     object_unparent(OBJECT(&r->mmio));
13949fe7101fSJiri Pirko err_world_type_by_name:
1395dc488f88SScott Feldman     for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1396dc488f88SScott Feldman         if (r->worlds[i]) {
1397dc488f88SScott Feldman             world_free(r->worlds[i]);
1398dc488f88SScott Feldman         }
1399dc488f88SScott Feldman     }
1400dc488f88SScott Feldman }
1401dc488f88SScott Feldman 
pci_rocker_uninit(PCIDevice * dev)1402dc488f88SScott Feldman static void pci_rocker_uninit(PCIDevice *dev)
1403dc488f88SScott Feldman {
1404ac7bc031SMao Zhongyi     Rocker *r = ROCKER(dev);
1405dc488f88SScott Feldman     int i;
1406dc488f88SScott Feldman 
1407dc488f88SScott Feldman     QLIST_REMOVE(r, next);
1408dc488f88SScott Feldman 
1409dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
1410dc488f88SScott Feldman         FpPort *port = r->fp_port[i];
1411dc488f88SScott Feldman 
1412dc488f88SScott Feldman         fp_port_free(port);
1413dc488f88SScott Feldman         r->fp_port[i] = NULL;
1414dc488f88SScott Feldman     }
1415dc488f88SScott Feldman 
1416dc488f88SScott Feldman     for (i = 0; i < rocker_pci_ring_count(r); i++) {
1417dc488f88SScott Feldman         if (r->rings[i]) {
1418dc488f88SScott Feldman             desc_ring_free(r->rings[i]);
1419dc488f88SScott Feldman         }
1420dc488f88SScott Feldman     }
1421dc488f88SScott Feldman     g_free(r->rings);
1422dc488f88SScott Feldman 
1423dc488f88SScott Feldman     rocker_msix_uninit(r);
1424dc488f88SScott Feldman     object_unparent(OBJECT(&r->msix_bar));
1425dc488f88SScott Feldman     object_unparent(OBJECT(&r->mmio));
1426dc488f88SScott Feldman 
1427dc488f88SScott Feldman     for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1428dc488f88SScott Feldman         if (r->worlds[i]) {
1429dc488f88SScott Feldman             world_free(r->worlds[i]);
1430dc488f88SScott Feldman         }
1431dc488f88SScott Feldman     }
1432dc488f88SScott Feldman     g_free(r->fp_ports_peers);
1433dc488f88SScott Feldman }
1434dc488f88SScott Feldman 
rocker_reset(DeviceState * dev)1435dc488f88SScott Feldman static void rocker_reset(DeviceState *dev)
1436dc488f88SScott Feldman {
1437ac7bc031SMao Zhongyi     Rocker *r = ROCKER(dev);
1438dc488f88SScott Feldman     int i;
1439dc488f88SScott Feldman 
1440dc488f88SScott Feldman     for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
1441dc488f88SScott Feldman         if (r->worlds[i]) {
1442dc488f88SScott Feldman             world_reset(r->worlds[i]);
1443dc488f88SScott Feldman         }
1444dc488f88SScott Feldman     }
1445dc488f88SScott Feldman     for (i = 0; i < r->fp_ports; i++) {
1446dc488f88SScott Feldman         fp_port_reset(r->fp_port[i]);
1447dc488f88SScott Feldman         fp_port_set_world(r->fp_port[i], r->world_dflt);
1448dc488f88SScott Feldman     }
1449dc488f88SScott Feldman 
1450dc488f88SScott Feldman     r->test_reg = 0;
1451dc488f88SScott Feldman     r->test_reg64 = 0;
1452dc488f88SScott Feldman     r->test_dma_addr = 0;
1453dc488f88SScott Feldman     r->test_dma_size = 0;
1454dc488f88SScott Feldman 
1455dc488f88SScott Feldman     for (i = 0; i < rocker_pci_ring_count(r); i++) {
1456dc488f88SScott Feldman         desc_ring_reset(r->rings[i]);
1457dc488f88SScott Feldman     }
1458dc488f88SScott Feldman 
1459dc488f88SScott Feldman     DPRINTF("Reset done\n");
1460dc488f88SScott Feldman }
1461dc488f88SScott Feldman 
1462dc488f88SScott Feldman static Property rocker_properties[] = {
1463dc488f88SScott Feldman     DEFINE_PROP_STRING("name", Rocker, name),
14649fe7101fSJiri Pirko     DEFINE_PROP_STRING("world", Rocker, world_name),
1465dc488f88SScott Feldman     DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker,
1466dc488f88SScott Feldman                         fp_start_macaddr),
1467dc488f88SScott Feldman     DEFINE_PROP_UINT64("switch_id", Rocker,
1468dc488f88SScott Feldman                        switch_id, 0),
1469dc488f88SScott Feldman     DEFINE_PROP_ARRAY("ports", Rocker, fp_ports,
1470dc488f88SScott Feldman                       fp_ports_peers, qdev_prop_netdev, NICPeers),
1471dc488f88SScott Feldman     DEFINE_PROP_END_OF_LIST(),
1472dc488f88SScott Feldman };
1473dc488f88SScott Feldman 
1474dc488f88SScott Feldman static const VMStateDescription rocker_vmsd = {
1475ac7bc031SMao Zhongyi     .name = TYPE_ROCKER,
1476dc488f88SScott Feldman     .unmigratable = 1,
1477dc488f88SScott Feldman };
1478dc488f88SScott Feldman 
rocker_class_init(ObjectClass * klass,void * data)1479dc488f88SScott Feldman static void rocker_class_init(ObjectClass *klass, void *data)
1480dc488f88SScott Feldman {
1481dc488f88SScott Feldman     DeviceClass *dc = DEVICE_CLASS(klass);
1482dc488f88SScott Feldman     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
1483dc488f88SScott Feldman 
14840c8f86eaSMao Zhongyi     k->realize = pci_rocker_realize;
1485dc488f88SScott Feldman     k->exit = pci_rocker_uninit;
1486dc488f88SScott Feldman     k->vendor_id = PCI_VENDOR_ID_REDHAT;
1487dc488f88SScott Feldman     k->device_id = PCI_DEVICE_ID_REDHAT_ROCKER;
1488dc488f88SScott Feldman     k->revision = ROCKER_PCI_REVISION;
1489dc488f88SScott Feldman     k->class_id = PCI_CLASS_NETWORK_OTHER;
1490dc488f88SScott Feldman     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1491dc488f88SScott Feldman     dc->desc = "Rocker Switch";
1492*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, rocker_reset);
14934f67d30bSMarc-André Lureau     device_class_set_props(dc, rocker_properties);
1494dc488f88SScott Feldman     dc->vmsd = &rocker_vmsd;
1495dc488f88SScott Feldman }
1496dc488f88SScott Feldman 
1497dc488f88SScott Feldman static const TypeInfo rocker_info = {
1498ac7bc031SMao Zhongyi     .name          = TYPE_ROCKER,
1499dc488f88SScott Feldman     .parent        = TYPE_PCI_DEVICE,
1500dc488f88SScott Feldman     .instance_size = sizeof(Rocker),
1501dc488f88SScott Feldman     .class_init    = rocker_class_init,
1502fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
1503fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
1504fd3b02c8SEduardo Habkost         { },
1505fd3b02c8SEduardo Habkost     },
1506dc488f88SScott Feldman };
1507dc488f88SScott Feldman 
rocker_register_types(void)1508dc488f88SScott Feldman static void rocker_register_types(void)
1509dc488f88SScott Feldman {
1510dc488f88SScott Feldman     type_register_static(&rocker_info);
1511dc488f88SScott Feldman }
1512dc488f88SScott Feldman 
1513dc488f88SScott Feldman type_init(rocker_register_types)
1514