📄 440gx_enet.c.svn-base
字号:
/*-----------------------------------------------------------------------------+ * * This source code has been made available to you by IBM on an AS-IS * basis. Anyone receiving this source is licensed under IBM * copyrights to use it in any way he or she deems fit, including * copying it, modifying it, compiling it, and redistributing it either * with or without modifications. No license under IBM patents or * patent applications is to be implied by the copyright license. * * Any user of this software should understand that IBM cannot provide * technical support for this software and will not be responsible for * any consequences resulting from the use of this software. * * Any person who transfers this source code or any derivative work * must include the IBM copyright notice, this paragraph, and the * preceding two paragraphs in the transferred software. * * COPYRIGHT I B M CORPORATION 1995 * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M *-----------------------------------------------------------------------------*//*-----------------------------------------------------------------------------+ * * File Name: enetemac.c * * Function: Device driver for the ethernet EMAC3 macro on the 405GP. * * Author: Mark Wisner * * Change Activity- * * Date Description of Change BY * --------- --------------------- --- * 05-May-99 Created MKW * 27-Jun-99 Clean up JWB * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW * 29-Jul-99 Added Full duplex support MKW * 06-Aug-99 Changed names for Mal CR reg MKW * 23-Aug-99 Turned off SYE when running at 10Mbs MKW * 24-Aug-99 Marked descriptor empty after call_xlc MKW * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG * to avoid chaining maximum sized packets. Push starting * RX descriptor address up to the next cache line boundary. * 16-Jan-00 Added support for booting with IP of 0x0 MKW * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the * EMAC_RXM register. JWB * 12-Mar-01 anne-sophie.harnois@nextream.fr * - Variables are compatible with those already defined in * include/net.h * - Receive buffer descriptor ring is used to send buffers * to the user * - Info print about send/received/handled packet number if * INFO_405_ENET is set * 17-Apr-01 stefan.roese@esd-electronics.com * - MAL reset in "eth_halt" included * - Enet speed and duplex output now in one line * 08-May-01 stefan.roese@esd-electronics.com * - MAL error handling added (eth_init called again) * 13-Nov-01 stefan.roese@esd-electronics.com * - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex * 04-Jan-02 stefan.roese@esd-electronics.com * - Wait for PHY auto negotiation to complete added * 06-Feb-02 stefan.roese@esd-electronics.com * - Bug fixed in waiting for auto negotiation to complete * 26-Feb-02 stefan.roese@esd-electronics.com * - rx and tx buffer descriptors now allocated (no fixed address * used anymore) * 17-Jun-02 stefan.roese@esd-electronics.com * - MAL error debug printf 'M' removed (rx de interrupt may * occur upon many incoming packets with only 4 rx buffers). *-----------------------------------------------------------------------------* * 17-Nov-03 travis.sawyer@sandburst.com * - ported from 405gp_enet.c to utilized upto 4 EMAC ports * in the 440GX. This port should work with the 440GP * (2 EMACs) also *-----------------------------------------------------------------------------*/#include <config.h>#if defined(CONFIG_440) && defined(CONFIG_NET_MULTI)#include <common.h>#include <net.h>#include <asm/processor.h>#include <ppc440.h>#include <commproc.h>#include <440gx_enet.h>#include <405_mal.h>#include <miiphy.h>#include <malloc.h>#include "vecnum.h"#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */#define PHY_AUTONEGOTIATE_TIMEOUT 4000 /* 4000 ms autonegotiate timeout *//* Ethernet Transmit and Receive Buffers *//* AS.HARNOIS * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from * PKTSIZE and PKTSIZE_ALIGN (include/net.h) */#define ENET_MAX_MTU PKTSIZE#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN/* define the number of channels implemented */#define EMAC_RXCHL EMAC_NUM_DEV#define EMAC_TXCHL EMAC_NUM_DEV/*-----------------------------------------------------------------------------+ * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal * Interrupt Controller). *-----------------------------------------------------------------------------*/#define MAL_UIC_ERR ( UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR)#define EMAC_UIC_DEF UIC_ENET#undef INFO_440_ENET#define BI_PHYMODE_NONE 0#define BI_PHYMODE_ZMII 1#define BI_PHYMODE_RGMII 2/*-----------------------------------------------------------------------------+ * Global variables. TX and RX descriptors and buffers. *-----------------------------------------------------------------------------*//* IER globals */static uint32_t mal_ier;/*-----------------------------------------------------------------------------+ * Prototypes and externals. *-----------------------------------------------------------------------------*/static void enet_rcv (struct eth_device *dev, unsigned long malisr);int enetInt (struct eth_device *dev);static void mal_err (struct eth_device *dev, unsigned long isr, unsigned long uic, unsigned long maldef, unsigned long mal_errr);static void emac_err (struct eth_device *dev, unsigned long isr);/*-----------------------------------------------------------------------------+| ppc_440x_eth_halt| Disable MAL channel, and EMACn||+-----------------------------------------------------------------------------*/static void ppc_440x_eth_halt (struct eth_device *dev){ EMAC_440GX_HW_PST hw_p = dev->priv; uint32_t failsafe = 10000; out32 (EMAC_IER + hw_p->hw_addr, 0x00000000); /* disable emac interrupts */ /* 1st reset MAL channel */ /* Note: writing a 0 to a channel has no effect */ mtdcr (maltxcarr, (MAL_CR_MMSR >> hw_p->devnum)); mtdcr (malrxcarr, (MAL_CR_MMSR >> hw_p->devnum)); /* wait for reset */ while (mfdcr (maltxcasr) & (MAL_CR_MMSR >> hw_p->devnum)) { udelay (1000); /* Delay 1 MS so as not to hammer the register */ failsafe--; if (failsafe == 0) break; } /* EMAC RESET */ out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST); hw_p->print_speed = 1; /* print speed message again next time */ return;}extern int phy_setup_aneg (unsigned char addr);extern int miiphy_reset (unsigned char addr);#if defined (CONFIG_440_GX)int ppc_440x_eth_setup_bridge(int devnum, bd_t * bis){ unsigned long pfc1; unsigned long zmiifer; unsigned long rmiifer; mfsdr(sdr_pfc1, pfc1); pfc1 = SDR0_PFC1_EPS_DECODE(pfc1); zmiifer = 0; rmiifer = 0; switch (pfc1) { case 1: zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_ZMII; bis->bi_phymode[3] = BI_PHYMODE_ZMII; break; case 2: zmiifer = ZMII_FER_SMII << ZMII_FER_V(0); zmiifer = ZMII_FER_SMII << ZMII_FER_V(1); zmiifer = ZMII_FER_SMII << ZMII_FER_V(2); zmiifer = ZMII_FER_SMII << ZMII_FER_V(3); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_ZMII; bis->bi_phymode[3] = BI_PHYMODE_ZMII; break; case 3: zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_NONE; bis->bi_phymode[2] = BI_PHYMODE_RGMII; bis->bi_phymode[3] = BI_PHYMODE_NONE; break; case 4: zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_RGMII; bis->bi_phymode[3] = BI_PHYMODE_RGMII; break; case 5: zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_ZMII; bis->bi_phymode[3] = BI_PHYMODE_RGMII; break; case 6: zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_RGMII; bis->bi_phymode[3] = BI_PHYMODE_RGMII; break; case 0: default: zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum); rmiifer = 0x0; bis->bi_phymode[0] = BI_PHYMODE_ZMII; bis->bi_phymode[1] = BI_PHYMODE_ZMII; bis->bi_phymode[2] = BI_PHYMODE_ZMII; bis->bi_phymode[3] = BI_PHYMODE_ZMII; break; } /* Ensure we setup mdio for this devnum and ONLY this devnum */ zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); out32 (ZMII_FER, zmiifer); out32 (RGMII_FER, rmiifer); return ((int)pfc1);}#endifstatic int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis){ int i; unsigned long reg; unsigned long msr; unsigned long speed; unsigned long duplex; unsigned long failsafe; unsigned mode_reg; unsigned short devnum; unsigned short reg_short; sys_info_t sysinfo; int ethgroup; EMAC_440GX_HW_PST hw_p = dev->priv; /* before doing anything, figure out if we have a MAC address */ /* if not, bail */ if (memcmp (dev->enetaddr, "\0\0\0\0\0\0", 6) == 0) return -1; /* Need to get the OPB frequency so we can access the PHY */ get_sys_info (&sysinfo); msr = mfmsr (); mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ devnum = hw_p->devnum;#ifdef INFO_440_ENET /* AS.HARNOIS * We should have : * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX * In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, but it * is possible that new packets (without relationship with * current transfer) have got the time to arrived before * netloop calls eth_halt */ printf ("About preceeding transfer (eth%d):\n" "- Sent packet number %d\n" "- Received packet number %d\n" "- Handled packet number %d\n", hw_p->devnum, hw_p->stats.pkts_tx, hw_p->stats.pkts_rx, hw_p->stats.pkts_handled); hw_p->stats.pkts_tx = 0; hw_p->stats.pkts_rx = 0; hw_p->stats.pkts_handled = 0;#endif /* MAL Channel RESET */ /* 1st reset MAL channel */ /* Note: writing a 0 to a channel has no effect */ mtdcr (maltxcarr, (MAL_TXRX_CASR >> hw_p->devnum)); mtdcr (malrxcarr, (MAL_TXRX_CASR >> hw_p->devnum)); /* wait for reset */ /* TBS: should have udelay and failsafe here */ failsafe = 10000; /* wait for reset */ while (mfdcr (maltxcasr) & (MAL_CR_MMSR >> hw_p->devnum)) { udelay (1000); /* Delay 1 MS so as not to hammer the register */ failsafe--; if (failsafe == 0) break; } hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ hw_p->rx_slot = 0; /* MAL Receive Slot */ hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */ hw_p->rx_u_index = 0; /* Receive User Queue Index */ hw_p->tx_slot = 0; /* MAL Transmit Slot */ hw_p->tx_i_index = 0; /* Transmit Interrupt Queue Index */ hw_p->tx_u_index = 0; /* Transmit User Queue Index */ /* set RMII mode */ /* NOTE: 440GX spec states that mode is mutually exclusive */ /* NOTE: Therefore, disable all other EMACS, since we handle */ /* NOTE: only one emac at a time */ reg = 0; out32 (ZMII_FER, 0); udelay (100);#if defined(CONFIG_440_GX) ethgroup = ppc_440x_eth_setup_bridge(devnum, bis);#else if ((devnum == 0) || (devnum == 1)) { out32 (ZMII_FER, (ZMII_FER_SMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); } else { /* ((devnum == 2) || (devnum == 3)) */ out32 (ZMII_FER, ZMII_FER_MDI << ZMII_FER_V (devnum)); out32 (RGMII_FER, ((RGMII_FER_RGMII << RGMII_FER_V (2)) | (RGMII_FER_RGMII << RGMII_FER_V (3)))); }#endif out32 (ZMII_SSR, ZMII_SSR_SP << ZMII_SSR_V(devnum)); __asm__ volatile ("eieio"); /* reset emac so we have access to the phy */ out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST); __asm__ volatile ("eieio"); failsafe = 1000; while ((in32 (EMAC_M0 + hw_p->hw_addr) & (EMAC_M0_SRST)) && failsafe) { udelay (1000); failsafe--; } /* Whack the M1 register */ mode_reg = 0x0; mode_reg &= ~0x00000038; if (sysinfo.freqOPB <= 50000000); else if (sysinfo.freqOPB <= 66666667) mode_reg |= EMAC_M1_OBCI_66; else if (sysinfo.freqOPB <= 83333333) mode_reg |= EMAC_M1_OBCI_83; else if (sysinfo.freqOPB <= 100000000) mode_reg |= EMAC_M1_OBCI_100; else mode_reg |= EMAC_M1_OBCI_GT100; out32 (EMAC_M1 + hw_p->hw_addr, mode_reg); /* wait for PHY to complete auto negotiation */ reg_short = 0;#ifndef CONFIG_CS8952_PHY switch (devnum) { case 0: reg = CONFIG_PHY_ADDR; break; case 1: reg = CONFIG_PHY1_ADDR; break;#if defined (CONFIG_440_GX) case 2: reg = CONFIG_PHY2_ADDR; break; case 3: reg = CONFIG_PHY3_ADDR; break;#endif default: reg = CONFIG_PHY_ADDR; break; } bis->bi_phynum[devnum] = reg; /* Reset the phy */ miiphy_reset (reg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -