📄 ax88796.c
字号:
/* * Copyright (C) 2003-2005 by egnite Software 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 EGNITE SOFTWARE GMBH 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 EGNITE * SOFTWARE GMBH 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/ * * -- * Initially taken from Marek Hummel's port of the Realtek driver. * * Revision 1.0 2004/07/20 17:29:08 MarekH *//* * $Log: ax88796.c,v $ * Revision 1.2 2005/10/22 08:55:47 haraldkipp * CPU specific headers moved to subdirectories for the CPU family. * * Revision 1.1 2005/07/26 18:02:26 haraldkipp * Moved from dev. * * Revision 1.1 2005/04/05 17:47:48 haraldkipp * Initial check in. For ARM only. * */#include <arch/arm/at91.h>#include <string.h>//#include <stdio.h>#include <sys/atom.h>#include <sys/heap.h>#include <sys/thread.h>#include <sys/event.h>#include <sys/timer.h>#include <sys/confnet.h>#include <dev/irqreg.h>#include <dev/ax88796.h>#include "reg_ax88796.h"#define ASIX_RESET_PIN 10static NICINFO dcb_eth0;/*! * \addtogroup xgNicAsix *//*@{*//*! * \brief Network interface information structure. * * Used to call. */static IFNET ifn_eth0 = { IFT_ETHER, /*!< \brief Interface type. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address. */ 0, /*!< \brief IP address. */ 0, /*!< \brief Remote IP address for point to point. */ 0, /*!< \brief IP network mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit. */ 0, /*!< \brief Packet identifier. */ 0, /*!< \brief Linked list of arp entries. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ AsixOutput, /*!< \brief Driver output routine, if_send(). */ NutEtherOutput /*!< \brief Media output routine, if_output(). */};/*! * \brief Device information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this Ethernet device driver to the Nut/OS kernel. * An application may then call NutNetIfConfig() with the name \em eth0 * of this driver to initialize the network interface. * */NUTDEVICE devAx88796 = { 0, /* Pointer to next device. */ {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /* Unique device name. */ IFTYP_NET, /* Type of device. */ 0, /* Base address. */ 0, /* First interrupt number. */ &ifn_eth0, /* Interface control block. */ &dcb_eth0, /* Driver control block. */ AsixInit, /* Driver initialization routine. */ 0, /* Driver specific control function. */ 0, /* Read from device. */ 0, /* Write to device. */ 0, /* Open a device or file. */ 0, /* Close a device or file. */ 0 /* Request file size. */};/*! * Asix packet header. */struct nic_pkt_header { u_char ph_status; /*!< \brief Status, contents of RSR register */ u_char ph_nextpg; /*!< \brief Page for next packet */ u_short ph_size; /*!< \brief Size of header and packet in octets */};/* * This delay has been added by Bengt Florin and is used to minimize * the effect of the IORDY line during reads. Bengt contributed a * more versatile loop, which unfortunately wasn't portable to the * ImageCraft compiler. * * Both versions depend on the CPU clock and had been tested with * 14.7456 MHz. */void Delay16Cycles(void){ asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop");}//==============================================================================/*! * \brief Read and write MII bus. * * */static u_short MIIPutGet(u_short data, u_char bitCount){ u_short rc = 0; u_short mask; u_char i; mask = 1 << (bitCount - 1); for (i = 0; i < bitCount; i++) { /* send data to MII */ if (data & mask) { Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDO)); } else { Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDO))); } /* clock and data recieve from MII */ Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDC)); //clock up Delay16Cycles(); data <<= 1; rc <<= 1; rc |= (Asix_Read(MII_EEP) & MII_EEP_MDI) != 0; //read MII Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDC))); //clock down } return rc;}//==============================================================================/*! * \brief Read contents of internel PHY register on 0x10 adress. * * * \return Contents of the specified register. */u_short NicPhyRead(u_char reg){ u_short rc = 0; /* Select Bank 0. */ Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0))); /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */ MIIPutGet(0xFFFF, 16); MIIPutGet(0xFFFF, 16); /* Start(01), Read(10), PhyAdr(10000) */ MIIPutGet(0xD0, 9); /* Reg address<5:0> */ MIIPutGet(reg, 5); /* TA(Z0), no support high-Z */ MIIPutGet(0x0, 1); /* Read data from internel PHY */ rc = MIIPutGet(0, 16); return rc;}//==============================================================================/*! * \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. */void NicPhyWrite(u_char reg, u_short val){ /* Select Bank 0. */ Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0))); /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */ MIIPutGet(0xFFFF, 16); MIIPutGet(0xFFFF, 16); /* Start(01), Write(01), PhyAdr(10000) */ MIIPutGet(0xB0, 9); /* Reg address<5:0> */ MIIPutGet(reg, 5); /* TA(01) */ MIIPutGet(0x02, 2); /* Write data to internel PHY */ MIIPutGet(val, 16);}//==============================================================================/*! * Complete remote DMA. */static void NicCompleteDma(void){ u_char i; /* Check that we have a DMA complete flag. */ do { i = Asix_Read(PG0_ISR); } while ((i & ISR_RDC) == 0); /* Complete remote dma. */ Asix_Write(CR, CR_START | CR_RD2); /* Reset remote dma complete flag. */ Asix_Write(PG0_ISR, ISR_RDC); /* Wait for intterupt flag. */ Delay16Cycles();}//==============================================================================/*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */static int NicReset(void){ int tmp; //u_short test; //printf("NicReset()\n"); outr(PIO_PER, _BV(ASIX_RESET_PIN)); /* Set PIO Enable Register */ outr(PIO_OER, _BV(ASIX_RESET_PIN)); /* Set PIO Status Register */ outr(PIO_SODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 1 */ NutDelay(100); outr(PIO_CODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 0 */ /* wait for PHY to come out of reset. */ tmp = 10; while (1) { NutDelay(255); if (!(Asix_Read(TR) & TR_RST_B)) break; if (tmp-- == 0) return -1; } //test = NicPhyRead(PHY_MR2); //printf("PHY_MR2 = 0x%.04x\n\r", test); //test = NicPhyRead(PHY_MR3); //printf("PHY_MR3 = 0x%.04x\n\r", test);/* Following actions will fix the problem of long auto negotiation. */// NicPhyWrite(0x00,0x0800);// NutSleep(2500);// NicPhyWrite(0x00,0x1200); /* set speed to auto */ return 0;}//==============================================================================/* * Fires up the network interface. NIC interrupts * should have been disabled when calling this * function. * * \param mac Six byte unique MAC address. */static int NicStart(CONST u_char * mac){ u_char i; //printf("NicStart()\n"); if (NicReset()) return -1; /* Stop the NIC, abort DMA, page 0. */ Asix_Write(CR, (CR_RD2 | CR_STOP)); /* Selects word-wide DMA transfers. */ Asix_Write(PG0_DCR, DCR_WTS); /* Load data byte count for remote DMA. */ Asix_Write(PG0_RBCR0, 0x00); Asix_Write(PG0_RBCR1, 0x00); /* Temporarily set receiver to monitor mode. */ Asix_Write(PG0_RCR, RCR_MON); /* Transmitter set to internal loopback mode. */ Asix_Write(PG0_TCR, TCR_LB0); /* Initialize Recieve Buffer Ring. */ Asix_Write(PG0_BNRY, RXSTART_INIT); Asix_Write(PG0_PSTART, RXSTART_INIT); Asix_Write(PG0_PSTOP, RXSTOP_INIT); /* Clear interrupt status. */ Asix_Write(PG0_ISR, 0xFF); /* Initialize interrupt mask. */ Asix_Write(PG0_IMR, IMR_PRXE | IMR_PTXE | IMR_RXEE | IMR_TXEE | IMR_OVWE); /* Stop the NIC, abort DMA, page 1. */ Asix_Write(CR, (CR_PS0 | CR_RD2 | CR_STOP)); Delay16Cycles(); /* Set Physical address - MAC. */ for (i = 0; i < 6; i++) { Asix_Write(PG1_PAR0 + i, mac[i]); } /* Set Multicast address. */ for (i = 0; i < 8; i++) { Asix_Write(PG1_MAR0 + i, 0x00); } /* Set Current pointer point. */ Asix_Write(PG1_CPR, RXSTART_INIT + 1); /* Start the NIC, Abort DMA, page 0. */ Asix_Write(CR, (CR_RD2 | CR_START)); // stop the NIC, abort DMA, page 0 Delay16Cycles(); /* Select media interfac. */ Asix_Write(GPOC, 0x10); /* Check PHY speed setting 100base = full duplex, 10base > half duplex. */ if (Asix_Read(GPI) & 0x04) { //printf("Full duplex\n"); Asix_Write(PG0_TCR, TCR_FDU); } else { //printf("Half duplex\n"); Asix_Write(PG0_TCR, 0); } /* Enable receiver and set accept broadcast. */ //Asix_Write(PG0_RCR, (RCR_INTT | RCR_AB)); Asix_Write(PG0_RCR, RCR_AB); return 0;}//==============================================================================/* * Write data block to the NIC. */static void NicWrite(u_char * buf, u_short len){ register u_short *wp = (u_short *) buf; if (len & 1) len++; len >>= 1; //printf("Write(%u): ", len); while (len--) { //printf("%04X ", *wp); Asix_WriteWord(DATAPORT, *wp); wp++; } //putchar('\n');}//==============================================================================/* * Read data block from the NIC. */static void NicRead(u_char * buf, u_short len){ register u_short *wp = (u_short *) buf; if (len & 1) len++; len >>= 1; //printf("Read(%u): ", len); while (len--) { *wp = Asix_ReadWord(DATAPORT); //printf("%04X ", *wp); wp++; } //putchar('\n');}//==============================================================================/*! * \brief Fetch the next packet out of the receive ring buffer. * * Nic interrupts must be disabled when calling this funtion. * * \return Pointer to an allocated ::NETBUF. If there is no * no data available, then the function returns a * null pointer. If the NIC's buffer seems to be * corrupted, a pointer to 0xFFFF is returned. */static NETBUF *NicGetPacket(void){ NETBUF *nb = 0; struct nic_pkt_header hdr; u_short count; u_char nextpg; u_char bnry; u_char curr; u_char drop = 0; /* we don't want to be interrupted by NIC owerflow */ NutEnterCritical(); /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ curr = Asix_Read(PG0_CPR); //printf("curr=%02X\n", curr); /* * Get the pointer to the last page we read from. The following page * is the one where we start reading. If it's equal to the current * page pointer, then there's nothing to read. In this case we return * a null pointer. */ if ((bnry = Asix_Read(PG0_BNRY) + 1) >= RXSTOP_INIT) { //printf("bnry=%02X?\n", bnry); bnry = RXSTART_INIT; } if (bnry == curr) { //printf("bnry=%02X\n", bnry); NutJumpOutCritical(); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -