#include <common.h>
#include <command.h>
+#include <config.h>
#include "smc91111.h"
#include <net.h>
"smc91111.c:v1.0 04/25/01 by Daris A Nevil (dnevil@snmc.com)\n";
#endif
+/* Autonegotiation timeout in seconds */
+#ifndef CONFIG_SMC_AUTONEG_TIMEOUT
+#define CONFIG_SMC_AUTONEG_TIMEOUT 10
+#endif
+
/*------------------------------------------------------------------------
.
. Configuration options, for the experienced user to change.
------------------------------------------------------------
*/
+#ifdef CONFIG_SMC_USE_IOFUNCS
+/*
+ * input and output functions
+ *
+ * Implemented due to inx,outx macros accessing the device improperly
+ * and putting the device into an unkown state.
+ *
+ * For instance, on Sharp LPD7A400 SDK, affects were chip memory
+ * could not be free'd (hence the alloc failures), duplicate packets,
+ * packets being corrupt (shifted) on the wire, etc. Switching to the
+ * inx,outx functions fixed this problem.
+ */
+static inline word SMC_inw(dword offset);
+static inline void SMC_outw(word value, dword offset);
+static inline byte SMC_inb(dword offset);
+static inline void SMC_outb(byte value, dword offset);
+static inline void SMC_insw(dword offset, volatile uchar* buf, dword len);
+static inline void SMC_outsw(dword offset, uchar* buf, dword len);
+
+#define barrier() __asm__ __volatile__("": : :"memory")
+
+static inline word SMC_inw(dword offset)
+{
+ word v;
+ v = *((volatile word*)(SMC_BASE_ADDRESS+offset));
+ barrier(); *(volatile u32*)(0xc0000000);
+ return v;
+}
+
+static inline void SMC_outw(word value, dword offset)
+{
+ *((volatile word*)(SMC_BASE_ADDRESS+offset)) = value;
+ barrier(); *(volatile u32*)(0xc0000000);
+}
+
+static inline byte SMC_inb(dword offset)
+{
+ word _w;
+
+ _w = SMC_inw(offset & ~((dword)1));
+ return (offset & 1) ? (byte)(_w >> 8) : (byte)(_w);
+}
+
+static inline void SMC_outb(byte value, dword offset)
+{
+ word _w;
+
+ _w = SMC_inw(offset & ~((dword)1));
+ if (offset & 1)
+ *((volatile word*)(SMC_BASE_ADDRESS+(offset & ~((dword)1)))) = (value<<8) | (_w & 0x00ff);
+ else
+ *((volatile word*)(SMC_BASE_ADDRESS+offset)) = value | (_w & 0xff00);
+}
+
+static inline void SMC_insw(dword offset, volatile uchar* buf, dword len)
+{
+ while (len-- > 0) {
+ *((word*)buf)++ = SMC_inw(offset);
+ barrier(); *((volatile u32*)(0xc0000000));
+ }
+}
+
+static inline void SMC_outsw(dword offset, uchar* buf, dword len)
+{
+ while (len-- > 0) {
+ SMC_outw(*((word*)buf)++, offset);
+ barrier(); *(volatile u32*)(0xc0000000);
+ }
+}
+#endif /* CONFIG_SMC_USE_IOFUNCS */
+
static char unsigned smc_mac_addr[6] = {0x02, 0x80, 0xad, 0x20, 0x31, 0xb8};
/*
*/
static void smc_reset (void)
{
- PRINTK2 ("%s:smc_reset\n", SMC_DEV_NAME);
+ PRINTK2 ("%s: smc_reset\n", SMC_DEV_NAME);
/* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
*/
static void smc_enable()
{
- PRINTK2("%s:smc_enable\n", SMC_DEV_NAME);
+ PRINTK2("%s: smc_enable\n", SMC_DEV_NAME);
SMC_SELECT_BANK( 0 );
/* see the header file for options in TCR/RCR DEFAULT*/
SMC_outw( TCR_DEFAULT, TCR_REG );
*/
static void smc_shutdown()
{
- PRINTK2(CARDNAME ":smc_shutdown\n");
+ PRINTK2(CARDNAME ": smc_shutdown\n");
/* no more interrupts for me */
SMC_SELECT_BANK( 2 );
int try = 0;
int time_out;
byte status;
+ byte saved_pnr;
+ word saved_ptr;
+ /* save PTR and PNR registers before manipulation */
+ SMC_SELECT_BANK (2);
+ saved_pnr = SMC_inb( PN_REG );
+ saved_ptr = SMC_inw( PTR_REG );
- PRINTK3 ("%s:smc_hardware_send_packet\n", SMC_DEV_NAME);
+ PRINTK3 ("%s: smc_hardware_send_packet\n", SMC_DEV_NAME);
length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN;
/* we have a packet address, so tell the card to use it */
SMC_outb (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 (PTR_AUTOINC, PTR_REG);
SMC_outw (MC_ENQUEUE, MMU_CMD_REG);
/* poll for TX INT */
- if (poll4int (IM_TX_INT, SMC_TX_TIMEOUT)) {
+ /* if (poll4int (IM_TX_INT, SMC_TX_TIMEOUT)) { */
+ /* poll for TX_EMPTY INT - autorelease enabled */
+ if (poll4int(IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) {
/* sending failed */
PRINTK2 ("%s: TX timeout, sending failed...\n", SMC_DEV_NAME);
/* release packet */
- SMC_outw (MC_FREEPKT, MMU_CMD_REG);
+ /* no need to release, MMU does that now */
+ /* SMC_outw (MC_FREEPKT, MMU_CMD_REG); */
/* wait for MMU getting ready (low) */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
return 0;
} else {
/* ack. int */
- SMC_outb (IM_TX_INT, SMC91111_INT_REG);
+ SMC_outb (IM_TX_EMPTY_INT, SMC91111_INT_REG);
+ /* SMC_outb (IM_TX_INT, SMC91111_INT_REG); */
PRINTK2 ("%s: Sent packet of length %d \n", SMC_DEV_NAME,
length);
/* release packet */
- SMC_outw (MC_FREEPKT, MMU_CMD_REG);
+ /* no need to release, MMU does that now */
+ /* SMC_outw (MC_FREEPKT, MMU_CMD_REG); */
/* wait for MMU getting ready (low) */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
}
+ /* restore previously saved registers */
+ SMC_outb( saved_pnr, PN_REG );
+ SMC_outw( saved_ptr, PTR_REG );
+
return length;
}
*/
void smc_destructor()
{
- PRINTK2(CARDNAME ":smc_destructor\n");
+ PRINTK2(CARDNAME ": smc_destructor\n");
}
{
int i, err;
- PRINTK2 ("%s:smc_open\n", SMC_DEV_NAME);
+ PRINTK2 ("%s: smc_open\n", SMC_DEV_NAME);
/* reset the hardware */
smc_reset ();
address = smc_mac_addr[i + 1] << 8;
address |= smc_mac_addr[i];
- SMC_outw (address, ADDR0_REG + i);
+ SMC_outw (address, (ADDR0_REG + i));
}
#else
for (i = 0; i < 6; i++)
- SMC_outb (smc_mac_addr[i], ADDR0_REG + i);
+ SMC_outb (smc_mac_addr[i], (ADDR0_REG + i));
#endif
return 0;
#ifdef USE_32_BIT
dword stat_len;
#endif
+ byte saved_pnr;
+ word saved_ptr;
SMC_SELECT_BANK(2);
+ /* save PTR and PTR registers */
+ saved_pnr = SMC_inb( PN_REG );
+ saved_ptr = SMC_inw( PTR_REG );
+
packet_number = SMC_inw( RXFIFO_REG );
if ( packet_number & RXFIFO_REMPTY ) {
return 0;
}
- PRINTK3("%s:smc_rcv\n", SMC_DEV_NAME);
+ PRINTK3("%s: smc_rcv\n", SMC_DEV_NAME);
/* start reading from the start of the packet */
SMC_outw( PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG );
while ( SMC_inw( MMU_CMD_REG ) & MC_BUSY )
udelay(1); /* Wait until not busy */
+ /* restore saved registers */
+ SMC_outb( saved_pnr, PN_REG );
+ SMC_outw( saved_ptr, PTR_REG );
+
if (!is_error) {
/* Pass the packet up to the protocol layers. */
NetReceive(NetRxPackets[0], packet_length);
-----------------------------------------------------*/
static int smc_close()
{
- PRINTK2("%s:smc_close\n", SMC_DEV_NAME);
+ PRINTK2("%s: smc_close\n", SMC_DEV_NAME);
/* clear everything */
smc_shutdown();
word status = 0; /*;my status = 0 */
int failed = 0;
- PRINTK3 ("%s:smc_program_phy()\n", SMC_DEV_NAME);
+ PRINTK3 ("%s: smc_program_phy()\n", SMC_DEV_NAME);
/* Get the detected phy address */
/* Update our Auto-Neg Advertisement Register */
smc_write_phy_register (PHY_AD_REG, my_ad_caps);
- PRINTK2 ("%s:phy caps=%x\n", SMC_DEV_NAME, my_phy_caps);
- PRINTK2 ("%s:phy advertised caps=%x\n", SMC_DEV_NAME, 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. */
+ smc_read_phy_register(PHY_AD_REG);
+
+ PRINTK2 ("%s: phy caps=%x\n", SMC_DEV_NAME, my_phy_caps);
+ PRINTK2 ("%s: phy advertised caps=%x\n", SMC_DEV_NAME, my_ad_caps);
/* Restart auto-negotiation process in order to advertise my caps */
smc_write_phy_register (PHY_CNTL_REG,
/* 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 = 20; /* Wait up to 10 seconds */
+ timeout = CONFIG_SMC_AUTONEG_TIMEOUT * 2;
while (timeout--) {
+
status = smc_read_phy_register (PHY_STAT_REG);
if (status & PHY_STAT_ANEG_ACK) {
/* auto-negotiate complete */
/* Restart auto-negotiation if remote fault */
if (status & PHY_STAT_REM_FLT) {
- printf ("%s:PHY remote fault detected\n",
+ printf ("%s: PHY remote fault detected\n",
SMC_DEV_NAME);
/* Restart auto-negotiation */
- printf ("%s:PHY restarting auto-negotiation\n",
+ printf ("%s: PHY restarting auto-negotiation\n",
SMC_DEV_NAME);
smc_write_phy_register (PHY_CNTL_REG,
PHY_CNTL_ANEG_EN |
}
if (timeout < 1) {
- printf ("%s:PHY auto-negotiate timed out\n", SMC_DEV_NAME);
- printf ("%s:PHY auto-negotiate timed out\n", SMC_DEV_NAME);
+ printf ("%s: PHY auto-negotiate timed out\n", SMC_DEV_NAME);
failed = 1;
}
/* 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);
- printf ("%s:PHY remote fault detected\n", SMC_DEV_NAME);
+ printf ("%s: PHY remote fault detected\n", SMC_DEV_NAME);
failed = 1;
}
/* Re-Configure the Receive/Phy Control register */
SMC_outw (RPC_DEFAULT, RPC_REG);
- smc_phy_configure_exit:
+smc_phy_configure_exit: ;
}
#endif /* !CONFIG_SMC91111_EXT_PHY */
return (1);
#else
int i;
+ int valid_mac = 0;
+
SMC_SELECT_BANK (1);
for (i=0; i<6; i++)
{
- v_rom_mac[i] = SMC_inb (ADDR0_REG + i);
+ v_rom_mac[i] = SMC_inb ((ADDR0_REG + i));
+ valid_mac |= v_rom_mac[i];
}
- return (1);
+
+ return (valid_mac ? 1 : 0);
#endif
}
#endif /* CONFIG_DRIVER_SMC91111 */