patch-2.4.21 linux-2.4.21/drivers/net/pcmcia/smc91c92_cs.c
Next file: linux-2.4.21/drivers/net/pcmcia/wavelan_cs.c
Previous file: linux-2.4.21/drivers/net/pcmcia/ray_cs.c
Back to the patch index
Back to the overall index
- Lines: 1207
- Date:
2003-06-13 07:51:35.000000000 -0700
- Orig file:
linux-2.4.20/drivers/net/pcmcia/smc91c92_cs.c
- Orig date:
2002-11-28 15:53:14.000000000 -0800
diff -urN linux-2.4.20/drivers/net/pcmcia/smc91c92_cs.c linux-2.4.21/drivers/net/pcmcia/smc91c92_cs.c
@@ -8,8 +8,8 @@
Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
- smc91c92_cs.c 1.2 2002/09/28 15:00:00
-
+ smc91c92_cs.c 1.122 2002/10/25 06:26:39
+
This driver contains code written by Donald Becker
(becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
@@ -18,7 +18,7 @@
incorporated some parts of his driver here. I (Dave) wrote most
of the PCMCIA glue code, and the Ositech support code. Kelly
Stephens (kstephen@holli.com) added support for the Motorola
- Mariner, with help from Allen Brost.
+ Mariner, with help from Allen Brost.
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
@@ -92,7 +92,7 @@
#endif
#define DRV_NAME "smc91c92_cs"
-#define DRV_VERSION "1.2"
+#define DRV_VERSION "1.122"
/*====================================================================*/
@@ -130,6 +130,8 @@
u_short fast_poll;
u_short link_status;
struct mii_if_info mii_if;
+ int duplex;
+ int rx_ovrn;
};
/* Special definitions for Megahertz multifunction cards */
@@ -257,7 +259,7 @@
#define MULTICAST2 2
#define MULTICAST4 4
#define MULTICAST6 6
-#define MGMT 8
+#define MGMT 8
#define REVISION 0x0a
/* Transmit status bits. */
@@ -287,22 +289,22 @@
static int smc91c92_event(event_t event, int priority,
event_callback_args_t *args);
-static int smc91c92_open(struct net_device *dev);
-static int smc91c92_close(struct net_device *dev);
+static int smc_open(struct net_device *dev);
+static int smc_close(struct net_device *dev);
+static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void smc_tx_timeout(struct net_device *dev);
static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void smc_rx(struct net_device *dev);
-static struct net_device_stats *smc91c92_get_stats(struct net_device *dev);
+static struct net_device_stats *smc_get_stats(struct net_device *dev);
static void set_rx_mode(struct net_device *dev);
static int s9k_config(struct net_device *dev, struct ifmap *map);
static void smc_set_xcvr(struct net_device *dev, int if_port);
static void smc_reset(struct net_device *dev);
static void media_check(u_long arg);
-static void smc_mdio_sync(ioaddr_t addr);
-static int smc_mdio_read(struct net_device *dev, int phy_id, int loc);
-static void smc_mdio_write(struct net_device *dev, int phy_id, int loc, int value);
-static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void mdio_sync(ioaddr_t addr);
+static int mdio_read(struct net_device *dev, int phy_id, int loc);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
static int smc_link_ok(struct net_device *dev);
/*======================================================================
@@ -310,7 +312,7 @@
This bit of code is used to avoid unregistering network devices
at inappropriate times. 2.2 and later kernels are fairly picky
about when this can happen.
-
+
======================================================================*/
static void flush_stale_links(void)
@@ -349,7 +351,7 @@
DEBUG(0, "smc91c92_attach()\n");
flush_stale_links();
-
+
/* Create new ethernet device */
smc = kmalloc(sizeof(struct smc_private), GFP_KERNEL);
if (!smc) return NULL;
@@ -375,25 +377,25 @@
/* The SMC91c92-specific entries in the device structure. */
dev->hard_start_xmit = &smc_start_xmit;
- dev->get_stats = &smc91c92_get_stats;
+ dev->get_stats = &smc_get_stats;
dev->set_config = &s9k_config;
dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &smc_ioctl;
ether_setup(dev);
- dev->open = &smc91c92_open;
- dev->stop = &smc91c92_close;
+ dev->open = &smc_open;
+ dev->stop = &smc_close;
+ dev->do_ioctl = &smc_ioctl;
#ifdef HAVE_TX_TIMEOUT
dev->tx_timeout = smc_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#endif
dev->priv = link->priv = link->irq.Instance = smc;
-
+
smc->mii_if.dev = dev;
- smc->mii_if.mdio_read = smc_mdio_read;
- smc->mii_if.mdio_write = smc_mdio_write;
+ smc->mii_if.mdio_read = mdio_read;
+ smc->mii_if.mdio_write = mdio_write;
smc->mii_if.phy_id_mask = 0x1f;
smc->mii_if.reg_num_mask = 0x1f;
-
+
/* Register with Card Services */
link->next = dev_list;
dev_list = link;
@@ -411,7 +413,7 @@
smc91c92_detach(link);
return NULL;
}
-
+
return link;
} /* smc91c92_attach */
@@ -430,13 +432,13 @@
dev_link_t **linkp;
DEBUG(0, "smc91c92_detach(0x%p)\n", link);
-
+
/* Locate device structure */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
if (*linkp == link) break;
if (*linkp == NULL)
return;
-
+
del_timer(&link->release);
if (link->state & DEV_CONFIG) {
smc91c92_release((u_long)link);
@@ -445,16 +447,16 @@
return;
}
}
-
+
if (link->handle)
CardServices(DeregisterClient, link->handle);
-
+
/* Unlink device structure, free bits */
*linkp = link->next;
if (link->dev)
unregister_netdev(&smc->dev);
kfree(smc);
-
+
} /* smc91c92_detach */
/*====================================================================*/
@@ -502,14 +504,14 @@
mhz_mfc_config() handles socket setup for multifunction (1144
and 3288) cards. mhz_setup() gets a card's hardware ethernet
address.
-
+
======================================================================*/
static int mhz_3288_power(dev_link_t *link)
{
struct smc_private *smc = link->priv;
u_char tmp;
-
+
/* Read the ISR twice... */
readb(smc->base+MEGAHERTZ_ISR);
udelay(5);
@@ -517,7 +519,7 @@
/* Pause 200ms... */
mdelay(200);
-
+
/* Now read and write the COR... */
tmp = readb(smc->base + link->conf.ConfigBase + CISREG_COR);
udelay(5);
@@ -569,7 +571,7 @@
if (i != CS_SUCCESS)
return i;
dev->base_addr = link->io.BasePort1;
-
+
/* Allocate a memory window, for accessing the ISR */
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = req.Size = 0;
@@ -583,12 +585,12 @@
if (smc->manfid == MANFID_MOTOROLA)
mem.CardOffset = link->conf.ConfigBase;
i = CardServices(MapMemPage, link->win, &mem);
-
+
if ((i == CS_SUCCESS)
&& (smc->manfid == MANFID_MEGAHERTZ)
&& (smc->cardid == PRODID_MEGAHERTZ_EM3288))
mhz_3288_power(link);
-
+
return i;
}
@@ -628,7 +630,7 @@
buf[12] = '\0';
if (cvt_ascii_address(dev, buf) == 0)
return 0;
-
+
return -1;
}
@@ -638,7 +640,7 @@
mot_config() writes directly to the Mariner configuration
registers because the CIS is just bogus.
-
+
======================================================================*/
static void mot_config(dev_link_t *link)
@@ -647,12 +649,12 @@
struct net_device *dev = &smc->dev;
ioaddr_t ioaddr = dev->base_addr;
ioaddr_t iouart = link->io.BasePort2;
-
+
/* Set UART base address and force map with COR bit 1 */
writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0);
writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR);
-
+
/* Set SMC base address and force map with COR bit 1 */
writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0);
writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
@@ -671,7 +673,7 @@
u_int addr;
/* Read Ethernet address from Serial EEPROM */
-
+
for (i = 0; i < 3; i++) {
SMC_SELECT_BANK(2);
outw(MOT_EEPROM + i, ioaddr + POINTER);
@@ -691,7 +693,7 @@
dev->dev_addr[2*i] = addr & 0xff;
dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
}
-
+
return 0;
}
@@ -743,7 +745,7 @@
tuple.Attributes = tuple.TupleOffset = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
-
+
/* Check for a LAN function extension tuple */
tuple.DesiredTuple = CISTPL_FUNCE;
i = first_tuple(handle, &tuple, &parse);
@@ -760,7 +762,7 @@
return 0;
}
}
-
+
/* Try the third string in the Version 1 Version/ID tuple. */
tuple.DesiredTuple = CISTPL_VERS_1;
if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
@@ -780,7 +782,7 @@
struct net_device *dev = &smc->dev;
static ioaddr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
int i, j;
-
+
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
link->irq.Attributes =
@@ -789,10 +791,10 @@
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
link->io.IOAddrLines = 16;
-
+
/* Enable Hard Decode, LAN, Modem */
link->conf.ConfigIndex = 0x23;
-
+
for (i = j = 0; j < 4; j++) {
link->io.BasePort2 = com[j];
i = CardServices(RequestIO, link->handle, &link->io);
@@ -816,12 +818,12 @@
tuple_t tuple;
u_char buf[255];
int i;
-
+
tuple.Attributes = TUPLE_RETURN_COMMON;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
-
+
/* Read the station address from tuple 0x90, subtuple 0x04 */
tuple.DesiredTuple = 0x90;
i = CardServices(GetFirstTuple, handle, &tuple);
@@ -862,7 +864,7 @@
This verifies that the chip is some SMC91cXX variant, and returns
the revision code if successful. Otherwise, it returns -ENODEV.
-
+
======================================================================*/
static int check_sig(dev_link_t *link)
@@ -888,7 +890,7 @@
else
s &= ~CFG_16BIT;
outb(s, ioaddr + CONFIG);
-
+
/* Check Base Address Register to make sure bus width is OK */
s = inw(ioaddr + BASE_ADDR);
if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
@@ -936,7 +938,7 @@
ioaddr_t ioaddr;
DEBUG(0, "smc91c92_config(0x%p)\n", link);
-
+
tuple.Attributes = tuple.TupleOffset = 0;
tuple.TupleData = (cisdata_t *)buf;
tuple.TupleDataMax = sizeof(buf);
@@ -946,14 +948,14 @@
CS_EXIT_TEST(i, ParseTuple, config_failed);
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
-
+
tuple.DesiredTuple = CISTPL_MANFID;
tuple.Attributes = TUPLE_RETURN_COMMON;
if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
smc->manfid = parse.manfid.manf;
smc->cardid = parse.manfid.card;
}
-
+
/* Configure card */
link->state |= DEV_CONFIG;
@@ -969,12 +971,12 @@
i = smc_config(link);
}
CS_EXIT_TEST(i, RequestIO, config_failed);
-
+
i = CardServices(RequestIRQ, link->handle, &link->irq);
CS_EXIT_TEST(i, RequestIRQ, config_failed);
i = CardServices(RequestConfiguration, link->handle, &link->conf);
CS_EXIT_TEST(i, RequestConfiguration, config_failed);
-
+
if (smc->manfid == MANFID_MOTOROLA)
mot_config(link);
@@ -984,7 +986,7 @@
dev->if_port = if_port;
else
printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n");
-
+
if (register_netdev(dev) != 0) {
printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
goto config_undo;
@@ -1004,16 +1006,16 @@
default: /* get the hw address from EEPROM */
i = mot_setup(link); break;
}
-
+
if (i != 0) {
printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n");
- link->state &= ~DEV_CONFIG_PENDING;
goto config_undo;
}
strcpy(smc->node.dev_name, dev->name);
link->dev = &smc->node;
- link->state &= ~DEV_CONFIG_PENDING;
+ smc->duplex = 0;
+ smc->rx_ovrn = 0;
rev = check_sig(link);
name = "???";
@@ -1055,12 +1057,12 @@
printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ?
"MII" : if_names[dev->if_port]);
}
-
+
if (smc->cfg & CFG_MII_SELECT) {
SMC_SELECT_BANK(3);
for (i = 0; i < 32; i++) {
- j = smc_mdio_read(dev, i, 1);
+ j = mdio_read(dev, i, 1);
if ((j != 0) && (j != 0xffff)) break;
}
smc->mii_if.phy_id = (i < 32) ? i : -1;
@@ -1073,13 +1075,15 @@
SMC_SELECT_BANK(0);
}
+ link->state &= ~DEV_CONFIG_PENDING;
return;
-
+
config_undo:
unregister_netdev(dev);
config_failed: /* CS_EXIT_TEST() calls jump to here... */
smc91c92_release((u_long)link);
-
+ link->state &= ~DEV_CONFIG_PENDING;
+
} /* smc91c92_config */
/*======================================================================
@@ -1096,14 +1100,14 @@
struct smc_private *smc = link->priv;
DEBUG(0, "smc91c92_release(0x%p)\n", link);
-
+
if (link->open) {
DEBUG(1, "smc91c92_cs: release postponed, '%s' still open\n",
link->dev->dev_name);
link->state |= DEV_STALE_CONFIG;
return;
}
-
+
CardServices(ReleaseConfiguration, link->handle);
CardServices(ReleaseIO, link->handle, &link->io);
CardServices(ReleaseIRQ, link->handle, &link->irq);
@@ -1111,7 +1115,7 @@
iounmap(smc->base);
CardServices(ReleaseWindow, link->win);
}
-
+
link->state &= ~DEV_CONFIG;
} /* smc91c92_release */
@@ -1131,8 +1135,8 @@
dev_link_t *link = args->client_data;
struct smc_private *smc = link->priv;
struct net_device *dev = &smc->dev;
- int i;
-
+ int i;
+
DEBUG(1, "smc91c92_event(0x%06x)\n", event);
switch (event) {
@@ -1144,7 +1148,7 @@
}
break;
case CS_EVENT_CARD_INSERTION:
- link->state |= DEV_PRESENT;
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
smc91c92_config(link);
break;
case CS_EVENT_PM_SUSPEND:
@@ -1206,7 +1210,7 @@
#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
#define MDIO_DATA_READ 0x02
-static void smc_mdio_sync(ioaddr_t addr)
+static void mdio_sync(ioaddr_t addr)
{
int bits;
for (bits = 0; bits < 32; bits++) {
@@ -1215,13 +1219,13 @@
}
}
-static int smc_mdio_read(struct net_device *dev, int phy_id, int loc)
+static int mdio_read(struct net_device *dev, int phy_id, int loc)
{
ioaddr_t addr = dev->base_addr + MGMT;
u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
int i, retval = 0;
- smc_mdio_sync(addr);
+ mdio_sync(addr);
for (i = 13; i >= 0; i--) {
int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
outb(dat, addr);
@@ -1235,13 +1239,13 @@
return (retval>>1) & 0xffff;
}
-static void smc_mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
{
ioaddr_t addr = dev->base_addr + MGMT;
u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
int i;
- smc_mdio_sync(addr);
+ mdio_sync(addr);
for (i = 31; i >= 0; i--) {
int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
outb(dat, addr);
@@ -1254,10 +1258,10 @@
}
/*======================================================================
-
+
The driver core code, most of which should be common with a
non-PCMCIA implementation.
-
+
======================================================================*/
#ifdef PCMCIA_DEBUG
@@ -1277,17 +1281,17 @@
}
#endif
-static int smc91c92_open(struct net_device *dev)
+static int smc_open(struct net_device *dev)
{
struct smc_private *smc = dev->priv;
dev_link_t *link = &smc->link;
#ifdef PCMCIA_DEBUG
- DEBUG(0, "%s: smc91c92_open(%p), ID/Window %4.4x.\n",
+ DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n",
dev->name, dev, inw(dev->base_addr + BANK_SELECT));
if (pc_debug > 1) smc_dump(dev);
#endif
-
+
/* Check that the PCMCIA card is still here. */
if (!DEV_OK(link))
return -ENODEV;
@@ -1302,25 +1306,25 @@
netif_start_queue(dev);
smc->saved_skb = 0;
smc->packets_waiting = 0;
-
+
smc_reset(dev);
smc->media.function = &media_check;
smc->media.data = (u_long)smc;
smc->media.expires = jiffies + HZ;
add_timer(&smc->media);
-
+
return 0;
-} /* smc91c92_open */
+} /* smc_open */
/*====================================================================*/
-static int smc91c92_close(struct net_device *dev)
+static int smc_close(struct net_device *dev)
{
struct smc_private *smc = dev->priv;
dev_link_t *link = &smc->link;
ioaddr_t ioaddr = dev->base_addr;
- DEBUG(0, "%s: smc91c92_close(), status %4.4x.\n",
+ DEBUG(0, "%s: smc_close(), status %4.4x.\n",
dev->name, inw(ioaddr + BANK_SELECT));
netif_stop_queue(dev);
@@ -1332,27 +1336,27 @@
SMC_SELECT_BANK(0);
mask_bits(0xff00, ioaddr + RCR);
mask_bits(0xff00, ioaddr + TCR);
-
+
/* Put the chip into power-down mode. */
SMC_SELECT_BANK(1);
outw(CTL_POWERDOWN, ioaddr + CONTROL );
-
+
link->open--;
del_timer(&smc->media);
if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20);
-
+
MOD_DEC_USE_COUNT;
-
+
return 0;
-} /* smc91c92_close */
+} /* smc_close */
/*======================================================================
Transfer a packet to the hardware and trigger the packet send.
This may be called at either from either the Tx queue code
or the interrupt handler.
-
+
======================================================================*/
static void smc_hardware_send_packet(struct net_device * dev)
@@ -1361,12 +1365,12 @@
struct sk_buff *skb = smc->saved_skb;
ioaddr_t ioaddr = dev->base_addr;
u_char packet_no;
-
+
if (!skb) {
printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name);
return;
}
-
+
/* There should be a packet slot waiting. */
packet_no = inw(ioaddr + PNR_ARR) >> 8;
if (packet_no & 0x80) {
@@ -1384,7 +1388,7 @@
outw(packet_no, ioaddr + PNR_ARR);
/* point to the beginning of the packet */
outw(PTR_AUTOINC , ioaddr + POINTER);
-
+
/* Send the packet length (+6 for status, length and ctl byte)
and the status word (set to zeros). */
{
@@ -1402,15 +1406,15 @@
/* The odd last byte, if there is one, goes in the control word. */
outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
}
-
+
/* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
(inw(ioaddr + INTERRUPT) & 0xff00),
ioaddr + INTERRUPT);
-
+
/* The chip does the rest of the work. */
outw(MC_ENQUEUE , ioaddr + MMU_CMD);
-
+
smc->saved_skb = NULL;
dev_kfree_skb_irq(skb);
dev->trans_start = jiffies;
@@ -1444,9 +1448,9 @@
netif_stop_queue(dev);
- DEBUG(2, "%s: smc91c92_start_xmit(length = %d) called,"
+ DEBUG(2, "%s: smc_start_xmit(length = %d) called,"
" status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2));
-
+
if (smc->saved_skb) {
/* THIS SHOULD NEVER HAPPEN. */
smc->stats.tx_aborted_errors++;
@@ -1455,9 +1459,9 @@
return 1;
}
smc->saved_skb = skb;
-
+
num_pages = skb->len >> 8;
-
+
if (num_pages > 7) {
printk(KERN_ERR "%s: Far too big packet error.\n", dev->name);
dev_kfree_skb (skb);
@@ -1467,9 +1471,15 @@
}
/* A packet is now waiting. */
smc->packets_waiting++;
-
+
SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */
-
+
+ /* need MC_RESET to keep the memory consistent. errata? */
+ if (smc->rx_ovrn) {
+ outw(MC_RESET, ioaddr + MMU_CMD);
+ smc->rx_ovrn = 0;
+ }
+
/* Allocate the memory; send the packet now if we win. */
outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
@@ -1481,18 +1491,18 @@
return 0;
}
}
-
+
/* Otherwise defer until the Tx-space-allocated interrupt. */
DEBUG(2, "%s: memory allocation deferred.\n", dev->name);
outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
-
+
return 0;
}
/*======================================================================
Handle a Tx anomolous event. Entered while in Window 2.
-
+
======================================================================*/
static void smc_tx_err(struct net_device * dev)
@@ -1502,13 +1512,13 @@
int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
int tx_status;
-
+
/* select this as the packet to read from */
outw(packet_no, ioaddr + PNR_ARR);
-
+
/* read the first word from this packet */
outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
-
+
tx_status = inw(ioaddr + DATA_1);
smc->stats.tx_errors++;
@@ -1518,21 +1528,21 @@
smc->stats.tx_aborted_errors++;
smc->tx_err++;
}
-
+
if (tx_status & TS_SUCCESS) {
printk(KERN_NOTICE "%s: Successful packet caused error "
"interrupt?\n", dev->name);
}
/* re-enable transmit */
SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+ outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
SMC_SELECT_BANK(2);
-
+
outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */
-
+
/* one less packet waiting for me */
smc->packets_waiting--;
-
+
outw(saved_packet, ioaddr + PNR_ARR);
return;
}
@@ -1544,7 +1554,7 @@
struct smc_private *smc = dev->priv;
ioaddr_t ioaddr = dev->base_addr;
u_short card_stats, ephs;
-
+
SMC_SELECT_BANK(0);
ephs = inw(ioaddr + EPH);
DEBUG(2, "%s: Ethernet protocol handler interrupt, status"
@@ -1561,7 +1571,7 @@
card_stats >>= 4; /* excess deferred */
#endif
/* If we had a transmit error we must re-enable the transmitter. */
- outw(inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+ outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
/* Clear a link error interrupt. */
SMC_SELECT_BANK(1);
@@ -1572,7 +1582,7 @@
}
/*====================================================================*/
-
+
static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct smc_private *smc = dev_id;
@@ -1584,10 +1594,10 @@
if (!netif_device_present(dev))
return;
ioaddr = dev->base_addr;
-
+
DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
irq, ioaddr);
-
+
smc->watchdog = 0;
saved_bank = inw(ioaddr + BANK_SELECT);
if ((saved_bank & 0xff00) != 0x3300) {
@@ -1597,13 +1607,13 @@
"/ejected device.\n", dev->name, irq);
goto irq_done;
}
-
+
SMC_SELECT_BANK(2);
saved_pointer = inw(ioaddr + POINTER);
mask = inw(ioaddr + INTERRUPT) >> 8;
/* clear all interrupts */
outw(0, ioaddr + INTERRUPT);
-
+
do { /* read the status flag, and mask it */
status = inw(ioaddr + INTERRUPT) & 0xff;
DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
@@ -1629,18 +1639,20 @@
if (status & IM_ALLOC_INT) {
/* Clear this interrupt so it doesn't happen again */
mask &= ~IM_ALLOC_INT;
-
+
smc_hardware_send_packet(dev);
-
+
/* enable xmit interrupts based on this */
mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
-
+
/* and let the card send more packets to me */
netif_wake_queue(dev);
}
if (status & IM_RX_OVRN_INT) {
smc->stats.rx_errors++;
- smc->stats.rx_fifo_errors++;
+ smc->stats.rx_fifo_errors++;
+ if (smc->duplex)
+ smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
}
if (status & IM_EPH_INT)
@@ -1649,7 +1661,7 @@
DEBUG(3, " Restoring saved registers mask %2.2x bank %4.4x"
" pointer %4.4x.\n", mask, saved_bank, saved_pointer);
-
+
/* restore state register */
outw((mask<<8), ioaddr + INTERRUPT);
outw(saved_pointer, ioaddr + POINTER);
@@ -1658,7 +1670,7 @@
DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
irq_done:
-
+
if ((smc->manfid == MANFID_OSITECH) &&
(smc->cardid != PRODID_OSITECH_SEVEN)) {
/* Retrigger interrupt if needed */
@@ -1691,15 +1703,15 @@
int rx_status;
int packet_length; /* Caution: not frame length, rather words
to transfer from the chip. */
-
+
/* Assertion: we are in Window 2. */
-
+
if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n",
dev->name);
return;
}
-
+
/* Reset the read pointer, and read the status and packet length. */
outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
rx_status = inw(ioaddr + DATA_1);
@@ -1707,7 +1719,7 @@
DEBUG(2, "%s: Receive status %4.4x length %d.\n",
dev->name, rx_status, packet_length);
-
+
if (!(rx_status & RS_ERRORS)) {
/* do stuff to make a new packet */
struct sk_buff *skb;
@@ -1725,7 +1737,7 @@
packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
skb_reserve(skb, 2);
insw(ioaddr+DATA_1, skb_put(skb, packet_length),
- (packet_length+1)>>1);
+ (packet_length+1)>>1);
skb->protocol = eth_type_trans(skb, dev);
skb->dev = dev;
@@ -1746,31 +1758,30 @@
}
/* Let the MMU free the memory of this packet. */
outw(MC_RELEASE, ioaddr + MMU_CMD);
-
+
return;
}
/*====================================================================*/
-static struct net_device_stats *smc91c92_get_stats(struct net_device *dev)
+static struct net_device_stats *smc_get_stats(struct net_device *dev)
{
struct smc_private *smc = (struct smc_private *)dev->priv;
/* Nothing to update - the 91c92 is a pretty primative chip. */
return &smc->stats;
}
-
/*======================================================================
-
+
Calculate values for the hardware multicast filter hash table.
-
+
======================================================================*/
static void fill_multicast_tbl(int count, struct dev_mc_list *addrs,
u_char *multicast_table)
{
struct dev_mc_list *mc_addr;
-
+
for (mc_addr = addrs; mc_addr && --count > 0; mc_addr = mc_addr->next) {
u_int position = ether_crc(6, mc_addr->dmi_addr);
#ifndef final_version /* Verify multicast address. */
@@ -1782,14 +1793,14 @@
}
/*======================================================================
-
+
Set the receive mode.
-
+
This routine is used by both the protocol level to notify us of
promiscuous/multicast mode changes, and by the open/reset code to
initialize the Rx registers. We always set the multicast list and
leave the receiver running.
-
+
======================================================================*/
static void set_rx_mode(struct net_device *dev)
@@ -1799,7 +1810,7 @@
u_int multicast_table[ 2 ] = { 0, };
unsigned long flags;
u_short rx_cfg_setting;
-
+
if (dev->flags & IFF_PROMISC) {
printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name);
rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
@@ -1812,7 +1823,7 @@
}
rx_cfg_setting = RxStripCRC | RxEnable;
}
-
+
/* Load MC table and Rx setting into the chip without interrupts. */
spin_lock_irqsave(&smc->lock, flags);
SMC_SELECT_BANK(3);
@@ -1822,14 +1833,14 @@
outw(rx_cfg_setting, ioaddr + RCR);
SMC_SELECT_BANK(2);
spin_unlock_irqrestore(&smc->lock, flags);
-
+
return;
}
/*======================================================================
Senses when a card's config changes. Here, it's coax or TP.
-
+
======================================================================*/
static int s9k_config(struct net_device *dev, struct ifmap *map)
@@ -1889,18 +1900,18 @@
int i;
DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);
-
+
/* The first interaction must be a write to bring the chip out
of sleep mode. */
SMC_SELECT_BANK(0);
/* Reset the chip. */
outw(RCR_SOFTRESET, ioaddr + RCR);
udelay(10);
-
+
/* Clear the transmit and receive configuration registers. */
outw(RCR_CLEAR, ioaddr + RCR);
outw(TCR_CLEAR, ioaddr + TCR);
-
+
/* Set the Window 1 control, configuration and station addr registers.
No point in writing the I/O base register ;-> */
SMC_SELECT_BANK(1);
@@ -1914,32 +1925,35 @@
outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
(inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
ioaddr - 0x10 + OSITECH_AUI_PWR);
-
+
/* Fill in the physical address. The databook is wrong about the order! */
for (i = 0; i < 6; i += 2)
outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
ioaddr + ADDR0 + i);
-
+
/* Reset the MMU */
SMC_SELECT_BANK(2);
outw(MC_RESET, ioaddr + MMU_CMD);
outw(0, ioaddr + INTERRUPT);
-
+
/* Re-enable the chip. */
SMC_SELECT_BANK(0);
outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
- TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);
+ TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
set_rx_mode(dev);
if (smc->cfg & CFG_MII_SELECT) {
SMC_SELECT_BANK(3);
/* Reset MII */
- smc_mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
+
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
/* Restart MII autonegotiation */
- smc_mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
- smc_mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
}
/* Enable interrupts. */
@@ -1951,7 +1965,7 @@
/*======================================================================
Media selection timer routine
-
+
======================================================================*/
static void media_check(u_long arg)
@@ -1968,12 +1982,18 @@
goto reschedule;
SMC_SELECT_BANK(2);
+
+ /* need MC_RESET to keep the memory consistent. errata? */
+ if (smc->rx_ovrn) {
+ outw(MC_RESET, ioaddr + MMU_CMD);
+ smc->rx_ovrn = 0;
+ }
i = inw(ioaddr + INTERRUPT);
SMC_SELECT_BANK(0);
media = inw(ioaddr + EPH) & EPH_LINK_OK;
SMC_SELECT_BANK(1);
media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
-
+
/* Check for pending interrupt with watchdog flag set: with
this, we can limp along even if the interrupt is blocked */
if (smc->watchdog++ && ((i>>8) & i)) {
@@ -1995,7 +2015,7 @@
goto reschedule;
SMC_SELECT_BANK(3);
- link = smc_mdio_read(dev, smc->mii_if.phy_id, 1);
+ link = mdio_read(dev, smc->mii_if.phy_id, 1);
if (!link || (link == 0xffff)) {
printk(KERN_INFO "%s: MII is missing!\n", dev->name);
smc->mii_if.phy_id = -1;
@@ -2004,21 +2024,23 @@
link &= 0x0004;
if (link != smc->link_status) {
- u_short p = smc_mdio_read(dev, smc->mii_if.phy_id, 5);
+ u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
printk(KERN_INFO "%s: %s link beat\n", dev->name,
(link) ? "found" : "lost");
+ smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
+ ? TCR_FDUPLX : 0);
if (link) {
printk(KERN_INFO "%s: autonegotiation complete: "
- "%sbaseT-%cD selected\n", dev->name,
- ((p & 0x0180) ? "100" : "10"),
- (((p & 0x0100) || ((p & 0x1c0) == 0x40)) ? 'F' : 'H'));
+ "%sbaseT-%cD selected\n", dev->name,
+ ((p & 0x0180) ? "100" : "10"),
+ (smc->duplex ? 'F' : 'H'));
}
+ SMC_SELECT_BANK(0);
+ outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
smc->link_status = link;
}
- }
-
- if (smc->cfg & CFG_MII_SELECT)
goto reschedule;
+ }
/* Ignore collisions unless we've had no rx's recently */
if (jiffies - dev->last_rx > HZ) {
@@ -2053,7 +2075,7 @@
}
smc->media_status = media;
}
-
+
reschedule:
smc->media.expires = jiffies + HZ;
add_timer(&smc->media);
@@ -2167,7 +2189,7 @@
return -EFAULT;
spin_lock_irq(&smc->lock);
if (smc->cfg & CFG_MII_SELECT)
- ret = mii_ethtool_sset(&smc->mii_if, &ecmd);
+ ret = mii_ethtool_sset(&smc->mii_if, &ecmd);
else
ret = smc_netdev_set_ecmd(dev, &ecmd);
spin_unlock_irq(&smc->lock);
@@ -2194,7 +2216,7 @@
return -EFAULT;
return 0;
}
-
+
/* set message-level */
case ETHTOOL_SMSGLVL: {
struct ethtool_value edata;
@@ -2211,7 +2233,7 @@
else
return -EOPNOTSUPP;
}
-
+
default:
break;
}
@@ -2233,7 +2255,7 @@
case SIOCETHTOOL:
rc = smc_ethtool_ioctl(dev, (void *) rq->ifr_data);
break;
-
+
default:
spin_lock_irq(&smc->lock);
rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
@@ -2255,7 +2277,7 @@
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_ERR
"smc91c92_cs: Card Services release does not match!\n");
- return -1;
+ return -EINVAL;
}
register_pccard_driver(&dev_info, &smc91c92_attach, &smc91c92_detach);
return 0;
@@ -2271,4 +2293,3 @@
module_init(init_smc91c92_cs);
module_exit(exit_smc91c92_cs);
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)