/* * drivers/netspcmac_eth.c * * Copyright (C) 2009 SPiDCOM Technologies * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Author(s): * Drasko DRASKOVIC */ #include #ifdef CONFIG_DRIVER_NETSPCMAC #include #include #include #include #include #include "netspcmac_eth.h" #ifdef CONFIG_ARCH_SPC300 #include #endif #ifdef CONFIG_ARCH_ARIZONA #define FORCE_10_HD 1 /* force 10MB Half Duplex */ #endif #define PHY_HW_RESET 1 /* PHY alwasy does HW reset */ #define LINK_TIMEOUT (2 * CFG_HZ) /* 2 sec waiting on LINK_UP after PHY setup */ #define PHYSADDR(a) (((unsigned long)(a)) & 0xffffffff) #if (CONFIG_COMMANDS & CFG_CMD_NET) //#define DEBUG_MAC #ifdef DEBUG_MAC #define PRINTK(args...) printf(args) #else #define PRINTK(args...) #endif extern void flush_dcache (void); extern void flush_caches (void); extern void dcache_disable (void); static int dcache_switch = 0; /* This structure is common for both receive and transmit DMA descriptors. * A descriptor should not be used for storing more than one frame. */ struct spcmac_dma_des_t { unsigned int des0; /* Status */ unsigned int des1; /* Ctrl bits, Buffer 2 length, Buffer 1 length */ void *des2; /* Buffer 1 Address Pointer */ void *des3; /* Buffer 2 Address Pointer or the next Descriptor */ }; typedef struct spcmac_dma_des_t spcmac_dma_des; /* Use single dma descriptors */ #define CONFIG_DMA_RX_SIZE 32 #define CONFIG_DMA_TX_SIZE 32 static volatile spcmac_dma_des *dma_tx; static volatile spcmac_dma_des *dma_rx; static int cur_rx; static int cur_tx; #define MAX_ETH_FRAME_SIZE 1536 #define MAX_PAUSE_TIME (MAC_FLOW_CONTROL_PT_MASK>>MAC_FLOW_CONTROL_PT_SHIFT) static void spcmac_mii_write (int phy_addr, int reg, int value); static unsigned int spcmac_mii_read (int phy_addr, int reg); static int mii_read (char *devname, unsigned char addr, unsigned char reg, unsigned short *value); static int mii_write (char *devname, unsigned char addr, unsigned char reg, unsigned short value); void spcmac_set_mac_mii_cap (int full_duplex, unsigned int speed); /* DMA structure */ struct dma_t { //uchar _dummy1[L1_CACHE_BYTES]; spcmac_dma_des desc_rx[CONFIG_DMA_RX_SIZE]; spcmac_dma_des desc_tx[CONFIG_DMA_TX_SIZE]; uchar rx_buff[CONFIG_DMA_RX_SIZE * (PKTSIZE_ALIGN)]; //uchar _dummy2[L1_CACHE_BYTES]; } dma; static uchar *rx_packets[CONFIG_DMA_RX_SIZE]; /* ---------------------------------------------------------------------------- Phy interface ---------------------------------------------------------------------------*/ struct phy_device { int addr; unsigned int id; int (*init) (const struct phy_device *phy_dev, int lp_an_able); int (*is_link_up) (const struct phy_device *phy_dev); int (*reset) (const struct phy_device *phy_dev); }; static inline int spcmac_phy_wait_link_up (const struct phy_device *phy_dev) { ulong now = 0; now = get_timer (0); while (get_timer (now) < LINK_TIMEOUT) { if (phy_dev->is_link_up (phy_dev)) return 0; } return -1; } #ifndef FORCE_10_HD static int spcmac_phy_negotiate (int phy_addr) { uint now, tmp, status; status = 0; tmp = spcmac_mii_read (phy_addr, MII_BMCR); tmp |= (BMCR_ANENABLE | BMCR_ANRESTART); spcmac_mii_write (phy_addr, MII_BMCR, tmp); now = get_timer (0); while (get_timer (now) < CONFIG_SPCMAC_AUTONEG_TIMEOUT) { status = spcmac_mii_read (phy_addr, MII_BMSR); if (status & BMSR_ANEGCOMPLETE) { break; } /* Restart auto-negotiation if remote fault */ if (status & BMSR_RFAULT) { printf ("PHY remote fault detected\n"); /* Restart auto-negotiation */ printf ("PHY restarting auto-negotiation\n"); spcmac_mii_write (phy_addr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); } } if (!(status & BMSR_ANEGCOMPLETE)) { printf ("PHY auto-negotiate timed out\n"); } if (status & BMSR_RFAULT) { printf ("PHY remote fault detected\n"); } return (1); } static unsigned int spcmac_phy_check_speed (const struct phy_device *phy_dev) { int full_duplex = 0; int speed = 0; printf ("SPCMAC: "); if (spcmac_phy_wait_link_up (phy_dev)) { printf ("*Warning* no link detected\n"); return 1; } else { int status = spcmac_mii_read (phy_dev->addr, MII_BMSR); int giga = spcmac_mii_read (phy_dev->addr, MII_EMSR); if ((status & BMSR_EXT) && ((giga & EMSR_1000FULL) || (giga & EMSR_1000HALF))) { /* PHY can manage GIGA bits */ int negotiated = spcmac_mii_read (phy_dev->addr, MII_MSR_1000); if (negotiated & LPA_1000FULL) { printf ("1000Mbs full duplex link detected\n"); full_duplex = 1; speed = 1000; } else if (negotiated & LPA_1000HALF) { printf ("1000Mbs half duplex link detected\n"); full_duplex = 0; speed = 1000; } } if (speed == 0) { /* No GIGA Bits detected */ int negotiated = spcmac_mii_read (phy_dev->addr, MII_LPA); if (negotiated & LPA_100FULL) { printf ("100Mbs full duplex link detected\n"); full_duplex = 1; speed = 100; } else if (negotiated & LPA_100HALF) { printf ("100Mbs half duplex link detected\n"); full_duplex = 0; speed = 100; } else if (negotiated & LPA_10FULL) { printf ("10Mbs full duplex link detected\n"); full_duplex = 1; speed = 10; } else { printf ("10Mbs half duplex link detected\n"); full_duplex = 0; speed = 10; } } } spcmac_set_mac_mii_cap (full_duplex, speed); return 0; } #endif static int spcmac_phy_gen_init (const struct phy_device *phy_dev, int lp_an_able) { uint advertised_caps, value; #ifndef FORCE_10_HD #if 0 #error "We need a mechanism to check if auto-neg is allowed or not" if (lp_an_able == 0) { /* if we are not doing autoneg, leave with default reset values * for speed and duplex */ return 0; } #endif #endif /* Read the ANE Advertisement register */ advertised_caps = spcmac_mii_read (phy_dev->addr, MII_ADVERTISE); /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */ value = spcmac_mii_read (phy_dev->addr, MII_BMSR); /* Set the advertised capabilities */ if (value & BMSR_100BASE4) advertised_caps |= ADVERTISE_100BASE4; if (value & BMSR_100FULL) advertised_caps |= ADVERTISE_100FULL; if (value & BMSR_100HALF) advertised_caps |= ADVERTISE_100HALF; if (value & BMSR_10FULL) advertised_caps |= ADVERTISE_10FULL; if (value & BMSR_10HALF) advertised_caps |= ADVERTISE_10HALF; #ifdef CONFIG_SPCMAC_FLOWCTRL advertised_caps |= MII_ADVERTISE_PAUSE; #else advertised_caps &= ~MII_ADVERTISE_PAUSE; #endif /* Update our Auto-Neg Advertisement Register */ spcmac_mii_write (phy_dev->addr, MII_ADVERTISE, advertised_caps); /* Check Giga bits capabilities */ if (value & BMSR_EXT) { advertised_caps = spcmac_mii_read (phy_dev->addr, MII_MCR_1000); #ifdef CONFIG_CHIP_FEATURE_SYNOP3504_NO_GIGABIT /* The Dini prototype does not support 1000-BaseT. */ advertised_caps &= ~ADVERTISE_1000FULL; advertised_caps &= ~ADVERTISE_1000HALF; #else uint extvalue = spcmac_mii_read (phy_dev->addr, MII_EMSR); if (extvalue & EMSR_1000FULL) advertised_caps |= ADVERTISE_1000FULL; if (extvalue & EMSR_1000HALF) advertised_caps |= ADVERTISE_1000HALF; #endif spcmac_mii_write (phy_dev->addr, MII_MCR_1000, advertised_caps); } #ifdef FORCE_10_HD #ifdef PHY_HW_RESET /* * if PHY is always hard reseted, it will start auto-negotiation * automatically. Before stopping it, check if auto-neg has compleated, * in order not to leave switch in undefined state. */ if (lp_an_able) { now = get_timer (0); while (get_timer (now) < CONFIG_SPCMAC_AUTONEG_TIMEOUT) { status = spcmac_mii_read (phy_dev->addr, MII_BMSR); if (status & BMSR_ANEGCOMPLETE) { break; } } if (!(status & BMSR_ANEGCOMPLETE)) { printf ("PHY auto-negotiate timed out\n"); } if (status & BMSR_RFAULT) { printf ("PHY remote fault detected\n"); } } #endif // Disable AUTONEGOTIATION printf ("Disabling Autonegotiation...\n"); value = (unsigned int) spcmac_mii_read(phy_dev->addr, MII_BMCR); value &= ~BMCR_ANENABLE; spcmac_mii_write (phy_dev->addr, MII_BMCR, value); // Force 10Mbs Half Duplex value = (unsigned int) spcmac_mii_read(phy_dev->addr, MII_BMCR); value &= ~(BMCR_FULLDPLX | BMCR_SPEED100); spcmac_mii_write (phy_dev->addr, MII_BMCR, value); printf ("PHY forced to 10MB HALF DUPLEX\n"); // set cap, force 10Mbs HALF DUPLEX spcmac_set_mac_mii_cap (0, 10); // 10 Mbs HD #else /* do autonegotiation */ #ifdef CONFIG_CHIP_FEATURE_NO_MDIO printf ("MAC forced to 100MB Full Duplex\n"); spcmac_set_mac_mii_cap (1, 100); #else spcmac_phy_negotiate (phy_dev->addr); spcmac_phy_check_speed (phy_dev); #endif /* CHIP_FEATURE_NO_MDIO */ #endif /* FORCE_10_HD */ return 0; } static int spcmac_phy_gen_is_link_up (const struct phy_device *phy_dev) { return BMSR_LSTATUS & spcmac_mii_read (phy_dev->addr, MII_BMSR); } static int spcmac_phy_gen_reset (const struct phy_device *phy_dev) { int loop = 100000; int tmp; spcmac_mii_write (phy_dev->addr, MII_BMCR, BMCR_RESET); do { udelay (10); tmp = spcmac_mii_read (phy_dev->addr, MII_BMCR); loop--; } while ((loop > 0) && (tmp & BMCR_RESET)); if (tmp & BMCR_RESET) { printf ("Warning: PHY reset failed to complete.\n"); return 1; } return 0; } static int spcmac_phy_ip1001_reset (const struct phy_device *phy_dev) { int ret; ret = spcmac_phy_gen_reset (phy_dev); if (ret) return ret; /* Fix hardware bug */ DECLARE_GLOBAL_DATA_PTR; spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); if (NVRAM_BFEXT (ETH1_MODE, nvram->pkg_cfg) == NVRAM_ETH_MODE_GMII) spcmac_mii_write (phy_dev->addr, 16, spcmac_mii_read (phy_dev->addr, 16) & ~(2)); return 0; } static int spcmac_phy_ip175_init (const struct phy_device *phy_dev, int lp_an_able) { #ifndef FORCE_10_HD if (phy_dev->addr == 5) { /* we are using a switch IP175 on MAC 5, leave with default reset values * and force MAC5 link to 100Mbs FD */ spcmac_set_mac_mii_cap (1, 100); } return 0; #endif return spcmac_phy_gen_init (phy_dev, lp_an_able); } static int spcmac_phy_ip175_is_link_up (const struct phy_device *phy_dev) { /* This is a switch, check if any of the 4 sub-PHYs has a link up. */ uint phy_addr; for (phy_addr = 0; phy_addr <= 4; phy_addr++) { uint status = spcmac_mii_read (phy_addr, MII_BMSR); if (status & BMSR_LSTATUS) return 1; } return 0; } static int spcmac_phy_ip175_reset (const struct phy_device *phy_dev) { /* check if it is an IP175D */ if (spcmac_mii_read (20, 0) == ICPLUS_ID_IP175D) { printf ("IC+ IP175D found.\n"); /* set MII0 / MAC5 to 100Mbps full-duplex */ spcmac_mii_write (20, 4, spcmac_mii_read(20, 4) | 0xA000); } return spcmac_phy_gen_reset (phy_dev); } static int spcmac_phy_vsc8601_reset (const struct phy_device *phy_dev) { int ret; ret = spcmac_phy_gen_reset (phy_dev); if (ret) return ret; #ifdef CONFIG_CHIP_FEATURE_SYNOP3504_PHY_DINI /* The PHY used on MSE500 DINI is VSC8601. */ /* Add a 2ns delay for TX and RX clock */ /* In register 23 "Extended Phy Control 1", enable "RGMII skew timing * compensation". */ spcmac_mii_write (phy_dev->addr, 23, 0x0100); /* To access the extended register 28E, put 1 in register 31 "Extended * Page Access". */ spcmac_mii_write (phy_dev->addr, 31, 0x0001); /* In register 28E "RGMII Skew Control", set both TX and RX RGMII Skew * compensation to 2ns. */ spcmac_mii_write (phy_dev->addr, 28, 0xf000); /* Switch back to main register space, by putting 0 in register 31 * "Extended Page Access". */ spcmac_mii_write (phy_dev->addr, 31, 0x0000); #endif /* CONFIG_CHIP_FEATURE_SYNOP3504_PHY_DINI */ return 0; } /* Automatically gets and returns the PHY device */ static unsigned int spcmac_phy_get_addr (void) { #ifdef CONFIG_ARCH_SPC300 DECLARE_GLOBAL_DATA_PTR; spidcom_nvram_t *nvram = NULL; //Get PHY address directly from NVRAM nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); return (nvram->eth1_phy_addr > 31 ? 0 : nvram->eth1_phy_addr); #else int i, phyaddr; //PRINTK ("%s\n", __FUNCTION__); uint spcmac_phy_id = 0; for (i = 0; i < 32; i++) { unsigned int phy_id; phyaddr = (i + 1) % 32; phy_id = PHYSID_GET_ID (spcmac_mii_read (phyaddr, MII_PHYSID1), spcmac_mii_read (phyaddr, MII_PHYSID2)); /* Make sure it is a valid identifier */ if (phy_id) { spcmac_phy_id = PHYSID_GET_OUI (phy_id); } if (spcmac_phy_id) return phyaddr; } return (-1); #endif } static unsigned long spcmac_phy_get_id (unsigned long phyaddr) { unsigned long phy_id; //Find PHY identifier phy_id = PHYSID_GET_ID (spcmac_mii_read (phyaddr, MII_PHYSID1), spcmac_mii_read (phyaddr, MII_PHYSID2)); //PHY identifier not found, but it can be a switch IP175 so let's check at //phyaddr 0 if (0 == phy_id) { phy_id = PHYSID_GET_ID (spcmac_mii_read (0, MII_PHYSID1), spcmac_mii_read (0, MII_PHYSID2)); if ((ICPLUS_OUI != PHYSID_GET_OUI (phy_id)) || (ICPLUS_IP175 != PHYSID_GET_MODEL (phy_id))) phy_id = 0; } return phy_id; } static int spcmac_phy_init_dev (struct phy_device *phy_dev) { int phy_addr; unsigned int phy_oui; unsigned int phy_model; phy_addr = spcmac_phy_get_addr (); printf ("PHY address is 0x%02x\n", phy_addr); if (phy_addr < 0) return -1; phy_dev->addr = phy_addr; phy_dev->id = spcmac_phy_get_id (phy_dev->addr); /* Set generic (default) functions. */ phy_dev->init = spcmac_phy_gen_init; phy_dev->is_link_up = spcmac_phy_gen_is_link_up; phy_dev->reset = spcmac_phy_gen_reset; /* Determine the PHY's model to replace some functions, if necessary. */ phy_oui = PHYSID_GET_OUI (phy_dev->id); phy_model = PHYSID_GET_MODEL (phy_dev->id); if ((phy_oui == ICPLUS_OUI) && (phy_model == ICPLUS_IP1001)) { phy_dev->reset = spcmac_phy_ip1001_reset; } else if ((phy_oui == ICPLUS_OUI) && (phy_model == ICPLUS_IP175) && (phy_addr == 5)) { phy_dev->init = spcmac_phy_ip175_init; phy_dev->is_link_up = spcmac_phy_ip175_is_link_up; phy_dev->reset = spcmac_phy_ip175_reset; } else if ((phy_oui == VITESSE_OUI) && (phy_model == VITESSE_VSC8601)) { phy_dev->reset = spcmac_phy_vsc8601_reset; } return 0; } static int spcmac_phy_reset (const struct phy_device *phy_dev) { return phy_dev->reset (phy_dev); } static int spcmac_phy_init (void) { uint value, lp_an_able; struct phy_device phy_device; if (spcmac_phy_init_dev (&phy_device)) return -1; const struct phy_device *const phy_dev = &phy_device; /* before reseting PHY, check if Link Partner is autoneg able * i.e. will we maybe kick autoneg by reseting PHY */ value = (unsigned int) spcmac_mii_read (phy_dev->addr, MII_EXPANSION); lp_an_able = value & ANEXP_LP_AUTONEG_ABLE; spcmac_phy_reset (phy_dev); if (phy_dev->init (phy_dev, lp_an_able)) return -1; /* Wait for LINK UP */ if (spcmac_phy_wait_link_up (phy_dev)) printf ("LINK UP timed out\n"); return 0; } /* ---------------------------------------------------------------------------- MII Interface ---------------------------------------------------------------------------*/ static int spcmac_mii_poll_busy (void) { /* arm simple, non interrupt dependent timer */ ulong now = get_timer (0); while (get_timer (now) < CONFIG_SPCMAC_MII_POLL_BUSY_DELAY) { if (!(SPCMAC_READ (MAC_MII_ADDR) & MAC_MII_ADDR_BUSY)) { return 1; } } printf ("spcmac_mii_busy timeout\n"); return (0); } static void spcmac_mii_write (int phy_addr, int reg, int value) { int mii_addr; /* Select register */ mii_addr = ((phy_addr & MAC_MII_ADDR_PHY_MASK) << MAC_MII_ADDR_PHY_SHIFT) | ((reg & MAC_MII_ADDR_REG_MASK) << MAC_MII_ADDR_REG_SHIFT) | MAC_MII_ADDR_WRITE; spcmac_mii_poll_busy (); /* Set the MII address register to write */ SPCMAC_WRITE (value, MAC_MII_DATA); SPCMAC_WRITE ( (mii_addr | MAC_MII_ADDR_CSR_CLOCK | MAC_MII_ADDR_BUSY), MAC_MII_ADDR); spcmac_mii_poll_busy (); (void) spcmac_mii_read (phy_addr, reg); } static unsigned int spcmac_mii_read (int phy_addr, int reg) { int mii_addr, val; mii_addr = ((phy_addr & MAC_MII_ADDR_PHY_MASK) << MAC_MII_ADDR_PHY_SHIFT) | ((reg & MAC_MII_ADDR_REG_MASK) << MAC_MII_ADDR_REG_SHIFT); /* Select register */ spcmac_mii_poll_busy (); SPCMAC_WRITE ( (mii_addr | MAC_MII_ADDR_CSR_CLOCK | MAC_MII_ADDR_BUSY), MAC_MII_ADDR ); spcmac_mii_poll_busy (); /* Return read value */ val = SPCMAC_READ (MAC_MII_DATA); return val; } static int mii_read (char *devname, unsigned char addr, unsigned char reg, unsigned short *value) { *value = (unsigned short)spcmac_mii_read((int)addr, (int)reg); return 0; } static int mii_write (char *devname, unsigned char addr, unsigned char reg, unsigned short value) { spcmac_mii_write ((int)addr, (int)reg, (int)value); return 0; } /* ---------------------------------------------------------------------------- MAC CORE Interface ---------------------------------------------------------------------------*/ static void spcmac_set_mac_addr (char *Addr) { unsigned long data; data = (Addr[5] << 8) | Addr[4]; SPCMAC_WRITE (data, MAC_ADDR_HIGH); data = (Addr[3] << 24) | (Addr[2] << 16) | (Addr[1] << 8) | Addr[0]; SPCMAC_WRITE (data, MAC_ADDR_LOW); return; } static int spcmac_get_mac_addr (char *addr) { unsigned int hi_addr, lo_addr; /* Read the MAC address from the hardware */ lo_addr = (unsigned int) SPCMAC_READ (MAC_ADDR_LOW); hi_addr = (unsigned int) SPCMAC_READ (MAC_ADDR_HIGH); hi_addr &= 0x0000FFFF; if ((hi_addr == 0x0000FFFFUL) && (lo_addr == 0xFFFFFFFF)) return 0; /* Extract the MAC address from the high and low words */ addr[0] = lo_addr & 0xff; addr[1] = (lo_addr >> 8) & 0xff; addr[2] = (lo_addr >> 16) & 0xff; addr[3] = (lo_addr >> 24) & 0xff; addr[4] = hi_addr & 0xff; addr[5] = (hi_addr >> 8) & 0xff; return 1; } static void spcmac_mac_enable (void) { unsigned int value = (unsigned int) SPCMAC_READ (MAC_CONTROL); PRINTK ("MAC RX/TX enabled\n"); /* set: TE (transmitter enable, bit 3), RE (receive enable, bit 2) and RA (receive all mode, bit 31) */ value |= (MAC_CONTROL_TE | MAC_CONTROL_RE); SPCMAC_WRITE (value, MAC_CONTROL); value = (unsigned int) SPCMAC_READ (MAC_CONTROL); return; } static void spcmac_mac_disable (void) { unsigned int value = (unsigned int) SPCMAC_READ (MAC_CONTROL); PRINTK ("%s: MAC RX/TX disabled\n", __FUNCTION__); value &= ~(MAC_CONTROL_TE | MAC_CONTROL_RE); SPCMAC_WRITE (value, MAC_CONTROL); return; } #if 0 static void spcmac_set_rx_mode (void) { unsigned int value = (unsigned int) SPCMAC_READ (MAC_FRAME_FILTER); PRINTK ("SPCMAC: perfect filtering mode.\n"); value &= ~( MAC_FRAME_FILTER_PRON | MAC_FRAME_FILTER_HUCON | MAC_FRAME_FILTER_HMCON | MAC_FRAME_FILTER_PMON | MAC_FRAME_FILTER_IFON); SPCMAC_WRITE (0x0, MAC_HASH_HIGH); SPCMAC_WRITE (0x0, MAC_HASH_LOW); SPCMAC_WRITE (value, MAC_FRAME_FILTER); return; } #endif #ifdef CONFIG_CHIP_FEATURE_SPCETH static void spcmac_set_rb_mii_cap (unsigned int speed) { unsigned int timeout; unsigned int eth_cfg; unsigned int rmii_clk; DECLARE_GLOBAL_DATA_PTR; spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); unsigned int mode = NVRAM_BFEXT(ETH1_MODE, nvram->pkg_cfg); if(mode == NVRAM_ETH_MODE_GMII) /* GMII */ { if(speed == 1000) { /* 1G was negotiate switch to GMII mode */ writel(CLK_IS_OFF, RB_CLK_CMD_ETH_TX_EXT); for(timeout=0xF ; (readl(RB_CLK_STAT_ETH_TX_EXT) != CLK_IS_OFF) && timeout ; timeout--); writel(CLK_SEL_ETH_TX_125, RB_CLK_SEL_ETH_TX); writel(CLK_IS_ON, RB_CLK_CMD_ETH_TX_125); for(timeout=0xF ; (readl(RB_CLK_STAT_ETH_TX_125) != CLK_IS_ON) && timeout ; timeout--); } else { /* 1G wasn't negotiate swith to MII mode */ writel(CLK_IS_OFF, RB_CLK_CMD_ETH_TX_125); for(timeout=0xF ; (readl(RB_CLK_STAT_ETH_TX_125) != CLK_IS_OFF) && timeout ; timeout--); writel(CLK_SEL_ETH_TX_EXT, RB_CLK_SEL_ETH_TX); writel(CLK_IS_ON, RB_CLK_CMD_ETH_TX_EXT); for(timeout=0xF ; (readl(RB_CLK_STAT_ETH_TX_EXT) != CLK_IS_ON) && timeout ; timeout--); } } if(mode == NVRAM_ETH_MODE_RMII) /* RMII */ { eth_cfg = (unsigned int) readl(RB_ETH_CONFIG); rmii_clk = (unsigned int) readl(RB_CLK_DIV_STAT_ETH_25); if(speed == 10) { eth_cfg &= ~ETH_CFG_RMII_100; rmii_clk |= CLK_DIV_ETH_25_20; } else { eth_cfg |= ETH_CFG_RMII_100; rmii_clk |= CLK_DIV_ETH_25_2; } writel(eth_cfg, RB_ETH_CONFIG); writel(rmii_clk, RB_CLK_DIV_ETH_25); } } #endif /* CONFIG_CHIP_FEATURE_SPCETH */ #ifdef CONFIG_CHIP_FEATURE_MSEETH static void spcmac_set_rb_mii_cap (unsigned int speed) { unsigned int timeout; DECLARE_GLOBAL_DATA_PTR; spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); unsigned int mode = NVRAM_BFEXT(ETH1_MODE, nvram->pkg_cfg); if (mode == NVRAM_ETH_MODE_GMII) /* GMII */ { if (speed == 1000) { /* 1G was negotiate switch to GMII mode */ writel (CLK_CMD_OFF, RB_CLK_CMD_ETH1_TX); writel (CLK_SEL_ETH_TX_RX_PLL_CLK, RB_CLK_SEL_ETH1_TX); writel (CLK_CMD_PLL_ON, RB_CLK_CMD_ETH1_TX); for (timeout = 0xF; (readl (RB_CLK_STAT_ETH1_TX) != CLK_ETH_TX_RX_PLL_IS_ON) && timeout; timeout--) ; } else { /* 1G wasn't negotiate swith to MII mode */ writel (CLK_CMD_OFF, RB_CLK_CMD_ETH1_TX); writel (CLK_SEL_ETH_TX_RX_MII_CLK, RB_CLK_SEL_ETH1_TX); writel (CLK_CMD_MII_ON, RB_CLK_CMD_ETH1_TX); for (timeout=0xF; (readl (RB_CLK_STAT_ETH1_TX) != CLK_ETH_TX_RX_MII_IS_ON) && timeout; timeout--) ; } } if (mode == NVRAM_ETH_MODE_RMII) /* RMII */ { if (speed == 10) { writel (CLK_ETH_DIV_TX_PLL_100, RB_CLK_DIV_ETH1_TX_PLL); writel (CLK_ETH_DIV_RX_PLL_100, RB_CLK_DIV_ETH1_RX_PLL); } else { writel (CLK_ETH_DIV_TX_PLL_10, RB_CLK_DIV_ETH1_TX_PLL); writel (CLK_ETH_DIV_RX_PLL_10, RB_CLK_DIV_ETH1_RX_PLL); } } if (mode == NVRAM_ETH_MODE_RGMII) /* RGMII */ { if (speed == 1000) writel (CLK_ETH_DIV_TX_PLL_2, RB_CLK_DIV_ETH1_TX_PLL); else if (speed == 10) writel (CLK_ETH_DIV_TX_PLL_100, RB_CLK_DIV_ETH1_TX_PLL); else writel (CLK_ETH_DIV_TX_PLL_10, RB_CLK_DIV_ETH1_TX_PLL); } } #endif /* CONFIG_CHIP_FEATURE_MSEETH */ void spcmac_set_mac_mii_cap (int full_duplex, unsigned int speed) { unsigned int flow = (unsigned int) SPCMAC_READ (MAC_FLOW_CONTROL); unsigned int ctrl = (unsigned int) SPCMAC_READ (MAC_CONTROL); if (!full_duplex) { /* Half Duplex */ flow &= ~(MAC_FLOW_CONTROL_FCE | MAC_FLOW_CONTROL_PT_MASK | MAC_FLOW_CONTROL_PCF); ctrl &= ~MAC_CONTROL_FD; ctrl |= MAC_CONTROL_DRO; #ifdef CONFIG_ARCH_ARIZONA // VERY IMPORTANT -- for HD must Disable Carrier Sense, because that // sinal is missing on Arizona board ctrl |= MAC_CONTROL_DCRS; #endif } else { /* Full Duplex */ flow |= MAC_FLOW_CONTROL_FCE | MAC_FLOW_CONTROL_PCF | (MAX_PAUSE_TIME << MAC_FLOW_CONTROL_PT_SHIFT); ctrl |= MAC_CONTROL_FD; ctrl &= ~MAC_CONTROL_DRO; } if (speed == 1000) { ctrl |= MAC_CONTROL_GMII; } else if (speed == 100) { ctrl |= MAC_CONTROL_MII; ctrl |= MAC_CONTROL_FES100; } else { ctrl |= MAC_CONTROL_MII; ctrl &= ~MAC_CONTROL_FES100; } SPCMAC_WRITE (flow, MAC_FLOW_CONTROL); SPCMAC_WRITE (ctrl, MAC_CONTROL); #if defined(CONFIG_CHIP_FEATURE_SPCETH) || defined(CONFIG_CHIP_FEATURE_MSEETH) /* Update Bank registers */ spcmac_set_rb_mii_cap (speed); #endif } /* This function provides the initial setup of the MAC controller */ static void spcmac_mac_core_init (void) { DECLARE_GLOBAL_DATA_PTR; spidcom_nvram_t *nvram = (spidcom_nvram_t *)(gd->bd->bi_nvram_addr); unsigned int mode = NVRAM_BFEXT(ETH1_MODE, nvram->pkg_cfg); unsigned int value = 0; unsigned int ctrl = (unsigned int) SPCMAC_READ (MAC_CONTROL); /* Set the MAC frame filter register with our default value */ value = (unsigned int) SPCMAC_READ (MAC_FRAME_FILTER); value |= MAC_FRAME_FILTER_PMON; /* but for this version we force filter off */ value |= MAC_FRAME_FILTER_OFF; SPCMAC_WRITE (value, MAC_FRAME_FILTER); /* Change the MAX_FRAME bits in the MMC control register. */ SPCMAC_WRITE (((MAX_ETH_FRAME_SIZE << MMC_CONTROL_MAX_FRM_SHIFT) & MMC_CONTROL_MAX_FRM_MASK), MMC_CONTROL); ctrl |= MAC_CONTROL_WD | MAC_CONTROL_JAD; if (mode == NVRAM_ETH_MODE_RGMII) ctrl |= MAC_CONTROL_RGMIIE; SPCMAC_WRITE (ctrl, MAC_CONTROL); return; } /* ---------------------------------------------------------------------------- * DESCRIPTORS functions * ---------------------------------------------------------------------------*/ static void display_dma_desc_ring (volatile spcmac_dma_des * p, int size) { int i; for (i = 0; i < size; i++) printf ("\t%d [0x%x]: " "desc0=0x%x desc1=0x%x buffer1=0x%x\n", i, (unsigned int) &p[i].des0, p[i].des0, p[i].des1, (unsigned int) p[i].des2); } static void init_desc_owner (volatile spcmac_dma_des * head, unsigned int size, unsigned int owner) { int i; volatile spcmac_dma_des *p = head; for (i = 0; i < size; i++) { p->des0 = owner; p++; } return; } static void init_dma_ring (volatile spcmac_dma_des * p, uchar ** phy, unsigned int ring_size, unsigned int own_bit) { int i; for (i = 0; i < ring_size; i++) { p->des0 = own_bit; p->des1 = (!(own_bit) ? 0 : ((PKTSIZE_ALIGN) << DES1_RBS1_SIZE_SHIFT)); if (i == ring_size - 1) { p->des1 |= DES1_CONTROL_TER; } p->des2 = (!(own_bit) ? 0 : ((void *) (PHYSADDR (phy[i])))); p->des3 = NULL; p++; } return; } /* Allocate and init the TX and RX descriptors rings. * The driver uses the 'implicit' scheme for implementing the TX/RX DMA * linked lists. */ static void init_dma_desc_rings (void) { int i; PRINTK ("allocate and init the DMA RX/TX lists\n"); #if 0 /* Clean out uncached buffers */ if ( dcache_status() != 0 ) { flush_dcache(); } #endif /* Allocate memory for the DMA RX/TX buffer descriptors */ dma_rx = (volatile spcmac_dma_des *) (&dma.desc_rx[0]); dma_tx = (volatile spcmac_dma_des *) (&dma.desc_tx[0]); cur_rx = 0; cur_tx = 0; if ((dma_rx == NULL) || (dma_tx == NULL)) { printf ("%s:ERROR allocating the DMA Tx/Rx desc\n", __FUNCTION__); return; } for (i = 0; i < CONFIG_DMA_RX_SIZE; i++) rx_packets[i] = dma.rx_buff + (PKTSIZE_ALIGN * i); init_dma_ring (dma_rx, rx_packets, CONFIG_DMA_RX_SIZE, OWN_BIT); init_dma_ring (dma_tx, 0, CONFIG_DMA_TX_SIZE, 0); #ifdef DEBUG_MAC printf (" - RX descriptor ring:\n"); display_dma_desc_ring (dma_rx, CONFIG_DMA_RX_SIZE); printf (" - TX descriptor ring:\n"); display_dma_desc_ring (dma_tx, CONFIG_DMA_TX_SIZE); #endif return; } /* Release and free the descriptor resources. */ static void free_dma_desc_resources (void) { dma_tx = NULL; dma_rx = NULL; return; } /* ---------------------------------------------------------------------------- DMA FUNCTIONS * ---------------------------------------------------------------------------*/ /* DMA SW reset. * NOTE1: the MII_TxClk and the MII_RxClk must be active before this * SW reset otherwise the MAC core won't exit the reset state. * NOTE2: after a SW reset all interrupts are disabled */ static void spcmac_dma_reset (void) { unsigned int value; #if defined(CONFIG_CHIP_FEATURE_SPCETH) || defined(CONFIG_CHIP_FEATURE_MSEETH) /* Force clock config to GMII before MAC reset */ spcmac_set_rb_mii_cap (1000); #endif value = (unsigned int) SPCMAC_READ (DMA_BUS_MODE); value |= DMA_BUS_MODE_SFT_RESET; SPCMAC_WRITE (value, DMA_BUS_MODE); while ((SPCMAC_READ (DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET)) { } value = (unsigned int) SPCMAC_READ (DMA_BUS_MODE); value |= (0x8<des0 & OWN_BIT) && (get_timer (now) < CONFIG_SPCMAC_TX_TIMEOUT)) { ; } if (p->des0 & OWN_BIT) { printf ("SPCMAC: tx timeout - no desc available\n"); return -1; } p->des2 = (spcmac_dma_des *) PHYSADDR (data); /* Clean and set the descriptor 1 */ p->des1 = ((TDES1_CONTROL_IC | TDES1_CONTROL_FS | TDES1_CONTROL_LS) | ((p->des1 & DES1_CONTROL_TER) ? DES1_CONTROL_TER : 0) | ((len << DES1_RBS1_SIZE_SHIFT) & DES1_RBS1_SIZE_MASK)); p->des0 = OWN_BIT; #ifdef DEBUG_MAC PRINTK ("\nSPCMAC: TX (dma_tx = %d)\n", dma_tx); display_dma_desc_ring (dma_tx, CONFIG_DMA_TX_SIZE); #endif /* CSR1 enables the transmit DMA to check for new descriptor */ SPCMAC_WRITE (DMA_STATUS_TI, DMA_STATUS); SPCMAC_WRITE (1, DMA_XMT_POLL_DEMAND); now = get_timer (0); while (get_timer (now) < CONFIG_SPCMAC_TX_TIMEOUT) { status = SPCMAC_READ (DMA_STATUS); if (status & DMA_STATUS_TI) break; } if (!(status & DMA_STATUS_TI)) { printf ("SPCMAC: tx timeout\n"); #ifdef DEBUG_MAC PRINTK ("DMA_STATUS = %#x\n", status); #endif } check_tx_error_summary (p->des0); if (p->des1 & DES1_CONTROL_TER) pos = 0; else pos++; cur_tx = pos; return (0); } /* Receive function */ void spcmac_eth_rx (void) { int frame_len = 0, pos; volatile spcmac_dma_des *drx; #if 0 /* DMA brought something from network to the memory. * We have to invalidate our D cache in order to force * taking of "update" data from the memory. */ if ( dcache_status() != 0 ) { flush_dcache(); /* Make sure data in memory */ } #endif pos = cur_rx; drx = dma_rx + pos; if ((pos < 0) || (pos >= CONFIG_DMA_RX_SIZE)) { printf ("SPCMAC %s: [dma drx = 0x%x, pos=%d]\n", __FUNCTION__, (unsigned int) drx, pos); display_dma_desc_ring ((spcmac_dma_des *)dma_rx, CONFIG_DMA_RX_SIZE); } if (!(drx->des0 & OWN_BIT) && (drx->des0 & RDES0_STATUS_LS)) { unsigned int status = drx->des0; #ifdef DEBUG_MAC PRINTK ("RX descriptor ring:\n"); display_dma_desc_ring ((spcmac_dma_des *)dma_rx, CONFIG_DMA_RX_SIZE); #endif /* Check if the frame was not successfully received */ if (check_rx_error_summary (status) < 0) { drx->des0 = OWN_BIT; } else if ((status & RDES0_STATUS_FS) && (status & RDES0_STATUS_LS)) { /* FL (frame length) indicates the length in byte including * the CRC */ frame_len = (status & RDES0_STATUS_FL_MASK) >> RDES0_STATUS_FL_SHIFT; if ((frame_len >= 0) && (frame_len <= PKTSIZE_ALIGN)) { memcpy ((void*)NetRxPackets[0], rx_packets[pos], frame_len); NetReceive (NetRxPackets[0], frame_len); } else { printf ("%s: Framelen %d too long\n", __FUNCTION__, frame_len); } drx->des0 = OWN_BIT; #ifdef DEBUG_MAC PRINTK ("%s: frame received \n", __FUNCTION__); #endif } else { printf ("%s: very long frame received\n", __FUNCTION__); } if (drx->des1 & DES1_CONTROL_TER) pos = 0; else pos++; /* drx = dma_rx + pos; */ cur_rx = pos; } else { SPCMAC_WRITE (1, DMA_RCV_POLL_DEMAND); /* request input */ } return; } int spcmac_get_ethaddr (bd_t * bd) { #ifdef CONFIG_ARCH_SPC300 int rom_valid; char v_mac[6]; //Mac address was set from NVRAM to ROM during board_init process //if ROM address is not good return error //Never used env Ethernet MAC address rom_valid = spcmac_get_mac_addr (v_mac); if (rom_valid) { memcpy (bd->bi_enetaddr, v_mac, 6); printf ("Using MAC Address %02X:%02X:%02X:%02X:%02X:%02X\n", v_mac[0], v_mac[1], v_mac[2], v_mac[3], v_mac[4], v_mac[5]); return 0; } else { printf ("\n*** ERROR: Ethernet MAC address is not set correctly. Check NVRAM!!\n"); return -1; } #else int env_size, rom_valid, env_present = 0, reg; char *s = NULL, *e, es[] = "11:22:33:44:55:66"; char s_env_mac[64], v_env_mac[6], v_rom_mac[6], *v_mac; env_size = getenv_r ("ethaddr", s_env_mac, sizeof (s_env_mac)); if ((env_size > 0) && (env_size < sizeof (es))) { /* exit if env is bad */ printf ("\n*** ERROR: ethaddr is not set properly!!\n"); return (-1); } if (env_size > 0) { env_present = 1; s = s_env_mac; } for (reg = 0; reg < 6; ++reg) { /* turn string into mac value */ v_env_mac[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s) s = (*e) ? e + 1 : e; } rom_valid = spcmac_get_mac_addr (v_rom_mac); /* get ROM mac value if any */ if (!env_present) { /* if NO env */ if (rom_valid) { /* but ROM is valid */ v_mac = v_rom_mac; sprintf (s_env_mac, "%02X:%02X:%02X:%02X:%02X:%02X", v_mac[0], v_mac[1], v_mac[2], v_mac[3], v_mac[4], v_mac[5]); setenv ("ethaddr", s_env_mac); } else { /* no env, bad ROM */ printf ("\n*** ERROR: ethaddr is NOT set !!\n"); return (-1); } } else { /* good env, don't care ROM */ v_mac = v_env_mac; /* always use a good env over a ROM */ } if (env_present && rom_valid) { /* if both env and ROM are good */ if (memcmp (v_env_mac, v_rom_mac, 6) != 0) { printf ("\nWarning: MAC addresses don't match:\n"); printf ("\tHW MAC address: " "%02X:%02X:%02X:%02X:%02X:%02X\n", v_rom_mac[0], v_rom_mac[1], v_rom_mac[2], v_rom_mac[3], v_rom_mac[4], v_rom_mac[5]); printf ("\t\"ethaddr\" value: " "%02X:%02X:%02X:%02X:%02X:%02X\n", v_env_mac[0], v_env_mac[1], v_env_mac[2], v_env_mac[3], v_env_mac[4], v_env_mac[5]); } } memcpy (bd->bi_enetaddr, v_mac, 6); /* update global address to match env (allows env changing) */ spcmac_set_mac_addr (v_mac); /* use old function to update default */ printf ("Using MAC Address %02X:%02X:%02X:%02X:%02X:%02X\n", v_mac[0], v_mac[1], v_mac[2], v_mac[3], v_mac[4], v_mac[5]); return (0); #endif } static int spcmac_reset_eth (bd_t * bd) { PRINTK ("%s\n", __FUNCTION__); int err; #ifdef CONFIG_ARCH_SPC300 /* MAC address was set during board_init */ /* So we need to save it before soft_reset under spcmac_dma_init */ char mac_addr[6]; spcmac_get_mac_addr (mac_addr); #endif /* spcmac_dma_init () MUST GO BEFORE MAC INIT - reset of the DMA will erase all MAC registers */ init_dma_desc_rings (); spcmac_dma_init (); #ifdef CONFIG_ARCH_SPC300 /* Soft Reset finish now, we can restore MAC address */ spcmac_set_mac_addr (mac_addr); #endif /* now we can init PHY and MAC */ err = spcmac_get_ethaddr (bd); /* set smc_mac_addr, and sync it with u-boot globals */ if (err < 0) { memset (bd->bi_enetaddr, 0, 6); /* hack to make error stick! upper code will abort if not set */ return (-1); /* upper code ignores this, but NOT bi_enetaddr */ } if (spcmac_phy_init () < 0) { printf ("Phy not detected\n"); return -1; } spcmac_mac_core_init (); /* We can keep reset value of filer (all 0), * so this function that sets filter bists is commented out for now. */ //spcmac_set_rx_mode (); spcmac_mac_enable (); spcmac_dma_start_rx (); spcmac_dma_start_tx (); SPCMAC_WRITE (1, DMA_RCV_POLL_DEMAND); /* request input */ /* Insert artificial delay of 100 ms, * because exeprience showed that PHY needs this * to have stable first RX */ udelay (100000); return (0); } extern int eth_init (bd_t * bd) { PRINTK ("%s\n", __FUNCTION__); /* Disable D-cache if it is turned on */ if (dcache_status () != 0) { flush_dcache (); dcache_disable (); dcache_switch = 1; /* flag to notice to eth_halt to turn it on */ } spcmac_reset_eth (bd); miiphy_register ("synop3504", mii_read, mii_write); return 0; } extern void eth_halt (void) { PRINTK ("%s\n", __FUNCTION__); /* Reset the TX/RX processes */ spcmac_dma_stop_rx (); spcmac_eth_stop_tx (); /* Disable the MAC core */ spcmac_mac_disable (); /* Free buffers */ free_dma_desc_resources (); /* If D-cache was turned off by eth_init, * turn it on now */ if (dcache_switch == 1) { /* Aways first invalidate cache and then disable it, * because moment of enablig cache can be disaterous */ flush_dcache (); dcache_enable (); dcache_switch = 0; } } /* Get a data block via Ethernet */ extern int eth_rx (void) { //PRINTK ("%s\n", __FUNCTION__); spcmac_eth_rx (); return 1; } /* Send a data block via Ethernet. */ extern int eth_send (volatile void *packet, int length) { PRINTK ("%s\n", __FUNCTION__); spcmac_eth_tx (packet, length); return 1; } #endif /* COMMANDS & CFG_NET */ #endif /* CONFIG_DRIVER_NETSPCMAC */