/[dynamips]/trunk/dev_nm_16esw.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/dev_nm_16esw.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11 - (show annotations)
Sat Oct 6 16:33:40 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.8-RC1/dev_nm_16esw.c
File MIME type: text/plain
File size: 71805 byte(s)
dynamips-0.2.8-RC1

1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2006 Christophe Fillot (cf@utc.fr)
4 *
5 * NM-16ESW ethernet switch module (experimental!)
6 *
7 * It's an attempt of proof of concept, so not optimized at all at this time.
8 * Only L2 switching will be managed (no L3 at all).
9 *
10 * To do next: QoS features (CoS/DSCP handling).
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stdarg.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <errno.h>
20 #include <assert.h>
21
22 #include "utils.h"
23 #include "timer.h"
24 #include "net.h"
25 #include "net_io.h"
26 #include "ptask.h"
27 #include "dev_nm_16esw.h"
28
29 /* Debugging flags */
30 #define DEBUG_ACCESS 0
31 #define DEBUG_UNKNOWN 0
32 #define DEBUG_MII 0
33 #define DEBUG_MEM 0
34 #define DEBUG_REG 0
35 #define DEBUG_TRANSMIT 0
36 #define DEBUG_RECEIVE 0
37 #define DEBUG_FORWARD 0
38 #define DEBUG_MIRROR 0
39 #define DEBUG_ARL 0
40
41 /* Invalid VLAN value */
42 #define VLAN_INVALID 0xFFF
43
44 /* Maximum packet size */
45 #define BCM5600_MAX_PKT_SIZE 2048
46
47 /* PCI vendor/product codes */
48 #define BCM5605_PCI_VENDOR_ID 0x14e4
49 #define BCM5605_PCI_PRODUCT_ID 0x5605
50
51 /* "S-channel" commands */
52 #define BCM5600_SCHAN_CMD_LINKSCAN 0x13
53 #define BCM5600_SCHAN_CMD_EXEC 0x80
54 #define BCM5600_SCHAN_CMD_READ_MII 0x90
55 #define BCM5600_SCHAN_CMD_WRITE_MII 0x91
56
57 /* Opcodes */
58 #define BCM5600_OP_BP_WARN_STATUS 0x01
59 #define BCM5600_OP_BP_DISCARD_STATUS 0x02
60 #define BCM5600_OP_COS_QSTAT_NOTIFY 0x03
61 #define BCM5600_OP_HOL_STAT_NOTIFY 0x04
62 #define BCM5600_OP_GBP_FULL_NOTIFY 0x05
63 #define BCM5600_OP_GBP_AVAIL_NOTIFY 0x06
64 #define BCM5600_OP_READ_MEM_CMD 0x07
65 #define BCM5600_OP_READ_MEM_ACK 0x08
66 #define BCM5600_OP_WRITE_MEM_CMD 0x09
67 #define BCM5600_OP_WRITE_MEM_ACK 0x0A
68 #define BCM5600_OP_READ_REG_CMD 0x0B
69 #define BCM5600_OP_READ_REG_ACK 0x0C
70 #define BCM5600_OP_WRITE_REG_CMD 0x0D
71 #define BCM5600_OP_WRITE_REG_ACK 0x0E
72 #define BCM5600_OP_ARL_INSERT_CMD 0x0F
73 #define BCM5600_OP_ARL_INSERT_DONE 0x10
74 #define BCM5600_OP_ARL_DELETE_CMD 0x11
75 #define BCM5600_OP_ARL_DELETE_DONE 0x12
76 #define BCM5600_OP_LINKSTAT_NOTIFY 0x13
77 #define BCM5600_OP_MEM_FAIL_NOTIFY 0x14
78 #define BCM5600_OP_INIT_CFAP 0x15
79 #define BCM5600_OP_INIT_SFAP 0x16
80 #define BCM5600_OP_ENTER_DEBUG_MODE 0x17
81 #define BCM5600_OP_EXIT_DEBUG_MODE 0x18
82 #define BCM5600_OP_ARL_LOOKUP_CMD 0x19
83
84 /* Command details */
85 #define BCM5600_CMD_OP_MASK 0xFC000000
86 #define BCM5600_CMD_OP_SHIFT 26
87 #define BCM5600_CMD_DST_MASK 0x03F00000
88 #define BCM5600_CMD_DST_SHIFT 20
89 #define BCM5600_CMD_SRC_MASK 0x000FC000
90 #define BCM5600_CMD_SRC_SHIFT 14
91 #define BCM5600_CMD_LEN_MASK 0x00003F80
92 #define BCM5600_CMD_LEN_SHIFT 7
93 #define BCM5600_CMD_EBIT_MASK 0x00000040
94 #define BCM5600_CMD_EBIT_SHIFT 6
95 #define BCM5600_CMD_ECODE_MASK 0x00000030
96 #define BCM5600_CMD_ECODE_SHIFT 4
97 #define BCM5600_CMD_COS_MASK 0x0000000E
98 #define BCM5600_CMD_COS_SHIFT 1
99 #define BCM5600_CMD_CPU_MASK 0x00000001
100 #define BCM5600_CMD_CPU_SHIFT 0
101
102 /* Memory zones */
103 #define BCM5600_ADDR_ARLCNT0 0x01000000
104 #define BCM5600_ADDR_ARLCNT1 0x01100000
105 #define BCM5600_ADDR_ARLCNT2 0x01200000
106 #define BCM5600_ADDR_ARL0 0x02000000
107 #define BCM5600_ADDR_ARL1 0x02100000
108 #define BCM5600_ADDR_ARL2 0x02200000
109 #define BCM5600_ADDR_PTABLE0 0x03000000
110 #define BCM5600_ADDR_PTABLE1 0x03100000
111 #define BCM5600_ADDR_PTABLE2 0x03200000
112 #define BCM5600_ADDR_VTABLE0 0x05000000
113 #define BCM5600_ADDR_VTABLE1 0x05100000
114 #define BCM5600_ADDR_VTABLE2 0x05200000
115 #define BCM5600_ADDR_TTR0 0x06000000
116 #define BCM5600_ADDR_TBMAP0 0x06010000
117 #define BCM5600_ADDR_TTR1 0x06100000
118 #define BCM5600_ADDR_TBMAP1 0x06110000
119 #define BCM5600_ADDR_TTR2 0x06200000
120 #define BCM5600_ADDR_TBMAP2 0x06210000
121 #define BCM5600_ADDR_IMASK0 0x07000000
122 #define BCM5600_ADDR_IRULE0 0x07020000
123 #define BCM5600_ADDR_IMASK1 0x07100000
124 #define BCM5600_ADDR_IRULE1 0x07120000
125 #define BCM5600_ADDR_IMASK2 0x07200000
126 #define BCM5600_ADDR_IRULE2 0x07220000
127 #define BCM5600_ADDR_GIMASK 0x07300000
128 #define BCM5600_ADDR_GIRULE 0x07320000
129 #define BCM5600_ADDR_MARL0 0x08000000
130 #define BCM5600_ADDR_MARL1 0x08100000
131 #define BCM5600_ADDR_MARL2 0x08200000
132 #define BCM5600_ADDR_L3 0x09000000
133 #define BCM5600_ADDR_DEFIP 0x09010000
134 #define BCM5600_ADDR_L3INTF 0x09020000
135 #define BCM5600_ADDR_IPMC 0x09030000
136 #define BCM5600_ADDR_CBPHDR 0x0A600000
137 #define BCM5600_ADDR_CAB0 0x0A610000
138 #define BCM5600_ADDR_CAB1 0x0A620000
139 #define BCM5600_ADDR_CAB2 0x0A630000
140 #define BCM5600_ADDR_CAB3 0x0A640000
141 #define BCM5600_ADDR_CCP 0x0A650000
142 #define BCM5600_ADDR_PPP 0x0A660000
143 #define BCM5600_ADDR_CFAP 0x0A670000
144 #define BCM5600_ADDR_SFAP 0x0A680000
145 #define BCM5600_ADDR_CBPDATA0 0x0A6A0000
146 #define BCM5600_ADDR_CBPDATA1 0x0A6B0000
147 #define BCM5600_ADDR_CBPDATA2 0x0A6C0000
148 #define BCM5600_ADDR_CBPDATA3 0x0A6D0000
149 #define BCM5600_ADDR_PID 0x0A900000
150 #define BCM5600_ADDR_XQ_BASE 0x0B600000
151 #define BCM5600_ADDR_GBP 0x12000000
152
153 /* Number of "Data Words" */
154 #define BCM5600_DW_MAX 32
155
156 /* === VTABLE definitions === */
157 /* Word 0 */
158 #define BCM5600_VTABLE_VLAN_TAG_MASK 0x00000FFF
159
160 /* Word 1: Port bitmap */
161 #define BCM5600_VTABLE_PORT_BMAP_MASK 0x1FFFFFFF
162
163 /* Word 2: Untagged port bitmap */
164 #define BCM5600_VTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF
165
166 /* Word 3: Module bitmap */
167 #define BCM5600_VTABLE_MOD_BMAP_MASK 0xFFFFFFFF
168
169 /* === PTABLE definitions === */
170 /* Word 0 */
171 #define BCM5600_PTABLE_VLAN_TAG_MASK 0x00000FFF
172 #define BCM5600_PTABLE_SP_ST_MASK 0x00003000
173 #define BCM5600_PTABLE_SP_ST_SHIFT 12
174 #define BCM5600_PTABLE_PRT_DIS_MASK 0x000FC000
175 #define BCM5600_PTABLE_PRT_DIS_SHIFT 14
176 #define BCM5600_PTABLE_JUMBO_FLAG 0x00100000
177 #define BCM5600_PTABLE_RTAG_MASK 0x00E00000
178 #define BCM5600_PTABLE_RTAG_SHIFT 21
179 #define BCM5600_PTABLE_TGID_MASK 0x07000000
180 #define BCM5600_PTABLE_TGID_SHIFT 24
181 #define BCM5600_PTABLE_TRUNK_FLAG 0x08000000
182 #define BCM5600_PTABLE_CPU_FLAG 0x10000000
183 #define BCM5600_PTABLE_PTYPE_MASK 0x60000000
184 #define BCM5600_PTABLE_PTYPE_SHIFT 29
185 #define BCM5600_PTABLE_BPDU_FLAG 0x80000000
186
187 /* Word 1 */
188 #define BCM5600_PTABLE_PORT_BMAP_MASK 0x1FFFFFFF
189 #define BCM5600_PTABLE_MI_FLAG 0x20000000
190 #define BCM5600_PTABLE_CML_MASK 0xC0000000
191 #define BCM5600_PTABLE_CML_SHIFT 30
192
193 /* Word 2 */
194 #define BCM5600_PTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF
195
196 /* Word 4 */
197 #define BCM5600_PTABLE_DSCP_MASK 0x0000003F
198 #define BCM5600_PTABLE_DSCP_SHIFT 0
199 #define BCM5600_PTABLE_DSE_MODE_MASK 0x000000C0
200 #define BCM5600_PTABLE_DSE_MODE_SHIFT 6
201 #define BCM5600_PTABLE_RPE_FLAG 0x00000100
202 #define BCM5600_PTABLE_PRI_MASK 0x00000E00
203 #define BCM5600_PTABLE_PRI_SHIFT 9
204 #define BCM5600_PTABLE_L3_DIS_FLAG 0x00001000
205
206
207 /* === ARL (Addess Resolution Logic) definitions === */
208 /* Word 0: MAC address LSB */
209
210 /* Word 1 */
211 #define BCM5600_ARL_MAC_MSB_MASK 0x0000FFFF
212 #define BCM5600_ARL_VLAN_TAG_MASK 0x0FFF0000
213 #define BCM5600_ARL_VLAN_TAG_SHIFT 16
214 #define BCM5600_ARL_COS_DST_MASK 0x70000000
215 #define BCM5600_ARL_COS_DST_SHIFT 28
216 #define BCM5600_ARL_CPU_FLAG 0x80000000
217
218 /* Word 2 */
219 #define BCM5600_ARL_L3_FLAG 0x00000001
220 #define BCM5600_ARL_SD_DIS_MASK 0x00000006
221 #define BCM5600_ARL_SD_DIS_SHIFT 1
222 #define BCM5600_ARL_ST_FLAG 0x00000008
223 #define BCM5600_ARL_HIT_FLAG 0x00000010
224 #define BCM5600_ARL_COS_SRC_MASK 0x000000E0
225 #define BCM5600_ARL_COS_SRC_SHIFT 5
226 #define BCM5600_ARL_TRUNK_FLAG 0x00000100
227 #define BCM5600_ARL_TGID_MASK 0x00000E00
228 #define BCM5600_ARL_TGID_SHIFT 9
229 #define BCM5600_ARL_RTAG_MASK 0x00007000
230 #define BCM5600_ARL_RTAG_SHIFT 12
231 #define BCM5600_ARL_PORT_MASK 0x001F8000
232 #define BCM5600_ARL_PORT_SHIFT 15
233 #define BCM5600_ARL_SCP_FLAG 0x00200000
234 #define BCM5600_ARL_MOD_ID_MASK 0x07C00000
235 #define BCM5600_ARL_MOD_ID_SHIFT 22
236
237 /* === Multicast ARL definitions === */
238 /* Word 0: MAC address LSB */
239
240 /* Word 1 */
241 #define BCM5600_MARL_MAC_MSB_MASK 0x0000FFFF
242 #define BCM5600_MARL_VLAN_TAG_MASK 0x0FFF0000
243 #define BCM5600_MARL_VLAN_TAG_SHIFT 16
244 #define BCM5600_MARL_COS_DST_MASK 0x70000000
245 #define BCM5600_MARL_COS_DST_SHIFT 28
246
247 /* Word 2 */
248 #define BCM5600_MARL_PORT_BMAP_MASK 0x1FFFFFFF
249
250 /* Word 3 */
251 #define BCM5600_MARL_UT_PORT_BMAP_MASK 0x1FFFFFFF
252
253 /* Word 4 */
254 #define BCM5600_MARL_MOD_BMAP_MASK 0xFFFFFFFF
255
256 /* === Trunk bitmap === */
257 #define BCM5600_TBMAP_MASK 0x0FFFFFFF
258
259 /* === Trunk table === */
260 /* Word 0 */
261 #define BCM5600_TTR_TP0_MASK 0x0000003F
262 #define BCM5600_TTR_TP0_SHIFT 0
263 #define BCM5600_TTR_TP1_MASK 0x00000FC0
264 #define BCM5600_TTR_TP1_SHIFT 6
265 #define BCM5600_TTR_TP2_MASK 0x0003F000
266 #define BCM5600_TTR_TP2_SHIFT 12
267 #define BCM5600_TTR_TP3_MASK 0x00FC0000
268 #define BCM5600_TTR_TP3_SHIFT 18
269 #define BCM5600_TTR_TP4_MASK 0x3F000000
270 #define BCM5600_TTR_TP4_SHIFT 24
271
272 /* Word 1 */
273 #define BCM5600_TTR_TP5_MASK 0x0000003F
274 #define BCM5600_TTR_TP5_SHIFT 0
275 #define BCM5600_TTR_TP6_MASK 0x00000FC0
276 #define BCM5600_TTR_TP6_SHIFT 6
277 #define BCM5600_TTR_TP7_MASK 0x0003F000
278 #define BCM5600_TTR_TP7_SHIFT 12
279
280 #define BCM5600_TTR_TG_SIZE_MASK 0x003C0000
281 #define BCM5600_TTR_TG_SIZE_SHIFT 18
282
283 /* Trunks (port aggregation) */
284 #define BCM5600_MAX_TRUNKS 6
285 #define BCM5600_MAX_PORTS_PER_TRUNK 8
286
287 /* ======================================================================= */
288
289 /* Transmit descriptor size */
290 #define BCM5600_TXD_SIZE 32
291 #define BCM5600_TXD_RING_CONT 0x80000000 /* ring is continuing */
292 #define BCM5600_TXD_UNKNOWN 0x04000000 /* valid packet (?) */
293 #define BCM5600_TXD_NEOP 0x00040000 /* end of packet if not set */
294
295 /* Receive descriptor size */
296 #define BCM5600_RXD_SIZE 32
297 #define BCM5600_RXD_RING_CONT 0x80000000 /* ring is continuing */
298 #define BCM5600_RXD_UNKNOWN 0x00040000 /* unknown */
299
300 /* Interrupt sources */
301 #define BCM5600_INTR_STAT_ITER_DONE 0x00100000 /* Unknown */
302 #define BCM5600_INTR_RX_UNDERRUN 0x00000400 /* RX ring underrun */
303 #define BCM5600_INTR_RX_AVAIL 0x00000200 /* packet available */
304 #define BCM5600_INTR_TX_UNDERRUN 0x00000100 /* TX ring underrun */
305 #define BCM5600_INTR_LINKSTAT_MOD 0x00000010 /* Link status modified */
306
307 /* ======================================================================= */
308
309 /* Port Mirroring */
310 #define BCM5600_MIRROR_ENABLE 0x40
311 #define BCM5600_MIRROR_PORT_MASK 0x3F
312
313 /* ======================================================================= */
314
315 #define BCM5600_REG_HASH_SIZE 8192
316
317 /* BCM5600 register */
318 struct bcm5600_reg {
319 m_uint32_t addr;
320 m_uint32_t value;
321 struct bcm5600_reg *next;
322 };
323
324 /* BCM5600 table */
325 struct bcm5600_table {
326 char *name;
327 long offset;
328 m_uint32_t addr;
329 u_int min_index,max_index;
330 u_int nr_words;
331 };
332
333 /* BCM5600 in-transit packet */
334 struct bcm5600_pkt {
335 /* Received packet data */
336 u_char *pkt;
337 ssize_t pkt_len;
338
339 /* Rewritten packet (802.1Q tag pushed or poped) */
340 u_char *rewr_pkt;
341 int rewrite_done;
342
343 /* Original VLAN (-1 for untagged packet) and Real VLAN */
344 int orig_vlan,real_vlan;
345
346 /* VLAN entry */
347 m_uint32_t *vlan_entry;
348
349 /* Ingress Port and Egress Port bitmap */
350 u_int ingress_port,egress_bitmap,egress_ut_bitmap;
351 u_int egress_filter_bitmap;
352
353 /* RX descriptor */
354 m_uint32_t rdes[4];
355
356 /* Packet sent to CPU */
357 u_int sent_to_cpu;
358 };
359
360 /* BCM5600 physical port */
361 struct bcm5600_port {
362 netio_desc_t *nio;
363 u_int id;
364 char name[32];
365 };
366
367 /* NM-16ESW private data */
368 struct nm_16esw_data {
369 char *name;
370 u_int nr_port;
371
372 vm_instance_t *vm;
373 struct vdevice *dev;
374 struct pci_device *pci_dev;
375
376 pthread_mutex_t lock;
377
378 /* Ager task */
379 timer_id ager_tid;
380
381 /* S-channel command and command result */
382 m_uint32_t schan_cmd,schan_cmd_res;
383
384 /* Data Words */
385 m_uint32_t dw[BCM5600_DW_MAX];
386
387 /* Interrupt mask */
388 m_uint32_t intr_mask;
389
390 /* MII registers */
391 m_uint16_t mii_regs[64][32];
392 m_uint32_t mii_input,mii_output;
393 u_int mii_intr;
394
395 /* RX/TX rings addresses */
396 m_uint32_t rx_ring_addr,tx_ring_addr;
397 m_uint32_t tx_current,tx_end_scan;
398 m_uint32_t rx_current,rx_end_scan;
399
400 /* TX ring scanner task id */
401 ptask_id_t tx_tid;
402
403 /* TX buffer */
404 u_char tx_buffer[BCM5600_MAX_PKT_SIZE];
405 u_int tx_bufsize;
406
407 /* Port Mirroring */
408 u_int mirror_dst_port;
409 u_int mirror_egress_ports;
410
411 /* Registers hash table */
412 struct bcm5600_reg *reg_hash_table[BCM5600_REG_HASH_SIZE];
413
414 /* Most used tables... */
415 struct bcm5600_table *t_ptable,*t_vtable;
416 struct bcm5600_table *t_arl,*t_marl;
417 struct bcm5600_table *t_tbmap,*t_ttr;
418
419 /* Ports (only 16 are "real" and usable) */
420 struct bcm5600_port ports[32];
421
422 /* CPU port */
423 u_int cpu_port;
424
425 /* Current egress port of all trunks */
426 u_int trunk_last_egress_port[BCM5600_MAX_TRUNKS];
427
428 /* ARL count table */
429 m_uint32_t *arl_cnt;
430
431 /* ARL (Address Resolution Logic) Table */
432 m_uint32_t *arl_table;
433
434 /* Multicast ARL Table */
435 m_uint32_t *marl_table;
436
437 /* VTABLE (VLAN Table) */
438 m_uint32_t *vtable;
439
440 /* Trunks */
441 m_uint32_t *ttr,*tbmap;
442
443 /* PTABLE (Port Table) */
444 m_uint32_t *ptable;
445 };
446
447 /* NM-16ESW Port physical port mapping table (Cisco => BCM) */
448 static int nm16esw_port_mapping[] = {
449 2, 0, 6, 4, 10, 8, 14, 12, 3, 1, 7, 5, 11, 9, 15, 13,
450 };
451
452 /* Log a BCM message */
453 #define BCM_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg)
454
455 /* Lock/Unlock primitives */
456 #define BCM_LOCK(d) pthread_mutex_lock(&(d)->lock)
457 #define BCM_UNLOCK(d) pthread_mutex_unlock(&(d)->lock)
458
459 /* Trunk group info */
460 struct bcm5600_tg_info {
461 u_int index,mask,shift;
462 };
463
464 static struct bcm5600_tg_info tg_info[8] = {
465 { 0, BCM5600_TTR_TP0_MASK, BCM5600_TTR_TP0_SHIFT },
466 { 0, BCM5600_TTR_TP1_MASK, BCM5600_TTR_TP1_SHIFT },
467 { 0, BCM5600_TTR_TP2_MASK, BCM5600_TTR_TP2_SHIFT },
468 { 0, BCM5600_TTR_TP3_MASK, BCM5600_TTR_TP3_SHIFT },
469 { 0, BCM5600_TTR_TP4_MASK, BCM5600_TTR_TP4_SHIFT },
470 { 1, BCM5600_TTR_TP5_MASK, BCM5600_TTR_TP5_SHIFT },
471 { 1, BCM5600_TTR_TP6_MASK, BCM5600_TTR_TP6_SHIFT },
472 { 1, BCM5600_TTR_TP7_MASK, BCM5600_TTR_TP7_SHIFT },
473 };
474
475 /* Return port status (up or down), based on the MII register */
476 static int bcm5600_mii_port_status(struct nm_16esw_data *d,u_int port)
477 {
478 u_int mii_ctrl;
479
480 mii_ctrl = d->mii_regs[port][0x00];
481
482 /* Isolate bit */
483 return(!(mii_ctrl & 0x400));
484 }
485
486 /* Build port status bitmap */
487 static m_uint32_t bcm5600_mii_port_status_bmp(struct nm_16esw_data *d)
488 {
489 m_uint32_t bmp;
490 int i;
491
492 for(i=0,bmp=0;i<d->nr_port;i++)
493 if (bcm5600_mii_port_status(d,i))
494 bmp |= 1 << i;
495
496 return(bmp);
497 }
498
499 /* Read a MII register */
500 static void bcm5600_mii_read(struct nm_16esw_data *d)
501 {
502 m_uint8_t port,reg;
503
504 port = (d->mii_input >> 16) & 0xFF;
505 reg = (d->mii_input >> 24) & 0xFF;
506
507 if ((port < 32) && (reg < 32)) {
508 d->mii_output = d->mii_regs[port][reg];
509
510 switch(reg) {
511 case 0x00:
512 d->mii_output &= ~0x8200;
513 break;
514 case 0x01:
515 if (d->ports[port].nio && bcm5600_mii_port_status(d,port))
516 d->mii_output = 0x782C;
517 else
518 d->mii_output = 0;
519 break;
520 case 0x02:
521 d->mii_output = 0x40;
522 break;
523 case 0x03:
524 d->mii_output = 0x61d4;
525 break;
526 case 0x04:
527 d->mii_output = 0x1E1;
528 break;
529 case 0x05:
530 d->mii_output = 0x41E1;
531 break;
532 default:
533 d->mii_output = 0;
534 }
535 }
536 }
537
538 /* Write a MII register */
539 static void bcm5600_mii_write(struct nm_16esw_data *d)
540 {
541 m_uint8_t port,reg;
542 m_uint16_t isolation;
543
544 port = (d->mii_input >> 16) & 0xFF;
545 reg = (d->mii_input >> 24) & 0xFF;
546
547 if ((port < 32) && (reg < 32))
548 {
549 #if DEBUG_MII
550 BCM_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n",
551 port,reg,d->mii_input & 0xFFFF);
552 #endif
553
554 /* Check if PHY isolation status is changing */
555 if (reg == 0) {
556 isolation = (d->mii_input ^ d->mii_regs[port][reg]) & 0x400;
557
558 if (isolation) {
559 #if DEBUG_MII
560 BCM_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port);
561 #endif
562 d->mii_intr = TRUE;
563 pci_dev_trigger_irq(d->vm,d->pci_dev);
564 }
565 }
566
567 d->mii_regs[port][reg] = d->mii_input & 0xFFFF;
568 }
569 }
570
571 /* Hash function for register */
572 static u_int bcm5600_reg_get_hash(m_uint32_t addr)
573 {
574 return((addr ^ (addr >> 16)) & (BCM5600_REG_HASH_SIZE - 1));
575 }
576
577 /* Find a register entry */
578 static struct bcm5600_reg *bcm5600_reg_find(struct nm_16esw_data *d,
579 m_uint32_t addr)
580 {
581 struct bcm5600_reg *reg;
582 u_int h_index;
583
584 h_index = bcm5600_reg_get_hash(addr);
585 for(reg=d->reg_hash_table[h_index];reg;reg=reg->next)
586 if (reg->addr == addr)
587 return reg;
588
589 return NULL;
590 }
591
592 /* Read a register */
593 static m_uint32_t bcm5600_reg_read(struct nm_16esw_data *d,m_uint32_t addr)
594 {
595 struct bcm5600_reg *reg;
596
597 if (!(reg = bcm5600_reg_find(d,addr)))
598 return(0);
599
600 return(reg->value);
601 }
602
603 /* Write a register */
604 static int bcm5600_reg_write(struct nm_16esw_data *d,m_uint32_t addr,
605 m_uint32_t value)
606 {
607 struct bcm5600_reg *reg;
608 u_int h_index;
609
610 if ((reg = bcm5600_reg_find(d,addr))) {
611 reg->value = value;
612 return(0);
613 }
614
615 /* create a new register */
616 if (!(reg = malloc(sizeof(*reg))))
617 return(-1);
618
619 reg->addr = addr;
620 reg->value = value;
621
622 /* insert new register in hash table */
623 h_index = bcm5600_reg_get_hash(addr);
624 reg->next = d->reg_hash_table[h_index];
625 d->reg_hash_table[h_index] = reg;
626
627 return(0);
628 }
629
630 /* Register special handling */
631 static void bcm5600_reg_write_special(struct nm_16esw_data *d,
632 m_uint32_t addr,m_uint32_t value)
633 {
634 switch(addr) {
635 case 0x80006:
636 d->mirror_dst_port = value;
637 break;
638
639 case 0x8000d:
640 d->mirror_egress_ports = value;
641 break;
642
643 case 0x80009:
644 /* age timer */
645 break;
646 }
647 }
648
649 /* Free memory used to store register info */
650 static void bcm5600_reg_free(struct nm_16esw_data *d)
651 {
652 struct bcm5600_reg *reg,*next;
653 int i;
654
655 for(i=0;i<BCM5600_REG_HASH_SIZE;i++)
656 for(reg=d->reg_hash_table[i];reg;reg=next) {
657 next = reg->next;
658 free(reg);
659 }
660 }
661
662 /* Dump all known registers */
663 static void bcm5600_reg_dump(struct nm_16esw_data *d,int show_null)
664 {
665 struct bcm5600_reg *reg;
666 int i;
667
668 printf("%s: dumping registers:\n",d->name);
669
670 for(i=0;i<BCM5600_REG_HASH_SIZE;i++)
671 for(reg=d->reg_hash_table[i];reg;reg=reg->next) {
672 if (reg->value || show_null)
673 printf(" 0x%8.8x: 0x%8.8x\n",reg->addr,reg->value);
674 }
675 }
676
677 /* Fill a string buffer with all ports of the specified bitmap */
678 static char *bcm5600_port_bitmap_str(struct nm_16esw_data *d,
679 char *buffer,m_uint32_t bitmap)
680 {
681 char *ptr = buffer;
682 int i;
683
684 *ptr = 0;
685
686 for(i=0;i<d->nr_port;i++)
687 if (bitmap & (1 << i)) {
688 ptr += sprintf(ptr,"%s ",d->ports[i].name);
689 }
690
691 return buffer;
692 }
693
694 /* BCM5600 tables */
695 #define BCM_OFFSET(x) (OFFSET(struct nm_16esw_data,x))
696
697 static struct bcm5600_table bcm5600_tables[] = {
698 /* ARL tables */
699 { "arlcnt0", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT0, 0, 0, 1 },
700 { "arlcnt1", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT1, 0, 0, 1 },
701 { "arlcnt2", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT2, 0, 0, 1 },
702
703 /* ARL tables */
704 { "arl0", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL0, 0, 8191, 3 },
705 { "arl1", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL1, 0, 8191, 3 },
706 { "arl2", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL2, 0, 8191, 3 },
707
708 /* Multicast ARL tables */
709 { "marl0", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL0, 1, 255, 5 },
710 { "marl1", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL1, 1, 255, 5 },
711 { "marl2", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL2, 1, 255, 5 },
712
713 /* PTABLE - Physical Ports */
714 { "ptable0", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE0, 0, 31, 6 },
715 { "ptable1", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE1, 0, 31, 6 },
716 { "ptable2", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE2, 0, 31, 6 },
717
718 /* VTABLE - VLANs */
719 { "vtable0", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE0, 1, 255, 4 },
720 { "vtable1", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE1, 1, 255, 4 },
721 { "vtable2", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE2, 1, 255, 4 },
722
723 /* TTR */
724 { "ttr0", BCM_OFFSET(ttr), BCM5600_ADDR_TTR0, 0, 5, 3 },
725 { "ttr1", BCM_OFFSET(ttr), BCM5600_ADDR_TTR1, 0, 5, 3 },
726 { "ttr2", BCM_OFFSET(ttr), BCM5600_ADDR_TTR2, 0, 5, 3 },
727
728 /* TBMAP */
729 { "tbmap0", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP0, 0, 5, 1 },
730 { "tbmap1", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP1, 0, 5, 1 },
731 { "tbmap2", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP2, 0, 5, 1 },
732
733 { NULL, -1, 0, 0, 0 },
734 };
735
736 /* Get table size (in number of words) */
737 static inline u_int bcm5600_table_get_size(struct bcm5600_table *table)
738 {
739 return(table->nr_words * (table->max_index + 1));
740 }
741
742 /* Create automatically tables */
743 static int bcm5600_table_create(struct nm_16esw_data *d)
744 {
745 struct bcm5600_table *table;
746 m_uint32_t *array;
747 size_t nr_words;
748 int i;
749
750 for(i=0;bcm5600_tables[i].name;i++)
751 {
752 table = &bcm5600_tables[i];
753 nr_words = bcm5600_table_get_size(table);
754
755 if (!(array = calloc(nr_words,sizeof(m_uint32_t)))) {
756 fprintf(stderr,"BCM5600: unable to create table '%s'\n",table->name);
757 return(-1);
758 }
759
760 *(PTR_ADJUST(m_uint32_t **,d,table->offset)) = array;
761 }
762
763 return(0);
764 }
765
766 /* Free tables */
767 static void bcm5600_table_free(struct nm_16esw_data *d)
768 {
769 struct bcm5600_table *table;
770 m_uint32_t **array;
771 int i;
772
773 for(i=0;bcm5600_tables[i].name;i++) {
774 table = &bcm5600_tables[i];
775 array = (PTR_ADJUST(m_uint32_t **,d,table->offset));
776 free(*array);
777
778 /* avoid freeing the same table multiple times */
779 *array = NULL;
780 }
781 }
782
783 /* Find a table given its address */
784 static struct bcm5600_table *bcm5600_table_find(struct nm_16esw_data *d,
785 m_uint32_t addr)
786 {
787 int i;
788
789 for(i=0;bcm5600_tables[i].name;i++)
790 if (bcm5600_tables[i].addr == addr)
791 return(&bcm5600_tables[i]);
792
793 #if DEBUG_UNKNOWN
794 BCM_LOG(d,"unknown table at address 0x%8.8x\n",addr);
795 #endif
796 return NULL;
797 }
798
799 /* Get a table entry */
800 static inline m_uint32_t *bcm5600_table_get_entry(struct nm_16esw_data *d,
801 struct bcm5600_table *table,
802 m_uint32_t index)
803 {
804 m_uint32_t *array;
805
806 if ((index < table->min_index) || (index > table->max_index))
807 return NULL;
808
809 array = *(PTR_ADJUST(m_uint32_t **,d,table->offset));
810 return(&array[index*table->nr_words]);
811 }
812
813 /* Read a table entry */
814 static int bcm5600_table_read_entry(struct nm_16esw_data *d)
815 {
816 struct bcm5600_table *table;
817 m_uint32_t addr,index,*entry;
818 int i;
819
820 addr = d->dw[1] & 0xFFFF0000;
821 index = d->dw[1] & 0x0000FFFF;
822
823 if (!(table = bcm5600_table_find(d,addr))) {
824 #if DEBUG_UNKNOWN
825 BCM_LOG(d,"unknown mem address at address 0x%8.8x\n",d->dw[1]);
826 #endif
827 return(-1);
828 }
829
830 if (!(entry = bcm5600_table_get_entry(d,table,index)))
831 return(-1);
832
833 #if DEBUG_MEM
834 BCM_LOG(d,"READ_MEM: addr=0x%8.8x (table %s)\n",d->dw[1],table->name);
835 #endif
836
837 for(i=0;i<table->nr_words;i++)
838 d->dw[i+1] = entry[i];
839
840 return(0);
841 }
842
843 /* Write a table entry */
844 static int bcm5600_table_write_entry(struct nm_16esw_data *d)
845 {
846 struct bcm5600_table *table;
847 m_uint32_t addr,index,*entry;
848 int i;
849
850 addr = d->dw[1] & 0xFFFF0000;
851 index = d->dw[1] & 0x0000FFFF;
852
853 if (!(table = bcm5600_table_find(d,addr)))
854 return(-1);
855
856 if (!(entry = bcm5600_table_get_entry(d,table,index)))
857 return(-1);
858
859 for(i=0;i<table->nr_words;i++)
860 entry[i] = d->dw[i+2];
861
862 #if DEBUG_MEM
863 {
864 char buffer[512],*ptr = buffer;
865
866 for(i=0;i<table->nr_words;i++)
867 ptr += sprintf(ptr,"data[%d]=0x%8.8x ",i,entry[i]);
868
869 BCM_LOG(d,"WRITE_MEM: addr=0x%8.8x (table %s) %s\n",
870 d->dw[1],table->name,buffer);
871 }
872 #endif
873 return(0);
874 }
875
876 /* Dump a table (for debugging) */
877 static int bcm5600_table_dump(struct nm_16esw_data *d,m_uint32_t addr)
878 {
879 struct bcm5600_table *table;
880 m_uint32_t *entry;
881 int i,j;
882
883 if (!(table = bcm5600_table_find(d,addr)))
884 return(-1);
885
886 printf("%s: dumping table \"%s\":\n",d->name,table->name);
887
888 for(i=table->min_index;i<=table->max_index;i++) {
889 if (!(entry = bcm5600_table_get_entry(d,table,i)))
890 break;
891
892 printf(" %4d:",i);
893
894 for(j=0;j<table->nr_words;j++)
895 printf("0x%8.8x ",entry[j]);
896
897 printf("\n");
898 }
899
900 printf("\n");
901 return(0);
902 }
903
904 /* Dump the VLAN table */
905 static int bcm5600_dump_vtable(struct nm_16esw_data *d)
906 {
907 struct bcm5600_table *table;
908 struct bcm5600_port *port;
909 m_uint32_t *entry,tbmp,ubmp;
910 u_int vlan;
911 int i,j;
912
913 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_VTABLE0)))
914 return(-1);
915
916 printf("%s: dumping VLAN table:\n",d->name);
917
918 for(i=table->min_index;i<=table->max_index;i++) {
919 if (!(entry = bcm5600_table_get_entry(d,table,i)))
920 break;
921
922 /* Extract the VLAN info */
923 vlan = entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK;
924
925 if (vlan == VLAN_INVALID)
926 continue;
927
928 printf(" VLAN %4u: ",vlan);
929
930 for(j=0;j<d->nr_port;j++) {
931 tbmp = entry[1] & (1 << j);
932 ubmp = entry[2] & (1 << j);
933
934 if (tbmp || ubmp) {
935 port = &d->ports[j];
936
937 printf("%s (",port->name);
938
939 if (tbmp)
940 printf("T%s",ubmp ? "/" : ") ");
941
942 if (ubmp)
943 printf("UT) ");
944 }
945 }
946
947 printf("\n");
948 }
949
950 printf("\n");
951 return(0);
952 }
953
954 /* Dump the "trunk" ports */
955 static int bcm5600_dump_trunks(struct nm_16esw_data *d)
956 {
957 struct bcm5600_table *table;
958 struct bcm5600_port *port;
959 m_uint32_t *entry;
960 int i,j;
961
962 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_TBMAP0)))
963 return(-1);
964
965 printf("%s: trunk ports:\n",d->name);
966
967 for(i=table->min_index;i<=table->max_index;i++) {
968 if (!(entry = bcm5600_table_get_entry(d,table,i)))
969 break;
970
971 if (!entry[0])
972 continue;
973
974 printf(" Trunk %d: ",i);
975
976 for(j=0;j<d->nr_port;j++) {
977 if (entry[0] & (1 << j)) {
978 port = &d->ports[j];
979 printf("%s ",port->name);
980 }
981 }
982
983 printf("\n");
984 }
985
986 printf("\n");
987 return(0);
988 }
989
990 /* Dump the physical port info */
991 static int bcm5600_dump_ports(struct nm_16esw_data *d)
992 {
993 struct bcm5600_table *table;
994 struct bcm5600_port *port;
995 m_uint32_t *entry;
996 u_int vlan,tgid;
997 int i;
998
999 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0)))
1000 return(-1);
1001
1002 printf("%s: physical ports:\n",d->name);
1003
1004 for(i=0;i<d->nr_port;i++) {
1005 if (!(entry = bcm5600_table_get_entry(d,table,i)))
1006 break;
1007
1008 port = &d->ports[i];
1009 vlan = entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK;
1010
1011 printf(" %-10s: VLAN %u",port->name,vlan);
1012
1013 if (entry[0] & BCM5600_PTABLE_TRUNK_FLAG) {
1014 tgid = entry[0] & BCM5600_PTABLE_TGID_MASK;
1015 tgid >>= BCM5600_PTABLE_TGID_SHIFT;
1016
1017 printf(", Trunk Group %u ",tgid);
1018 }
1019
1020 printf("\n");
1021 }
1022
1023 printf("\n");
1024 return(0);
1025 }
1026
1027 /* Dump the physical port bitmaps */
1028 static int bcm5600_dump_port_bitmaps(struct nm_16esw_data *d)
1029 {
1030 struct bcm5600_table *table;
1031 struct bcm5600_port *port;
1032 m_uint32_t *entry,tbmp,ubmp;
1033 int i,j;
1034
1035 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0)))
1036 return(-1);
1037
1038 printf("%s: dumping bitmaps of the port table:\n",d->name);
1039
1040 for(i=0;i<d->nr_port;i++) {
1041 if (!(entry = bcm5600_table_get_entry(d,table,i)))
1042 break;
1043
1044 port = &d->ports[i];
1045
1046 printf(" %-10s: ",port->name);
1047
1048 for(j=0;j<d->nr_port;j++) {
1049 tbmp = entry[1] & (1 << j);
1050 ubmp = entry[2] & (1 << j);
1051
1052 if (tbmp || ubmp) {
1053 printf("%s (",d->ports[j].name);
1054
1055 if (tbmp)
1056 printf("T%s",ubmp ? "/" : ") ");
1057
1058 if (ubmp)
1059 printf("UT) ");
1060 }
1061 }
1062
1063 printf("\n");
1064 }
1065
1066 printf("\n");
1067 return(0);
1068 }
1069
1070 /* Dump main tables */
1071 static void bcm5600_dump_main_tables(struct nm_16esw_data *d)
1072 {
1073 bcm5600_dump_ports(d);
1074 bcm5600_dump_port_bitmaps(d);
1075 bcm5600_dump_vtable(d);
1076 bcm5600_dump_trunks(d);
1077 }
1078
1079 /* Find a free ARL entry */
1080 static int bcm5600_find_free_arl_entry(struct nm_16esw_data *d)
1081 {
1082 struct bcm5600_table *table = d->t_arl;
1083
1084 if (d->arl_cnt[0] == table->max_index)
1085 return(-1);
1086
1087 return(d->arl_cnt[0] - 1);
1088 }
1089
1090 /* ARL Lookup. TODO: this must be optimized in the future. */
1091 static inline int bcm5600_gen_arl_lookup(struct nm_16esw_data *d,
1092 struct bcm5600_table *table,
1093 u_int index_start,u_int index_end,
1094 n_eth_addr_t *mac_addr,
1095 u_int vlan)
1096 {
1097 m_uint32_t *entry,tmp[2],mask;
1098 int i;
1099
1100 tmp[0] = mac_addr->eth_addr_byte[2] << 24;
1101 tmp[0] |= mac_addr->eth_addr_byte[3] << 16;
1102 tmp[0] |= mac_addr->eth_addr_byte[4] << 8;
1103 tmp[0] |= mac_addr->eth_addr_byte[5];
1104
1105 tmp[1] = (mac_addr->eth_addr_byte[0] << 8) | mac_addr->eth_addr_byte[1];
1106 tmp[1] |= vlan << BCM5600_ARL_VLAN_TAG_SHIFT;
1107
1108 mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK;
1109
1110 for(i=index_start;i<index_end;i++) {
1111 entry = bcm5600_table_get_entry(d,table,i);
1112
1113 if ((entry[0] == tmp[0]) && ((entry[1] & mask) == tmp[1]))
1114 return(i);
1115 }
1116
1117 return(-1);
1118 }
1119
1120 /* ARL Lookup */
1121 static inline int bcm5600_arl_lookup(struct nm_16esw_data *d,
1122 n_eth_addr_t *mac_addr,
1123 u_int vlan)
1124 {
1125 struct bcm5600_table *table = d->t_arl;
1126 return(bcm5600_gen_arl_lookup(d,table,1,d->arl_cnt[0]-1,mac_addr,vlan));
1127 }
1128
1129 /* MARL Lookup */
1130 static inline int bcm5600_marl_lookup(struct nm_16esw_data *d,
1131 n_eth_addr_t *mac_addr,
1132 u_int vlan)
1133 {
1134 struct bcm5600_table *table = d->t_marl;
1135 return(bcm5600_gen_arl_lookup(d,table,table->min_index,table->max_index+1,
1136 mac_addr,vlan));
1137 }
1138
1139 /* Invalidate an ARL entry */
1140 static void bcm5600_invalidate_arl_entry(m_uint32_t *entry)
1141 {
1142 entry[0] = entry[1] = entry[2] = 0;
1143 }
1144
1145 /* Insert an entry into the ARL table */
1146 static int bcm5600_insert_arl_entry(struct nm_16esw_data *d)
1147 {
1148 struct bcm5600_table *table = d->t_arl;
1149 m_uint32_t *entry,mask;
1150 int i,index;
1151
1152 mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK;
1153
1154 for(i=0;i<d->arl_cnt[0]-1;i++) {
1155 entry = bcm5600_table_get_entry(d,table,i);
1156
1157 /* If entry already exists, just modify it */
1158 if ((entry[0] == d->dw[1]) && ((entry[1] & mask) == (d->dw[2] & mask))) {
1159 entry[0] = d->dw[1];
1160 entry[1] = d->dw[2];
1161 entry[2] = d->dw[3];
1162 d->dw[1] = i;
1163 return(0);
1164 }
1165 }
1166
1167 index = d->arl_cnt[0] - 1;
1168
1169 entry = bcm5600_table_get_entry(d,table,index);
1170 entry[0] = d->dw[1];
1171 entry[1] = d->dw[2];
1172 entry[2] = d->dw[3];
1173 d->dw[1] = index;
1174
1175 d->arl_cnt[0]++;
1176 return(0);
1177 }
1178
1179 /* Delete an entry from the ARL table */
1180 static int bcm5600_delete_arl_entry(struct nm_16esw_data *d)
1181 {
1182 struct bcm5600_table *table;
1183 m_uint32_t *entry,*last_entry,mac_msb;
1184 u_int cvlan,vlan;
1185 int i;
1186
1187 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0)))
1188 return(-1);
1189
1190 vlan = d->dw[2] & BCM5600_ARL_VLAN_TAG_MASK;
1191 vlan >>= BCM5600_ARL_VLAN_TAG_SHIFT;
1192
1193 mac_msb = d->dw[2] & BCM5600_ARL_MAC_MSB_MASK;
1194
1195 for(i=table->min_index;i<=table->max_index;i++) {
1196 entry = bcm5600_table_get_entry(d,table,i);
1197
1198 /* compare VLANs and MAC addresses */
1199 cvlan = (entry[1] & BCM5600_ARL_VLAN_TAG_MASK);
1200 cvlan >>= BCM5600_ARL_VLAN_TAG_SHIFT;
1201
1202 if ((cvlan == vlan) && (entry[0] == d->dw[1]) &&
1203 ((entry[1] & BCM5600_ARL_MAC_MSB_MASK) == mac_msb))
1204 {
1205 d->dw[1] = i;
1206
1207 last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2);
1208
1209 entry[0] = last_entry[0];
1210 entry[1] = last_entry[1];
1211 entry[2] = last_entry[2];
1212
1213 d->arl_cnt[0]--;
1214 return(i);
1215 }
1216 }
1217
1218 return(0);
1219 }
1220
1221 /* Reset the ARL tables */
1222 static int bcm5600_reset_arl(struct nm_16esw_data *d)
1223 {
1224 struct bcm5600_table *table;
1225 m_uint32_t *entry;
1226 int i;
1227
1228 if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0)))
1229 return(-1);
1230
1231 for(i=table->min_index;i<=table->max_index;i++) {
1232 entry = bcm5600_table_get_entry(d,table,i);
1233 bcm5600_invalidate_arl_entry(entry);
1234 }
1235
1236 return(0);
1237 }
1238
1239 /* MAC Address Ager */
1240 static int bcm5600_arl_ager(struct nm_16esw_data *d)
1241 {
1242 m_uint32_t *entry,*last_entry;
1243 int i;
1244
1245 BCM_LOCK(d);
1246
1247 for(i=1;i<d->arl_cnt[0]-1;i++) {
1248 entry = bcm5600_table_get_entry(d,d->t_arl,i);
1249 assert(entry);
1250
1251 if (entry[2] & BCM5600_ARL_ST_FLAG)
1252 continue;
1253
1254 /* The entry has expired, purge it */
1255 if (!(entry[2] & BCM5600_ARL_HIT_FLAG)) {
1256 last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2);
1257
1258 entry[0] = last_entry[0];
1259 entry[1] = last_entry[1];
1260 entry[2] = last_entry[2];
1261
1262 d->arl_cnt[0]--;
1263 i--;
1264 } else {
1265 entry[2] &= ~BCM5600_ARL_HIT_FLAG;
1266 }
1267 }
1268
1269 BCM_UNLOCK(d);
1270 return(TRUE);
1271 }
1272
1273 /* Get the VTABLE entry matching the specified VLAN */
1274 static m_uint32_t *bcm5600_vtable_get_entry_by_vlan(struct nm_16esw_data *d,
1275 u_int vlan)
1276 {
1277 struct bcm5600_table *table = d->t_vtable;
1278 m_uint32_t *entry;
1279 int i;
1280
1281 for(i=table->min_index;i<=table->max_index;i++) {
1282 if (!(entry = bcm5600_table_get_entry(d,table,i)))
1283 break;
1284
1285 if ((entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK) == vlan)
1286 return entry;
1287 }
1288
1289 return NULL;
1290 }
1291
1292 /* Read memory command */
1293 static void bcm5600_handle_read_mem_cmd(struct nm_16esw_data *d)
1294 {
1295 int i;
1296
1297 if (bcm5600_table_read_entry(d) != 0) {
1298 for(i=1;i<BCM5600_DW_MAX;i++)
1299 d->dw[i] = 0;
1300 }
1301
1302 d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1303 }
1304
1305 /* Write memory command */
1306 static void bcm5600_handle_write_mem_cmd(struct nm_16esw_data *d)
1307 {
1308 bcm5600_table_write_entry(d);
1309 d->dw[0] = BCM5600_OP_WRITE_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1310 }
1311
1312 /* Handle a "general" command */
1313 static void bcm5600_handle_gen_cmd(struct nm_16esw_data *d)
1314 {
1315 m_uint32_t op,src,dst,len;
1316
1317 /* Extract the opcode */
1318 op = (d->dw[0] & BCM5600_CMD_OP_MASK) >> BCM5600_CMD_OP_SHIFT;
1319 src = (d->dw[0] & BCM5600_CMD_SRC_MASK) >> BCM5600_CMD_SRC_SHIFT;
1320 dst = (d->dw[0] & BCM5600_CMD_DST_MASK) >> BCM5600_CMD_DST_SHIFT;
1321 len = (d->dw[0] & BCM5600_CMD_LEN_MASK) >> BCM5600_CMD_LEN_SHIFT;
1322
1323 #if DEBUG_ACCESS
1324 BCM_LOG(d,"gen_cmd: opcode 0x%2.2x [src=0x%2.2x,dst=0x%2.2x,len=0x%2.2x] "
1325 "(dw[0]=0x%8.8x, dw[1]=0x%8.8x, dw[2]=0x%8.8x, dw[3]=0x%8.8x)\n",
1326 op,src,dst,len,d->dw[0],d->dw[1],d->dw[2],d->dw[3]);
1327 #endif
1328
1329 switch(op) {
1330 case BCM5600_OP_READ_MEM_CMD:
1331 bcm5600_handle_read_mem_cmd(d);
1332 break;
1333
1334 case BCM5600_OP_WRITE_MEM_CMD:
1335 bcm5600_handle_write_mem_cmd(d);
1336 break;
1337
1338 case BCM5600_OP_READ_REG_CMD:
1339 d->dw[0] = BCM5600_OP_READ_REG_ACK << BCM5600_CMD_OP_SHIFT;
1340 #if DEBUG_REG
1341 BCM_LOG(d,"READ_REG: reg_addr=0x%8.8x\n",d->dw[1]);
1342 #endif
1343 d->dw[1] = bcm5600_reg_read(d,d->dw[1]);
1344 break;
1345
1346 case BCM5600_OP_WRITE_REG_CMD:
1347 d->dw[0] = BCM5600_OP_WRITE_REG_ACK << BCM5600_CMD_OP_SHIFT;
1348 #if DEBUG_REG
1349 BCM_LOG(d,"WRITE_REG: reg_addr=0x%8.8x val=0x%8.8x\n",
1350 d->dw[1],d->dw[2]);
1351 #endif
1352 bcm5600_reg_write(d,d->dw[1],d->dw[2]);
1353 bcm5600_reg_write_special(d,d->dw[1],d->dw[2]);
1354 break;
1355
1356 case BCM5600_OP_ARL_INSERT_CMD:
1357 d->dw[0] = BCM5600_OP_ARL_INSERT_DONE << BCM5600_CMD_OP_SHIFT;
1358
1359 #if DEBUG_ARL
1360 BCM_LOG(d,"ARL_INSERT_CMD "
1361 "(dw[1]=0x%8.8x,dw[2]=0x%8.8x,dw[3]=0x%8.8x)\n",
1362 d->dw[1],d->dw[2],d->dw[3]);
1363 #endif
1364 bcm5600_insert_arl_entry(d);
1365 break;
1366
1367 case BCM5600_OP_ARL_DELETE_CMD:
1368 d->dw[0] = BCM5600_OP_ARL_DELETE_DONE << BCM5600_CMD_OP_SHIFT;
1369
1370 #if DEBUG_ARL
1371 BCM_LOG(d,"ARL_DELETE_CMD (dw[1]=0x%8.8x,dw[2]=0x%8.8x)\n",
1372 d->dw[1],d->dw[2]);
1373 #endif
1374 bcm5600_delete_arl_entry(d);
1375 break;
1376
1377 case BCM5600_OP_ARL_LOOKUP_CMD:
1378 d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1379 break;
1380
1381 default:
1382 BCM_LOG(d,"unknown opcode 0x%8.8x (cmd=0x%8.8x)\n",op,d->dw[0]);
1383 }
1384 }
1385
1386 /* Handle a s-channel command */
1387 static void bcm5600_handle_schan_cmd(struct nm_16esw_data *d,m_uint32_t cmd)
1388 {
1389 d->schan_cmd = cmd;
1390
1391 #if DEBUG_ACCESS
1392 BCM_LOG(d,"s-chan command 0x%8.8x\n",cmd);
1393 #endif
1394
1395 switch(cmd) {
1396 case BCM5600_SCHAN_CMD_EXEC:
1397 bcm5600_handle_gen_cmd(d);
1398 d->schan_cmd_res = 0x00008002;
1399 break;
1400
1401 case BCM5600_SCHAN_CMD_READ_MII:
1402 bcm5600_mii_read(d);
1403 d->schan_cmd_res = 0x00048000;
1404 break;
1405
1406 case BCM5600_SCHAN_CMD_WRITE_MII:
1407 bcm5600_mii_write(d);
1408 d->schan_cmd_res = 0x00048000;
1409 break;
1410
1411 case BCM5600_SCHAN_CMD_LINKSCAN:
1412 d->schan_cmd_res = 0x0;
1413 break;
1414
1415 default:
1416 #if DEBUG_UNKNOWN
1417 BCM_LOG(d,"unknown s-chan command 0x%8.8x\n",cmd);
1418 #endif
1419 d->schan_cmd_res = 0xFFFFFFFF;
1420 }
1421 }
1422
1423 /*
1424 * dev_bcm5605_access()
1425 */
1426 void *dev_bcm5605_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset,
1427 u_int op_size,u_int op_type,m_uint64_t *data)
1428 {
1429 struct nm_16esw_data *d = dev->priv_data;
1430 u_int reg;
1431
1432 if (op_type == MTS_READ)
1433 *data = 0;
1434
1435 #if DEBUG_ACCESS
1436 if (op_type == MTS_READ) {
1437 BCM_LOG(d,"read access to offset=0x%x, pc=0x%llx\n",
1438 offset,cpu_get_pc(cpu));
1439 } else {
1440 BCM_LOG(d,"write access to offset=0x%x, pc=0x%llx, val=0x%llx\n",
1441 offset,cpu_get_pc(cpu),*data);
1442 }
1443 #endif
1444
1445 BCM_LOCK(d);
1446
1447 switch(offset) {
1448 case 0x50:
1449 if (op_type == MTS_WRITE) {
1450 bcm5600_handle_schan_cmd(d,*data);
1451 } else {
1452 *data = d->schan_cmd_res;
1453 }
1454 break;
1455
1456 case 0x140:
1457 if (op_type == MTS_READ)
1458 *data = bcm5600_mii_port_status_bmp(d);
1459 break;
1460
1461 /* MII input register */
1462 case 0x158:
1463 if (op_type == MTS_WRITE)
1464 d->mii_input = *data;
1465 break;
1466
1467 /* MII output register */
1468 case 0x15c:
1469 if (op_type == MTS_READ)
1470 *data = d->mii_output;
1471 break;
1472
1473 /* Unknown (related to RX/TX rings ?) */
1474 case 0x104:
1475 break;
1476
1477 /* TX ring address */
1478 case 0x110:
1479 if (op_type == MTS_READ)
1480 *data = d->tx_ring_addr;
1481 else {
1482 d->tx_ring_addr = d->tx_current = *data;
1483 d->tx_end_scan = 0;
1484 #if DEBUG_TRANSMIT
1485 BCM_LOG(d,"tx_ring_addr = 0x%8.8x\n",d->tx_ring_addr);
1486 #endif
1487 }
1488 break;
1489
1490 /* RX ring address */
1491 case 0x114:
1492 if (op_type == MTS_READ)
1493 *data = d->rx_ring_addr;
1494 else {
1495 d->rx_ring_addr = d->rx_current = *data;
1496 d->rx_end_scan = 0;
1497 #if DEBUG_RECEIVE
1498 BCM_LOG(d,"rx_ring_addr = 0x%8.8x\n",d->rx_ring_addr);
1499 #endif
1500 }
1501 break;
1502
1503 /* Interrupt status */
1504 case 0x144:
1505 if (op_type == MTS_READ) {
1506 *data = 0;
1507
1508 /* RX/TX underrun (end of rings reached) */
1509 if (d->tx_end_scan)
1510 *data |= BCM5600_INTR_TX_UNDERRUN;
1511
1512 if (d->rx_end_scan)
1513 *data |= BCM5600_INTR_RX_UNDERRUN;
1514
1515 /* RX packet available */
1516 *data |= BCM5600_INTR_RX_AVAIL;
1517
1518 /* Link status changed */
1519 if (d->mii_intr) {
1520 *data |= BCM5600_INTR_LINKSTAT_MOD;
1521 d->mii_intr = FALSE;
1522 }
1523
1524 pci_dev_clear_irq(d->vm,d->pci_dev);
1525 }
1526 break;
1527
1528 /* Interrupt mask */
1529 case 0x148:
1530 if (op_type == MTS_READ)
1531 *data = d->intr_mask;
1532 else
1533 d->intr_mask = *data;
1534 break;
1535
1536 /* Data Words */
1537 case 0x800 ... 0x850:
1538 reg = (offset - 0x800) >> 2;
1539
1540 if (op_type == MTS_READ)
1541 *data = d->dw[reg];
1542 else
1543 d->dw[reg] = *data;
1544 break;
1545
1546 #if DEBUG_UNKNOWN
1547 /* Unknown offset */
1548 default:
1549 if (op_type == MTS_READ) {
1550 BCM_LOG(d,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n",
1551 offset,cpu_get_pc(cpu),op_size);
1552 } else {
1553 BCM_LOG(d,"write to unknown addr 0x%x, value=0x%llx, "
1554 "pc=0x%llx (size=%u)\n",
1555 offset,*data,cpu_get_pc(cpu),op_size);
1556 }
1557 #endif
1558 }
1559
1560 BCM_UNLOCK(d);
1561 return NULL;
1562 }
1563
1564 /* Show mirroring status */
1565 static int bcm5600_mirror_show_status(struct nm_16esw_data *d)
1566 {
1567 m_uint32_t *port,dst_port;
1568 int i;
1569
1570 printf("Mirroring status: ");
1571
1572 if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) {
1573 printf("disabled.\n\n");
1574 return(FALSE);
1575 }
1576
1577 printf("enabled. Dest port: ");
1578
1579 dst_port = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK;
1580
1581 if (dst_port < 32)
1582 printf("%s\n",d->ports[dst_port].name);
1583 else
1584 printf("none set.\n");
1585
1586 /* Ingress info */
1587 printf(" Ingress Ports: ");
1588
1589 for(i=0;i<d->nr_port;i++) {
1590 port = bcm5600_table_get_entry(d,d->t_ptable,i);
1591 if (port[1] & BCM5600_PTABLE_MI_FLAG)
1592 printf("%s ",d->ports[i].name);
1593 }
1594
1595 printf("\n");
1596
1597 /* Egress info */
1598 printf(" Egress Ports: ");
1599
1600 for(i=0;i<d->nr_port;i++)
1601 if (d->mirror_egress_ports & (1 << i))
1602 printf("%s ",d->ports[i].name);
1603
1604 printf("\n\n");
1605 return(TRUE);
1606 }
1607
1608 /* Mirror a packet */
1609 static int bcm5600_mirror_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p,
1610 int reason)
1611 {
1612 u_int mport;
1613
1614 if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE))
1615 return(FALSE);
1616
1617 #if DEBUG_MIRROR
1618 if (reason == 0) {
1619 BCM_LOG(d,"mirroring packet on ingress port %s\n",
1620 d->ports[p->ingress_port]);
1621 } else {
1622 BCM_LOG(d,"mirroring packet on egress port (input port %s)\n",
1623 d->ports[p->ingress_port]);
1624 }
1625 mem_dump(d->vm->log_fd,pkt,pkt_len);
1626 #endif
1627
1628 mport = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK;
1629 if (mport < 32)
1630 netio_send(d->ports[mport].nio,p->pkt,p->pkt_len);
1631 return(TRUE);
1632 }
1633
1634 /* Put a packet into the RX ring (tag it if necessary) */
1635 static int bcm5600_send_pkt_to_cpu(struct nm_16esw_data *d,
1636 struct bcm5600_pkt *p)
1637 {
1638 m_uint32_t pkt_addr,pkt_len,dot1q_data;
1639
1640 /* If the packet was already sent to CPU, don't send it again */
1641 if (p->sent_to_cpu)
1642 return(FALSE);
1643
1644 pkt_addr = p->rdes[0];
1645 pkt_len = p->pkt_len;
1646
1647 if (p->orig_vlan != -1) {
1648 /* 802.1Q packet: copy it directly */
1649 physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,pkt_len);
1650 } else {
1651 /* untagged packet: copy the dst and src addresses first */
1652 physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,N_ETH_HLEN - 2);
1653
1654 /* add the 802.1Q protocol field (0x8100) + VLAN info */
1655 dot1q_data = (N_ETH_PROTO_DOT1Q << 16) | p->real_vlan;
1656 physmem_copy_u32_to_vm(d->vm,pkt_addr+N_ETH_HLEN-2,dot1q_data);
1657
1658 /* copy the payload */
1659 physmem_copy_to_vm(d->vm,p->pkt+N_ETH_HLEN-2,
1660 pkt_addr+sizeof(n_eth_dot1q_hdr_t),
1661 pkt_len - (N_ETH_HLEN - 2));
1662 pkt_len += 4;
1663 }
1664
1665 physmem_copy_u32_to_vm(d->vm,d->rx_current+0x14,0x40000000 + (pkt_len+4));
1666 physmem_copy_u32_to_vm(d->vm,d->rx_current+0x18,0x100 + p->ingress_port);
1667 p->sent_to_cpu = TRUE;
1668
1669 #if DEBUG_RECEIVE
1670 BCM_LOG(d,"sending packet to CPU (orig_vlan=%d).\n",p->orig_vlan);
1671 #endif
1672 return(TRUE);
1673 }
1674
1675 /* Source MAC address learning */
1676 static int bcm5600_src_mac_learning(struct nm_16esw_data *d,
1677 struct bcm5600_pkt *p)
1678 {
1679 n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1680 n_eth_addr_t *src_mac = &eth_hdr->saddr;
1681 m_uint32_t *arl_entry,*src_port,*trunk;
1682 u_int trunk_id,old_ingress_port;
1683 int src_mac_index;
1684
1685 trunk = NULL;
1686 trunk_id = 0;
1687
1688 /* Skip multicast sources */
1689 if (eth_addr_is_mcast(src_mac))
1690 return(FALSE);
1691
1692 src_port = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port);
1693 assert(src_port != NULL);
1694
1695 /*
1696 * The packet comes from a trunk port. Prevent sending the packet
1697 * to the other ports of the trunk.
1698 */
1699 if (src_port[0] & BCM5600_PTABLE_TRUNK_FLAG) {
1700 trunk_id = src_port[0] & BCM5600_PTABLE_TGID_MASK;
1701 trunk_id >>= BCM5600_PTABLE_TGID_SHIFT;
1702
1703 trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id);
1704 assert(trunk != NULL);
1705
1706 p->egress_filter_bitmap |= trunk[0] & BCM5600_TBMAP_MASK;
1707 }
1708
1709 /* Source MAC address learning */
1710 src_mac_index = bcm5600_arl_lookup(d,src_mac,p->real_vlan);
1711
1712 if (src_mac_index != -1) {
1713 arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index);
1714 assert(arl_entry != NULL);
1715
1716 old_ingress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK;
1717 old_ingress_port >>= BCM5600_ARL_PORT_SHIFT;
1718
1719 if (old_ingress_port != p->ingress_port)
1720 {
1721 /*
1722 * Determine if we have a station movement.
1723 * If we have a trunk, check if the old ingress port is member
1724 * of this trunk, in this case this is not a movement.
1725 */
1726 if (trunk != NULL) {
1727 if (trunk[0] & (1 << old_ingress_port))
1728 arl_entry[2] |= BCM5600_ARL_HIT_FLAG;
1729 else
1730 arl_entry[2] &= ~BCM5600_ARL_HIT_FLAG;
1731 } else {
1732 arl_entry[2] &= ~(BCM5600_ARL_TRUNK_FLAG|BCM5600_ARL_HIT_FLAG);
1733 arl_entry[2] &= ~BCM5600_ARL_TGID_MASK;
1734 }
1735
1736 /* Change the ingress port */
1737 arl_entry[2] &= ~BCM5600_ARL_PORT_MASK;
1738 arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT;
1739 return(TRUE);
1740 }
1741
1742 arl_entry[2] |= BCM5600_ARL_HIT_FLAG;
1743 return(TRUE);
1744 }
1745
1746 #if DEBUG_FORWARD
1747 BCM_LOG(d,"source MAC address unknown, learning it.\n");
1748 #endif
1749
1750 /* Add the new learned MAC address */
1751 src_mac_index = bcm5600_find_free_arl_entry(d);
1752
1753 if (src_mac_index == -1) {
1754 BCM_LOG(d,"no free entries in ARL table!\n");
1755 return(FALSE);
1756 }
1757
1758 arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index);
1759 assert(arl_entry != NULL);
1760
1761 /* Fill the new ARL entry */
1762 arl_entry[0] = src_mac->eth_addr_byte[2] << 24;
1763 arl_entry[0] |= src_mac->eth_addr_byte[3] << 16;
1764 arl_entry[0] |= src_mac->eth_addr_byte[4] << 8;
1765 arl_entry[0] |= src_mac->eth_addr_byte[5];
1766
1767 arl_entry[1] = src_mac->eth_addr_byte[0] << 8;
1768 arl_entry[1] |= src_mac->eth_addr_byte[1];
1769 arl_entry[1] |= p->real_vlan << BCM5600_ARL_VLAN_TAG_SHIFT;
1770
1771 arl_entry[2] = BCM5600_ARL_HIT_FLAG;
1772 arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT;
1773
1774 if (trunk != NULL) {
1775 arl_entry[2] |= BCM5600_ARL_TRUNK_FLAG;
1776 arl_entry[2] |= (trunk_id << BCM5600_ARL_TGID_SHIFT);
1777 }
1778
1779 d->arl_cnt[0]++;
1780 return(TRUE);
1781 }
1782
1783 /* Select an egress port the specified trunk */
1784 static int bcm5600_trunk_egress_port(struct nm_16esw_data *d,
1785 struct bcm5600_pkt *p,
1786 u_int trunk_id)
1787 {
1788 n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1789 struct bcm5600_tg_info *tgi;
1790 m_uint32_t *ttr_entry;
1791 u_int i,nr_links;
1792 u_int hash,port_id;
1793
1794 ttr_entry = bcm5600_table_get_entry(d,d->t_ttr,trunk_id);
1795 assert(ttr_entry != NULL);
1796
1797 nr_links = ttr_entry[1] & BCM5600_TTR_TG_SIZE_MASK;
1798 nr_links >>= BCM5600_TTR_TG_SIZE_SHIFT;
1799
1800 #if 0
1801 /* Hash on source and destination MAC addresses */
1802 for(i=0,hash=0;i<N_ETH_ALEN;i++) {
1803 hash ^= eth_hdr->saddr.eth_addr_byte[i];
1804 hash ^= eth_hdr->daddr.eth_addr_byte[i];
1805 }
1806
1807 hash ^= (hash >> 4);
1808 port_id = hash % nr_links;
1809
1810 /* Maximum of 8 ports per trunk */
1811 assert(hash < BCM5600_MAX_PORTS_PER_TRUNK);
1812 #else
1813 port_id = d->trunk_last_egress_port[trunk_id] + 1;
1814 port_id %= nr_links;
1815 #endif
1816
1817 /* Save the latest port used for this trunk */
1818 d->trunk_last_egress_port[trunk_id] = port_id;
1819
1820 /* Select the egress port */
1821 tgi = &tg_info[port_id];
1822 return((ttr_entry[tgi->index] & tgi->mask) >> tgi->shift);
1823 }
1824
1825 /* Destination address lookup (take the forwarding decision) */
1826 static int bcm5600_dst_mac_lookup(struct nm_16esw_data *d,
1827 struct bcm5600_pkt *p)
1828 {
1829 n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1830 n_eth_addr_t *dst_mac = &eth_hdr->daddr;
1831 struct bcm5600_table *arl_table;
1832 m_uint32_t *arl_entry;
1833 u_int egress_port;
1834 u_int trunk_id;
1835 int dst_mac_index;
1836 int is_mcast;
1837
1838 /* Select the appropriate ARL table and do the lookup on dst MAC + VLAN */
1839 if (eth_addr_is_mcast(dst_mac)) {
1840 is_mcast = TRUE;
1841 arl_table = d->t_marl;
1842 dst_mac_index = bcm5600_marl_lookup(d,dst_mac,p->real_vlan);
1843 } else {
1844 is_mcast = FALSE;
1845 arl_table = d->t_arl;
1846 dst_mac_index = bcm5600_arl_lookup(d,dst_mac,p->real_vlan);
1847 }
1848
1849 /*
1850 * Destination Lookup Failure (DLF).
1851 *
1852 * Use the VLAN bitmap to compute the Egress port bitmap.
1853 * Remove the ingress port from it.
1854 */
1855 if (dst_mac_index == -1) {
1856 #if DEBUG_FORWARD
1857 BCM_LOG(d,"Destination MAC address "
1858 "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x unknown, flooding.\n",
1859 dst_mac->eth_addr_byte[0],dst_mac->eth_addr_byte[1],
1860 dst_mac->eth_addr_byte[2],dst_mac->eth_addr_byte[3],
1861 dst_mac->eth_addr_byte[4],dst_mac->eth_addr_byte[5]);
1862 #endif
1863 p->egress_bitmap = p->vlan_entry[1] & BCM5600_VTABLE_PORT_BMAP_MASK;
1864
1865 /* Add the CPU to the egress ports */
1866 p->egress_bitmap |= 1 << d->cpu_port;
1867
1868 p->egress_ut_bitmap = p->vlan_entry[2];
1869 p->egress_ut_bitmap &= BCM5600_VTABLE_UT_PORT_BMAP_MASK;
1870 return(TRUE);
1871 }
1872
1873 /* The MAC address was found in the ARL/MARL table */
1874 arl_entry = bcm5600_table_get_entry(d,arl_table,dst_mac_index);
1875 assert(arl_entry != NULL);
1876
1877 /* If the CPU bit is set, send a copy of the packet to the CPU */
1878 if (arl_entry[1] & BCM5600_ARL_CPU_FLAG)
1879 bcm5600_send_pkt_to_cpu(d,p);
1880
1881 if (!is_mcast) {
1882 /* Unicast: send the packet to the port or trunk found in ARL table */
1883 if (arl_entry[2] & BCM5600_ARL_TRUNK_FLAG) {
1884 trunk_id = arl_entry[2] & BCM5600_ARL_TGID_MASK;
1885 trunk_id >>= BCM5600_ARL_TGID_SHIFT;
1886
1887 /* Select an output port for this trunk */
1888 egress_port = bcm5600_trunk_egress_port(d,p,trunk_id);
1889
1890 #if DEBUG_FORWARD
1891 BCM_LOG(d,"Sending packet to trunk port %u, egress port %u\n",
1892 trunk_id,egress_port);
1893 #endif
1894 } else {
1895 egress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK;
1896 egress_port >>= BCM5600_ARL_PORT_SHIFT;
1897 }
1898
1899 p->egress_bitmap = 1 << egress_port;
1900 p->egress_ut_bitmap = p->vlan_entry[2] &
1901 BCM5600_VTABLE_UT_PORT_BMAP_MASK;
1902 } else {
1903 /* Multicast: send the packet to the egress ports found in MARL table */
1904 p->egress_bitmap = arl_entry[2] & BCM5600_MARL_PORT_BMAP_MASK;
1905 p->egress_ut_bitmap = arl_entry[3] & BCM5600_MARL_UT_PORT_BMAP_MASK;
1906 }
1907
1908 #if DEBUG_FORWARD
1909 {
1910 char buffer[1024];
1911
1912 BCM_LOG(d,"bitmap: 0x%8.8x, filter: 0x%8.8x\n",
1913 p->egress_bitmap,p->egress_filter_bitmap);
1914
1915 bcm5600_port_bitmap_str(d,buffer,p->egress_bitmap);
1916
1917 /* without egress port filtering */
1918 if (*buffer)
1919 BCM_LOG(d,"forwarding to egress port list w/o filter: %s\n",buffer);
1920 else
1921 BCM_LOG(d,"w/o filter: empty egress port list.\n");
1922
1923 /* with egress port filtering */
1924 bcm5600_port_bitmap_str(d,buffer,
1925 p->egress_bitmap & ~p->egress_filter_bitmap);
1926
1927 if (*buffer)
1928 BCM_LOG(d,"forwarding to egress port list w/ filter: %s\n",buffer);
1929 }
1930 #endif
1931
1932 return(p->egress_bitmap != 0);
1933 }
1934
1935 /* Prototype for a packet sending function */
1936 typedef void (*bcm5600_send_pkt_t)(struct nm_16esw_data *d,
1937 struct bcm5600_pkt *p,
1938 netio_desc_t *nio);
1939
1940 /* Directly forward a packet (not rewritten) */
1941 static void bcm5600_send_pkt_direct(struct nm_16esw_data *d,
1942 struct bcm5600_pkt *p,
1943 netio_desc_t *nio)
1944 {
1945 netio_send(nio,p->pkt,p->pkt_len);
1946 }
1947
1948 /* Send a packet with a 802.1Q tag */
1949 static void bcm5600_send_pkt_push_dot1q(struct nm_16esw_data *d,
1950 struct bcm5600_pkt *p,
1951 netio_desc_t *nio)
1952 {
1953 n_eth_dot1q_hdr_t *hdr;
1954
1955 if (!p->rewrite_done) {
1956 memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2));
1957
1958 hdr = (n_eth_dot1q_hdr_t *)p->rewr_pkt;
1959 hdr->type = htons(N_ETH_PROTO_DOT1Q);
1960 hdr->vlan_id = htons(p->real_vlan);
1961
1962 memcpy(p->rewr_pkt + sizeof(n_eth_dot1q_hdr_t),
1963 p->pkt + (N_ETH_HLEN - 2),
1964 p->pkt_len - (N_ETH_HLEN - 2));
1965
1966 p->rewrite_done = TRUE;
1967 }
1968
1969 netio_send(nio,p->rewr_pkt,p->pkt_len+4);
1970 }
1971
1972 /* Send a packet deleting its 802.1Q tag */
1973 static void bcm5600_send_pkt_pop_dot1q(struct nm_16esw_data *d,
1974 struct bcm5600_pkt *p,
1975 netio_desc_t *nio)
1976 {
1977 if (!p->rewrite_done) {
1978 memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2));
1979
1980 memcpy(p->rewr_pkt + (N_ETH_HLEN - 2),
1981 p->pkt + sizeof(n_eth_dot1q_hdr_t),
1982 p->pkt_len - sizeof(n_eth_dot1q_hdr_t));
1983
1984 p->rewrite_done = TRUE;
1985 }
1986
1987 netio_send(nio,p->rewr_pkt,p->pkt_len-4);
1988 }
1989
1990 /* Forward a packet on physical ports (egress bitmap must be defined) */
1991 static int bcm5600_forward_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p)
1992 {
1993 u_char rewr_pkt[BCM5600_MAX_PKT_SIZE];
1994 bcm5600_send_pkt_t send_pkt;
1995 u_int egress_untagged,trunk_id;
1996 m_uint32_t *dst_port,*trunk;
1997 int i;
1998
1999 p->egress_bitmap &= ~p->egress_filter_bitmap;
2000
2001 if (!p->egress_bitmap)
2002 return(FALSE);
2003
2004 /* Process egress mirroring (if enabled) */
2005 if (p->egress_bitmap & d->mirror_egress_ports)
2006 bcm5600_mirror_pkt(d,p,1);
2007
2008 /* No rewrite done at this time */
2009 p->rewr_pkt = rewr_pkt;
2010 p->rewrite_done = FALSE;
2011
2012 /* Forward to CPU port ? */
2013 if (p->egress_bitmap & (1 << d->cpu_port))
2014 bcm5600_send_pkt_to_cpu(d,p);
2015
2016 for(i=0;i<d->nr_port;i++) {
2017 if (!(p->egress_bitmap & (1 << i)))
2018 continue;
2019
2020 /*
2021 * If this port is a member of a trunk, remove all other ports to avoid
2022 * duplicate frames (typically, when a dest MAC address is unknown
2023 * or for a broadcast/multicast).
2024 */
2025 dst_port = bcm5600_table_get_entry(d,d->t_ptable,i);
2026 assert(dst_port != NULL);
2027
2028 if (dst_port[0] & BCM5600_PTABLE_TRUNK_FLAG) {
2029 trunk_id = dst_port[0] & BCM5600_PTABLE_TGID_MASK;
2030 trunk_id >>= BCM5600_PTABLE_TGID_SHIFT;
2031
2032 trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id);
2033 assert(trunk != NULL);
2034
2035 p->egress_bitmap &= ~trunk[0];
2036 }
2037
2038 /* select the appropriate output vector */
2039 if (p->orig_vlan == 0)
2040 send_pkt = bcm5600_send_pkt_direct;
2041 else {
2042 egress_untagged = p->egress_ut_bitmap & (1 << i);
2043
2044 if (p->orig_vlan == -1) {
2045 /* Untagged packet */
2046 if (egress_untagged)
2047 send_pkt = bcm5600_send_pkt_direct;
2048 else
2049 send_pkt = bcm5600_send_pkt_push_dot1q;
2050 } else {
2051 /* Tagged packet */
2052 if (egress_untagged)
2053 send_pkt = bcm5600_send_pkt_pop_dot1q;
2054 else
2055 send_pkt = bcm5600_send_pkt_direct;
2056 }
2057 }
2058
2059 #if DEBUG_FORWARD > 1
2060 BCM_LOG(d,"forwarding on port %s (vector=%p)\n",
2061 d->ports[i].name,send_pkt);
2062 #endif
2063 send_pkt(d,p,d->ports[i].nio);
2064 }
2065
2066 return(TRUE);
2067 }
2068
2069 /* Determine if the specified MAC address matches a BPDU */
2070 static inline int bcm5600_is_bpdu(n_eth_addr_t *m)
2071 {
2072 /* PVST+ */
2073 if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcd",6))
2074 return(TRUE);
2075
2076 /* Classical 802.1D */
2077 if (!memcmp(m,"\x01\x80\xc2\x00\x00",5) && !(m->eth_addr_byte[5] & 0xF0))
2078 return(TRUE);
2079
2080 /*
2081 * CDP: this is cleary a hack, but IOS seems to program this address
2082 * in BPDU registers.
2083 */
2084 if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcc",6))
2085 return(TRUE);
2086
2087 return(FALSE);
2088 }
2089
2090 /* Handle a received packet */
2091 static int bcm5600_handle_rx_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p)
2092 {
2093 m_uint32_t *port_entry;
2094 n_eth_dot1q_hdr_t *eth_hdr;
2095 u_int discard;
2096
2097 /* No egress port at this time */
2098 p->egress_bitmap = 0;
2099
2100 /* Never send back frames to the source port */
2101 p->egress_filter_bitmap = 1 << p->ingress_port;
2102
2103 if (!(port_entry = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port)))
2104 return(FALSE);
2105
2106 /* Analyze the Ethernet header */
2107 eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt;
2108
2109 /* Determine VLAN */
2110 if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) {
2111 p->orig_vlan = -1;
2112 p->real_vlan = port_entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK;
2113
2114 /* TODO: 802.1p/CoS remarking */
2115 if (port_entry[4] & BCM5600_PTABLE_RPE_FLAG) {
2116 }
2117 } else {
2118 p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF;
2119 }
2120
2121 /* Check that this VLAN exists */
2122 if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan)))
2123 return(FALSE);
2124
2125 /* Check for the reserved addresses (BPDU for spanning-tree) */
2126 if (bcm5600_is_bpdu(&eth_hdr->daddr)) {
2127 #if DEBUG_RECEIVE
2128 BCM_LOG(d,"Received a BPDU packet:\n");
2129 mem_dump(d->vm->log_fd,p->pkt,p->pkt_len);
2130 #endif
2131 p->egress_bitmap |= 1 << d->cpu_port;
2132 return(bcm5600_forward_pkt(d,p));
2133 }
2134
2135 /* Check that this port is a member of this VLAN */
2136 if (!(p->vlan_entry[1] & (1 << p->ingress_port)))
2137 return(FALSE);
2138
2139 /* Discard packet ? */
2140 discard = port_entry[0] & BCM5600_PTABLE_PRT_DIS_MASK;
2141 discard >>= BCM5600_PTABLE_PRT_DIS_SHIFT;
2142
2143 if ((p->orig_vlan == -1) && discard) {
2144 if (discard != 0x20) {
2145 printf("\n\n\n"
2146 "-----------------------------------------------------------"
2147 "---------------------------------\n"
2148 "Unspported feature: please post your current configuration "
2149 "on http://www.ipflow.utc.fr/blog/\n"
2150 "-----------------------------------------------------------"
2151 "---------------------------------\n");
2152 }
2153
2154 /* Drop the packet */
2155 return(FALSE);
2156 }
2157
2158 /* Mirroring on Ingress ? */
2159 if (port_entry[1] & BCM5600_PTABLE_MI_FLAG)
2160 bcm5600_mirror_pkt(d,p,0);
2161
2162 #if DEBUG_RECEIVE
2163 BCM_LOG(d,"%s: received a packet on VLAN %u\n",
2164 d->ports[p->ingress_port].name,p->real_vlan);
2165 #endif
2166
2167 /* Source MAC address learning */
2168 if (!bcm5600_src_mac_learning(d,p))
2169 return(FALSE);
2170
2171 /* Take forwarding decision based on destination MAC address */
2172 if (!bcm5600_dst_mac_lookup(d,p))
2173 return(FALSE);
2174
2175 /* Send the packet to the egress ports */
2176 return(bcm5600_forward_pkt(d,p));
2177 }
2178
2179 /* Handle a packet to transmit */
2180 static int bcm5600_handle_tx_pkt(struct nm_16esw_data *d,
2181 struct bcm5600_pkt *p,
2182 u_int egress_bitmap)
2183 {
2184 n_eth_dot1q_hdr_t *eth_hdr;
2185
2186 /* Never send back frames to the source port */
2187 p->egress_filter_bitmap = 1 << p->ingress_port;
2188
2189 /* We take the complete forwarding decision if bit 23 is set */
2190 if (egress_bitmap & (1 << 23)) {
2191 /* No egress port at this time */
2192 p->egress_bitmap = 0;
2193
2194 /* The packet must be tagged so that we can determine the VLAN */
2195 eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt;
2196
2197 if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) {
2198 BCM_LOG(d,"bcm5600_handle_tx_pkt: untagged packet ?\n");
2199 return(FALSE);
2200 }
2201
2202 /* Find the appropriate, check it exists (just in case) */
2203 p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF;
2204
2205 if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan)))
2206 return(FALSE);
2207
2208 #if DEBUG_TRANSMIT
2209 BCM_LOG(d,"Transmitting a packet from TX ring to VLAN %u\n",
2210 p->real_vlan);
2211 #endif
2212
2213 /* Take forwarding decision based on destination MAC address */
2214 if (!bcm5600_dst_mac_lookup(d,p))
2215 return(FALSE);
2216 } else {
2217 #if DEBUG_TRANSMIT
2218 BCM_LOG(d,"Transmitting natively a packet from TX ring.\n");
2219 #endif
2220 /* The egress ports are specified, send the packet natively */
2221 p->orig_vlan = 0;
2222 p->egress_bitmap = egress_bitmap;
2223 }
2224
2225 /* Send the packet to the egress ports */
2226 return(bcm5600_forward_pkt(d,p));
2227 }
2228
2229 /* Handle the TX ring */
2230 static int dev_bcm5600_handle_txring(struct nm_16esw_data *d)
2231 {
2232 struct bcm5600_pkt pkt_data;
2233 m_uint32_t tdes[4],txd_len;
2234
2235 BCM_LOCK(d);
2236
2237 if (!d->tx_current || d->tx_end_scan) {
2238 BCM_UNLOCK(d);
2239 return(FALSE);
2240 }
2241
2242 /* Read the current TX descriptor */
2243 physmem_copy_from_vm(d->vm,tdes,d->tx_current,4*sizeof(m_uint32_t));
2244 tdes[0] = vmtoh32(tdes[0]);
2245 tdes[1] = vmtoh32(tdes[1]);
2246 tdes[2] = vmtoh32(tdes[2]);
2247 tdes[3] = vmtoh32(tdes[3]);
2248
2249 #if DEBUG_TRANSMIT
2250 BCM_LOG(d,"=== TRANSMIT PATH ===\n");
2251
2252 BCM_LOG(d,"tx_current=0x%8.8x, "
2253 "tdes[0]=0x%8.8x, tdes[1]=0x%8.8x, tdes[2]=0x%8.8x\n",
2254 d->tx_current,tdes[0],tdes[1],tdes[2]);
2255 #endif
2256
2257 /* Get the buffer size */
2258 txd_len = tdes[1] & 0x7FF;
2259
2260 /* Check buffer size */
2261 if ((d->tx_bufsize + txd_len) >= sizeof(d->tx_buffer))
2262 goto done;
2263
2264 /* Copy the packet from memory */
2265 physmem_copy_from_vm(d->vm,d->tx_buffer+d->tx_bufsize,tdes[0],txd_len);
2266 d->tx_bufsize += txd_len;
2267
2268 /* Packet not complete: handle it later */
2269 if (tdes[1] & BCM5600_TXD_NEOP)
2270 goto done;
2271
2272 #if DEBUG_TRANSMIT
2273 mem_dump(d->vm->log_fd,d->tx_buffer,d->tx_bufsize);
2274 #endif
2275
2276 /* Transmit the packet */
2277 pkt_data.ingress_port = d->cpu_port;
2278 pkt_data.pkt = d->tx_buffer;
2279 pkt_data.pkt_len = d->tx_bufsize - 4;
2280 pkt_data.sent_to_cpu = TRUE;
2281 bcm5600_handle_tx_pkt(d,&pkt_data,tdes[2]);
2282
2283 /* Reset the TX buffer (packet fully transmitted) */
2284 d->tx_bufsize = 0;
2285
2286 done:
2287 /* We have reached end of ring: trigger the TX underrun interrupt */
2288 if (!(tdes[1] & BCM5600_TXD_RING_CONT)) {
2289 d->tx_end_scan = 1;
2290 pci_dev_trigger_irq(d->vm,d->pci_dev);
2291 BCM_UNLOCK(d);
2292 return(TRUE);
2293 }
2294
2295 /* Go to the next descriptor */
2296 d->tx_current += BCM5600_TXD_SIZE;
2297 BCM_UNLOCK(d);
2298 return(TRUE);
2299 }
2300
2301 /* Handle the RX ring */
2302 static int dev_bcm5600_handle_rxring(netio_desc_t *nio,
2303 u_char *pkt,ssize_t pkt_len,
2304 struct nm_16esw_data *d,
2305 struct bcm5600_port *port)
2306 {
2307 struct bcm5600_pkt pkt_data;
2308 m_uint32_t rxd_len;
2309
2310 #if DEBUG_RECEIVE
2311 BCM_LOG(d,"=== RECEIVE PATH ===\n");
2312
2313 BCM_LOG(d,"%s: received a packet of %ld bytes.\n",
2314 port->name,(u_long)pkt_len);
2315 mem_dump(d->vm->log_fd,pkt,pkt_len);
2316 #endif
2317
2318 BCM_LOCK(d);
2319
2320 if (!d->rx_current || d->rx_end_scan) {
2321 BCM_UNLOCK(d);
2322 return(FALSE);
2323 }
2324
2325 /* Read the current TX descriptor */
2326 physmem_copy_from_vm(d->vm,pkt_data.rdes,d->rx_current,
2327 (4 * sizeof(m_uint32_t)));
2328
2329 pkt_data.rdes[0] = vmtoh32(pkt_data.rdes[0]);
2330 pkt_data.rdes[1] = vmtoh32(pkt_data.rdes[1]);
2331 pkt_data.rdes[2] = vmtoh32(pkt_data.rdes[2]);
2332 pkt_data.rdes[3] = vmtoh32(pkt_data.rdes[3]);
2333
2334 #if DEBUG_RECEIVE
2335 BCM_LOG(d,"rx_current=0x%8.8x, "
2336 "rdes[0]=0x%8.8x, rdes[1]=0x%8.8x, rdes[2]=0x%8.8x\n",
2337 d->rx_current,pkt_data.rdes[0],pkt_data.rdes[1],pkt_data.rdes[2]);
2338 #endif
2339
2340 /* Get the buffer size */
2341 rxd_len = pkt_data.rdes[1] & 0x7FF;
2342
2343 if (pkt_len > rxd_len) {
2344 BCM_UNLOCK(d);
2345 return(FALSE);
2346 }
2347
2348 /* Fill the packet info */
2349 pkt_data.ingress_port = port->id;
2350 pkt_data.pkt = pkt;
2351 pkt_data.pkt_len = pkt_len;
2352 pkt_data.sent_to_cpu = FALSE;
2353
2354 /* Handle the packet */
2355 bcm5600_handle_rx_pkt(d,&pkt_data);
2356
2357 /* Signal only an interrupt when a packet has been sent to the CPU */
2358 if (pkt_data.sent_to_cpu) {
2359 /* We have reached end of ring: trigger the RX underrun interrupt */
2360 if (!(pkt_data.rdes[1] & BCM5600_RXD_RING_CONT)) {
2361 d->rx_end_scan = 1;
2362 pci_dev_trigger_irq(d->vm,d->pci_dev);
2363 BCM_UNLOCK(d);
2364 return(TRUE);
2365 }
2366
2367 /* A packet was received */
2368 pci_dev_trigger_irq(d->vm,d->pci_dev);
2369
2370 /* Go to the next descriptor */
2371 d->rx_current += BCM5600_RXD_SIZE;
2372 }
2373
2374 BCM_UNLOCK(d);
2375 return(TRUE);
2376 }
2377
2378 /* pci_bcm5605_read() */
2379 static m_uint32_t pci_bcm5605_read(cpu_gen_t *cpu,struct pci_device *dev,
2380 int reg)
2381 {
2382 struct nm_16esw_data *d = dev->priv_data;
2383
2384 switch(reg) {
2385 case PCI_REG_BAR0:
2386 return(d->dev->phys_addr);
2387 default:
2388 return(0);
2389 }
2390 }
2391
2392 /* pci_bcm5605_write() */
2393 static void pci_bcm5605_write(cpu_gen_t *cpu,struct pci_device *dev,
2394 int reg,m_uint32_t value)
2395 {
2396 struct nm_16esw_data *d = dev->priv_data;
2397
2398 switch(reg) {
2399 case PCI_REG_BAR0:
2400 vm_map_device(cpu->vm,d->dev,(m_uint64_t)value);
2401 BCM_LOG(d,"BCM5600 registers are mapped at 0x%x\n",value);
2402 break;
2403 }
2404 }
2405
2406 /* Rewrite the base MAC address */
2407 int dev_nm_16esw_burn_mac_addr(vm_instance_t *vm,u_int nm_bay,
2408 struct cisco_eeprom *eeprom)
2409 {
2410 m_uint8_t eeprom_ver;
2411 size_t offset;
2412 n_eth_addr_t addr;
2413 m_uint16_t pid;
2414
2415 pid = (m_uint16_t)getpid();
2416
2417 /* Generate automatically the MAC address */
2418 addr.eth_addr_byte[0] = vm_get_mac_addr_msb(vm);
2419 addr.eth_addr_byte[1] = vm->instance_id & 0xFF;
2420 addr.eth_addr_byte[2] = pid >> 8;
2421 addr.eth_addr_byte[3] = pid & 0xFF;
2422 addr.eth_addr_byte[4] = 0xF0 + nm_bay;
2423 addr.eth_addr_byte[5] = 0x00;
2424
2425 /* Read EEPROM format version */
2426 cisco_eeprom_get_byte(eeprom,0,&eeprom_ver);
2427
2428 if (eeprom_ver != 4)
2429 return(-1);
2430
2431 if (cisco_eeprom_v4_find_field(eeprom,0xCF,&offset) == -1)
2432 return(-1);
2433
2434 cisco_eeprom_set_region(eeprom,offset,addr.eth_addr_byte,6);
2435 return(0);
2436 }
2437
2438 /* Initialize a NM-16ESW module */
2439 struct nm_16esw_data *
2440 dev_nm_16esw_init(vm_instance_t *vm,char *name,u_int nm_bay,
2441 struct pci_bus *pci_bus,int pci_device,int irq)
2442 {
2443 struct nm_16esw_data *data;
2444 struct bcm5600_port *port;
2445 struct vdevice *dev;
2446 int i,port_id;
2447
2448 /* Allocate the private data structure */
2449 if (!(data = malloc(sizeof(*data)))) {
2450 fprintf(stderr,"%s: out of memory\n",name);
2451 return NULL;
2452 }
2453
2454 memset(data,0,sizeof(*data));
2455 pthread_mutex_init(&data->lock,NULL);
2456 data->name = name;
2457 data->nr_port = 16;
2458 data->vm = vm;
2459
2460 /* Create the BCM5600 tables */
2461 if (bcm5600_table_create(data) == -1)
2462 return NULL;
2463
2464 /* Clear the various tables */
2465 bcm5600_reset_arl(data);
2466 data->arl_cnt[0] = 1;
2467 data->t_ptable = bcm5600_table_find(data,BCM5600_ADDR_PTABLE0);
2468 data->t_vtable = bcm5600_table_find(data,BCM5600_ADDR_VTABLE0);
2469 data->t_arl = bcm5600_table_find(data,BCM5600_ADDR_ARL0);
2470 data->t_marl = bcm5600_table_find(data,BCM5600_ADDR_MARL0);
2471 data->t_tbmap = bcm5600_table_find(data,BCM5600_ADDR_TBMAP0);
2472 data->t_ttr = bcm5600_table_find(data,BCM5600_ADDR_TTR0);
2473
2474 /* Initialize ports */
2475 data->cpu_port = 27;
2476
2477 for(i=0;i<data->nr_port;i++) {
2478 port_id = nm16esw_port_mapping[i];
2479
2480 port = &data->ports[port_id];
2481 port->id = port_id;
2482 snprintf(port->name,sizeof(port->name),"Fa%u/%d",nm_bay,i);
2483 }
2484
2485 /* Create the BCM5605 PCI device */
2486 data->pci_dev = pci_dev_add(pci_bus,name,
2487 BCM5605_PCI_VENDOR_ID,BCM5605_PCI_PRODUCT_ID,
2488 pci_device,0,irq,data,
2489 NULL,pci_bcm5605_read,pci_bcm5605_write);
2490
2491 if (!data->pci_dev) {
2492 fprintf(stderr,"%s: unable to create PCI device.\n",name);
2493 return NULL;
2494 }
2495
2496 /* Create the BCM5605 device itself */
2497 if (!(dev = dev_create(name))) {
2498 fprintf(stderr,"%s: unable to create device.\n",name);
2499 return NULL;
2500 }
2501
2502 dev->phys_addr = 0;
2503 dev->phys_len = 0x200000;
2504 dev->handler = dev_bcm5605_access;
2505
2506 /* Store device info */
2507 dev->priv_data = data;
2508 data->dev = dev;
2509
2510 /* Create the TX ring scanner */
2511 data->tx_tid = ptask_add((ptask_callback)dev_bcm5600_handle_txring,
2512 data,NULL);
2513
2514 /* Start the MAC address ager */
2515 data->ager_tid = timer_create_entry(15000,FALSE,10,
2516 (timer_proc)bcm5600_arl_ager,data);
2517 return data;
2518 }
2519
2520 /* Remove a NM-16ESW from the specified slot */
2521 int dev_nm_16esw_remove(struct nm_16esw_data *data)
2522 {
2523 /* Stop the Ager */
2524 timer_remove(data->ager_tid);
2525
2526 /* Stop the TX ring task */
2527 ptask_remove(data->tx_tid);
2528
2529 /* Remove device + PCI stuff */
2530 pci_dev_remove(data->pci_dev);
2531 vm_unbind_device(data->vm,data->dev);
2532 cpu_group_rebuild_mts(data->vm->cpu_group);
2533 free(data->dev);
2534
2535 /* Free all tables and registers */
2536 bcm5600_table_free(data);
2537 bcm5600_reg_free(data);
2538 free(data);
2539 return(0);
2540 }
2541
2542 /* Bind a Network IO descriptor */
2543 int dev_nm_16esw_set_nio(struct nm_16esw_data *d,u_int port_id,
2544 netio_desc_t *nio)
2545 {
2546 struct bcm5600_port *port;
2547
2548 if (!d || (port_id >= d->nr_port))
2549 return(-1);
2550
2551 /* define the new NIO */
2552 port = &d->ports[nm16esw_port_mapping[port_id]];
2553 port->nio = nio;
2554 netio_rxl_add(nio,(netio_rx_handler_t)dev_bcm5600_handle_rxring,d,port);
2555 return(0);
2556 }
2557
2558 /* Unbind a Network IO descriptor */
2559 int dev_nm_16esw_unset_nio(struct nm_16esw_data *d,u_int port_id)
2560 {
2561 struct bcm5600_port *port;
2562
2563 if (!d || (port_id >= d->nr_port))
2564 return(-1);
2565
2566 port = &d->ports[nm16esw_port_mapping[port_id]];
2567
2568 if (port->nio) {
2569 netio_rxl_remove(port->nio);
2570 port->nio = NULL;
2571 }
2572
2573 return(0);
2574 }
2575
2576 /* Show debugging information */
2577 int dev_nm_16esw_show_info(struct nm_16esw_data *d)
2578 {
2579 BCM_LOCK(d);
2580 printf("ARL count = %u\n\n",d->arl_cnt[0]);
2581 bcm5600_dump_main_tables(d);
2582 bcm5600_mirror_show_status(d);
2583 bcm5600_reg_dump(d,FALSE);
2584 BCM_UNLOCK(d);
2585 return(0);
2586 }

  ViewVC Help
Powered by ViewVC 1.1.26