diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-06-15 11:11:28 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-06-15 11:11:28 +0000 | 
| commit | 128434cb236523f4cec881622b8606cb1533b677 (patch) | |
| tree | 628e1de7f62f279d851f47ad1ee6ab63c320c1c8 /package/broadcom-57xx/src/bcmrobo.c | |
| parent | 27e96c3c29014c2bca2ec21d7c09d5839ee66cb5 (diff) | |
(6/6) bcm57xx: package
This is the bcm57xx package.  I have tested default vlan functions,
but I dont have the equipment to test more advanced setups.  The default
vlan setup seems to be working fine.  I also added the activate_gpio
parameter which will make the driver activate the switch via gpio before
probing for it.
I'm not sure which method is best for autoload.  For the wrt350n, I
need the activate_gpio parameter.  But its probably not a good idea
to add that to the autoload file.  On a system without a bcm57xx switch,
isn't it a bad idea to mess with the gpios looking for the switch? Ideally,
wouldn't it be best to load the bcm57xx module from broadcom-diag, after
it has determined which router its on?  I tried using 'request_module' from
there, but had no success.  For now, I am relying on preinit to load
the bcm57xx module with activate_gpio param, after it has failed to load
switch_robo and switch_adm.
Signed-off-by: Ben Pfountz <netprince (at) vt (dot) edu>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11471 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/broadcom-57xx/src/bcmrobo.c')
| -rw-r--r-- | package/broadcom-57xx/src/bcmrobo.c | 1329 | 
1 files changed, 1329 insertions, 0 deletions
diff --git a/package/broadcom-57xx/src/bcmrobo.c b/package/broadcom-57xx/src/bcmrobo.c new file mode 100644 index 000000000..72824f6f6 --- /dev/null +++ b/package/broadcom-57xx/src/bcmrobo.c @@ -0,0 +1,1329 @@ +/* + * Broadcom BCM5325E/536x switch configuration module + * + * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * Based on: + *   Broadcom 53xx RoboSwitch device driver. + * + *   Copyright 2007, Broadcom Corporation + *   All Rights Reserved. + *  + *   THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + *   KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + *   SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + *   FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + */ + + +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/init.h> +#include <asm/uaccess.h> + +#include <typedefs.h> +#include <osl.h> +#include <sbutils.h> +#include <sbconfig.h> +#include <bcmendian.h> +#include "bcmparams.h" +#include <bcmnvram.h> +#include <bcmdevs.h> +#include "bcmrobo.h" +#include "proto/ethernet.h" +#include <switch-core.h> + +#define DRIVER_NAME		"bcm57xx" +#define DRIVER_VERSION	"0.1" + +#ifndef GPIO_PIN_NOTDEFINED +#define GPIO_PIN_NOTDEFINED	0x20 +#endif + +#ifdef	BCMDBG +#define	ET_ERROR(args)	printk args +#else	/* BCMDBG */ +#define	ET_ERROR(args) +#endif	/* BCMDBG */ +#define	ET_MSG(args) + +/* + * Switch can be programmed through SPI interface, which + * has a rreg and a wreg functions to read from and write to + * registers. + */ + +/* MII access registers */ +#define PSEUDO_PHYAD	0x1E	/* MII Pseudo PHY address */ +#define REG_MII_PAGE	0x10	/* MII Page register */ +#define REG_MII_ADDR	0x11	/* MII Address register */ +#define REG_MII_DATA0	0x18	/* MII Data register 0 */ +#define REG_MII_DATA1	0x19	/* MII Data register 1 */ +#define REG_MII_DATA2	0x1a	/* MII Data register 2 */ +#define REG_MII_DATA3	0x1b	/* MII Data register 3 */ + +/* Page numbers */ +#define PAGE_CTRL	0x00	/* Control page */ +#define PAGE_MMR	0x02	/* 5397 Management/Mirroring page */ +#define PAGE_VTBL	0x05	/* ARL/VLAN Table access page */ +#define PAGE_VLAN	0x34	/* VLAN page */ + +/* Control page registers */ +#define REG_CTRL_PORT0	0x00	/* Port 0 traffic control register */ +#define REG_CTRL_PORT1	0x01	/* Port 1 traffic control register */ +#define REG_CTRL_PORT2	0x02	/* Port 2 traffic control register */ +#define REG_CTRL_PORT3	0x03	/* Port 3 traffic control register */ +#define REG_CTRL_PORT4	0x04	/* Port 4 traffic control register */ +#define REG_CTRL_PORT5	0x05	/* Port 5 traffic control register */ +#define REG_CTRL_PORT6	0x06	/* Port 6 traffic control register */ +#define REG_CTRL_PORT7	0x07	/* Port 7 traffic control register */ +#define REG_CTRL_MODE	0x0B	/* Switch Mode register */ +#define REG_CTRL_MIIPO	0x0E	/* 5325: MII Port Override register */ +#define REG_CTRL_SRST	0x79	/* Software reset control register */ + +#define REG_DEVICE_ID	0x30	/* 539x Device id: */ +#define	DEVID5325	0x25	/*  5325 (Not really be we fake it) */ +#define	DEVID5395	0x95	/*  5395 */ +#define	DEVID5397	0x97	/*  5397 */ +#define	DEVID5398	0x98	/*  5398 */ + +/* VLAN page registers */ +#define REG_VLAN_CTRL0	0x00	/* VLAN Control 0 register */ +#define REG_VLAN_CTRL1	0x01	/* VLAN Control 1 register */ +#define REG_VLAN_CTRL4	0x04	/* VLAN Control 4 register */ +#define REG_VLAN_CTRL5	0x05	/* VLAN Control 5 register */ +#define REG_VLAN_ACCESS	0x06	/* VLAN Table Access register */ +#define REG_VLAN_WRITE	0x08	/* VLAN Write register */ +#define REG_VLAN_READ	0x0C	/* VLAN Read register */ +#define REG_VLAN_PTAG0	0x10	/* VLAN Default Port Tag register - port 0 */ +#define REG_VLAN_PTAG1	0x12	/* VLAN Default Port Tag register - port 1 */ +#define REG_VLAN_PTAG2	0x14	/* VLAN Default Port Tag register - port 2 */ +#define REG_VLAN_PTAG3	0x16	/* VLAN Default Port Tag register - port 3 */ +#define REG_VLAN_PTAG4	0x18	/* VLAN Default Port Tag register - port 4 */ +#define REG_VLAN_PTAG5	0x1a	/* VLAN Default Port Tag register - port 5 */ +#define REG_VLAN_PTAG6	0x1c	/* VLAN Default Port Tag register - port 6 */ +#define REG_VLAN_PTAG7	0x1e	/* VLAN Default Port Tag register - port 7 */ +#define REG_VLAN_PTAG8	0x20	/* 539x: VLAN Default Port Tag register - IMP port */ +#define REG_VLAN_PMAP	0x20	/* 5325: VLAN Priority Re-map register */ + +#define VLAN_NUMVLANS	16	/* # of VLANs */ + + +/* ARL/VLAN Table Access page registers */ +#define REG_VTBL_CTRL		0x00	/* ARL Read/Write Control */ +#define REG_VTBL_MINDX		0x02	/* MAC Address Index */ +#define REG_VTBL_VINDX		0x08	/* VID Table Index */ +#define REG_VTBL_ARL_E0		0x10	/* ARL Entry 0 */ +#define REG_VTBL_ARL_E1		0x18	/* ARL Entry 1 */ +#define REG_VTBL_DAT_E0		0x18	/* ARL Table Data Entry 0 */ +#define REG_VTBL_SCTRL		0x20	/* ARL Search Control */ +#define REG_VTBL_SADDR		0x22	/* ARL Search Address */ +#define REG_VTBL_SRES		0x24	/* ARL Search Result */ +#define REG_VTBL_SREXT		0x2c	/* ARL Search Result */ +#define REG_VTBL_VID_E0		0x30	/* VID Entry 0 */ +#define REG_VTBL_VID_E1		0x32	/* VID Entry 1 */ +#define REG_VTBL_PREG		0xFF	/* Page Register */ +#define REG_VTBL_ACCESS		0x60	/* VLAN table access register */ +#define REG_VTBL_INDX		0x61	/* VLAN table address index register */ +#define REG_VTBL_ENTRY		0x63	/* VLAN table entry register */ +#define REG_VTBL_ACCESS_5395	0x80	/* VLAN table access register */ +#define REG_VTBL_INDX_5395	0x81	/* VLAN table address index register */ +#define REG_VTBL_ENTRY_5395	0x83	/* VLAN table entry register */ + +/* SPI registers */ +#define REG_SPI_PAGE	0xff	/* SPI Page register */ + +/* Access switch registers through GPIO/SPI */ + +/* Minimum timing constants */ +#define SCK_EDGE_TIME	2	/* clock edge duration - 2us */ +#define MOSI_SETUP_TIME	1	/* input setup duration - 1us */ +#define SS_SETUP_TIME	1 	/* select setup duration - 1us */ + +/* misc. constants */ +#define SPI_MAX_RETRY	100 + +static int config_attach(robo_info_t *robo); +static void config_detach(robo_info_t *robo); + +/* Enable GPIO access to the chip */ +static void +gpio_enable(robo_info_t *robo) +{ +	/* Enable GPIO outputs with SCK and MOSI low, SS high */ +	sb_gpioout(robo->sbh, robo->ss | robo->sck | robo->mosi, robo->ss, GPIO_DRV_PRIORITY); +	sb_gpioouten(robo->sbh, robo->ss | robo->sck | robo->mosi, +	             robo->ss | robo->sck | robo->mosi, GPIO_DRV_PRIORITY); +} + +/* Disable GPIO access to the chip */ +static void +gpio_disable(robo_info_t *robo) +{ +	/* Disable GPIO outputs with all their current values */ +	sb_gpioouten(robo->sbh, robo->ss | robo->sck | robo->mosi, 0, GPIO_DRV_PRIORITY); +} + +/* Write a byte stream to the chip thru SPI */ +static int +spi_write(robo_info_t *robo, uint8 *buf, uint len) +{ +	uint i; +	uint8 mask; + +	/* Byte bang from LSB to MSB */ +	for (i = 0; i < len; i++) { +		/* Bit bang from MSB to LSB */ +		for (mask = 0x80; mask; mask >>= 1) { +			/* Clock low */ +			sb_gpioout(robo->sbh, robo->sck, 0, GPIO_DRV_PRIORITY); +			OSL_DELAY(SCK_EDGE_TIME); + +			/* Sample on rising edge */ +			if (mask & buf[i]) +				sb_gpioout(robo->sbh, robo->mosi, robo->mosi, GPIO_DRV_PRIORITY); +			else +				sb_gpioout(robo->sbh, robo->mosi, 0, GPIO_DRV_PRIORITY); +			OSL_DELAY(MOSI_SETUP_TIME); + +			/* Clock high */ +			sb_gpioout(robo->sbh, robo->sck, robo->sck, GPIO_DRV_PRIORITY); +			OSL_DELAY(SCK_EDGE_TIME); +		} +	} + +	return 0; +} + +/* Read a byte stream from the chip thru SPI */ +static int +spi_read(robo_info_t *robo, uint8 *buf, uint len) +{ +	uint i, timeout; +	uint8 rack, mask, byte; + +	/* Timeout after 100 tries without RACK */ +	for (i = 0, rack = 0, timeout = SPI_MAX_RETRY; i < len && timeout;) { +		/* Bit bang from MSB to LSB */ +		for (mask = 0x80, byte = 0; mask; mask >>= 1) { +			/* Clock low */ +			sb_gpioout(robo->sbh, robo->sck, 0, GPIO_DRV_PRIORITY); +			OSL_DELAY(SCK_EDGE_TIME); + +			/* Sample on falling edge */ +			if (sb_gpioin(robo->sbh) & robo->miso) +				byte |= mask; + +			/* Clock high */ +			sb_gpioout(robo->sbh, robo->sck, robo->sck, GPIO_DRV_PRIORITY); +			OSL_DELAY(SCK_EDGE_TIME); +		} +		/* RACK when bit 0 is high */ +		if (!rack) { +			rack = (byte & 1); +			timeout--; +			continue; +		} +		/* Byte bang from LSB to MSB */ +		buf[i] = byte; +		i++; +	} + +	if (timeout == 0) { +		ET_ERROR(("spi_read: timeout")); +		return -1; +	} + +	return 0; +} + +/* Enable/disable SPI access */ +static void +spi_select(robo_info_t *robo, uint8 spi) +{ +	if (spi) { +		/* Enable SPI access */ +		sb_gpioout(robo->sbh, robo->ss, 0, GPIO_DRV_PRIORITY); +	} else { +		/* Disable SPI access */ +		sb_gpioout(robo->sbh, robo->ss, robo->ss, GPIO_DRV_PRIORITY); +	} +	OSL_DELAY(SS_SETUP_TIME); +} + + +/* Select chip and page */ +static void +spi_goto(robo_info_t *robo, uint8 page) +{ +	uint8 reg8 = REG_SPI_PAGE;	/* page select register */ +	uint8 cmd8; + +	/* Issue the command only when we are on a different page */ +	if (robo->page == page) +		return; + +	robo->page = page; + +	/* Enable SPI access */ +	spi_select(robo, 1); + +	/* Select new page with CID 0 */ +	cmd8 = ((6 << 4) |		/* normal SPI */ +	        1);			/* write */ +	spi_write(robo, &cmd8, 1); +	spi_write(robo, ®8, 1); +	spi_write(robo, &page, 1); + +	/* Disable SPI access */ +	spi_select(robo, 0); +} + +/* Write register thru SPI */ +static int +spi_wreg(robo_info_t *robo, uint8 page, uint8 addr, void *val, int len) +{ +	int status = 0; +	uint8 cmd8; +	union { +		uint8 val8; +		uint16 val16; +		uint32 val32; +	} bytes; + +	/* validate value length and buffer address */ +	ASSERT(len == 1 || (len == 2 && !((int)val & 1)) || +	       (len == 4 && !((int)val & 3))); + +	/* Select chip and page */ +	spi_goto(robo, page); + +	/* Enable SPI access */ +	spi_select(robo, 1); + +	/* Write with CID 0 */ +	cmd8 = ((6 << 4) |		/* normal SPI */ +	        1);			/* write */ +	spi_write(robo, &cmd8, 1); +	spi_write(robo, &addr, 1); +	switch (len) { +	case 1: +		bytes.val8 = *(uint8 *)val; +		break; +	case 2: +		bytes.val16 = htol16(*(uint16 *)val); +		break; +	case 4: +		bytes.val32 = htol32(*(uint32 *)val); +		break; +	} +	spi_write(robo, (uint8 *)val, len); + +	ET_MSG(("%s: [0x%x-0x%x] := 0x%x (len %d)\n", __FUNCTION__, page, addr, +	        *(uint16 *)val, len)); +	/* Disable SPI access */ +	spi_select(robo, 0); +	return status; +} + +/* Read register thru SPI in fast SPI mode */ +static int +spi_rreg(robo_info_t *robo, uint8 page, uint8 addr, void *val, int len) +{ +	int status = 0; +	uint8 cmd8; +	union { +		uint8 val8; +		uint16 val16; +		uint32 val32; +	} bytes; + +	/* validate value length and buffer address */ +	ASSERT(len == 1 || (len == 2 && !((int)val & 1)) || +	       (len == 4 && !((int)val & 3))); + +	/* Select chip and page */ +	spi_goto(robo, page); + +	/* Enable SPI access */ +	spi_select(robo, 1); + +	/* Fast SPI read with CID 0 and byte offset 0 */ +	cmd8 = (1 << 4);		/* fast SPI */ +	spi_write(robo, &cmd8, 1); +	spi_write(robo, &addr, 1); +	status = spi_read(robo, (uint8 *)&bytes, len); +	switch (len) { +	case 1: +		*(uint8 *)val = bytes.val8; +		break; +	case 2: +		*(uint16 *)val = ltoh16(bytes.val16); +		break; +	case 4: +		*(uint32 *)val = ltoh32(bytes.val32); +		break; +	} + +	ET_MSG(("%s: [0x%x-0x%x] => 0x%x (len %d)\n", __FUNCTION__, page, addr, +	        *(uint16 *)val, len)); + +	/* Disable SPI access */ +	spi_select(robo, 0); +	return status; +} + +/* SPI/gpio interface functions */ +static dev_ops_t spigpio = { +	gpio_enable, +	gpio_disable, +	spi_wreg, +	spi_rreg, +	"SPI (GPIO)" +}; + + +/* Access switch registers through MII (MDC/MDIO) */ + +#define MII_MAX_RETRY	100 + +/* Write register thru MDC/MDIO */ +static  int +mii_wreg(robo_info_t *robo, uint8 page, uint8 reg, void *val, int len) +{ +	uint16 cmd16, val16,val48[3]; +	void *h = robo->h; +    uint32 val64[2]; +	memset(val48,0,6); +	memset(val64,0,8); +	int i; +	uint8 *ptr = (uint8 *)val; + +	/* validate value length and buffer address */ +	ASSERT(len == 1 || len == 6 || len == 8 || +	       ((len == 2) && !((int)val & 1)) || ((len == 4) && !((int)val & 3))); + +	ET_MSG(("%s: [0x%x-0x%x] := 0x%x (len %d)\n", __FUNCTION__, page, reg, +	          *(uint16 *)val, len)); + +	/* set page number - MII register 0x10 */ +	if (robo->page != page) { +		cmd16 = ((page << 8) |		/* page number */ +		         1);			/* mdc/mdio access enable */ +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_PAGE, cmd16); +		robo->page = page; +	} + +	switch (len) { +	case 8: +		val16 = ptr[7]; +		val16 = ((val16 << 8) | ptr[6]); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA3, val16); +		/* FALLTHRU */ + +	case 6: +		val16 = ptr[5]; +		val16 = ((val16 << 8) | ptr[4]); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA2, val16); +		val16 = ptr[3]; +		val16 = ((val16 << 8) | ptr[2]); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA1, val16); +		val16 = ptr[1]; +		val16 = ((val16 << 8) | ptr[0]); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA0, val16); +		break; + +	case 4: +		val16 = (uint16)((*(uint32 *)val) >> 16); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA1, val16); +		val16 = (uint16)(*(uint32 *)val); +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA0, val16); +		break; + +	case 2: +		val16 = *(uint16 *)val; +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA0, val16); +		break; + +	case 1: +		val16 = *(uint8 *)val; +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_DATA0, val16); +		break; +	} + +	/* set register address - MII register 0x11 */ +	cmd16 = ((reg << 8) |		/* register address */ +	         1);		/* opcode write */ +	robo->miiwr(h, PSEUDO_PHYAD, REG_MII_ADDR, cmd16); + +	/* is operation finished? */ +	for (i = MII_MAX_RETRY; i > 0; i --) { +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_ADDR); +		if ((val16 & 3) == 0) +			break; +	} + +	/* timed out */ +	if (!i) { +		ET_ERROR(("mii_wreg: timeout")); +		return -1; +	} +	return 0; +} + +/* Read register thru MDC/MDIO */ +static  int +mii_rreg(robo_info_t *robo, uint8 page, uint8 reg, void *val, int len) +{ +	uint16 cmd16, val16; +	void *h = robo->h; +	int i; +	uint8 *ptr = (uint8 *)val; + +	/* validate value length and buffer address */ +	ASSERT(len == 1 || len == 6 || len == 8 || +	       ((len == 2) && !((int)val & 1)) || ((len == 4) && !((int)val & 3))); + +	/* set page number - MII register 0x10 */ +	if (robo->page != page) { +		cmd16 = ((page << 8) |		/* page number */ +		         1);			/* mdc/mdio access enable */ +		robo->miiwr(h, PSEUDO_PHYAD, REG_MII_PAGE, cmd16); +		robo->page = page; +	} + +	/* set register address - MII register 0x11 */ +	cmd16 = ((reg << 8) |		/* register address */ +	         2);			/* opcode read */ +	robo->miiwr(h, PSEUDO_PHYAD, REG_MII_ADDR, cmd16); + +	/* is operation finished? */ +	for (i = MII_MAX_RETRY; i > 0; i --) { +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_ADDR); +		if ((val16 & 3) == 0) +			break; +	} +	/* timed out */ +	if (!i) { +		ET_ERROR(("mii_rreg: timeout")); +		return -1; +	} + +	ET_MSG(("%s: [0x%x-0x%x] => 0x%x (len %d)\n", __FUNCTION__, page, reg, val16, len)); + +	switch (len) { +	case 8: +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA3); +		ptr[7] = (val16 >> 8); +		ptr[6] = (val16 & 0xff); +		/* FALLTHRU */ + +	case 6: +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA2); +		ptr[5] = (val16 >> 8); +		ptr[4] = (val16 & 0xff); +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA1); +		ptr[3] = (val16 >> 8); +		ptr[2] = (val16 & 0xff); +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA0); +		ptr[1] = (val16 >> 8); +		ptr[0] = (val16 & 0xff); +		break; + +	case 4: +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA1); +		*(uint32 *)val = (((uint32)val16) << 16); +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA0); +		*(uint32 *)val |= val16; +		break; + +	case 2: +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA0); +		*(uint16 *)val = val16; +		break; +	case 1: +		val16 = robo->miird(h, PSEUDO_PHYAD, REG_MII_DATA0); +		*(uint8 *)val = (uint8)(val16 & 0xff); +		break; +	} + +	return 0; +} + +/* MII interface functions */ +static dev_ops_t mdcmdio = { +	NULL, +	NULL, +	mii_wreg, +	mii_rreg, +	"MII (MDC/MDIO)" +}; + +/* High level switch configuration functions. */ + +static int +findmatch(char *string, char *name) +{ +	uint len; +	char *c; + +	len = strlen(name); +	/* CSTYLED */ +	while ((c = strchr(string, ',')) != NULL) { +		if (len == (uint)(c - string) && !strncmp(string, name, len)) +			return 1; +		string = c + 1; +	} + +	return (!strcmp(string, name)); +} + +static uint +getgpiopin(char *vars, char *pin_name, uint def_pin) +{ +	char name[] = "gpioXXXX"; +	char *val; +	uint pin; + +	/* Go thru all possibilities till a match in pin name */ +	for (pin = 0; pin < GPIO_NUMPINS; pin ++) { +		snprintf(name, sizeof(name), "gpio%d", pin); +		val = getvar(vars, name); +		if (val && findmatch(val, pin_name)) +			return pin; +	} + +	if (def_pin != GPIO_PIN_NOTDEFINED) { +		/* make sure the default pin is not used by someone else */ +		snprintf(name, sizeof(name), "gpio%d", def_pin); +		if (getvar(vars, name)) { +			def_pin =  GPIO_PIN_NOTDEFINED; +		} +	} + +	return def_pin; +} + + +/* Port flags */ +#define FLAG_TAGGED	't'	/* output tagged (external ports only) */ +#define FLAG_UNTAG	'u'	/* input & output untagged (CPU port only, for OS (linux, ...) */ +#define FLAG_LAN	'*'	/* input & output untagged (CPU port only, for CFE */ + +/* port descriptor */ +typedef	struct { +	uint32 untag;	/* untag enable bit (Page 0x05 Address 0x63-0x66 Bit[17:9]) */ +	uint32 member;	/* vlan member bit (Page 0x05 Address 0x63-0x66 Bit[7:0]) */ +	uint8 ptagr;	/* port tag register address (Page 0x34 Address 0x10-0x1F) */ +	uint8 cpu;	/* is this cpu port? */ +} pdesc_t; + +pdesc_t pdesc97[] = { +	/* 5395/5397/5398 is 0 ~ 7.  port 8 is IMP port. */ +	/* port 0 */ {1 << 9, 1 << 0, REG_VLAN_PTAG0, 0}, +	/* port 1 */ {1 << 10, 1 << 1, REG_VLAN_PTAG1, 0}, +	/* port 2 */ {1 << 11, 1 << 2, REG_VLAN_PTAG2, 0}, +	/* port 3 */ {1 << 12, 1 << 3, REG_VLAN_PTAG3, 0}, +	/* port 4 */ {1 << 13, 1 << 4, REG_VLAN_PTAG4, 0}, +	/* port 5 */ {1 << 14, 1 << 5, REG_VLAN_PTAG5, 0}, +	/* port 6 */ {1 << 15, 1 << 6, REG_VLAN_PTAG6, 0}, +	/* port 7 */ {1 << 16, 1 << 7, REG_VLAN_PTAG7, 0}, +	/* mii port */ {1 << 17, 1 << 8, REG_VLAN_PTAG8, 1}, +}; + +pdesc_t pdesc25[] = { +	/* port 0 */ {1 << 6, 1 << 0, REG_VLAN_PTAG0, 0}, +	/* port 1 */ {1 << 7, 1 << 1, REG_VLAN_PTAG1, 0}, +	/* port 2 */ {1 << 8, 1 << 2, REG_VLAN_PTAG2, 0}, +	/* port 3 */ {1 << 9, 1 << 3, REG_VLAN_PTAG3, 0}, +	/* port 4 */ {1 << 10, 1 << 4, REG_VLAN_PTAG4, 0}, +	/* mii port */ {1 << 11, 1 << 5, REG_VLAN_PTAG5, 1}, +}; + + +#define to_robo(driver) ((robo_info_t *) ((switch_driver *) driver)->priv) +#define ROBO_START(driver) \ +	do { \ +		robo_info_t *robo = to_robo(driver); \ +		if (robo->ops->enable_mgmtif) \ +			robo->ops->enable_mgmtif(robo) + +#define ROBO_END(driver) \ +		if (robo->ops->disable_mgmtif) \ +			robo->ops->disable_mgmtif(robo); \ +	} while (0) + +int +bcm_robo_reset(robo_info_t *robo) +{ +	int i, max_port_ind; +	uint8 val8; +	uint16 val16; +	uint32 val32; +	pdesc_t *pdesc; +	int pdescsz; + +/* printk(KERN_WARNING "bcmrobo.c: bcm_robo_reset\n"); */ + +	if (robo->ops->enable_mgmtif) +		robo->ops->enable_mgmtif(robo); + +	/* setup global vlan configuration, FIXME: necessary? */ +	/* VLAN Control 0 Register (Page 0x34, Address 0) */ +	val8 = ((1 << 7) |		/* enable 802.1Q VLAN */ +	        (3 << 5));		/* individual VLAN learning mode */ +	robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL0, &val8, sizeof(val8)); + +	/* VLAN Control 1 Register (Page 0x34, Address 1) */ +	robo->ops->read_reg(robo, PAGE_VLAN, REG_VLAN_CTRL0, &val8, sizeof(val8)); +	val8 |= ((1 << 2) |		/* enable RSV multicast V Fwdmap */ +		(1 << 3));		/* enable RSV multicast V Untagmap */ +	if (robo->devid == DEVID5325) +		val8 |= (1 << 1);	/* enable RSV multicast V Tagging */ +	robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL1, &val8, sizeof(val8)); + +	bcm_robo_set_macaddr(robo, NULL); + +	if (robo->devid == DEVID5325) { +		/* VLAN Control 4 Register (Page 0x34, Address 4) */ +		val8 = (1 << 6);		/* drop frame with VID violation */ +		robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL4, &val8, sizeof(val8)); + +		/* VLAN Control 5 Register (Page 0x34, Address 5) */ +		val8 = (1 << 3);		/* drop frame when miss V table */ +		robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL5, &val8, sizeof(val8)); + +		pdesc = pdesc25; +		pdescsz = sizeof(pdesc25) / sizeof(pdesc_t); +	} else { +		pdesc = pdesc97; +		pdescsz = sizeof(pdesc97) / sizeof(pdesc_t); +	} + +	if (robo->devid == DEVID5325) { +		/* setup priority mapping - applies to tagged ingress frames */ +		/* Priority Re-map Register (Page 0x34, Address 0x20-0x23) */ +		/* FIXME: un-hardcode */ +		val32 = ((0 << 0) |	/* 0 -> 0 */ +		         (1 << 3) |	/* 1 -> 1 */ +		         (2 << 6) |	/* 2 -> 2 */ +		         (3 << 9) |	/* 3 -> 3 */ +		         (4 << 12) |	/* 4 -> 4 */ +		         (5 << 15) |	/* 5 -> 5 */ +		         (6 << 18) |	/* 6 -> 6 */ +		         (7 << 21));	/* 7 -> 7 */ +		robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_PMAP, &val32, sizeof(val32)); +	} + +	/* Set unmanaged mode */ +	robo->ops->read_reg(robo, PAGE_CTRL, REG_CTRL_MODE, &val8, sizeof(val8)); +	val8 &= (~(1 << 0)); +	robo->ops->write_reg(robo, PAGE_CTRL, REG_CTRL_MODE, &val8, sizeof(val8)); + +	/* No spanning tree for unmanaged mode */ +	val8 = 0; +	max_port_ind = ((robo->devid == DEVID5398) ? REG_CTRL_PORT7 : REG_CTRL_PORT4); +	for (i = REG_CTRL_PORT0; i <= max_port_ind; i++) { +		robo->ops->write_reg(robo, PAGE_CTRL, i, &val8, sizeof(val8)); +	} + +	/* WAN port LED */ +	val16 = 0x1f; +	robo->ops->write_reg(robo, PAGE_CTRL, 0x16, &val16, 2); + +	if (robo->ops->enable_mgmtif) +		robo->ops->disable_mgmtif(robo); + +	return 0; +} + +/* Get access to the RoboSwitch */ +robo_info_t * +bcm_robo_attach(sb_t *sbh, void *h, char *name, char *vars, miird_f miird, miiwr_f miiwr) +{ +	robo_info_t *robo; +	uint32 reset, idx; +	uint8 val8; +	uint16 val16; + +	/* Allocate and init private state */ +	if (!(robo = MALLOC(sb_osh(sbh), sizeof(robo_info_t)))) { +		ET_ERROR(("robo_attach: out of memory, malloced %d bytes", MALLOCED(sb_osh(sbh)))); +		return NULL; +	} +	bzero(robo, sizeof(robo_info_t)); + +	robo->h = h; +	robo->sbh = sbh; +	robo->vars = vars; +	robo->miird = miird; +	robo->miiwr = miiwr; +	robo->page = -1; +	robo->name = name; + +	/* Trigger external reset by nvram variable existance */ +	if ((reset = getgpiopin(robo->vars, "robo_reset", GPIO_PIN_NOTDEFINED)) != +	    GPIO_PIN_NOTDEFINED) { +		/* +		 * Reset sequence: RESET low(50ms)->high(20ms) +		 * +		 * We have to perform a full sequence for we don't know how long +		 * it has been from power on till now. +		 */ +		ET_MSG(("%s: Using external reset in gpio pin %d\n", __FUNCTION__, reset)); +		reset = 1 << reset; + +		/* Keep RESET low for 50 ms */ +		sb_gpioout(robo->sbh, reset, 0, GPIO_DRV_PRIORITY); +		sb_gpioouten(robo->sbh, reset, reset, GPIO_DRV_PRIORITY); +		bcm_mdelay(50); + +		if (robo->devid == DEVID5395) +			nvram_set("switch_type", "BCM5395"); +		else if(robo->devid == DEVID5397) +			nvram_set("switch_type", "BCM5397"); +		else +			nvram_set("switch_type", "unknown"); + +		/* Keep RESET high for at least 20 ms */ +		sb_gpioout(robo->sbh, reset, reset, GPIO_DRV_PRIORITY); +		bcm_mdelay(20); +	} else { +		/* In case we need it */ +		idx = sb_coreidx(robo->sbh); + +		if (sb_setcore(robo->sbh, SB_ROBO, 0)) { +			/* If we have an internal robo core, reset it using sb_core_reset */ +			ET_MSG(("%s: Resetting internal robo core\n", __FUNCTION__)); +			sb_core_reset(robo->sbh, 0, 0); +		} + +		sb_setcoreidx(robo->sbh, idx); +	} + +	if (miird && miiwr) { +		uint16 tmp; +		int rc, retry_count = 0; + +		/* Read the PHY ID */ +		tmp = miird(h, PSEUDO_PHYAD, 2); +		if (tmp != 0xffff) { +			do { +				rc = mii_rreg(robo, PAGE_MMR, REG_DEVICE_ID, \ +							  &robo->devid, sizeof(uint16)); +				if (rc != 0) +					break; +				retry_count++; +			} while ((robo->devid == 0) && (retry_count < 10)); + +			ET_MSG(("%s: devid read %ssuccesfully via mii: 0x%x\n", __FUNCTION__, \ +			        rc ? "un" : "", robo->devid)); +			ET_MSG(("%s: mii access to switch works\n", __FUNCTION__)); +			robo->ops = &mdcmdio; +			if ((rc != 0) || (robo->devid == 0)) { +				ET_MSG(("%s: error reading devid, assuming 5325e\n", __FUNCTION__)); +				robo->devid = DEVID5325; +			} +			ET_MSG(("%s: devid: 0x%x\n", __FUNCTION__, robo->devid)); +		} +	} + +	if ((robo->devid == DEVID5395) || +	    (robo->devid == DEVID5397) || +	    (robo->devid == DEVID5398)) { +		uint8 srst_ctrl; + +		/* If it is a 539x switch, use the soft reset register */ +		ET_MSG(("%s: Resetting 539x robo switch\n", __FUNCTION__)); + +		/* Reset the 539x switch core and register file */ +		srst_ctrl = 0x83; +		mii_wreg(robo, PAGE_CTRL, REG_CTRL_SRST, &srst_ctrl, sizeof(uint8)); + +		bcm_mdelay(500); + +		srst_ctrl = 0x00; +		mii_wreg(robo, PAGE_CTRL, REG_CTRL_SRST, &srst_ctrl, sizeof(uint8)); +	} + +	if (!robo->ops) { +		int mosi, miso, ss, sck; + +		robo->ops = &spigpio; +		robo->devid = DEVID5325; + +		/* Init GPIO mapping. Default 2, 3, 4, 5 */ +		ss = getgpiopin(vars, "robo_ss", 2); +		if (ss == GPIO_PIN_NOTDEFINED) { +			ET_ERROR(("robo_attach: robo_ss gpio fail: GPIO 2 in use")); +			goto error; +		} +		robo->ss = 1 << ss; +		sck = getgpiopin(vars, "robo_sck", 3); +		if (sck == GPIO_PIN_NOTDEFINED) { +			ET_ERROR(("robo_attach: robo_sck gpio fail: GPIO 3 in use")); +			goto error; +		} +		robo->sck = 1 << sck; +		mosi = getgpiopin(vars, "robo_mosi", 4); +		if (mosi == GPIO_PIN_NOTDEFINED) { +			ET_ERROR(("robo_attach: robo_mosi gpio fail: GPIO 4 in use")); +			goto error; +		} +		robo->mosi = 1 << mosi; +		miso = getgpiopin(vars, "robo_miso", 5); +		if (miso == GPIO_PIN_NOTDEFINED) { +			ET_ERROR(("robo_attach: robo_miso gpio fail: GPIO 5 in use")); +			goto error; +		} +		robo->miso = 1 << miso; +		ET_MSG(("%s: ss %d sck %d mosi %d miso %d\n", __FUNCTION__, +		        ss, sck, mosi, miso)); +	} + +	/* sanity check */ +	ASSERT(robo->ops); +	ASSERT(robo->ops->write_reg); +	ASSERT(robo->ops->read_reg); +	ASSERT((robo->devid == DEVID5325) || +	       (robo->devid == DEVID5395) || +	       (robo->devid == DEVID5397) || +	       (robo->devid == DEVID5398)); + +	bcm_robo_reset(robo); +	config_attach(robo); + +	return robo; + +error: +	MFREE(sb_osh(robo->sbh), robo, sizeof(robo_info_t)); +	return NULL; +} + +/* Release access to the RoboSwitch */ +void +bcm_robo_detach(robo_info_t *robo) +{ +	config_detach(robo); +	MFREE(sb_osh(robo->sbh), robo, sizeof(robo_info_t)); +} + +/* Enable the device and set it to a known good state */ +int +bcm_robo_enable_device(robo_info_t *robo) +{ +	uint8 reg_offset, reg_val; +	int ret = 0; + +	/* Enable management interface access */ +	if (robo->ops->enable_mgmtif) +		robo->ops->enable_mgmtif(robo); + +	if (robo->devid == DEVID5398) { +		/* Disable unused ports: port 6 and 7 */ +		for (reg_offset = REG_CTRL_PORT6; reg_offset <= REG_CTRL_PORT7; reg_offset ++) { +			/* Set bits [1:0] to disable RX and TX */ +			reg_val = 0x03; +			robo->ops->write_reg(robo, PAGE_CTRL, reg_offset, ®_val, +			                     sizeof(reg_val)); +		} +	} + +	if (robo->devid == DEVID5325) { +		/* Must put the switch into Reverse MII mode! */ + +		/* MII port state override (page 0 register 14) */ +		robo->ops->read_reg(robo, PAGE_CTRL, REG_CTRL_MIIPO, ®_val, sizeof(reg_val)); + +		/* Bit 4 enables reverse MII mode */ +		if (!(reg_val & (1 << 4))) { +			/* Enable RvMII */ +			reg_val |= (1 << 4); +			robo->ops->write_reg(robo, PAGE_CTRL, REG_CTRL_MIIPO, ®_val, +			                     sizeof(reg_val)); + +			/* Read back */ +			robo->ops->read_reg(robo, PAGE_CTRL, REG_CTRL_MIIPO, ®_val, +			                    sizeof(reg_val)); +			if (!(reg_val & (1 << 4))) { +				ET_ERROR(("robo_enable_device: enabling RvMII mode failed\n")); +				ret = -1; +			} +		} +	} + +	/* Disable management interface access */ +	if (robo->ops->disable_mgmtif) +		robo->ops->disable_mgmtif(robo); + +	return ret; +} + + +void bcm_robo_set_macaddr(robo_info_t *robo, char *mac_addr) +{ +	uint8 arl_entry[8] = { 0 }, arl_entry1[8] = { 0 }; + +	if (mac_addr != NULL) +		memcpy(robo->macaddr, mac_addr, 6); + +	mac_addr = robo->macaddr; + +	/* setup mac address */ +	arl_entry[0] = mac_addr[5]; +	arl_entry[1] = mac_addr[4]; +	arl_entry[2] = mac_addr[3]; +	arl_entry[3] = mac_addr[2]; +	arl_entry[4] = mac_addr[1]; +	arl_entry[5] = mac_addr[0]; + +	if (robo->devid == DEVID5325) { +		/* Init the entry 1 of the bin */ +		robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_ARL_E1, \ +				     arl_entry1, sizeof(arl_entry1)); +		robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_VID_E1, \ +				     arl_entry1, 1); + +		/* Init the entry 0 of the bin */ +		arl_entry[6] = 0x8;		/* Port Id: MII */ +		arl_entry[7] = 0xc0;	/* Static Entry, Valid */ + +		robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_ARL_E0, \ +				     arl_entry, sizeof(arl_entry)); +		robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_MINDX, \ +				     arl_entry, ETHER_ADDR_LEN); + +	} else { +		/* Initialize the MAC Addr Index Register */ +		robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_MINDX, \ +				     arl_entry, ETHER_ADDR_LEN); +	} +} + +static int handle_reset(void *driver, char *buf, int nr) +{ +	ROBO_START(driver); +	bcm_robo_reset(robo); +	ROBO_END(driver); + +	return 0; +} + + +static int handle_enable_read(void *driver, char *buf, int nr) +{ +	int ret; +	uint8 val8; + +	ROBO_START(driver); +	robo->ops->read_reg(robo, PAGE_CTRL, REG_CTRL_MODE, &val8, sizeof(val8)); +	ret = sprintf(buf, "%d\n", !!(val8 & (1 << 1))); +	ROBO_END(driver); + +	return ret; +} + +static int handle_enable_write(void *driver, char *buf, int nr) +{ +	uint8 val8; + +/* printk(KERN_WARNING "bcmrobo.c: handle_enable_write\n"); */ + +	ROBO_START(driver); +	robo->ops->read_reg(robo, PAGE_CTRL, REG_CTRL_MODE, &val8, sizeof(val8)); +	val8 &= ~(1 << 1); +	val8 |= ((buf[0] == '1') << 1); +	robo->ops->write_reg(robo, PAGE_CTRL, REG_CTRL_MODE, &val8, sizeof(val8)); +	ROBO_END(driver); + +	return 0; +} + +static int handle_enable_vlan_read(void *driver, char *buf, int nr) +{ +	uint8 val8; + +	ROBO_START(driver); +	robo->ops->read_reg(robo, PAGE_VLAN, REG_VLAN_CTRL0, &val8, sizeof(val8)); +	ROBO_END(driver); + +	return sprintf(buf, "%d\n", (((val8 & (1 << 7)) == (1 << 7)) ? 1 : 0)); +} +static int handle_enable_vlan_write(void *driver, char *buf, int nr) +{ +	int disable = ((buf[0] != '1') ? 1 : 0); + +	uint8 val8; +	uint16 val16; +	pdesc_t *pdesc; +	int pdescsz; +	uint16 vid; +	uint8 arl_entry[8] = { 0 }, arl_entry1[8] = { 0 }; + +/* printk(KERN_WARNING "bcmrobo.c: handle_enable_vlan_write\n"); */ + +	ROBO_START(driver); + +	/* setup global vlan configuration */ +	/* VLAN Control 0 Register (Page 0x34, Address 0) */ +	val8 = disable ? 0 :  +		((1 << 7) |		/* enable/disable 802.1Q VLAN */ +		(3 << 5));		/* individual VLAN learning mode */ +	robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL0, &val8, sizeof(val8)); + +	/* VLAN Control 1 Register (Page 0x34, Address 1) */ +	val8 = disable ? 0 :  +		((1 << 2) |		/* enable/disable RSV multicast V Fwdmap */ +		(1 << 3));		/* enable/disable RSV multicast V Untagmap */ +	if (robo->devid == DEVID5325) +		val8 |= disable ? 0 : (1 << 1);		/* enable/disable RSV multicast V Tagging */ +	robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL1, &val8, sizeof(val8)); + +	if ( disable == 0 ) {		/* FIXME: ok to stop here when disabling? */ +		arl_entry[0] = robo->macaddr[5]; +		arl_entry[1] = robo->macaddr[4]; +		arl_entry[2] = robo->macaddr[3]; +		arl_entry[3] = robo->macaddr[2]; +		arl_entry[4] = robo->macaddr[1]; +		arl_entry[5] = robo->macaddr[0]; + +		if (robo->devid == DEVID5325) { +			/* Init the entry 1 of the bin */ +			robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_ARL_E1, \ +					     arl_entry1, sizeof(arl_entry1)); +			robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_VID_E1, \ +					     arl_entry1, 1); + +			/* Init the entry 0 of the bin */ +			arl_entry[6] = 0x8;		/* Port Id: MII */ +			arl_entry[7] = 0xc0;	/* Static Entry, Valid */ + +			robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_ARL_E0, \ +					     arl_entry, sizeof(arl_entry)); +			robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_MINDX, \ +					     arl_entry, ETHER_ADDR_LEN); + +			/* VLAN Control 4 Register (Page 0x34, Address 4) */ +			val8 = (1 << 6);		/* drop frame with VID violation */ +			robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL4, &val8, sizeof(val8)); + +			/* VLAN Control 5 Register (Page 0x34, Address 5) */ +			val8 = (1 << 3);		/* drop frame when miss V table */ +			robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_CTRL5, &val8, sizeof(val8)); + +			pdesc = pdesc25; +			pdescsz = sizeof(pdesc25) / sizeof(pdesc_t); +		} else { +			/* Initialize the MAC Addr Index Register */ +			robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_MINDX, \ +					     arl_entry, ETHER_ADDR_LEN); + +			pdesc = pdesc97; +			pdescsz = sizeof(pdesc97) / sizeof(pdesc_t); +		} + +		/* setup each vlan. max. 16 vlans. */ +		/* force vlan id to be equal to vlan number */ +		for (vid = 0; vid < VLAN_NUMVLANS; vid ++) { + +			/* Add static ARL entries */ +			if (robo->devid == DEVID5325) { +				val8 = vid; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_VID_E0, \ +						     &val8, sizeof(val8)); +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_VINDX, \ +						     &val8, sizeof(val8)); + +				/* Write the entry */ +				val8 = 0x80; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_CTRL, \ +						     &val8, sizeof(val8)); +				/* Wait for write to complete */ +				SPINWAIT((robo->ops->read_reg(robo, PAGE_VTBL, REG_VTBL_CTRL, \ +					 &val8, sizeof(val8)), ((val8 & 0x80) != 0)), +					 100 /* usec */); +			} else { +				/* Set the VLAN Id in VLAN ID Index Register */ +				val8 = vid; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_VINDX, \ +						     &val8, sizeof(val8)); + +				/* Set the MAC addr and VLAN Id in ARL Table MAC/VID Entry 0 +				 * Register. +				 */ +				arl_entry[6] = vid; +				arl_entry[7] = 0x0; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_ARL_E0, \ +						     arl_entry, sizeof(arl_entry)); + +				/* Set the Static bit , Valid bit and Port ID fields in +				 * ARL Table Data Entry 0 Register +				 */ +				val16 = 0xc008; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_DAT_E0, \ +						     &val16, sizeof(val16)); + +				/* Clear the ARL_R/W bit and set the START/DONE bit in +				 * the ARL Read/Write Control Register. +				 */ +				val8 = 0x80; +				robo->ops->write_reg(robo, PAGE_VTBL, REG_VTBL_CTRL, \ +						     &val8, sizeof(val8)); +				/* Wait for write to complete */ +				SPINWAIT((robo->ops->read_reg(robo, PAGE_VTBL, REG_VTBL_CTRL, \ +					 &val8, sizeof(val8)), ((val8 & 0x80) != 0)), +					 100 /* usec */); +			} +		} +	} + +	ROBO_END(driver); +	return 0; +} + +static int handle_vlan_port_read(void *driver, char *buf, int nr) +{ +	/* FIXME: yeah, some work is missing here */ +	return sprintf(buf, "bcmrobo.c: handle_vlan_port_read unimplimented\n"); +} + +static int handle_vlan_port_write(void *driver, char *buf, int nr) +{ + +	switch_driver *d = (switch_driver *) driver; +	switch_vlan_config *c = switch_parse_vlan(d, buf); + +	uint8 val8; +	uint16 val16; +	uint32 val32; +	int j; +	pdesc_t *pdesc; +	int pdescsz; + +/* printk(KERN_WARNING "bcmrobo.c: handle_vlan_port_write, nr %d\n", nr); */ + +        if (c == NULL) +                return -EINVAL; + +	ROBO_START(driver); + +	if (robo->devid == DEVID5325) { +		pdesc = pdesc25; +		pdescsz = sizeof(pdesc25) / sizeof(pdesc_t); +	} else { +		pdesc = pdesc97; +		pdescsz = sizeof(pdesc97) / sizeof(pdesc_t); +	} + + +        for (j = 0; j < d->ports; j++) { +                if ((c->untag | c->pvid) & (1 << j)) +			if ((j != d->cpuport) || (c->untag & (1 << j))) { + +				/* change default vlan tag */ + +/* printk(KERN_WARNING "bcmrobo.c: set default vlan tag, port %d -> vlan %d\n", j, nr); */ + +				val16 = ((0 << 13) |		/* priority - always 0 */ +					nr);			/* vlan id */ +				robo->ops->write_reg(robo, PAGE_VLAN, pdesc[j].ptagr, &val16, sizeof(val16)); +			} +        } + + +	if (robo->devid == DEVID5325) { +		val32 = ((c->untag << 6) |		/* untag enable */ +			 c->port);			/* vlan members */ +		val32 |= ((1 << 20) |           /* valid write */ +			  ((nr >> 4) << 12));  /* vlan id bit[11:4] */ +		/* VLAN Write Register (Page 0x34, Address 0x08-0x0B) */ +		robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_WRITE, &val32, +				     sizeof(val32)); +		/* VLAN Table Access Register (Page 0x34, Address 0x06-0x07) */ +		val16 = ((1 << 13) |    /* start command */ +			 (1 << 12) |    /* write state */ +			 nr);          /* vlan id */ +		robo->ops->write_reg(robo, PAGE_VLAN, REG_VLAN_ACCESS, &val16, +				     sizeof(val16)); +	} else { +		uint8 vtble, vtbli, vtbla; +		val32 = ((c->untag << 9) |		/* untag enable */ +			 c->port);			/* vlan members */ + +		if (robo->devid == DEVID5395) { +			vtble = REG_VTBL_ENTRY_5395; +			vtbli = REG_VTBL_INDX_5395; +			vtbla = REG_VTBL_ACCESS_5395; +		} else { +			vtble = REG_VTBL_ENTRY; +			vtbli = REG_VTBL_INDX; +			vtbla = REG_VTBL_ACCESS; +		} + +		/* VLAN Table Entry Register (Page 0x05, Address 0x63-0x66/0x83-0x86) */ +		robo->ops->write_reg(robo, PAGE_VTBL, vtble, &val32, +				     sizeof(val32)); +		/* VLAN Table Address Index Reg (Page 0x05, Address 0x61-0x62/0x81-0x82) */ +		val16 = nr;        /* vlan id */ +		robo->ops->write_reg(robo, PAGE_VTBL, vtbli, &val16, +				     sizeof(val16)); + +		/* VLAN Table Access Register (Page 0x34, Address 0x60/0x80) */ +		val8 = ((1 << 7) |      /* start command */ +			0);             /* write */ +		robo->ops->write_reg(robo, PAGE_VTBL, vtbla, &val8, +				     sizeof(val8)); +	} + +	ROBO_END(driver); +	return 0; +} + +static int __init config_attach(robo_info_t *robo) +{ +	switch_config cfg[] = { +		{"enable", handle_enable_read, handle_enable_write}, +		{"reset", NULL, handle_reset}, +		{"enable_vlan", handle_enable_vlan_read, handle_enable_vlan_write}, +		{NULL, NULL, NULL} +	}; +	switch_config vlan[] = { +		{"ports", handle_vlan_port_read, handle_vlan_port_write}, +		{NULL, NULL, NULL} +	}; +	switch_driver driver = { +		name: DRIVER_NAME, +		version: DRIVER_VERSION, +		interface: robo->name, +		cpuport: 8, +		ports: 9, +		vlans: 16, +		driver_handlers: cfg, +		port_handlers: NULL, +		vlan_handlers: vlan, +	}; +	if (robo->devid == DEVID5325) { +		driver.ports = 6; +		driver.cpuport = 5; +	} +	driver.priv = (void *) robo; + +	return switch_register_driver(&driver); +} + +static void __exit config_detach(robo_info_t *robo) +{ +	switch_unregister_driver(DRIVER_NAME); +} + +  | 
