| /* |
| * This file contains GBE related definitions |
| */ |
| #ifndef PRISM_GBE_H |
| #define PRISM_GBE_H |
| |
| #include <config.h> |
| #include <common.h> |
| #include <command.h> |
| #include <malloc.h> |
| #include <pci.h> |
| #include <net.h> |
| |
| #include "mvTypes.h" |
| #include "mvCtrlEnvLib.h" |
| #include "boardEnv/mvBoardEnvLib.h" |
| |
| #if defined(MV_INCLUDE_UNM_ETH) || defined(MV_INCLUDE_GIG_ETH) |
| #include "eth-phy/mvEthPhy.h" |
| #endif |
| #if defined(CONFIG_MV_ETH_LEGACY) |
| #include "eth/mvEth.h" |
| #include "eth/gbe/mvEthDebug.h" |
| #else |
| #include "neta/gbe/mvNeta.h" |
| #endif /* CONFIG_MV_ETH_LEGACY */ |
| |
| #if defined(GBE_CMD_DBG) |
| #if defined(PRISM_DBG) |
| #undef PRISM_DBG |
| #endif |
| #define PRISM_DBG(format, args...) \ |
| printf("%s: "format"\n", __func__, ## args) |
| #elif !defined(PRISM_DBG) |
| #define PRISM_DBG(format, args...) |
| #endif |
| |
| #if (defined(CONFIG_POST) && defined(CONFIG_SYS_POST_ETHER)) |
| #define GBE_LOG(format, args...) |
| #define GBE_ERR_LOG(format, args...) \ |
| post_log("GE: "format"\n", ## args) |
| #else |
| #define GBE_LOG(format, args...) printf("GE: "format"\n", ## args) |
| #define GBE_ERR_LOG GBE_LOG |
| #endif /* end of CONFIG_POST & CONFIG_SYS_POST_ETHER */ |
| |
| #define GBE_LPBK_TIMEOUT_INTERVAL 5 /* 5 msec */ |
| #define GBE_LPBK_TIMEOUT_LOOPS 30 |
| |
| #define MAX_LPBK_PACKETS 3 |
| #define MAX_PACKET_LENGTH 1510 /* Exclude CRC field */ |
| static uchar *prism_tx_buf = NULL; |
| static uchar *prism_rx_buf = NULL; |
| |
| static int prism_tx_len, prism_rx_len; |
| |
| #define GBE_DIAG_CONTINUE 1 |
| #define GBE_DIAG_SUCCESS 2 |
| #define GBE_DIAG_FAIL 3 |
| static int gbe_res = GBE_DIAG_CONTINUE; |
| |
| #define GBE_ETH_REG(reg_addr, reg_name) \ |
| PRISM_DBG("%-32s: 0x%x = 0x%08x", reg_name, reg_addr, MV_REG_READ(reg_addr)) |
| |
| static void prism_dbgPrintEthRegs(int port) |
| { |
| #if defined(GBE_CMD_DUMP_REGS) |
| GBE_ETH_REG(ETH_PORT_STATUS_REG(port), "ETH_PORT_STATUS_REG"); |
| GBE_ETH_REG(ETH_PORT_SERIAL_CTRL_REG(port), "ETH_PORT_SERIAL_CTRL_REG"); |
| GBE_ETH_REG(NETA_GMAC_CTRL_0_REG(port), "NETA_GMAC_CTRL_0_REG"); |
| GBE_ETH_REG(NETA_GMAC_CTRL_1_REG(port), "NETA_GMAC_CTRL_1_REG"); |
| GBE_ETH_REG(NETA_GMAC_CTRL_2_REG(port), "NETA_GMAC_CTRL_2_REG"); |
| GBE_ETH_REG(NETA_GMAC_AN_CTRL_REG(port), "NETA_GMAC_AN_CTRL_REG"); |
| GBE_ETH_REG(NETA_GMAC_STATUS_REG(port), "NETA_GMAC_STATUS_REG"); |
| GBE_ETH_REG(NETA_GMAC_SERIAL_REG(port), "NETA_GMAC_SERIAL_REG"); |
| GBE_ETH_REG(NETA_GMAC_FIFO_PARAM_0_REG(port), "NETA_GMAC_FIFO_PARAM_0_REG"); |
| GBE_ETH_REG(NETA_GMAC_FIFO_PARAM_1_REG(port), "NETA_GMAC_FIFO_PARAM_1_REG"); |
| GBE_ETH_REG(NETA_GMAC_CAUSE_REG(port), "NETA_GMAC_CAUSE_REG"); |
| GBE_ETH_REG(NETA_GMAC_MASK_REG(port), "NETA_GMAC_MASK_REG"); |
| GBE_ETH_REG(NETA_GMAC_MIB_CTRL_REG(port), "NETA_GMAC_MIB_CTRL_REG"); |
| GBE_ETH_REG(0x188a0, "NETA_GBE_PHY_CTRL_0_REG"); |
| GBE_ETH_REG(0x188a4, "NETA_GBE_PHY_CTRL_2_REG"); |
| GBE_ETH_REG(0x188a8, "NETA_GBE_PHY_STATUS_REG"); |
| #endif |
| } |
| |
| #define NAME_MAX_SIZE (NAMESIZE + 1) |
| static struct eth_device *gbe_ether_init(int port) |
| { |
| struct eth_device *dev = NULL; |
| MV_8 name[NAME_MAX_SIZE]; |
| bd_t *bd = gd->bd; |
| |
| sprintf(name, "egiga%d", port); |
| dev = eth_get_dev_by_name(name); |
| if (dev == NULL) { |
| GBE_ERR_LOG("%s open failed\n", name); |
| return NULL; |
| } |
| else |
| GBE_LOG("name = %s, enetaddr = %pM, state = 0x%x", |
| dev->name, dev->enetaddr, dev->state); |
| |
| dev->halt(dev); |
| if (!dev->init(dev, bd)) { |
| GBE_ERR_LOG("%s init failed\n", name); |
| return NULL; |
| } |
| |
| return dev; |
| } |
| |
| static int gbe_set_force_up_link(int port, MV_BOOL *isChanged) |
| { |
| int res = 0; |
| MV_U32 value; |
| |
| do { |
| *isChanged = MV_FALSE; /* default "force link up" is not changed */ |
| |
| if (mvNetaLinkIsUp(port) == MV_TRUE) |
| break; |
| |
| /* Check if the bits are set */ |
| value = MV_REG_READ(NETA_GMAC_AN_CTRL_REG(port)); |
| if (value & NETA_FORCE_LINK_PASS_MASK) { |
| break; /* The bits are set. exit */ |
| } |
| |
| /* Set "force link up" bit */ |
| if ((res = mvNetaForceLinkModeSet(port, 1, 0)) != 0) { |
| PRISM_DBG("mvNetaForceLinkModeSet() failed"); |
| break; |
| } |
| *isChanged = MV_TRUE; /* The "force link up" is changed */ |
| } while (0); |
| |
| return res; |
| } |
| |
| static int gbe_clear_force_up_link(int port, MV_BOOL isChanged) |
| { |
| int res = 0; |
| |
| if (isChanged == MV_TRUE) { |
| /* Clear force link up */ |
| if ((res = mvNetaForceLinkModeSet(port, 0, 0)) != 0) |
| PRISM_DBG("mvNetaForceLinkModeSet() failed"); |
| } |
| return res; |
| } |
| |
| |
| static int mvNetaGmiiLoopback(int port, MV_BOOL isLpbk) |
| { |
| MV_U32 value; |
| int res = 1; /* default fail */ |
| |
| if ((res = mvNetaPortCheck(port)) != 0) |
| return res; |
| |
| value = MV_REG_READ(NETA_GMAC_CTRL_1_REG(port)); |
| if (isLpbk == MV_TRUE) { |
| /* Enable GMII loopback */ |
| value |= NETA_GMAC_LOOPBACK_EN_MASK; |
| } else { |
| /* Disable GMII loopback */ |
| value &= ~NETA_GMAC_LOOPBACK_EN_MASK; |
| } |
| MV_REG_WRITE(NETA_GMAC_CTRL_1_REG(port), value); |
| |
| return res; |
| } |
| |
| /* isLpbk: 1 - loopback mode; 0 - normal */ |
| static int gbe_set_loopback_mode(struct eth_device *dev, int port, MV_BOOL isLpbk) |
| { |
| int res = 1; /* default fail */ |
| MV_U32 value; |
| MV_ETH_PORT_SPEED config_speed; |
| MV_ETH_PORT_DUPLEX config_duplex; |
| MV_ETH_PORT_FC config_fc; |
| |
| do { |
| /* Disable Port */ |
| if ((res = mvNetaPortDisable(port)) != 0) { |
| PRISM_DBG("mvNetaPortDisable() failed"); |
| break; |
| } |
| |
| /* Force to link down */ |
| if ((res = mvNetaForceLinkModeSet(port, 0, 1)) != 0) { |
| PRISM_DBG("mvNetaForceLinkModeSet() failed"); |
| break; |
| } |
| |
| if (isLpbk == MV_TRUE) { |
| /* Per Marvell, |
| * - Disable AutoNeg(Speed/FC/Duplex): Address 0x72C0C bit 7,11,13 (0x0) |
| * - Force FC Disable: Address 0x72C0C bit 8 (0x0) |
| * - Force Full Duplex: Address 0x72C0C bit 12 (0x1) |
| */ |
| config_speed = MV_ETH_SPEED_1000; |
| config_duplex = MV_ETH_DUPLEX_FULL; |
| config_fc = MV_ETH_FC_DISABLE; |
| } else { |
| /* Set to normal mode */ |
| config_speed = MV_ETH_SPEED_AN; |
| config_duplex = MV_ETH_DUPLEX_AN; |
| config_fc = MV_ETH_FC_AN_SYM; |
| } |
| |
| if ((res = mvNetaSpeedDuplexSet(port, config_speed, config_duplex)) != 0) { |
| PRISM_DBG("mvNetaForceLinkModeSet() failed"); |
| break; |
| } |
| |
| if ((res = mvNetaFlowCtrlSet(port, config_fc)) != 0) { |
| PRISM_DBG("mvNetaFlowCtrlSet() failed"); |
| break; |
| } |
| |
| prism_dbgPrintEthRegs(port); |
| |
| if ((res = mvNetaGmiiLoopback(port, isLpbk)) != 0) { |
| PRISM_DBG("mvNetaGmiiLoopback() failed"); |
| break; |
| } |
| |
| /* Force link up */ |
| if ((res = mvNetaForceLinkModeSet(port, 1, 0)) != 0) { |
| PRISM_DBG("mvNetaForceLinkModeSet() failed"); |
| break; |
| } |
| |
| if ((res = mvNetaPortEnable(port)) != 0) { |
| PRISM_DBG("mvNetaPortEnable() failed"); |
| break; |
| } |
| |
| PRISM_DBG ("looplback %s.....", (isLpbk == MV_TRUE)? "enabled": "disabled"); |
| prism_dbgPrintEthRegs(port); |
| |
| value = MV_REG_READ(NETA_GMAC_STATUS_REG(port)); |
| if (!(value & NETA_GMAC_LINK_UP_MASK)) { |
| GBE_ERR_LOG("egiga%d link is down (value=0x%x)", port, value); |
| res = 1; /* fail */ |
| } |
| } while (0); |
| |
| return res; |
| } |
| |
| static void gbe_dbg_dump_packet(uchar *packet, int len, int off) |
| { |
| #ifdef ETH_DEBUG_DUMP |
| int i; |
| |
| printf("packet data(len = %d, off=%d)\n", len, off); |
| for (i = off; i < len; i++) { |
| if (i % 16 == 0) |
| printf("\n"); |
| printf("%02x ", packet[i]); |
| } |
| printf("\n"); |
| #endif |
| } |
| |
| #define GBE_LPKB_PKT_HDR_LEN (ETHER_HDR_SIZE + IP_HDR_SIZE) |
| static int gbe_packet_check(void) |
| { |
| int res = 1; |
| int i; |
| MV_BOOL isMiscompare = MV_FALSE; |
| |
| PRISM_DBG("%s is called", __func__); |
| |
| do { |
| if (prism_tx_len != prism_rx_len) { |
| GBE_ERR_LOG("mismatch packet lengths: prism_tx_len=%d, prism_rx_len=%d", |
| prism_tx_len, prism_rx_len); |
| break; |
| } |
| |
| for (i = GBE_LPKB_PKT_HDR_LEN; i < prism_tx_len; i++) { |
| if (prism_tx_buf[i] != prism_rx_buf[i]) { |
| isMiscompare = MV_TRUE; |
| GBE_ERR_LOG("mismatch: off=0x%x, expected data=0x%02x, recv_data=0x%02x", |
| i, prism_tx_buf[i], prism_rx_buf[i]); |
| break; |
| } |
| } |
| |
| if (isMiscompare == MV_FALSE) |
| res = 0; |
| } while (0); |
| |
| if (res != 0) |
| GBE_ERR_LOG("rx/tx data miscompare error"); |
| |
| return res; |
| } |
| |
| static void gbe_recv_packet(uchar *packet, unsigned dest, unsigned src, unsigned len) |
| { |
| int i, j; |
| |
| PRISM_DBG("%s is called (len=%d)", __func__, len); |
| gbe_dbg_dump_packet(packet, len, 0); |
| |
| /* Note - |
| * Due to the loopback packet is an IP frame, |
| * 1) the input packet len is IP payload data only |
| * len = prism_tx_len - ETHER_HDR_SIZE - IP_HDR_SIZE |
| * 2) In prism_rx_len, there is no eth and IP header data |
| * Copy data in "packet" to starting offset of payload in prism_rx_buf |
| */ |
| if ((len + GBE_LPKB_PKT_HDR_LEN) <= MAX_PACKET_LENGTH) { |
| prism_rx_len = len + GBE_LPKB_PKT_HDR_LEN; |
| |
| for (i = 0, j = GBE_LPKB_PKT_HDR_LEN; i < len; i++, j++) { |
| prism_rx_buf[j] = packet[i]; |
| } |
| |
| gbe_dbg_dump_packet(prism_rx_buf, prism_rx_len, GBE_LPKB_PKT_HDR_LEN); |
| gbe_res = GBE_DIAG_SUCCESS; |
| } else { |
| GBE_ERR_LOG("failed to reveice data(%d)", len); |
| gbe_res = GBE_DIAG_FAIL; |
| } |
| } |
| |
| /* Unused port numbers for loopback test */ |
| #define PORT_UNUSED_S 48222 /* Unused port number */ |
| #define PORT_UNUSED_C 48223 /* Unused port number */ |
| #define MAC_ADDR_LEN 6 |
| static int gbe_send_packet(struct eth_device *dev, int pkt_len) |
| { |
| int i; |
| int rand_data = (int)get_timer(0); |
| int payload_len; |
| |
| gbe_res = GBE_DIAG_CONTINUE; |
| prism_tx_len = pkt_len; |
| prism_rx_len = 0; |
| |
| /* Set up ethernet header */ |
| /* To minimize the modification, setup the packet to |
| * be an IP protocol frame which is known by NetLoop() |
| */ |
| memcpy(NetOurEther, dev->enetaddr, MAC_ADDR_LEN); |
| i = NetSetEther(prism_tx_buf, NetBcastAddr, PROT_IP); |
| payload_len = prism_tx_len - i - IP_HDR_SIZE; |
| NetSetIP((prism_tx_buf + i), 0xFFFFFFFFL, |
| PORT_UNUSED_S, PORT_UNUSED_C, payload_len); |
| |
| i += IP_HDR_SIZE; |
| for ( ; i < prism_tx_len; i++) { |
| if (i >= ETHER_HDR_SIZE) |
| prism_tx_buf[i] = (char)(rand_data + i); |
| } |
| |
| gbe_dbg_dump_packet(prism_tx_buf, prism_tx_len, 0); |
| NetSetHandler(gbe_recv_packet); |
| |
| return dev->send(dev, prism_tx_buf, prism_tx_len); |
| } |
| |
| static int gbe_loopback_init(struct eth_device **dev, int port) |
| { |
| int res = 1; |
| |
| do { |
| gbe_res = GBE_DIAG_CONTINUE; |
| |
| *dev = gbe_ether_init(port); |
| if (*dev == NULL) |
| break; |
| |
| prism_tx_buf = malloc(MAX_PACKET_LENGTH); |
| prism_rx_buf = malloc(MAX_PACKET_LENGTH); |
| if (!prism_tx_buf || !prism_rx_buf) { |
| GBE_ERR_LOG("Failed to allocate packet buffers\n"); |
| break; |
| } |
| |
| res = 0; |
| } while (0); |
| |
| return res; |
| } |
| |
| static void gbe_loopback_uninit(struct eth_device *dev, int port, MV_BOOL isLpbk) |
| { |
| if (dev != NULL) { |
| if (isLpbk == MV_TRUE) { |
| /* set to normal mode */ |
| gbe_set_loopback_mode(dev, port, MV_FALSE); |
| } |
| dev->halt(dev); |
| } |
| |
| if (prism_tx_buf != NULL) { |
| free(prism_tx_buf); |
| prism_tx_buf = NULL; |
| } |
| |
| if (prism_rx_buf != NULL) { |
| free(prism_rx_buf); |
| prism_rx_buf = NULL; |
| } |
| |
| /* Set the routine ptr to NULL */ |
| NetSetHandler(NULL); |
| |
| return; |
| } |
| |
| static int gbe_loopback_test(int port, MV_BOOL isLpbk) |
| { |
| struct eth_device *dev; |
| int i, j, res = 1; |
| MV_BOOL isForceLinkUpChanged = MV_FALSE; |
| int pkt_lens[MAX_LPBK_PACKETS] = {60, 507, 1510}; /* not include CRC len */ |
| |
| do { |
| PRISM_DBG("--> normal mode \n"); |
| prism_dbgPrintEthRegs(port); |
| |
| if (isLpbk == MV_TRUE) { |
| /* For gbe loopback diag command, we would like to run w/o link also. |
| * Let's set to "force link up" |
| */ |
| if ((res = gbe_set_force_up_link(port, &isForceLinkUpChanged)) != 0) { |
| break; |
| } |
| } |
| |
| if ((res = gbe_loopback_init(&dev, port)) != 0) |
| break; |
| |
| if (isLpbk == MV_TRUE) { |
| if ((res = gbe_set_loopback_mode(dev, port, MV_TRUE)) != 0) |
| break; /* failed. exit */ |
| } else { |
| /* test receive only */ |
| NetSetHandler(gbe_recv_packet); |
| } |
| |
| for (j = 0; j < MAX_LPBK_PACKETS; j++) { |
| if (isLpbk == MV_TRUE) { |
| /* send test packet */ |
| if ((res = gbe_send_packet(dev, pkt_lens[j])) != 0) { |
| GBE_ERR_LOG("egiga%d test failed\n", port); |
| break; /* failed. exit */ |
| } |
| } |
| |
| /* Wait until either timeout or packet received */ |
| for (i = GBE_LPBK_TIMEOUT_LOOPS; |
| (i > 0) && (gbe_res == GBE_DIAG_CONTINUE); i--) { |
| /* |
| * Check the ethernet for a new packet. If a packet is received, |
| * the gbe_recv_packet() will be invoked. |
| */ |
| dev->recv(dev); |
| mvOsSleep(GBE_LPBK_TIMEOUT_INTERVAL); /* delay for 5ms */ |
| } |
| |
| if (gbe_res == GBE_DIAG_CONTINUE) { |
| GBE_ERR_LOG("failed to receive timed out (gbe_res=%d)\n", gbe_res); |
| res = 1; |
| break; /* failed. exit */ |
| } |
| |
| if ((gbe_res == GBE_DIAG_SUCCESS) && (isLpbk == MV_TRUE)) { |
| res = gbe_packet_check(); |
| } |
| } /* end of for(j) */ |
| |
| } while (0); |
| |
| gbe_loopback_uninit(dev, port, isLpbk); |
| gbe_clear_force_up_link(port, isForceLinkUpChanged); |
| |
| return res; |
| } |
| |
| #endif /* end of ifndef PRISM_GBE_H */ |
| |