/* * Copyright (C) 2001-2007 by egnite Software GmbH * Copyright (C) 2010 by egnite GmbH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /* * $Id: ether.c 4115 2012-04-12 21:06:13Z olereinhardt $ */ #include "dialog.h" #include "arp.h" #include "ether.h" #include "bootmon.h" #define PIO_BASE 0xFFFF0000 /*!< \brief PIO base address. */ #define PIO_OER (PIO_BASE + 0x10) /*!< \brief Output enable register. */ #define PIO_ODR (PIO_BASE + 0x14) /*!< \brief Output disable register. */ #define PIO_SODR (PIO_BASE + 0x30) /*!< \brief Set output data register. */ #define PIO_CODR (PIO_BASE + 0x34) /*!< \brief Clear output data register. */ #ifndef NIC_BASE_ADDR #define NIC_BASE_ADDR 0x20000000 #endif #ifndef NIC_DATA_ADDR #define NIC_DATA_ADDR (NIC_BASE_ADDR + 4) #endif #define NIC_NCR 0x00 /* Network control register (0x00). */ #define NIC_NCR_LBM 0x06 /* Loopback mode. */ #define NIC_NCR_LBNORM 0x00 /* Normal mode. */ #define NIC_NCR_LBMAC 0x02 /* MAC loopback. */ #define NIC_NCR_LBPHY 0x04 /* PHY loopback. */ #define NIC_NCR_RST 0x01 /* Software reset, auto clear. */ #define NIC_NSR 0x01 /* Network status register (0x00). */ #define NIC_NSR_SPEED 0x80 #define NIC_NSR_LINKST 0x40 #define NIC_NSR_WAKEST 0x20 #define NIC_NSR_TX2END 0x08 #define NIC_NSR_TX1END 0x04 #define NIC_NSR_RXOV 0x02 #define NIC_TCR 0x02 /* TX control register (0x00). */ #define NIC_TCR_TXREQ 0x01 /* TX request */ #define NIC_TSR1 0x03 /* TX status register I (0x00). */ #define NIC_TSR2 0x04 /* TX status register II (0x00). */ #define NIC_RCR 0x05 /* RX control register (0x00). */ #define NIC_RCR_DIS_LONG 0x20 /* Discard long packets. */ #define NIC_RCR_DIS_CRC 0x10 /* Discard CRC error packets. */ #define NIC_RCR_PRMSC 0x02 /* Enable promiscuous mode. */ #define NIC_RCR_RXEN 0x01 /* Enable receiver. */ #define NIC_RSR 0x06 /* RX status register (0x00). */ #define NIC_RSR_ERRORS 0xBF /* Error bit mask. */ #define NIC_RSR_RF 0x80 /* Runt frame. */ #define NIC_RSR_MF 0x40 /* Multicast frame. */ #define NIC_RSR_LCS 0x20 /* Late collision. */ #define NIC_RSR_RWTO 0x10 /* Receiver watchdog time out. */ #define NIC_RSR_PLE 0x08 /* Physical layer error. */ #define NIC_RSR_AE 0x04 /* Alignment error. */ #define NIC_RSR_CE 0x02 /* CRC error. */ #define NIC_RSR_FOE 0x01 /* FIFO overflow error. */ #define NIC_ROCR 0x07 /* Receive overflow counter register (0x00). */ #define NIC_BPTR 0x08 /* Back pressure threshold register (0x37). */ #define NIC_FCTR 0x09 /* Flow control threshold register (0x38). */ #define NIC_FCR 0x0A /* RX flow control register (0x00). */ #define NIC_EPCR 0x0B /* EEPROM and PHY control register. */ #define NIC_EPAR 0x0C /* EEPROM and PHY address register. */ #define NIC_EPDRL 0x0D /* EEPROM and PHY low byte data register. */ #define NIC_EPDRH 0x0E /* EEPROM and PHY high byte data register. */ #define NIC_WCR 0x0F /* Wake up control register (0x00). */ #define NIC_PAR 0x10 /* 6 byte physical address register. */ #define NIC_MAR 0x16 /* 8 byte multicast address register. */ #define NIC_GPCR 0x1E /* General purpose control register (?). */ #define NIC_GPR 0x1F /* General purpose register (?). */ #define NIC_TRPA 0x22 /* 2 byte TX SRAM read pointer address, low/high (0x0000). */ #define NIC_RWPA 0x24 /* 2 byte RX SRAM write pointer address, low/high (0x0000). */ #define NIC_VID 0x28 /* 2 byte vendor ID (0x0A46). */ #define NIC_PID 0x2A /* 2 byte product ID (0x0900). */ #define NIC_CHIPR 0x2C /* Chip revision (0x00). */ #define NIC_SMCR 0x2F /* Special mode register (0x00). */ #define NIC_MRCMDX 0xF0 /* Memory data read command w/o increment (?). */ #define NIC_MRCMD 0xF2 /* Memory data read command with increment (?). */ #define NIC_MRR 0xF4 /* 2 byte memory data read register, low/high (?). */ #define NIC_MWCMDX 0xF6 /* Memory data write command register w/o increment (?). */ #define NIC_MWCMD 0xF8 /* Memory data write command register with increment (?). */ #define NIC_MWR 0xFA /* Memory data write command register with increment (?). */ #define NIC_TXPL 0xFC /* 2 byte TX packet length register. (?). */ #define NIC_ISR 0xFE /* Interrupt status register (0x00). */ #define NIC_ISR_IOM 0xC0 /* I/O mode mask */ #define NIC_ISR_M16 0x00 /* 16-bit I/O mode */ #define NIC_ISR_M32 0x40 /* 32-bit I/O mode */ #define NIC_ISR_M8 0x80 /* 8-bit I/O mode */ #define NIC_ISR_ROOS 0x08 /* Receiver overflow counter interrupt. */ #define NIC_ISR_ROS 0x04 /* Receiver overflow interrupt. */ #define NIC_ISR_PTS 0x02 /* Transmitter interrupt. */ #define NIC_ISR_PRS 0x01 /* Receiver interrupt. */ #define NIC_IMR 0xFF /* Interrupt mask register (0x00). */ #define NIC_IMR_PAR 0x80 /* Enable read/write pointer wrap around. */ #define NIC_IMR_ROOM 0x08 /* Enable receiver overflow counter interrupts. */ #define NIC_IMR_ROM 0x04 /* Enable receiver overflow interrupts. */ #define NIC_IMR_PTM 0x02 /* Enable transmitter interrupts. */ #define NIC_IMR_PRM 0x01 /* Enable receiver interrupts. */ #define NIC_PHY_BMCR 0x00 /* Basic mode control register. */ #define NIC_PHY_BMSR 0x01 /* Basic mode status register. */ #define NIC_PHY_BMSR_ANCOMPL 0x0020 /* Auto negotiation complete. */ #define NIC_PHY_BMSR_LINKSTAT 0x0004 /* Link status. */ #define NIC_PHY_ID1 0x02 /* PHY identifier register 1. */ #define NIC_PHY_ID2 0x03 /* PHY identifier register 2. */ #define NIC_PHY_ANAR 0x04 /* Auto negotiation advertisement register. */ #define NIC_PHY_ANLPAR 0x05 /* Auto negotiation link partner availability register. */ #define NIC_PHY_ANER 0x06 /* Auto negotiation expansion register. */ #define NIC_PHY_DSCR 0x10 /* Davicom specified configuration register. */ #define NIC_PHY_DSCSR 0x11 /* Davicom specified configuration and status register. */ #define NIC_PHY_10BTCSR 0x12 /* 10BASE-T configuration and status register. */ #define outb(_reg, _val) (*((volatile unsigned char *)(_reg)) = (_val)) #define outw(_reg, _val) (*((volatile unsigned short *)(_reg)) = (_val)) #define outr(_reg, _val) (*((volatile unsigned int *)(_reg)) = (_val)) #define inb(_reg) (*((volatile unsigned char *)(_reg))) #define inw(_reg) (*((volatile unsigned short *)(_reg))) #define inr(_reg) (*((volatile unsigned int *)(_reg))) #define _BV(bit) (1 << bit) static inline void nic_outb(unsigned char reg, unsigned char val) { outb(NIC_BASE_ADDR, reg); outb(NIC_DATA_ADDR, val); } static inline void nic_outw(unsigned char reg, unsigned short val) { outb(NIC_BASE_ADDR, reg); outb(NIC_DATA_ADDR, (unsigned char)val); outb(NIC_BASE_ADDR, reg + 1); outb(NIC_DATA_ADDR, (unsigned char)(val >> 8)); } static inline unsigned char nic_inb(unsigned short reg) { outb(NIC_BASE_ADDR, reg); return inb(NIC_DATA_ADDR); } static inline unsigned short nic_inw(unsigned short reg) { unsigned short val; outb(NIC_BASE_ADDR, reg); val = inb(NIC_DATA_ADDR); outb(NIC_BASE_ADDR, reg + 1); val |= inb(NIC_DATA_ADDR) << 8; return val; } /*! * \brief Read contents of PHY register. * * \param reg PHY register number. * * \return Contents of the specified register. */ static unsigned short phy_inw(unsigned char reg) { /* Select PHY register */ nic_outb(NIC_EPAR, 0x40 | reg); /* PHY read command. */ nic_outb(NIC_EPCR, 0x0C); MicroDelay(100); nic_outb(NIC_EPCR, 0x00); /* Get data from PHY data register. */ return nic_inw(NIC_EPDRL); } /*! * \brief Write value to PHY register. * * \note NIC interrupts must have been disabled before calling this routine. * * \param reg PHY register number. * \param val Value to write. */ static void phy_outw(unsigned char reg, unsigned short val) { /* Select PHY register */ nic_outb(NIC_EPAR, 0x40 | reg); /* Store value in PHY data register. */ nic_outw(NIC_EPDRL, val); /* PHY write command. */ nic_outb(NIC_EPCR, 0x0A); MicroDelay(100); nic_outb(NIC_EPCR, 0x00); } static int NicPhyInit(void) { /* Restart auto negotiation. */ phy_outw(NIC_PHY_ANAR, 0x01E1); phy_outw(NIC_PHY_BMCR, 0x1200); nic_outb(NIC_GPCR, 1); nic_outb(NIC_GPR, 0); return 0; } /*! * \brief Write data block to the NIC. * * NIC interrupts must be disabled when calling this function. */ static void NicWrite16(unsigned char *buf, unsigned int len) { unsigned short val; /* Make sure that the total size is an even value. */ len = (len + 1) / 2; while (len--) { val = *buf++; val |= *buf++ << 8; outw(NIC_DATA_ADDR, val); } } /*! * \brief Read data block from the NIC. * * NIC interrupts must be disabled when calling this function. */ static void NicRead16(unsigned char *buf, unsigned short len) { unsigned short val; /* Make sure that the total size is an even value. */ len = (len + 1) / 2; while (len--) { val = inw(NIC_DATA_ADDR); *buf++ = (unsigned char) val; *buf++ = (unsigned char) (val >> 8); } } /*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */ static int NicReset(void) { /* Software reset. */ nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC); MicroDelay(10000); return NicPhyInit(); } /*! * \brief Initialize the NIC. * * \return 0 on success, -1 otherwise. */ int EtherInit(void) { unsigned long id; int link_wait; int i; unsigned char val; /* Software reset. */ nic_outb(NIC_NCR, NIC_NCR_RST); MicroDelay(10000); /* Probe chip by verifying the identifier registers. */ id = (unsigned long) nic_inw(NIC_VID); id |= ((unsigned long) nic_inw(NIC_PID)) << 16; DEBUG("[ID="); DEBUGULONG(id); DEBUG("]"); if (id != 0x90000A46) { return -1; } /* NIC reset. */ if (NicReset()) { DEBUG("[RESET-ERR]"); return -1; } /* Determine bus mode. We do not support 32 bit access. */ val = nic_inb(NIC_ISR); if ((val & NIC_ISR_IOM) != NIC_ISR_M16) { DEBUG("[BUS-ERR ISR=0x"); DEBUGUCHAR(val); DEBUG("]"); return -1; } /* Power up the PHY. */ nic_outb(NIC_GPR, 0); MicroDelay(5000); /* Software reset with MAC loopback. */ nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC); MicroDelay(5000); nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC); MicroDelay(5000); /* * PHY power down followed by PHY power up. This should activate * the auto sense link. */ nic_outb(NIC_GPR, 1); nic_outb(NIC_GPR, 0); /* Set MAC address. */ DEBUG("[MAC="); for (i = 0; i < 6; i++) { DEBUGUCHAR(confnet.cdn_mac[i]); nic_outb(NIC_PAR + i, confnet.cdn_mac[i]); } DEBUG("]"); /* Enable broadcast receive. */ for (i = 0; i < 7; i++) { nic_outb(NIC_MAR + i, 0); } nic_outb(NIC_MAR + 7, 0x80); /* Clear interrupts. */ nic_outb(NIC_ISR, NIC_ISR_ROOS | NIC_ISR_ROS | NIC_ISR_PTS | NIC_ISR_PRS); /* Enable late collision retries on the DM9000A. */ if (nic_inb(NIC_CHIPR) == 0x19) { nic_outb(0x2D, 0x40); } /* Enable receiver. */ nic_outb(NIC_RCR, NIC_RCR_DIS_LONG | NIC_RCR_DIS_CRC | NIC_RCR_RXEN); /* Wait for link. */ for (link_wait = 50;; link_wait--) { unsigned short bmsr; bmsr = phy_inw(NIC_PHY_BMSR); DEBUG("[BMSR="); DEBUGUSHORT(bmsr); DEBUG("]"); if (bmsr & NIC_PHY_BMSR_ANCOMPL) { break; } if (link_wait == 0) { DEBUG("[LINK-ERR]"); return -1; } MicroDelay(100000); } /* Enable interrupts. */ nic_outb(NIC_IMR, NIC_IMR_PAR | NIC_IMR_PTM | NIC_IMR_PRM); DEBUG("[OK]"); return 0; } /*! * \brief Send an Ethernet frame. * * \param dmac Destination MAC address, NULL for broadcast. * \param type Frame type. * \param len Frame size. * * \return 0 on success, -1 otherwise. */ int EtherOutput(const unsigned char *dmac, unsigned int type, unsigned int len) { ETHERHDR *eh; unsigned char *ed; /* * Set the Ethernet header. */ if (type == ETHERTYPE_ARP) { eh = &arpheader; ed = (unsigned char *) &arpframe; } else { eh = &sheader; ed = (unsigned char *) &sframe; } memcpy_(eh->ether_shost, confnet.cdn_mac, 6); if (dmac) { memcpy_(eh->ether_dhost, dmac, 6); } else { memset_(eh->ether_dhost, 0xFF, 6); } eh->ether_type = (unsigned short)type; DEBUG("\nTx("); DEBUGUSHORT(len); DEBUG(")"); /* Enable data write. */ outb(NIC_BASE_ADDR, NIC_MWCMD); /* Transfer the Ethernet frame. */ NicWrite16((unsigned char *)eh, sizeof(ETHERHDR)); NicWrite16(ed, len); /* Start the transmission. */ nic_outw(NIC_TXPL, len + sizeof(ETHERHDR)); nic_outb(NIC_TCR, NIC_TCR_TXREQ); return 0; } /*! * \brief Receive an Ethernet frame. * * \param type Unused? * \param tms Return with timeout after the specified number of polling loops. * * \return The number of bytes received, 0 on timeout or -1 in case of * a failure. */ int EtherInput(unsigned short type, unsigned int tms) { unsigned short fsw; unsigned short fbc; unsigned char isr; for (;;) { /* Read the status word w/o auto increment. */ nic_inb(NIC_MRCMDX); fsw = inb(NIC_DATA_ADDR); if (fsw != 1) { DEBUG("[ETH-POLL]"); DEBUGUSHORT(tms); while (tms) { tms--; random_id++; DEBUG("."); isr = nic_inb(NIC_ISR); nic_outb(NIC_ISR, isr); /* Receiver interrupt. */ if (isr & NIC_ISR_PRS) { nic_inb(NIC_MRCMDX); fsw = inb(NIC_DATA_ADDR); if (fsw == 1) { break; } } MicroDelay(10000); } } if (fsw != 1) { return 0; } /* Now read status word and byte count with auto increment. */ outb(NIC_BASE_ADDR, NIC_MRCMD); fsw = inw(NIC_DATA_ADDR); fbc = inw(NIC_DATA_ADDR); /* * Receiving long packets is unexpected, because we disabled * this during initialization. Let's declare the chip insane. * Short packets will be handled by the caller. */ if (fbc > 1536) { //ni->ni_insane = 1; DEBUG("[FBC "); DEBUGUSHORT(fbc); DEBUG("]"); return -1; } /* * The high byte of the status word contains a copy of the * receiver status register. */ fsw >>= 8; fsw &= NIC_RSR_ERRORS; /* Discard error packets. */ if (fsw) { DEBUG(" RxError("); DEBUGUSHORT(fbc); DEBUG(")"); fbc = (fbc + 1) / 2; while (fbc--) { fsw = inw(NIC_DATA_ADDR); } return -1; } DEBUG(" Rx("); DEBUGUSHORT(fbc); DEBUG(")"); /* Read packet data from 16 bit bus. */ fbc -= 4; NicRead16((unsigned char *) &rheader, sizeof(ETHERHDR)); NicRead16((unsigned char *) &rframe, fbc - sizeof(ETHERHDR)); /* Read packet CRC. */ fsw = inw(NIC_DATA_ADDR); fsw = inw(NIC_DATA_ADDR); if (rheader.ether_type == type) { break; } /* * Handle incoming ARP requests. */ if (rheader.ether_type == ETHERTYPE_ARP) ArpRespond(); } return fbc; }