/*********************************************************************** * OK335xS 網絡鏈接打印信息 hacking * 說明: * 當咱們插入網線的時候,常常會看到對應的網卡已鏈接,當前屬於10M、 * 100M網卡工做狀態等等信息,那麼這些信息是如何被輸出的,工做機制是什麼, * 網卡的速度是由phy決定的仍是由mac決定的,是否是在phy對應的中斷裏處理, * 等等,這些內容都須要去確認。 * * 2016-2-2 深圳 南山平山村 曾劍鋒 **********************************************************************/ /** * 參考文章: * Linux PHY幾個狀態的跟蹤 * http://www.latelee.org/programming-under-linux/linux-phy-state.html */ 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); | | } +-------------------------------------------+ | } | | | | V | struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, | void (*handler)(struct net_device *), u32 flags, | phy_interface_t interface) | { | struct phy_device *phydev; | struct device *d; | int rc; | | /* Search the list of PHY devices on the mdio bus for the | * PHY with the requested name */ | d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); | if (!d) { | pr_err("PHY %s not found\n", bus_id); | return ERR_PTR(-ENODEV); | } | phydev = to_phy_device(d); | | rc = phy_connect_direct(dev, phydev, handler, flags, interface); ----+ | if (rc) | | return ERR_PTR(rc); | | | | return phydev; | | } | | EXPORT_SYMBOL(phy_connect); | | +----------------------------------------------------------+ | V | int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, | void (*handler)(struct net_device *), u32 flags, | phy_interface_t interface) | { | int rc; | | rc = phy_attach_direct(dev, phydev, flags, interface); | if (rc) | return rc; | | phy_prepare_link(phydev, handler); ----------------+ | phy_start_machine(phydev, NULL); ---------------------*--------+ | if (phydev->irq > 0) | | | phy_start_interrupts(phydev); | | | | | | return 0; | | | } | | | EXPORT_SYMBOL(phy_connect_direct); | | | | | | | | | static void phy_prepare_link(struct phy_device *phydev, <-----+ | | void (*handler)(struct net_device *)) | | { | | phydev->adjust_link = handler; | | } | | | | | | /** | | * phy_start_machine - start PHY state machine tracking | | * @phydev: the phy_device struct | | * @handler: callback function for state change notifications | | * | | * Description: The PHY infrastructure can run a state machine | | * which tracks whether the PHY is starting up, negotiating, | | * etc. This function starts the timer which tracks the state | | * of the PHY. If you want to be notified when the state changes, | | * pass in the callback @handler, otherwise, pass NULL. If you | | * want to maintain your own state machine, do not call this | | * function. | | */ | | void phy_start_machine(struct phy_device *phydev, <--------------+ | void (*handler)(struct net_device *)) | { | phydev->adjust_state = handler; | | schedule_delayed_work(&phydev->state_queue, HZ); | } | | | 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自動決定。 */