// SPDX-License-Identifier: GPL-2.0 /* 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
*/
MODULE_AUTHOR("David S. Miller ");
MODULE_DESCRIPTION("Sun HappyMealEthernet(HME) 10/100baseT ethernet driver");
MODULE_LICENSE("GPL");
staticint 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 staticstruct quattro *qfe_sbus_list; #endif
#ifdef CONFIG_PCI staticstruct quattro *qfe_pci_list; #endif
this = txlog_cur_entry; for (i = 0; i < TX_LOG_LEN; i++) {
pr_err("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);
}
} #else #define tx_add_log(hp, a, s) #define tx_dump_log() #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
*/
/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */ staticvoid 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);
}
staticvoid happy_meal_tcvr_write(struct happy_meal *hp, void __iomem *tregs, int reg, unsignedshort value)
{ int tries = TCVR_WRITE_TRIES;
ASD("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);
/* 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 its 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.
*/ staticint 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;
}
netdev_info(hp->dev, "Link has been forced up using %s transceiver at %dMb/s, %s Duplex.\n",
hp->tcvr_type == external ? "external" : "internal",
hp->sw_bmcr & BMCR_SPEED100 ? 100 : 10,
hp->sw_bmcr & BMCR_FULLDPLX ? "Full" : "Half");
}
staticint 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; elseif (hp->sw_lpa & LPA_100HALF)
full = 0; elseif (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;
}
staticint is_lucent_phy(struct happy_meal *hp)
{ void __iomem *tregs = hp->tcvregs; unsignedshort mr2, mr3; int ret = 0;
/* hp->happy_lock must be held */ staticvoid
happy_meal_begin_auto_negotiation(struct happy_meal *hp, void __iomem *tregs, conststruct ethtool_link_ksettings *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 || ep->base.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);
/* 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.
*/
/* 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) {
netdev_err(hp->dev, "Happy Meal would not start auto negotiation BMCR=0x%04x\n",
hp->sw_bmcr);
netdev_notice(hp->dev, "Performing force link detection.\n"); goto force_link;
} else {
hp->timer_state = arbwait;
}
} else {
force_link: /* Force the link up, trying first a particular mode. * Either we are here at the request of ethtool or * because the Happy Meal would not start to autoneg.
*/
/* Disable auto-negotiation in BMCR, enable the duplex and * speed setting, init the timer state machine, and fire it off.
*/ if (!ep || ep->base.autoneg == AUTONEG_ENABLE) {
hp->sw_bmcr = BMCR_SPEED100;
} else { if (ep->base.speed == SPEED_100)
hp->sw_bmcr = BMCR_SPEED100; else
hp->sw_bmcr = 0; if (ep->base.duplex == DUPLEX_FULL)
hp->sw_bmcr |= BMCR_FULLDPLX;
}
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++; 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);
netdev_notice(hp->dev, "Auto-Negotiation unsuccessful, trying force link mode\n");
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) {
netdev_notice(hp->dev, "Auto negotiation successful, link still not completely up.\n");
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... */
netdev_notice(hp->dev, "Link down, cable problem?\n");
case asleep: default: /* Can't happens.... */
netdev_err(hp->dev, "Aieee, link timer is asleep but we got one anyways!\n");
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);
}
/* hp->happy_lock must be held */ staticvoid happy_meal_tx_reset(struct happy_meal *hp, void __iomem *bregs)
{ int tries = TX_RESET_TRIES;
HMD("reset...\n");
/* 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)
netdev_err(hp->dev, "Transceiver BigMac ATTACK!");
/* Take care. */
HMD("done\n");
}
/* hp->happy_lock must be held */ staticvoid happy_meal_rx_reset(struct happy_meal *hp, void __iomem *bregs)
{ int tries = RX_RESET_TRIES;
HMD("reset...\n");
/* 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)
netdev_err(hp->dev, "Receiver BigMac ATTACK!\n");
/* Don't forget your vik_1137125_wa. Have a nice day. */
HMD("done\n");
}
#define STOP_TRIES 16
/* hp->happy_lock must be held */ staticvoid happy_meal_stop(struct happy_meal *hp, void __iomem *gregs)
{ int tries = STOP_TRIES;
/* 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 */ staticint happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs)
{
u32 tconfig; int result, tries = TCVR_RESET_TRIES;
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("UNISOLATE 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
*/ staticvoid happy_meal_transceiver_check(struct happy_meal *hp, void __iomem *tregs)
{ unsignedlong tconfig = hme_read32(hp, tregs + TCVR_CFG);
u32 reread = hme_read32(hp, tregs + TCVR_CFG);
/* 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.
*/ staticvoid 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;
/* Stop transmitter and receiver. */
HMD("to happy_meal_stop\n");
happy_meal_stop(hp, gregs);
/* Alloc and reset the tx/rx descriptor chains. */
HMD("to happy_meal_init_rings\n");
happy_meal_init_rings(hp);
/* See if we can enable the MIF frame on this card to speak to the DP83840. */ if (hp->happy_flags & HFLAG_FENABLE) {
HMD("use frame old[%08x]\n",
hme_read32(hp, tregs + TCVR_CFG));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_BENABLE));
} else {
HMD("use bitbang old[%08x]\n",
hme_read32(hp, tregs + TCVR_CFG));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) | TCV_CFG_BENABLE);
}
/* Check the state of the transceiver. */
HMD("to happy_meal_transceiver_check\n");
happy_meal_transceiver_check(hp, tregs);
/* Put the Big Mac into a sane state. */ switch(hp->tcvr_type) { case none: /* Cannot operate if we don't know the transceiver type! */
HMD("AAIEEE no transceiver type, EAGAIN\n"); return -EAGAIN;
case internal: /* Using the MII buffers. */
HMD("internal, using MII\n");
hme_write32(hp, bregs + BMAC_XIFCFG, 0); break;
case external: /* Not using the MII, disable it. */
HMD("external, disable MII\n");
hme_write32(hp, bregs + BMAC_XIFCFG, BIGMAC_XCFG_MIIDISAB); break;
}
if (happy_meal_tcvr_reset(hp, tregs)) return -EAGAIN;
/* Reset the Happy Meal Big Mac transceiver and the receiver. */
HMD("tx/rx reset\n");
happy_meal_tx_reset(hp, bregs);
happy_meal_rx_reset(hp, bregs);
/* Set jam size and inter-packet gaps to reasonable defaults. */
hme_write32(hp, bregs + BMAC_JSIZE, DEFAULT_JAMSIZE);
hme_write32(hp, bregs + BMAC_IGAP1, DEFAULT_IPG1);
hme_write32(hp, bregs + BMAC_IGAP2, DEFAULT_IPG2);
/* Load up the MAC address and random seed. */
/* The docs recommend to use the 10LSB of our MAC here. */
hme_write32(hp, bregs + BMAC_RSEED, ((e[5] | e[4]<<8)&0x3ff));
/* Set the RX and TX ring ptrs. */
HMD("ring ptrs rxr[%08x] txr[%08x]\n",
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
hme_write32(hp, erxregs + ERX_RING,
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
hme_write32(hp, etxregs + ETX_RING,
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
/* Parity issues in the ERX unit of some HME revisions can cause some * registers to not be written unless their parity is even. Detect such * lost writes and simply rewrite with a low bit set (which will be ignored * since the rxring needs to be 2K aligned).
*/ if (hme_read32(hp, erxregs + ERX_RING) !=
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)))
hme_write32(hp, erxregs + ERX_RING,
((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0))
| 0x4);
/* Set the supported burst sizes. */ #ifndef CONFIG_SPARC /* It is always PCI and can handle 64byte bursts. */
hme_write32(hp, gregs + GREG_CFG, GREG_CFG_BURST64); #else if ((hp->happy_bursts & DMA_BURST64) &&
((hp->happy_flags & HFLAG_PCI) != 0 #ifdef CONFIG_SBUS
|| sbus_can_burst64() #endif
|| 0)) {
u32 gcfg = GREG_CFG_BURST64;
/* I have no idea if I should set the extended * transfer mode bit for Cheerio, so for now I * do not. -DaveM
*/ #ifdef CONFIG_SBUS if ((hp->happy_flags & HFLAG_PCI) == 0) { struct platform_device *op = hp->happy_dev; if (sbus_can_dma_64bit()) {
sbus_set_sbus64(&op->dev,
hp->happy_bursts);
gcfg |= GREG_CFG_64BIT;
}
} #endif
/* Turn off interrupts we do not want to hear. */
hme_write32(hp, gregs + GREG_IMASK,
(GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP |
GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR));
/* Set the transmit ring buffer size. */
HMD("tx rsize=%d oreg[%08x]\n", (int)TX_RING_SIZE,
hme_read32(hp, etxregs + ETX_RSIZE));
hme_write32(hp, etxregs + ETX_RSIZE, (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1);
/* This chip really rots, for the receiver sometimes when you * write to its control registers not all the bits get there * properly. I cannot think of a sane way to provide complete * coverage for this hardware bug yet.
*/
HMD("erx regs bug old[%08x]\n",
hme_read32(hp, erxregs + ERX_CFG));
hme_write32(hp, erxregs + ERX_CFG, ERX_CFG_DEFAULT(RX_OFFSET));
regtmp = hme_read32(hp, erxregs + ERX_CFG);
hme_write32(hp, erxregs + ERX_CFG, ERX_CFG_DEFAULT(RX_OFFSET)); if (hme_read32(hp, erxregs + ERX_CFG) != ERX_CFG_DEFAULT(RX_OFFSET)) {
netdev_err(hp->dev, "Eieee, rx config register gets greasy fries.\n");
netdev_err(hp->dev, "Trying to set %08x, reread gives %08x\n",
ERX_CFG_DEFAULT(RX_OFFSET), regtmp); /* XXX Should return failure here... */
}
/* Let the bits settle in the chip. */
udelay(10);
/* Ok, configure the Big Mac transmitter. */
HMD("BIGMAC init\n");
regtmp = 0; if (hp->happy_flags & HFLAG_FULL)
regtmp |= BIGMAC_TXCFG_FULLDPLX;
/* Don't turn on the "don't give up" bit for now. It could cause hme * to deadlock with the PHY if a Jabber occurs.
*/
hme_write32(hp, bregs + BMAC_TXCFG, regtmp /*| BIGMAC_TXCFG_DGIVEUP*/);
/* Give up after 16 TX attempts. */
hme_write32(hp, bregs + BMAC_ALIMIT, 16);
/* Enable the output drivers no matter what. */
regtmp = BIGMAC_XCFG_ODENABLE;
/* If card can do lance mode, enable it. */ if (hp->happy_flags & HFLAG_LANCE)
regtmp |= (DEFAULT_IPG0 << 5) | BIGMAC_XCFG_LANCE;
/* Disable the MII buffers if using external transceiver. */ if (hp->tcvr_type == external)
regtmp |= BIGMAC_XCFG_MIIDISAB;
/* Once status is latched (by happy_meal_interrupt) it is cleared by * the hardware, so we cannot re-read it and get a correct value. * * hp->happy_lock must be held
*/ staticint happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status)
{ int reset = 0;
/* Only print messages for non-counter related interrupts. */ if (status & (GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND |
GREG_STAT_MAXPKTERR | GREG_STAT_RXERR |
GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR |
GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR |
GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR |
GREG_STAT_SLVPERR))
netdev_err(hp->dev, "Error interrupt for happy meal, status = %08x\n",
status);
if (status & GREG_STAT_RFIFOVF) { /* Receive FIFO overflow is harmless and the hardware will take
care of it, just some packets are lost. Who cares. */
netdev_dbg(hp->dev, "Happy Meal receive FIFO overflow.\n");
}
if (status & GREG_STAT_STSTERR) { /* BigMAC SQE link test failed. */
netdev_err(hp->dev, "Happy Meal BigMAC SQE test failed.\n");
reset = 1;
}
if (status & GREG_STAT_MAXPKTERR) { /* Driver error, tried to transmit something larger * than ethernet max mtu.
*/
netdev_err(hp->dev, "Happy Meal MAX Packet size error.\n");
reset = 1;
}
if (status & GREG_STAT_NORXD) { /* This is harmless, it just means the system is * quite loaded and the incoming packet rate was * faster than the interrupt handler could keep up * with.
*/
netdev_info(hp->dev, "Happy Meal out of receive descriptors, packet dropped.\n");
}
if (status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) { /* All sorts of DMA receive errors. */
netdev_err(hp->dev, "Happy Meal rx DMA errors [ %s%s%s]\n",
status & GREG_STAT_RXERR ? "GenericError " : "",
status & GREG_STAT_RXPERR ? "ParityError " : "",
status & GREG_STAT_RXTERR ? "RxTagBotch " : "");
reset = 1;
}
if (status & GREG_STAT_EOPERR) { /* Driver bug, didn't set EOP bit in tx descriptor given * to the happy meal.
*/
netdev_err(hp->dev, "EOP not set in happy meal transmit descriptor!\n");
reset = 1;
}
if (status & GREG_STAT_MIFIRQ) { /* MIF signalled an interrupt, were we polling it? */
netdev_err(hp->dev, "Happy Meal MIF interrupt.\n");
}
if (status &
(GREG_STAT_TXEACK|GREG_STAT_TXLERR|GREG_STAT_TXPERR|GREG_STAT_TXTERR)) { /* All sorts of transmit DMA errors. */
netdev_err(hp->dev, "Happy Meal tx DMA errors [ %s%s%s%s]\n",
status & GREG_STAT_TXEACK ? "GenericError " : "",
status & GREG_STAT_TXLERR ? "LateError " : "",
status & GREG_STAT_TXPERR ? "ParityError " : "",
status & GREG_STAT_TXTERR ? "TagBotch " : "");
reset = 1;
}
if (status & (GREG_STAT_SLVERR|GREG_STAT_SLVPERR)) { /* Bus or parity error when cpu accessed happy meal registers * or it's internal FIFO's. Should never see this.
*/
netdev_err(hp->dev, "Happy Meal register access SBUS slave (%s) error.\n",
(status & GREG_STAT_SLVPERR) ? "parity" : "generic");
reset = 1;
}
if (netif_queue_stopped(dev) &&
TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1))
netif_wake_queue(dev);
}
/* Originally I used to handle the allocation failure by just giving back just * that one ring buffer to the happy meal. Problem is that usually when that * condition is triggered, the happy meal expects you to do something reasonable * with all of the packets it has DMA'd in. So now I just drop the entire * ring when we cannot get a new skb and give them all back to the happy meal, * maybe things will be "happier" now. * * hp->happy_lock must be held
*/ staticvoid happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
{ struct happy_meal_rxd *rxbase = &hp->happy_block->happy_meal_rxd[0]; struct happy_meal_rxd *this; int elem = hp->rx_new, drops = 0;
u32 flags;
this = &rxbase[elem]; while (!((flags = hme_read_desc32(hp, &this->rx_flags)) & RXFLAG_OWN)) { struct sk_buff *skb; int len = flags >> 16;
u16 csum = flags & RXFLAG_CSUM;
u32 dma_addr = hme_read_desc32(hp, &this->rx_addr);
/* Check for errors. */ if ((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) {
netdev_vdbg(dev, "RX[%d ERR(%08x)]", elem, flags);
dev->stats.rx_errors++; if (len < ETH_ZLEN)
dev->stats.rx_length_errors++; if (len & (RXFLAG_OVERFLOW >> 16)) {
dev->stats.rx_over_errors++;
dev->stats.rx_fifo_errors++;
}
/* Return it to the Happy meal. */
drop_it:
dev->stats.rx_dropped++;
hme_write_rxd(hp, this,
(RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
dma_addr); goto next;
}
skb = hp->rx_skbs[elem]; if (len > RX_COPY_THRESHOLD) { struct sk_buff *new_skb;
u32 mapping;
/* Now refill the entry, if we can. */
new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); if (new_skb == NULL) {
drops++; goto drop_it;
}
skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
mapping = dma_map_single(hp->dma_dev, new_skb->data,
RX_BUF_ALLOC_SIZE,
DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(hp->dma_dev, mapping))) {
dev_kfree_skb_any(new_skb);
drops++; goto drop_it;
}
if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) {
netif_stop_queue(dev);
spin_unlock_irq(&hp->happy_lock);
netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); return NETDEV_TX_BUSY;
}
/* We must give this initial chunk to the device last. * Otherwise we could race with the device.
*/
first_len = skb_headlen(skb);
first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len,
DMA_TO_DEVICE); if (unlikely(dma_mapping_error(hp->dma_dev, first_mapping))) goto out_dma_error;
entry = NEXT_TX(entry);
/* Verify the settings we care about. */ if (cmd->base.autoneg != AUTONEG_ENABLE &&
cmd->base.autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->base.autoneg == AUTONEG_DISABLE &&
((cmd->base.speed != SPEED_100 &&
cmd->base.speed != SPEED_10) ||
(cmd->base.duplex != DUPLEX_HALF &&
cmd->base.duplex != DUPLEX_FULL))) return -EINVAL;
/* Ok, do it to it. */
spin_lock_irq(&hp->happy_lock);
timer_delete(&hp->happy_timer);
happy_meal_begin_auto_negotiation(hp, hp->tcvregs, cmd);
spin_unlock_irq(&hp->happy_lock);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.