/* <:copyright-broadcom Copyright (c) 2007 Broadcom Corporation All Rights Reserved No portions of this material may be reproduced in any form without the written permission of: Broadcom Corporation 16215 Alton Parkway Irvine, California 92619 All information contained in this document is Broadcom Corporation company private, proprietary, and trade secret. :> */ #include "bcm_map.h" #include "lib_types.h" #include "lib_malloc.h" #include "lib_string.h" #include "lib_printf.h" #include "mii.h" #include "bcmmii.h" #include "sbmips.h" #include "cfe_iocb.h" #include "cfe_timer.h" #include "robosw_reg.h" #include "dev_bcm63xx_eth.h" #include "bcmSpiRes.h" static uint32 mii_read(uint32 uPhyAddr, uint32 uRegAddr); static void mii_write(uint32 uPhyAddr, uint32 uRegAddr, uint32 data); static int ext_switch_init(void); static int ethsw_spi_ss_id(void); static void ethsw_spi_select(int page); static void ethsw_spi_rreg(int page, int reg, uint8 *data, int len); static void ethsw_spi_wreg(int page, int reg, uint8 *data, int len); void ethsw_rreg_ext(int access_type, int page, int reg, uint8 *data, int len); void ethsw_wreg_ext(int access_type, int page, int reg, uint8 *data, int len); static ETHERNET_MAC_INFO EnetInfo[BP_MAX_ENET_MACS]; static uint16 PortLinkState[BP_MAX_SWITCH_PORTS]; #define MDIO_BUS 0 #define SPI_BUS 1 #define TX_BDS 3 #define RX_BDS 16 #define CACHE_ALIGN 16 #define BUF_LENGTH 160 extern void _cfe_flushcache(int, uint8_t *, uint8_t *); #define INVAL_RANGE(s,l) _cfe_flushcache(CFE_CACHE_INVAL_RANGE,((uint8_t *) (s)),((uint8_t *) (s))+(l)) /* read a value from the MII */ static uint32 mii_read(uint32 uPhyAddr, uint32 uRegAddr) { SWITCH->MdioCtrl = 0x0; SWITCH->MdioCtrl = MdioCtrl_Read | (IsExtPhyId(uPhyAddr) ? MdioCtrl_Ext : 0) | ((uPhyAddr << MdioCtrl_ID_Shift) & MdioCtrl_ID_Mask) | (uRegAddr << MdioCtrl_Addr_Shift); cfe_usleep(100); return SWITCH->MdioData; } /* write a value to the MII */ static void mii_write(uint32 uPhyAddr, uint32 uRegAddr, uint32 data) { SWITCH->MdioCtrl = 0x0; SWITCH->MdioCtrl = MdioCtrl_Write | (IsExtPhyId(uPhyAddr) ? MdioCtrl_Ext : 0) | ((uPhyAddr << MdioCtrl_ID_Shift) & MdioCtrl_ID_Mask) | (uRegAddr << MdioCtrl_Addr_Shift) | data; cfe_usleep(100); } static int clkHz = 781000; #if !defined(_BCM96328_) static int clk781k = 2; #endif static int ethsw_spi_ss_id() { int slave_select; switch(EnetInfo[1].usConfigType) { case BP_ENET_CONFIG_SPI_SSB_0: slave_select = 0; break; case BP_ENET_CONFIG_SPI_SSB_1: slave_select = 1; break; case BP_ENET_CONFIG_SPI_SSB_2: slave_select = 2; break; case BP_ENET_CONFIG_SPI_SSB_3: slave_select = 3; break; default: slave_select = 1; xprintf("Invalid SPI_SS in usConfigType, Assuming 1\n"); break; } return slave_select; } static void ethsw_spi_select(int page) { unsigned char buf[3]; int spi_ss, cid = 0; spi_ss = ethsw_spi_ss_id(); /* Select new chip */ buf[0] = BCM5325_SPI_CMD_NORMAL | BCM5325_SPI_CMD_WRITE | ((cid & BCM5325_SPI_CHIPID_MASK) << BCM5325_SPI_CHIPID_SHIFT); /* Select new page */ buf[1] = PAGE_SELECT; buf[2] = (char)page; #if defined(_BCM96328_) BcmSpi_Write(buf, sizeof(buf), HS_SPI_BUS_NUM, spi_ss, clkHz); #else BcmSpi_Write(buf, sizeof(buf), LEG_SPI_BUS_NUM, spi_ss, clkHz); #endif } static void ethsw_spi_rreg(int page, int reg, uint8 *data, int len) { unsigned char buf[64]; int rc; int i; int max_check_spi_sts; int prependCnt = BCM5325_SPI_PREPENDCNT, spi_ss, cid = 0; spi_ss = ethsw_spi_ss_id(); ethsw_spi_select(page); /* write command byte and register address */ buf[0] = BCM5325_SPI_CMD_NORMAL | BCM5325_SPI_CMD_READ | ((cid & BCM5325_SPI_CHIPID_MASK) << BCM5325_SPI_CHIPID_SHIFT); buf[1] = (unsigned char)reg; #if defined(_BCM96328_) rc = BcmSpi_Read(buf, prependCnt, 1, HS_SPI_BUS_NUM, spi_ss, clkHz); #else rc = BcmSpi_Read(buf, prependCnt, 1, LEG_SPI_BUS_NUM, spi_ss, clkHz); #endif if (rc == SPI_STATUS_OK) { max_check_spi_sts = 0; do { /* write command byte and read spi_sts address */ buf[0] = BCM5325_SPI_CMD_NORMAL | BCM5325_SPI_CMD_READ | ((cid & BCM5325_SPI_CHIPID_MASK) << BCM5325_SPI_CHIPID_SHIFT); buf[1] = (unsigned char)BCM5325_SPI_STS; #if defined(_BCM96328_) rc = BcmSpi_Read(buf, prependCnt, 1, HS_SPI_BUS_NUM, spi_ss, clkHz); #else rc = BcmSpi_Read(buf, prependCnt, 1, LEG_SPI_BUS_NUM, spi_ss, clkHz); #endif if (rc == SPI_STATUS_OK) { /* check the bit 0 RACK bit is set */ if (buf[0] & BCM5325_SPI_CMD_RACK) { break; } cfe_usleep(10000); } else { break; } } while (max_check_spi_sts++ < 10); if (rc == SPI_STATUS_OK) { for (i = 0; i < len; i++) { buf[0] = BCM5325_SPI_CMD_NORMAL | BCM5325_SPI_CMD_READ | ((cid & BCM5325_SPI_CHIPID_MASK) << BCM5325_SPI_CHIPID_SHIFT); buf[1] = (unsigned char)0xf0; #if defined(_BCM96328_) rc = BcmSpi_Read(buf, prependCnt, 1, HS_SPI_BUS_NUM, spi_ss, clkHz); #else rc = BcmSpi_Read(buf, prependCnt, 1, LEG_SPI_BUS_NUM, spi_ss, clkHz); #endif if (rc == SPI_STATUS_OK) { *(data + (len - i - 1)) = buf[0]; } } } } } static void ethsw_spi_wreg(int page, int reg, uint8 *data, int len) { unsigned char buf[64]; int i; int spi_ss, cid = 0; ethsw_spi_select(page); spi_ss = ethsw_spi_ss_id(); buf[0] = BCM5325_SPI_CMD_NORMAL | BCM5325_SPI_CMD_WRITE | ((cid & BCM5325_SPI_CHIPID_MASK) << BCM5325_SPI_CHIPID_SHIFT); buf[1] = (char)reg; for (i = 0; i < len; i++) { /* Write the data out in LE format to the switch */ buf[BCM5325_SPI_PREPENDCNT+i] = *(data + (len - i - 1)); } BcmSpi_Write(buf, len+BCM5325_SPI_PREPENDCNT, LEG_SPI_BUS_NUM, spi_ss, clkHz); } /* External switch register access through MDC/MDIO */ static void ethsw_mdio_rreg(int page, int reg, uint8 *data, int len) { uint32 cmd, res, ret; int max_retry = 0; cmd = (page << REG_PPM_REG16_SWITCH_PAGE_NUMBER_SHIFT) | REG_PPM_REG16_MDIO_ENABLE; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG16, cmd); cmd = (reg << REG_PPM_REG17_REG_NUMBER_SHIFT) | REG_PPM_REG17_OP_READ; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG17, cmd); do { res = mii_read(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG17); cfe_usleep(10); } while (((res & (REG_PPM_REG17_OP_WRITE|REG_PPM_REG17_OP_READ)) != REG_PPM_REG17_OP_DONE) && (max_retry++ < 5)); ret = 0; ret |= mii_read(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG24) << 0; ret |= mii_read(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG25) << 16; switch (len) { case 1: *data = (uint8)ret; break; case 2: *(uint16 *)data = (uint16)ret; break; case 4: *(uint32 *)data = ret; break; } } static void ethsw_mdio_wreg(int page, int reg, uint8 *data, int len) { uint32 cmd, res; uint32 val = 0; int max_retry = 0; switch (len) { case 1: val = *data; break; case 2: val = *(uint16 *)data; break; case 4: val = *(uint32 *)data; break; } cmd = (page << REG_PPM_REG16_SWITCH_PAGE_NUMBER_SHIFT) | REG_PPM_REG16_MDIO_ENABLE; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG16, cmd); cmd = val>>0 & 0xffff; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG24, cmd); cmd = val>>16 & 0xffff; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG25, cmd); cmd = 0; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG26, cmd); cmd = 0; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG27, cmd); cmd = (reg << REG_PPM_REG17_REG_NUMBER_SHIFT) | REG_PPM_REG17_OP_WRITE; mii_write(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG17, cmd); do { res = mii_read(PSEUDO_PHY_ADDR, REG_PSEUDO_PHY_MII_REG17); cfe_usleep(10); } while (((res & (REG_PPM_REG17_OP_WRITE|REG_PPM_REG17_OP_READ)) != REG_PPM_REG17_OP_DONE) && (max_retry++ < 5)); } void ethsw_rreg_ext(int access_type, int page, int reg, uint8 *data, int len) { if (access_type == MDIO_BUS) { ethsw_mdio_rreg(page, reg, data, len); } else { ethsw_spi_rreg(page, reg, data, len); } } void ethsw_wreg_ext(int access_type, int page, int reg, uint8 *data, int len) { if (access_type == MDIO_BUS) { ethsw_mdio_wreg(page, reg, data, len); } else { ethsw_spi_wreg(page, reg, data, len); } } int ext_switch_init(void) { uint8 data8; uint32 data32, access_type; #if !defined(_BCM96328_) uint32 clkSave; #endif switch (EnetInfo[1].usConfigType) { case BP_ENET_CONFIG_SPI_SSB_0: case BP_ENET_CONFIG_SPI_SSB_1: case BP_ENET_CONFIG_SPI_SSB_2: case BP_ENET_CONFIG_SPI_SSB_3: access_type = SPI_BUS; #if !defined(_BCM96328_) clkSave = SPI->spiClkCfg & SPI_CLK_MASK; xprintf("spiClkCfg = %x; clkSave = %d \n", SPI->spiClkCfg, clkSave); SPI->spiClkCfg = (SPI->spiClkCfg & ~SPI_CLK_MASK) | clk781k; #endif break; case BP_ENET_CONFIG_MDIO_PSEUDO_PHY: access_type = MDIO_BUS; break; default: xprintf("Unknown PHY configuration type\n"); return -1; } ethsw_rreg_ext(access_type, PAGE_MANAGEMENT, REG_DEVICE_ID, (uint8 *)&data32, sizeof(data32)); xprintf("External switch id = %x \n", data32); if (data32 == 0x53115) { /* setup Switch MII1 port state override */ data8 = (REG_CONTROL_MPSO_MII_SW_OVERRIDE | REG_CONTROL_MPSO_SPEED1000 | REG_CONTROL_MPSO_FDX | REG_CONTROL_MPSO_LINKPASS); ethsw_wreg_ext(access_type, PAGE_CONTROL, REG_CONTROL_MII1_PORT_STATE_OVERRIDE, &data8, sizeof(data8)); /* management mode, enable forwarding */ data8 = REG_SWITCH_MODE_FRAME_MANAGE_MODE | REG_SWITCH_MODE_SW_FWDG_EN; ethsw_wreg_ext(access_type, PAGE_CONTROL, REG_SWITCH_MODE, &data8, sizeof(data8)); /* Enable IMP Port */ data8 = ENABLE_MII_PORT; ethsw_wreg_ext(access_type, PAGE_MANAGEMENT, REG_GLOBAL_CONFIG, &data8, sizeof(data8)); /* Disable BRCM Tag for IMP */ data8 = ~REG_BRCM_HDR_ENABLE; ethsw_wreg_ext(access_type, PAGE_MANAGEMENT, REG_BRCM_HDR_CTRL, &data8, sizeof(data8)); /* enable rx bcast, ucast and mcast of imp port */ data8 = (REG_MII_PORT_CONTROL_RX_UCST_EN | REG_MII_PORT_CONTROL_RX_MCST_EN | REG_MII_PORT_CONTROL_RX_BCST_EN); ethsw_wreg_ext(access_type, PAGE_CONTROL, REG_MII_PORT_CONTROL, &data8, sizeof(data8)); } #if !defined(_BCM96328_) if (access_type == SPI_BUS) SPI->spiClkCfg = (SPI->spiClkCfg & ~SPI_CLK_MASK) | clkSave; #endif return 0; } void robosw_init(void) { int port; BpGetEthernetMacInfo(EnetInfo, BP_MAX_ENET_MACS); // Power up and reset EPHYs GPIO->RoboswEphyCtrl = 0; cfe_usleep(1000); // Take EPHYs out of reset GPIO->RoboswEphyCtrl = EPHY_RST_4 | EPHY_RST_3 | EPHY_RST_2 | EPHY_RST_1; cfe_usleep(1000); #if defined(_BCM96328_) || defined(_BCM96362_) GPIO->RoboswSwitchCtrl |= (RSW_MII_DUMB_FWDG_EN | RSW_HW_FWDG_EN); #endif #if defined(_BCM96368_) || defined(_BCM96816_) GPIO->RoboswEphyCtrl |= (RSW_MII_DUMB_FWDG_EN | RSW_HW_FWDG_EN); #endif // Enable Switch clock PERF->blkEnables |= ROBOSW_CLK_EN; #if defined(_BCM96368_) PERF->blkEnables |= SWPKT_SAR_CLK_EN | SWPKT_USB_CLK_EN; #endif #if defined(_BCM96816_) PERF->blkEnables |= SWPKT_GPON_CLK_EN | SWPKT_USB_CLK_EN; #endif cfe_usleep(1000); PERF->softResetB &= ~SOFT_RST_SWITCH; cfe_usleep(1000); PERF->softResetB |= SOFT_RST_SWITCH; cfe_usleep(1000); /* Disable Rx and Tx on all Ethernet Ports */ for (port = 0; port < EPHY_PORTS; port++) { SWITCH->PortCtrl[port] = 0x03; } if (EnetInfo[1].ucPhyType == BP_ENET_EXTERNAL_SWITCH) { ext_switch_init(); } } void robosw_configure_ports() { uint16 data; int i; for (i = 0; i < 6; i++) { if ((EnetInfo[0].sw.port_map & (1 << i)) != 0) { if (EnetInfo[0].sw.phy_id[i] == 0xff) { #if defined(_BCM96368_) if (i == 4) GPIO->GPIOBaseMode |= (EN_GMII1); if (i == 5) GPIO->GPIOBaseMode |= (EN_GMII2); #endif continue; } if (IsWanPort(EnetInfo[0].sw.phy_id[i])) { *(SWITCH_PBVLAN + i) = PBMAP_MIPS; SWITCH->DisableLearn |= (1 << i); } // Reset mii_write(EnetInfo[0].sw.phy_id[i], MII_BMCR, BMCR_RESET); PortLinkState[i] = 0; if (!IsExtPhyId(EnetInfo[0].sw.phy_id[i])) { // Configure PHY link/act LED #if defined(_BCM96368_) // Enable status change notification */ mii_write(EnetInfo[0].sw.phy_id[i], MII_INTERRUPT, MII_INTR_ENABLE); // Configure LEDs data = mii_read(EnetInfo[0].sw.phy_id[i], MII_RESERVED_1B); mii_write(EnetInfo[0].sw.phy_id[i], MII_RESERVED_1B, data | MII_RESERVED_1B_ACT_LED); #elif defined(_BCM96816_) // Configure LEDs mii_write(EnetInfo[0].sw.phy_id[i], 0x1c, 0xa410); #elif defined(_BCM96328_) || defined(_BCM96362_) // Configure LEDs // Enable Shadow register 2 data = mii_read(EnetInfo[0].sw.phy_id[i], MII_BRCM_TEST); mii_write(EnetInfo[0].sw.phy_id[i], MII_BRCM_TEST, (data | MII_BRCM_TEST_SHADOW2_ENABLE)); // Set LED0 to speed. Set LED1 to blinky link mii_write(EnetInfo[0].sw.phy_id[i], 0x15, 0x71); // Disable Shadow register 2 mii_write(EnetInfo[0].sw.phy_id[i], MII_BRCM_TEST, (data & ~MII_BRCM_TEST_SHADOW2_ENABLE)); #endif } else { #if defined(_BCM96368_) if (i == 4) GPIO->GPIOBaseMode |= (EN_GMII1); if (i == 5) GPIO->GPIOBaseMode |= (EN_GMII2); #endif #if defined(_BCM96816_) if (i == 2) GPIO->GPIOBaseMode |= (EN_GMII1); if (i == 3) GPIO->GPIOBaseMode |= (EN_GMII2); #endif #if defined(_BCM96362_) if (i == 4) GPIO->RoboswSwitchCtrl |= (RSW_MII_SEL_2P5V << RSW_MII_SEL_SHIFT); if (i == 5) GPIO->RoboswSwitchCtrl |= (RSW_MII_2_IFC_EN | (RSW_MII_SEL_2P5V << RSW_MII_2_SEL_SHIFT)); #endif #if defined(_BCM96328_) if (i == 4) MISC->miscPadCtrlHigh |= (MISC_MII_SEL_2P5V << MISC_MII_SEL_SHIFT); #endif // Reset mii_write(EnetInfo[0].sw.phy_id[i], MII_BMCR, BMCR_RESET); // Enable auto-negotiation mii_write(EnetInfo[0].sw.phy_id[i], MII_ANAR, ANAR_TXFD | ANAR_TXHD | ANAR_10FD | ANAR_10HD | PSB_802_3); // Configure LED for link/activity data = MII_1C_SHADOW_LED_CONTROL << MII_1C_SHADOW_REG_SEL_S; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); data = mii_read(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C); data |= ACT_LINK_LED_ENABLE; data |= MII_1C_WRITE_ENABLE; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); data = mii_read(EnetInfo[0].sw.phy_id[i], MII_PHYIDR2); if ((data & BCM_PHYID_M) == (BCM54610_PHYID2 & BCM_PHYID_M)) { // Configure RGMII timing for 54610 GPHY data = MII_1C_SHADOW_CLK_ALIGN_CTRL << MII_1C_SHADOW_REG_SEL_S; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); data = mii_read(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C); data &= (~GTXCLK_DELAY_BYPASS_DISABLE); data |= MII_1C_WRITE_ENABLE; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); // Configure LOM LED Mode data = MII_1C_EXTERNAL_CONTROL_1 << MII_1C_SHADOW_REG_SEL_S; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); data = mii_read(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C); data |= LOM_LED_MODE; data |= MII_1C_WRITE_ENABLE; mii_write(EnetInfo[0].sw.phy_id[i], MII_REGISTER_1C, data); } } // Restart auto-negotiation data = mii_read(EnetInfo[0].sw.phy_id[i], MII_BMCR); mii_write(EnetInfo[0].sw.phy_id[i], MII_BMCR, data | BMCR_RESTARTAN); } } #if defined(_BCM96816_) // Disable SERDES, MoCA, USB and GPON port SWITCH->PortOverride[SERDES_PORT_ID] = PortOverride_Enable; SWITCH->PortOverride[MOCA_PORT_ID] = PortOverride_Enable; SWITCH->PortOverride[USB_PORT_ID] = PortOverride_Enable; SWITCH->PortOverride[GPON_PORT_ID] = PortOverride_Enable; #endif #if defined(_BCM96328_) || defined(_BCM96362_) // Ports 6 and 7 are not used on 6328 and 6362 SWITCH->PortOverride[PORT_6_PORT_ID] = PortOverride_Enable; SWITCH->PortOverride[PORT_7_PORT_ID] = PortOverride_Enable; #endif #if defined(_BCM96368_) // Disable SAR and USB port SWITCH->PortOverride[USB_PORT_ID] = PortOverride_Enable; SWITCH->PortOverride[SAR_PORT_ID] = PortOverride_Enable; #endif // Enable the GMII clocks. SWITCH->ImpRgmiiCtrlP4 |= ImpRgmiiCtrl_GMII_En; /* RGMII Delay Programming. Enable ID mode */ SWITCH->ImpRgmiiCtrlP4 |= ImpRgmiiCtrl_Timing_Sel; #if !defined(_BCM96328_) SWITCH->ImpRgmiiCtrlP5 |= ImpRgmiiCtrl_GMII_En; SWITCH->ImpRgmiiCtrlP5 |= ImpRgmiiCtrl_Timing_Sel; #endif // Reset MIB counters SWITCH->GlbMgmt = GlbMgmt_ResetMib; cfe_usleep(100); SWITCH->GlbMgmt = 0; SWITCH->ImpOverride |= ImpOverride_Force | ImpOverride_Linkup; } void robosw_check_ports() { uint16 data; uint8 PortOverride; int i; for (i = 0; i < EPHY_PORTS; i++) { if ((EnetInfo[0].sw.port_map & (1 << i)) != 0) { if (EnetInfo[0].sw.phy_id[i] == 0xff) { PortOverride = PortOverride_Enable | PortOverride_1000Mbs | PortOverride_Fdx | PortOverride_Linkup; if ((SWITCH->PortOverride[i] & PortOverride) != PortOverride) { SWITCH->PortOverride[i] = PortOverride; SWITCH->PortCtrl[i] = 0; PortLinkState[i] = 1; } continue; } PortOverride = PortOverride_Enable; data = mii_read(EnetInfo[0].sw.phy_id[i], MII_ASR); if (PortLinkState[i] != MII_ASR_LINK(data)) { if (MII_ASR_LINK(data)) PortOverride |= PortOverride_Linkup; if (MII_ASR_DONE(data)) { if (MII_ASR_FDX(data)) PortOverride |= PortOverride_Fdx; if (MII_ASR_1000(data)) PortOverride |= PortOverride_1000Mbs; else if (MII_ASR_100(data)) PortOverride |= PortOverride_100Mbs; else PortOverride |= PortOverride_10Mbs; } SWITCH->PortOverride[i] = PortOverride; if(PortOverride & PortOverride_Linkup) { /* Enable Rx and Tx */ SWITCH->PortCtrl[i] = 0; } PortLinkState[i] = MII_ASR_LINK(data); } } } }