📄 sungem.c
字号:
/* Make us not-running to avoid timers respawning */ gp->hw_running = 0; /* Stop the link timer */ del_timer_sync(&gp->link_timer); /* Stop the reset task */ while (gp->reset_task_pending) schedule(); /* Actually stop the chip */ if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { gem_stop_phy(gp);#ifdef CONFIG_ALL_PPC /* Power down the chip */ gem_apple_powerdown(gp);#endif /* CONFIG_ALL_PPC */ } else gem_stop(gp);}static void gem_pm_task(void *data){ struct gem *gp = (struct gem *) data; /* We assume if we can't lock the pm_sem, then open() was * called again (or suspend()), and we can safely ignore * the PM request */ if (down_trylock(&gp->pm_sem)) return; /* Driver was re-opened or already shut down */ if (gp->opened || !gp->hw_running) { up(&gp->pm_sem); return; } gem_shutdown(gp); up(&gp->pm_sem);}static void gem_pm_timer(unsigned long data){ struct gem *gp = (struct gem *) data; schedule_task(&gp->pm_task);}static int gem_open(struct net_device *dev){ struct gem *gp = dev->priv; int hw_was_up = gp->hw_running; down(&gp->pm_sem); /* Stop the PM timer/task */ del_timer(&gp->pm_timer); flush_scheduled_tasks(); if (!gp->hw_running) {#ifdef CONFIG_ALL_PPC /* First, we need to bring up the chip */ if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { gem_apple_powerup(gp); gem_check_invariants(gp); }#endif /* CONFIG_ALL_PPC */ /* Reset the chip */ gem_stop(gp); gp->hw_running = 1; } /* We can now request the interrupt as we know it's masked * on the controller */ if (request_irq(gp->pdev->irq, gem_interrupt, SA_SHIRQ, dev->name, (void *)dev)) { printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);#ifdef CONFIG_ALL_PPC if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE) gem_apple_powerdown(gp);#endif /* CONFIG_ALL_PPC */ /* Fire the PM timer that will shut us down in about 10 seconds */ gp->pm_timer.expires = jiffies + 10*HZ; add_timer(&gp->pm_timer); up(&gp->pm_sem); return -EAGAIN; } /* Allocate & setup ring buffers */ gem_init_rings(gp, 0); /* Init & setup chip hardware */ gem_init_hw(gp, !hw_was_up); gp->opened = 1; up(&gp->pm_sem); return 0;}static int gem_close(struct net_device *dev){ struct gem *gp = dev->priv; /* Make sure we don't get distracted by suspend/resume */ down(&gp->pm_sem); /* Stop traffic, mark us closed */ spin_lock_irq(&gp->lock); gp->opened = 0; writel(0xffffffff, gp->regs + GREG_IMASK); netif_stop_queue(dev); spin_unlock_irq(&gp->lock); /* Stop chip */ gem_stop(gp); /* Get rid of rings */ gem_clean_rings(gp); /* Bye, the pm timer will finish the job */ free_irq(gp->pdev->irq, (void *) dev); /* Fire the PM timer that will shut us down in about 10 seconds */ gp->pm_timer.expires = jiffies + 10*HZ; add_timer(&gp->pm_timer); up(&gp->pm_sem); return 0;}#ifdef CONFIG_PMstatic int gem_suspend(struct pci_dev *pdev, u32 state){ struct net_device *dev = pci_get_drvdata(pdev); struct gem *gp = dev->priv; /* We hold the PM semaphore during entire driver * sleep time */ down(&gp->pm_sem); printk(KERN_INFO "%s: suspending, WakeOnLan %s\n", dev->name, gp->wake_on_lan ? "enabled" : "disabled"); /* If the driver is opened, we stop the DMA */ if (gp->opened) { /* Stop traffic, mark us closed */ netif_device_detach(dev); spin_lock_irq(&gp->lock); writel(0xffffffff, gp->regs + GREG_IMASK); spin_unlock_irq(&gp->lock); /* Stop chip */ gem_stop(gp); /* Get rid of ring buffers */ gem_clean_rings(gp); if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) disable_irq(gp->pdev->irq); } if (gp->hw_running) { /* Kill PM timer if any */ del_timer_sync(&gp->pm_timer); flush_scheduled_tasks(); gem_shutdown(gp); } return 0;}static int gem_resume(struct pci_dev *pdev){ struct net_device *dev = pci_get_drvdata(pdev); struct gem *gp = dev->priv; printk(KERN_INFO "%s: resuming\n", dev->name); if (gp->opened) {#ifdef CONFIG_ALL_PPC /* First, we need to bring up the chip */ if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) { gem_apple_powerup(gp); gem_check_invariants(gp); }#endif /* CONFIG_ALL_PPC */ gem_stop(gp); gp->hw_running = 1; gem_init_rings(gp, 0); gem_init_hw(gp, 1); netif_device_attach(dev); if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) enable_irq(gp->pdev->irq); } up(&gp->pm_sem); return 0;}#endif /* CONFIG_PM */static struct net_device_stats *gem_get_stats(struct net_device *dev){ struct gem *gp = dev->priv; struct net_device_stats *stats = &gp->net_stats; if (gp->hw_running) { stats->rx_crc_errors += readl(gp->regs + MAC_FCSERR); writel(0, gp->regs + MAC_FCSERR); stats->rx_frame_errors += readl(gp->regs + MAC_AERR); writel(0, gp->regs + MAC_AERR); stats->rx_length_errors += readl(gp->regs + MAC_LERR); writel(0, gp->regs + MAC_LERR); stats->tx_aborted_errors += readl(gp->regs + MAC_ECOLL); stats->collisions += (readl(gp->regs + MAC_ECOLL) + readl(gp->regs + MAC_LCOLL)); writel(0, gp->regs + MAC_ECOLL); writel(0, gp->regs + MAC_LCOLL); } return &gp->net_stats;}static void gem_set_multicast(struct net_device *dev){ struct gem *gp = dev->priv; u32 rxcfg, rxcfg_new; int limit = 10000; if (!gp->hw_running) return; netif_stop_queue(dev); rxcfg = readl(gp->regs + MAC_RXCFG); rxcfg_new = gem_setup_multicast(gp); writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG); while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) { if (!limit--) break; udelay(10); } rxcfg &= ~(MAC_RXCFG_PROM | MAC_RXCFG_HFE); rxcfg |= rxcfg_new; writel(rxcfg, gp->regs + MAC_RXCFG); /* Hrm... we may walk on the reset task here... */ netif_wake_queue(dev);}/* Eventually add support for changing the advertisement * on autoneg. */static int gem_ethtool_ioctl(struct net_device *dev, void *ep_user){ struct gem *gp = dev->priv; u16 bmcr; int full_duplex, speed, pause; struct ethtool_cmd ecmd; if (copy_from_user(&ecmd, ep_user, sizeof(ecmd))) return -EFAULT; switch(ecmd.cmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { cmd: ETHTOOL_GDRVINFO }; strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN); strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN); info.fw_version[0] = '\0'; strncpy(info.bus_info, gp->pdev->slot_name, ETHTOOL_BUSINFO_LEN); info.regdump_len = 0; /*SUNGEM_NREGS;*/ if (copy_to_user(ep_user, &info, sizeof(info))) return -EFAULT; return 0; } case ETHTOOL_GSET: ecmd.supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); if (gp->gigabit_capable) ecmd.supported |= (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); /* XXX hardcoded stuff for now */ ecmd.port = PORT_MII; ecmd.transceiver = XCVR_EXTERNAL; ecmd.phy_address = 0; /* XXX fixed PHYAD */ /* Record PHY settings if HW is on. */ if (gp->hw_running) { bmcr = phy_read(gp, MII_BMCR); gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause); } else bmcr = 0; if (bmcr & BMCR_ANENABLE) { ecmd.autoneg = AUTONEG_ENABLE; ecmd.speed = speed == 10 ? SPEED_10 : (speed == 1000 ? SPEED_1000 : SPEED_100); ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; } else { ecmd.autoneg = AUTONEG_DISABLE; ecmd.speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; ecmd.duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } if (copy_to_user(ep_user, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; case ETHTOOL_SSET: if (!capable(CAP_NET_ADMIN)) return -EPERM; /* Verify the settings we care about. */ if (ecmd.autoneg != AUTONEG_ENABLE && ecmd.autoneg != AUTONEG_DISABLE) return -EINVAL; if (ecmd.autoneg == AUTONEG_DISABLE && ((ecmd.speed != SPEED_100 && ecmd.speed != SPEED_10) || (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL))) return -EINVAL; /* Apply settings and restart link process */ if (gp->hw_running) del_timer(&gp->link_timer); gem_begin_auto_negotiation(gp, &ecmd); return 0; case ETHTOOL_NWAY_RST: if ((gp->link_cntl & BMCR_ANENABLE) == 0) return -EINVAL; if (gp->hw_running) del_timer(&gp->link_timer); gem_begin_auto_negotiation(gp, NULL); return 0; case ETHTOOL_GWOL: case ETHTOOL_SWOL: break; /* todo */ /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = { cmd: ETHTOOL_GLINK }; edata.data = (gp->lstate == link_up); if (copy_to_user(ep_user, &edata, sizeof(edata))) return -EFAULT; return 0; } /* get message-level */ case ETHTOOL_GMSGLVL: { struct ethtool_value edata = { cmd: ETHTOOL_GMSGLVL }; edata.data = gp->msg_enable; if (copy_to_user(ep_user, &edata, sizeof(edata))) return -EFAULT; return 0; } /* set message-level */ case ETHTOOL_SMSGLVL: { struct ethtool_value edata; if (copy_from_user(&edata, ep_user, sizeof(edata))) return -EFAULT; gp->msg_enable = edata.data; return 0; }#if 0 case ETHTOOL_GREGS: { struct ethtool_regs regs; u32 *regbuf; int r = 0; if (copy_from_user(®s, useraddr, sizeof(regs))) return -EFAULT; if (regs.len > SUNGEM_NREGS) { regs.len = SUNGEM_NREGS; } regs.version = 0; if (copy_to_user(useraddr, ®s, sizeof(regs))) return -EFAULT; if (!gp->hw_running) return -ENODEV; useraddr += offsetof(struct ethtool_regs, data); /* Use kmalloc to avoid bloating the stack */ regbuf = kmalloc(4 * SUNGEM_NREGS, GFP_KERNEL); if (!regbuf) return -ENOMEM; spin_lock_irq(&np->lock); gem_get_regs(gp, regbuf); spin_unlock_irq(&np->lock); if (copy_to_user(useraddr, regbuf, regs.len*sizeof(u32))) r = -EFAULT; kfree(regbuf); return r; }#endif }; return -EOPNOTSUPP;}static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct gem *gp = dev->priv; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; int rc = -EOPNOTSUPP; /* Hold the PM semaphore while doing ioctl's or we may collide * with open/close and power management and oops. */ down(&gp->pm_sem); switch (cmd) { case SIOCETHTOOL: rc = gem_ethtool_ioctl(dev, ifr->ifr_data); break; case SIOCGMIIPHY: /* Get address of MII PHY in use. */ data->phy_id = gp->mii_phy_addr; /* Fallthrough... */ case SIOCGMIIREG: /* Read MII PHY register. */ data->val_out = __phy_read(gp, data->reg_num & 0x1f, data->phy_id & 0x1f); rc = 0; break; case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable(CAP_NET_ADMIN)) { rc = -EPERM; } else { __phy_write(gp, data->reg_num & 0x1f, data->val_in, data->phy_id & 0x1f); rc = 0; } break; }; up(&gp->pm_sem); return rc;}static int __devinit gem_get_device_address(struct gem *gp){#if defined(__sparc__) || defined(CONFIG_ALL_PPC) struct net_device *dev = gp->dev;#endif#ifdef __sparc__ struct pci_dev *pdev = gp->pdev; struct pcidev_cookie *pcp = pdev->sysdata; int node = -1; if (pcp != NULL) { node = pcp->prom_node; if (prom_getproplen(node, "local-mac-address") == 6) prom_getproperty(node, "local-mac-address", dev->dev_addr, 6); else node = -1; } if (node == -1) memcpy(dev->dev_addr, idprom->id_ethaddr, 6);#endif#ifdef CONFIG_ALL_PPC unsigned char *addr; addr = get_property(gp->of_node, "local-mac-address", NULL); if (addr == NULL) { printk("\n"); printk(KERN_ERR "%s: can't get mac-address\n", dev->name); return -1; } memcpy(dev->dev_addr, addr, MAX_ADDR_LEN);#endif return 0;}static int __devinit gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ static int gem_version_printed = 0; unsigned long gemreg_base, gemreg_len; struct net_device *dev; struct gem *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -