* This driver is for PCnet32 and PCnetPCI based ethercards
*/
-static const char *version = "pcnet32.c:v1.21 31.3.99 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de\n";
#include <linux/config.h>
#include <linux/module.h>
* rewritten PCI card detection
* added dwio mode to get driver working on some PPC machines
* v1.21: added mii selection and mii ioctl
+ * v1.22: changed pci scanning code to make PPC people happy
+ * fixed switching to 32bit mode in pcnet32_open() (thanks
+ * to Michael Richard <mcr@solidum.com> for noticing this one)
+ * added sub vendor/device id matching (thanks again to
+ * Michael Richard <mcr@solidum.com>)
+ * added chip id for 79c973/975 (thanks to Zach Brown <zab@zabbo.net>)
+ * v1.23 fixed small bug, when manual selecting MII speed/duplex
*/
#define PCNET32_TOTAL_SIZE 0x20
+/* some PCI ids */
+#ifndef PCI_DEVICE_ID_AMD_LANCE
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_LANCE 0x2000
+#endif
+#ifndef PCI_DEVICE_ID_AMD_PCNETHOME
+#define PCI_DEVICE_ID_AMD_PCNETHOME 0x2001
+#endif
+
+
#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
/* The PCNET32 Rx and Tx ring descriptors. */
struct pcnet32_pci_id_info {
const char *name;
- u16 vendor_id, device_id, device_id_mask, flags;
+ u16 vendor_id, device_id, svid, sdid, flags;
int io_size;
int (*probe1) (struct device *, unsigned long, unsigned char, int, int);
};
static struct pcnet32_pci_id_info pcnet32_tbl[] = {
{ "AMD PCnetPCI series",
- 0x1022, 0x2000, 0xfffe, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
+ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0, 0,
+ PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
+ pcnet32_probe1},
+ { "AMD PCnetHome series",
+ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0,
+ PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
pcnet32_probe1},
{0,}
};
#if defined(CONFIG_PCI)
if (pci_present()) {
- struct pci_dev *pdev;
- unsigned char pci_bus, pci_device_fn;
- int pci_index;
+ struct pci_dev *pdev = NULL;
printk("pcnet32.c: PCI bios is present, checking for devices...\n");
- for (pci_index = 0; pci_index < 0xff; pci_index++) {
- u16 vendor, device, pci_command;
+ while ((pdev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) {
+ u16 pci_command;
int chip_idx;
+ u16 sdid,svid;
- if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
- pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
- break;
-
- pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor);
- pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device);
-
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &sdid);
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &svid);
for (chip_idx = 0; pcnet32_tbl[chip_idx].vendor_id; chip_idx++)
- if (vendor == pcnet32_tbl[chip_idx].vendor_id &&
- (device & pcnet32_tbl[chip_idx].device_id_mask) == pcnet32_tbl[chip_idx].device_id)
+ if ((pdev->vendor == pcnet32_tbl[chip_idx].vendor_id) &&
+ (pdev->device == pcnet32_tbl[chip_idx].device_id) &&
+ (pcnet32_tbl[chip_idx].svid == 0 ||
+ (svid == pcnet32_tbl[chip_idx].svid)) &&
+ (pcnet32_tbl[chip_idx].sdid == 0 ||
+ (sdid == pcnet32_tbl[chip_idx].sdid)))
break;
if (pcnet32_tbl[chip_idx].vendor_id == 0)
continue;
- pdev = pci_find_slot(pci_bus, pci_device_fn);
ioaddr = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
#if defined(ADDR_64BITS) && defined(__alpha__)
ioaddr |= ((long)pdev->base_address[1]) << 32;
chipname = "PCnet/FAST+ 79C972";
fdx = 1; mii = 1;
break;
+ case 0x2625:
+ chipname = "PCnet/FAST III 79C973";
+ fdx = 1; mii = 1;
+ break;
case 0x2626:
chipname = "PCnet/Home 79C978";
fdx = 1;
printk("pcnet32: pcnet32 media reset to %#x.\n", media);
a->write_bcr (ioaddr, 49, media);
break;
+ case 0x2627:
+ chipname = "PCnet/FAST III 79C975";
+ fdx = 1; mii = 1;
default:
printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
return ENODEV;
lp->a.reset (ioaddr);
/* switch pcnet32 to 32bit mode */
- lp->a.write_csr (ioaddr, 20, 2);
+ lp->a.write_bcr (ioaddr, 20, 2);
if (pcnet32_debug > 1)
printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
val |= 0x10;
lp->a.write_csr (ioaddr, 124, val);
- if (lp->mii & (lp->options & PORT_ASEL)) {
+ if (lp->mii & !(lp->options & PORT_ASEL)) {
val = lp->a.read_bcr (ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
if (lp->options & PORT_FD)
val |= 0x10;
struct device *dev = (struct device *)dev_id;
struct pcnet32_private *lp;
unsigned long ioaddr;
- u16 csr0;
+ u16 csr0,rap;
int boguscnt = max_interrupt_work;
int must_restart;
dev->interrupt = 1;
+ rap = lp->a.read_rap(ioaddr);
while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
/* Acknowledge all of the current interrupt sources ASAP. */
lp->a.write_csr (ioaddr, 0, csr0 & ~0x004f);
/* Clear any other interrupt, and set interrupt enable. */
lp->a.write_csr (ioaddr, 0, 0x7940);
+ lp->a.write_rap(ioaddr,rap);
if (pcnet32_debug > 4)
printk("%s: exiting interrupt, csr0=%#4.4x.\n",