/* called back by PHY layer if there is change in link state of hw port*/ staticvoid emac_adjust_link(struct net_device *ndev)
{ struct prueth_emac *emac = netdev_priv(ndev); struct phy_device *phydev = ndev->phydev; struct prueth *prueth = emac->prueth; bool new_state = false; unsignedlong flags;
if (phydev->link) { /* check the mode of operation - full/half duplex */ if (phydev->duplex != emac->duplex) {
new_state = true;
emac->duplex = phydev->duplex;
} if (phydev->speed != emac->speed) {
new_state = true;
emac->speed = phydev->speed;
} if (!emac->link) {
new_state = true;
emac->link = 1;
}
} elseif (emac->link) {
new_state = true;
emac->link = 0;
/* f/w should support 100 & 1000 */
emac->speed = SPEED_1000;
/* half duplex may not be supported by f/w */
emac->duplex = DUPLEX_FULL;
}
if (new_state) {
phy_print_status(phydev);
/* update RGMII and MII configuration based on PHY negotiated *values
*/ if (emac->link) { if (emac->duplex == DUPLEX_HALF)
icssg_config_half_duplex(emac); /* Set the RGMII cfg for gig en and full duplex */
icssg_update_rgmii_cfg(prueth->miig_rt, emac);
/* update the Tx IPG based on 100M/1G speed */
spin_lock_irqsave(&emac->lock, flags);
icssg_config_ipg(emac);
spin_unlock_irqrestore(&emac->lock, flags);
icssg_config_set_speed(emac);
icssg_set_port_state(emac, ICSSG_EMAC_PORT_FORWARD);
if (!emac->half_duplex) {
dev_dbg(prueth->dev, "half duplex mode is not supported\n");
phy_remove_link_mode(ndev->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
phy_remove_link_mode(ndev->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
}
/* f/w requires at least 1uS within a cycle so CMP *cantriggerafterSYNCisenabled
*/ if (offset < 5 * NSEC_PER_USEC)
offset = 5 * NSEC_PER_USEC;
/* if offset is close to cycle time then we will miss *theCMPeventforlasttickwhenIEProllsover. *Innormalmode,IEPtickis4ns. *Inslowcompensationitcouldbe0nsor8nsat *everyslowcompensationcycle.
*/ if (offset > IEP_DEFAULT_CYCLE_TIME_NS - 8)
offset = IEP_DEFAULT_CYCLE_TIME_NS - 8;
/* we're in shadow mode so need to set upper 32-bits */
*cmp = (u64)offset << 32;
/* Notify the stack of the actual queue counts. */
ret = netif_set_real_num_tx_queues(ndev, num_data_chn); if (ret) {
dev_err(dev, "cannot set real number of tx queues\n"); return ret;
}
init_completion(&emac->cmd_complete);
ret = prueth_init_tx_chns(emac); if (ret) {
dev_err(dev, "failed to init tx channel: %d\n", ret); return ret;
}
max_rx_flows = PRUETH_MAX_RX_FLOWS;
ret = prueth_init_rx_chns(emac, &emac->rx_chns, "rx",
max_rx_flows, PRUETH_MAX_RX_DESC); if (ret) {
dev_err(dev, "failed to init rx channel: %d\n", ret); goto cleanup_tx;
}
ret = prueth_ndev_add_tx_napi(emac); if (ret) goto cleanup_rx;
/* we use only the highest priority flow for now i.e. @irq[3] */
rx_flow = PRUETH_RX_FLOW_DATA;
ret = request_irq(emac->rx_chns.irq[rx_flow], prueth_rx_irq,
IRQF_TRIGGER_HIGH, dev_name(dev), emac); if (ret) {
dev_err(dev, "unable to request RX IRQ\n"); goto cleanup_napi;
}
if (!prueth->emacs_initialized) {
ret = prueth_emac_common_start(prueth); if (ret) goto free_rx_irq;
icssg_enable_fw_offload(prueth);
}
/* inform the upper layers. */
netif_tx_stop_all_queues(ndev);
/* block packets from wire */ if (ndev->phydev)
phy_stop(ndev->phydev);
if (emac->prueth->is_hsr_offload_mode)
__dev_mc_unsync(ndev, icssg_prueth_hsr_del_mcast); else
__dev_mc_unsync(ndev, icssg_prueth_del_mcast);
atomic_set(&emac->tdown_cnt, emac->tx_ch_num); /* ensure new tdown_cnt value is visible */
smp_mb__after_atomic(); /* tear down and disable UDMA channels */
reinit_completion(&emac->tdown_complete); for (i = 0; i < emac->tx_ch_num; i++)
k3_udma_glue_tdown_tx_chn(emac->tx_chns[i].tx_chn, false);
ret = wait_for_completion_timeout(&emac->tdown_complete,
msecs_to_jiffies(1000)); if (!ret)
netdev_err(ndev, "tx teardown timeout\n");
prueth_reset_tx_chan(emac, emac->tx_ch_num, true); for (i = 0; i < emac->tx_ch_num; i++) {
napi_disable(&emac->tx_chns[i].napi_tx);
hrtimer_cancel(&emac->tx_chns[i].tx_hrtimer);
}
ret = pruss_request_mem_region(prueth->pruss,
port == PRUETH_PORT_MII0 ?
PRUSS_MEM_DRAM0 : PRUSS_MEM_DRAM1,
&emac->dram); if (ret) {
dev_err(prueth->dev, "unable to get DRAM: %d\n", ret);
ret = -ENOMEM; goto free_wq;
}
emac->tx_ch_num = 1;
irq_name = "tx_ts0"; if (emac->port_id == PRUETH_PORT_MII1)
irq_name = "tx_ts1";
emac->tx_ts_irq = platform_get_irq_byname_optional(prueth->pdev, irq_name); if (emac->tx_ts_irq < 0) {
ret = dev_err_probe(prueth->dev, emac->tx_ts_irq, "could not get tx_ts_irq\n"); goto free;
}
emac->phy_node = of_parse_phandle(eth_node, "phy-handle", 0); if (!emac->phy_node && !of_phy_is_fixed_link(eth_node)) {
dev_err(prueth->dev, "couldn't find phy-handle\n");
ret = -ENODEV; goto free;
} elseif (of_phy_is_fixed_link(eth_node)) {
ret = of_phy_register_fixed_link(eth_node); if (ret) {
ret = dev_err_probe(prueth->dev, ret, "failed to register fixed-link phy\n"); goto free;
}
emac->phy_node = eth_node;
}
ret = of_get_phy_mode(eth_node, &emac->phy_if); if (ret) {
dev_err(prueth->dev, "could not get phy-mode property\n"); goto free;
}
if (emac->phy_if != PHY_INTERFACE_MODE_MII &&
!phy_interface_mode_is_rgmii(emac->phy_if)) {
dev_err(prueth->dev, "PHY mode unsupported %s\n", phy_modes(emac->phy_if));
ret = -EINVAL; goto free;
}
/* AM65 SR2.0 has TX Internal delay always enabled by hardware *anditisnotpossibletodisableTXInternaldelay.Thebelow *switchcaseblockdescribeshowwehandledifferentphymodes *basedonhardwarerestriction.
*/ switch (emac->phy_if) { case PHY_INTERFACE_MODE_RGMII_ID:
emac->phy_if = PHY_INTERFACE_MODE_RGMII_RXID; break; case PHY_INTERFACE_MODE_RGMII_TXID:
emac->phy_if = PHY_INTERFACE_MODE_RGMII; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_RXID:
dev_err(prueth->dev, "RGMII mode without TX delay is not supported");
ret = -EINVAL; goto free; default: break;
}
/* get mac address from DT and set private and netdev addr */
ret = of_get_ethdev_address(eth_node, ndev); if (!is_valid_ether_addr(ndev->dev_addr)) {
eth_hw_addr_random(ndev);
dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n",
port, ndev->dev_addr);
}
ether_addr_copy(emac->mac_addr, ndev->dev_addr);
/* Detach the net_device for both PRUeth ports*/ if (netif_running(emac0->ndev))
netif_device_detach(emac0->ndev); if (netif_running(emac1->ndev))
netif_device_detach(emac1->ndev);
/* Disable both PRUeth ports */
ret = icssg_set_port_state(emac0, ICSSG_EMAC_PORT_DISABLE);
ret |= icssg_set_port_state(emac1, ICSSG_EMAC_PORT_DISABLE); if (ret) return ret;
/* Stop both pru cores for both PRUeth ports*/
ret = prueth_emac_common_stop(prueth); if (ret) {
dev_err(prueth->dev, "Failed to stop the firmwares"); return ret;
}
/* Start both pru cores for both PRUeth ports */
ret = prueth_emac_common_start(prueth); if (ret) {
dev_err(prueth->dev, "Failed to start the firmwares"); return ret;
}
/* Enable forwarding for both PRUeth ports */
ret = icssg_set_port_state(emac0, ICSSG_EMAC_PORT_FORWARD);
ret |= icssg_set_port_state(emac1, ICSSG_EMAC_PORT_FORWARD);
/* Attache net_device for both PRUeth ports */
netif_device_attach(emac0->ndev);
netif_device_attach(emac1->ndev);
return ret;
}
staticvoid icssg_change_mode(struct prueth *prueth)
{ int ret;
ret = prueth_emac_restart(prueth); if (ret) {
dev_err(prueth->dev, "Failed to restart the firmwares, aborting the process"); return;
}
if (!prueth->br_members) {
prueth->hw_bridge_dev = br_ndev;
} else { /* This is adding the port to a second bridge, this is *unsupported
*/ if (prueth->hw_bridge_dev != br_ndev) return -EOPNOTSUPP;
}
if (prueth->is_switch_mode) {
prueth->is_switch_mode = false;
emac->port_vlan = 0;
ret = prueth_emac_restart(prueth); if (ret) {
dev_err(prueth->dev, "Failed to restart the firmwares, aborting the process"); return;
}
}
prueth_offload_fwd_mark_update(prueth);
if (!prueth->br_members)
prueth->hw_bridge_dev = NULL;
}
if (strcmp(eth_node->name, "port")) continue;
ret = of_property_read_u32(eth_node, "reg", ®); if (ret < 0) {
dev_err(dev, "%pOF error reading port_id %d\n",
eth_node, ret);
}
of_node_get(eth_node);
if (reg == 0) {
eth0_node = eth_node; if (!of_device_is_available(eth0_node)) {
of_node_put(eth0_node);
eth0_node = NULL;
}
} elseif (reg == 1) {
eth1_node = eth_node; if (!of_device_is_available(eth1_node)) {
of_node_put(eth1_node);
eth1_node = NULL;
}
} else {
dev_err(dev, "port reg should be 0 or 1\n");
}
}
of_node_put(eth_ports_node);
/* At least one node must be present and available else we fail */ if (!eth0_node && !eth1_node) {
dev_err(dev, "neither port0 nor port1 node available\n"); return -ENODEV;
}
if (eth0_node == eth1_node) {
dev_err(dev, "port0 and port1 can't have same reg\n");
of_node_put(eth0_node); return -ENODEV;
}
prueth->miig_rt = syscon_regmap_lookup_by_phandle(np, "ti,mii-g-rt"); if (IS_ERR(prueth->miig_rt)) {
dev_err(dev, "couldn't get ti,mii-g-rt syscon regmap\n"); return -ENODEV;
}
prueth->mii_rt = syscon_regmap_lookup_by_phandle(np, "ti,mii-rt"); if (IS_ERR(prueth->mii_rt)) {
dev_err(dev, "couldn't get ti,mii-rt syscon regmap\n"); return -ENODEV;
}
prueth->pa_stats = syscon_regmap_lookup_by_phandle(np, "ti,pa-stats"); if (IS_ERR(prueth->pa_stats)) {
dev_err(dev, "couldn't get ti,pa-stats syscon regmap\n");
prueth->pa_stats = NULL;
}
if (eth0_node || eth1_node) {
ret = prueth_get_cores(prueth, ICSS_SLICE0, false); if (ret) goto put_cores;
ret = prueth_get_cores(prueth, ICSS_SLICE1, false); if (ret) goto put_cores;
}
pruss = pruss_get(eth0_node ?
prueth->pru[ICSS_SLICE0] : prueth->pru[ICSS_SLICE1]); if (IS_ERR(pruss)) {
ret = PTR_ERR(pruss);
dev_err(dev, "unable to get pruss handle\n"); goto put_cores;
}
prueth->pruss = pruss;
ret = pruss_request_mem_region(pruss, PRUSS_MEM_SHRD_RAM2,
&prueth->shram); if (ret) {
dev_err(dev, "unable to get PRUSS SHRD RAM2: %d\n", ret); goto put_pruss;
}
prueth->sram_pool = of_gen_pool_get(np, "sram", 0); if (!prueth->sram_pool) {
dev_err(dev, "unable to get SRAM pool\n");
ret = -ENODEV;
goto put_mem;
}
prueth->is_switchmode_supported = prueth->pdata.switch_mode; if (prueth->pdata.banked_ms_ram) { /* Reserve 2 MSMC RAM banks for buffers to avoid arbitration */
msmc_ram_size = (2 * MSMC_RAM_BANK_SIZE);
} else {
msmc_ram_size = PRUETH_EMAC_TOTAL_BUF_SIZE; if (prueth->is_switchmode_supported)
msmc_ram_size = PRUETH_SW_TOTAL_BUF_SIZE;
}
/* NOTE: FW bug needs buffer base to be 64KB aligned */
prueth->msmcram.va =
(void __iomem *)gen_pool_alloc_algo(prueth->sram_pool,
msmc_ram_size,
gen_pool_first_fit_align,
&gp_data);
if (!prueth->msmcram.va) {
ret = -ENOMEM;
dev_err(dev, "unable to allocate MSMC resource\n"); goto put_mem;
}
prueth->msmcram.pa = gen_pool_virt_to_phys(prueth->sram_pool,
(unsignedlong)prueth->msmcram.va);
prueth->msmcram.size = msmc_ram_size;
memset_io(prueth->msmcram.va, 0, msmc_ram_size);
dev_dbg(dev, "sram: pa %llx va %p size %zx\n", prueth->msmcram.pa,
prueth->msmcram.va, prueth->msmcram.size);
prueth->iep0 = icss_iep_get_idx(np, 0); if (IS_ERR(prueth->iep0)) {
ret = dev_err_probe(dev, PTR_ERR(prueth->iep0), "iep0 get failed\n");
prueth->iep0 = NULL; goto free_pool;
}
prueth->iep1 = icss_iep_get_idx(np, 1); if (IS_ERR(prueth->iep1)) {
ret = dev_err_probe(dev, PTR_ERR(prueth->iep1), "iep1 get failed\n"); goto put_iep0;
}
if (prueth->pdata.quirk_10m_link_issue) { /* Enable IEP1 for FW in 64bit mode as W/A for 10M FD link detect issue under TX *traffic.
*/
icss_iep_init_fw(prueth->iep1);
}
/* Read EMAC firmware names from device tree */
icssg_read_firmware_names(np, prueth->icssg_emac_firmwares);
/* Generate other mode firmware names based on EMAC firmware names */
icssg_mode_firmware_names(dev, prueth->icssg_emac_firmwares,
prueth->icssg_switch_firmwares, "eth", "sw");
icssg_mode_firmware_names(dev, prueth->icssg_emac_firmwares,
prueth->icssg_hsr_firmwares, "eth", "hsr");
icssg_mode_firmware_names(dev, prueth->icssg_emac_firmwares,
prueth->icssg_prp_firmwares, "eth", "prp");
spin_lock_init(&prueth->vtbl_lock);
spin_lock_init(&prueth->stats_lock); /* setup netdev interfaces */ if (eth0_node) {
ret = prueth_netdev_init(prueth, eth0_node); if (ret) {
dev_err_probe(dev, ret, "netdev init %s failed\n",
eth0_node->name); goto exit_iep;
}
/* register the network devices */ if (eth0_node) {
ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); if (ret) {
dev_err(dev, "can't register netdev for port MII0"); goto netdev_exit;
}
ret = emac_phy_connect(prueth->emac[PRUETH_MAC0]); if (ret) {
dev_err(dev, "can't connect to MII0 PHY, error -%d", ret); goto netdev_unregister;
}
phy_attached_info(prueth->emac[PRUETH_MAC0]->ndev->phydev);
}
if (eth1_node) {
ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); if (ret) {
dev_err(dev, "can't register netdev for port MII1"); goto netdev_unregister;
}
prueth->registered_netdevs[PRUETH_MAC1] = prueth->emac[PRUETH_MAC1]->ndev;
ret = emac_phy_connect(prueth->emac[PRUETH_MAC1]); if (ret) {
dev_err(dev, "can't connect to MII1 PHY, error %d", ret); goto netdev_unregister;
}
phy_attached_info(prueth->emac[PRUETH_MAC1]->ndev->phydev);
}
if (prueth->is_switchmode_supported) {
ret = prueth_register_notifiers(prueth); if (ret) goto netdev_unregister;
if (eth1_node)
of_node_put(eth1_node); if (eth0_node)
of_node_put(eth0_node); return0;
netdev_unregister: for (i = 0; i < PRUETH_NUM_MACS; i++) { if (!prueth->registered_netdevs[i]) continue; if (prueth->emac[i]->ndev->phydev) {
phy_disconnect(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
}
unregister_netdev(prueth->registered_netdevs[i]);
}
netdev_exit: for (i = 0; i < PRUETH_NUM_MACS; i++) {
eth_node = prueth->eth_node[i]; if (!eth_node) continue;
prueth_netdev_exit(prueth, eth_node);
}
exit_iep: if (prueth->pdata.quirk_10m_link_issue)
icss_iep_exit_fw(prueth->iep1);
icss_iep_put(prueth->iep1);
for (i = 0; i < PRUETH_NUM_MACS; i++) { if (!prueth->registered_netdevs[i]) continue;
phy_stop(prueth->emac[i]->ndev->phydev);
phy_disconnect(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
unregister_netdev(prueth->registered_netdevs[i]);
}
for (i = 0; i < PRUETH_NUM_MACS; i++) {
eth_node = prueth->eth_node[i]; if (!eth_node) continue;
prueth_netdev_exit(prueth, eth_node);
}
if (prueth->pdata.quirk_10m_link_issue)
icss_iep_exit_fw(prueth->iep1);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.