📄 pcnet_cs.c
字号:
static void pcnet_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; pcnet_dev_t *info = link->priv; DEBUG(0, "pcnet_release(0x%p)\n", link); if (link->open) { DEBUG(1, "pcnet_cs: release postponed, '%s' still open\n", info->node.dev_name); link->state |= DEV_STALE_CONFIG; return; } if (info->flags & USE_SHMEM) { iounmap(info->base); CardServices(ReleaseWindow, link->win); } CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG;} /* pcnet_release *//*====================================================================== The card status event handler. Mostly, this schedules other stuff to run after an event is received. A CARD_REMOVAL event also sets some flags to discourage the net drivers from trying to talk to the card any more.======================================================================*/static int pcnet_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; pcnet_dev_t *info = link->priv; DEBUG(2, "pcnet_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { netif_device_detach(&info->dev); mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT; pcnet_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(&info->dev); CardServices(ReleaseConfiguration, link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { CardServices(RequestConfiguration, link->handle, &link->conf); if (link->open) { pcnet_reset_8390(&info->dev); NS8390_init(&info->dev, 1); netif_device_attach(&info->dev); } } break; } return 0;} /* pcnet_event *//*====================================================================== 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 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(ioaddr_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(ioaddr_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(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, 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); }}static void mdio_reset(ioaddr_t addr, int phy_id){ outb_p(0x08, addr); outb_p(0x0c, addr); outb_p(0x08, addr); outb_p(0x0c, addr); outb_p(0x00, addr);}/*====================================================================*/static void set_misc_reg(struct net_device *dev){ ioaddr_t nic_base = dev->base_addr; pcnet_dev_t *info = (pcnet_dev_t *)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) { mdio_reset(nic_base + DLINK_GPIO, info->eth_phy); /* 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); } }}/*====================================================================*/static void mii_phy_probe(struct net_device *dev){ pcnet_dev_t *info = (pcnet_dev_t *)dev; ioaddr_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 = (pcnet_dev_t *)dev; dev_link_t *link = &info->link; DEBUG(2, "pcnet_open('%s')\n", dev->name); if (!DEV_OK(link)) return -ENODEV; link->open++; MOD_INC_USE_COUNT; set_misc_reg(dev); request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); info->phy_id = info->eth_phy; info->link_status = 0x00; info->watchdog.function = &ei_watchdog; info->watchdog.data = (u_long)info; 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 = (pcnet_dev_t *)dev; dev_link_t *link = &info->link; DEBUG(2, "pcnet_close('%s')\n", dev->name); free_irq(dev->irq, dev); link->open--; netif_stop_queue(dev); del_timer(&info->watchdog); if (link->state & DEV_STALE_CONFIG) mod_timer(&link->release, jiffies + HZ/20); MOD_DEC_USE_COUNT; 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){ ioaddr_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 = (pcnet_dev_t *)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 void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs){ pcnet_dev_t *info = dev_id; info->stale = 0; ei_interrupt(irq, dev_id, regs);}static void ei_watchdog(u_long arg){ pcnet_dev_t *info = (pcnet_dev_t *)(arg); struct net_device *dev = &info->dev; ioaddr_t nic_base = dev->base_addr; ioaddr_t mii_addr = nic_base + DLINK_GPIO; u_short link; if (!netif_device_present(dev)) goto reschedule; /* Check for pending interrupt with expired latency timer: with this, we can limp along even if the interrupt is blocked */ outb_p(E8390_NODMA+E8390_PAGE0, nic_base + E8390_CMD); if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { if (!info->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); ei_irq_wrapper(dev->irq, dev, NULL); info->fast_poll = HZ; } if (info->fast_poll) { info->fast_poll--; info->watchdog.expires = jiffies + 1; add_timer(&info->watchdog); return; } if (!(info->flags & HAS_MII)) goto reschedule; mdio_read(mii_addr, info->phy_id, 1); link = mdio_read(mii_addr, info->phy_id, 1); if (!link || (link == 0xffff)) { if (info->eth_phy) { info->phy_id = info->eth_phy = 0; } else { printk(KERN_INFO "%s: MII is missing!\n", dev->name); info->flags &= ~HAS_MII; } goto reschedule; } link &= 0x0004; if (link != info->link_status) { u_short p = mdio_read(mii_addr, info->phy_id, 5); printk(KERN_INFO "%s: %s link beat\n", dev->name, (link) ? "found" : "lost"); if (link && (info->flags & IS_DL10022)) { /* Disable collision detection on full duplex links */ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG); } if (link) { if (info->phy_id == info->eth_phy) { if (p) printk(KERN_INFO "%s: autonegotiation complete: " "%sbaseT-%cD selected\n", dev->name, ((p & 0x0180) ? "100" : "10"), ((p & 0x0140) ? 'F' : 'H')); else printk(KERN_INFO "%s: link partner did not " "autonegotiate\n", dev->name); } NS8390_init(dev, 1); } info->link_status = link; } if (info->pna_phy && (jiffies - info->mii_reset > 6*HZ)) { link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004; if (((info->phy_id == info->pna_phy) && link) || ((info->phy_id != info->pna_phy) && !link)) { /* isolate this MII and try flipping to the other one */ mdio_write(mii_addr, info->phy_id, 0, 0x0400); info->phy_id ^= info->pna_phy ^ info->eth_phy; printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name, (info->phy_id == info->eth_phy) ? "ethernet" : "PNA"); mdio_write(mii_addr, info->phy_id, 0, (info->phy_id == info->eth_phy) ? 0x1000 : 0); info->link_status = 0; info->mii_reset = jiffies; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -