/* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
*
* Copyright (C) 1996, 1998, 1999, 2002, 2003,
* 2006, 2008 David S. Miller (davem@davemloft.net)
*
* Changes :
* 2000/11/11 Willy Tarreau <willy AT meta-x.org>
* - port to non-sparc architectures. Tested only on x86 and
* only currently works with QFE PCI cards.
* - ability to specify the MAC address at module load time by passing this
* argument : macaddr=0x00,0x10,0x20,0x30,0x40,0x50
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/random.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/mm.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#ifdef CONFIG_SPARC
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/idprom.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/prom.h>
#include <asm/auxio.h>
#endif
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#ifdef CONFIG_PCI
#include <linux/pci.h>
#endif
#include "sunhme.h"
#define DRV_NAME "sunhme"
#define DRV_VERSION "3.10"
#define DRV_RELDATE "August 26, 2008"
#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"
static char version[] =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION("Sun HappyMealEthernet(HME) 10/100baseT ethernet driver");
MODULE_LICENSE("GPL");
static int macaddr[6];
/* accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */
module_param_array(macaddr, int, NULL, 0);
MODULE_PARM_DESC(macaddr, "Happy Meal MAC address to set");
#ifdef CONFIG_SBUS
static struct quattro *qfe_sbus_list;
#endif
#ifdef CONFIG_PCI
static struct quattro *qfe_pci_list;
#endif
#undef HMEDEBUG
#undef SXDEBUG
#undef RXDEBUG
#undef TXDEBUG
#undef TXLOGGING
#ifdef TXLOGGING
struct hme_tx_logent {
unsigned int tstamp;
int tx_new, tx_old;
unsigned int action;
#define TXLOG_ACTION_IRQ 0x01
#define TXLOG_ACTION_TXMIT 0x02
#define TXLOG_ACTION_TBUSY 0x04
#define TXLOG_ACTION_NBUFS 0x08
unsigned int status;
};
#define TX_LOG_LEN 128
static struct hme_tx_logent tx_log[TX_LOG_LEN];
static int txlog_cur_entry;
static __inline__ void tx_add_log(struct happy_meal *hp, unsigned int a, unsigned int s)
{
struct hme_tx_logent *tlp;
unsigned long flags;
local_irq_save(flags);
tlp = &tx_log[txlog_cur_entry];
tlp->tstamp = (unsigned int)jiffies;
tlp->tx_new = hp->tx_new;
tlp->tx_old = hp->tx_old;
tlp->action = a;
tlp->status = s;
txlog_cur_entry = (txlog_cur_entry + 1) & (TX_LOG_LEN - 1);
local_irq_restore(flags);
}
static __inline__ void tx_dump_log(void)
{
int i, this;
this = txlog_cur_entry;
for (i = 0; i < TX_LOG_LEN; i++) {
printk("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i,
tx_log[this].tstamp,
tx_log[this].tx_new, tx_log[this].tx_old,
tx_log[this].action, tx_log[this].status);
this = (this + 1) & (TX_LOG_LEN - 1);
}
}
static __inline__ void tx_dump_ring(struct happy_meal *hp)
{
struct hmeal_init_block *hb = hp->happy_block;
struct happy_meal_txd *tp = &hb->happy_meal_txd[0];
int i;
for (i = 0; i < TX_RING_SIZE; i+=4) {
printk("TXD[%d..%d]: [%08x:%08x] [%08x:%08x] [%08x:%08x] [%08x:%08x]\n",
i, i + 4,
le32_to_cpu(tp[i].tx_flags), le32_to_cpu(tp[i].tx_addr),
le32_to_cpu(tp[i + 1].tx_flags), le32_to_cpu(tp[i + 1].tx_addr),
le32_to_cpu(tp[i + 2].tx_flags), le32_to_cpu(tp[i + 2].tx_addr),
le32_to_cpu(tp[i + 3].tx_flags), le32_to_cpu(tp[i + 3].tx_addr));
}
}
#else
#define tx_add_log(hp, a, s) do { } while(0)
#define tx_dump_log() do { } while(0)
#define tx_dump_ring(hp) do { } while(0)
#endif
#ifdef HMEDEBUG
#define HMD(x) printk x
#else
#define HMD(x)
#endif
/* #define AUTO_SWITCH_DEBUG */
#ifdef AUTO_SWITCH_DEBUG
#define ASD(x) printk x
#else
#define ASD(x)
#endif
#define DEFAULT_IPG0 16 /* For lance-mode only */
#define DEFAULT_IPG1 8 /* For all modes */
#define DEFAULT_IPG2 4 /* For all modes */
#define DEFAULT_JAMSIZE 4 /* Toe jam */
/* NOTE: In the descriptor writes one _must_ write the address
* member _first_. The card must not be allowed to see
* the updated descriptor flags until the address is
* correct. I've added a write memory barrier between
* the two stores so that I can sleep well at night... -DaveM
*/
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
static void sbus_hme_write32(void __iomem *reg, u32 val)
{
sbus_writel(val, reg);
}
static u32 sbus_hme_read32(void __iomem *reg)
{
return sbus_readl(reg);
}
static void sbus_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
{
rxd->rx_addr = (__force hme32)addr;
wmb();
rxd->rx_flags = (__force hme32)flags;
}
static void sbus_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
{
txd->tx_addr = (__force hme32)addr;
wmb();
txd->tx_flags = (__force hme32)flags;
}
static u32 sbus_hme_read_desc32(hme32 *p)
{
return (__force u32)*p;
}
static void pci_hme_write32(void __iomem *reg, u32 val)
{
writel(val, reg);
}
static u32 pci_hme_read32(void __iomem *reg)
{
return readl(reg);
}
static void pci_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
{
rxd->rx_addr = (__force hme32)cpu_to_le32(addr);
wmb();
rxd->rx_flags = (__force hme32)cpu_to_le32(flags);
}
static void pci_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
{
txd->tx_addr = (__force hme32)cpu_to_le32(addr);
wmb();
txd->tx_flags = (__force hme32)cpu_to_le32(flags);
}
static u32 pci_hme_read_desc32(hme32 *p)
{
return le32_to_cpup((__le32 *)p);
}
#define hme_write32(__hp, __reg, __val) \
((__hp)->write32((__reg), (__val)))
#define hme_read32(__hp, __reg) \
((__hp)->read32(__reg))
#define hme_write_rxd(__hp, __rxd, __flags, __addr) \
((__hp)->write_rxd((__rxd), (__flags), (__addr)))
#define hme_write_txd(__hp, __txd, __flags, __addr) \
((__hp)->write_txd((__txd), (__flags), (__addr)))
#define hme_read_desc32(__hp, __p) \
((__hp)->read_desc32(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
((__hp)->dma_map((__hp)->dma_dev, (__ptr), (__size), (__dir)))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
((__hp)->dma_unmap((__hp)->dma_dev, (__addr), (__size), (__dir)))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
((__hp)->dma_sync_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir)))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
((__hp)->dma_sync_for_device((__hp)->dma_dev, (__addr), (__size), (__dir)))
#else
#ifdef CONFIG_SBUS
/* SBUS only compilation */
#define hme_write32(__hp, __reg, __val) \
sbus_writel((__val), (__reg))
#define hme_read32(__hp, __reg) \
sbus_readl(__reg)
#define hme_write_rxd(__hp, __rxd, __flags, __addr) \
do { (__rxd)->rx_addr = (__force hme32)(u32)(__addr); \
wmb(); \
(__rxd)->rx_flags = (__force hme32)(u32)(__flags); \
} while(0)
#define hme_write_txd(__hp, __txd, __flags, __addr) \
do { (__txd)->tx_addr = (__force hme32)(u32)(__addr); \
wmb(); \
(__txd)->tx_flags = (__force hme32)(u32)(__flags); \
} while(0)
#define hme_read_desc32(__hp, __p) ((__force u32)(hme32)*(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
dma_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
dma_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
dma_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
dma_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir))
#else
/* PCI only compilation */
#define hme_write32(__hp, __reg, __val) \
writel((__val), (__reg))
#define hme_read32(__hp, __reg) \
readl(__reg)
#define hme_write_rxd(__hp, __rxd, __flags, __addr) \
do { (__rxd)->rx_addr = (__force hme32)cpu_to_le32(__addr); \
wmb(); \
(__rxd)->rx_flags = (__force hme32)cpu_to_le32(__flags); \
} while(0)
#define hme_write_txd(__hp, __txd, __flags, __addr) \
do { (__txd)->tx_addr = (__force hme32)cpu_to_le32(__addr); \
wmb(); \
(__txd)->tx_flags = (__force hme32)cpu_to_le32(__flags); \
} while(0)
static inline u32 hme_read_desc32(struct happy_meal *hp, hme32 *p)
{
return le32_to_cpup((__le32 *)p);
}
#define hme_dma_map(__hp, __ptr, __size, __dir) \
pci_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
pci_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
pci_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
pci_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir))
#endif
#endif
/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */
static void BB_PUT_BIT(struct happy_meal *hp, void __iomem *tregs, int bit)
{
hme_write32(hp, tregs + TCVR_BBDATA, bit);
hme_write32(hp, tregs + TCVR_BBCLOCK, 0);
hme_write32(hp, tregs + TCVR_BBCLOCK, 1);
}
#if 0
static u32 BB_GET_BIT(struct happy_meal *hp, void __iomem *tregs, int internal)
{
u32 ret;
hme_write32(hp, tregs + TCVR_BBCLOCK, 0);
hme_write32(hp, tregs + TCVR_BBCLOCK, 1);
ret = hme_read32(hp, tregs + TCVR_CFG);
if (internal)
ret &= TCV_CFG_MDIO0;
else
ret &= TCV_CFG_MDIO1;
return ret;
}
#endif
static u32 BB_GET_BIT2(struct happy_meal *hp, void __iomem *tregs, int internal)
{
u32 retval;
hme_write32(hp, tregs + TCVR_BBCLOCK, 0);
udelay(1);
retval = hme_read32(hp, tregs + TCVR_CFG);
if (internal)
retval &= TCV_CFG_MDIO0;
else
retval &= TCV_CFG_MDIO1;
hme_write32(hp, tregs + TCVR_BBCLOCK, 1);
return retval;
}
#define TCVR_FAILURE 0x80000000 /* Impossible MIF read value */
static int happy_meal_bb_read(struct happy_meal *hp,
void __iomem *tregs, int reg)
{
u32 tmp;
int retval = 0;
int i;
ASD(("happy_meal_bb_read: reg=%d ", reg));
/* Enable the MIF BitBang outputs. */
hme_write32(hp, tregs + TCVR_BBOENAB, 1);
/* Force BitBang into the idle state. */
for (i = 0; i < 32; i++)
BB_PUT_BIT(hp, tregs, 1);
/* Give it the read sequence. */
BB_PUT_BIT(hp, tregs, 0);
BB_PUT_BIT(hp, tregs, 1);
BB_PUT_BIT(hp, tregs, 1);
BB_PUT_BIT(hp, tregs, 0);
/* Give it the PHY address. */
tmp = hp->paddr & 0xff;
for (i = 4; i >= 0; i--)
BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it what register we want to read. */
tmp = (reg & 0xff);
for (i = 4; i >= 0; i--)
BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Close down the MIF BitBang outputs. */
hme_write32(hp, tregs + TCVR_BBOENAB, 0);
/* Now read in the value. */
(void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
for (i = 15; i >= 0; i--)
retval |= BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
(void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
(void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
(void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
ASD(("value=%x\n", retval));
return retval;
}
static void happy_meal_bb_write(struct happy_meal *hp,
void __iomem *tregs, int reg,
unsigned short value)
{
u32 tmp;
int i;
ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value));
/* Enable the MIF BitBang outputs. */
hme_write32(hp, tregs + TCVR_BBOENAB, 1);
/* Force BitBang into the idle state. */
for (i = 0; i < 32; i++)
BB_PUT_BIT(hp, tregs, 1);
/* Give it write sequence. */
BB_PUT_BIT(hp, tregs, 0);
BB_PUT_BIT(hp, tregs, 1);
BB_PUT_BIT(hp, tregs, 0);
BB_PUT_BIT(hp, tregs, 1);
/* Give it the PHY address. */
tmp = (hp->paddr & 0xff);
for (i = 4; i >= 0; i--)
BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it what register we will be writing. */
tmp = (reg & 0xff);
for (i = 4; i >= 0; i--)
BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
/* Tell it to become ready for the bits. */
BB_PUT_BIT(hp, tregs, 1);
BB_PUT_BIT(hp, tregs, 0);
for (i = 15; i >= 0; i--)
BB_PUT_BIT(hp, tregs, ((value >> i) & 1));
/* Close down the MIF BitBang outputs. */
hme_write32(hp, tregs + TCVR_BBOENAB, 0);
}
#define TCVR_READ_TRIES 16
static int happy_meal_tcvr_read(struct happy_meal *hp,
void __iomem *tregs, int reg)
{
int tries = TCVR_READ_TRIES;
int retval;
ASD(("happy_meal_tcvr_read: reg=0x%02x ", reg));
if (hp->tcvr_type == none) {
ASD(("no transceiver, value=TCVR_FAILURE\n"));
return TCVR_FAILURE;
}
if (!(hp->happy_flags & HFLAG_FENABLE)) {
ASD(("doing bit bang\n"));
return happy_meal_bb_read(hp, tregs, reg);
}
hme_write32(hp, tregs + TCVR_FRAME,
(FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18)));
while (!(hme_read32(hp, tregs + TCVR_FRAME) & 0x10000) && --tries)
udelay(20);
if (!tries) {
printk(KERN_ERR "happy meal: Aieee, transceiver MIF read bolixed\n");
return TCVR_FAILURE;
}
retval = hme_read32(hp, tregs + TCVR_FRAME) & 0xffff;
ASD(("value=%04x\n", retval));
return retval;
}
#define TCVR_WRITE_TRIES 16
static void happy_meal_tcvr_write(struct happy_meal *hp,
void __iomem *tregs, int reg,
unsigned short value)
{
int tries = TCVR_WRITE_TRIES;
ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value));
/* Welcome to Sun Microsystems, can I take your order please? */
if (!(hp->happy_flags & HFLAG_FENABLE)) {
happy_meal_bb_write(hp, tregs, reg, value);
return;
}
/* Would you like fries with that? */
hme_write32(hp, tregs + TCVR_FRAME,
(FRAME_WRITE | (hp->paddr << 23) |
((reg & 0xff) << 18) | (value & 0xffff)));
while (!(hme_read32(hp, tregs + TCVR_FRAME) & 0x10000) && --tries)
udelay(20);
/* Anything else? */
if (!tries)
printk(KERN_ERR "happy meal: Aieee, transceiver MIF write bolixed\n");
/* Fifty-two cents is your change, have a nice day. */
}
/* Auto negotiation. The scheme is very simple. We have a timer routine
* that keeps watching the auto negotiation process as it progresses.
* The DP83840 is first told to start doing it's thing, we set up the time
* and place the timer state machine in it's initial state.
*
* Here the timer peeks at the DP83840 status registers at each click to see
* if the auto negotiation has completed, we assume here that the DP83840 PHY
* will time out at some point and just tell us what (didn't) happen. For
* complete coverage we only allow so many of the ticks at this level to run,
* when this has expired we print a warning message and try another strategy.
* This "other" strategy is to force the interface into various speed/duplex
* configurations and we stop when we see a link-up condition before the
* maximum number of "peek" ticks have occurred.
*
* Once a valid link status has been detected we configure the BigMAC and
* the rest of the Happy Meal to speak the most efficient protocol we could
* get a clean link for. The priority for link configurations, highest first
* is:
* 100 Base-T Full Duplex
* 100 Base-T Half Duplex
* 10 Base-T Full Duplex
* 10 Base-T Half Duplex
*
* We start a new timer now, after a successful auto negotiation status has
* been detected. This timer just waits for the link-up bit to get set in
* the BMCR of the DP83840. When this occurs we print a kernel log message
* describing the link type in use and the fact that it is up.
*
* If a fatal error of some sort is signalled and detected in the interrupt
* service routine, and the chip is reset, or the link is ifconfig'd down
* and then back up, this entire process repeats itself all over again.
*/
static int try_next_permutation(struct happy_meal *hp, void __iomem *tregs)
{
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
/* Downgrade from full to half duplex. Only possible
* via ethtool.
*/
if (hp->sw_bmcr & BMCR_FULLDPLX) {
hp->sw_bmcr &= ~(BMCR_FULLDPLX);
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
return 0;
}
/* Downgrade from 100 to 10. */
if (hp->sw_bmcr & BMCR_SPEED100) {
hp->sw_bmcr &= ~(BMCR_SPEED100);
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
return 0;
}
/* We've tried everything. */
return -1;
}
static void display_link_mode(struct happy_meal *hp, void __iomem *tregs)
{
printk(KERN_INFO "%s: Link is up using ", hp->dev->name);
if (hp->tcvr_type == external)
printk("external ");
else
printk("internal ");
printk("transceiver at ");
hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, MII_LPA);
if (hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) {
if (hp->sw_lpa & LPA_100FULL)
printk("100Mb/s, Full Duplex.\n");
else
printk("100Mb/s, Half Duplex.\n");
} else {
if (hp->sw_lpa & LPA_10FULL)
printk("10Mb/s, Full Duplex.\n");
else
printk("10Mb/s, Half Duplex.\n");
}
}
static void display_forced_link_mode(struct happy_meal *hp, void __iomem *tregs)
{
printk(KERN_INFO "%s: Link has been forced up using ", hp->dev->name);
if (hp->tcvr_type == external)
printk("external ");
else
printk("internal ");
printk("transceiver at ");
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (hp->sw_bmcr & BMCR_SPEED100)
printk("100Mb/s, ");
else
printk("10Mb/s, ");
if (hp->sw_bmcr & BMCR_FULLDPLX)
printk("Full Duplex.\n");
else
printk("Half Duplex.\n");
}
static int set_happy_link_modes(struct happy_meal *hp, void __iomem *tregs)
{
int full;
/* All we care about is making sure the bigmac tx_cfg has a
* proper duplex setting.
*/
if (hp->timer_state == arbwait) {
hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, MII_LPA);
if (!(hp->sw_lpa & (LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL)))
goto no_response;
if (hp->sw_lpa & LPA_100FULL)
full = 1;
else if (hp->sw_lpa & LPA_100HALF)
full = 0;
else if (hp->sw_lpa & LPA_10FULL)
full = 1;
else
full = 0;
} else {
/* Forcing a link mode. */
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (hp->sw_bmcr & BMCR_FULLDPLX)
full = 1;
else
full = 0;
}
/* Before changing other bits in the tx_cfg register, and in
* general any of other the TX config registers too, you
* must:
* 1) Clear Enable
* 2) Poll with reads until that bit reads back as zero
* 3) Make TX configuration changes
* 4) Set Enable once more
*/
hme_write32(hp, hp->bigmacregs + BMAC_TXCFG,
hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) &
~(BIGMAC_TXCFG_ENABLE));
while (hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) & BIGMAC_TXCFG_ENABLE)
barrier();
if (full) {
hp->happy_flags |= HFLAG_FULL;
hme_write32(hp, hp->bigmacregs + BMAC_TXCFG,
hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) |
BIGMAC_TXCFG_FULLDPLX);
} else {
hp->happy_flags &= ~(HFLAG_FULL);
hme_write32(hp, hp->bigmacregs + BMAC_TXCFG,
hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) &
~(BIGMAC_TXCFG_FULLDPLX));
}
hme_write32(hp, hp->bigmacregs + BMAC_TXCFG,
hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) |
BIGMAC_TXCFG_ENABLE);
return 0;
no_response:
return 1;
}
static int happy_meal_init(struct happy_meal *hp);
static int is_lucent_phy(struct happy_meal *hp)
{
void __iomem *tregs = hp->tcvregs;
unsigned short mr2, mr3;
int ret = 0;
mr2 = happy_meal_tcvr_read(hp, tregs, 2);
mr3 = happy_meal_tcvr_read(hp, tregs, 3);
if ((mr2 & 0xffff) == 0x0180 &&
((mr3 & 0xffff) >> 10) == 0x1d)
ret = 1;
return ret;
}
static void happy_meal_timer(unsigned long data)
{
struct happy_meal *hp = (struct happy_meal *) data;
void __iomem *tregs = hp->tcvregs;
int restart_timer = 0;
spin_lock_irq(&hp->happy_lock);
hp->timer_ticks++;
switch(hp->timer_state) {
case arbwait:
/* Only allow for 5 ticks, thats 10 seconds and much too
* long to wait for arbitration to complete.
*/
if (hp->timer_ticks >= 10) {
/* Enter force mode. */
do_force_mode:
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful, trying force link mode\n",
hp->dev->name);
hp->sw_bmcr = BMCR_SPEED100;
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
if (!is_lucent_phy(hp)) {
/* OK, seems we need do disable the transceiver for the first
* tick to make sure we get an accurate link state at the
* second tick.
*/
hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
}
hp->timer_state = ltrywait;
hp->timer_ticks = 0;
restart_timer = 1;
} else {
/* Anything interesting happen? */
hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR);
if (hp->sw_bmsr & BMSR_ANEGCOMPLETE) {
int ret;
/* Just what we've been waiting for... */
ret = set_happy_link_modes(hp, tregs);
if (ret) {
/* Ooops, something bad happened, go to force
* mode.
*
* XXX Broken hubs which don't support 802.3u
* XXX auto-negotiation make this happen as well.
*/
goto do_force_mode;
}
/* Success, at least so far, advance our state engine. */
hp->timer_state = lupwait;
restart_timer = 1;
} else {
restart_timer = 1;
}
}
break;
case lupwait:
/* Auto negotiation was successful and we are awaiting a
* link up status. I have decided to let this timer run
* forever until some sort of error is signalled, reporting
* a message to the user at 10 second intervals.
*/
hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR);
if (hp->sw_bmsr & BMSR_LSTATUS) {
/* Wheee, it's up, display the link mode in use and put
* the timer to sleep.
*/
display_link_mode(hp, tregs);
hp->timer_state = asleep;
restart_timer = 0;
} else {
if (hp->timer_ticks >= 10) {
printk(KERN_NOTICE "%s: Auto negotiation successful, link still "
"not completely up.\n", hp->dev->name);
hp->timer_ticks = 0;
restart_timer = 1;
} else {
restart_timer = 1;
}
}
break;
case ltrywait:
/* Making the timeout here too long can make it take
* annoyingly long to attempt all of the link mode
* permutations, but then again this is essentially
* error recovery code for the most part.
*/
hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR);
hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
if (hp->timer_ticks == 1) {
if (!is_lucent_phy(hp)) {
/* Re-enable transceiver, we'll re-enable the transceiver next
* tick, then check link state on the following tick.
*/
hp->sw_csconfig |= CSCONFIG_TCVDISAB;
happy_meal_tcvr_write(hp, tregs,
DP83840_CSCONFIG, hp->sw_csconfig);
}
restart_timer = 1;
break;
}
if (hp->timer_ticks == 2) {
if (!is_lucent_phy(hp)) {
hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
happy_meal_tcvr_write(hp, tregs,
DP83840_CSCONFIG, hp->sw_csconfig);
}
restart_timer = 1;
break;
}
if (hp->sw_bmsr & BMSR_LSTATUS) {
/* Force mode selection success. */
display_forced_link_mode(hp, tregs);
set_happy_link_modes(hp, tregs); /* XXX error? then what? */
hp->timer_state = asleep;
restart_timer = 0;
} else {
if (hp->timer_ticks >= 4) { /* 6 seconds or so... */
int ret;
ret = try_next_permutation(hp, tregs);
if (ret == -1) {
/* Aieee, tried them all, reset the
* chip and try all over again.
*/
/* Let the user know... */
printk(KERN_NOTICE "%s: Link down, cable problem?\n",
hp->dev->name);
ret = happy_meal_init(hp);
if (ret) {
/* ho hum... */
printk(KERN_ERR "%s: Error, cannot re-init the "
"Happy Meal.\n", hp->dev->name);
}
goto out;
}
if (!is_lucent_phy(hp)) {
hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
DP83840_CSCONFIG);
hp->sw_csconfig |= CSCONFIG_TCVDISAB;
happy_meal_tcvr_write(hp, tregs,
DP83840_CSCONFIG, hp->sw_csconfig);
}
hp->timer_ticks = 0;
restart_timer = 1;
} else {
restart_timer = 1;
}
}
break;
case asleep:
default:
/* Can't happens.... */
printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n",
hp->dev->name);
restart_timer = 0;
hp->timer_ticks = 0;
hp->timer_state = asleep; /* foo on you */
break;
};
if (restart_timer) {
hp->happy_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */
add_timer(&hp->happy_timer);
}
out:
spin_unlock_irq(&hp->happy_lock);
}
#define TX_RESET_TRIES 32
#define RX_RESET_TRIES 32
/* hp->happy_lock must be held */
static void happy_meal_tx_reset(struct happy_meal *hp, void __iomem *bregs)
{
int tries = TX_RESET_TRIES;
HMD(("happy_meal_tx_reset: reset, "));
/* Would you like to try our SMCC Delux? */
hme_write32(hp, bregs + BMAC_TXSWRESET, 0);
while ((hme_read32(hp, bregs + BMAC_TXSWRESET) & 1) && --tries)
udelay(20);
/* Lettuce, tomato, buggy hardware (no extra charge)? */
if (!tries)
printk(KERN_ERR "happy meal: Transceiver BigMac ATTACK!");
/* Take care. */
HMD(("done\n"));
}
/* hp->happy_lock must be held */
static void happy_meal_rx_reset(struct happy_meal *hp, void __iomem *bregs)
{
int tries = RX_RESET_TRIES;
HMD(("happy_meal_rx_reset: reset, "));
/* We have a special on GNU/Viking hardware bugs today. */
hme_write32(hp, bregs + BMAC_RXSWRESET, 0);
while ((hme_read32(hp, bregs + BMAC_RXSWRESET) & 1) && --tries)
udelay(20);
/* Will that be all? */
if (!tries)
printk(KERN_ERR "happy meal: Receiver BigMac ATTACK!");
/* Don't forget your vik_1137125_wa. Have a nice day. */
HMD(("done\n"));
}
#define STOP_TRIES 16
/* hp->happy_lock must be held */
static void happy_meal_stop(struct happy_meal *hp, void __iomem *gregs)
{
int tries = STOP_TRIES;
HMD(("happy_meal_stop: reset, "));
/* We're consolidating our STB products, it's your lucky day. */
hme_write32(hp, gregs + GREG_SWRESET, GREG_RESET_ALL);
while (hme_read32(hp, gregs + GREG_SWRESET) && --tries)
udelay(20);
/* Come back next week when we are "Sun Microelectronics". */
if (!tries)
printk(KERN_ERR "happy meal: Fry guys.");
/* Remember: "Different name, same old buggy as shit hardware." */
HMD(("done\n"));
}
/* hp->happy_lock must be held */
static void happy_meal_get_counters(struct happy_meal *hp, void __iomem *bregs)
{
struct net_device_stats *stats = &hp->net_stats;
stats->rx_crc_errors += hme_read32(hp, bregs + BMAC_RCRCECTR);
hme_write32(hp, bregs + BMAC_RCRCECTR, 0);
stats->rx_frame_errors += hme_read32(hp, bregs + BMAC_UNALECTR);
hme_write32(hp, bregs + BMAC_UNALECTR, 0);
stats->rx_length_errors += hme_read32(hp, bregs + BMAC_GLECTR);
hme_write32(hp, bregs + BMAC_GLECTR, 0);
stats->tx_aborted_errors += hme_read32(hp, bregs + BMAC_EXCTR);
stats->collisions +=
(hme_read32(hp, bregs + BMAC_EXCTR) +
hme_read32(hp, bregs + BMAC_LTCTR));
hme_write32(hp, bregs + BMAC_EXCTR, 0);
hme_write32(hp, bregs + BMAC_LTCTR, 0);
}
/* hp->happy_lock must be held */
static void happy_meal_poll_stop(struct happy_meal *hp, void __iomem *tregs)
{
ASD(("happy_meal_poll_stop: "));
/* If polling disabled or not polling already, nothing to do. */
if ((hp->happy_flags & (HFLAG_POLLENABLE | HFLAG_POLL)) !=
(HFLAG_POLLENABLE | HFLAG_POLL)) {
HMD(("not polling, return\n"));
return;
}
/* Shut up the MIF. */
ASD(("were polling, mif ints off, "));
hme_write32(hp, tregs + TCVR_IMASK, 0xffff);
/* Turn off polling. */
ASD(("polling off, "));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_PENABLE));
/* We are no longer polling. */
hp->happy_flags &= ~(HFLAG_POLL);
/* Let the bits set. */
udelay(200);
ASD(("done\n"));
}
/* Only Sun can take such nice parts and fuck up the programming interface
* like this. Good job guys...
*/
#define TCVR_RESET_TRIES 16 /* It should reset quickly */
#define TCVR_UNISOLATE_TRIES 32 /* Dis-isolation can take longer. */
/* hp->happy_lock must be held */
static int happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs)
{
u32 tconfig;
int result, tries = TCVR_RESET_TRIES;
tconfig = hme_read32(hp, tregs + TCVR_CFG);
ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig));
if (hp->tcvr_type == external) {
ASD(("external<"));
hme_write32(hp, tregs + TCVR_CFG, tconfig & ~(TCV_CFG_PSELECT));
hp->tcvr_type = internal;
hp->paddr = TCV_PADDR_ITX;
ASD(("ISOLATE,"));
happy_meal_tcvr_write(hp, tregs, MII_BMCR,
(BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE));
result = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (result == TCVR_FAILURE) {
ASD(("phyread_fail>\n"));
return -1;
}
ASD(("phyread_ok,PSELECT>"));
hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT);
hp->tcvr_type = external;
hp->paddr = TCV_PADDR_ETX;
} else {
if (tconfig & TCV_CFG_MDIO1) {
ASD(("internal<PSELECT,"));
hme_write32(hp, tregs + TCVR_CFG, (tconfig | TCV_CFG_PSELECT));
ASD(("ISOLATE,"));
happy_meal_tcvr_write(hp, tregs, MII_BMCR,
(BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE));
result = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (result == TCVR_FAILURE) {
ASD(("phyread_fail>\n"));
return -1;
}
ASD(("phyread_ok,~PSELECT>"));
hme_write32(hp, tregs + TCVR_CFG, (tconfig & ~(TCV_CFG_PSELECT)));
hp->tcvr_type = internal;
hp->paddr = TCV_PADDR_ITX;
}
}
ASD(("BMCR_RESET "));
happy_meal_tcvr_write(hp, tregs, MII_BMCR, BMCR_RESET);
while (--tries) {
result = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (result == TCVR_FAILURE)
return -1;
hp->sw_bmcr = result;
if (!(result & BMCR_RESET))
break;
udelay(20);
}
if (!tries) {
ASD(("BMCR RESET FAILED!\n"));
return -1;
}
ASD(("RESET_OK\n"));
/* Get fresh copies of the PHY registers. */
hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR);
hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, MII_PHYSID1);
hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, MII_PHYSID2);
hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, MII_ADVERTISE);
ASD(("UNISOLATE"));
hp->sw_bmcr &= ~(BMCR_ISOLATE);
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
tries = TCVR_UNISOLATE_TRIES;
while (--tries) {
result = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (result == TCVR_FAILURE)
return -1;
if (!(result & BMCR_ISOLATE))
break;
udelay(20);
}
if (!tries) {
ASD((" FAILED!\n"));
return -1;
}
ASD((" SUCCESS and CSCONFIG_DFBYPASS\n"));
if (!is_lucent_phy(hp)) {
result = happy_meal_tcvr_read(hp, tregs,
DP83840_CSCONFIG);
happy_meal_tcvr_write(hp, tregs,
DP83840_CSCONFIG, (result | CSCONFIG_DFBYPASS));
}
return 0;
}
/* Figure out whether we have an internal or external transceiver.
*
* hp->happy_lock must be held
*/
static void happy_meal_transceiver_check(struct happy_meal *hp, void __iomem *tregs)
{
unsigned long tconfig = hme_read32(hp, tregs + TCVR_CFG);
ASD(("happy_meal_transceiver_check: tcfg=%08lx ", tconfig));
if (hp->happy_flags & HFLAG_POLL) {
/* If we are polling, we must stop to get the transceiver type. */
ASD(("<polling> "));
if (hp->tcvr_type == internal) {
if (tconfig & TCV_CFG_MDIO1) {
ASD(("<internal> <poll stop> "));
happy_meal_poll_stop(hp, tregs);
hp->paddr = TCV_PADDR_ETX;
hp->tcvr_type = external;
ASD(("<external>\n"));
tconfig &= ~(TCV_CFG_PENABLE);
tconfig |= TCV_CFG_PSELECT;
hme_write32(hp, tregs + TCVR_CFG, tconfig);
}
} else {
if (hp->tcvr_type == external) {
ASD(("<external> "));
if (!(hme_read32(hp, tregs + TCVR_STATUS) >> 16)) {
ASD(("<poll stop> "));
happy_meal_poll_stop(hp, tregs);
hp->paddr = TCV_PADDR_ITX;
hp->tcvr_type = internal;
ASD(("<internal>\n"));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) &
~(TCV_CFG_PSELECT));
}
ASD(("\n"));
} else {
ASD(("<none>\n"));
}
}
} else {
u32 reread = hme_read32(hp, tregs + TCVR_CFG);
/* Else we can just work off of the MDIO bits. */
ASD(("<not polling> "));
if (reread & TCV_CFG_MDIO1) {
hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT);
hp->paddr = TCV_PADDR_ETX;
hp->tcvr_type = external;
ASD(("<external>\n"));
} else {
if (reread & TCV_CFG_MDIO0) {
hme_write32(hp, tregs + TCVR_CFG,
tconfig & ~(TCV_CFG_PSELECT));
hp->paddr = TCV_PADDR_ITX;
hp->tcvr_type = internal;
ASD(("<internal>\n"));
} else {
printk(KERN_ERR "happy meal: Transceiver and a coke please.");
hp->tcvr_type = none; /* Grrr... */
ASD(("<none>\n"));
}
}
}
}
/* The receive ring buffers are a bit tricky to get right. Here goes...
*
* The buffers we dma into must be 64 byte aligned. So we use a special
* alloc_skb() routine for the happy meal to allocate 64 bytes more than
* we really need.
*
* We use skb_reserve() to align the data block we get in the skb. We
* also program the etxregs->cfg register to use an offset of 2. This
* imperical constant plus the ethernet header size will always leave
* us with a nicely aligned ip header once we pass things up to the
* protocol layers.
*
* The numbers work out to:
*
* Max ethernet frame size 1518
* Ethernet header size 14
* Happy Meal base offset 2
*
* Say a skb data area is at 0xf001b010, and its size alloced is
* (ETH_FRAME_LEN + 64 + 2) = (1514 + 64 + 2) = 1580 bytes.
*
* First our alloc_skb() routine aligns the data base to a 64 byte
* boundary. We now have 0xf001b040 as our skb data address. We
* plug this into the receive descriptor address.
*
* Next, we skb_reserve() 2 bytes to account for the Happy Meal offset.
* So now the data we will end up looking at starts at 0xf001b042. When
* the packet arrives, we will check out the size received and subtract
* this from the skb->length. Then we just pass the packet up to the
* protocols as is, and allocate a new skb to replace this slot we have
* just received from.
*
* The ethernet layer will strip the ether header from the front of the
* skb we just sent to it, this leaves us with the ip header sitting
* nicely aligned at 0xf001b050. Also, for tcp and udp packets the
* Happy Meal has even checksummed the tcp/udp data for us. The 16
* bit checksum is obtained from the low bits of the receive descriptor
* flags, thus:
*
* skb->csum = rxd->rx_flags & 0xffff;
* skb->ip_summed = CHECKSUM_COMPLETE;
*
* before sending off the skb to the protocols, and we are good as gold.
*/
static void happy_meal_clean_rings(struct happy_meal *hp)
{
int i;
for (i = 0; i < RX_RING_SIZE; i++) {
if (hp->rx_skbs[i] != NULL) {
struct sk_buff *skb = hp->rx_skbs[i];
struct happy_meal_rxd *rxd;
u32 dma_addr;
rxd = &hp->happy_block->happy_meal_rxd[i];
dma_addr = hme_read_desc32(hp, &rxd->rx_addr);
dma_unmap_single(hp->dma_dev, dma_addr,
RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
hp->rx_skbs[i] = NULL;
}
}
for (i = 0; i < TX_RING_SIZE; i++) {
if (hp->tx_skbs[i] != NULL) {
struct sk_buff *skb = hp->tx_skbs[i];
struct happy_meal_txd *txd;
u32 dma_addr;
int frag;
hp->tx_skbs[i] = NULL;
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
txd = &hp->happy_block->happy_meal_txd[i];
dma_addr = hme_read_desc32(hp, &txd->tx_addr);
dma_unmap_single(hp->dma_dev, dma_addr,
(hme_read_desc32(hp, &txd->tx_flags)
& TXFLAG_SIZE),
DMA_TO_DEVICE);
if (frag != skb_shinfo(skb)->nr_frags)
i++;
}
dev_kfree_skb_any(skb);
}
}
}
/* hp->happy_lock must be held */
static void happy_meal_init_rings(struct happy_meal *hp)
{
struct hmeal_init_block *hb = hp->happy_block;
struct net_device *dev = hp->dev;
int i;
HMD(("happy_meal_init_rings: counters to zero, "));
hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0;
/* Free any skippy bufs left around in the rings. */
HMD(("clean, "));
happy_meal_clean_rings(hp);
/* Now get new skippy bufs for the receive ring. */
HMD(("init rxring, "));
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
if (!skb) {
hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0);
continue;
}
hp->rx_skbs[i] = skb;
skb->dev = dev;
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
hme_write_rxd(hp, &hb->happy_meal_rxd[i],
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)),
dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
DMA_FROM_DEVICE));
skb_reserve(skb, RX_OFFSET);
}
HMD(("init txring, "));
for (i = 0; i < TX_RING_SIZE; i++)
hme_write_txd(hp, &hb->happy_meal_txd[i], 0, 0);
HMD(("done\n"));
}
/* hp->happy_lock must be held */
static void happy_meal_begin_auto_negotiation(struct happy_meal *hp,
void __iomem *tregs,
struct ethtool_cmd *ep)
{
int timeout;
/* Read all of the registers we are interested in now. */
hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR);
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, MII_PHYSID1);
hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, MII_PHYSID2);
/* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */
hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, MII_ADVERTISE);
if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
/* Advertise everything we can support. */
if (hp->sw_bmsr & BMSR_10HALF)
hp->sw_advertise |= (ADVERTISE_10HALF);
else
hp->sw_advertise &= ~(ADVERTISE_10HALF);
if (hp->sw_bmsr & BMSR_10FULL)
hp->sw_advertise |= (ADVERTISE_10FULL);
else
hp->sw_advertise &= ~(ADVERTISE_10FULL);
if (hp->sw_bmsr & BMSR_100HALF)
hp->sw_advertise |= (ADVERTISE_100HALF);
else
hp->sw_advertise &= ~(ADVERTISE_100HALF);
if (hp->sw_bmsr & BMSR_100FULL)
hp->sw_advertise |= (ADVERTISE_100FULL);
else
hp->sw_advertise &= ~(ADVERTISE_100FULL);
happy_meal_tcvr_write(hp, tregs, MII_ADVERTISE, hp->sw_advertise);
/* XXX Currently no Happy Meal cards I know off support 100BaseT4,
* XXX and this is because the DP83840 does not support it, changes
* XXX would need to be made to the tx/rx logic in the driver as well
* XXX so I completely skip checking for it in the BMSR for now.
*/
#ifdef AUTO_SWITCH_DEBUG
ASD(("%s: Advertising [ ", hp->dev->name));
if (hp->sw_advertise & ADVERTISE_10HALF)
ASD(("10H "));
if (hp->sw_advertise & ADVERTISE_10FULL)
ASD(("10F "));
if (hp->sw_advertise & ADVERTISE_100HALF)
ASD(("100H "));
if (hp->sw_advertise & ADVERTISE_100FULL)
ASD(("100F "));
#endif
/* Enable Auto-Negotiation, this is usually on already... */
hp->sw_bmcr |= BMCR_ANENABLE;
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
/* Restart it to make sure it is going. */
hp->sw_bmcr |= BMCR_ANRESTART;
happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr);
/* BMCR_ANRESTART self clears when the process has begun. */
timeout = 64; /* More than enough. */
while (--timeout) {
hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR);
if (!(hp->sw_bmcr & BMCR_ANRESTART))
break; /* got it. */
udelay(10);
}
if (!timeout) {
printk(KERN_ERR "%s: Happy Meal would not start auto negotiation "
"BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr);
printk(KERN_NOTICE "%s: Performing force link detection.\n",
hp->dev->name);