📄 ibm_emac_core.c
字号:
if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0) return -EINVAL; if (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) return -EINVAL; if (cmd->autoneg == AUTONEG_DISABLE) switch (cmd->speed) { case SPEED_10: if (cmd->duplex == DUPLEX_HALF && (features & SUPPORTED_10baseT_Half) == 0) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && (features & SUPPORTED_10baseT_Full) == 0) return -EINVAL; break; case SPEED_100: if (cmd->duplex == DUPLEX_HALF && (features & SUPPORTED_100baseT_Half) == 0) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && (features & SUPPORTED_100baseT_Full) == 0) return -EINVAL; break; case SPEED_1000: if (cmd->duplex == DUPLEX_HALF && (features & SUPPORTED_1000baseT_Half) == 0) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && (features & SUPPORTED_1000baseT_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, cmd); spin_unlock_irq(&fep->lock); return 0;}static voidemac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info){ struct ocp_enet_private *fep = ndev->priv; strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); info->fw_version[0] = '\0'; sprintf(info->bus_info, "IBM EMAC %d", fep->ocpdev->def->index); info->regdump_len = 0;}static int emac_nway_reset(struct net_device *ndev){ struct ocp_enet_private *fep = ndev->priv; if (!fep->want_autoneg) return -EINVAL; spin_lock_irq(&fep->lock); emac_start_link(fep, NULL); spin_unlock_irq(&fep->lock); return 0;}static u32 emac_get_link(struct net_device *ndev){ return netif_carrier_ok(ndev);}static struct ethtool_ops emac_ethtool_ops = { .get_settings = emac_get_settings, .set_settings = emac_set_settings, .get_drvinfo = emac_get_drvinfo, .nway_reset = emac_nway_reset, .get_link = emac_get_link};static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct ocp_enet_private *fep = dev->priv; uint *data = (uint *) & rq->ifr_ifru; switch (cmd) { case SIOCGMIIPHY: data[0] = fep->mii_phy_addr; /* Fall through */ case SIOCGMIIREG: data[3] = emac_phy_read(dev, fep->mii_phy_addr, data[1]); return 0; 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 int emac_open(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; int rc; spin_lock_irq(&fep->lock); fep->opened = 1; netif_carrier_off(dev); /* 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, "IBM EMAC MAC", dev); if (rc != 0) { printk("dev->irq %d failed\n", dev->irq); 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 int emac_close(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; 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_carrier_off(dev); netif_stop_queue(dev); /* * Check for a link, some PHYs don't provide a clock if * no link is present. Some EMACs will not come out of * soft reset without a PHY clock present. */ if (fep->phy_mii.def->ops->poll_link(&fep->phy_mii)) { 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); spin_unlock_irq(&fep->lock); return 0;}static void emac_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->rgmii_dev) emac_close_rgmii(ep->rgmii_dev); if (ep->zmii_dev) emac_close_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,};#ifdef CONFIG_NET_POLL_CONTROLLERstatic int emac_netpoll(struct net_device *ndev){ emac_rxeob_dev((void *)ndev, 0); emac_txeob_dev((void *)ndev, 0); return 0;}#endifstatic int emac_init_device(struct ocp_device *ocpdev, struct ibm_ocp_mal *mal){ int deferred_init = 0; int rc = 0, i; struct net_device *ndev; struct ocp_enet_private *ep; struct ocp_func_emac_data *emacdata; int commac_reg = 0; u32 phy_map; emacdata = (struct ocp_func_emac_data *)ocpdev->def->additions; if (!emacdata) { printk(KERN_ERR "emac%d: Missing additional data!\n", ocpdev->def->index); return -ENODEV; } /* 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; ep->ndev = ndev; ep->ocpdev = ocpdev; ndev->irq = ocpdev->def->irq; ep->wol_irq = emacdata->wol_irq; if (emacdata->mdio_idx >= 0) { if (emacdata->mdio_idx == ocpdev->def->index) { /* Set the common MDIO net_device */ mdio_ndev = ndev; deferred_init = 1; } ep->mdio_dev = mdio_ndev; } else { ep->mdio_dev = 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_tx_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 = (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, ep->zmii_input, emacdata->phy_mode)) != 0) goto bail; } /* Check if we need to attach to a RGMII */ if (emacdata->rgmii_idx >= 0) { ep->rgmii_input = emacdata->rgmii_mux; ep->rgmii_dev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_RGMII, emacdata->rgmii_idx); if (ep->rgmii_dev == NULL) printk(KERN_WARNING "emac%d: RGMII %d requested but not found !\n", ocpdev->def->index, emacdata->rgmii_idx); else if ((rc = emac_init_rgmii(ep->rgmii_dev, ep->rgmii_input, emacdata->phy_mode)) != 0) goto bail; } /* Check if we need to attach to a TAH */ if (emacdata->tah_idx >= 0) { ep->tah_dev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_TAH, emacdata->tah_idx); if (ep->tah_dev == NULL) printk(KERN_WARNING "emac%d: TAH %d requested but not found !\n", ocpdev->def->index, emacdata->tah_idx); else if ((rc = emac_init_tah(ep)) != 0) goto bail; } if (deferred_init) { if (!list_empty(&emac_init_list)) { struct list_head *entry; struct emac_def_dev *ddev; list_for_each(entry, &emac_init_list) { ddev = list_entry(entry, struct emac_def_dev, link); emac_init_device(ddev->ocpdev, ddev->mal); } } } /* 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; ep->phy_mii.mode = emacdata->phy_mode; /* Find PHY */ phy_map = emacdata->phy_map | 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 */ for (i = 0; i < 6; i++) ndev->dev_addr[i] = emacdata->mac_addr[i]; /* 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; if (emacdata->jumbo) ndev->change_mtu = &emac_change_mtu; ndev->set_mac_address = &emac_set_mac_address; ndev->set_multicast_list = &emac_set_multicast_list; ndev->do_ioctl = &emac_ioctl; SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops); if (emacdata->tah_idx >= 0) ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG;#ifdef CONFIG_NET_POLL_CONTROLLER ndev->poll_controller = emac_netpoll;#endif 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;}static int emac_probe(struct ocp_device *ocpdev){ struct ocp_device *maldev; struct ibm_ocp_mal *mal; struct ocp_func_emac_data *emacdata; 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; } /* Get the MAL device */ maldev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_MAL, emacdata->mal_idx); if (maldev == NULL) { printk("No maldev\n"); return -ENODEV; } /* * Get MAL driver data, it must be here due to link order. * When the driver is modularized, symbol dependencies will * ensure the MAL driver is already present if built as a * module. */ mal = (struct ibm_ocp_mal *)ocp_get_drvdata(maldev); if (mal == NULL) { printk("No maldrv\n"); return -ENODEV; } /* 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) && !mdio_ndev) { struct emac_def_dev *ddev; /* Add this index to the deferred init table */ ddev = kmalloc(sizeof(struct emac_def_dev), GFP_KERNEL); ddev->ocpdev = ocpdev; ddev->mal = mal; list_add_tail(&ddev->link, &emac_init_list); } else { emac_init_device(ocpdev, mal); } return 0;}/* 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 __init emac_init(void){ 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; } return ocp_register_driver(&emac_driver);}static void __exit emac_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 + -