diff options
-rw-r--r-- | src/drivers/net/mtd80x.c | 1022 | ||||
-rw-r--r-- | src/drivers/net/myson.c | 668 | ||||
-rw-r--r-- | src/drivers/net/myson.h | 200 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 |
4 files changed, 869 insertions, 1022 deletions
diff --git a/src/drivers/net/mtd80x.c b/src/drivers/net/mtd80x.c deleted file mode 100644 index 170b5c5..0000000 --- a/src/drivers/net/mtd80x.c +++ /dev/null @@ -1,1022 +0,0 @@ -/************************************************************************** -* -* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip. -* Written 2004-2004 by Erdem Güven <zuencap@yahoo.com> -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Portions of this code based on: -* fealnx.c: A Linux device driver for the mtd80x Ethernet chip -* Written 1998-2000 by Donald Becker -* -***************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/* to get some global routines like printf */ -#include "etherboot.h" -/* to get the interface to the body of the program */ -#include "nic.h" -/* to get the PCI support functions, if this is a PCI NIC */ -#include <ipxe/pci.h> -#include <ipxe/ethernet.h> -#include <mii.h> - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) -#define get_unaligned(ptr) (*(ptr)) - - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for compile efficiency. */ -/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */ -/* Making the Tx ring too large decreases the effectiveness of channel */ -/* bonding and packet priority. */ -/* There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 2 -#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ -#define RX_RING_SIZE 4 - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define HZ 100 -#define TX_TIME_OUT (6*HZ) - -/* Allocation size of Rx buffers with normal sized Ethernet frames. - Do not change this value without good reason. This is not a limit, - but a way to keep a consistent allocation size among drivers. - */ -#define PKT_BUF_SZ 1536 - -/* for different PHY */ -enum phy_type_flags { - MysonPHY = 1, - AhdocPHY = 2, - SeeqPHY = 3, - MarvellPHY = 4, - Myson981 = 5, - LevelOnePHY = 6, - OtherPHY = 10, -}; - -/* A chip capabilities table*/ -enum chip_capability_flags { - HAS_MII_XCVR, - HAS_CHIP_XCVR, -}; - -#if 0 /* not used */ -static -struct chip_info -{ - u16 dev_id; - int flag; -} -mtd80x_chips[] = { - {0x0800, HAS_MII_XCVR}, - {0x0803, HAS_CHIP_XCVR}, - {0x0891, HAS_MII_XCVR} - }; -static int chip_cnt = sizeof( mtd80x_chips ) / sizeof( struct chip_info ); -#endif - -/* Offsets to the Command and Status Registers. */ -enum mtd_offsets { - PAR0 = 0x0, /* physical address 0-3 */ - PAR1 = 0x04, /* physical address 4-5 */ - MAR0 = 0x08, /* multicast address 0-3 */ - MAR1 = 0x0C, /* multicast address 4-7 */ - FAR0 = 0x10, /* flow-control address 0-3 */ - FAR1 = 0x14, /* flow-control address 4-5 */ - TCRRCR = 0x18, /* receive & transmit configuration */ - BCR = 0x1C, /* bus command */ - TXPDR = 0x20, /* transmit polling demand */ - RXPDR = 0x24, /* receive polling demand */ - RXCWP = 0x28, /* receive current word pointer */ - TXLBA = 0x2C, /* transmit list base address */ - RXLBA = 0x30, /* receive list base address */ - ISR = 0x34, /* interrupt status */ - IMR = 0x38, /* interrupt mask */ - FTH = 0x3C, /* flow control high/low threshold */ - MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */ - TALLY = 0x44, /* tally counters for crc and mpa */ - TSR = 0x48, /* tally counter for transmit status */ - BMCRSR = 0x4c, /* basic mode control and status */ - PHYIDENTIFIER = 0x50, /* phy identifier */ - ANARANLPAR = 0x54, /* auto-negotiation advertisement and link - partner ability */ - ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */ - BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */ -}; - -/* Bits in the interrupt status/enable registers. */ -/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ -enum intr_status_bits { - RFCON = 0x00020000, /* receive flow control xon packet */ - RFCOFF = 0x00010000, /* receive flow control xoff packet */ - LSCStatus = 0x00008000, /* link status change */ - ANCStatus = 0x00004000, /* autonegotiation completed */ - FBE = 0x00002000, /* fatal bus error */ - FBEMask = 0x00001800, /* mask bit12-11 */ - ParityErr = 0x00000000, /* parity error */ - TargetErr = 0x00001000, /* target abort */ - MasterErr = 0x00000800, /* master error */ - TUNF = 0x00000400, /* transmit underflow */ - ROVF = 0x00000200, /* receive overflow */ - ETI = 0x00000100, /* transmit early int */ - ERI = 0x00000080, /* receive early int */ - CNTOVF = 0x00000040, /* counter overflow */ - RBU = 0x00000020, /* receive buffer unavailable */ - TBU = 0x00000010, /* transmit buffer unavilable */ - TI = 0x00000008, /* transmit interrupt */ - RI = 0x00000004, /* receive interrupt */ - RxErr = 0x00000002, /* receive error */ -}; - -/* Bits in the NetworkConfig register. */ -enum rx_mode_bits { - RxModeMask = 0xe0, - AcceptAllPhys = 0x80, /* promiscuous mode */ - AcceptBroadcast = 0x40, /* accept broadcast */ - AcceptMulticast = 0x20, /* accept mutlicast */ - AcceptRunt = 0x08, /* receive runt pkt */ - ALP = 0x04, /* receive long pkt */ - AcceptErr = 0x02, /* receive error pkt */ - - AcceptMyPhys = 0x00000000, - RxEnable = 0x00000001, - RxFlowCtrl = 0x00002000, - TxEnable = 0x00040000, - TxModeFDX = 0x00100000, - TxThreshold = 0x00e00000, - - PS1000 = 0x00010000, - PS10 = 0x00080000, - FD = 0x00100000, -}; - -/* Bits in network_desc.status */ -enum rx_desc_status_bits { - RXOWN = 0x80000000, /* own bit */ - FLNGMASK = 0x0fff0000, /* frame length */ - FLNGShift = 16, - MARSTATUS = 0x00004000, /* multicast address received */ - BARSTATUS = 0x00002000, /* broadcast address received */ - PHYSTATUS = 0x00001000, /* physical address received */ - RXFSD = 0x00000800, /* first descriptor */ - RXLSD = 0x00000400, /* last descriptor */ - ErrorSummary = 0x80, /* error summary */ - RUNT = 0x40, /* runt packet received */ - LONG = 0x20, /* long packet received */ - FAE = 0x10, /* frame align error */ - CRC = 0x08, /* crc error */ - RXER = 0x04, /* receive error */ -}; - -enum rx_desc_control_bits { - RXIC = 0x00800000, /* interrupt control */ - RBSShift = 0, -}; - -enum tx_desc_status_bits { - TXOWN = 0x80000000, /* own bit */ - JABTO = 0x00004000, /* jabber timeout */ - CSL = 0x00002000, /* carrier sense lost */ - LC = 0x00001000, /* late collision */ - EC = 0x00000800, /* excessive collision */ - UDF = 0x00000400, /* fifo underflow */ - DFR = 0x00000200, /* deferred */ - HF = 0x00000100, /* heartbeat fail */ - NCRMask = 0x000000ff, /* collision retry count */ - NCRShift = 0, -}; - -enum tx_desc_control_bits { - TXIC = 0x80000000, /* interrupt control */ - ETIControl = 0x40000000, /* early transmit interrupt */ - TXLD = 0x20000000, /* last descriptor */ - TXFD = 0x10000000, /* first descriptor */ - CRCEnable = 0x08000000, /* crc control */ - PADEnable = 0x04000000, /* padding control */ - RetryTxLC = 0x02000000, /* retry late collision */ - PKTSMask = 0x3ff800, /* packet size bit21-11 */ - PKTSShift = 11, - TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */ - TBSShift = 0, -}; - -/* BootROM/EEPROM/MII Management Register */ -#define MASK_MIIR_MII_READ 0x00000000 -#define MASK_MIIR_MII_WRITE 0x00000008 -#define MASK_MIIR_MII_MDO 0x00000004 -#define MASK_MIIR_MII_MDI 0x00000002 -#define MASK_MIIR_MII_MDC 0x00000001 - -/* ST+OP+PHYAD+REGAD+TA */ -#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ -#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ - -/* ------------------------------------------------------------------------- */ -/* Constants for Myson PHY */ -/* ------------------------------------------------------------------------- */ -#define MysonPHYID 0xd0000302 -/* 89-7-27 add, (begin) */ -#define MysonPHYID0 0x0302 -#define StatusRegister 18 -#define SPEED100 0x0400 // bit10 -#define FULLMODE 0x0800 // bit11 -/* 89-7-27 add, (end) */ - -/* ------------------------------------------------------------------------- */ -/* Constants for Seeq 80225 PHY */ -/* ------------------------------------------------------------------------- */ -#define SeeqPHYID0 0x0016 - -#define MIIRegister18 18 -#define SPD_DET_100 0x80 -#define DPLX_DET_FULL 0x40 - -/* ------------------------------------------------------------------------- */ -/* Constants for Ahdoc 101 PHY */ -/* ------------------------------------------------------------------------- */ -#define AhdocPHYID0 0x0022 - -#define DiagnosticReg 18 -#define DPLX_FULL 0x0800 -#define Speed_100 0x0400 - -/* 89/6/13 add, */ -/* -------------------------------------------------------------------------- */ -/* Constants */ -/* -------------------------------------------------------------------------- */ -#define MarvellPHYID0 0x0141 -#define LevelOnePHYID0 0x0013 - -#define MII1000BaseTControlReg 9 -#define MII1000BaseTStatusReg 10 -#define SpecificReg 17 - -/* for 1000BaseT Control Register */ -#define PHYAbletoPerform1000FullDuplex 0x0200 -#define PHYAbletoPerform1000HalfDuplex 0x0100 -#define PHY1000AbilityMask 0x300 - -// for phy specific status register, marvell phy. -#define SpeedMask 0x0c000 -#define Speed_1000M 0x08000 -#define Speed_100M 0x4000 -#define Speed_10M 0 -#define Full_Duplex 0x2000 - -// 89/12/29 add, for phy specific status register, levelone phy, (begin) -#define LXT1000_100M 0x08000 -#define LXT1000_1000M 0x0c000 -#define LXT1000_Full 0x200 -// 89/12/29 add, for phy specific status register, levelone phy, (end) - -#if 0 -/* for 3-in-1 case */ -#define PS10 0x00080000 -#define FD 0x00100000 -#define PS1000 0x00010000 -#endif - -/* for PHY */ -#define LinkIsUp 0x0004 -#define LinkIsUp2 0x00040000 - -/* Create a static buffer of size PKT_BUF_SZ for each -RX and TX Descriptor. All descriptors point to a -part of this buffer */ -struct { - u8 txb[PKT_BUF_SZ * TX_RING_SIZE] __attribute__ ((aligned(8))); - u8 rxb[PKT_BUF_SZ * RX_RING_SIZE] __attribute__ ((aligned(8))); -} mtd80x_bufs __shared; -#define txb mtd80x_bufs.txb -#define rxb mtd80x_bufs.rxb - -/* The Tulip Rx and Tx buffer descriptors. */ -struct mtd_desc -{ - s32 status; - s32 control; - u32 buffer; - u32 next_desc; - struct mtd_desc *next_desc_logical; - u8* skbuff; - u32 reserved1; - u32 reserved2; -}; - -struct mtd_private -{ - struct mtd_desc rx_ring[RX_RING_SIZE]; - struct mtd_desc tx_ring[TX_RING_SIZE]; - - /* Frequently used values: keep some adjacent for cache effect. */ - int flags; - struct pci_dev *pci_dev; - unsigned long crvalue; - unsigned long bcrvalue; - /*unsigned long imrvalue;*/ - struct mtd_desc *cur_rx; - struct mtd_desc *lack_rxbuf; - int really_rx_count; - struct mtd_desc *cur_tx; - struct mtd_desc *cur_tx_copy; - int really_tx_count; - int free_tx_count; - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - - /* These values are keep track of the transceiver/media in use. */ - unsigned int linkok; - unsigned int line_speed; - unsigned int duplexmode; - unsigned int default_port: - 4; /* Last dev->if_port value. */ - unsigned int PHYType; - - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - unsigned char phys[1]; /* MII device addresses. */ - - /*other*/ - const char *nic_name; - int ioaddr; - u16 dev_id; -}; - -static struct mtd_private mtdx; - -static int mdio_read(struct nic * , int phy_id, int location); -static void getlinktype(struct nic * ); -static void getlinkstatus(struct nic * ); -static void set_rx_mode(struct nic *); - -/************************************************************************** - * init_ring - setup the tx and rx descriptors - *************************************************************************/ -static void init_ring(struct nic *nic __unused) -{ - int i; - - mtdx.cur_rx = &mtdx.rx_ring[0]; - - mtdx.rx_buf_sz = PKT_BUF_SZ; - /*mtdx.rx_head_desc = &mtdx.rx_ring[0];*/ - - /* Initialize all Rx descriptors. */ - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) - { - mtdx.rx_ring[i].status = RXOWN; - mtdx.rx_ring[i].control = mtdx.rx_buf_sz << RBSShift; - mtdx.rx_ring[i].next_desc = virt_to_le32desc(&mtdx.rx_ring[i+1]); - mtdx.rx_ring[i].next_desc_logical = &mtdx.rx_ring[i+1]; - mtdx.rx_ring[i].buffer = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]); - mtdx.rx_ring[i].skbuff = &rxb[i * PKT_BUF_SZ]; - } - /* Mark the last entry as wrapping the ring. */ - mtdx.rx_ring[i-1].next_desc = virt_to_le32desc(&mtdx.rx_ring[0]); - mtdx.rx_ring[i-1].next_desc_logical = &mtdx.rx_ring[0]; - - /* We only use one transmit buffer, but two - * descriptors so transmit engines have somewhere - * to point should they feel the need */ - mtdx.tx_ring[0].status = 0x00000000; - mtdx.tx_ring[0].buffer = virt_to_bus(&txb[0]); - mtdx.tx_ring[0].next_desc = virt_to_le32desc(&mtdx.tx_ring[1]); - - /* This descriptor is never used */ - mtdx.tx_ring[1].status = 0x00000000; - mtdx.tx_ring[1].buffer = 0; /*virt_to_bus(&txb[1]); */ - mtdx.tx_ring[1].next_desc = virt_to_le32desc(&mtdx.tx_ring[0]); - - return; -} - -/************************************************************************** -RESET - Reset Adapter -***************************************************************************/ -static void mtd_reset( struct nic *nic ) -{ - /* Reset the chip to erase previous misconfiguration. */ - outl(0x00000001, mtdx.ioaddr + BCR); - - init_ring(nic); - - outl(virt_to_bus(mtdx.rx_ring), mtdx.ioaddr + RXLBA); - outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA); - - /* Initialize other registers. */ - /* Configure the PCI bus bursts and FIFO thresholds. */ - mtdx.bcrvalue = 0x10; /* little-endian, 8 burst length */ - mtdx.crvalue = 0xa00; /* rx 128 burst length */ - - if ( mtdx.dev_id == 0x891 ) { - mtdx.bcrvalue |= 0x200; /* set PROG bit */ - mtdx.crvalue |= 0x02000000; /* set enhanced bit */ - } - - outl( mtdx.bcrvalue, mtdx.ioaddr + BCR); - - /* Restart Rx engine if stopped. */ - outl(0, mtdx.ioaddr + RXPDR); - - getlinkstatus(nic); - if (mtdx.linkok) - { - static const char* texts[]={"half","full","10","100","1000"}; - getlinktype(nic); - DBG ( "Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] ); - } else - { - DBG ( "No link!!!\n" ); - } - - mtdx.crvalue |= /*TxEnable |*/ RxEnable | TxThreshold; - set_rx_mode(nic); - - /* Clear interrupts by setting the interrupt mask. */ - outl(FBE | TUNF | CNTOVF | RBU | TI | RI, mtdx.ioaddr + ISR); - outl( 0, mtdx.ioaddr + IMR); -} - -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int mtd_poll(struct nic *nic, __unused int retrieve) -{ - s32 rx_status = mtdx.cur_rx->status; - int retval = 0; - - if( ( rx_status & RXOWN ) != 0 ) - { - return 0; - } - - if (rx_status & ErrorSummary) - { /* there was a fatal error */ - printf( "%s: Receive error, Rx status %8.8x, Error(s) %s%s%s\n", - mtdx.nic_name, (unsigned int) rx_status, - (rx_status & (LONG | RUNT)) ? "length_error ":"", - (rx_status & RXER) ? "frame_error ":"", - (rx_status & CRC) ? "crc_error ":"" ); - retval = 0; - } else if( !((rx_status & RXFSD) && (rx_status & RXLSD)) ) - { - /* this pkt is too long, over one rx buffer */ - printf("Pkt is too long, over one rx buffer.\n"); - retval = 0; - } else - { /* this received pkt is ok */ - /* Omit the four octet CRC from the length. */ - short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4; - - DBG ( " netdev_rx() normal Rx pkt length %d" - " status %x.\n", pkt_len, (unsigned int) rx_status ); - - nic->packetlen = pkt_len; - memcpy(nic->packet, mtdx.cur_rx->skbuff, pkt_len); - - retval = 1; - } - - while( ( mtdx.cur_rx->status & RXOWN ) == 0 ) - { - mtdx.cur_rx->status = RXOWN; - mtdx.cur_rx = mtdx.cur_rx->next_desc_logical; - } - - /* Restart Rx engine if stopped. */ - outl(0, mtdx.ioaddr + RXPDR); - - return retval; -} - -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void mtd_transmit( - struct nic *nic, - const char *dest, /* Destination */ - unsigned int type, /* Type */ - unsigned int size, /* size */ - const char *data) /* Packet */ -{ - u32 to; - u32 tx_status; - unsigned int nstype = htons ( type ); - - memcpy( txb, dest, ETH_ALEN ); - memcpy( txb + ETH_ALEN, nic->node_addr, ETH_ALEN ); - memcpy( txb + 2 * ETH_ALEN, &nstype, 2 ); - memcpy( txb + ETH_HLEN, data, size ); - - size += ETH_HLEN; - size &= 0x0FFF; - while( size < ETH_ZLEN ) - { - txb[size++] = '\0'; - } - - mtdx.tx_ring[0].control = TXLD | TXFD | CRCEnable | PADEnable; - mtdx.tx_ring[0].control |= (size << PKTSShift); /* pkt size */ - mtdx.tx_ring[0].control |= (size << TBSShift); /* buffer size */ - mtdx.tx_ring[0].status = TXOWN; - - /* Point to transmit descriptor */ - outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA); - /* Enable Tx */ - outl( mtdx.crvalue | TxEnable, mtdx.ioaddr + TCRRCR); - /* Wake the potentially-idle transmit channel. */ - outl(0, mtdx.ioaddr + TXPDR); - - to = currticks() + TX_TIME_OUT; - while(( mtdx.tx_ring[0].status & TXOWN) && (currticks() < to)); - - /* Disable Tx */ - outl( mtdx.crvalue & (~TxEnable), mtdx.ioaddr + TCRRCR); - - tx_status = mtdx.tx_ring[0].status; - if (currticks() >= to){ - DBG ( "TX Time Out" ); - } else if( tx_status & (CSL | LC | EC | UDF | HF)){ - printf( "Transmit error: %8.8x %s %s %s %s %s\n", - (unsigned int) tx_status, - tx_status & EC ? "abort" : "", - tx_status & CSL ? "carrier" : "", - tx_status & LC ? "late" : "", - tx_status & UDF ? "fifo" : "", - tx_status & HF ? "heartbeat" : "" ); - } - - /*hex_dump( txb, size );*/ - /*pause();*/ - - DBG ( "TRANSMIT\n" ); -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void mtd_disable ( struct nic *nic ) { - - /* Disable Tx Rx*/ - outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR ); - - /* Reset the chip to erase previous misconfiguration. */ - mtd_reset(nic); - - DBG ( "DISABLE\n" ); -} - -static struct nic_operations mtd_operations = { - .connect = dummy_connect, - .poll = mtd_poll, - .transmit = mtd_transmit, - .irq = dummy_irq, - -}; - -static struct pci_device_id mtd80x_nics[] = { - PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800", 0), - PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X", 0), - PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891", 0), -}; - -PCI_DRIVER ( mtd80x_driver, mtd80x_nics, PCI_NO_CLASS ); - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -***************************************************************************/ - -static int mtd_probe ( struct nic *nic, struct pci_device *pci ) { - - int i; - - if (pci->ioaddr == 0) - return 0; - - adjust_pci_device(pci); - - nic->ioaddr = pci->ioaddr; - nic->irqno = 0; - - mtdx.nic_name = pci->id->name; - mtdx.dev_id = pci->device; - mtdx.ioaddr = nic->ioaddr; - - /* read ethernet id */ - for (i = 0; i < 6; ++i) - { - nic->node_addr[i] = inb(mtdx.ioaddr + PAR0 + i); - } - - if (memcmp(nic->node_addr, "\0\0\0\0\0\0", 6) == 0) - { - return 0; - } - - DBG ( "%s: ioaddr %4.4x MAC %s\n", mtdx.nic_name, mtdx.ioaddr, eth_ntoa ( nic->node_addr ) ); - - /* Reset the chip to erase previous misconfiguration. */ - outl(0x00000001, mtdx.ioaddr + BCR); - - /* find the connected MII xcvrs */ - - if( mtdx.dev_id != 0x803 ) - { - int phy, phy_idx = 0; - - for (phy = 1; phy < 32 && phy_idx < 1; phy++) { - int mii_status = mdio_read(nic, phy, 1); - - if (mii_status != 0xffff && mii_status != 0x0000) { - mtdx.phys[phy_idx] = phy; - - DBG ( "%s: MII PHY found at address %d, status " - "0x%4.4x.\n", mtdx.nic_name, phy, mii_status ); - /* get phy type */ - { - unsigned int data; - - data = mdio_read(nic, mtdx.phys[phy_idx], 2); - if (data == SeeqPHYID0) - mtdx.PHYType = SeeqPHY; - else if (data == AhdocPHYID0) - mtdx.PHYType = AhdocPHY; - else if (data == MarvellPHYID0) - mtdx.PHYType = MarvellPHY; - else if (data == MysonPHYID0) - mtdx.PHYType = Myson981; - else if (data == LevelOnePHYID0) - mtdx.PHYType = LevelOnePHY; - else - mtdx.PHYType = OtherPHY; - } - phy_idx++; - } - } - - mtdx.mii_cnt = phy_idx; - if (phy_idx == 0) { - printf("%s: MII PHY not found -- this device may " - "not operate correctly.\n", mtdx.nic_name); - } - } else { - mtdx.phys[0] = 32; - /* get phy type */ - if (inl(mtdx.ioaddr + PHYIDENTIFIER) == MysonPHYID ) { - mtdx.PHYType = MysonPHY; - DBG ( "MysonPHY\n" ); - } else { - mtdx.PHYType = OtherPHY; - DBG ( "OtherPHY\n" ); - } - } - - getlinkstatus(nic); - if( !mtdx.linkok ) - { - printf("No link!!!\n"); - return 0; - } - - mtd_reset( nic ); - - /* point to NIC specific routines */ - nic->nic_op = &mtd_operations; - return 1; -} - - -/**************************************************************************/ -static void set_rx_mode(struct nic *nic __unused) -{ - u32 mc_filter[2]; /* Multicast hash filter */ - u32 rx_mode; - - /* Too many to match, or accept all multicasts. */ - mc_filter[1] = mc_filter[0] = ~0; - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - - outl(mc_filter[0], mtdx.ioaddr + MAR0); - outl(mc_filter[1], mtdx.ioaddr + MAR1); - - mtdx.crvalue = ( mtdx.crvalue & ~RxModeMask ) | rx_mode; - outb( mtdx.crvalue, mtdx.ioaddr + TCRRCR); -} -/**************************************************************************/ -static unsigned int m80x_read_tick(void) -/* function: Reads the Timer tick count register which decrements by 2 from */ -/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the */ -/* count represents 838 nsec's. */ -/* input : none. */ -/* output : none. */ -{ - unsigned char tmp; - int value; - - outb((char) 0x06, 0x43); // Command 8254 to latch T0's count - - // now read the count. - tmp = (unsigned char) inb(0x40); - value = ((int) tmp) << 8; - tmp = (unsigned char) inb(0x40); - value |= (((int) tmp) & 0xff); - return (value); -} - -static void m80x_delay(unsigned int interval) -/* function: to wait for a specified time. */ -/* input : interval ... the specified time. */ -/* output : none. */ -{ - unsigned int interval1, interval2, i = 0; - - interval1 = m80x_read_tick(); // get initial value - do - { - interval2 = m80x_read_tick(); - if (interval1 < interval2) - interval1 += 65536; - ++i; - } while (((interval1 - interval2) < (u16) interval) && (i < 65535)); -} - - -static u32 m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad) -{ - u32 miir; - int i; - unsigned int mask, data; - - /* enable MII output */ - miir = (u32) inl(miiport); - miir &= 0xfffffff0; - - miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO; - - /* send 32 1's preamble */ - for (i = 0; i < 32; i++) { - /* low MDC; MDO is already high (miir) */ - miir &= ~MASK_MIIR_MII_MDC; - outl(miir, miiport); - - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - outl(miir, miiport); - } - - /* calculate ST+OP+PHYAD+REGAD+TA */ - data = opcode | (phyad << 7) | (regad << 2); - - /* sent out */ - mask = 0x8000; - while (mask) { - /* low MDC, prepare MDO */ - miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); - if (mask & data) - miir |= MASK_MIIR_MII_MDO; - - outl(miir, miiport); - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - outl(miir, miiport); - m80x_delay(30); - - /* next */ - mask >>= 1; - if (mask == 0x2 && opcode == OP_READ) - miir &= ~MASK_MIIR_MII_WRITE; - } - return miir; -} - -static int mdio_read(struct nic *nic __unused, int phyad, int regad) -{ - long miiport = mtdx.ioaddr + MANAGEMENT; - u32 miir; - unsigned int mask, data; - - miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad); - - /* read data */ - mask = 0x8000; - data = 0; - while (mask) - { - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - outl(miir, miiport); - - /* read MDI */ - miir = inl(miiport); - if (miir & MASK_MIIR_MII_MDI) - data |= mask; - - /* high MDC, and wait */ - miir |= MASK_MIIR_MII_MDC; - outl(miir, miiport); - m80x_delay((int) 30); - - /* next */ - mask >>= 1; - } - - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - outl(miir, miiport); - - return data & 0xffff; -} - -#if 0 /* not used */ -static void mdio_write(struct nic *nic __unused, int phyad, int regad, - int data) -{ - long miiport = mtdx.ioaddr + MANAGEMENT; - u32 miir; - unsigned int mask; - - miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad); - - /* write data */ - mask = 0x8000; - while (mask) - { - /* low MDC, prepare MDO */ - miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); - if (mask & data) - miir |= MASK_MIIR_MII_MDO; - outl(miir, miiport); - - /* high MDC */ - miir |= MASK_MIIR_MII_MDC; - outl(miir, miiport); - - /* next */ - mask >>= 1; - } - - /* low MDC */ - miir &= ~MASK_MIIR_MII_MDC; - outl(miir, miiport); - - return; -} -#endif - -static void getlinkstatus(struct nic *nic) -/* function: Routine will read MII Status Register to get link status. */ -/* input : dev... pointer to the adapter block. */ -/* output : none. */ -{ - unsigned int i, DelayTime = 0x1000; - - mtdx.linkok = 0; - - if (mtdx.PHYType == MysonPHY) - { - for (i = 0; i < DelayTime; ++i) { - if (inl(mtdx.ioaddr + BMCRSR) & LinkIsUp2) { - mtdx.linkok = 1; - return; - } - // delay - m80x_delay(100); - } - } else - { - for (i = 0; i < DelayTime; ++i) { - if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) { - mtdx.linkok = 1; - return; - } - // delay - m80x_delay(100); - } - } -} - - -static void getlinktype(struct nic *dev) -{ - if (mtdx.PHYType == MysonPHY) - { /* 3-in-1 case */ - if (inl(mtdx.ioaddr + TCRRCR) & FD) - mtdx.duplexmode = 2; /* full duplex */ - else - mtdx.duplexmode = 1; /* half duplex */ - if (inl(mtdx.ioaddr + TCRRCR) & PS10) - mtdx.line_speed = 1; /* 10M */ - else - mtdx.line_speed = 2; /* 100M */ - } else - { - if (mtdx.PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */ - unsigned int data; - - data = mdio_read(dev, mtdx.phys[0], MIIRegister18); - if (data & SPD_DET_100) - mtdx.line_speed = 2; /* 100M */ - else - mtdx.line_speed = 1; /* 10M */ - if (data & DPLX_DET_FULL) - mtdx.duplexmode = 2; /* full duplex mode */ - else - mtdx.duplexmode = 1; /* half duplex mode */ - } else if (mtdx.PHYType == AhdocPHY) { - unsigned int data; - - data = mdio_read(dev, mtdx.phys[0], DiagnosticReg); - if (data & Speed_100) - mtdx.line_speed = 2; /* 100M */ - else - mtdx.line_speed = 1; /* 10M */ - if (data & DPLX_FULL) - mtdx.duplexmode = 2; /* full duplex mode */ - else - mtdx.duplexmode = 1; /* half duplex mode */ - } - /* 89/6/13 add, (begin) */ - else if (mtdx.PHYType == MarvellPHY) { - unsigned int data; - - data = mdio_read(dev, mtdx.phys[0], SpecificReg); - if (data & Full_Duplex) - mtdx.duplexmode = 2; /* full duplex mode */ - else - mtdx.duplexmode = 1; /* half duplex mode */ - data &= SpeedMask; - if (data == Speed_1000M) - mtdx.line_speed = 3; /* 1000M */ - else if (data == Speed_100M) - mtdx.line_speed = 2; /* 100M */ - else - mtdx.line_speed = 1; /* 10M */ - } - /* 89/6/13 add, (end) */ - /* 89/7/27 add, (begin) */ - else if (mtdx.PHYType == Myson981) { - unsigned int data; - - data = mdio_read(dev, mtdx.phys[0], StatusRegister); - - if (data & SPEED100) - mtdx.line_speed = 2; - else - mtdx.line_speed = 1; - - if (data & FULLMODE) - mtdx.duplexmode = 2; - else - mtdx.duplexmode = 1; - } - /* 89/7/27 add, (end) */ - /* 89/12/29 add */ - else if (mtdx.PHYType == LevelOnePHY) { - unsigned int data; - - data = mdio_read(dev, mtdx.phys[0], SpecificReg); - if (data & LXT1000_Full) - mtdx.duplexmode = 2; /* full duplex mode */ - else - mtdx.duplexmode = 1; /* half duplex mode */ - data &= SpeedMask; - if (data == LXT1000_1000M) - mtdx.line_speed = 3; /* 1000M */ - else if (data == LXT1000_100M) - mtdx.line_speed = 2; /* 100M */ - else - mtdx.line_speed = 1; /* 10M */ - } - // chage crvalue - // mtdx.crvalue&=(~PS10)&(~FD); - mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000); - if (mtdx.line_speed == 1) - mtdx.crvalue |= PS10; - else if (mtdx.line_speed == 3) - mtdx.crvalue |= PS1000; - if (mtdx.duplexmode == 2) - mtdx.crvalue |= FD; - } -} - -DRIVER ( "MTD80X", nic_driver, pci_driver, mtd80x_driver, - mtd_probe, mtd_disable ); diff --git a/src/drivers/net/myson.c b/src/drivers/net/myson.c new file mode 100644 index 0000000..25f1a27 --- /dev/null +++ b/src/drivers/net/myson.c @@ -0,0 +1,668 @@ +/* + * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/iobuf.h> +#include <ipxe/malloc.h> +#include <ipxe/pci.h> +#include <ipxe/mii.h> +#include "myson.h" + +/** @file + * + * Myson Technology network card driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset controller chip + * + * @v myson Myson device + * @ret rc Return status code + */ +static int myson_soft_reset ( struct myson_nic *myson ) { + uint32_t bcr; + unsigned int i; + + /* Initiate reset */ + bcr = readl ( myson->regs + MYSON_BCR ); + writel ( ( bcr | MYSON_BCR_SWR ), myson->regs + MYSON_BCR ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < MYSON_RESET_MAX_WAIT_MS ; i++ ) { + + /* If reset is not complete, delay 1ms and retry */ + if ( readl ( myson->regs + MYSON_BCR ) & MYSON_BCR_SWR ) { + mdelay ( 1 ); + continue; + } + + /* Apply a sensible default bus configuration */ + bcr = readl ( myson->regs + MYSON_BCR ); + bcr &= ~MYSON_BCR_PBL_MASK; + bcr |= ( MYSON_BCR_RLE | MYSON_BCR_RME | MYSON_BCR_WIE | + MYSON_BCR_PBL_DEFAULT ); + writel ( bcr, myson->regs + MYSON_BCR ); + DBGC ( myson, "MYSON %p using configuration %08x\n", + myson, bcr ); + + return 0; + } + + DBGC ( myson, "MYSON %p timed out waiting for reset\n", myson ); + return -ETIMEDOUT; +} + +/** + * Reload configuration from EEPROM + * + * @v myson Myson device + * @ret rc Return status code + */ +static int myson_reload_config ( struct myson_nic *myson ) { + unsigned int i; + + /* Initiate reload */ + writel ( MYSON_ROM_AUTOLD, myson->regs + MYSON_ROM_MII ); + + /* Wait for reload to complete */ + for ( i = 0 ; i < MYSON_AUTOLD_MAX_WAIT_MS ; i++ ) { + + /* If reload is not complete, delay 1ms and retry */ + if ( readl ( myson->regs + MYSON_ROM_MII ) & MYSON_ROM_AUTOLD ){ + mdelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( myson, "MYSON %p timed out waiting for configuration " + "reload\n", myson ); + return -ETIMEDOUT; +} + +/** + * Reset hardware + * + * @v myson Myson device + * @ret rc Return status code + */ +static int myson_reset ( struct myson_nic *myson ) { + int rc; + + /* Disable all interrupts */ + writel ( 0, myson->regs + MYSON_IMR ); + + /* Perform soft reset */ + if ( ( rc = myson_soft_reset ( myson ) ) != 0 ) + return rc; + + /* Reload configuration from EEPROM */ + if ( ( rc = myson_reload_config ( myson ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v myson Myson device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int myson_create_ring ( struct myson_nic *myson, + struct myson_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + struct myson_descriptor *desc; + struct myson_descriptor *next; + physaddr_t address; + unsigned int i; + int rc; + + /* Allocate descriptor ring */ + ring->desc = malloc_dma ( len, MYSON_RING_ALIGN ); + if ( ! ring->desc ) { + rc = -ENOMEM; + goto err_alloc; + } + address = virt_to_bus ( ring->desc ); + + /* Check address is usable by card */ + if ( ! myson_address_ok ( address + len ) ) { + DBGC ( myson, "MYSON %p cannot support 64-bit ring address\n", + myson ); + rc = -ENOTSUP; + goto err_64bit; + } + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + desc = &ring->desc[i]; + next = &ring->desc[ ( i + 1 ) % ring->count ]; + desc->next = cpu_to_le32 ( virt_to_bus ( next ) ); + } + + /* Program ring address */ + writel ( address, myson->regs + ring->reg ); + DBGC ( myson, "MYSON %p ring %02x is at [%08llx,%08llx)\n", + myson, ring->reg, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + + return 0; + + err_64bit: + free_dma ( ring->desc, len ); + ring->desc = NULL; + err_alloc: + return rc; +} + +/** + * Destroy descriptor ring + * + * @v myson Myson device + * @v ring Descriptor ring + */ +static void myson_destroy_ring ( struct myson_nic *myson, + struct myson_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writel ( 0, myson->regs + ring->reg ); + + /* Free descriptor ring */ + free_dma ( ring->desc, len ); + ring->desc = NULL; + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill receive descriptor ring + * + * @v netdev Network device + */ +static void myson_refill_rx ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + struct myson_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + + while ( ( myson->rx.prod - myson->rx.cons ) < MYSON_NUM_RX_DESC ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( MYSON_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + return; + } + + /* Check address is usable by card */ + address = virt_to_bus ( iobuf->data ); + if ( ! myson_address_ok ( address ) ) { + DBGC ( myson, "MYSON %p cannot support 64-bit RX " + "buffer address\n", myson ); + netdev_rx_err ( netdev, iobuf, -ENOTSUP ); + return; + } + + /* Get next receive descriptor */ + rx_idx = ( myson->rx.prod++ % MYSON_NUM_RX_DESC ); + rx = &myson->rx.desc[rx_idx]; + + /* Populate receive descriptor */ + rx->address = cpu_to_le32 ( address ); + rx->control = + cpu_to_le32 ( MYSON_RX_CTRL_RBS ( MYSON_RX_MAX_LEN ) ); + wmb(); + rx->status = cpu_to_le32 ( MYSON_RX_STAT_OWN ); + wmb(); + + /* Record I/O buffer */ + assert ( myson->rx_iobuf[rx_idx] == NULL ); + myson->rx_iobuf[rx_idx] = iobuf; + + /* Notify card that there are descriptors available */ + writel ( 0, myson->regs + MYSON_RXPDR ); + + DBGC2 ( myson, "MYSON %p RX %d is [%llx,%llx)\n", myson, + rx_idx, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + MYSON_RX_MAX_LEN ) ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int myson_open ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + union myson_physical_address mac; + int rc; + + /* Set MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + writel ( le32_to_cpu ( mac.reg.low ), myson->regs + MYSON_PAR0 ); + writel ( le32_to_cpu ( mac.reg.high ), myson->regs + MYSON_PAR4 ); + + /* Create transmit descriptor ring */ + if ( ( rc = myson_create_ring ( myson, &myson->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = myson_create_ring ( myson, &myson->rx ) ) != 0 ) + goto err_create_rx; + + /* Configure transmitter and receiver */ + writel ( ( MYSON_TCR_TE | MYSON_RCR_PROM | MYSON_RCR_AB | MYSON_RCR_AM | + MYSON_RCR_ARP | MYSON_RCR_ALP | MYSON_RCR_RE ), + myson->regs + MYSON_TCR_RCR ); + + /* Fill receive ring */ + myson_refill_rx ( netdev ); + + return 0; + + myson_destroy_ring ( myson, &myson->rx ); + err_create_rx: + myson_destroy_ring ( myson, &myson->tx ); + err_create_tx: + return rc; +} + +/** + * Wait for transmit and receive to become idle + * + * @v myson Myson device + * @ret rc Return status code + */ +static int myson_wait_idle ( struct myson_nic *myson ) { + uint32_t tcr_rcr; + unsigned int i; + + /* Wait for both transmit and receive to be idle */ + for ( i = 0 ; i < MYSON_IDLE_MAX_WAIT_MS ; i++ ) { + + /* If either process is running, delay 1ms and retry */ + tcr_rcr = readl ( myson->regs + MYSON_TCR_RCR ); + if ( tcr_rcr & ( MYSON_TCR_TXS | MYSON_RCR_RXS ) ) { + mdelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( myson, "MYSON %p timed out waiting for idle state (status " + "%08x)\n", myson, tcr_rcr ); + return -ETIMEDOUT; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void myson_close ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + unsigned int i; + + /* Disable receiver and transmitter */ + writel ( 0, myson->regs + MYSON_TCR_RCR ); + + /* Allow time for receiver and transmitter to become idle */ + myson_wait_idle ( myson ); + + /* Destroy receive descriptor ring */ + myson_destroy_ring ( myson, &myson->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < MYSON_NUM_RX_DESC ; i++ ) { + if ( myson->rx_iobuf[i] ) + free_iob ( myson->rx_iobuf[i] ); + myson->rx_iobuf[i] = NULL; + } + + /* Destroy transmit descriptor ring */ + myson_destroy_ring ( myson, &myson->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int myson_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct myson_nic *myson = netdev->priv; + struct myson_descriptor *tx; + unsigned int tx_idx; + physaddr_t address; + + /* Check address is usable by card */ + address = virt_to_bus ( iobuf->data ); + if ( ! myson_address_ok ( address ) ) { + DBGC ( myson, "MYSON %p cannot support 64-bit TX buffer " + "address\n", myson ); + return -ENOTSUP; + } + + /* Get next transmit descriptor */ + if ( ( myson->tx.prod - myson->tx.cons ) >= MYSON_NUM_TX_DESC ) { + DBGC ( myson, "MYSON %p out of transmit descriptors\n", + myson ); + return -ENOBUFS; + } + tx_idx = ( myson->tx.prod++ % MYSON_NUM_TX_DESC ); + tx = &myson->tx.desc[tx_idx]; + + /* Populate transmit descriptor */ + tx->address = cpu_to_le32 ( address ); + tx->control = cpu_to_le32 ( MYSON_TX_CTRL_IC | MYSON_TX_CTRL_LD | + MYSON_TX_CTRL_FD | MYSON_TX_CTRL_CRC | + MYSON_TX_CTRL_PAD | MYSON_TX_CTRL_RTLC | + MYSON_TX_CTRL_PKTS ( iob_len ( iobuf ) ) | + MYSON_TX_CTRL_TBS ( iob_len ( iobuf ) ) ); + wmb(); + tx->status = cpu_to_le32 ( MYSON_TX_STAT_OWN ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writel ( 0, myson->regs + MYSON_TXPDR ); + + DBGC2 ( myson, "MYSON %p TX %d is [%llx,%llx)\n", myson, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void myson_poll_tx ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + struct myson_descriptor *tx; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( myson->tx.cons != myson->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( myson->tx.cons % MYSON_NUM_TX_DESC ); + tx = &myson->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( tx->status & cpu_to_le32 ( MYSON_TX_STAT_OWN ) ) + return; + + /* Complete TX descriptor */ + if ( tx->status & cpu_to_le32 ( MYSON_TX_STAT_ABORT | + MYSON_TX_STAT_CSL ) ) { + DBGC ( myson, "MYSON %p TX %d completion error " + "(%08x)\n", myson, tx_idx, + le32_to_cpu ( tx->status ) ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } else { + DBGC2 ( myson, "MYSON %p TX %d complete\n", + myson, tx_idx ); + netdev_tx_complete_next ( netdev ); + } + myson->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void myson_poll_rx ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + struct myson_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( myson->rx.cons != myson->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( myson->rx.cons % MYSON_NUM_RX_DESC ); + rx = &myson->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( rx->status & MYSON_RX_STAT_OWN ) + return; + + /* Populate I/O buffer */ + iobuf = myson->rx_iobuf[rx_idx]; + myson->rx_iobuf[rx_idx] = NULL; + len = MYSON_RX_STAT_FLNG ( le32_to_cpu ( rx->status ) ); + iob_put ( iobuf, len - 4 /* strip CRC */ ); + + /* Hand off to network stack */ + if ( rx->status & cpu_to_le32 ( MYSON_RX_STAT_ES ) ) { + DBGC ( myson, "MYSON %p RX %d error (length %zd, " + "status %08x)\n", myson, rx_idx, len, + le32_to_cpu ( rx->status ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( myson, "MYSON %p RX %d complete (length " + "%zd)\n", myson, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } + myson->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void myson_poll ( struct net_device *netdev ) { + struct myson_nic *myson = netdev->priv; + uint32_t isr; + unsigned int i; + + /* Polling the ISR seems to really upset this card; it ends up + * getting no useful PCI transfers done and, for some reason, + * flooding the network with invalid packets. Work around + * this by introducing deliberate delays between ISR reads. + */ + for ( i = 0 ; i < MYSON_ISR_IODELAY_COUNT ; i++ ) + iodelay(); + + /* Check for and acknowledge interrupts */ + isr = readl ( myson->regs + MYSON_ISR ); + if ( ! isr ) + return; + writel ( isr, myson->regs + MYSON_ISR ); + + /* Poll for TX completions, if applicable */ + if ( isr & MYSON_IRQ_TI ) + myson_poll_tx ( netdev ); + + /* Poll for RX completionsm, if applicable */ + if ( isr & MYSON_IRQ_RI ) + myson_poll_rx ( netdev ); + + /* Refill RX ring */ + myson_refill_rx ( netdev ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void myson_irq ( struct net_device *netdev, int enable ) { + struct myson_nic *myson = netdev->priv; + uint32_t imr; + + imr = ( enable ? ( MYSON_IRQ_TI | MYSON_IRQ_RI ) : 0 ); + writel ( imr, myson->regs + MYSON_IMR ); +} + +/** Myson network device operations */ +static struct net_device_operations myson_operations = { + .open = myson_open, + .close = myson_close, + .transmit = myson_transmit, + .poll = myson_poll, + .irq = myson_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int myson_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct myson_nic *myson; + union myson_physical_address mac; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *myson ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &myson_operations ); + myson = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( myson, 0, sizeof ( *myson ) ); + myson_init_ring ( &myson->tx, MYSON_NUM_TX_DESC, MYSON_TXLBA ); + myson_init_ring ( &myson->rx, MYSON_NUM_RX_DESC, MYSON_RXLBA ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + myson->regs = ioremap ( pci->membase, MYSON_BAR_SIZE ); + + /* Reset the NIC */ + if ( ( rc = myson_reset ( myson ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + mac.reg.low = cpu_to_le32 ( readl ( myson->regs + MYSON_PAR0 ) ); + mac.reg.high = cpu_to_le32 ( readl ( myson->regs + MYSON_PAR4 ) ); + memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + myson_reset ( myson ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void myson_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct myson_nic *myson = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + myson_reset ( myson ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Myson PCI device IDs */ +static struct pci_device_id myson_nics[] = { + PCI_ROM ( 0x1516, 0x0800, "mtd800", "MTD-8xx", 0 ), + PCI_ROM ( 0x1516, 0x0803, "mtd803", "Surecom EP-320X-S", 0 ), + PCI_ROM ( 0x1516, 0x0891, "mtd891", "MTD-8xx", 0 ), +}; + +/** Myson PCI driver */ +struct pci_driver myson_driver __pci_driver = { + .ids = myson_nics, + .id_count = ( sizeof ( myson_nics ) / sizeof ( myson_nics[0] ) ), + .probe = myson_probe, + .remove = myson_remove, +}; diff --git a/src/drivers/net/myson.h b/src/drivers/net/myson.h new file mode 100644 index 0000000..8d7cc58 --- /dev/null +++ b/src/drivers/net/myson.h @@ -0,0 +1,200 @@ +#ifndef _MYSON_H +#define _MYSON_H + +/** @file + * + * Myson Technology network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <ipxe/if_ether.h> + +/** BAR size */ +#define MYSON_BAR_SIZE 256 + +/** A packet descriptor */ +struct myson_descriptor { + /** Status */ + uint32_t status; + /** Control */ + uint32_t control; + /** Buffer start address */ + uint32_t address; + /** Next descriptor address */ + uint32_t next; +} __attribute__ (( packed )); + +/* Transmit status */ +#define MYSON_TX_STAT_OWN 0x80000000UL /**< Owner */ +#define MYSON_TX_STAT_ABORT 0x00002000UL /**< Abort */ +#define MYSON_TX_STAT_CSL 0x00001000UL /**< Carrier sense lost */ + +/* Transmit control */ +#define MYSON_TX_CTRL_IC 0x80000000UL /**< Interrupt control */ +#define MYSON_TX_CTRL_LD 0x20000000UL /**< Last descriptor */ +#define MYSON_TX_CTRL_FD 0x10000000UL /**< First descriptor */ +#define MYSON_TX_CTRL_CRC 0x08000000UL /**< CRC append */ +#define MYSON_TX_CTRL_PAD 0x04000000UL /**< Pad control */ +#define MYSON_TX_CTRL_RTLC 0x02000000UL /**< Retry late collision */ +#define MYSON_TX_CTRL_PKTS(x) ( (x) << 11 ) /**< Packet size */ +#define MYSON_TX_CTRL_TBS(x) ( (x) << 0 ) /**< Transmit buffer size */ + +/* Receive status */ +#define MYSON_RX_STAT_OWN 0x80000000UL /**< Owner */ +#define MYSON_RX_STAT_FLNG(status) ( ( (status) >> 16 ) & 0xfff ) +#define MYSON_RX_STAT_ES 0x00000080UL /**< Error summary */ + +/* Receive control */ +#define MYSON_RX_CTRL_RBS(x) ( (x) << 0 ) /**< Receive buffer size */ + +/** Descriptor ring alignment */ +#define MYSON_RING_ALIGN 4 + +/** Physical Address Register 0 */ +#define MYSON_PAR0 0x00 + +/** Physical Address Register 4 */ +#define MYSON_PAR4 0x04 + +/** Physical address */ +union myson_physical_address { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )) reg; + uint8_t raw[ETH_ALEN]; +}; + +/** Transmit and Receive Configuration Register */ +#define MYSON_TCR_RCR 0x18 +#define MYSON_TCR_TXS 0x80000000UL /**< Transmit status */ +#define MYSON_TCR_TE 0x00040000UL /**< Transmit enable */ +#define MYSON_RCR_RXS 0x00008000UL /**< Receive status */ +#define MYSON_RCR_PROM 0x00000080UL /**< Promiscuous mode */ +#define MYSON_RCR_AB 0x00000040UL /**< Accept broadcast */ +#define MYSON_RCR_AM 0x00000020UL /**< Accept multicast */ +#define MYSON_RCR_ARP 0x00000008UL /**< Accept runt packet */ +#define MYSON_RCR_ALP 0x00000004UL /**< Accept long packet */ +#define MYSON_RCR_RE 0x00000001UL /**< Receive enable */ + +/** Maximum time to wait for transmit and receive to be idle, in milliseconds */ +#define MYSON_IDLE_MAX_WAIT_MS 100 + +/** Bus Command Register */ +#define MYSON_BCR 0x1c +#define MYSON_BCR_RLE 0x00000100UL /**< Read line enable */ +#define MYSON_BCR_RME 0x00000080UL /**< Read multiple enable */ +#define MYSON_BCR_WIE 0x00000040UL /**< Write and invalidate */ +#define MYSON_BCR_PBL(x) ( (x) << 3 ) /**< Burst length */ +#define MYSON_BCR_PBL_MASK MYSON_BCR_PBL ( 0x7 ) +#define MYSON_BCR_PBL_DEFAULT MYSON_BCR_PBL ( 0x6 ) +#define MYSON_BCR_SWR 0x00000001UL /**< Software reset */ + +/** Maximum time to wait for a reset, in milliseconds */ +#define MYSON_RESET_MAX_WAIT_MS 100 + +/** Transmit Poll Demand Register */ +#define MYSON_TXPDR 0x20 + +/** Receive Poll Demand Register */ +#define MYSON_RXPDR 0x24 + +/** Transmit List Base Address */ +#define MYSON_TXLBA 0x2c + +/** Number of transmit descriptors */ +#define MYSON_NUM_TX_DESC 4 + +/** Receive List Base Address */ +#define MYSON_RXLBA 0x30 + +/** Number of receive descriptors */ +#define MYSON_NUM_RX_DESC 4 + +/** Receive buffer length */ +#define MYSON_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) + +/** Interrupt Status Register */ +#define MYSON_ISR 0x34 +#define MYSON_IRQ_TI 0x00000008UL /**< Transmit interrupt */ +#define MYSON_IRQ_RI 0x00000004UL /**< Receive interrupt */ + +/** Number of I/O delays between ISR reads */ +#define MYSON_ISR_IODELAY_COUNT 4 + +/** Interrupt Mask Register */ +#define MYSON_IMR 0x38 + +/** Boot ROM / EEPROM / MII Management Register */ +#define MYSON_ROM_MII 0x40 +#define MYSON_ROM_AUTOLD 0x00100000UL /**< Auto load */ + +/** Maximum time to wait for a configuration reload, in milliseconds */ +#define MYSON_AUTOLD_MAX_WAIT_MS 100 + +/** A Myson descriptor ring */ +struct myson_ring { + /** Descriptors */ + struct myson_descriptor *desc; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Descriptor start address register */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v reg Descriptor base address register + */ +static inline __attribute__ (( always_inline)) void +myson_init_ring ( struct myson_ring *ring, unsigned int count, + unsigned int reg ) { + ring->count = count; + ring->reg = reg; +} + +/** A myson network card */ +struct myson_nic { + /** Registers */ + void *regs; + + /** Transmit descriptor ring */ + struct myson_ring tx; + /** Receive descriptor ring */ + struct myson_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[MYSON_NUM_RX_DESC]; +}; + +/** + * Check if card can access physical address + * + * @v address Physical address + * @v address_ok Card can access physical address + */ +static inline __attribute__ (( always_inline )) int +myson_address_ok ( physaddr_t address ) { + + /* In a 32-bit build, all addresses can be accessed */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 1; + + /* Card can access all addresses below 4GB */ + if ( ( address & ~0xffffffffULL ) == 0 ) + return 1; + + return 0; +} + +#endif /* _MYSON_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 471ba77..7de833d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -145,6 +145,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_realtek ( ERRFILE_DRIVER | 0x00630000 ) #define ERRFILE_skeleton ( ERRFILE_DRIVER | 0x00640000 ) #define ERRFILE_intel ( ERRFILE_DRIVER | 0x00650000 ) +#define ERRFILE_myson ( ERRFILE_DRIVER | 0x00660000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) |