1/*
2 * Broadcom BCM5325E/536x switch configuration utility
3 *
4 * Copyright (C) 2005 Oleg I. Vdovikin
5 * Copyright (C) 2005 Dmitry 'dimss' Ivanov of "Telecentrs" (Riga, Latvia)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/param.h>
27#include <sys/ioctl.h>
28#include <sys/socket.h>
29
30/* linux stuff */
31typedef u_int64_t u64;
32typedef u_int32_t u32;
33typedef u_int16_t u16;
34typedef u_int8_t u8;
35
36#include <linux/if.h>
37#include <linux/sockios.h>
38#include <linux/ethtool.h>
39#include <linux/types.h>
40#include <linux/mii.h>
41
42#include "etc53xx.h"
43#define ROBO_PHY_ADDR	0x1E	/* robo switch phy address */
44
45/* MII registers */
46#define REG_MII_PAGE	0x10	/* MII Page register */
47#define REG_MII_ADDR	0x11	/* MII Address register */
48#define REG_MII_DATA0	0x18	/* MII Data register 0 */
49
50#define REG_MII_PAGE_ENABLE	1
51#define REG_MII_ADDR_WRITE	1
52#define REG_MII_ADDR_READ	2
53
54/* Private et.o ioctls */
55#define SIOCGETCPHYRD		(SIOCDEVPRIVATE + 9)
56#define SIOCSETCPHYWR		(SIOCDEVPRIVATE + 10)
57#define SIOCGETCPHYRD2		(SIOCDEVPRIVATE + 12)
58#define SIOCSETCPHYWR2		(SIOCDEVPRIVATE + 13)
59#define SIOCGETCROBORD		(SIOCDEVPRIVATE + 14)
60#define SIOCSETCROBOWR		(SIOCDEVPRIVATE + 15)
61
62#define ROBO_DEVICE_ID		0x30
63
64typedef struct {
65	struct ifreq ifr;
66	int fd;
67	u8 et;			/* use private ioctls */
68	u8 gmii;		/* gigabit mii */
69} robo_t;
70
71#ifndef BCM5301X
72static u16 __mdio_access(robo_t *robo, u16 phy_id, u8 reg, u16 val, u16 wr)
73{
74	static int __ioctl_args[2][3] = { {SIOCGETCPHYRD, SIOCGETCPHYRD2, SIOCGMIIREG},
75					  {SIOCSETCPHYWR, SIOCSETCPHYWR2, SIOCSMIIREG} };
76
77	if (robo->et) {
78		int args[2] = { reg, val };
79		int cmd = 0;
80
81		if (phy_id != ROBO_PHY_ADDR) {
82			cmd = 1;
83			args[0] |= phy_id << 16;
84		}
85		robo->ifr.ifr_data = (caddr_t) args;
86		if (ioctl(robo->fd, __ioctl_args[wr][cmd], (caddr_t)&robo->ifr) < 0) {
87			if (phy_id != ROBO_PHY_ADDR) {
88				fprintf(stderr,
89					"Access to real 'phy' registers unavaliable.\n"
90					"Upgrade kernel driver.\n");
91				return 0xffff;
92			}
93			perror("ET ioctl");
94			exit(1);
95		}
96		return args[1];
97	} else {
98		struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo->ifr.ifr_data;
99		mii->phy_id = phy_id;
100		mii->reg_num = reg;
101		mii->val_in = val;
102		if (ioctl(robo->fd, __ioctl_args[wr][2], &robo->ifr) < 0) {
103			perror("MII ioctl");
104			exit(1);
105		}
106		return mii->val_out;
107	}
108}
109
110static inline u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg)
111{
112	return __mdio_access(robo, phy_id, reg, 0, 0);
113}
114
115static inline void mdio_write(robo_t *robo, u16 phy_id, u8 reg, u16 val)
116{
117	__mdio_access(robo, phy_id, reg, val, 1);
118}
119
120static int _robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
121{
122	int i = 3;
123
124	/* set page number */
125	mdio_write(robo, ROBO_PHY_ADDR, REG_MII_PAGE,
126		(page << 8) | REG_MII_PAGE_ENABLE);
127
128	/* set register address */
129	mdio_write(robo, ROBO_PHY_ADDR, REG_MII_ADDR,
130		(reg << 8) | op);
131
132	/* check if operation completed */
133	while (i--) {
134		if ((mdio_read(robo, ROBO_PHY_ADDR, REG_MII_ADDR) & 3) == 0)
135			return 0;
136	}
137
138	return -1;
139}
140
141static int robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
142{
143	if (_robo_reg(robo, page, reg, op))
144	{
145		fprintf(stderr, "robo_reg: %x/%x timeout\n", page, reg);
146		exit(1);
147	}
148
149	return 0;
150}
151#else
152
153static u16 robo_read16(robo_t *robo, u8 page, u8 reg);
154static u32 robo_read32(robo_t *robo, u8 page, u8 reg);
155static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16);
156static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32);
157
158static inline u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg)
159{
160	return robo_read16(robo, 0x10 + phy_id, reg);
161}
162
163static inline void mdio_write(robo_t *robo, u16 phy_id, u8 reg, u16 val)
164{
165	robo_write16(robo, 0x10 + phy_id, reg, val);
166}
167
168static int _robo_reg(robo_t *robo, u8 page, u8 reg, u8 op)
169{
170	return 0;
171}
172#endif
173
174static void robo_read(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
175{
176#ifdef BCM5301X
177	int args[5];
178
179	args[0] = (page << 16) | (reg & 0xffff);
180	args[1] = count * 2;
181	args[2] = 0;
182
183	robo->ifr.ifr_data = (caddr_t) args;
184
185	if (ioctl(robo->fd, SIOCGETCROBORD, (caddr_t)&robo->ifr) < 0)
186		return;
187
188	memcpy(val, &args[2], count * 2);
189#else
190	int i;
191
192	robo_reg(robo, page, reg, REG_MII_ADDR_READ);
193
194	for (i = 0; i < count; i++)
195		val[i] = mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + i);
196#endif
197}
198
199static u16 robo_read16(robo_t *robo, u8 page, u8 reg)
200{
201#ifdef BCM5301X
202	u16 val16;
203
204	robo_read(robo, page, reg, &val16, 1);
205	return val16;
206#else
207	robo_reg(robo, page, reg, REG_MII_ADDR_READ);
208
209	return mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0);
210#endif
211}
212
213static u32 robo_read32(robo_t *robo, u8 page, u8 reg)
214{
215#ifdef BCM5301X
216	u32 val32;
217
218	robo_read(robo, page, reg, (u16 *) &val32, 2);
219	return val32;
220#else
221	robo_reg(robo, page, reg, REG_MII_ADDR_READ);
222
223	return ((u32 )mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0)) |
224		((u32 )mdio_read(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1) << 16);
225#endif
226}
227
228static void robo_write(robo_t *robo, u8 page, u8 reg, u16 *val, int count)
229{
230#ifdef BCM5301X
231	int args[5];
232
233	args[0] = (page << 16) | (reg & 0xffff);
234	args[1] = count * 2;
235	memcpy(&args[2], val, count * 2);
236
237	robo->ifr.ifr_data = (caddr_t) args;
238
239	ioctl(robo->fd, SIOCSETCROBOWR, (caddr_t)&robo->ifr);
240#else
241	int i;
242
243	for (i = 0; i < count; i++)
244		mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + i, val[i]);
245
246	robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
247#endif
248}
249
250static void robo_write16(robo_t *robo, u8 page, u8 reg, u16 val16)
251{
252#ifdef BCM5301X
253	robo_write(robo, page, reg, &val16, 1);
254#else
255	/* write data */
256	mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, val16);
257
258	robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
259#endif
260}
261
262static void robo_write32(robo_t *robo, u8 page, u8 reg, u32 val32)
263{
264#ifdef BCM5301X
265	robo_write(robo, page, reg, (u16 *) &val32, 2);
266#else
267	/* write data */
268	mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0, (u16 )(val32 & 0xFFFF));
269	mdio_write(robo, ROBO_PHY_ADDR, REG_MII_DATA0 + 1, (u16 )(val32 >> 16));
270
271	robo_reg(robo, page, reg, REG_MII_ADDR_WRITE);
272#endif
273}
274
275/* checks that attached switch is 5325/5352/5354/5356/5357/53115/5301x */
276static int robo_vlan535x(robo_t *robo, u32 phyid)
277{
278#ifdef BCM5301X
279	if ((robo_read32(robo, ROBO_MGMT_PAGE, ROBO_DEVICE_ID) & 0xfffffff0) == 0x53010)
280		return 5;
281#else
282	/* set vlan access id to 15 and read it back */
283	u16 val16 = 15;
284	robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
285
286	/* 5365 will refuse this as it does not have this reg */
287	if (robo_read16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) != val16)
288		return 0;
289	/* gigabit ? */
290	if (robo->et != 1 && (mdio_read(robo, 0, ROBO_MII_STAT) & 0x0100))
291		robo->gmii = ((mdio_read(robo, 0, 0x0f) & 0xf000) != 0);
292	/* 53115 ? */
293	if (robo->gmii && robo_read32(robo, ROBO_STAT_PAGE, ROBO_LSA_IM_PORT) != 0) {
294		robo_write16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395, val16);
295		robo_write16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395,
296					 (1 << 7) /* start */ | 1 /* read */);
297		if (robo_read16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395) == 1
298		    && robo_read16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395) == val16)
299			return 4;
300	}
301	/* dirty trick for 5356/5357 */
302	if ((phyid & 0xfff0ffff ) == 0x5da00362 ||
303	    (phyid & 0xfff0ffff ) == 0x5e000362)
304		return 3;
305	/* 5325/5352/5354*/
306	return 1;
307#endif
308}
309
310u8 port[9] = { 0, 1, 2, 3, 4, 8, 0, 0, 8};
311char ports[] = "01234???5???????";
312char *speed[4] = { "10", "100", "1000" , "4" };
313char *rxtx[4] = { "enabled", "rx_disabled", "tx_disabled", "disabled" };
314char *stp[8] = { "none", "disable", "block", "listen", "learn", "forward", "6", "7" };
315char *jumbo[2] = { "off", "on" };
316
317struct {
318	char *name;
319	u16 bmcr;
320} media[7] = {
321	{ "auto", BMCR_ANENABLE | BMCR_ANRESTART },
322	{ "10HD", 0 },
323	{ "10FD", BMCR_FULLDPLX },
324	{ "100HD", BMCR_SPEED100 },
325	{ "100FD", BMCR_SPEED100 | BMCR_FULLDPLX },
326	{ "1000HD", BMCR_SPEED1000 },
327	{ "1000FD", BMCR_SPEED1000 | BMCR_FULLDPLX }
328};
329
330struct {
331	char *name;
332	u16 value;
333	u16 value1;
334	u16 value2;
335	u16 value3;
336} mdix[3] = {
337	{ "auto", 0x0000, 0x0000, 0x8207, 0x0000 },
338	{ "on",   0x1800, 0x4000, 0x8007, 0x0080 },
339	{ "off",  0x0800, 0x4000, 0x8007, 0x0000 }
340};
341
342void usage()
343{
344	fprintf(stderr, "Broadcom BCM5325/535x/536x/5311x switch configuration utility\n"
345		"Copyright (C) 2005-2008 Oleg I. Vdovikin (oleg@cs.msu.su)\n"
346		"Copyright (C) 2005 Dmitry 'dimss' Ivanov of \"Telecentrs\" (Riga, Latvia)\n\n"
347		"This program is distributed in the hope that it will be useful,\n"
348		"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
349		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
350		"GNU General Public License for more details.\n\n");
351
352	fprintf(stderr, "Usage: robocfg <op> ... <op>\n"
353			"Operations are as below:\n"
354			"\tshow -- show current config\n"
355			"\tshowmacs -- show known MAC addresses\n"
356			"\tswitch <enable|disable>\n"
357			"\tport <port_number> [state <%s|%s|%s|%s>]\n"
358			"\t\t[stp %s|%s|%s|%s|%s|%s] [tag <vlan_tag>]\n"
359			"\t\t[media %s|%s|%s|%s|%s|%s|%s]\n"
360			"\t\t[mdi-x %s|%s|%s] [jumbo %s|%s]\n"
361			"\tvlan <vlan_number> [ports <ports_list>]\n"
362			"\tvlans <enable|disable|reset>\n\n"
363			"\tports_list should be one argument, space separated, quoted if needed,\n"
364			"\tport number could be followed by 't' to leave packet vlan tagged (CPU \n"
365			"\tport default) or by 'u' to untag packet (other ports default) before \n"
366			"\tbringing it to the port, '*' is ignored\n"
367			"\nSamples:\n"
368			"1) ASUS WL-500g Deluxe stock config (eth0 is WAN, eth0.1 is LAN):\n"
369			"robocfg switch disable vlans enable reset vlan 0 ports \"0 5u\" vlan 1 ports \"1 2 3 4 5t\""
370			" port 0 state enabled stp none switch enable\n"
371			"2) WRT54g, WL-500g Deluxe OpenWRT config (vlan0 is LAN, vlan1 is WAN):\n"
372			"robocfg switch disable vlans enable reset vlan 0 ports \"1 2 3 4 5t\" vlan 1 ports \"0 5t\""
373			" port 0 state enabled stp none switch enable\n",
374			rxtx[0], rxtx[1], rxtx[2], rxtx[3], stp[0], stp[1], stp[2], stp[3], stp[4], stp[5],
375			media[0].name, media[1].name, media[2].name, media[3].name, media[4].name, media[5].name, media[6].name,
376			mdix[0].name, mdix[1].name, mdix[2].name,
377			jumbo[0], jumbo[1]);
378}
379
380int
381main(int argc, char *argv[])
382{
383	u16 val16;
384	u32 val32;
385	u16 mac[3];
386	int i = 0, j;
387	int robo535x = 0; /* 0 - 5365, 1 - 5325/5352/5354, 3 - 5356, 4 - 53115, 5 - 5301x */
388	u32 phyid;
389
390	static robo_t robo;
391	struct ethtool_drvinfo info;
392
393	if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
394		perror("socket");
395		exit(1);
396	}
397
398	/* the only interface for now */
399	strcpy(robo.ifr.ifr_name, "eth0");
400
401	memset(&info, 0, sizeof(info));
402	info.cmd = ETHTOOL_GDRVINFO;
403	robo.ifr.ifr_data = (caddr_t)&info;
404	if (ioctl(robo.fd, SIOCETHTOOL, (caddr_t)&robo.ifr) < 0) {
405		perror("SIOCETHTOOL: your ethernet module is either unsupported or outdated");
406		exit(1);
407	} else
408	if (strcmp(info.driver, "et0") && strcmp(info.driver, "et1") && strcmp(info.driver, "et2") &&
409	    strcmp(info.driver, "b44")) {
410		fprintf(stderr, "No suitable module found for %s (managed by %s)\n",
411			robo.ifr.ifr_name, info.driver);
412		exit(1);
413	}
414
415	/* try access using MII ioctls - get phy address */
416	if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0) {
417		int args[2] = { ROBO_PHY_ADDR << 16, 0x0 };
418
419		robo.ifr.ifr_data = (caddr_t) args;
420		if (ioctl(robo.fd, SIOCGETCPHYRD2, &robo.ifr) < 0)
421			robo.et = 1;
422		else	robo.et = 2;
423	} else {
424		/* got phy address check for robo address */
425		struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
426		if (mii->phy_id != ROBO_PHY_ADDR) {
427			fprintf(stderr, "Invalid phy address (%d)\n", mii->phy_id);
428			exit(1);
429		}
430	}
431
432	phyid = mdio_read(&robo, ROBO_PHY_ADDR, 0x2) |
433		(mdio_read(&robo, ROBO_PHY_ADDR, 0x3) << 16);
434	if (phyid == 0 && robo.et != 1)
435	    phyid = mdio_read(&robo, 0, 0x2) |
436			(mdio_read(&robo, 0, 0x3) << 16);
437
438	if (phyid == 0xffffffff || phyid == 0x55210022) {
439		fprintf(stderr, "No Robo switch in managed mode found\n");
440		exit(1);
441	}
442
443	robo535x = robo_vlan535x(&robo, phyid);
444	/* fprintf(stderr, "phyid %08x id %d\n", phyid, robo535x); */
445
446	for (i = 1; i < argc;) {
447		if (strcasecmp(argv[i], "showmacs") == 0)
448		{
449			/* show MAC table of switch */
450			u16 buf[6];
451			int idx, off, base_vlan;
452
453			base_vlan = 0; /*get_vid_by_idx(&robo, 0);*/
454
455			printf(
456				"--------------------------------------\n"
457				"VLAN  MAC                Type     Port\n"
458				"--------------------------------------\n");
459			robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_RW_CTRL, 0x81);
460			robo_write16(&robo, ROBO_ARLIO_PAGE, (robo535x >= 4) ?
461			    ROBO_ARL_SEARCH_CTRL_53115 : ROBO_ARL_SEARCH_CTRL, 0x80);
462			for (idx = 0; idx < ((robo535x >= 4) ?
463				NUM_ARL_TABLE_ENTRIES_53115 : robo535x ?
464				NUM_ARL_TABLE_ENTRIES_5350 : NUM_ARL_TABLE_ENTRIES); idx++)
465			{
466				if (robo535x >= 4)
467				{
468					off = (idx & 0x01) << 4;
469					if (!off && (robo_read16(&robo, ROBO_ARLIO_PAGE,
470					    ROBO_ARL_SEARCH_CTRL_53115) & 0x80) == 0) break;
471					robo_read(&robo, ROBO_ARLIO_PAGE,
472					    ROBO_ARL_SEARCH_RESULT_53115 + off, buf, 4);
473					robo_read(&robo, ROBO_ARLIO_PAGE,
474					    ROBO_ARL_SEARCH_RESULT_EXT_53115 + off, &buf[4], 2);
475				} else
476					robo_read(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_SEARCH_RESULT,
477					    buf, robo535x ? 4 : 5);
478				if ((robo535x >= 4) ? (buf[5] & 0x01) : (buf[3] & 0x8000) /* valid */)
479				{
480					printf("%04i  %02x:%02x:%02x:%02x:%02x:%02x  %7s  %c\n",
481						(base_vlan | (robo535x >= 4) ?
482						    (base_vlan | (buf[3] & 0xfff)) :
483						    ((buf[3] >> 5) & 0x0f) |
484							(robo535x ? 0 : ((buf[4] & 0x0f) << 4))),
485						buf[2] >> 8, buf[2] & 255,
486						buf[1] >> 8, buf[1] & 255,
487						buf[0] >> 8, buf[0] & 255,
488						((robo535x >= 4 ?
489						    (buf[4] & 0x8000) : (buf[3] & 0x4000)) ? "STATIC" : "DYNAMIC"),
490						((robo535x >= 4) ?
491						    '0'+(buf[4] & 0x0f) : ports[buf[3] & 0x0f])
492					);
493				}
494			}
495			i++;
496		} else
497		if (strcasecmp(argv[i], "port") == 0 && (i + 1) < argc)
498		{
499			int index = atoi(argv[++i]);
500			/* read port specs */
501			while (++i < argc) {
502				if (strcasecmp(argv[i], "state") == 0 && ++i < argc) {
503					for (j = 0; j < 4 && strcasecmp(argv[i], rxtx[j]); j++);
504					if (j < 4) {
505						/* change state */
506						robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
507							(robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(3 << 0)) | (j << 0));
508					} else {
509						fprintf(stderr, "Invalid state '%s'.\n", argv[i]);
510						exit(1);
511					}
512				} else
513				if (strcasecmp(argv[i], "stp") == 0 && ++i < argc) {
514					for (j = 0; j < 8 && strcasecmp(argv[i], stp[j]); j++);
515					if (j < 8) {
516						/* change stp */
517						robo_write16(&robo,ROBO_CTRL_PAGE, port[index],
518							(robo_read16(&robo, ROBO_CTRL_PAGE, port[index]) & ~(7 << 5)) | (j << 5));
519					} else {
520						fprintf(stderr, "Invalid stp '%s'.\n", argv[i]);
521						exit(1);
522					}
523				} else
524				if (strcasecmp(argv[i], "media") == 0 && ++i < argc) {
525					for (j = 0; j < 7 && strcasecmp(argv[i], media[j].name); j++);
526					if (j < ((robo535x >= 4) ? 7 : 5)) {
527						/* change media */
528                                    		mdio_write(&robo, port[index], MII_BMCR, media[j].bmcr);
529					} else {
530						fprintf(stderr, "Invalid media '%s'.\n", argv[i]);
531						exit(1);
532					}
533				} else
534				if (strcasecmp(argv[i], "mdi-x") == 0 && ++i < argc) {
535					for (j = 0; j < 3 && strcasecmp(argv[i], mdix[j].name); j++);
536					if (j < 3) {
537						/* change mdi-x */
538						if (robo535x >= 4) {
539							mdio_write(&robo, port[index], 0x10, mdix[j].value1 |
540							    (mdio_read(&robo, port[index], 0x10) & ~0x4000));
541							mdio_write(&robo, port[index], 0x18, 0x7007);
542							mdio_write(&robo, port[index], 0x18, mdix[j].value2 |
543							    (mdio_read(&robo, port[index], 0x18) & ~0x8207));
544							mdio_write(&robo, port[index], 0x1e, mdix[j].value3 |
545							    (mdio_read(&robo, port[index], 0x1e) & ~0x0080));
546						} else
547                                    		    mdio_write(&robo, port[index], 0x1c, mdix[j].value |
548							(mdio_read(&robo, port[index], 0x1c) & ~0x1800));
549					} else {
550						fprintf(stderr, "Invalid mdi-x '%s'.\n", argv[i]);
551						exit(1);
552					}
553				} else
554				if (strcasecmp(argv[i], "tag") == 0 && ++i < argc) {
555					j = atoi(argv[i]);
556					/* change vlan tag */
557					robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (index << 1), j);
558				} else
559				if (strcasecmp(argv[i], "jumbo") == 0 && ++i < argc) {
560					for (j = 0; j < 2 && strcasecmp(argv[i], jumbo[j]); j++);
561					if (robo535x >= 4 && j < 2) {
562						/* change jumbo frame feature */
563						robo_write32(&robo, ROBO_JUMBO_PAGE, ROBO_JUMBO_CTRL,
564							(robo_read32(&robo, ROBO_JUMBO_PAGE, ROBO_JUMBO_CTRL) &
565							~(1 << port[index])) | (j << port[index]));
566					} else {
567						fprintf(stderr, "Invalid jumbo state '%s'.\n", argv[i]);
568						exit(1);
569					}
570				} else break;
571			}
572		} else
573		if (strcasecmp(argv[i], "vlan") == 0 && (i + 1) < argc)
574		{
575			int vid = atoi(argv[++i]);
576			while (++i < argc) {
577				if (strcasecmp(argv[i], "ports") == 0 && ++i < argc) {
578					char *ports = argv[i];
579					int untag = 0;
580					int member = 0;
581
582					while (*ports >= '0' && *ports <= '9') {
583						j = *ports++ - '0';
584						member |= 1 << j;
585
586						/* untag if needed, CPU port requires special handling */
587						if (*ports == 'u' || (j != 5 && (*ports == ' ' || *ports == 0)))
588						{
589							untag |= 1 << j;
590							if (*ports) ports++;
591							/* change default vlan tag */
592							robo_write16(&robo, ROBO_VLAN_PAGE,
593								ROBO_VLAN_PORT0_DEF_TAG + (j << 1), vid);
594						} else
595						if (*ports == '*' || *ports == 't' || *ports == ' ') ports++;
596						else break;
597
598						while (*ports == ' ') ports++;
599					}
600
601					if (*ports) {
602						fprintf(stderr, "Invalid ports '%s'.\n", argv[i]);
603						exit(1);
604					} else {
605						/* write config now */
606						val16 = (vid) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
607						if (robo535x >= 4) {
608							val32 = (untag << 9) | member;
609							/* entry */
610							robo_write32(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ENTRY_5395, val32);
611							/* index */
612							robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395, vid);
613							/* access */
614							robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395,
615										 (1 << 7) /* start */ | 0 /* write */);
616						} else if (robo535x) {
617							if (robo535x == 3)
618								val32 = (1 << 24) /* valid */ | (untag << 6) | member | (vid << 12);
619							else
620								val32 = (1 << 20) /* valid */ | (untag << 6) | member | ((vid >> 4) << 12);
621							robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, val32);
622							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
623						} else {
624							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
625								(1 << 14)  /* valid */ | (untag << 7) | member);
626							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
627						}
628					}
629				} else break;
630			}
631		} else
632		if (strcasecmp(argv[i], "switch") == 0 && (i + 1) < argc)
633		{
634			/* enable/disable switching */
635			robo_write16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE,
636				(robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & ~2) |
637				(*argv[++i] == 'e' ? 2 : 0));
638			i++;
639		} else
640		if (strcasecmp(argv[i], "vlans") == 0 && (i + 1) < argc)
641		{
642			while (++i < argc) {
643				if (strcasecmp(argv[i], "reset") == 0) {
644					if (robo535x >= 4) {
645						robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395,
646									 (1 << 7) /* start */ | 2 /* flush */);
647					} else
648					/* reset vlan validity bit */
649					for (j = 0; j <= ((robo535x == 1) ? VLAN_ID_MAX5350 : VLAN_ID_MAX); j++)
650					{
651						/* write config now */
652						val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
653						if (robo535x) {
654							robo_write32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, 0);
655							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
656						} else {
657							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
658							robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
659						}
660					}
661				} else
662				if (strcasecmp(argv[i], "enable") == 0 || strcasecmp(argv[i], "disable") == 0)
663				{
664					int disable = (*argv[i] == 'd') || (*argv[i] == 'D');
665					/* enable/disable vlans */
666					robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0, disable ? 0 :
667						(1 << 7) /* 802.1Q VLAN */ | (3 << 5) /* mac check and hash */);
668
669					robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
670						(1 << 1) | (1 << 2) | (1 << 3) /* RSV multicast */);
671
672					robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
673						(1 << 6) /* drop invalid VID frames */);
674
675					robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
676						(1 << 3) /* drop miss V table frames */);
677
678				} else break;
679			}
680		} else
681		if (strcasecmp(argv[i], "show") == 0)
682		{
683			break;
684		} else
685		if (strncasecmp(argv[i], "robowr", 6) == 0 && (i + 2) < argc)
686		{
687			long pagereg = strtoul(argv[i + 1], NULL, 0);
688			int size = strtoul(argv[i] + 6, NULL, 0);
689			int k;
690			unsigned long long int v;
691			u16 buf[4];
692
693			size = (size > 0 && size <= sizeof(buf) * 16) ? (size + 15) >> 4 : 1;
694
695			v = strtoull(argv[i + 2], NULL, 0);
696			for (k = 0; k < size; k++)
697			{
698				buf[k] = (u16 )(v & 0xFFFF);
699				v >>= 16;
700			}
701			robo_write(&robo, pagereg >> 8, pagereg & 255, buf, size);
702
703			printf("Page 0x%02x, Reg 0x%02x: ",
704				(u16 )(pagereg >> 8), (u8 )(pagereg & 255));
705			robo_read(&robo, pagereg >> 8, pagereg & 255, buf, size);
706			while (size > 0)
707				printf("%04x", buf[--size]);
708			printf("\n");
709
710			i += 3;
711		} else
712		if (strncasecmp(argv[i], "robord", 6) == 0 && (i + 1) < argc)
713		{
714			long pagereg = strtoul(argv[i + 1], NULL, 0);
715			int size = strtoul(argv[i] + 6, NULL, 0);
716			u16 buf[4];
717
718			size = (size > 0 && size <= sizeof(buf) * 16) ? (size + 15) >> 4 : 1;
719
720			printf("Page 0x%02x, Reg 0x%02x: ",
721				(u16 )(pagereg >> 8), (u8 )(pagereg & 255));
722
723			robo_read(&robo, pagereg >> 8, pagereg & 255, buf, size);
724			while (size > 0)
725				printf("%04x", buf[--size]);
726			printf("\n");
727
728			i += 2;
729		} else
730		if (strcasecmp(argv[i], "dump") == 0)
731		{
732			for (i = 0; i < 256; i++)
733			{
734				if (_robo_reg(&robo, i, 0, REG_MII_ADDR_READ))
735					continue;
736
737				printf("Page %02x\n", i);
738
739				for (j = 0; j < 128; j++) {
740					printf(" %04x%s",
741						robo_read16(&robo, i, j), (j % 16) == 15 ? "\n" : "");
742				}
743			}
744
745			i = 2;
746		} else {
747			fprintf(stderr, "Invalid option %s\n", argv[i]);
748			usage();
749			exit(1);
750		}
751	}
752
753	if (i == argc) {
754		if (argc == 1) usage();
755		return 0;
756	}
757
758	/* show config */
759
760	printf("Switch: %sabled %s\n", robo_read16(&robo, ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2 ? "en" : "dis",
761		    robo.gmii ? "gigabit" : "");
762
763	for (i = 0; i < 6; i++) {
764		printf(robo_read16(&robo, ROBO_STAT_PAGE, ROBO_LINK_STAT_SUMMARY) & (1 << port[i]) ?
765			"Port %d: %4s%s " : "Port %d:   DOWN ",
766			(robo535x >= 4) ? port[i] : i,
767			speed[(robo535x >= 4) ?
768				(robo_read32(&robo, ROBO_STAT_PAGE, ROBO_SPEED_STAT_SUMMARY) >> port[i] * 2) & 3 :
769				(robo_read16(&robo, ROBO_STAT_PAGE, ROBO_SPEED_STAT_SUMMARY) >> port[i]) & 1],
770			robo_read16(&robo, ROBO_STAT_PAGE, (robo535x >= 4) ?
771				ROBO_DUPLEX_STAT_SUMMARY_53115 : ROBO_DUPLEX_STAT_SUMMARY) & (1 << port[i]) ? "FD" : "HD");
772
773		val16 = robo_read16(&robo, ROBO_CTRL_PAGE, port[i]);
774
775		printf("%s stp: %s vlan: %d ", rxtx[val16 & 3], stp[(val16 >> 5) & 7],
776			robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (i << 1)));
777
778		if (robo535x >= 4)
779			printf("jumbo: %s ", jumbo[(robo_read32(&robo, ROBO_JUMBO_PAGE, ROBO_JUMBO_CTRL) >> port[i]) & 1]);
780
781		robo_read(&robo, ROBO_STAT_PAGE, ROBO_LSA_PORT0 + port[i] * 6, mac, 3);
782
783		printf("mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
784			mac[2] >> 8, mac[2] & 255, mac[1] >> 8, mac[1] & 255, mac[0] >> 8, mac[0] & 255);
785	}
786
787	val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0);
788
789	printf("VLANs: %s %sabled%s%s\n",
790		(robo535x == 5) ? "BCM5301x" :
791		(robo535x == 4) ? "BCM53115" : (robo535x ? "BCM5325/535x" : "BCM536x"),
792		(val16 & (1 << 7)) ? "en" : "dis",
793		(val16 & (1 << 6)) ? " mac_check" : "",
794		(val16 & (1 << 5)) ? " mac_hash" : "");
795
796	/* scan VLANs */
797	for (i = 0; i <= ((robo535x >= 4) ? VLAN_ID_MAX5395 /* slow, needs rework, but how? */ :
798			  (robo535x ? VLAN_ID_MAX5350 : VLAN_ID_MAX)); i++)
799	{
800		/* issue read */
801		val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
802
803		if (robo535x >= 4) {
804			/* index */
805			robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395, i);
806			/* access */
807			robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395,
808						 (1 << 7) /* start */ | 1 /* read */);
809			/* actual read */
810			val32 = robo_read32(&robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ENTRY_5395);
811			if ((val32)) {
812				printf("%4d: vlan%d:", i, i);
813				for (j = 0; j <= 8; j++) {
814					if (val32 & (1 << j)) {
815						printf(" %d%s", j, (val32 & (1 << (j + 9))) ?
816							(j == 8 ? "u" : "") : "t");
817					}
818				}
819				printf("\n");
820			}
821		} else if (robo535x) {
822			robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
823			/* actual read */
824			val32 = robo_read32(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
825			if ((val32 & (robo535x == 3 ? (1 << 24) : (1 << 20))) /* valid */) {
826				val16 = (robo535x == 3)
827					? ((val32 & 0x00fff000) >> 12)
828					: (((val32 & 0xff000) >> 12) << 4) | i;
829				printf("%4d: vlan%d:", i, val16);
830				for (j = 0; j < 6; j++) {
831					if (val32 & (1 << j)) {
832						printf(" %d%s", j, (val32 & (1 << (j + 6))) ?
833							(j == 5 ? "u" : "") : "t");
834					}
835				}
836				printf("\n");
837			}
838		} else {
839			robo_write16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
840			/* actual read */
841			val16 = robo_read16(&robo, ROBO_VLAN_PAGE, ROBO_VLAN_READ);
842			if ((val16 & (1 << 14)) /* valid */) {
843				printf("%4d: vlan%d:", i, i);
844				for (j = 0; j < 6; j++) {
845					if (val16 & (1 << j)) {
846						printf(" %d%s", j, (val16 & (1 << (j + 7))) ?
847							(j == 5 ? "u" : "") : "t");
848					}
849				}
850				printf("\n");
851			}
852		}
853	}
854
855	return (0);
856}
857