patch-2.4.15 linux/drivers/net/pcmcia/smc91c92_cs.c
Next file: linux/drivers/net/pcmcia/wavelan_cs.c
Previous file: linux/drivers/net/pcmcia/pcnet_cs.c
Back to the patch index
Back to the overall index
- Lines: 331
- Date:
Tue Nov 13 09:02:30 2001
- Orig file:
v2.4.14/linux/drivers/net/pcmcia/smc91c92_cs.c
- Orig date:
Tue Oct 9 17:06:52 2001
diff -u --recursive --new-file v2.4.14/linux/drivers/net/pcmcia/smc91c92_cs.c linux/drivers/net/pcmcia/smc91c92_cs.c
@@ -8,7 +8,7 @@
Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
- smc91c92_cs.c 1.106 2001/02/07 00:19:58
+ smc91c92_cs.c 1.113 2001/10/13 00:08:53
This driver contains code written by Donald Becker
(becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
@@ -56,19 +56,14 @@
/*====================================================================*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-static const char *version =
-"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@scyld.com.\n";
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
-
static char *if_names[] = { "auto", "10baseT", "10base2"};
-/* Parameters that can be set with 'insmod' */
+/* Module parameters */
+
+MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
/*
Transceiver/media type.
@@ -76,16 +71,23 @@
1 = 10baseT (and autoselect if #define AUTOSELECT),
2 = AUI/10base2,
*/
-static int if_port;
+INT_MODULE_PARM(if_port, 0);
/* Bit map of interrupts to choose from. */
-static u_int irq_mask = 0xdeb8;
+INT_MODULE_PARM(irq_mask, 0xdeb8);
static int irq_list[4] = { -1 };
-
-MODULE_PARM(if_port, "i");
-MODULE_PARM(irq_mask, "i");
MODULE_PARM(irq_list, "1-4i");
-MODULE_LICENSE("GPL");
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+static const char *version =
+"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@scyld.com.\n";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
/* Operational parameter that usually are not changed. */
@@ -118,7 +120,8 @@
int watchdog, tx_err;
u_short media_status;
u_short fast_poll;
- u_long last_rx;
+ u_short link_status;
+ int phy_id;
};
/* Special definitions for Megahertz multifunction cards */
@@ -246,6 +249,7 @@
#define MULTICAST2 2
#define MULTICAST4 4
#define MULTICAST6 6
+#define MGMT 8
#define REVISION 0x0a
/* Transmit status bits. */
@@ -287,6 +291,9 @@
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 mdio_sync(ioaddr_t addr);
+static int mdio_read(ioaddr_t addr, int phy_id, int loc);
+static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value);
/*======================================================================
@@ -908,7 +915,8 @@
cisparse_t parse;
u_short buf[32];
char *name;
- int i, rev;
+ int i, j, rev;
+ ioaddr_t ioaddr;
DEBUG(0, "smc91c92_config(0x%p)\n", link);
@@ -1006,9 +1014,10 @@
dev->irq);
for (i = 0; i < 6; i++)
printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+ ioaddr = dev->base_addr;
if (rev > 0) {
u_long mir, mcr;
- ioaddr_t ioaddr = dev->base_addr;
SMC_SELECT_BANK(0);
mir = inw(ioaddr + MEMINFO) & 0xff;
if (mir == 0xff) mir++;
@@ -1030,6 +1039,23 @@
"MII" : if_names[dev->if_port]);
}
+ if (smc->cfg & CFG_MII_SELECT) {
+ SMC_SELECT_BANK(3);
+
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev->base_addr + MGMT, i, 1);
+ if ((j != 0) && (j != 0xffff)) break;
+ }
+ smc->phy_id = (i < 32) ? i : -1;
+ if (i < 32) {
+ DEBUG(0, " MII transceiver at index %d, status %x.\n", i, j);
+ } else {
+ printk(KERN_NOTICE " No MII transceivers found!\n");
+ }
+
+ SMC_SELECT_BANK(0);
+ }
+
return;
config_undo:
@@ -1088,7 +1114,8 @@
dev_link_t *link = args->client_data;
struct smc_private *smc = link->priv;
struct net_device *dev = &smc->dev;
-
+ int i;
+
DEBUG(1, "smc91c92_event(0x%06x)\n", event);
switch (event) {
@@ -1130,6 +1157,16 @@
set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
}
+ if (((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid == PRODID_OSITECH_SEVEN)) ||
+ ((smc->manfid == MANFID_PSION) &&
+ (smc->cardid == PRODID_PSION_NET100))) {
+ /* Download the Seven of Diamonds firmware */
+ for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+ outb(__Xilinx7OD[i], link->io.BasePort1+2);
+ udelay(50);
+ }
+ }
if (link->open) {
smc_reset(dev);
netif_device_attach(dev);
@@ -1141,6 +1178,63 @@
} /* smc91c92_event */
/*======================================================================
+
+ MII interface support for SMC91cXX based cards
+======================================================================*/
+
+#define MDIO_SHIFT_CLK 0x04
+#define MDIO_DATA_OUT 0x01
+#define MDIO_DIR_WRITE 0x08
+#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ 0x02
+
+static void mdio_sync(ioaddr_t addr)
+{
+ int bits;
+ for (bits = 0; bits < 32; bits++) {
+ outb(MDIO_DATA_WRITE1, addr);
+ outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(ioaddr_t addr, int phy_id, int loc)
+{
+ u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+ int i, retval = 0;
+
+ mdio_sync(addr);
+ for (i = 13; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(dat, addr);
+ outb(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb(0, addr);
+ retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+ outb(MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value)
+{
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(dat, addr);
+ outb(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb(0, addr);
+ outb(MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*======================================================================
The driver core code, most of which should be common with a
non-PCMCIA implementation.
@@ -1501,7 +1595,6 @@
if (status & IM_RCV_INT) {
/* Got a packet(s). */
smc_rx(dev);
- smc->last_rx = jiffies;
}
if (status & IM_TX_INT) {
smc_tx_err(dev);
@@ -1844,6 +1937,17 @@
TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);
set_rx_mode(dev);
+ if (smc->cfg & CFG_MII_SELECT) {
+ SMC_SELECT_BANK(3);
+
+ /* Reset MII */
+ mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x8000);
+
+ /* Restart MII autonegotiation */
+ mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x0000);
+ mdio_write(ioaddr + MGMT, smc->phy_id, 0, 0x1200);
+ }
+
/* Enable interrupts. */
SMC_SELECT_BANK(2);
outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
@@ -1862,18 +1966,20 @@
struct net_device *dev = &smc->dev;
ioaddr_t ioaddr = dev->base_addr;
u_short i, media, saved_bank;
+ ioaddr_t mii_addr = dev->base_addr + MGMT;
+ u_short link;
+
+ saved_bank = inw(ioaddr + BANK_SELECT);
if (!netif_device_present(dev))
goto reschedule;
- saved_bank = inw(ioaddr + BANK_SELECT);
SMC_SELECT_BANK(2);
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;
- SMC_SELECT_BANK(saved_bank);
/* Check for pending interrupt with watchdog flag set: with
this, we can limp along even if the interrupt is blocked */
@@ -1887,14 +1993,42 @@
smc->fast_poll--;
smc->media.expires = jiffies + 1;
add_timer(&smc->media);
+ SMC_SELECT_BANK(saved_bank);
return;
}
+ if (smc->cfg & CFG_MII_SELECT) {
+ if (smc->phy_id < 0)
+ goto reschedule;
+
+ SMC_SELECT_BANK(3);
+ link = mdio_read(mii_addr, smc->phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+ smc->phy_id = -1;
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != smc->link_status) {
+ u_short p = mdio_read(mii_addr, smc->phy_id, 5);
+ printk(KERN_INFO "%s: %s link beat\n", dev->name,
+ (link) ? "found" : "lost");
+ 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'));
+ }
+ smc->link_status = link;
+ }
+ }
+
if (smc->cfg & CFG_MII_SELECT)
goto reschedule;
/* Ignore collisions unless we've had no rx's recently */
- if (jiffies - smc->last_rx > HZ) {
+ if (jiffies - dev->last_rx > HZ) {
if (smc->tx_err || (smc->media_status & EPH_16COL))
media |= EPH_16COL;
}
@@ -1930,6 +2064,7 @@
reschedule:
smc->media.expires = jiffies + HZ;
add_timer(&smc->media);
+ SMC_SELECT_BANK(saved_bank);
}
/*====================================================================*/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)