From 128434cb236523f4cec881622b8606cb1533b677 Mon Sep 17 00:00:00 2001 From: nbd Date: Sun, 15 Jun 2008 11:11:28 +0000 Subject: (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 git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11471 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- package/broadcom-57xx/src/bcmrobo.c | 1329 +++++++++++++++++++++++++++++++++++ 1 file changed, 1329 insertions(+) create mode 100644 package/broadcom-57xx/src/bcmrobo.c (limited to 'package/broadcom-57xx/src/bcmrobo.c') 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 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include "bcmparams.h" +#include +#include +#include "bcmrobo.h" +#include "proto/ethernet.h" +#include + +#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); +} + + -- cgit v1.2.3