/[dynamips]/upstream/dynamips-0.2.7-RC2/dev_c3600_esw.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 /upstream/dynamips-0.2.7-RC2/dev_c3600_esw.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 8 - (show annotations)
Sat Oct 6 16:24:54 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 72069 byte(s)
dynamips-0.2.7-RC2

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

  ViewVC Help
Powered by ViewVC 1.1.26