diff options
author | Juergen Beisert <j.beisert@pengutronix.de> | 2009-06-23 16:23:37 +0200 |
---|---|---|
committer | Juergen Beisert <j.beisert@pengutronix.de> | 2009-07-01 14:36:40 +0200 |
commit | bf9679991a8af00adbb2107c727423e571ea5590 (patch) | |
tree | 0b79b42fdadee64b8f338df35f69abad038facb2 /drivers/net | |
parent | 5139e618b8d732bfbea69d9a9bdf83832de30472 (diff) | |
download | barebox-bf9679991a8af00adbb2107c727423e571ea5590.tar.gz barebox-bf9679991a8af00adbb2107c727423e571ea5590.tar.xz |
Adding the network driver for the SMSC91C111 ethernet device.
Signed-off-by: Juergen Beisert <j.beisert@pengutronix.de>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/smc91111.c | 1451 |
3 files changed, 1459 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c080eedbab..f48cd01054 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -33,6 +33,13 @@ config DRIVER_NET_SMC911X_ADDRESS_SHIFT default 1 if MACH_IPE337 default 0 +config DRIVER_NET_SMC91111 + bool "smc91111 ethernet driver" + select MIIPHY + help + This option enables support for the SMSC LAN91C111 + ethernet chip. + config DRIVER_NET_DM9000 bool "Davicom dm9000 ethernet driver" depends on HAS_DM9000 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a5895c36dd..6751920470 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o +obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o obj-$(CONFIG_DRIVER_NET_DM9000) += dm9000.o obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c new file mode 100644 index 0000000000..4375d34afd --- /dev/null +++ b/drivers/net/smc91111.c @@ -0,0 +1,1451 @@ +/*------------------------------------------------------------------------ + . smc91111.c + . This is a driver for SMSC's 91C111 single-chip Ethernet device. + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH <www.elinos.com> + . Rolf Offermanns <rof@sysgo.de> + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . + . "Features" of the SMC chip: + . Integrated PHY/MAC for 10/100BaseT Operation + . Supports internal and external MII + . Integrated 8K packet memory + . EEPROM interface for configuration + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMSC LAN91C111 databook (www.smsc.com) + . o smc9194.c by Erik Stahlman + . o skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + . + . History: + . 06/19/03 Richard Woodruff Made u-boot environment aware and added mac addr checks. + . 10/17/01 Marco Hasewinkel Modify for DNP/1110 + . 07/25/01 Woojung Huh Modify for ADS Bitsy + . 04/25/01 Daris A Nevil Initial public release through SMSC + . 03/16/01 Daris A Nevil Modified smc9194.c for use with LAN91C111 + ----------------------------------------------------------------------------*/ + +#ifdef CONFIG_ENABLE_DEVICE_NOISE +# define DEBUG +#endif + +#include <common.h> + +#include <command.h> +#include <net.h> +#include <miiphy.h> +#include <malloc.h> +#include <init.h> +#include <xfuncs.h> +#include <errno.h> +#include <clock.h> +#include <asm/io.h> + +/*--------------------------------------------------------------- + . + . A description of the SMSC registers is probably in order here, + . although for details, the SMC datasheet is invaluable. + . + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + . + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks. + -----------------------------------------------------------------------*/ + +/* + * Bank Select Register: + * yyyy yyyy 0000 00xx + * xx = bank number + * yyyy yyyy = 0x33, for identification purposes. +*/ + +#define BANK_SELECT 14 + +/* Transmit Control Register */ +/* BANK 0 */ +#define TCR_REG 0x0000 /* transmit control register */ +#define TCR_ENABLE 0x0001 /* When 1 we can transmit */ +#define TCR_LOOP 0x0002 /* Controls output pin LBK */ +#define TCR_FORCOL 0x0004 /* When 1 will force a collision */ +#define TCR_PAD_EN 0x0080 /* When 1 will pad tx frames < 64 bytes w/0 */ +#define TCR_NOCRC 0x0100 /* When 1 will not append CRC to tx frames */ +#define TCR_MON_CSN 0x0400 /* When 1 tx monitors carrier */ +#define TCR_FDUPLX 0x0800 /* When 1 enables full duplex operation */ +#define TCR_STP_SQET 0x1000 /* When 1 stops tx if Signal Quality Error */ +#define TCR_EPH_LOOP 0x2000 /* When 1 enables EPH block loopback */ +#define TCR_SWFDUP 0x8000 /* When 1 enables Switched Full Duplex mode */ + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the default settings for the TCR register : */ +/* QUESTION: do I want to enable padding of short packets ? */ +#define TCR_DEFAULT TCR_ENABLE + +/* EPH Status Register */ +/* BANK 0 */ +#define EPH_STATUS_REG 0x0002 +#define ES_TX_SUC 0x0001 /* Last TX was successful */ +#define ES_SNGL_COL 0x0002 /* Single collision detected for last tx */ +#define ES_MUL_COL 0x0004 /* Multiple collisions detected for last tx */ +#define ES_LTX_MULT 0x0008 /* Last tx was a multicast */ +#define ES_16COL 0x0010 /* 16 Collisions Reached */ +#define ES_SQET 0x0020 /* Signal Quality Error Test */ +#define ES_LTXBRD 0x0040 /* Last tx was a broadcast */ +#define ES_TXDEFR 0x0080 /* Transmit Deferred */ +#define ES_LATCOL 0x0200 /* Late collision detected on last tx */ +#define ES_LOSTCARR 0x0400 /* Lost Carrier Sense */ +#define ES_EXC_DEF 0x0800 /* Excessive Deferral */ +#define ES_CTR_ROL 0x1000 /* Counter Roll Over indication */ +#define ES_LINK_OK 0x4000 /* Driven by inverted value of nLNK pin */ +#define ES_TXUNRN 0x8000 /* Tx Underrun */ + + +/* Receive Control Register */ +/* BANK 0 */ +#define RCR_REG 0x0004 +#define RCR_RX_ABORT 0x0001 /* Set if a rx frame was aborted */ +#define RCR_PRMS 0x0002 /* Enable promiscuous mode */ +#define RCR_ALMUL 0x0004 /* When set accepts all multicast frames */ +#define RCR_RXEN 0x0100 /* IFF this is set, we can receive packets */ +#define RCR_STRIP_CRC 0x0200 /* When set strips CRC from rx packets */ +#define RCR_ABORT_ENB 0x0200 /* When set will abort rx on collision */ +#define RCR_FILT_CAR 0x0400 /* When set filters leading 12 bit s of carrier */ +#define RCR_SOFTRST 0x8000 /* resets the chip */ + +/* the normal settings for the RCR register : */ +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 /* set it to a base state */ + +/* Counter Register */ +/* BANK 0 */ +#define COUNTER_REG 0x0006 + +/* Memory Information Register */ +/* BANK 0 */ +#define MIR_REG 0x0008 + +/* Receive/Phy Control Register */ +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 /* When 1 PHY is in 100Mbps mode. */ +#define RPC_DPLX 0x1000 /* When 1 PHY is in Full-Duplex Mode */ +#define RPC_ANEG 0x0800 /* When 1 PHY is in Auto-Negotiate Mode */ +#define RPC_LSXA_SHFT 5 /* Bits to shift LS2A,LS1A,LS0A to lsb */ +#define RPC_LSXB_SHFT 2 /* Bits to get LS2B,LS1B,LS0B to lsb */ +#define RPC_LED_100_10 (0x00) /* LED = 100Mbps OR's with 10Mbps link detect */ +#define RPC_LED_RES (0x01) /* LED = Reserved */ +#define RPC_LED_10 (0x02) /* LED = 10Mbps link detect */ +#define RPC_LED_FD (0x03) /* LED = Full Duplex Mode */ +#define RPC_LED_TX_RX (0x04) /* LED = TX or RX packet occurred */ +#define RPC_LED_100 (0x05) /* LED = 100Mbps link dectect */ +#define RPC_LED_TX (0x06) /* LED = TX packet occurred */ +#define RPC_LED_RX (0x07) /* LED = RX packet occurred */ + +/* SMSC reference design: LEDa --> green, LEDb --> yellow */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_100_10 << RPC_LSXA_SHFT) \ + | (RPC_LED_TX_RX << RPC_LSXB_SHFT) ) + +/* Bank 0 0x000C is reserved */ + +/* Bank Select Register */ +/* All Banks */ +#define BSR_REG 0x000E + +/* Configuration Reg */ +/* BANK 1 */ +#define CONFIG_REG 0x0000 +#define CONFIG_EXT_PHY 0x0200 /* 1=external MII, 0=internal Phy */ +#define CONFIG_GPCNTRL 0x0400 /* Inverse value drives pin nCNTRL */ +#define CONFIG_NO_WAIT 0x1000 /* When 1 no extra wait states on ISA bus */ +#define CONFIG_EPH_POWER_EN 0x8000 /* When 0 EPH is placed into low power mode. */ + +/* Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low */ +#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN) + +/* Base Address Register */ +/* BANK 1 */ +#define BASE_REG 0x0002 + +/* Individual Address Registers */ +/* BANK 1 */ +#define ADDR0_REG 0x0004 +#define ADDR1_REG 0x0006 +#define ADDR2_REG 0x0008 + +/* General Purpose Register */ +/* BANK 1 */ +#define GP_REG 0x000A + +/* Control Register */ +/* BANK 1 */ +#define CTL_REG 0x000C +#define CTL_RCV_BAD 0x4000 /* When 1 bad CRC packets are received */ +#define CTL_AUTO_RELEASE 0x0800 /* When 1 tx pages are released automatically */ +#define CTL_LE_ENABLE 0x0080 /* When 1 enables Link Error interrupt */ +#define CTL_CR_ENABLE 0x0040 /* When 1 enables Counter Rollover interrupt */ +#define CTL_TE_ENABLE 0x0020 /* When 1 enables Transmit Error interrupt */ +#define CTL_EEPROM_SELECT 0x0004 /* Controls EEPROM reload & store */ +#define CTL_RELOAD 0x0002 /* When set reads EEPROM into registers */ +#define CTL_STORE 0x0001 /* When set stores registers into EEPROM */ +#define CTL_DEFAULT (0x1A10) /* Autorelease enabled*/ + +/* MMU Command Register */ +/* BANK 2 */ +#define MMU_CMD_REG 0x0000 +#define MC_BUSY 1 /* When 1 the last release has not completed */ +#define MC_NOP (0<<5) /* No Op */ +#define MC_ALLOC (1<<5) /* OR with number of 256 byte packets */ +#define MC_RESET (2<<5) /* Reset MMU to initial state */ +#define MC_REMOVE (3<<5) /* Remove the current rx packet */ +#define MC_RELEASE (4<<5) /* Remove and release the current rx packet */ +#define MC_FREEPKT (5<<5) /* Release packet in PNR register */ +#define MC_ENQUEUE (6<<5) /* Enqueue the packet for transmit */ +#define MC_RSTTXFIFO (7<<5) /* Reset the TX FIFOs */ + +/* Packet Number Register */ +/* BANK 2 */ +#define PN_REG 0x0002 + +/* Allocation Result Register */ +/* BANK 2 */ +#define AR_REG 0x0003 +#define AR_FAILED 0x80 /* Alocation Failed */ + +/* RX FIFO Ports Register */ +/* BANK 2 */ +#define RXFIFO_REG 0x0004 /* Must be read as a unsigned short*/ +#define RXFIFO_REMPTY 0x8000 /* RX FIFO Empty */ + +/* TX FIFO Ports Register */ +/* BANK 2 */ +#define TXFIFO_REG RXFIFO_REG /* Must be read as a unsigned short*/ +#define TXFIFO_TEMPTY 0x80 /* TX FIFO Empty */ + +/* Pointer Register */ +/* BANK 2 */ +#define PTR_REG 0x0006 +#define PTR_RCV 0x8000 /* 1=Receive area, 0=Transmit area */ +#define PTR_AUTOINC 0x4000 /* Auto increment the pointer on each access */ +#define PTR_READ 0x2000 /* When 1 the operation is a read */ +#define PTR_NOTEMPTY 0x0800 /* When 1 _do not_ write fifo DATA REG */ + +/* Data Register */ +/* BANK 2 */ +#define SMC91111_DATA_REG 0x0008 + +/* Interrupt Status/Acknowledge Register */ +/* BANK 2 */ +#define SMC91111_INT_REG 0x000C + +/* Interrupt Mask Register */ +/* BANK 2 */ +#define IM_REG 0x000D +#define IM_MDINT 0x80 /* PHY MI Register 18 Interrupt */ +#define IM_ERCV_INT 0x40 /* Early Receive Interrupt */ +#define IM_EPH_INT 0x20 /* Set by Etheret Protocol Handler section */ +#define IM_RX_OVRN_INT 0x10 /* Set by Receiver Overruns */ +#define IM_ALLOC_INT 0x08 /* Set when allocation request is completed */ +#define IM_TX_EMPTY_INT 0x04 /* Set if the TX FIFO goes empty */ +#define IM_TX_INT 0x02 /* Transmit Interrrupt */ +#define IM_RCV_INT 0x01 /* Receive Interrupt */ + +/* Multicast Table Registers */ +/* BANK 3 */ +#define MCAST_REG1 0x0000 +#define MCAST_REG2 0x0002 +#define MCAST_REG3 0x0004 +#define MCAST_REG4 0x0006 + +/* Management Interface Register (MII) */ +/* BANK 3 */ +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 /* Disables CRS100 detection during tx half dup */ +#define MII_MDOE 0x0008 /* MII Output Enable */ +#define MII_MCLK 0x0004 /* MII Clock, pin MDCLK */ +#define MII_MDI 0x0002 /* MII Input, pin MDI */ +#define MII_MDO 0x0001 /* MII Output, pin MDO */ + +/* Revision Register */ +/* BANK 3 */ +#define REV_REG 0x000A /* ( hi: chip id low: rev # ) */ + +/* Early RCV Register */ +/* BANK 3 */ +/* this is NOT on SMC9192 */ +#define ERCV_REG 0x000C +#define ERCV_RCV_DISCRD 0x0080 /* When 1 discards a packet being received */ +#define ERCV_THRESHOLD 0x001F /* ERCV Threshold Mask */ + +/* External Register */ +/* BANK 7 */ +#define EXT_REG 0x0000 + +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +/* Transmit status bits*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* Receive status bits */ +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 /* bug: the LAN91C111 never sets this on receive */ +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +/* PHY Register Addresses (LAN91C111 Internal PHY) */ + +/* PHY Control Register */ +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 /* 1=PHY Reset */ +#define PHY_CNTL_LPBK 0x4000 /* 1=PHY Loopback */ +#define PHY_CNTL_SPEED 0x2000 /* 1=100Mbps, 0=10Mpbs */ +#define PHY_CNTL_ANEG_EN 0x1000 /* 1=Enable Auto negotiation */ +#define PHY_CNTL_PDN 0x0800 /* 1=PHY Power Down mode */ +#define PHY_CNTL_MII_DIS 0x0400 /* 1=MII 4 bit interface disabled */ +#define PHY_CNTL_ANEG_RST 0x0200 /* 1=Reset Auto negotiate */ +#define PHY_CNTL_DPLX 0x0100 /* 1=Full Duplex, 0=Half Duplex */ +#define PHY_CNTL_COLTST 0x0080 /* 1= MII Colision Test */ + +/* PHY Status Register */ +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 /* 1=100Base-T4 capable */ +#define PHY_STAT_CAP_TXF 0x4000 /* 1=100Base-X full duplex capable */ +#define PHY_STAT_CAP_TXH 0x2000 /* 1=100Base-X half duplex capable */ +#define PHY_STAT_CAP_TF 0x1000 /* 1=10Mbps full duplex capable */ +#define PHY_STAT_CAP_TH 0x0800 /* 1=10Mbps half duplex capable */ +#define PHY_STAT_CAP_SUPR 0x0040 /* 1=recv mgmt frames with not preamble */ +#define PHY_STAT_ANEG_ACK 0x0020 /* 1=ANEG has completed */ +#define PHY_STAT_REM_FLT 0x0010 /* 1=Remote Fault detected */ +#define PHY_STAT_CAP_ANEG 0x0008 /* 1=Auto negotiate capable */ +#define PHY_STAT_LINK 0x0004 /* 1=valid link */ +#define PHY_STAT_JAB 0x0002 /* 1=10Mbps jabber condition */ +#define PHY_STAT_EXREG 0x0001 /* 1=extended registers implemented */ + +/* PHY Identifier Registers */ +#define PHY_ID1_REG 0x02 /* PHY Identifier 1 */ +#define PHY_ID2_REG 0x03 /* PHY Identifier 2 */ + +/* PHY Auto-Negotiation Advertisement Register */ +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 /* 1=PHY requests exchange of Next Page */ +#define PHY_AD_ACK 0x4000 /* 1=got link code word from remote */ +#define PHY_AD_RF 0x2000 /* 1=advertise remote fault */ +#define PHY_AD_T4 0x0200 /* 1=PHY is capable of 100Base-T4 */ +#define PHY_AD_TX_FDX 0x0100 /* 1=PHY is capable of 100Base-TX FDPLX */ +#define PHY_AD_TX_HDX 0x0080 /* 1=PHY is capable of 100Base-TX HDPLX */ +#define PHY_AD_10_FDX 0x0040 /* 1=PHY is capable of 10Base-T FDPLX */ +#define PHY_AD_10_HDX 0x0020 /* 1=PHY is capable of 10Base-T HDPLX */ +#define PHY_AD_CSMA 0x0001 /* 1=PHY is capable of 802.3 CMSA */ + +/* PHY Auto-negotiation Remote End Capability Register */ +#define PHY_RMT_REG 0x05 +/* Uses same bit definitions as PHY_AD_REG */ + +/* PHY Configuration Register 1 */ +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 /* 1=Rx Link Detect Function disabled */ +#define PHY_CFG1_XMTDIS 0x4000 /* 1=TP Transmitter Disabled */ +#define PHY_CFG1_XMTPDN 0x2000 /* 1=TP Transmitter Powered Down */ +#define PHY_CFG1_BYPSCR 0x0400 /* 1=Bypass scrambler/descrambler */ +#define PHY_CFG1_UNSCDS 0x0200 /* 1=Unscramble Idle Reception Disable */ +#define PHY_CFG1_EQLZR 0x0100 /* 1=Rx Equalizer Disabled */ +#define PHY_CFG1_CABLE 0x0080 /* 1=STP(150ohm), 0=UTP(100ohm) */ +#define PHY_CFG1_RLVL0 0x0040 /* 1=Rx Squelch level reduced by 4.5db */ +#define PHY_CFG1_TLVL_SHIFT 2 /* Transmit Output Level Adjust */ +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 /* Transmitter Rise/Fall time */ + +/* PHY Configuration Register 2 */ +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 /* 1=Auto Polarity Correction disabled */ +#define PHY_CFG2_JABDIS 0x0010 /* 1=Jabber disabled */ +#define PHY_CFG2_MREG 0x0008 /* 1=Multiple register access (MII mgt) */ +#define PHY_CFG2_INTMDIO 0x0004 /* 1=Interrupt signaled with MDIO pulseo */ + +/* PHY Status Output (and Interrupt status) Register */ +#define PHY_INT_REG 0x12 /* Status Output (Interrupt Status) */ +#define PHY_INT_INT 0x8000 /* 1=bits have changed since last read */ +#define PHY_INT_LNKFAIL 0x4000 /* 1=Link Not detected */ +#define PHY_INT_LOSSSYNC 0x2000 /* 1=Descrambler has lost sync */ +#define PHY_INT_CWRD 0x1000 /* 1=Invalid 4B5B code detected on rx */ +#define PHY_INT_SSD 0x0800 /* 1=No Start Of Stream detected on rx */ +#define PHY_INT_ESD 0x0400 /* 1=No End Of Stream detected on rx */ +#define PHY_INT_RPOL 0x0200 /* 1=Reverse Polarity detected */ +#define PHY_INT_JAB 0x0100 /* 1=Jabber detected */ +#define PHY_INT_SPDDET 0x0080 /* 1=100Base-TX mode, 0=10Base-T mode */ +#define PHY_INT_DPLXDET 0x0040 /* 1=Device in Full Duplex */ + +/* PHY Interrupt/Status Mask Register */ +#define PHY_MASK_REG 0x13 /* Interrupt Mask */ +/* Uses the same bit definitions as PHY_INT_REG */ + +#define SMC_DEBUG 0 + +/* Autonegotiation timeout in seconds */ +#define CONFIG_SMC_AUTONEG_TIMEOUT 10 + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + +struct accessors { + void (*ob)(unsigned, unsigned long); + void (*ow)(unsigned, unsigned long); + void (*ol)(unsigned long, unsigned long); + void (*osl)(unsigned long, const void*, int); + unsigned (*ib)(unsigned long); + unsigned (*iw)(unsigned long); + unsigned long (*il)(unsigned long); + void (*isl)(unsigned long, void*, int); +}; + +struct smc91c111_priv { + struct miiphy_device miiphy; + struct accessors a; + unsigned long base; +}; + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + + +#define SMC_DEV_NAME "SMC91111" +#define SMC_ALLOC_MAX_TRY 5 +#define SMC_TX_TIMEOUT 30 + +#define SMC_PHY_CLOCK_DELAY 1000 + +#define ETH_ZLEN 60 + +static void a_outb(unsigned value, unsigned long offset) +{ + writeb(value, offset); +} + +static void a_outw(unsigned value, unsigned long offset) +{ + writew(value, offset); +} + +static void a_outl(unsigned long value, unsigned long offset) +{ + writel(value, offset); +} + +static void a_outsl(unsigned long offset, const void *data, int count) +{ + writesl((void*)offset, data, count); +} + +static unsigned a_inb(unsigned long offset) +{ + return readb(offset); +} + +static unsigned a_inw(unsigned long offset) +{ + return readw(offset); +} + +static unsigned long a_inl(unsigned long offset) +{ + return readl(offset); +} + +static inline void a_insl(unsigned long offset, void *data, int count) +{ + readsl((void*)offset, data, count); +} + +/* access happens via a 32 bit bus */ +static const struct accessors access_via_32bit = { + .ob = a_outb, + .ow = a_outw, + .ol = a_outl, + .osl = a_outsl, + .ib = a_inb, + .iw = a_inw, + .il = a_inl, + .isl = a_insl, +}; + +/* ------------------------------------------------------------------------ */ + +static inline void SMC_outb(struct smc91c111_priv *p, unsigned value, + unsigned offset) +{ + (p->a.ob)(value, p->base + offset); +} + +static inline void SMC_outw(struct smc91c111_priv *p, unsigned value, + unsigned offset) +{ + (p->a.ow)(value, p->base + offset); +} + +static inline void SMC_outl(struct smc91c111_priv *p, unsigned long value, + unsigned offset) +{ + (p->a.ol)(value, p->base + offset); +} + +static inline void SMC_outsl(struct smc91c111_priv *p, unsigned offset, + const void *data, int count) +{ + (p->a.osl)(p->base + offset, data, count); +} + +static inline unsigned SMC_inb(struct smc91c111_priv *p, unsigned offset) +{ + return (p->a.ib)(p->base + offset); +} + +static inline unsigned SMC_inw(struct smc91c111_priv *p, unsigned offset) +{ + return (p->a.iw)(p->base + offset); +} + +static inline unsigned long SMC_inl(struct smc91c111_priv *p, unsigned offset) +{ + return (p->a.il)(p->base + offset); +} + +static inline void SMC_insl(struct smc91c111_priv *p, unsigned offset, + void *data, int count) +{ + (p->a.isl)(p->base + offset, data, count); +} + +static inline void SMC_SELECT_BANK(struct smc91c111_priv *p, int bank) +{ + SMC_outw(p, bank, BANK_SELECT); +} + +/* note: timeout in seconds */ +static int poll4int(struct smc91c111_priv *priv, unsigned char mask, + int timeout) +{ + unsigned old_bank = SMC_inw(priv, BSR_REG); + int i; + + timeout *= 1000; + SMC_SELECT_BANK(priv, 2); + + for (i = 0; i < timeout; i++) { + if (SMC_inw(priv, SMC91111_INT_REG) & mask) { + SMC_SELECT_BANK(priv, old_bank); + return 0; /* return happy */ + } + mdelay(1); + } + + SMC_SELECT_BANK(priv, old_bank); + return 1; +} + +static void smc_wait_mmu_release_complete(struct smc91c111_priv *priv) +{ + int count = 0; + + /* assume bank 2 selected */ + while (SMC_inw(priv, MMU_CMD_REG) & MC_BUSY) { + udelay(1); /* Wait until not busy */ + if (++count > 200) + break; + } +} + +static int smc91c111_phy_write(struct miiphy_device *mdev, uint8_t phyaddr, + uint8_t phyreg, uint16_t phydata) +{ + struct eth_device *edev = mdev->edev; + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + int oldBank; + int i; + unsigned mask; + unsigned short mii_reg; + unsigned char bits[65]; + int clk_idx = 0; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Write command <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Output the PHY address, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + /* Write out 16 bits of data, msb first */ + mask = 0x8000; + for (i = 0; i < 16; ++i) { + if (phydata & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Final clock bit (tristate) */ + bits[clk_idx++] = 0; + + /* Save the current bank */ + oldBank = SMC_inw(priv, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK(priv, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw(priv, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw(priv, mii_reg | bits[i], MII_REG); + udelay(SMC_PHY_CLOCK_DELAY); + + /* Clock Hi - input data */ + SMC_outw(priv, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw(priv, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw(priv, mii_reg, MII_REG); + udelay(SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK(priv, oldBank); + + return 0; +} + +static int smc91c111_phy_read(struct miiphy_device *mdev, uint8_t phyaddr, + uint8_t phyreg, uint16_t * val) +{ + struct eth_device *edev = mdev->edev; + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + int oldBank; + int i; + unsigned char mask; + unsigned short mii_reg; + unsigned char bits[64]; + int clk_idx = 0; + int input_idx; + uint16_t phydata; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Read command <10> */ + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + /* Output the PHY address, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + /*bits[clk_idx++] = 0; */ + + /* Input starts at this bit time */ + input_idx = clk_idx; + + /* Will input 16 bits */ + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + /* Final clock bit */ + bits[clk_idx++] = 0; + + /* Save the current bank */ + oldBank = SMC_inw(priv, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK(priv, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw(priv, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw(priv, mii_reg | bits[i], MII_REG); + udelay(SMC_PHY_CLOCK_DELAY); + + /* Clock Hi - input data */ + SMC_outw(priv, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay(SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw(priv, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw(priv, mii_reg, MII_REG); + udelay(SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK(priv, oldBank); + + /* Recover input data */ + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + + *val = phydata; + return 0; +} + +static int smc91c111_phy_configure(struct eth_device *edev) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + int timeout; + uint16_t my_phy_caps; /* My PHY capabilities */ + uint16_t my_ad_caps; /* My Advertised capabilities */ + uint16_t status; + + /* Reset the PHY, setting all other bits to zero */ + smc91c111_phy_write(&priv->miiphy, priv->miiphy.address, PHY_CNTL_REG, + PHY_CNTL_RST); + + /* Wait up to 6 seconds for the reset to complete, or time out */ + timeout = 6; + while (timeout--) { + smc91c111_phy_read(&priv->miiphy, priv->miiphy.address, + PHY_CNTL_REG, &status); + if (!(status & PHY_CNTL_RST)) + break; /* reset complete */ + mdelay(500); + } + + if (timeout < 1) { + printf ("%s:PHY reset timed out\n", SMC_DEV_NAME); + return -1; + } + + /* Read PHY Register 18, Status Output */ + /* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */ + + /* Enable PHY Interrupts (for register 18) */ + /* Interrupts listed here are disabled */ + smc91c111_phy_write(&priv->miiphy, priv->miiphy.address, PHY_MASK_REG, + 0xffff); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK(priv, 0); + SMC_outw(priv, RPC_DEFAULT, RPC_REG); + + /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */ + smc91c111_phy_read(&priv->miiphy, priv->miiphy.address, PHY_STAT_REG, + &my_phy_caps); + my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */ + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + /* Update our Auto-Neg Advertisement Register */ + smc91c111_phy_write(&priv->miiphy, priv->miiphy.address, PHY_AD_REG, + my_ad_caps); + + /* Read the register back. Without this, it appears that when */ + /* auto-negotiation is restarted, sometimes it isn't ready and */ + /* the link does not come up. */ + smc91c111_phy_read(&priv->miiphy, priv->miiphy.address, PHY_AD_REG, + &status); + + /* Restart auto-negotiation process in order to advertise my caps */ + smc91c111_phy_write(&priv->miiphy, priv->miiphy.address, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST); + + /* Wait for the auto-negotiation to complete. This may take from */ + /* 2 to 3 seconds. */ + /* Wait for the reset to complete, or time out */ + timeout = CONFIG_SMC_AUTONEG_TIMEOUT * 2; + while (timeout--) { + smc91c111_phy_read(&priv->miiphy, priv->miiphy.address, + PHY_STAT_REG, &status); + if (status & PHY_STAT_ANEG_ACK)/* auto-negotiate complete */ + break; + + mdelay(500); + + /* Restart auto-negotiation if remote fault */ + if (status & PHY_STAT_REM_FLT) { + printf ("%s: PHY remote fault detected\n", + SMC_DEV_NAME); + + /* Restart auto-negotiation */ + printf ("%s: PHY restarting auto-negotiation\n", + SMC_DEV_NAME); + smc91c111_phy_write(&priv->miiphy, + priv->miiphy.address, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | PHY_CNTL_DPLX); + } + } + + if (timeout < 1) + printf("%s: PHY auto-negotiate timed out\n", SMC_DEV_NAME); + + /* Fail if we detected an auto-negotiate remote fault */ + if (status & PHY_STAT_REM_FLT) + printf("%s: PHY remote fault detected\n", SMC_DEV_NAME); + + /* Re-Configure the Receive/Phy Control register */ + SMC_outw(priv, RPC_DEFAULT, RPC_REG); + + return 0; +} + +static void smc91c111_reset(struct eth_device *edev) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(priv, 0); + SMC_outw(priv, RCR_SOFTRST, RCR_REG); + + /* Setup the Configuration Register */ + /* This is necessary because the CONFIG_REG is not affected */ + /* by a soft reset */ + + SMC_SELECT_BANK(priv, 1); + SMC_outw(priv, CONFIG_DEFAULT, CONFIG_REG); + + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_outw(priv, SMC_inw(priv, CONFIG_REG) | CONFIG_EPH_POWER_EN, + CONFIG_REG); + + SMC_SELECT_BANK(priv, 0); + + /* this should pause enough for the chip to be happy */ + udelay (10); + + /* Disable transmit and receive functionality */ + SMC_outw(priv, RCR_CLEAR, RCR_REG); + SMC_outw(priv, TCR_CLEAR, TCR_REG); + + /* set the control register */ + SMC_SELECT_BANK(priv, 1); + SMC_outw(priv, CTL_DEFAULT, CTL_REG); + + /* Reset the MMU */ + SMC_SELECT_BANK(priv, 2); + smc_wait_mmu_release_complete(priv); + SMC_outw(priv, MC_RESET, MMU_CMD_REG); + + while (SMC_inw(priv, MMU_CMD_REG) & MC_BUSY) + udelay(1); /* Wait until not busy */ + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + /* Disable all interrupts */ + SMC_outb(priv, 0, IM_REG); +} + +static void smc91c111_enable(struct eth_device *edev) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + + SMC_SELECT_BANK(priv, 0); + /* see the header file for options in TCR/RCR DEFAULT*/ + SMC_outw(priv, TCR_DEFAULT, TCR_REG ); + SMC_outw(priv, RCR_DEFAULT, RCR_REG ); +} + +static int smc91c111_eth_open(struct eth_device *edev) +{ + smc91c111_enable(edev); + /* Configure the PHY */ + return smc91c111_phy_configure(edev); +} + +static int smc91c111_eth_send(struct eth_device *edev, void *packet, + int packet_length) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + unsigned char packet_no; + unsigned char *buf; + int length; + int numPages; + int try = 0; + int time_out; + unsigned char status; + unsigned char saved_pnr; + unsigned short saved_ptr; + + /* save PTR and PNR registers before manipulation */ + SMC_SELECT_BANK(priv, 2); + saved_pnr = SMC_inb(priv, PN_REG ); + saved_ptr = SMC_inw(priv, PTR_REG ); + + length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN; + + /* allocate memory + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; /* Divide by 256 */ + + if (numPages > 7) { + printf ("%s: Far too big packet error. \n", SMC_DEV_NAME); + return 0; + } + + /* now, try to allocate the memory */ + SMC_SELECT_BANK(priv, 2); + SMC_outw(priv, MC_ALLOC | numPages, MMU_CMD_REG); + + /* FIXME: the ALLOC_INT bit never gets set * + * so the following will always give a * + * memory allocation error. * + * same code works in armboot though * + * -ro + */ + +again: + try++; + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb(priv, SMC91111_INT_REG); + if (status & IM_ALLOC_INT) { + /* acknowledge the interrupt */ + SMC_outb(priv, IM_ALLOC_INT, SMC91111_INT_REG); + break; + } + } while (--time_out); + + if (!time_out) { + if (try < SMC_ALLOC_MAX_TRY) + goto again; + else + return 0; + } + + PRINTK2 ("%s: memory allocation, try %d succeeded ...\n", + SMC_DEV_NAME, try); + + /* I can send the packet now.. */ + + buf = (unsigned char *) packet; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb(priv, AR_REG); + if (packet_no & AR_FAILED) { + /* or isn't there? BAD CHIP! */ + printf ("%s: Memory allocation failed. \n", SMC_DEV_NAME); + return 0; + } + + /* we have a packet address, so tell the card to use it */ + SMC_outb(priv, packet_no, PN_REG); + + /* do not write new ptr value if Write data fifo not empty */ + while ( saved_ptr & PTR_NOTEMPTY ) + printf ("Write data fifo not empty!\n"); + + /* point to the beginning of the packet */ + SMC_outw(priv, PTR_AUTOINC, PTR_REG); + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status unsigned short( set to zeros ) */ + SMC_outl(priv, (length + 6) << 16, SMC91111_DATA_REG); + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ + SMC_outsl(priv, SMC91111_DATA_REG, buf, length >> 2); + if (length & 0x2) + SMC_outw(priv, + *((unsigned short*) (buf + (length & 0xFFFFFFFC))), + SMC91111_DATA_REG); + + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) + SMC_outw(priv, 0, SMC91111_DATA_REG); + else + SMC_outw(priv, buf[length - 1] | 0x2000, SMC91111_DATA_REG); + + /* and let the chipset deal with it */ + SMC_outw(priv, MC_ENQUEUE, MMU_CMD_REG); + + /* poll for TX INT */ + /* if (poll4int (IM_TX_INT, SMC_TX_TIMEOUT)) { */ + /* poll for TX_EMPTY INT - autorelease enabled */ + if (poll4int(priv, IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) { + /* release packet */ + /* no need to release, MMU does that now */ + + /* wait for MMU getting ready (low) */ + while (SMC_inw(priv, MMU_CMD_REG) & MC_BUSY) + udelay (10); + return 0; + } else { + /* ack. int */ + SMC_outb(priv, IM_TX_EMPTY_INT, SMC91111_INT_REG); + + /* release packet */ + /* no need to release, MMU does that now */ + + /* wait for MMU getting ready (low) */ + while (SMC_inw(priv, MMU_CMD_REG) & MC_BUSY) + udelay (10); + } + + /* restore previously saved registers */ + SMC_outb(priv, saved_pnr, PN_REG ); + SMC_outw(priv, saved_ptr, PTR_REG ); + + return length; +} + +static void smc91c111_eth_halt(struct eth_device *edev) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + + /* no more interrupts for me */ + SMC_SELECT_BANK(priv, 2); + SMC_outb(priv, 0, IM_REG); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(priv, 0); + SMC_outb(priv, RCR_CLEAR, RCR_REG); + SMC_outb(priv, TCR_CLEAR, TCR_REG); +} + +static int smc91c111_eth_rx(struct eth_device *edev) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + int packet_number; + unsigned short status; + unsigned short packet_length; + int is_error = 0; + unsigned long stat_len; + unsigned char saved_pnr; + unsigned short saved_ptr; + + SMC_SELECT_BANK(priv, 2); + /* save PTR and PTR registers */ + saved_pnr = SMC_inb(priv, PN_REG ); + saved_ptr = SMC_inw(priv, PTR_REG ); + + packet_number = SMC_inw(priv, RXFIFO_REG); + + if (packet_number & RXFIFO_REMPTY) + return 0; + + /* start reading from the start of the packet */ + SMC_outw(priv, PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG ); + + /* First two words are status and packet_length */ + stat_len = SMC_inl(priv, SMC91111_DATA_REG); + status = stat_len & 0xffff; + packet_length = stat_len >> 16; + + packet_length &= 0x07ff; /* mask off top bits */ + + if ( !(status & RS_ERRORS ) ){ + /* Adjust for having already read the first two words */ + packet_length -= 4; /*4; */ + + + /* set odd length for bug in LAN91C111, */ + /* which never sets RS_ODDFRAME */ + /* TODO ? */ + + + PRINTK3(" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3 ); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl(priv, SMC91111_DATA_REG , NetRxPackets[0], + packet_length >> 2); + /* read the left over bytes */ + if (packet_length & 3) { + int i; + + unsigned char *tail = + (unsigned char *)(NetRxPackets[0] + + (packet_length & ~3)); + unsigned long leftover = SMC_inl(priv, + SMC91111_DATA_REG); + for (i=0; i<(packet_length & 3); i++) + *tail++ = + (unsigned char) (leftover >> (8*i)) & 0xff; + } + +#if SMC_DEBUG > 2 + printf("Receiving Packet\n"); + print_packet( NetRxPackets[0], packet_length ); +#endif + } else { + /* error ... */ + /* TODO ? */ + is_error = 1; + } + + while (SMC_inw(priv, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* error or good, tell the card to get rid of this packet */ + SMC_outw(priv, MC_RELEASE, MMU_CMD_REG ); + + while (SMC_inw(priv, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* restore saved registers */ + SMC_outb(priv, saved_pnr, PN_REG ); + SMC_outw(priv, saved_ptr, PTR_REG ); + + if (!is_error) { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[0], packet_length); + return packet_length; + } + + return 0; +} + +static int smc91c111_get_ethaddr(struct eth_device *edev, unsigned char *m) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + unsigned address[3]; + + SMC_SELECT_BANK(priv, 1); + address[0] = SMC_inw(priv, ADDR0_REG); + address[1] = SMC_inw(priv, ADDR0_REG + 2); + address[2] = SMC_inw(priv, ADDR0_REG + 4); + + if (address[0] + address[1] + address[2] == 0) + return -1; /* no eeprom, no mac */ + + m[0] = address[0] >> 8; + m[1] = address[0]; + m[2] = address[1] >> 8; + m[3] = address[1]; + m[4] = address[2] >> 8; + m[5] = address[2]; + + return 0; +} + +static int smc91c111_set_ethaddr(struct eth_device *edev, + unsigned char *mac_addr) +{ + struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + unsigned address; + int i; + + SMC_SELECT_BANK(priv, 1); + + for (i = 0; i < 6; i += 2) { + address = mac_addr[i + 1] << 8; + address |= mac_addr[i]; + SMC_outw(priv, address, (ADDR0_REG + i)); + } + + return -1; +} + +#if (SMC_DEBUG > 2 ) + +/*------------------------------------------------------------ + . Debugging function for viewing MII Management serial bitstream + .-------------------------------------------------------------*/ +static void smc_dump_mii_stream (unsigned char * bits, int size) +{ + int i; + + printf ("BIT#:"); + for (i = 0; i < size; ++i) { + printf ("%d", i % 10); + } + + printf ("\nMDOE:"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDOE) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDO :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDO) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDI :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDI) + printf ("1"); + else + printf ("0"); + } + + printf ("\n"); +} +#endif + + +#if SMC_DEBUG > 2 +static void print_packet( unsigned char * buf, int length ) +{ + int i; + int remainder; + int lines; + + printf("Packet of length %d \n", length ); + +#if SMC_DEBUG > 3 + lines = length / 16; + remainder = length % 16; + + for ( i = 0; i < lines ; i ++ ) { + int cur; + + for ( cur = 0; cur < 8; cur ++ ) { + unsigned char a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + unsigned char a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); +#endif +} +#endif + +static int smc91c111_init_dev(struct eth_device *edev) +{ + return 0; +} + +static int smc91c111_probe(struct device_d *dev) +{ + struct eth_device *edev; + struct smc91c111_priv *priv; + + edev = xzalloc(sizeof(struct eth_device) + + sizeof(struct smc91c111_priv)); + dev->type_data = edev; + edev->dev = dev; + edev->priv = (struct smc91c111_priv *)(edev + 1); + + priv = edev->priv; + + priv->a = access_via_32bit; + + edev->init = smc91c111_init_dev; + edev->open = smc91c111_eth_open; + edev->send = smc91c111_eth_send; + edev->recv = smc91c111_eth_rx; + edev->halt = smc91c111_eth_halt; + edev->get_ethaddr = smc91c111_get_ethaddr; + edev->set_ethaddr = smc91c111_set_ethaddr; + + priv->miiphy.read = smc91c111_phy_read; + priv->miiphy.write = smc91c111_phy_write; + priv->miiphy.address = 0; + priv->miiphy.flags = 0; + priv->miiphy.edev = edev; + priv->base = dev->map_base; + + smc91c111_reset(edev); + + miiphy_register(&priv->miiphy); + eth_register(edev); + + return 0; +} + +static struct driver_d smc91c111_driver = { + .name = "smc91c111", + .probe = smc91c111_probe, + .type = DEVICE_TYPE_ETHER, +}; + +static int smc91c111_init(void) +{ + register_driver(&smc91c111_driver); + return 0; +} + +device_initcall(smc91c111_init); |