/* checks that attached switch is 5325/5352/5354/5356/5357/53115/5301x */ static int robo_vlan535x(robo_t *robo, u32 phyid) { #ifdef BCM5301X if ((robo_read32(robo, ROBO_MGMT_PAGE, ROBO_DEVICE_ID) & 0xfffffff0) == 0x53010) return 5; #else /* set vlan access id to 15 and read it back */ u16 val16 = 15; robo_write16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* 5365 will refuse this as it does not have this reg */ if (robo_read16(robo, ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) != val16) return 0; /* gigabit ? */ if (robo->et != 1 && (mdio_read(robo, 0, ROBO_MII_STAT) & 0x0100)) robo->gmii = ((mdio_read(robo, 0, 0x0f) & 0xf000) != 0); /* 53115 ? */ if (robo->gmii && robo_read32(robo, ROBO_STAT_PAGE, ROBO_LSA_IM_PORT) != 0) { robo_write16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395, val16); robo_write16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395, (1 << 7) /* start */ | 1 /* read */); if (robo_read16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_ACCESS_5395) == 1 && robo_read16(robo, ROBO_ARLIO_PAGE, ROBO_VTBL_INDX_5395) == val16) return 4; } /* dirty trick for 5356/5357 */ if ((phyid & 0xfff0ffff ) == 0x5da00362 || (phyid & 0xfff0ffff ) == 0x5e000362) return 3; /* 5325/5352/5354*/ return 1; #endif }
static int robo_switch_enable(void) { unsigned int i, last_port; u16 val; val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE); if (!(val & (1 << 1))) { /* Unmanaged mode */ val &= ~(1 << 0); /* With forwarding */ val |= (1 << 1); robo_write16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE, val); val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE); if (!(val & (1 << 1))) { printk("Failed to enable switch\n"); return -EBUSY; } last_port = (robo.devid == ROBO_DEVICE_ID_5398) ? ROBO_PORT6_CTRL : ROBO_PORT3_CTRL; for (i = ROBO_PORT0_CTRL; i < last_port + 1; i++) robo_write16(ROBO_CTRL_PAGE, i, 0); } /* WAN port LED */ robo_write16(ROBO_CTRL_PAGE, 0x16, 0x1F); return 0; }
static int handle_vlan_port_read(void *driver, char *buf, int nr) { __u16 val16; int len = 0; int j; val16 = (nr) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */; if (is_5350) { u32 val32; robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* actual read */ val32 = robo_read32(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val32 & (1 << 20)) /* valid */) { for (j = 0; j < 6; j++) { if (val32 & (1 << j)) { len += sprintf(buf + len, "%d", j); if (val32 & (1 << (j + 6))) { if (j == 5) buf[len++] = 'u'; } else { buf[len++] = 't'; if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr) buf[len++] = '*'; } buf[len++] = '\t'; } } len += sprintf(buf + len, "\n"); } } else { robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16); /* actual read */ val16 = robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val16 & (1 << 14)) /* valid */) { for (j = 0; j < 6; j++) { if (val16 & (1 << j)) { len += sprintf(buf + len, "%d", j); if (val16 & (1 << (j + 7))) { if (j == 5) buf[len++] = 'u'; } else { buf[len++] = 't'; if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr) buf[len++] = '*'; } buf[len++] = '\t'; } } len += sprintf(buf + len, "\n"); } } buf[len] = '\0'; return len; }
/* checks that attached switch is 5325E/5350 */ static int robo_vlan5350() { /* set vlan access id to 15 and read it back */ __u16 val16 = 15; robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* 5365 will refuse this as it does not have this reg */ return (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) == val16); }
static int robo_switch_enable(void) { unsigned int i, last_port; u16 val; #ifdef CONFIG_BCM47XX char buf[20]; #endif val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE); if (!(val & (1 << 1))) { /* Unmanaged mode */ val &= ~(1 << 0); /* With forwarding */ val |= (1 << 1); robo_write16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE, val); val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE); if (!(val & (1 << 1))) { printk("Failed to enable switch\n"); return -EBUSY; } last_port = (robo.devid == ROBO_DEVICE_ID_5398) ? ROBO_PORT6_CTRL : ROBO_PORT3_CTRL; for (i = ROBO_PORT0_CTRL; i < last_port + 1; i++) robo_write16(ROBO_CTRL_PAGE, i, 0); } #ifdef CONFIG_BCM47XX /* WAN port LED, except for Netgear WGT634U */ if (nvram_getenv("nvram_type", buf, sizeof(buf)) >= 0) { if (strcmp(buf, "cfe") != 0) robo_write16(ROBO_CTRL_PAGE, 0x16, 0x1F); } #endif return 0; }
static int handle_vlan_port_read(char *buf, int nr) { __u16 val16; int len = 0; int j; val16 = (nr) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */; if (is_5350) { u32 val32; robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* actual read */ val32 = robo_read32(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val32 & (1 << 20)) /* valid */) { for (j = 0; j < 6; j++) { if (val32 & (1 << j)) { len += sprintf(buf + len, "%d%s\t", j, (val32 & (1 << (j + 6))) ? (j == 5 ? "u" : "") : "t"); } } len += sprintf(buf + len, "\n"); } } else { robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16); /* actual read */ val16 = robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val16 & (1 << 14)) /* valid */) { for (j = 0; j < 6; j++) { if (val16 & (1 << j)) { len += sprintf(buf + len, "%d%s\t", j, (val16 & (1 << (j + 7))) ? (j == 5 ? "u" : "") : "t"); } } len += sprintf(buf + len, "\n"); } } return len; }
static int handle_enable_vlan_read(void *driver, char *buf, int nr) { return sprintf(buf, "%d\n", (((robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0) & (1 << 7)) == (1 << 7)) ? 1 : 0)); }
static int handle_enable_read(void *driver, char *buf, int nr) { return sprintf(buf, "%d\n", (((robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2) == 2) ? 1 : 0)); }
int main(int argc, char *argv[]) { u16 val16; u32 val32; u16 mac[3]; int i = 0, j; int robo535x = 0; /* 0 - 5365, 1 - 5325/5352/5354, 3 - 5356, 4 - 53115, 5 - 5301x */ u32 phyid; static robo_t robo; struct ethtool_drvinfo info; if ((robo.fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } /* the only interface for now */ strcpy(robo.ifr.ifr_name, "eth0"); memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GDRVINFO; robo.ifr.ifr_data = (caddr_t)&info; if (ioctl(robo.fd, SIOCETHTOOL, (caddr_t)&robo.ifr) < 0) { perror("SIOCETHTOOL: your ethernet module is either unsupported or outdated"); exit(1); } else if (strcmp(info.driver, "et0") && strcmp(info.driver, "et1") && strcmp(info.driver, "b44")) { fprintf(stderr, "No suitable module found for %s (managed by %s)\n", robo.ifr.ifr_name, info.driver); exit(1); } /* try access using MII ioctls - get phy address */ if (ioctl(robo.fd, SIOCGMIIPHY, &robo.ifr) < 0) { int args[2] = { ROBO_PHY_ADDR << 16, 0x0 }; robo.ifr.ifr_data = (caddr_t) args; if (ioctl(robo.fd, SIOCGETCPHYRD2, &robo.ifr) < 0) robo.et = 1; else robo.et = 2; } else { /* got phy address check for robo address */ struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data; if (mii->phy_id != ROBO_PHY_ADDR) { fprintf(stderr, "Invalid phy address (%d)\n", mii->phy_id); exit(1); } } phyid = mdio_read(&robo, ROBO_PHY_ADDR, 0x2) | (mdio_read(&robo, ROBO_PHY_ADDR, 0x3) << 16); if (phyid == 0 && robo.et != 1) phyid = mdio_read(&robo, 0, 0x2) | (mdio_read(&robo, 0, 0x3) << 16); if (phyid == 0xffffffff || phyid == 0x55210022) { fprintf(stderr, "No Robo switch in managed mode found\n"); exit(1); } robo535x = robo_vlan535x(&robo, phyid); /* fprintf(stderr, "phyid %08x id %d\n", phyid, robo535x); */ for (i = 1; i < argc;) { if (strcasecmp(argv[i], "showmacs") == 0) { /* show MAC table of switch */ u16 buf[6]; int idx, off, base_vlan; base_vlan = 0; /*get_vid_by_idx(&robo, 0);*/ printf( "--------------------------------------\n" "VLAN MAC Type Port\n" "--------------------------------------\n"); robo_write16(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_RW_CTRL, 0x81); robo_write16(&robo, ROBO_ARLIO_PAGE, (robo535x >= 4) ? ROBO_ARL_SEARCH_CTRL_53115 : ROBO_ARL_SEARCH_CTRL, 0x80); for (idx = 0; idx < ((robo535x >= 4) ? NUM_ARL_TABLE_ENTRIES_53115 : robo535x ? NUM_ARL_TABLE_ENTRIES_5350 : NUM_ARL_TABLE_ENTRIES); idx++) { if (robo535x >= 4) { off = (idx & 0x01) << 4; if (!off && (robo_read16(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_SEARCH_CTRL_53115) & 0x80) == 0) break; robo_read(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_SEARCH_RESULT_53115 + off, buf, 4); robo_read(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_SEARCH_RESULT_EXT_53115 + off, &buf[4], 2); } else robo_read(&robo, ROBO_ARLIO_PAGE, ROBO_ARL_SEARCH_RESULT, buf, robo535x ? 4 : 5); if ((robo535x >= 4) ? (buf[5] & 0x01) : (buf[3] & 0x8000) /* valid */) { printf("%04i %02x:%02x:%02x:%02x:%02x:%02x %7s %c\n", (base_vlan | (robo535x >= 4) ? (base_vlan | (buf[3] & 0xfff)) : ((buf[3] >> 5) & 0x0f) | (robo535x ? 0 : ((buf[4] & 0x0f) << 4))), buf[2] >> 8, buf[2] & 255, buf[1] >> 8, buf[1] & 255, buf[0] >> 8, buf[0] & 255, ((robo535x >= 4 ? (buf[4] & 0x8000) : (buf[3] & 0x4000)) ? "STATIC" : "DYNAMIC"), ((robo535x >= 4) ? '0'+(buf[4] & 0x0f) : ports[buf[3] & 0x0f]) ); } } i++; } else
static inline u16 mdio_read(robo_t *robo, u16 phy_id, u8 reg) { return robo_read16(robo, 0x10 + phy_id, reg); }
static int robo_probe(char *devname) { __u32 phyid; unsigned int i; int err = 1; printk(KERN_INFO PFX "Probing device %s: ", devname); strcpy(robo.ifr.ifr_name, devname); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) if ((robo.dev = dev_get_by_name(devname)) == NULL) { #else if ((robo.dev = dev_get_by_name(&init_net, devname)) == NULL) { #endif printk("No such device\n"); return 1; } robo.device = devname; for (i = 0; i < 5; i++) robo.port[i] = i; robo.port[5] = 8; /* try access using MII ioctls - get phy address */ if (do_ioctl(SIOCGMIIPHY, NULL) < 0) { robo.use_et = 1; robo.phy_addr = ROBO_PHY_ADDR; } else { /* got phy address check for robo address */ struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &robo.ifr.ifr_data; if ((mii->phy_id != ROBO_PHY_ADDR) && (mii->phy_id != ROBO_PHY_ADDR_BCM63XX) && (mii->phy_id != ROBO_PHY_ADDR_TG3)) { printk("Invalid phy address (%d)\n", mii->phy_id); goto done; } robo.use_et = 0; /* The robo has a fixed PHY address that is different from the * Tigon3 and BCM63xx PHY address. */ robo.phy_addr = ROBO_PHY_ADDR; } phyid = mdio_read(robo.phy_addr, 0x2) | (mdio_read(robo.phy_addr, 0x3) << 16); if (phyid == 0xffffffff || phyid == 0x55210022) { printk("No Robo switch in managed mode found, phy_id = 0x%08x\n", phyid); goto done; } /* Get the device ID */ for (i = 0; i < 10; i++) { robo.devid = robo_read16(ROBO_MGMT_PAGE, ROBO_DEVICE_ID); if (robo.devid) break; udelay(10); } if (!robo.devid) robo.devid = ROBO_DEVICE_ID_5325; /* Fake it */ robo.is_5350 = robo_vlan5350(); robo_switch_reset(); err = robo_switch_enable(); if (err) goto done; err = 0; printk("found a 5%s%x!%s\n", robo.devid & 0xff00 ? "" : "3", robo.devid, robo.is_5350 ? " It's a 5350." : ""); done: if (err) { dev_put(robo.dev); robo.dev = NULL; } return err; } static int handle_vlan_port_read(void *driver, char *buf, int nr) { __u16 val16; int len = 0; int j; val16 = (nr) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */; if (robo.is_5350) { u32 val32; robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* actual read */ val32 = robo_read32(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val32 & (1 << 20)) /* valid */) { for (j = 0; j < 6; j++) { if (val32 & (1 << j)) { len += sprintf(buf + len, "%d", j); if (val32 & (1 << (j + 6))) { if (j == 5) buf[len++] = 'u'; } else { buf[len++] = 't'; if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr) buf[len++] = '*'; } buf[len++] = '\t'; } } len += sprintf(buf + len, "\n"); } } else { robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16); /* actual read */ val16 = robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val16 & (1 << 14)) /* valid */) { for (j = 0; j < 6; j++) { if (val16 & (1 << j)) { len += sprintf(buf + len, "%d", j); if (val16 & (1 << (j + 7))) { if (j == 5) buf[len++] = 'u'; } else { buf[len++] = 't'; if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr) buf[len++] = '*'; } buf[len++] = '\t'; } } len += sprintf(buf + len, "\n"); } } buf[len] = '\0'; return len; }
static int robo_probe(char *devname) { struct ethtool_drvinfo info; int i; __u32 phyid; printk("Probing device %s: ", devname); strcpy(ifr.ifr_name, devname); if ((dev = dev_get_by_name(devname)) == NULL) { printk("No such device\n"); return 1; } info.cmd = ETHTOOL_GDRVINFO; if (do_ioctl(SIOCETHTOOL, (void *) &info) < 0) { printk("SIOCETHTOOL: not supported\n"); return 1; } /* try access using MII ioctls - get phy address */ if (do_ioctl(SIOCGMIIPHY, NULL) < 0) { use_et = 1; } else { /* got phy address check for robo address */ struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &ifr.ifr_data; if (mii->phy_id != ROBO_PHY_ADDR) { printk("Invalid phy address (%d)\n", mii->phy_id); return 1; } } phyid = mdio_read(ROBO_PHY_ADDR, 0x2) | (mdio_read(ROBO_PHY_ADDR, 0x3) << 16); if (phyid == 0xffffffff || phyid == 0x55210022) { printk("No Robo switch in managed mode found\n"); return 1; } is_5350 = robo_vlan5350(); max_ports = 6; for (i = 0; i <= (is_5350 ? VLAN_ID_MAX5350 : VLAN_ID_MAX); i++) { /* issue read */ __u16 val16 = (i) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */; if (is_5350) { u32 val32; robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16); /* actual read */ val32 = robo_read32(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val32 & (1 << 20)) /* valid */) { max_vlans = i + 1; } } else { robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16); /* actual read */ val16 = robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_READ); if ((val16 & (1 << 14)) /* valid */) { max_vlans = i + 1; } } } printk("found!\n"); return 0; }