?? pcnet_cs.c
字號:
======================================================================*/static int pcnet_suspend(struct pcmcia_device *link){ struct net_device *dev = link->priv; if (link->open) netif_device_detach(dev); return 0;}static int pcnet_resume(struct pcmcia_device *link){ struct net_device *dev = link->priv; if (link->open) { pcnet_reset_8390(dev); NS8390_init(dev, 1); netif_device_attach(dev); } return 0;}/*====================================================================== MII interface support for DL10019 and DL10022 based cards On the DL10019, the MII IO direction bit is 0x10; on the DL10022 it is 0x20. Setting both bits seems to work on both card types.======================================================================*/#define DLINK_GPIO 0x1c#define DLINK_DIAG 0x1d#define DLINK_EEPROM 0x1e#define MDIO_SHIFT_CLK 0x80#define MDIO_DATA_OUT 0x40#define MDIO_DIR_WRITE 0x30#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)#define MDIO_DATA_READ 0x10#define MDIO_MASK 0x0fstatic void mdio_sync(kio_addr_t addr){ int bits, mask = inb(addr) & MDIO_MASK; for (bits = 0; bits < 32; bits++) { outb(mask | MDIO_DATA_WRITE1, addr); outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); }}static int mdio_read(kio_addr_t addr, int phy_id, int loc){ u_int cmd = (0x06<<10)|(phy_id<<5)|loc; int i, retval = 0, mask = inb(addr) & MDIO_MASK; mdio_sync(addr); for (i = 13; i >= 0; i--) { int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outb(mask | dat, addr); outb(mask | dat | MDIO_SHIFT_CLK, addr); } for (i = 19; i > 0; i--) { outb(mask, addr); retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); outb(mask | MDIO_SHIFT_CLK, addr); } return (retval>>1) & 0xffff;}static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value){ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; int i, mask = inb(addr) & MDIO_MASK; mdio_sync(addr); for (i = 31; i >= 0; i--) { int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; outb(mask | dat, addr); outb(mask | dat | MDIO_SHIFT_CLK, addr); } for (i = 1; i >= 0; i--) { outb(mask, addr); outb(mask | MDIO_SHIFT_CLK, addr); }}/*====================================================================== EEPROM access routines for DL10019 and DL10022 based cards======================================================================*/#define EE_EEP 0x40#define EE_ASIC 0x10#define EE_CS 0x08#define EE_CK 0x04#define EE_DO 0x02#define EE_DI 0x01#define EE_ADOT 0x01 /* DataOut for ASIC */#define EE_READ_CMD 0x06#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */static int read_eeprom(kio_addr_t ioaddr, int location){ int i, retval = 0; kio_addr_t ee_addr = ioaddr + DLINK_EEPROM; int read_cmd = location | (EE_READ_CMD << 8); outb(0, ee_addr); outb(EE_EEP|EE_CS, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DO : 0; outb_p(EE_EEP|EE_CS|dataval, ee_addr); outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr); } outb(EE_EEP|EE_CS, ee_addr); for (i = 16; i > 0; i--) { outb_p(EE_EEP|EE_CS | EE_CK, ee_addr); retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0); outb_p(EE_EEP|EE_CS, ee_addr); } /* Terminate the EEPROM access. */ outb(0, ee_addr); return retval;}/* The internal ASIC registers can be changed by EEPROM READ access with EE_ASIC bit set. In ASIC mode, EE_ADOT is used to output the data to the ASIC.*/static void write_asic(kio_addr_t ioaddr, int location, short asic_data){ int i; kio_addr_t ee_addr = ioaddr + DLINK_EEPROM; short dataval; int read_cmd = location | (EE_READ_CMD << 8); asic_data |= read_eeprom(ioaddr, location); outb(0, ee_addr); outb(EE_ASIC|EE_CS|EE_DI, ee_addr); read_cmd = read_cmd >> 1; /* Shift the read command bits out. */ for (i = 9; i >= 0; i--) { dataval = (read_cmd & (1 << i)) ? EE_DO : 0; outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr); outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr); outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr); } // sync outb(EE_ASIC|EE_CS, ee_addr); outb(EE_ASIC|EE_CS|EE_CK, ee_addr); outb(EE_ASIC|EE_CS, ee_addr); for (i = 15; i >= 0; i--) { dataval = (asic_data & (1 << i)) ? EE_ADOT : 0; outb_p(EE_ASIC|EE_CS|dataval, ee_addr); outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr); outb_p(EE_ASIC|EE_CS|dataval, ee_addr); } /* Terminate the ASIC access. */ outb(EE_ASIC|EE_DI, ee_addr); outb(EE_ASIC|EE_DI| EE_CK, ee_addr); outb(EE_ASIC|EE_DI, ee_addr); outb(0, ee_addr);}/*====================================================================*/static void set_misc_reg(struct net_device *dev){ kio_addr_t nic_base = dev->base_addr; pcnet_dev_t *info = PRIV(dev); u_char tmp; if (info->flags & HAS_MISC_REG) { tmp = inb_p(nic_base + PCNET_MISC) & ~3; if (dev->if_port == 2) tmp |= 1; if (info->flags & USE_BIG_BUF) tmp |= 2; if (info->flags & HAS_IBM_MISC) tmp |= 8; outb_p(tmp, nic_base + PCNET_MISC); } if (info->flags & IS_DL10022) { if (info->flags & HAS_MII) { /* Advertise 100F, 100H, 10F, 10H */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1); /* Restart MII autonegotiation */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000); mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200); info->mii_reset = jiffies; } else { outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG); } } else if (info->flags & IS_DL10019) { /* Advertise 100F, 100H, 10F, 10H */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1); /* Restart MII autonegotiation */ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000); mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200); }}/*====================================================================*/static void mii_phy_probe(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO; int i; u_int tmp, phyid; for (i = 31; i >= 0; i--) { tmp = mdio_read(mii_addr, i, 1); if ((tmp == 0) || (tmp == 0xffff)) continue; tmp = mdio_read(mii_addr, i, MII_PHYID_REG1); phyid = tmp << 16; phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2); phyid &= MII_PHYID_REV_MASK; DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid); if (phyid == AM79C9XX_HOME_PHY) { info->pna_phy = i; } else if (phyid != AM79C9XX_ETH_PHY) { info->eth_phy = i; } }}static int pcnet_open(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; DEBUG(2, "pcnet_open('%s')\n", dev->name); if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; set_misc_reg(dev); request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev_info, dev); info->phy_id = info->eth_phy; info->link_status = 0x00; init_timer(&info->watchdog); info->watchdog.function = &ei_watchdog; info->watchdog.data = (u_long)dev; info->watchdog.expires = jiffies + HZ; add_timer(&info->watchdog); return ei_open(dev);} /* pcnet_open *//*====================================================================*/static int pcnet_close(struct net_device *dev){ pcnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; DEBUG(2, "pcnet_close('%s')\n", dev->name); ei_close(dev); free_irq(dev->irq, dev); link->open--; netif_stop_queue(dev); del_timer_sync(&info->watchdog); return 0;} /* pcnet_close *//*====================================================================== Hard reset the card. This used to pause for the same period that a 8390 reset command required, but that shouldn't be necessary.======================================================================*/static void pcnet_reset_8390(struct net_device *dev){ kio_addr_t nic_base = dev->base_addr; int i; ei_status.txing = ei_status.dmaing = 0; outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD); outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET); for (i = 0; i < 100; i++) { if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) break; udelay(100); } outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ if (i == 100) printk(KERN_ERR "%s: pcnet_reset_8390() did not complete.\n", dev->name); set_misc_reg(dev);} /* pcnet_reset_8390 *//*====================================================================*/static int set_config(struct net_device *dev, struct ifmap *map){ pcnet_dev_t *info = PRIV(dev); if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { if (!(info->flags & HAS_MISC_REG)) return -EOPNOTSUPP; else if ((map->port < 1) || (map->port > 2)) return -EINVAL; dev->if_port = map->port; printk(KERN_INFO "%s: switched to %s port\n", dev->name, if_names[dev->if_port]); NS8390_init(dev, 1); } return 0;}/*====================================================================*/static irqreturn_t ei_irq_wrapper(int irq, void *dev_id){ struct net_device *dev = dev_id; pcnet_dev_t *info; irqreturn_t ret = ei_interrupt(irq, dev_id); if (ret == IRQ_HANDLED) { info = PRIV(dev); info->stale = 0; } return ret;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -