📄 ibm_emac_core.c
字号:
if (cmd->duplex == DUPLEX_FULL && !(f & SUPPORTED_100baseT_Full)) return -EINVAL; break; case SPEED_1000: if (cmd->duplex == DUPLEX_HALF && !(f & SUPPORTED_1000baseT_Half)) return -EINVAL; if (cmd->duplex == DUPLEX_FULL && !(f & SUPPORTED_1000baseT_Full)) return -EINVAL; break; default: return -EINVAL; } local_bh_disable(); dev->phy.def->ops->setup_forced(&dev->phy, cmd->speed, cmd->duplex); } else { if (!(f & SUPPORTED_Autoneg)) return -EINVAL; local_bh_disable(); dev->phy.def->ops->setup_aneg(&dev->phy, (cmd->advertising & f) | (dev->phy.advertising & (ADVERTISED_Pause | ADVERTISED_Asym_Pause))); } emac_force_link_update(dev); local_bh_enable(); return 0;}static void emac_ethtool_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *rp){ rp->rx_max_pending = rp->rx_pending = NUM_RX_BUFF; rp->tx_max_pending = rp->tx_pending = NUM_TX_BUFF;}static void emac_ethtool_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pp){ struct ocp_enet_private *dev = ndev->priv; local_bh_disable(); if ((dev->phy.features & SUPPORTED_Autoneg) && (dev->phy.advertising & (ADVERTISED_Pause | ADVERTISED_Asym_Pause))) pp->autoneg = 1; if (dev->phy.duplex == DUPLEX_FULL) { if (dev->phy.pause) pp->rx_pause = pp->tx_pause = 1; else if (dev->phy.asym_pause) pp->tx_pause = 1; } local_bh_enable();}static u32 emac_ethtool_get_rx_csum(struct net_device *ndev){ struct ocp_enet_private *dev = ndev->priv; return dev->tah_dev != 0;}static int emac_get_regs_len(struct ocp_enet_private *dev){ return sizeof(struct emac_ethtool_regs_subhdr) + EMAC_ETHTOOL_REGS_SIZE;}static int emac_ethtool_get_regs_len(struct net_device *ndev){ struct ocp_enet_private *dev = ndev->priv; return sizeof(struct emac_ethtool_regs_hdr) + emac_get_regs_len(dev) + mal_get_regs_len(dev->mal) + zmii_get_regs_len(dev->zmii_dev) + rgmii_get_regs_len(dev->rgmii_dev) + tah_get_regs_len(dev->tah_dev);}static void *emac_dump_regs(struct ocp_enet_private *dev, void *buf){ struct emac_ethtool_regs_subhdr *hdr = buf; hdr->version = EMAC_ETHTOOL_REGS_VER; hdr->index = dev->def->index; memcpy_fromio(hdr + 1, dev->emacp, EMAC_ETHTOOL_REGS_SIZE); return ((void *)(hdr + 1) + EMAC_ETHTOOL_REGS_SIZE);}static void emac_ethtool_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *buf){ struct ocp_enet_private *dev = ndev->priv; struct emac_ethtool_regs_hdr *hdr = buf; hdr->components = 0; buf = hdr + 1; local_irq_disable(); buf = mal_dump_regs(dev->mal, buf); buf = emac_dump_regs(dev, buf); if (dev->zmii_dev) { hdr->components |= EMAC_ETHTOOL_REGS_ZMII; buf = zmii_dump_regs(dev->zmii_dev, buf); } if (dev->rgmii_dev) { hdr->components |= EMAC_ETHTOOL_REGS_RGMII; buf = rgmii_dump_regs(dev->rgmii_dev, buf); } if (dev->tah_dev) { hdr->components |= EMAC_ETHTOOL_REGS_TAH; buf = tah_dump_regs(dev->tah_dev, buf); } local_irq_enable();}static int emac_ethtool_nway_reset(struct net_device *ndev){ struct ocp_enet_private *dev = ndev->priv; int res = 0; DBG("%d: nway_reset" NL, dev->def->index); if (dev->phy.address < 0) return -EOPNOTSUPP; local_bh_disable(); if (!dev->phy.autoneg) { res = -EINVAL; goto out; } dev->phy.def->ops->setup_aneg(&dev->phy, dev->phy.advertising); emac_force_link_update(dev); out: local_bh_enable(); return res;}static int emac_ethtool_get_stats_count(struct net_device *ndev){ return EMAC_ETHTOOL_STATS_COUNT;}static void emac_ethtool_get_strings(struct net_device *ndev, u32 stringset, u8 * buf){ if (stringset == ETH_SS_STATS) memcpy(buf, &emac_stats_keys, sizeof(emac_stats_keys));}static void emac_ethtool_get_ethtool_stats(struct net_device *ndev, struct ethtool_stats *estats, u64 * tmp_stats){ struct ocp_enet_private *dev = ndev->priv; local_irq_disable(); memcpy(tmp_stats, &dev->stats, sizeof(dev->stats)); tmp_stats += sizeof(dev->stats) / sizeof(u64); memcpy(tmp_stats, &dev->estats, sizeof(dev->estats)); local_irq_enable();}static void emac_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info){ struct ocp_enet_private *dev = ndev->priv; strcpy(info->driver, "ibm_emac"); strcpy(info->version, DRV_VERSION); info->fw_version[0] = '\0'; sprintf(info->bus_info, "PPC 4xx EMAC %d", dev->def->index); info->n_stats = emac_ethtool_get_stats_count(ndev); info->regdump_len = emac_ethtool_get_regs_len(ndev);}static struct ethtool_ops emac_ethtool_ops = { .get_settings = emac_ethtool_get_settings, .set_settings = emac_ethtool_set_settings, .get_drvinfo = emac_ethtool_get_drvinfo, .get_regs_len = emac_ethtool_get_regs_len, .get_regs = emac_ethtool_get_regs, .nway_reset = emac_ethtool_nway_reset, .get_ringparam = emac_ethtool_get_ringparam, .get_pauseparam = emac_ethtool_get_pauseparam, .get_rx_csum = emac_ethtool_get_rx_csum, .get_strings = emac_ethtool_get_strings, .get_stats_count = emac_ethtool_get_stats_count, .get_ethtool_stats = emac_ethtool_get_ethtool_stats, .get_link = ethtool_op_get_link, .get_tx_csum = ethtool_op_get_tx_csum, .get_sg = ethtool_op_get_sg,};static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd){ struct ocp_enet_private *dev = ndev->priv; uint16_t *data = (uint16_t *) & rq->ifr_ifru; DBG("%d: ioctl %08x" NL, dev->def->index, cmd); if (dev->phy.address < 0) return -EOPNOTSUPP; switch (cmd) { case SIOCGMIIPHY: case SIOCDEVPRIVATE: data[0] = dev->phy.address; /* Fall through */ case SIOCGMIIREG: case SIOCDEVPRIVATE + 1: data[3] = emac_mdio_read(ndev, dev->phy.address, data[1]); return 0; case SIOCSMIIREG: case SIOCDEVPRIVATE + 2: if (!capable(CAP_NET_ADMIN)) return -EPERM; emac_mdio_write(ndev, dev->phy.address, data[1], data[2]); return 0; default: return -EOPNOTSUPP; }}static int __init emac_probe(struct ocp_device *ocpdev){ struct ocp_func_emac_data *emacdata = ocpdev->def->additions; struct net_device *ndev; struct ocp_device *maldev; struct ocp_enet_private *dev; int err, i; DBG("%d: probe" NL, ocpdev->def->index); 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) { printk(KERN_ERR "emac%d: could not allocate ethernet device!\n", ocpdev->def->index); return -ENOMEM; } dev = ndev->priv; dev->ndev = ndev; dev->ldev = &ocpdev->dev; dev->def = ocpdev->def; SET_MODULE_OWNER(ndev); /* Find MAL device we are connected to */ maldev = ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_MAL, emacdata->mal_idx); if (!maldev) { printk(KERN_ERR "emac%d: unknown mal%d device!\n", dev->def->index, emacdata->mal_idx); err = -ENODEV; goto out; } dev->mal = ocp_get_drvdata(maldev); if (!dev->mal) { printk(KERN_ERR "emac%d: mal%d hasn't been initialized yet!\n", dev->def->index, emacdata->mal_idx); err = -ENODEV; goto out; } /* Register with MAL */ dev->commac.ops = &emac_commac_ops; dev->commac.dev = dev; dev->commac.tx_chan_mask = MAL_CHAN_MASK(emacdata->mal_tx_chan); dev->commac.rx_chan_mask = MAL_CHAN_MASK(emacdata->mal_rx_chan); err = mal_register_commac(dev->mal, &dev->commac); if (err) { printk(KERN_ERR "emac%d: failed to register with mal%d!\n", dev->def->index, emacdata->mal_idx); goto out; } dev->rx_skb_size = emac_rx_skb_size(ndev->mtu); dev->rx_sync_size = emac_rx_sync_size(ndev->mtu); /* Get pointers to BD rings */ dev->tx_desc = dev->mal->bd_virt + mal_tx_bd_offset(dev->mal, emacdata->mal_tx_chan); dev->rx_desc = dev->mal->bd_virt + mal_rx_bd_offset(dev->mal, emacdata->mal_rx_chan); DBG("%d: tx_desc %p" NL, ocpdev->def->index, dev->tx_desc); DBG("%d: rx_desc %p" NL, ocpdev->def->index, dev->rx_desc); /* Clean rings */ memset(dev->tx_desc, 0, NUM_TX_BUFF * sizeof(struct mal_descriptor)); memset(dev->rx_desc, 0, NUM_RX_BUFF * sizeof(struct mal_descriptor)); /* If we depend on another EMAC for MDIO, check whether it was probed already */ if (emacdata->mdio_idx >= 0 && emacdata->mdio_idx != ocpdev->def->index) { struct ocp_device *mdiodev = ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, emacdata->mdio_idx); if (!mdiodev) { printk(KERN_ERR "emac%d: unknown emac%d device!\n", dev->def->index, emacdata->mdio_idx); err = -ENODEV; goto out2; } dev->mdio_dev = ocp_get_drvdata(mdiodev); if (!dev->mdio_dev) { printk(KERN_ERR "emac%d: emac%d hasn't been initialized yet!\n", dev->def->index, emacdata->mdio_idx); err = -ENODEV; goto out2; } } /* Attach to ZMII, if needed */ if ((err = zmii_attach(dev)) != 0) goto out2; /* Attach to RGMII, if needed */ if ((err = rgmii_attach(dev)) != 0) goto out3; /* Attach to TAH, if needed */ if ((err = tah_attach(dev)) != 0) goto out4; /* Map EMAC regs */ dev->emacp = (struct emac_regs *)ioremap(dev->def->paddr, sizeof(struct emac_regs)); if (!dev->emacp) { printk(KERN_ERR "emac%d: could not ioremap device registers!\n", dev->def->index); err = -ENOMEM; goto out5; } /* Fill in MAC address */ for (i = 0; i < 6; ++i) ndev->dev_addr[i] = emacdata->mac_addr[i]; /* Set some link defaults before we can find out real parameters */ dev->phy.speed = SPEED_100; dev->phy.duplex = DUPLEX_FULL; dev->phy.autoneg = AUTONEG_DISABLE; dev->phy.pause = dev->phy.asym_pause = 0; dev->stop_timeout = STOP_TIMEOUT_100; init_timer(&dev->link_timer); dev->link_timer.function = emac_link_timer; dev->link_timer.data = (unsigned long)dev; /* Find PHY if any */ dev->phy.dev = ndev; dev->phy.mode = emacdata->phy_mode; if (emacdata->phy_map != 0xffffffff) { u32 phy_map = emacdata->phy_map | busy_phy_map; u32 adv; DBG("%d: PHY maps %08x %08x" NL, dev->def->index, emacdata->phy_map, busy_phy_map); EMAC_RX_CLK_TX(dev->def->index); dev->phy.mdio_read = emac_mdio_read; dev->phy.mdio_write = emac_mdio_write; /* Configure EMAC with defaults so we can at least use MDIO * This is needed mostly for 440GX */ if (emac_phy_gpcs(dev->phy.mode)) { /* XXX * Make GPCS PHY address equal to EMAC index. * We probably should take into account busy_phy_map * and/or phy_map here. */ dev->phy.address = dev->def->index; } emac_configure(dev); for (i = 0; i < 0x20; phy_map >>= 1, ++i) if (!(phy_map & 1)) { int r; busy_phy_map |= 1 << i; /* Quick check if there is a PHY at the address */ r = emac_mdio_read(dev->ndev, i, MII_BMCR); if (r == 0xffff || r < 0) continue; if (!mii_phy_probe(&dev->phy, i)) break; } if (i == 0x20) { printk(KERN_WARNING "emac%d: can't find PHY!\n", dev->def->index); goto out6; } /* Init PHY */ if (dev->phy.def->ops->init) dev->phy.def->ops->init(&dev->phy); /* Disable any PHY features not supported by the platform */ dev->phy.def->features &= ~emacdata->phy_feat_exc; /* Setup initial link parameters */ if (dev->phy.features & SUPPORTED_Autoneg) { adv = dev->phy.features;#if !defined(CONFIG_40x) adv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;#endif /* Restart autonegotiation */ dev->phy.def->ops->setup_aneg(&dev->phy, adv); } else { u32 f = dev->phy.def->features; int speed = SPEED_10, fd = DUPLEX_HALF; /* Select highest supported speed/duplex */ if (f & SUPPORTED_1000baseT_Full) { speed = SPEED_1000; fd = DUPLEX_FULL; } else if (f & SUPPORTED_1000baseT_Half) speed = SPEED_1000; else if (f & SUPPORTED_100baseT_Full) { speed = SPEED_100; fd = DUPLEX_FULL; } else if (f & SUPPORTED_100baseT_Half) speed = SPEED_100; else if (f & SUPPORTED_10baseT_Full) fd = DUPLEX_FULL; /* Force link parameters */ dev->phy.def->ops->setup_forced(&dev->phy, speed, fd); } } else { emac_reset(dev); /* PHY-less configuration. * XXX I probably should move these settings to emacdata */ dev->phy.address = -1; dev->phy.features = SUPPORTED_100baseT_Full | SUPPORTED_MII; dev->phy.pause = 1; } /* Fill in the driver function table */ ndev->open = &emac_open; if (dev->tah_dev) { ndev->hard_start_xmit = &emac_start_xmit_sg; ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; } else ndev->hard_start_xmit = &emac_start_xmit; ndev->tx_timeout = &emac_full_tx_reset; ndev->watchdog_timeo = 5 * HZ; ndev->stop = &emac_close; ndev->get_stats = &emac_stats; ndev->set_multicast_list = &emac_set_multicast_list; ndev->do_ioctl = &emac_ioctl; if (emac_phy_supports_gige(emacdata->phy_mode)) { ndev->change_mtu = &emac_change_mtu; dev->commac.ops = &emac_commac_sg_ops; } SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops); netif_carrier_off(ndev); netif_stop_queue(ndev); err = register_netdev(ndev); if (err) { printk(KERN_ERR "emac%d: failed to register net device (%d)!\n", dev->def->index, err); goto out6; } ocp_set_drvdata(ocpdev, dev); printk("%s: emac%d, MAC %02x:%02x:%02x:%02x:%02x:%02x\n", ndev->name, dev->def->index, ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]); if (dev->phy.address >= 0) printk("%s: found %s PHY (0x%02x)\n", ndev->name, dev->phy.def->name, dev->phy.address); emac_dbg_register(dev->def->index, dev); return 0; out6: iounmap((void *)dev->emacp); out5: tah_fini(dev->tah_dev); out4: rgmii_fini(dev->rgmii_dev, dev->rgmii_input); out3: zmii_fini(dev->zmii_dev, dev->zmii_input); out2: mal_unregister_commac(dev->mal, &dev->commac); out: kfree(ndev); return err;}static struct ocp_device_id emac_ids[] = { { .vendor = OCP_VENDOR_IBM, .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_DESC ", version " DRV_VERSION "\n"); DBG(": init" NL); if (mal_init()) return -ENODEV; EMAC_CLK_INTERNAL; if (ocp_register_driver(&emac_driver)) { EMAC_CLK_EXTERNAL; ocp_unregister_driver(&emac_driver); mal_exit(); return -ENODEV; } EMAC_CLK_EXTERNAL; emac_init_debug(); return 0;}static void __exit emac_exit(void){ DBG(": exit" NL); ocp_unregister_driver(&emac_driver); mal_exit(); emac_fini_debug();}module_init(emac_init);module_exit(emac_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -