📄 ibm_ocp_enet.c
字号:
if (link == netif_carrier_ok(fep->ndev)) { if (!link && fep->want_autoneg && (++fep->timer_ticks) > 10) emac_start_link(fep, NULL); goto out; } printk(KERN_INFO "%s: Link is %s\n", fep->ndev->name, link ? "Up" : "Down"); if (link) { netif_carrier_on(fep->ndev); /* Chip needs a full reset on config change. That sucks, so I * should ultimately move that to some tasklet to limit * latency peaks caused by this code */ emac_reset_configure(fep); if (fep->opened) emac_kick(fep); } else { fep->timer_ticks = 0; netif_carrier_off(fep->ndev); }out: mod_timer(&fep->link_timer, jiffies + HZ); spin_unlock_irq(&fep->lock);}static voidemac_set_multicast_list(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; spin_lock_irq(&fep->lock); __emac_set_multicast_list(dev); spin_unlock_irq(&fep->lock);}static intemac_ethtool(struct net_device *dev, void* ep_user){ struct ocp_enet_private *fep = dev->priv; struct ethtool_cmd ecmd; unsigned long features = fep->phy_mii.def->features; if (copy_from_user(&ecmd, ep_user, sizeof(ecmd))) return -EFAULT; switch(ecmd.cmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info; memset(&info, 0, sizeof(info)); 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'; sprintf(info.bus_info, "OCP EMAC %d", fep->ocpdev->def->index); info.regdump_len = 0; if (copy_to_user(ep_user, &info, sizeof(info))) return -EFAULT; return 0; } case ETHTOOL_GSET: ecmd.supported = features; ecmd.port = PORT_MII; ecmd.transceiver = XCVR_EXTERNAL; ecmd.phy_address = fep->mii_phy_addr; spin_lock_irq(&fep->lock); ecmd.autoneg = fep->want_autoneg; ecmd.speed = fep->phy_mii.speed; ecmd.duplex = fep->phy_mii.duplex; spin_unlock_irq(&fep->lock); if (copy_to_user(ep_user, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; case ETHTOOL_SSET: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (ecmd.autoneg != AUTONEG_ENABLE && ecmd.autoneg != AUTONEG_DISABLE) return -EINVAL; if (ecmd.autoneg == AUTONEG_ENABLE && ecmd.advertising == 0) return -EINVAL; if (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL) return -EINVAL; if (ecmd.autoneg == AUTONEG_DISABLE) switch(ecmd.speed) { case SPEED_10: if (ecmd.duplex == DUPLEX_HALF && (features & SUPPORTED_10baseT_Half) == 0) return -EINVAL; if (ecmd.duplex == DUPLEX_FULL && (features & SUPPORTED_10baseT_Full) == 0) return -EINVAL; break; case SPEED_100: if (ecmd.duplex == DUPLEX_HALF && (features & SUPPORTED_100baseT_Half) == 0) return -EINVAL; if (ecmd.duplex == DUPLEX_FULL && (features & SUPPORTED_100baseT_Full) == 0) return -EINVAL; break; default: return -EINVAL; } else if ((features & SUPPORTED_Autoneg) == 0) return -EINVAL; spin_lock_irq(&fep->lock); emac_start_link(fep, &ecmd); spin_unlock_irq(&fep->lock); return 0; case ETHTOOL_NWAY_RST: if (!fep->want_autoneg) return -EINVAL; spin_lock_irq(&fep->lock); emac_start_link(fep, NULL); spin_unlock_irq(&fep->lock); return 0; case ETHTOOL_GLINK: { struct ethtool_value edata; memset(&edata, 0, sizeof(edata)); edata.cmd = ETHTOOL_GLINK; edata.data = netif_carrier_ok(dev); if (copy_to_user(ep_user, &edata, sizeof(edata))) return -EFAULT; return 0; } } return -EOPNOTSUPP;}static intemac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct ocp_enet_private *fep = dev->priv; uint *data = (uint *) & rq->ifr_data; switch (cmd) { case SIOCETHTOOL: return emac_ethtool(dev, rq->ifr_data); case SIOCDEVPRIVATE: case SIOCGMIIPHY: data[0] = fep->mii_phy_addr; /*FALLTHRU*/ case SIOCDEVPRIVATE + 1: case SIOCGMIIREG: data[3] = emac_phy_read(dev, fep->mii_phy_addr, data[1]); return 0; case SIOCDEVPRIVATE + 2: case SIOCSMIIREG: if (!capable(CAP_NET_ADMIN)) return -EPERM; emac_phy_write(dev, fep->mii_phy_addr, data[1], data[2]); return 0; default: return -EOPNOTSUPP; }} static intemac_open(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; int rc; spin_lock_irq(&fep->lock); fep->opened = 1; /* Reset & configure the chip */ emac_reset_configure(fep); spin_unlock_irq(&fep->lock); /* Request our interrupt lines */ rc = request_irq(dev->irq, emac_mac_irq, 0, "OCP EMAC MAC", dev); if (rc != 0) goto bail; rc = request_irq(fep->wol_irq, emac_wakeup_irq, 0, "OCP EMAC Wakeup", dev); if (rc != 0) { free_irq(dev->irq, dev); goto bail; } /* Kick the chip rx & tx channels into life */ spin_lock_irq(&fep->lock); emac_kick(fep); spin_unlock_irq(&fep->lock); netif_start_queue(dev);bail: return rc;}static intemac_close(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; volatile emac_t *emacp = fep->emacp; /* XXX Stop IRQ emitting here */ spin_lock_irq(&fep->lock); fep->opened = 0; mal_disable_tx_channels(fep->mal, fep->commac.tx_chan_mask); mal_disable_rx_channels(fep->mal, fep->commac.rx_chan_mask); netif_stop_queue(dev); out_be32(&emacp->em0mr0, EMAC_M0_SRST); udelay(10); if (emacp->em0mr0 & EMAC_M0_SRST) { /*not sure what to do here hopefully it clears before another open */ printk(KERN_ERR "%s: Phy SoftReset didn't clear, no link?\n", dev->name); } /* Free the irq's */ free_irq(dev->irq, dev); free_irq(fep->wol_irq, dev); spin_unlock_irq(&fep->lock); return 0;}static voidemac_remove(struct ocp_device *ocpdev){ struct net_device *dev = ocp_get_drvdata(ocpdev); struct ocp_enet_private *ep = dev->priv; /* FIXME: locking, races, ... */ ep->going_away = 1; ocp_set_drvdata(ocpdev, NULL); if (ep->zmii_dev) emac_fini_zmii(ep->zmii_dev); unregister_netdev(dev); del_timer_sync(&ep->link_timer); mal_unregister_commac(ep->mal, &ep->commac); iounmap((void *)ep->emacp); kfree(dev);}struct mal_commac_ops emac_commac_ops = { .txeob = &emac_txeob_dev, .txde = &emac_txde_dev, .rxeob = &emac_rxeob_dev, .rxde = &emac_rxde_dev,};static intemac_probe(struct ocp_device *ocpdev){ int rc = 0, i; bd_t *bd; struct net_device *ndev; struct ocp_enet_private *ep; struct ocp_device *maldev; struct ibm_ocp_mal *mal; struct ocp_func_emac_data *emacdata; struct ocp_device *mdiodev; struct net_device *mdio_ndev = NULL; int commac_reg = 0; u32 phy_map; emacdata = (struct ocp_func_emac_data *)ocpdev->def->additions; if (emacdata == NULL) { printk(KERN_ERR "emac%d: Missing additional datas !\n", ocpdev->def->index); return -ENODEV; } /* Wait for MAL to show up */ maldev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_MAL, emacdata->mal_idx); if (maldev == NULL) return -EAGAIN; /* Check if MAL driver attached yet */ mal = (struct ibm_ocp_mal *)ocp_get_drvdata(maldev); if (mal == NULL) return -EAGAIN; /* If we depend on another EMAC for MDIO, wait for it to show up */ if (emacdata->mdio_idx >= 0 && emacdata->mdio_idx != ocpdev->def->index) { mdiodev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_EMAC, emacdata->mdio_idx); if (mdiodev == NULL) return -EAGAIN; mdio_ndev = (struct net_device *)ocp_get_drvdata(mdiodev); if (mdio_ndev == NULL) return -EAGAIN; } /* Allocate our net_device structure */ ndev = alloc_etherdev(sizeof (struct ocp_enet_private)); if (ndev == NULL) { printk(KERN_ERR "emac%d: Could not allocate ethernet device.\n", ocpdev->def->index); return -ENOMEM; } ep = ndev->priv; memset(ep, 0, sizeof(*ep)); ep->ndev = ndev; ep->ocpdev = ocpdev; ndev->irq = ocpdev->def->irq; ep->wol_irq = emacdata->wol_irq; ep->mdio_dev = mdio_ndev; ocp_set_drvdata(ocpdev, ndev); spin_lock_init(&ep->lock); /* Fill out MAL informations and register commac */ ep->mal = mal; ep->mal_tx_chan = emacdata->mal_tx1_chan; ep->mal_rx_chan = emacdata->mal_rx_chan; ep->commac.ops = &emac_commac_ops; ep->commac.dev = ndev; ep->commac.tx_chan_mask = MAL_CHAN_MASK(ep->mal_tx_chan); ep->commac.rx_chan_mask = MAL_CHAN_MASK(ep->mal_rx_chan); rc = mal_register_commac(ep->mal, &ep->commac); if (rc != 0) goto bail; commac_reg = 1; /* Map our MMIOs */ ep->emacp = (volatile emac_t *)ioremap(ocpdev->def->paddr, sizeof (emac_t)); /* Check if we need to attach to a ZMII */ if (emacdata->zmii_idx >= 0) { ep->zmii_input = emacdata->zmii_mux; ep->zmii_dev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_ZMII, emacdata->zmii_idx); if (ep->zmii_dev == NULL) printk(KERN_WARNING "emac%d: ZMII %d requested but not found !\n", ocpdev->def->index, emacdata->zmii_idx); else if ((rc = emac_init_zmii(ep->zmii_dev, ZMII_AUTO)) != 0) goto bail; } /* Reset the EMAC */ out_be32(&ep->emacp->em0mr0, EMAC_M0_SRST); udelay(20); for (i=0; i<100; i++) { if ((in_be32(&ep->emacp->em0mr0) & EMAC_M0_SRST) == 0) break; udelay(10); } if (i >= 100) { printk(KERN_ERR "emac%d: Cannot reset EMAC\n", ocpdev->def->index); rc = -ENXIO; goto bail; } /* Init link monitoring timer */ init_timer(&ep->link_timer); ep->link_timer.function = emac_link_timer; ep->link_timer.data = (unsigned long) ep; ep->timer_ticks = 0; /* Fill up the mii_phy structure */ ep->phy_mii.dev = ndev; ep->phy_mii.mdio_read = emac_phy_read; ep->phy_mii.mdio_write = emac_phy_write; /* Find PHY */ phy_map = emac_phy_map[ocpdev->def->index] | busy_phy_map; for (i = 0; i <= 0x1f; i++, phy_map >>= 1) { if ((phy_map & 0x1) == 0) { int val = emac_phy_read(ndev, i, MII_BMCR); if (val != 0xffff && val != -1) break; } } if (i == 0x20) { printk(KERN_WARNING "emac%d: Can't find PHY.\n", ocpdev->def->index); rc = -ENODEV; goto bail; } busy_phy_map |= 1 << i; ep->mii_phy_addr = i; rc = mii_phy_probe(&ep->phy_mii, i); if (rc) { printk(KERN_WARNING "emac%d: Failed to probe PHY type.\n", ocpdev->def->index); rc = -ENODEV; goto bail; } /* Setup initial PHY config & startup aneg */ if (ep->phy_mii.def->ops->init) ep->phy_mii.def->ops->init(&ep->phy_mii); netif_carrier_off(ndev); if (ep->phy_mii.def->features & SUPPORTED_Autoneg) ep->want_autoneg = 1; emac_start_link(ep, NULL); /* read the MAC Address */ bd = (bd_t *) __res; for (i = 0; i < 6; i++) ndev->dev_addr[i] = bd->BD_EMAC_ADDR(ocpdev->def->index, i); /* Marco to disques array */ /* Fill in the driver function table */ ndev->open = &emac_open; ndev->hard_start_xmit = &emac_start_xmit; ndev->stop = &emac_close; ndev->get_stats = &emac_stats; ndev->set_multicast_list = &emac_set_multicast_list; ndev->do_ioctl = &emac_ioctl; SET_MODULE_OWNER(ndev); rc = register_netdev(ndev); if (rc != 0) goto bail; printk("%s: IBM emac, MAC %02x:%02x:%02x:%02x:%02x:%02x\n", ndev->name, ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]); printk(KERN_INFO "%s: Found %s PHY (0x%02x)\n", ndev->name, ep->phy_mii.def->name, ep->mii_phy_addr);bail: if (rc && commac_reg) mal_unregister_commac(ep->mal, &ep->commac); if (rc && ndev) kfree(ndev); return rc;}/* Structure for a device driver */static struct ocp_device_id emac_ids[] ={ { .vendor = OCP_ANY_ID, .function = OCP_FUNC_EMAC }, { .vendor = OCP_VENDOR_INVALID }};static struct ocp_driver emac_driver ={ .name = "emac", .id_table = emac_ids, .probe = emac_probe, .remove = emac_remove,};static int __initemac_init(void){ int rc; printk(KERN_INFO DRV_NAME ": " DRV_DESC ", version " DRV_VERSION "\n"); printk(KERN_INFO "Maintained by " DRV_AUTHOR "\n"); if (skb_res > 2) { printk(KERN_WARNING "Invalid skb_res: %d, cropping to 2\n", skb_res); skb_res = 2; } rc = ocp_register_driver(&emac_driver); if (rc == 0) { ocp_unregister_driver(&emac_driver); return -ENODEV; } return 0;}static void __exitemac_exit(void){ ocp_unregister_driver(&emac_driver);}module_init(emac_init);module_exit(emac_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -