/*********************************************************************** * OK335xS 网络连接打印信息 hacking * 说明: * 当我们插入网线的时候,经常会看到对应的网卡已连接,当前属于10M、 * 100M网卡工作状态等等信息,那么这些信息是如何被输出的,工作机制是什么, * 网卡的速度是由phy决定的还是由mac决定的,是不是在phy对应的中断里处理, * 等等,这些内容都需要去确认。 * * 2016-2-2 深圳 南山平山村 曾剑锋 **********************************************************************/ static const struct dev_pm_ops cpsw_pm_ops = { <------+ .suspend = cpsw_suspend, | .resume = cpsw_resume, -----------+ }; | | | | static struct platform_driver cpsw_driver = { <---|-+ | .driver = { | | | .name = "cpsw", | | | .owner = THIS_MODULE, | | | .pm = &cpsw_pm_ops, ----------+ | | }, | | .probe = cpsw_probe, | | .remove = __devexit_p(cpsw_remove), | | }; | | | | static int __init cpsw_init(void) | | { | | return platform_driver_register(&cpsw_driver); -----+ | } | late_initcall(cpsw_init); | | static void __exit cpsw_exit(void) | { | platform_driver_unregister(&cpsw_driver); | } | module_exit(cpsw_exit); | | MODULE_LICENSE("GPL"); | MODULE_DESCRIPTION("TI CPSW Ethernet driver"); | | | | static int cpsw_resume(struct device *dev) <----------+ { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = platform_get_drvdata(pdev); pm_runtime_get_sync(&pdev->dev); cpsw_start_slaves_interface(ndev); ----+ | return 0; | } | | V static inline void cpsw_start_slaves_interface(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); u32 i = 0; for (i = 0; i < priv->data.slaves; i++) { ndev = cpsw_get_slave_ndev(priv, i); if (netif_running(ndev)) cpsw_ndo_open(ndev); ----------+ } | } | | | static int cpsw_ndo_open(struct net_device *ndev) <--+ { struct cpsw_priv *priv = netdev_priv(ndev); int i, ret; u32 reg; if (!cpsw_common_res_usage_state(priv)) cpsw_intr_disable(priv); netif_carrier_off(ndev); if (priv->data.phy_control) (*priv->data.phy_control)(true); reg = __raw_readl(&priv->regs->id_ver); priv->version = reg; msg(info, ifup, "initializing cpsw version %d.%d (%d)\n", (reg >> 8 & 0x7), reg & 0xff, (reg >> 11) & 0x1f); /* initialize host and slave ports */ if (!cpsw_common_res_usage_state(priv)) cpsw_init_host_port(priv); for_each_slave(priv, cpsw_slave_open, priv); -----------+ | /* Add default VLAN */ | cpsw_add_default_vlan(priv); | | if (!cpsw_common_res_usage_state(priv)) { | ret = device_create_file(&ndev->dev, &dev_attr_hw_stats); | if (ret < 0) { | dev_err(priv->dev, "unable to add device attr\n"); | return ret; | } | | /* setup tx dma to fixed prio and zero offset */ | cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); | cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); | | /* disable priority elevation and enable statistics */ | /* on all ports */ | writel(0, &priv->regs->ptype); | writel(0x7, &priv->regs->stat_port_en); | | if (WARN_ON(!priv->data.rx_descs)) | priv->data.rx_descs = 128; | | for (i = 0; i < priv->data.rx_descs; i++) { | struct sk_buff *skb; | | ret = -ENOMEM; | skb = netdev_alloc_skb_ip_align(priv->ndev, | priv->rx_packet_max); | if (!skb) | break; | ret = cpdma_chan_submit(priv->rxch, skb, skb->data, | skb_tailroom(skb), 0, GFP_KERNEL); | if (WARN_ON(ret < 0)) { | dev_kfree_skb_any(skb); | break; | } | } | /* | * continue even if we didn‘t manage to submit all | * receive descs | */ | msg(info, ifup, "submitted %d rx descriptors\n", i); | | if (cpts_register(&priv->pdev->dev, priv->cpts, | priv->data.cpts_clock_mult, | priv->data.cpts_clock_shift)) | dev_err(priv->dev, "error registering cpts device\n"); | } | | /* Enable Interrupt pacing if configured */ | if (priv->coal_intvl != 0) { | struct ethtool_coalesce coal; | | coal.rx_coalesce_usecs = (priv->coal_intvl << 4); | cpsw_set_coalesce(ndev, &coal); | } | | /* Enable Timer for capturing cpsw rx interrupts */ | omap_dm_timer_set_int_enable(dmtimer_rx, OMAP_TIMER_INT_CAPTURE);| omap_dm_timer_set_capture(dmtimer_rx, 1, 0, 0); | omap_dm_timer_enable(dmtimer_rx); | | /* Enable Timer for capturing cpsw tx interrupts */ | omap_dm_timer_set_int_enable(dmtimer_tx, OMAP_TIMER_INT_CAPTURE);| omap_dm_timer_set_capture(dmtimer_tx, 1, 0, 0); | omap_dm_timer_enable(dmtimer_tx); | | cpdma_ctlr_start(priv->dma); | cpsw_intr_enable(priv); | napi_enable(&priv->napi); | cpdma_ctlr_eoi(priv->dma); | | cpsw_update_slave_open_state(priv, true) | | return 0; +-----------------------------------------------+ } | | V static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) { char name[32]; u32 slave_port; sprintf(name, "slave-%d", slave->slave_num); soft_reset(name, &slave->sliver->soft_reset); /* setup priority mapping */ writel(0x76543210, &slave->sliver->rx_pri_map); if (priv->version == CPSW_VERSION1) slave_write(slave, 0x33221100, CPSW1_TX_PRI_MAP); else slave_write(slave, 0x33221100, CPSW2_TX_PRI_MAP); /* setup max packet size, and mac address */ __raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen); cpsw_set_slave_mac(slave, priv); slave->mac_control = 0; /* no link yet */ slave_port = cpsw_get_slave_port(priv, slave->slave_num); cpsw_add_dual_emac_mode_default_ale_entries(priv, slave, slave_port); cpsw_add_switch_mode_bcast_ale_entries(priv, slave_port); priv->port_state[slave_port] = ALE_PORT_STATE_FORWARD; slave->phy = phy_connect(priv->ndev, slave->data->phy_id, &cpsw_adjust_link, 0, slave->data->phy_if); ------+ if (IS_ERR(slave->phy)) { | msg(err, ifup, "phy %s not found on slave %d\n", | slave->data->phy_id, slave->slave_num); | slave->phy = NULL; | } else { | dev_info(priv->dev, "CPSW phy found : id is : 0x%x\n", | slave->phy->phy_id); | cpsw_set_phy_config(priv, slave->phy); | phy_start(slave->phy); | } | } | | | static void cpsw_adjust_link(struct net_device *ndev) <---------+ { struct cpsw_priv *priv = netdev_priv(ndev); bool link = false; for_each_slave(priv, _cpsw_adjust_link, priv, &link); ----------+ | if (link) { | netif_carrier_on(ndev); | if (netif_running(ndev)) | netif_wake_queue(ndev); | } else { | netif_carrier_off(ndev); | netif_stop_queue(ndev); | } | } | | | static void _cpsw_adjust_link(struct cpsw_slave *slave, <--------+ struct cpsw_priv *priv, bool *link) { struct phy_device *phy = slave->phy; u32 mac_control = 0; u32 slave_port; if (!phy) return; slave_port = cpsw_get_slave_port(priv, slave->slave_num); if (phy->link) { /* enable forwarding */ cpsw_ale_control_set(priv->ale, slave_port, ALE_PORT_STATE, priv->port_state[slave_port]); mac_control = priv->data.mac_control; if (phy->speed == 10) mac_control |= BIT(18); /* In Band mode */ if (phy->speed == 1000) { mac_control |= BIT(7); /* Enable gigabit mode */ } if (phy->speed == 100) mac_control |= BIT(15); if (phy->duplex) mac_control |= BIT(0); /* FULLDUPLEXEN */ if (phy->interface == PHY_INTERFACE_MODE_RGMII) /* RGMII */ mac_control |= (BIT(15)|BIT(16)); *link = true; } else { cpsw_ale_control_set(priv->ale, slave_port, ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); mac_control = 0; } if (mac_control != slave->mac_control) { phy_print_status(phy); ---------------+ __raw_writel(mac_control, &slave->sliver->mac_control); | } | | slave->mac_control = mac_control; | } | | /** | * phy_print_status - Convenience function to print out the current phy status | * @phydev: the phy_device struct | */ | void phy_print_status(struct phy_device *phydev) <-------------+ { pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev), phydev->link ? "Up" : "Down"); if (phydev->link) printk(KERN_CONT " - %d/%s", phydev->speed, DUPLEX_FULL == phydev->duplex ? "Full" : "Half"); printk(KERN_CONT "\n"); } EXPORT_SYMBOL(phy_print_status); /* * 1. LAN8710A/LAN8710Ai datasheet * 1. http://ww1.microchip.com/downloads/en/DeviceDoc/8710a.pdf * 2. 1.2 General Description * ...... * The LAN8710A/LAN8710Ai supports communication with an Ethernet MAC via a standard MII (IEEE 802.3u)/RMII interface. It contains a full-duplex 10-BASE-T/100BASE-TX transceiver and supports 10Mbps (10BASE-T) and 100Mbps (100BASE-TX) operation. The LAN8710A/LAN8710Ai implements auto-negotiation to automatically determine the best possible speed and duplex mode of operation. HP Auto-MDIX support allows the use of direct connect or cross-over LAN cables. * ...... * 2. 由上面可知,决定网卡的工作速度的是phy自动决定。 */
时间: 2024-10-25 11:02:08