This series includes adding Motorcomm YT6801 Gigabit ethernet driver and adding yt6801 ethernet driver entry in MAINTAINERS file.
YT6801 integrates a YT8531S phy.
Frank Sae (16): motorcomm:yt6801: Add support for a pci table in this module motorcomm:yt6801: Implement pci_driver shutdown motorcomm:yt6801: Implement the fxgmac_drv_probe function motorcomm:yt6801: Implement the .ndo_open function motorcomm:yt6801: Implement the fxgmac_start function motorcomm:yt6801: Implement the poll functions motorcomm:yt6801: Implement the fxgmac_init function motorcomm:yt6801: Implement the fxgmac_read_mac_addr function motorcomm:yt6801: Implement some hw_ops function motorcomm:yt6801: Implement .ndo_start_xmit function motorcomm:yt6801: Implement some net_device_ops function motorcomm:yt6801: Implement .ndo_tx_timeout and .ndo_change_mtu functions motorcomm:yt6801: Implement some ethtool_ops function motorcomm:yt6801: Implement the WOL function of ethtool_ops motorcomm:yt6801: Implement pci_driver suspend and resume motorcomm:yt6801: Update the Makefile, Kconfig and maintainer for yt6801
MAINTAINERS | 8 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/motorcomm/Kconfig | 27 + drivers/net/ethernet/motorcomm/Makefile | 6 + .../net/ethernet/motorcomm/yt6801/Makefile | 9 + .../net/ethernet/motorcomm/yt6801/yt6801.h | 617 +++ .../ethernet/motorcomm/yt6801/yt6801_desc.c | 638 ++++ .../ethernet/motorcomm/yt6801/yt6801_desc.h | 39 + .../motorcomm/yt6801/yt6801_ethtool.c | 838 ++++ .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 3383 +++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 2908 ++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.h | 32 + .../ethernet/motorcomm/yt6801/yt6801_pci.c | 191 + .../ethernet/motorcomm/yt6801/yt6801_type.h | 1398 +++++++ 15 files changed, 10096 insertions(+) create mode 100644 drivers/net/ethernet/motorcomm/Kconfig create mode 100644 drivers/net/ethernet/motorcomm/Makefile create mode 100644 drivers/net/ethernet/motorcomm/yt6801/Makefile create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_net.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_type.h
driver inclusion category: feature
--------------------------------
Implement the fxgmac_drv_probe function to init interrupts, register mdio and netdev.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../ethernet/motorcomm/yt6801/yt6801_net.c | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index 925bc1e7d..0cb2808b7 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -249,3 +249,196 @@ int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en)
return 0; } + +#ifdef CONFIG_PCI_MSI +static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *pdata) +{ + struct pci_dev *pdev = to_pci_dev(pdata->dev); + u32 i, *flags = &pdata->int_flags; + int vectors, rc, req_vectors; + + /* Since we have 4 channels, we must ensure the number of cpu core > 4 + * otherwise, just roll back to legacy + * 0-3 for rx, 4 for tx, 5 for misc + */ + vectors = num_online_cpus(); + if (vectors < FXGMAC_MAX_DMA_RX_CHANNELS) + goto enable_msi_interrupt; + + req_vectors = FXGMAC_MSIX_INT_NUMS; + pdata->msix_entries = + kcalloc(req_vectors, sizeof(struct msix_entry), GFP_KERNEL); + if (!pdata->msix_entries) + goto enable_msi_interrupt; + + for (i = 0; i < req_vectors; i++) + pdata->msix_entries[i].entry = i; + + rc = pci_enable_msix_exact(pdev, pdata->msix_entries, req_vectors); + if (rc < 0) { + yt_err(pdata, "enable MSIx err, clear msix entries.\n"); + /* Roll back to msi */ + kfree(pdata->msix_entries); + pdata->msix_entries = NULL; + req_vectors = 0; + goto enable_msi_interrupt; + } + + yt_dbg(pdata, "enable MSIx ok, cpu=%d,vectors=%d.\n", vectors, + req_vectors); + fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, + FXGMAC_FLAG_MSIX_ENABLED); + pdata->per_channel_irq = 1; + pdata->misc_irq = pdata->msix_entries[MSI_ID_PHY_OTHER].vector; + return; + +enable_msi_interrupt: + rc = pci_enable_msi(pdev); + if (rc < 0) { + fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, + FXGMAC_FLAG_LEGACY_ENABLED); + yt_err(pdata, "MSI err, rollback to LEGACY.\n"); + } else { + fxgmac_set_bits(flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, + FXGMAC_FLAG_MSI_ENABLED); + pdata->dev_irq = pdev->irq; + yt_dbg(pdata, "enable MSI ok, cpu=%d, irq=%d.\n", vectors, + pdev->irq); + } +} +#endif + +static int fxgmac_mdio_write_reg(struct mii_bus *mii_bus, int phyaddr, + int phyreg, u16 val) +{ + struct fxgmac_pdata *yt = mii_bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + return yt->hw_ops.write_phy_reg(yt, phyreg, val); +} + +static int fxgmac_mdio_read_reg(struct mii_bus *mii_bus, int phyaddr, int phyreg) +{ + struct fxgmac_pdata *yt = mii_bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + return yt->hw_ops.read_phy_reg(yt, phyreg); +} + +static int fxgmac_mdio_register(struct fxgmac_pdata *pdata) +{ + struct pci_dev *pdev = to_pci_dev(pdata->dev); + struct phy_device *phydev; + struct mii_bus *new_bus; + int ret; + + new_bus = devm_mdiobus_alloc(&pdev->dev); + if (!new_bus) { + yt_err(pdata, "devm_mdiobus_alloc err\n"); + return -ENOMEM; + } + + new_bus->name = "yt6801"; + new_bus->priv = pdata; + new_bus->parent = &pdev->dev; + new_bus->irq[0] = PHY_MAC_INTERRUPT; + snprintf(new_bus->id, MII_BUS_ID_SIZE, "yt6801-%x-%x", + pci_domain_nr(pdev->bus), pci_dev_id(pdev)); + + new_bus->read = fxgmac_mdio_read_reg; + new_bus->write = fxgmac_mdio_write_reg; + + ret = devm_mdiobus_register(&pdev->dev, new_bus); + if (ret < 0) { + yt_err(pdata, "devm_mdiobus_register err:%x\n", ret); + return ret; + } + + phydev = mdiobus_get_phy(new_bus, 0); + if (!phydev) { + yt_err(pdata, "mdiobus_get_phy err\n"); + return -ENODEV; + } + + pdata->phydev = phydev; + phydev->mac_managed_pm = true; + phy_support_asym_pause(phydev); + + /* PHY will be woken up in rtl_open() */ + phy_suspend(phydev); + + return 0; +} + +int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res) +{ + struct fxgmac_hw_ops *hw_ops; + struct fxgmac_pdata *pdata; + struct net_device *netdev; + int ret; + + netdev = alloc_etherdev_mq(sizeof(struct fxgmac_pdata), + FXGMAC_MAX_DMA_RX_CHANNELS); + if (!netdev) { + dev_err(dev, "alloc_etherdev_mq err\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(netdev, dev); + pdata = netdev_priv(netdev); + + pdata->dev = dev; + pdata->netdev = netdev; + pdata->dev_irq = res->irq; + pdata->hw_addr = res->addr; + pdata->msg_enable = NETIF_MSG_DRV; + pdata->dev_state = FXGMAC_DEV_PROBE; + + /* Default to legacy interrupt */ + fxgmac_set_bits(&pdata->int_flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, FXGMAC_FLAG_LEGACY_ENABLED); + pdata->misc_irq = pdata->dev_irq; + pci_set_drvdata(to_pci_dev(pdata->dev), pdata); + +#ifdef CONFIG_PCI_MSI + fxgmac_init_interrupt_scheme(pdata); +#endif + + ret = fxgmac_init(pdata, true); + if (ret < 0) { + yt_err(pdata, "fxgmac_init err:%d\n", ret); + goto err_free_netdev; + } + + hw_ops = &pdata->hw_ops; + hw_ops->reset_phy(pdata); + hw_ops->release_phy(pdata); + ret = fxgmac_mdio_register(pdata); + if (ret < 0) { + yt_err(pdata, "fxgmac_mdio_register err:%d\n", ret); + goto err_free_netdev; + } + + netif_carrier_off(netdev); + ret = register_netdev(netdev); + if (ret) { + yt_err(pdata, "register_netdev err:%d\n", ret); + goto err_free_netdev; + } + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s, netdev num_tx_q=%u\n", __func__, + netdev->real_num_tx_queues); + + return 0; + +err_free_netdev: + free_netdev(netdev); + return ret; +}
driver inclusion category: feature
--------------------------------
Implement the .ndo_open function to Calculate the Rx buffer sizeļ¼ allocate the channels and rings.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../ethernet/motorcomm/yt6801/yt6801_desc.c | 273 ++++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 99 +++++++ 2 files changed, 372 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c index 476cf6633..2edf53d9b 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_desc.c @@ -47,3 +47,276 @@ void fxgmac_desc_data_unmap(struct fxgmac_pdata *pdata,
desc_data->mapped_as_page = 0; } + +static void fxgmac_ring_free(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring) +{ + if (!ring) + return; + + if (ring->desc_data_head) { + for (u32 i = 0; i < ring->dma_desc_count; i++) + fxgmac_desc_data_unmap(pdata, + FXGMAC_GET_DESC_DATA(ring, i)); + + kfree(ring->desc_data_head); + ring->desc_data_head = NULL; + } + + if (ring->rx_hdr_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, + ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_hdr_pa.pages); + + ring->rx_hdr_pa.pages = NULL; + ring->rx_hdr_pa.pages_len = 0; + ring->rx_hdr_pa.pages_offset = 0; + ring->rx_hdr_pa.pages_dma = 0; + } + + if (ring->rx_buf_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, + ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_buf_pa.pages); + + ring->rx_buf_pa.pages = NULL; + ring->rx_buf_pa.pages_len = 0; + ring->rx_buf_pa.pages_offset = 0; + ring->rx_buf_pa.pages_dma = 0; + } + if (ring->dma_desc_head) { + dma_free_coherent(pdata->dev, (sizeof(struct fxgmac_dma_desc) * + ring->dma_desc_count), ring->dma_desc_head, + ring->dma_desc_head_addr); + ring->dma_desc_head = NULL; + } +} + +static int fxgmac_ring_init(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring, + unsigned int dma_desc_count) +{ + /* Descriptors */ + ring->dma_desc_count = dma_desc_count; + ring->dma_desc_head = + dma_alloc_coherent(pdata->dev, (sizeof(struct fxgmac_dma_desc) * + dma_desc_count), + &ring->dma_desc_head_addr, GFP_KERNEL); + if (!ring->dma_desc_head) + return -ENOMEM; + + /* Array of descriptor data */ + ring->desc_data_head = kcalloc(dma_desc_count, + sizeof(struct fxgmac_desc_data), + GFP_KERNEL); + if (!ring->desc_data_head) + return -ENOMEM; + + yt_dbg(pdata, + "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n", + ring->dma_desc_head, &ring->dma_desc_head_addr, + ring->desc_data_head); + return 0; +} + +static void fxgmac_rings_free(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel = pdata->channel_head; + + fxgmac_ring_free(pdata, channel->tx_ring); + + for (u32 i = 0; i < pdata->channel_count; i++, channel++) + fxgmac_ring_free(pdata, channel->rx_ring); +} + +static int fxgmac_rings_alloc(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel = pdata->channel_head; + int ret; + + yt_dbg(pdata, "%s - Tx ring:\n", channel->name); + ret = fxgmac_ring_init(pdata, channel->tx_ring, + pdata->tx_desc_count); + if (ret < 0) { + yt_err(pdata, "error initializing Tx ring"); + goto err_init_ring; + } + if (netif_msg_drv(pdata)) + yt_dbg(pdata, + "%s, ch=%u,tx_desc_cnt=%u\n", + __func__, 0, pdata->tx_desc_count); + + for (u32 i = 0; i < pdata->channel_count; i++, channel++) { + yt_dbg(pdata, "%s - Rx ring:\n", channel->name); + ret = fxgmac_ring_init(pdata, channel->rx_ring, + pdata->rx_desc_count); + if (ret < 0) { + yt_err(pdata, "error initializing Rx ring\n"); + goto err_init_ring; + } + if (netif_msg_drv(pdata)) + yt_dbg(pdata, + "%s, ch=%u,rx_desc_cnt=%u\n", + __func__, i, + pdata->rx_desc_count); + } + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s ok\n", __func__); + + return 0; + +err_init_ring: + fxgmac_rings_free(pdata); + return ret; +} + +static void fxgmac_channels_free(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel = pdata->channel_head; + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "free_channels,tx_ring=%p\n", channel->tx_ring); + + kfree(channel->tx_ring); + channel->tx_ring = NULL; + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "free_channels,rx_ring=%p\n", channel->rx_ring); + + kfree(channel->rx_ring); + channel->rx_ring = NULL; + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "free_channels,channel=%p\n", channel); + + kfree(channel); + pdata->channel_head = NULL; +} + +#ifdef CONFIG_PCI_MSI +static void fxgmac_set_msix_tx_irq(struct fxgmac_pdata *pdata, + struct fxgmac_channel *channel, u32 i) +{ + if (!FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) + return; + + pdata->channel_irq[FXGMAC_MAX_DMA_RX_CHANNELS] = + pdata->msix_entries[FXGMAC_MAX_DMA_RX_CHANNELS].vector; + channel->dma_irq_tx = pdata->channel_irq[FXGMAC_MAX_DMA_RX_CHANNELS]; + + yt_dbg(pdata, "%s, for MSIx, channel %d dma_irq_tx=%u\n", __func__, i, + channel->dma_irq_tx); +} +#endif + +static int fxgmac_channels_alloc(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel_head, *channel; + struct fxgmac_ring *tx_ring, *rx_ring; + int ret = -ENOMEM; + + channel_head = kcalloc(pdata->channel_count, + sizeof(struct fxgmac_channel), GFP_KERNEL); + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "alloc_channels,channel_head=%p,size=%d*%ld\n", + channel_head, pdata->channel_count, + sizeof(struct fxgmac_channel)); + + if (!channel_head) + return ret; + + tx_ring = kcalloc(FXGMAC_TX_1_RING, sizeof(struct fxgmac_ring), + GFP_KERNEL); + if (!tx_ring) + goto err_tx_ring; + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "alloc_channels,tx_ring=%p,size=%d*%ld\n", + tx_ring, FXGMAC_TX_1_RING, + sizeof(struct fxgmac_ring)); + + rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct fxgmac_ring), + GFP_KERNEL); + if (!rx_ring) + goto err_rx_ring; + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "alloc_channels,rx_ring=%p,size=%d*%ld\n", + rx_ring, pdata->rx_ring_count, + sizeof(struct fxgmac_ring)); + + channel = channel_head; + for (u32 i = 0; i < pdata->channel_count; i++, channel++) { + snprintf(channel->name, sizeof(channel->name), "channel-%u", i); + channel->pdata = pdata; + channel->queue_index = i; + channel->dma_reg_offset = DMA_CH_BASE + (DMA_CH_INC * i); + + if (pdata->per_channel_irq) { + pdata->channel_irq[i] = pdata->msix_entries[i].vector; +#ifdef CONFIG_PCI_MSI + fxgmac_set_msix_tx_irq(pdata, channel, i); +#endif + + /* Get the per DMA rx interrupt */ + ret = pdata->channel_irq[i]; + if (ret < 0) { + yt_err(pdata, "get_irq %u err\n", i + 1); + goto err_irq; + } + + channel->dma_irq_rx = ret; + yt_dbg(pdata, + "%s, for MSIx, channel %d dma_irq_rx=%u\n", + __func__, i, channel->dma_irq_rx); + } + + if (i < FXGMAC_TX_1_RING) + channel->tx_ring = tx_ring++; + + if (i < pdata->rx_ring_count) + channel->rx_ring = rx_ring++; + } + + pdata->channel_head = channel_head; + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s ok\n", __func__); + return 0; + +err_irq: + kfree(rx_ring); + +err_rx_ring: + kfree(tx_ring); + +err_tx_ring: + kfree(channel_head); + + yt_err(pdata, "%s err:%d\n", __func__, ret); + return ret; +} + +void fxgmac_channels_rings_free(struct fxgmac_pdata *pdata) +{ + fxgmac_rings_free(pdata); + fxgmac_channels_free(pdata); +} + +int fxgmac_channels_rings_alloc(struct fxgmac_pdata *pdata) +{ + int ret; + + ret = fxgmac_channels_alloc(pdata); + if (ret < 0) + goto err_alloc; + + ret = fxgmac_rings_alloc(pdata); + if (ret < 0) + goto err_alloc; + + return 0; + +err_alloc: + fxgmac_channels_rings_free(pdata); + return ret; +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index 0cb2808b7..39b91cc26 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -11,6 +11,26 @@ #include "yt6801_desc.h" #include "yt6801_net.h"
+static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu) +{ + u32 rx_buf_size, max_mtu; + + max_mtu = FXGMAC_JUMBO_PACKET_MTU - ETH_HLEN; + if (mtu > max_mtu) { + yt_err(pdata, "MTU exceeds maximum supported value\n"); + return -EINVAL; + } + + rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + rx_buf_size = + clamp_val(rx_buf_size, FXGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE * 4); + + rx_buf_size = (rx_buf_size + FXGMAC_RX_BUF_ALIGN - 1) & + ~(FXGMAC_RX_BUF_ALIGN - 1); + + return rx_buf_size; +} + #define FXGMAC_NAPI_ENABLE 0x1 #define FXGMAC_NAPI_DISABLE 0x0 static void fxgmac_napi_disable(struct fxgmac_pdata *pdata) @@ -197,6 +217,36 @@ void fxgmac_stop(struct fxgmac_pdata *pdata) netdev_tx_reset_queue(txq); }
+void fxgmac_restart(struct fxgmac_pdata *pdata) +{ + int ret; + + /* If not running, "restart" will happen on open */ + if (!netif_running(pdata->netdev) && + pdata->dev_state != FXGMAC_DEV_START) + return; + + mutex_lock(&pdata->mutex); + fxgmac_stop(pdata); + fxgmac_free_tx_data(pdata); + fxgmac_free_rx_data(pdata); + ret = fxgmac_start(pdata); + if (ret < 0) + yt_err(pdata, "%s err.\n", __func__); + + mutex_unlock(&pdata->mutex); +} + +static void fxgmac_restart_work(struct work_struct *work) +{ + struct fxgmac_pdata *pdata = + container_of(work, struct fxgmac_pdata, restart_work); + + rtnl_lock(); + fxgmac_restart(pdata); + rtnl_unlock(); +} + int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; @@ -250,6 +300,46 @@ int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en) return 0; }
+static int fxgmac_open(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret; + + mutex_lock(&pdata->mutex); + pdata->dev_state = FXGMAC_DEV_OPEN; + + /* Calculate the Rx buffer size before allocating rings */ + ret = fxgmac_calc_rx_buf_size(pdata, netdev->mtu); + if (ret < 0) + goto unlock; + pdata->rx_buf_size = ret; + + /* Allocate the channels and rings */ + ret = fxgmac_channels_rings_alloc(pdata); + if (ret < 0) + goto unlock; + + INIT_WORK(&pdata->restart_work, fxgmac_restart_work); + + ret = fxgmac_start(pdata); + if (ret < 0) + goto err_channels_and_rings; + + mutex_unlock(&pdata->mutex); + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s ok\n", __func__); + + return 0; + +err_channels_and_rings: + fxgmac_channels_rings_free(pdata); + yt_dbg(pdata, "%s, channel alloc err\n", __func__); +unlock: + mutex_unlock(&pdata->mutex); + return ret; +} + #ifdef CONFIG_PCI_MSI static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *pdata) { @@ -442,3 +532,12 @@ int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res) free_netdev(netdev); return ret; } + +static const struct net_device_ops fxgmac_netdev_ops = { + .ndo_open = fxgmac_open, +}; + +const struct net_device_ops *fxgmac_get_netdev_ops(void) +{ + return &fxgmac_netdev_ops; +}
driver inclusion category: feature
--------------------------------
Implement the fxgmac_start function to connect phy and init phy lib, enable napi, phy and msix irq.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../ethernet/motorcomm/yt6801/yt6801_net.c | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index 39b91cc26..eedf4dcb0 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -11,6 +11,8 @@ #include "yt6801_desc.h" #include "yt6801_net.h"
+static void fxgmac_napi_enable(struct fxgmac_pdata *pdata); + static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu) { u32 rx_buf_size, max_mtu; @@ -31,6 +33,26 @@ static int fxgmac_calc_rx_buf_size(struct fxgmac_pdata *pdata, unsigned int mtu) return rx_buf_size; }
+static void fxgmac_enable_rx_tx_ints(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel = pdata->channel_head; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + enum fxgmac_int int_id; + + for (u32 i = 0; i < pdata->channel_count; i++, channel++) { + if (channel->tx_ring && channel->rx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_RI; + else + continue; + + hw_ops->enable_channel_irq(channel, int_id); + } +} + #define FXGMAC_NAPI_ENABLE 0x1 #define FXGMAC_NAPI_DISABLE 0x0 static void fxgmac_napi_disable(struct fxgmac_pdata *pdata) @@ -180,6 +202,160 @@ void fxgmac_free_rx_data(struct fxgmac_pdata *pdata) } }
+static void fxgmac_phylink_handler(struct net_device *ndev) +{ + struct fxgmac_pdata *pdata = netdev_priv(ndev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + pdata->phy_link = pdata->phydev->link; + pdata->phy_speed = pdata->phydev->speed; + pdata->phy_duplex = pdata->phydev->duplex; + + yt_dbg(pdata, "EPHY_CTRL:%x, link:%d, speed:%d, duplex:%x.\n", + rd32_mem(pdata, EPHY_CTRL), pdata->phy_link, pdata->phy_speed, + pdata->phy_duplex); + + if (pdata->phy_link) { + hw_ops->config_mac_speed(pdata); + hw_ops->enable_rx(pdata); + hw_ops->enable_tx(pdata); + netif_carrier_on(pdata->netdev); + if (netif_running(pdata->netdev)) { + netif_tx_wake_all_queues(pdata->netdev); + yt_dbg(pdata, "%s now is link up, mac_speed=%d.\n", + netdev_name(pdata->netdev), pdata->phy_speed); + } + } else { + netif_carrier_off(pdata->netdev); + netif_tx_stop_all_queues(pdata->netdev); + hw_ops->disable_rx(pdata); + hw_ops->disable_tx(pdata); + yt_dbg(pdata, "%s now is link down\n", + netdev_name(pdata->netdev)); + } + + phy_print_status(pdata->phydev); +} + +static int fxgmac_phy_connect(struct fxgmac_pdata *pdata) +{ + struct phy_device *phydev = pdata->phydev; + int ret; + + ret = phy_connect_direct(pdata->netdev, phydev, fxgmac_phylink_handler, + PHY_INTERFACE_MODE_RGMII); + if (ret) + return ret; + + phy_attached_info(phydev); + return 0; +} + +static void fxgmac_enable_msix_irqs(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + for (u32 intid = 0; intid < MSIX_TBL_MAX_NUM; intid++) + hw_ops->enable_msix_one_irq(pdata, intid); +} + +int fxgmac_phy_irq_enable(struct fxgmac_pdata *pdata, bool clear_phy_interrupt) +{ + struct phy_device *phydev = pdata->phydev; + + if (clear_phy_interrupt && + phy_read(phydev, PHY_INT_STATUS) < 0) + return -ETIMEDOUT; + + return phy_modify(phydev, PHY_INT_MASK, + PHY_INT_MASK_LINK_UP | + PHY_INT_MASK_LINK_DOWN, + PHY_INT_MASK_LINK_UP | + PHY_INT_MASK_LINK_DOWN); +} + +int fxgmac_start(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 val; + int ret; + + if (pdata->dev_state != FXGMAC_DEV_OPEN && + pdata->dev_state != FXGMAC_DEV_STOP && + pdata->dev_state != FXGMAC_DEV_RESUME) { + yt_dbg(pdata, " dev_state err:%x\n", pdata->dev_state); + return 0; + } + + if (pdata->dev_state != FXGMAC_DEV_STOP) { + hw_ops->reset_phy(pdata); + hw_ops->release_phy(pdata); + yt_dbg(pdata, "reset phy.\n"); + } + + if (pdata->dev_state == FXGMAC_DEV_OPEN) { + ret = fxgmac_phy_connect(pdata); + if (ret < 0) + return ret; + + yt_dbg(pdata, "fxgmac_phy_connect.\n"); + } + + phy_init_hw(pdata->phydev); + phy_resume(pdata->phydev); + + hw_ops->pcie_init(pdata); + if (test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate)) { + yt_err(pdata, + "fxgmac powerstate is %lu when config power up.\n", + pdata->powerstate); + } + + hw_ops->config_power_up(pdata); + hw_ops->dismiss_all_int(pdata); + ret = hw_ops->init(pdata); + if (ret < 0) { + yt_err(pdata, "fxgmac hw init error.\n"); + return ret; + } + + fxgmac_napi_enable(pdata); + ret = fxgmac_request_irqs(pdata); + if (ret < 0) + return ret; + + /* Config interrupt to level signal */ + val = rd32_mac(pdata, DMA_MR); + fxgmac_set_bits(&val, DMA_MR_INTM_POS, DMA_MR_INTM_LEN, 2); + fxgmac_set_bits(&val, DMA_MR_QUREAD_POS, DMA_MR_QUREAD_LEN, 1); + wr32_mac(pdata, val, DMA_MR); + + hw_ops->enable_mgm_irq(pdata); + hw_ops->set_interrupt_moderation(pdata); + + if (pdata->per_channel_irq) { + fxgmac_enable_msix_irqs(pdata); + ret = fxgmac_phy_irq_enable(pdata, true); + if (ret < 0) + goto dis_napi; + } + + fxgmac_enable_rx_tx_ints(pdata); + phy_speed_up(pdata->phydev); + genphy_soft_reset(pdata->phydev); + + pdata->dev_state = FXGMAC_DEV_START; + phy_start(pdata->phydev); + + return 0; + +dis_napi: + fxgmac_napi_disable(pdata); + hw_ops->exit(pdata); + yt_err(pdata, "%s irq err.\n", __func__); + return ret; +} + static void fxgmac_disable_msix_irqs(struct fxgmac_pdata *pdata) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; @@ -540,4 +716,70 @@ static const struct net_device_ops fxgmac_netdev_ops = { const struct net_device_ops *fxgmac_get_netdev_ops(void) { return &fxgmac_netdev_ops; + +static void fxgmac_napi_enable(struct fxgmac_pdata *pdata) +{ + u32 i, *flags = &pdata->int_flags; + struct fxgmac_channel *channel; + u32 misc_napi, tx, rx; + + misc_napi = FIELD_GET(BIT(FXGMAC_FLAG_MISC_NAPI_POS), *flags); + tx = FXGMAC_GET_BITS(*flags, FXGMAC_FLAG_TX_NAPI_POS, + FXGMAC_FLAG_TX_NAPI_LEN); + rx = FXGMAC_GET_BITS(*flags, FXGMAC_FLAG_RX_NAPI_POS, + FXGMAC_FLAG_RX_NAPI_LEN); + + if (!pdata->per_channel_irq) { + i = FIELD_GET(BIT(FXGMAC_FLAG_LEGACY_NAPI_POS), *flags); + if (i) + return; + + netif_napi_add_weight(pdata->netdev, &pdata->napi, + fxgmac_all_poll, + NAPI_POLL_WEIGHT); + + napi_enable(&pdata->napi); + fxgmac_set_bits(flags, FXGMAC_FLAG_LEGACY_NAPI_POS, + FXGMAC_FLAG_LEGACY_NAPI_LEN, + FXGMAC_NAPI_ENABLE); + return; + } + + channel = pdata->channel_head; + + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!FXGMAC_GET_BITS(rx, i, FXGMAC_FLAG_PER_RX_NAPI_LEN)) { + netif_napi_add_weight(pdata->netdev, + &channel->napi_rx, + fxgmac_one_poll_rx, + NAPI_POLL_WEIGHT); + + napi_enable(&channel->napi_rx); + fxgmac_set_bits(flags, FXGMAC_FLAG_RX_NAPI_POS + i, + FXGMAC_FLAG_PER_RX_NAPI_LEN, + FXGMAC_NAPI_ENABLE); + } + + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && !tx) { + netif_napi_add_weight(pdata->netdev, &channel->napi_tx, + fxgmac_one_poll_tx, + NAPI_POLL_WEIGHT); + napi_enable(&channel->napi_tx); + fxgmac_set_bits(flags, FXGMAC_FLAG_TX_NAPI_POS, + FXGMAC_FLAG_TX_NAPI_LEN, + FXGMAC_NAPI_ENABLE); + } + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "msix ch%d napi enabled done.\n", i); + } + + /* Misc */ + if (!misc_napi) { + netif_napi_add_weight(pdata->netdev, &pdata->napi_misc, + fxgmac_misc_poll, NAPI_POLL_WEIGHT); + + napi_enable(&pdata->napi_misc); + fxgmac_set_bits(flags, FXGMAC_FLAG_MISC_NAPI_POS, + FXGMAC_FLAG_MISC_NAPI_LEN, FXGMAC_NAPI_ENABLE); + } }
driver inclusion category: feature
--------------------------------
Implement the fxgmac_read_mac_addr function to read mac address form efuse.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../ethernet/motorcomm/yt6801/yt6801_net.c | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index aa51ecdd7..c3baa06b0 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -796,6 +796,153 @@ static int fxgmac_open(struct net_device *netdev) return ret; }
+#define EFUSE_FISRT_UPDATE_ADDR 255 +#define EFUSE_SECOND_UPDATE_ADDR 209 +#define EFUSE_MAX_ENTRY 39 +#define EFUSE_PATCH_ADDR_START 0 +#define EFUSE_PATCH_DATA_START 2 +#define EFUSE_PATCH_SIZE 6 +#define EFUSE_REGION_A_B_LENGTH 18 + +static bool fxgmac_efuse_read_data(struct fxgmac_pdata *pdata, u32 offset, + u8 *value) +{ + bool ret = false; + u32 wait = 1000; + u32 val = 0; + + fxgmac_set_bits(&val, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, offset); + fxgmac_set_bits(&val, EFUSE_OP_START_POS, EFUSE_OP_START_LEN, 1); + fxgmac_set_bits(&val, EFUSE_OP_MODE_POS, EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_READ); + wr32_mem(pdata, val, EFUSE_OP_CTRL_0); + + while (wait--) { + fsleep(20); + val = rd32_mem(pdata, EFUSE_OP_CTRL_1); + if (FXGMAC_GET_BITS(val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + ret = true; + break; + } + } + + if (!ret) { + yt_err(pdata, "Fail to reading efuse Byte%d\n", offset); + return ret; + } + + if (value) + *value = FXGMAC_GET_BITS(val, EFUSE_OP_RD_DATA_POS, + EFUSE_OP_RD_DATA_LEN) & 0xff; + + return ret; +} + +static bool fxgmac_efuse_read_index_patch(struct fxgmac_pdata *pdata, u8 index, + u32 *offset, u32 *value) +{ + u8 tmp[EFUSE_PATCH_SIZE - EFUSE_PATCH_DATA_START]; + u32 addr, i; + bool ret; + + if (index >= EFUSE_MAX_ENTRY) { + yt_err(pdata, "Reading efuse out of range, index %d\n", index); + return false; + } + + for (i = EFUSE_PATCH_ADDR_START; i < EFUSE_PATCH_DATA_START; i++) { + addr = EFUSE_REGION_A_B_LENGTH + index * EFUSE_PATCH_SIZE + i; + ret = fxgmac_efuse_read_data(pdata, addr, + tmp + i - EFUSE_PATCH_ADDR_START); + if (!ret) { + yt_err(pdata, "Fail to reading efuse Byte%d\n", addr); + return ret; + } + } + if (offset) { + /* tmp[0] is low 8bit date, tmp[1] is high 8bit date */ + *offset = tmp[0] | (tmp[1] << 8); + } + + for (i = EFUSE_PATCH_DATA_START; i < EFUSE_PATCH_SIZE; i++) { + addr = EFUSE_REGION_A_B_LENGTH + index * EFUSE_PATCH_SIZE + i; + ret = fxgmac_efuse_read_data(pdata, addr, + tmp + i - EFUSE_PATCH_DATA_START); + if (!ret) { + yt_err(pdata, "Fail to reading efuse Byte%d\n", addr); + return ret; + } + } + if (value) { + /* tmp[0] is low 8bit date, tmp[1] is low 8bit date + * ... tmp[3] is highest 8bit date + */ + *value = tmp[0] | (tmp[1] << 8) | (tmp[2] << 16) | + (tmp[3] << 24); + } + + return ret; +} + +static bool fxgmac_efuse_read_mac_subsys(struct fxgmac_pdata *pdata, + u8 *mac_addr, u32 *subsys, u32 *revid) +{ + u32 machr = 0, maclr = 0; + u32 offset = 0, val = 0; + bool ret = true; + u8 index; + + for (index = 0;; index++) { + if (!fxgmac_efuse_read_index_patch(pdata, index, &offset, &val)) + return false; + + if (offset == 0x00) + break; /* Reach the blank. */ + + if (offset == MACA0LR_FROM_EFUSE) + maclr = val; + + if (offset == MACA0HR_FROM_EFUSE) + machr = val; + + if (offset == PCI_REVISION_ID && revid) + *revid = val; + + if (offset == PCI_SUBSYSTEM_VENDOR_ID && subsys) + *subsys = val; + } + + if (mac_addr) { + mac_addr[5] = (u8)(maclr & 0xFF); + mac_addr[4] = (u8)((maclr >> 8) & 0xFF); + mac_addr[3] = (u8)((maclr >> 16) & 0xFF); + mac_addr[2] = (u8)((maclr >> 24) & 0xFF); + mac_addr[1] = (u8)(machr & 0xFF); + mac_addr[0] = (u8)((machr >> 8) & 0xFF); + } + + return ret; +} + +static int fxgmac_read_mac_addr(struct fxgmac_pdata *pdata) +{ + u8 default_addr[ETH_ALEN] = { 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7 }; + struct net_device *netdev = pdata->netdev; + int ret; + + /* If efuse have mac addr, use it. if not, use static mac address. */ + ret = fxgmac_efuse_read_mac_subsys(pdata, pdata->mac_addr, NULL, NULL); + if (!ret) + return -1; + + if (is_zero_ether_addr(pdata->mac_addr)) + /* Use a static mac address for test */ + memcpy(pdata->mac_addr, default_addr, netdev->addr_len); + + return 0; +} + #define FXGMAC_SYSCLOCK 125000000 /* System clock is 125 MHz */
static void fxgmac_default_config(struct fxgmac_pdata *pdata)
driver inclusion category: feature
--------------------------------
Implement following callback function .ndo_tx_timeout .ndo_change_mtu The .ndo_tx_timeout function also will call fxgmac_dump_state to dump nessary debug information.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 69 ++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 160 ++++++++++++++++++ 2 files changed, 229 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c index 791dd69b7..a70fa4ede 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c @@ -1204,6 +1204,71 @@ static void fxgmac_rx_mmc_int(struct fxgmac_pdata *yt) stats->rxcontrolframe_g += rd32_mac(yt, MMC_RXCONTROLFRAME_G); }
+static void fxgmac_read_mmc_stats(struct fxgmac_pdata *yt) +{ + struct fxgmac_stats *stats = &yt->stats; + + stats->txoctetcount_gb += rd32_mac(yt, MMC_TXOCTETCOUNT_GB_LO); + stats->txframecount_gb += rd32_mac(yt, MMC_TXFRAMECOUNT_GB_LO); + stats->txbroadcastframes_g += rd32_mac(yt, MMC_TXBROADCASTFRAMES_G_LO); + stats->txmulticastframes_g += rd32_mac(yt, MMC_TXMULTICASTFRAMES_G_LO); + stats->tx64octets_gb += rd32_mac(yt, MMC_TX64OCTETS_GB_LO); + stats->tx65to127octets_gb += rd32_mac(yt, MMC_TX65TO127OCTETS_GB_LO); + stats->tx128to255octets_gb += rd32_mac(yt, MMC_TX128TO255OCTETS_GB_LO); + stats->tx256to511octets_gb += rd32_mac(yt, MMC_TX256TO511OCTETS_GB_LO); + stats->tx512to1023octets_gb += + rd32_mac(yt, MMC_TX512TO1023OCTETS_GB_LO); + stats->tx1024tomaxoctets_gb += + rd32_mac(yt, MMC_TX1024TOMAXOCTETS_GB_LO); + stats->txunicastframes_gb += rd32_mac(yt, MMC_TXUNICASTFRAMES_GB_LO); + stats->txmulticastframes_gb += + rd32_mac(yt, MMC_TXMULTICASTFRAMES_GB_LO); + stats->txbroadcastframes_g += rd32_mac(yt, MMC_TXBROADCASTFRAMES_GB_LO); + stats->txunderflowerror += rd32_mac(yt, MMC_TXUNDERFLOWERROR_LO); + stats->txsinglecollision_g += rd32_mac(yt, MMC_TXSINGLECOLLISION_G); + stats->txmultiplecollision_g += rd32_mac(yt, MMC_TXMULTIPLECOLLISION_G); + stats->txdeferredframes += rd32_mac(yt, MMC_TXDEFERREDFRAMES); + stats->txlatecollisionframes += rd32_mac(yt, MMC_TXLATECOLLISIONFRAMES); + stats->txexcessivecollisionframes += + rd32_mac(yt, MMC_TXEXCESSIVECOLLSIONFRAMES); + stats->txcarriererrorframes += rd32_mac(yt, MMC_TXCARRIERERRORFRAMES); + stats->txoctetcount_g += rd32_mac(yt, MMC_TXOCTETCOUNT_G_LO); + stats->txframecount_g += rd32_mac(yt, MMC_TXFRAMECOUNT_G_LO); + stats->txexcessivedeferralerror += + rd32_mac(yt, MMC_TXEXCESSIVEDEFERRALERROR); + stats->txpauseframes += rd32_mac(yt, MMC_TXPAUSEFRAMES_LO); + stats->txvlanframes_g += rd32_mac(yt, MMC_TXVLANFRAMES_G_LO); + stats->txoversize_g += rd32_mac(yt, MMC_TXOVERSIZEFRAMES); + stats->rxframecount_gb += rd32_mac(yt, MMC_RXFRAMECOUNT_GB_LO); + stats->rxoctetcount_gb += rd32_mac(yt, MMC_RXOCTETCOUNT_GB_LO); + stats->rxoctetcount_g += rd32_mac(yt, MMC_RXOCTETCOUNT_G_LO); + stats->rxbroadcastframes_g += rd32_mac(yt, MMC_RXBROADCASTFRAMES_G_LO); + stats->rxmulticastframes_g += rd32_mac(yt, MMC_RXMULTICASTFRAMES_G_LO); + stats->rxcrcerror += rd32_mac(yt, MMC_RXCRCERROR_LO); + stats->rxalignerror += rd32_mac(yt, MMC_RXALIGNERROR); + stats->rxrunterror += rd32_mac(yt, MMC_RXRUNTERROR); + stats->rxjabbererror += rd32_mac(yt, MMC_RXJABBERERROR); + stats->rxundersize_g += rd32_mac(yt, MMC_RXUNDERSIZE_G); + stats->rxoversize_g += rd32_mac(yt, MMC_RXOVERSIZE_G); + stats->rx64octets_gb += rd32_mac(yt, MMC_RX64OCTETS_GB_LO); + stats->rx65to127octets_gb += rd32_mac(yt, MMC_RX65TO127OCTETS_GB_LO); + stats->rx128to255octets_gb += rd32_mac(yt, MMC_RX128TO255OCTETS_GB_LO); + stats->rx256to511octets_gb += rd32_mac(yt, MMC_RX256TO511OCTETS_GB_LO); + stats->rx512to1023octets_gb += + rd32_mac(yt, MMC_RX512TO1023OCTETS_GB_LO); + stats->rx1024tomaxoctets_gb += + rd32_mac(yt, MMC_RX1024TOMAXOCTETS_GB_LO); + stats->rxunicastframes_g += rd32_mac(yt, MMC_RXUNICASTFRAMES_G_LO); + stats->rxlengtherror += rd32_mac(yt, MMC_RXLENGTHERROR_LO); + stats->rxoutofrangetype += rd32_mac(yt, MMC_RXOUTOFRANGETYPE_LO); + stats->rxpauseframes += rd32_mac(yt, MMC_RXPAUSEFRAMES_LO); + stats->rxfifooverflow += rd32_mac(yt, MMC_RXFIFOOVERFLOW_LO); + stats->rxvlanframes_gb += rd32_mac(yt, MMC_RXVLANFRAMES_GB_LO); + stats->rxwatchdogerror += rd32_mac(yt, MMC_RXWATCHDOGERROR); + stats->rxreceiveerrorframe += rd32_mac(yt, MMC_RXRECEIVEERRORFRAME); + stats->rxcontrolframe_g += rd32_mac(yt, MMC_RXCONTROLFRAME_G); +} + static void fxgmac_config_mmc(struct fxgmac_pdata *pdata) { u32 val; @@ -2610,6 +2675,10 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops) /* RX coalescing */ hw_ops->config_rx_coalesce = fxgmac_config_rx_coalesce; hw_ops->usec_to_riwt = fxgmac_usec_to_riwt; + + /* MMC statistics support */ + hw_ops->read_mmc_stats = fxgmac_read_mmc_stats; + /* Receive Side Scaling */ hw_ops->enable_rss = fxgmac_enable_rss; hw_ops->disable_rss = fxgmac_disable_rss; diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index ed65c9cc9..c5c13601e 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -1015,6 +1015,129 @@ static int fxgmac_close(struct net_device *netdev) return 0; }
+static void fxgmac_dump_state(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel = pdata->channel_head; + struct fxgmac_stats *pstats = &pdata->stats; + struct fxgmac_ring *ring; + + ring = &channel->tx_ring[0]; + yt_err(pdata, "Tx descriptor info:\n"); + yt_err(pdata, "Tx cur = 0x%x\n", ring->cur); + yt_err(pdata, "Tx dirty = 0x%x\n", ring->dirty); + yt_err(pdata, "Tx dma_desc_head = %pad\n", &ring->dma_desc_head); + yt_err(pdata, "Tx desc_data_head = %pad\n", &ring->desc_data_head); + + for (u32 i = 0; i < pdata->channel_count; i++, channel++) { + ring = &channel->rx_ring[0]; + yt_err(pdata, "Rx[%d] descriptor info:\n", i); + yt_err(pdata, "Rx cur = 0x%x\n", ring->cur); + yt_err(pdata, "Rx dirty = 0x%x\n", ring->dirty); + yt_err(pdata, "Rx dma_desc_head = %pad\n", + &ring->dma_desc_head); + yt_err(pdata, "Rx desc_data_head = %pad\n", + &ring->desc_data_head); + } + + yt_err(pdata, "Device Registers:\n"); + yt_err(pdata, "MAC_ISR = %08x\n", rd32_mac(pdata, MAC_ISR)); + yt_err(pdata, "MAC_IER = %08x\n", rd32_mac(pdata, MAC_IER)); + yt_err(pdata, "MMC_RISR = %08x\n", rd32_mac(pdata, MMC_RISR)); + yt_err(pdata, "MMC_RIER = %08x\n", rd32_mac(pdata, MMC_RIER)); + yt_err(pdata, "MMC_TISR = %08x\n", rd32_mac(pdata, MMC_TISR)); + yt_err(pdata, "MMC_TIER = %08x\n", rd32_mac(pdata, MMC_TIER)); + + yt_err(pdata, "EPHY_CTRL = %04x\n", rd32_mem(pdata, EPHY_CTRL)); + yt_err(pdata, "MGMT_INT_CTRL0 = %04x\n", + rd32_mem(pdata, MGMT_INT_CTRL0)); + yt_err(pdata, "LPW_CTRL = %04x\n", rd32_mem(pdata, LPW_CTRL)); + yt_err(pdata, "MSIX_TBL_MASK = %04x\n", rd32_mem(pdata, MSIX_TBL_MASK)); + + yt_err(pdata, "Dump nonstick regs:\n"); + for (u32 i = GLOBAL_CTRL0; i < MSI_PBA; i += 4) + yt_err(pdata, "[%d] = %04x\n", i / 4, rd32_mem(pdata, i)); + + pdata->hw_ops.read_mmc_stats(pdata); + + yt_err(pdata, "Dump TX counters:\n"); + yt_err(pdata, "tx_packets %lld\n", pstats->txframecount_gb); + yt_err(pdata, "tx_errors %lld\n", + pstats->txframecount_gb - pstats->txframecount_g); + yt_err(pdata, "tx_multicastframes_errors %lld\n", + pstats->txmulticastframes_gb - pstats->txmulticastframes_g); + yt_err(pdata, "tx_broadcastframes_errors %lld\n", + pstats->txbroadcastframes_gb - pstats->txbroadcastframes_g); + + yt_err(pdata, "txunderflowerror %lld\n", pstats->txunderflowerror); + yt_err(pdata, "txdeferredframes %lld\n", + pstats->txdeferredframes); + yt_err(pdata, "txlatecollisionframes %lld\n", + pstats->txlatecollisionframes); + yt_err(pdata, "txexcessivecollisionframes %lld\n", + pstats->txexcessivecollisionframes); + yt_err(pdata, "txcarriererrorframes %lld\n", + pstats->txcarriererrorframes); + yt_err(pdata, "txexcessivedeferralerror %lld\n", + pstats->txexcessivedeferralerror); + + yt_err(pdata, "txsinglecollision_g %lld\n", + pstats->txsinglecollision_g); + yt_err(pdata, "txmultiplecollision_g %lld\n", + pstats->txmultiplecollision_g); + yt_err(pdata, "txoversize_g %lld\n", pstats->txoversize_g); + + yt_err(pdata, "Dump RX counters:\n"); + yt_err(pdata, "rx_packets %lld\n", pstats->rxframecount_gb); + yt_err(pdata, "rx_errors %lld\n", + pstats->rxframecount_gb - pstats->rxbroadcastframes_g - + pstats->rxmulticastframes_g - pstats->rxunicastframes_g); + + yt_err(pdata, "rx_crc_errors %lld\n", pstats->rxcrcerror); + yt_err(pdata, "rxalignerror %lld\n", pstats->rxalignerror); + yt_err(pdata, "rxrunterror %lld\n", pstats->rxrunterror); + yt_err(pdata, "rxjabbererror %lld\n", pstats->rxjabbererror); + yt_err(pdata, "rx_length_errors %lld\n", pstats->rxlengtherror); + yt_err(pdata, "rxoutofrangetype %lld\n", pstats->rxoutofrangetype); + yt_err(pdata, "rx_fifo_errors %lld\n", pstats->rxfifooverflow); + yt_err(pdata, "rxwatchdogerror %lld\n", pstats->rxwatchdogerror); + yt_err(pdata, "rxreceiveerrorframe %lld\n", + pstats->rxreceiveerrorframe); + + yt_err(pdata, "rxbroadcastframes_g %lld\n", + pstats->rxbroadcastframes_g); + yt_err(pdata, "rxmulticastframes_g %lld\n", + pstats->rxmulticastframes_g); + yt_err(pdata, "rxundersize_g %lld\n", pstats->rxundersize_g); + yt_err(pdata, "rxoversize_g %lld\n", pstats->rxoversize_g); + yt_err(pdata, "rxunicastframes_g %lld\n", pstats->rxunicastframes_g); + yt_err(pdata, "rxcontrolframe_g %lld\n", pstats->rxcontrolframe_g); + + yt_err(pdata, "Dump Extra counters:\n"); + yt_err(pdata, "tx_tso_packets %lld\n", pstats->tx_tso_packets); + yt_err(pdata, "rx_split_header_packets %lld\n", + pstats->rx_split_header_packets); + yt_err(pdata, "tx_process_stopped %lld\n", pstats->tx_process_stopped); + yt_err(pdata, "rx_process_stopped %lld\n", pstats->rx_process_stopped); + yt_err(pdata, "tx_buffer_unavailable %lld\n", + pstats->tx_buffer_unavailable); + yt_err(pdata, "rx_buffer_unavailable %lld\n", + pstats->rx_buffer_unavailable); + yt_err(pdata, "fatal_bus_error %lld\n", pstats->fatal_bus_error); + yt_err(pdata, "napi_poll_isr %lld\n", pstats->napi_poll_isr); + yt_err(pdata, "napi_poll_txtimer %lld\n", pstats->napi_poll_txtimer); + yt_err(pdata, "ephy_poll_timer_cnt %lld\n", + pstats->ephy_poll_timer_cnt); + yt_err(pdata, "mgmt_int_isr %lld\n", pstats->mgmt_int_isr); +} + +static void fxgmac_tx_timeout(struct net_device *netdev, unsigned int unused) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + fxgmac_dump_state(pdata); + schedule_work(&pdata->restart_work); +} + #define EFUSE_FISRT_UPDATE_ADDR 255 #define EFUSE_SECOND_UPDATE_ADDR 209 #define EFUSE_MAX_ENTRY 39 @@ -2046,6 +2169,41 @@ static int fxgmac_set_mac_address(struct net_device *netdev, void *addr) return 0; }
+static int fxgmac_change_mtu(struct net_device *netdev, int mtu) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int ret; + + mutex_lock(&pdata->mutex); + fxgmac_stop(pdata); + fxgmac_free_tx_data(pdata); + + /* We must unmap rx desc's dma before we change rx_buf_size. + * Becaues the size of the unmapped DMA is set according to rx_buf_size + */ + fxgmac_free_rx_data(pdata); + pdata->jumbo = mtu > ETH_DATA_LEN ? 1 : 0; + ret = fxgmac_calc_rx_buf_size(pdata, mtu); + if (ret < 0) + return ret; + + pdata->rx_buf_size = ret; + netdev->mtu = mtu; + + if (netif_running(netdev)) + fxgmac_start(pdata); + + netdev_update_features(netdev); + + mutex_unlock(&pdata->mutex); + + yt_dbg(pdata, "fxgmac,set MTU from %d to %d. min, max=(%d,%d)\n", + old_mtu, netdev->mtu, netdev->min_mtu, netdev->max_mtu); + + return 0; +} + static int fxgmac_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) { @@ -2169,7 +2327,9 @@ static const struct net_device_ops fxgmac_netdev_ops = { .ndo_open = fxgmac_open, .ndo_stop = fxgmac_close, .ndo_start_xmit = fxgmac_xmit, + .ndo_tx_timeout = fxgmac_tx_timeout, .ndo_get_stats64 = fxgmac_get_stats64, + .ndo_change_mtu = fxgmac_change_mtu, .ndo_set_mac_address = fxgmac_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = fxgmac_vlan_rx_add_vid,
driver inclusion category: feature
--------------------------------
Implement following callback function .get_drvinfo .get_link .get_msglevel .set_msglevel .get_channels .get_coalesce .set_coalesce .supported_coalesce_params .get_strings .get_sset_count .get_ethtool_stats .get_regs_len .get_regs .get_ringparam .set_ringparam .get_rxfh_indir_size .get_rxfh_key_size .get_rxfh .set_rxfh .nway_reset .reset .get_link_ksettings .set_link_ksettings .get_pauseparam .set_pauseparam
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../motorcomm/yt6801/yt6801_ethtool.c | 669 ++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c new file mode 100644 index 000000000..12150584f --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2022 - 2024 Motorcomm Electronic Technology Co.,Ltd. */ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/kernel.h> + +#include "yt6801_desc.h" +#include "yt6801_net.h" + +struct fxgmac_stats_desc { + char stat_string[ETH_GSTRING_LEN]; + int stat_offset; +}; + +#define FXGMAC_STAT(str, var) \ + { \ + str, offsetof(struct fxgmac_pdata, stats.var), \ + } + +static const struct fxgmac_stats_desc fxgmac_gstring_stats[] = { + /* MMC TX counters */ + FXGMAC_STAT("tx_bytes", txoctetcount_gb), + FXGMAC_STAT("tx_bytes_good", txoctetcount_g), + FXGMAC_STAT("tx_packets", txframecount_gb), + FXGMAC_STAT("tx_packets_good", txframecount_g), + FXGMAC_STAT("tx_unicast_packets", txunicastframes_gb), + FXGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb), + FXGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g), + FXGMAC_STAT("tx_multicast_packets", txmulticastframes_gb), + FXGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g), + FXGMAC_STAT("tx_vlan_packets_good", txvlanframes_g), + FXGMAC_STAT("tx_64_byte_packets", tx64octets_gb), + FXGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), + FXGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb), + FXGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb), + FXGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb), + FXGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb), + FXGMAC_STAT("tx_underflow_errors", txunderflowerror), + FXGMAC_STAT("tx_pause_frames", txpauseframes), + FXGMAC_STAT("tx_single_collision", txsinglecollision_g), + FXGMAC_STAT("tx_multiple_collision", txmultiplecollision_g), + FXGMAC_STAT("tx_deferred_frames", txdeferredframes), + FXGMAC_STAT("tx_late_collision_frames", txlatecollisionframes), + FXGMAC_STAT("tx_excessive_collision_frames", + txexcessivecollisionframes), + FXGMAC_STAT("tx_carrier_error_frames", txcarriererrorframes), + FXGMAC_STAT("tx_excessive_deferral_error", txexcessivedeferralerror), + FXGMAC_STAT("tx_oversize_frames_good", txoversize_g), + + /* MMC RX counters */ + FXGMAC_STAT("rx_bytes", rxoctetcount_gb), + FXGMAC_STAT("rx_bytes_good", rxoctetcount_g), + FXGMAC_STAT("rx_packets", rxframecount_gb), + FXGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g), + FXGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g), + FXGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g), + FXGMAC_STAT("rx_vlan_packets_mac", rxvlanframes_gb), + FXGMAC_STAT("rx_64_byte_packets", rx64octets_gb), + FXGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), + FXGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), + FXGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb), + FXGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb), + FXGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb), + FXGMAC_STAT("rx_undersize_packets_good", rxundersize_g), + FXGMAC_STAT("rx_oversize_packets_good", rxoversize_g), + FXGMAC_STAT("rx_crc_errors", rxcrcerror), + FXGMAC_STAT("rx_align_error", rxalignerror), + FXGMAC_STAT("rx_crc_errors_small_packets", rxrunterror), + FXGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror), + FXGMAC_STAT("rx_length_errors", rxlengtherror), + FXGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype), + FXGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow), + FXGMAC_STAT("rx_watchdog_errors", rxwatchdogerror), + FXGMAC_STAT("rx_pause_frames", rxpauseframes), + FXGMAC_STAT("rx_receive_error_frames", rxreceiveerrorframe), + FXGMAC_STAT("rx_control_frames_good", rxcontrolframe_g), + + /* Extra counters */ + FXGMAC_STAT("tx_tso_packets", tx_tso_packets), + FXGMAC_STAT("rx_split_header_packets", rx_split_header_packets), + FXGMAC_STAT("tx_process_stopped", tx_process_stopped), + FXGMAC_STAT("rx_process_stopped", rx_process_stopped), + FXGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable), + FXGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable), + FXGMAC_STAT("fatal_bus_error", fatal_bus_error), + FXGMAC_STAT("tx_vlan_packets_net", tx_vlan_packets), + FXGMAC_STAT("rx_vlan_packets_net", rx_vlan_packets), + FXGMAC_STAT("napi_poll_isr", napi_poll_isr), + FXGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer), + FXGMAC_STAT("alive_cnt_txtimer", cnt_alive_txtimer), + FXGMAC_STAT("ephy_poll_timer", ephy_poll_timer_cnt), + FXGMAC_STAT("mgmt_int_isr", mgmt_int_isr), +}; + +#define FXGMAC_STATS_COUNT ARRAY_SIZE(fxgmac_gstring_stats) + +static void fxgmac_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u32 ver = pdata->hw_feat.version; + u32 sver, devid, userver; + + strscpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); + + /* S|SVER: MAC Version + * D|DEVID: Indicates the Device family + * U|USERVER: User-defined Version + */ + sver = FXGMAC_GET_BITS(ver, MAC_VR_SVER_POS, MAC_VR_SVER_LEN); + devid = FXGMAC_GET_BITS(ver, MAC_VR_DEVID_POS, MAC_VR_DEVID_LEN); + userver = FXGMAC_GET_BITS(ver, MAC_VR_USERVER_POS, MAC_VR_USERVER_LEN); + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "S.D.U: %x.%x.%x", sver, devid, userver); +} + +static u32 fxgmac_ethtool_get_msglevel(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void fxgmac_ethtool_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + yt_dbg(pdata, "set msglvl from %08x to %08x\n", pdata->msg_enable, + msglevel); + pdata->msg_enable = msglevel; +} + +static void fxgmac_ethtool_get_channels(struct net_device *netdev, + struct ethtool_channels *channel) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + /* report maximum channels */ + channel->max_rx = FXGMAC_MAX_DMA_RX_CHANNELS; + channel->max_tx = FXGMAC_MAX_DMA_TX_CHANNELS; + channel->max_other = 0; + channel->max_combined = + channel->max_rx + channel->max_tx + channel->max_other; + + yt_dbg(pdata, "channels max rx:%d, tx:%d, other:%d, combined:%d\n", + channel->max_rx, channel->max_tx, channel->max_other, + channel->max_combined); + + channel->rx_count = pdata->rx_q_count; + channel->tx_count = FXGMAC_TX_1_Q; + channel->other_count = 0; + /* record RSS queues */ + channel->combined_count = + channel->rx_count + channel->tx_count + channel->other_count; + + yt_dbg(pdata, "channels count rx:%d, tx:%d, other:%d, combined:%d\n", + channel->rx_count, channel->tx_count, channel->other_count, + channel->combined_count); +} + +static int +fxgmac_ethtool_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) + +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + memset(ec, 0, sizeof(struct ethtool_coalesce)); + ec->rx_coalesce_usecs = pdata->rx_usecs; + ec->tx_coalesce_usecs = pdata->tx_usecs; + + return 0; +} + +#define FXGMAC_MAX_DMA_RIWT 0xff +#define FXGMAC_MIN_DMA_RIWT 0x01 + +static int +fxgmac_ethtool_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int rx_frames, rx_riwt, rx_usecs; + unsigned int tx_frames; + + /* Check for not supported parameters */ + if (ec->rx_coalesce_usecs_irq || ec->rx_max_coalesced_frames_irq || + ec->tx_coalesce_usecs_high || ec->tx_max_coalesced_frames_irq || + ec->tx_coalesce_usecs_irq || ec->stats_block_coalesce_usecs || + ec->pkt_rate_low || ec->use_adaptive_rx_coalesce || + ec->use_adaptive_tx_coalesce || ec->rx_max_coalesced_frames_low || + ec->rx_coalesce_usecs_low || ec->tx_coalesce_usecs_low || + ec->tx_max_coalesced_frames_low || ec->pkt_rate_high || + ec->rx_coalesce_usecs_high || ec->rx_max_coalesced_frames_high || + ec->tx_max_coalesced_frames_high || ec->rate_sample_interval) + return -EOPNOTSUPP; + + rx_usecs = ec->rx_coalesce_usecs; + rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs); + rx_frames = ec->rx_max_coalesced_frames; + tx_frames = ec->tx_max_coalesced_frames; + + if (rx_riwt > FXGMAC_MAX_DMA_RIWT || rx_riwt < FXGMAC_MIN_DMA_RIWT || + rx_frames > pdata->rx_desc_count) + return -EINVAL; + + if (tx_frames > pdata->tx_desc_count) + return -EINVAL; + + pdata->rx_usecs = rx_usecs; + pdata->rx_frames = rx_frames; + pdata->rx_riwt = rx_riwt; + hw_ops->config_rx_coalesce(pdata); + + pdata->tx_frames = tx_frames; + pdata->tx_usecs = ec->tx_coalesce_usecs; + hw_ops->set_interrupt_moderation(pdata); + + return 0; +} + +static u32 fxgmac_get_rxfh_key_size(struct net_device *netdev) +{ + return FXGMAC_RSS_HASH_KEY_SIZE; +} + +static u32 fxgmac_rss_indir_size(struct net_device *netdev) +{ + return FXGMAC_RSS_MAX_TABLE_SIZE; +} + +static int fxgmac_get_rss_hash_opts(struct fxgmac_pdata *pdata, + struct ethtool_rxnfc *cmd) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 reg_opt; + + cmd->data = 0; + reg_opt = hw_ops->get_rss_options(pdata); + yt_dbg(pdata, "%s, hw=%02x, %02x\n", __func__, reg_opt, + pdata->rss_options); + + if (reg_opt != pdata->rss_options) + yt_dbg(pdata, "warning, options are not consistent\n"); + + /* Report default options for RSS */ + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + if ((cmd->flow_type == TCP_V4_FLOW && + (pdata->rss_options & FXGMAC_RSS_TCP4TE)) || + (cmd->flow_type == UDP_V4_FLOW && + (pdata->rss_options & FXGMAC_RSS_UDP4TE))) { + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + } + fallthrough; + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + if ((cmd->flow_type == TCP_V4_FLOW && + (pdata->rss_options & FXGMAC_RSS_TCP4TE)) || + (cmd->flow_type == UDP_V4_FLOW && + (pdata->rss_options & FXGMAC_RSS_UDP4TE)) || + (pdata->rss_options & BIT(FXGMAC_RSS_IP4TE))) { + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + } + break; + case TCP_V6_FLOW: + case UDP_V6_FLOW: + if ((cmd->flow_type == TCP_V6_FLOW && + (pdata->rss_options & FXGMAC_RSS_TCP6TE)) || + (cmd->flow_type == UDP_V6_FLOW && + (pdata->rss_options & FXGMAC_RSS_UDP6TE))) { + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + } + fallthrough; + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + if ((cmd->flow_type == TCP_V6_FLOW && + (pdata->rss_options & FXGMAC_RSS_TCP6TE)) || + (cmd->flow_type == UDP_V6_FLOW && + (pdata->rss_options & FXGMAC_RSS_UDP6TE)) || + (pdata->rss_options & FXGMAC_RSS_IP6TE)) { + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fxgmac_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct fxgmac_pdata *pdata = netdev_priv(dev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = pdata->rx_q_count; + yt_dbg(pdata, "%s, rx ring cnt\n", __func__); + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = 0; + yt_dbg(pdata, "%s, classify rule cnt\n", __func__); + break; + case ETHTOOL_GRXCLSRULE: + yt_dbg(pdata, "%s, classify rules\n", __func__); + break; + case ETHTOOL_GRXCLSRLALL: + cmd->rule_cnt = 0; + yt_dbg(pdata, "%s, classify both cnt and rules\n", __func__); + break; + case ETHTOOL_GRXFH: + ret = fxgmac_get_rss_hash_opts(pdata, cmd); + yt_dbg(pdata, "%s, hash options\n", __func__); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +#define UDP_RSS_FLAGS (FXGMAC_RSS_UDP4TE | FXGMAC_RSS_UDP6TE) + +static int fxgmac_set_rss_hash_opt(struct fxgmac_pdata *pdata, + struct ethtool_rxnfc *nfc) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + __u64 data = nfc->data; + u32 rssopt = 0; + + yt_dbg(pdata, "%s nfc_data=%llx,cur opt=%x\n", __func__, data, + pdata->rss_options); + + /* For RSS, it does not support anything other than hashing + * to queues on src,dst IPs and L4 ports + */ + if (data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + /* default to TCP flow and do nothting */ + if (!(data & + (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3))) + return -EINVAL; + if (nfc->flow_type == TCP_V4_FLOW) + rssopt |= BIT(FXGMAC_RSS_IP4TE) | FXGMAC_RSS_TCP4TE; + if (nfc->flow_type == TCP_V6_FLOW) + rssopt |= FXGMAC_RSS_IP6TE | FXGMAC_RSS_TCP6TE; + break; + case UDP_V4_FLOW: + if (!(data & (RXH_IP_SRC | RXH_IP_DST))) + return -EINVAL; + rssopt |= BIT(FXGMAC_RSS_IP4TE); + switch (data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rssopt |= FXGMAC_RSS_UDP4TE; + break; + default: + return -EINVAL; + } + break; + case UDP_V6_FLOW: + if (!(data & (RXH_IP_SRC | RXH_IP_DST))) + return -EINVAL; + rssopt |= FXGMAC_RSS_IP6TE; + + switch (data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rssopt |= FXGMAC_RSS_UDP6TE; + break; + default: + return -EINVAL; + } + break; + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case SCTP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case SCTP_V6_FLOW: + if (!(data & + (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3))) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* if options are changed, then update to hw */ + if (rssopt != pdata->rss_options) { + if ((rssopt & UDP_RSS_FLAGS) && + !(pdata->rss_options & UDP_RSS_FLAGS)) + yt_dbg(pdata, + "enabling UDP RSS: fragmented packets may arrive out of order to the stack above."); + + yt_dbg(pdata, "rss option changed from %x to %x\n", + pdata->rss_options, rssopt); + pdata->rss_options = rssopt; + hw_ops->set_rss_options(pdata); + } + + return 0; +} + +static int fxgmac_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + struct fxgmac_pdata *pdata = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + yt_dbg(pdata, "%s, rx cls rule insert-n\a\n", __func__); + break; + case ETHTOOL_SRXCLSRLDEL: + yt_dbg(pdata, "%s, rx cls rule del-n\a\n", __func__); + break; + case ETHTOOL_SRXFH: + yt_dbg(pdata, "%s, rx rss option\n", __func__); + ret = fxgmac_set_rss_hash_opt(pdata, cmd); + break; + default: + break; + } + + return ret; +} + +static void fxgmac_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *exact) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + ring->rx_max_pending = FXGMAC_RX_DESC_CNT; + ring->tx_max_pending = FXGMAC_TX_DESC_CNT; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = pdata->rx_desc_count; + ring->tx_pending = pdata->tx_desc_count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static int fxgmac_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *exact) + +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret; + + if (pdata->dev_state != FXGMAC_DEV_START) + return 0; + + mutex_lock(&pdata->mutex); + pdata->tx_desc_count = ring->tx_pending; + pdata->rx_desc_count = ring->rx_pending; + + fxgmac_stop(pdata); + fxgmac_free_tx_data(pdata); + fxgmac_free_rx_data(pdata); + ret = fxgmac_channels_rings_alloc(pdata); + if (ret < 0) + goto unlock; + + ret = fxgmac_start(pdata); + +unlock: + mutex_unlock(&pdata->mutex); + return ret; +} + +static int fxgmac_get_regs_len(struct net_device __always_unused *netdev) +{ + return FXGMAC_PHY_REG_CNT * sizeof(u32); +} + +static void fxgmac_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u32 *regs_buff = p; + + memset(p, 0, FXGMAC_PHY_REG_CNT * sizeof(u32)); + for (u32 i = MII_BMCR; i < FXGMAC_PHY_REG_CNT; i++) + regs_buff[i] = phy_read(pdata->phydev, i); + + regs->version = regs_buff[MII_PHYSID1] << 16 | regs_buff[MII_PHYSID2]; +} + +static void fxgmac_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *data) +{ + struct fxgmac_pdata *yt = netdev_priv(dev); + bool tx_pause, rx_pause; + + phy_get_pause(yt->phydev, &tx_pause, &rx_pause); + + data->autoneg = yt->phydev->autoneg; + data->tx_pause = tx_pause ? 1 : 0; + data->rx_pause = rx_pause ? 1 : 0; + + yt_dbg(yt, "%s, rx=%d, tx=%d\n", __func__, data->rx_pause, + data->tx_pause); +} + +static int fxgmac_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *data) +{ + struct fxgmac_pdata *yt = netdev_priv(dev); + + if (dev->mtu > ETH_DATA_LEN) + return -EOPNOTSUPP; + + phy_set_asym_pause(yt->phydev, data->rx_pause, data->tx_pause); + + yt_dbg(yt, "%s, autoneg=%d, rx=%d, tx=%d\n", __func__, + data->autoneg, data->rx_pause, data->tx_pause); + + return 0; +} + +static void fxgmac_ethtool_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + switch (stringset) { + case ETH_SS_STATS: + for (u32 i = 0; i < FXGMAC_STATS_COUNT; i++) { + memcpy(data, fxgmac_gstring_stats[i].stat_string, + strlen(fxgmac_gstring_stats[i].stat_string)); + data += ETH_GSTRING_LEN; + } + break; + default: + WARN_ON(1); + break; + } +} + +static int fxgmac_ethtool_get_sset_count(struct net_device *netdev, + int stringset) +{ + int ret; + + switch (stringset) { + case ETH_SS_STATS: + ret = FXGMAC_STATS_COUNT; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static void fxgmac_ethtool_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate)) + pdata->hw_ops.read_mmc_stats(pdata); + + for (u32 i = 0; i < FXGMAC_STATS_COUNT; i++) + *data++ = *(u64 *)((u8 *)pdata + + fxgmac_gstring_stats[i].stat_offset); +} + +static int fxgmac_ethtool_reset(struct net_device *netdev, u32 *flag) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret = 0; + u32 val; + + val = *flag & (ETH_RESET_ALL | ETH_RESET_PHY); + if (!val) { + yt_err(pdata, "Operation not support.\n"); + return -EOPNOTSUPP; + } + + switch (*flag) { + case ETH_RESET_ALL: + fxgmac_restart(pdata); + *flag = 0; + break; + case ETH_RESET_PHY: + /* Power off and on the phy in order to properly + * configure the MAC timing + */ + ret = phy_modify(pdata->phydev, MII_BMCR, BMCR_PDOWN, + BMCR_PDOWN); + if (ret < 0) + return ret; + fsleep(9000); + + ret = phy_modify(pdata->phydev, MII_BMCR, BMCR_PDOWN, 0); + if (ret < 0) + return ret; + *flag = 0; + break; + default: + break; + } + + return 0; +} + +static const struct ethtool_ops fxgmac_ethtool_ops = { + .get_drvinfo = fxgmac_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = fxgmac_ethtool_get_msglevel, + .set_msglevel = fxgmac_ethtool_set_msglevel, + .get_channels = fxgmac_ethtool_get_channels, + .get_coalesce = fxgmac_ethtool_get_coalesce, + .set_coalesce = fxgmac_ethtool_set_coalesce, + .reset = fxgmac_ethtool_reset, + .supported_coalesce_params = ETHTOOL_COALESCE_USECS, + .get_strings = fxgmac_ethtool_get_strings, + .get_sset_count = fxgmac_ethtool_get_sset_count, + .get_ethtool_stats = fxgmac_ethtool_get_ethtool_stats, + .get_regs_len = fxgmac_get_regs_len, + .get_regs = fxgmac_get_regs, + .get_ringparam = fxgmac_get_ringparam, + .set_ringparam = fxgmac_set_ringparam, + .get_rxnfc = fxgmac_get_rxnfc, + .set_rxnfc = fxgmac_set_rxnfc, + .get_rxfh_indir_size = fxgmac_rss_indir_size, + .get_rxfh_key_size = fxgmac_get_rxfh_key_size, + .nway_reset = phy_ethtool_nway_reset, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_pauseparam = fxgmac_get_pauseparam, + .set_pauseparam = fxgmac_set_pauseparam, +}; + +const struct ethtool_ops *fxgmac_get_ethtool_ops(void) +{ + return &fxgmac_ethtool_ops; +}
driver inclusion category: feature
--------------------------------
Implement the .get_wol and .set_wol callback function.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../motorcomm/yt6801/yt6801_ethtool.c | 169 +++++ .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 576 ++++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 118 ++++ 3 files changed, 863 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c index 12150584f..decef3849 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_ethtool.c @@ -498,6 +498,173 @@ static int fxgmac_set_ringparam(struct net_device *netdev, return ret; }
+static void fxgmac_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC | + WAKE_ARP | WAKE_PHY; + + wol->wolopts = 0; + if (!(pdata->hw_feat.rwk) || !device_can_wakeup(pdata->dev)) { + yt_err(pdata, "%s, pci does not support wakeup.\n", __func__); + return; + } + + wol->wolopts = pdata->wol; +} + +#pragma pack(1) +/* it's better to make this struct's size to 128byte. */ +struct pattern_packet { + u8 ether_daddr[ETH_ALEN]; + u8 ether_saddr[ETH_ALEN]; + u16 ether_type; + + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol */ + u8 ar_hln; /* length of hardware address */ + u8 ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + u8 ar_sha[ETH_ALEN]; /* sender hardware address */ + u8 ar_sip[4]; /* sender IP address */ + u8 ar_tha[ETH_ALEN]; /* target hardware address */ + u8 ar_tip[4]; /* target IP address */ + + u8 reverse[86]; +}; + +#pragma pack() + +static int fxgmac_set_pattern_data(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u8 type_offset, tip_offset, op_offset; + struct wol_bitmap_pattern pattern[4]; + struct pattern_packet packet; + u32 ip_addr, i = 0; + + memset(pattern, 0, sizeof(struct wol_bitmap_pattern) * 4); + + /* Config ucast */ + if (pdata->wol & WAKE_UCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memcpy(pattern[i].pattern_info, pdata->mac_addr, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + /* Config bcast */ + if (pdata->wol & WAKE_BCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memset(pattern[i].pattern_info, 0xFF, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + /* Config mcast */ + if (pdata->wol & WAKE_MCAST) { + pattern[i].mask_info[0] = 0x7; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_info[0] = 0x1; + pattern[i].pattern_info[1] = 0x0; + pattern[i].pattern_info[2] = 0x5E; + pattern[i].pattern_offset = 0; + i++; + } + + /* Config arp */ + if (pdata->wol & WAKE_ARP) { + memset(pattern[i].mask_info, 0, sizeof(pattern[0].mask_info)); + type_offset = offsetof(struct pattern_packet, ar_pro); + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + type_offset++; + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + op_offset = offsetof(struct pattern_packet, ar_op); + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + op_offset++; + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + tip_offset = offsetof(struct pattern_packet, ar_tip); + pattern[i].mask_info[tip_offset / 8] |= 1 << tip_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + + packet.ar_pro = 0x0 << 8 | 0x08; + /* ARP type is 0x0800, notice that ar_pro and ar_op is + * big endian + */ + packet.ar_op = 0x1 << 8; + /* 1 is arp request,2 is arp replay, 3 is rarp request, + * 4 is rarp replay + */ + ip_addr = fxgmac_get_netdev_ip4addr(pdata); + packet.ar_tip[0] = ip_addr & 0xFF; + packet.ar_tip[1] = (ip_addr >> 8) & 0xFF; + packet.ar_tip[2] = (ip_addr >> 16) & 0xFF; + packet.ar_tip[3] = (ip_addr >> 24) & 0xFF; + memcpy(pattern[i].pattern_info, &packet, MAX_PATTERN_SIZE); + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_offset = 0; + i++; + } + + return hw_ops->set_wake_pattern(pdata, pattern, i); +} + +static int fxgmac_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret; + + if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_FILTER)) { + yt_err(pdata, "%s, not supported wol options, 0x%x\n", __func__, + wol->wolopts); + return -EOPNOTSUPP; + } + + if (!(pdata->hw_feat.rwk)) { + yt_err(pdata, "%s, hw wol feature is n/a\n", __func__); + return wol->wolopts ? -EOPNOTSUPP : 0; + } + + pdata->wol = 0; + if (wol->wolopts & WAKE_UCAST) + pdata->wol |= WAKE_UCAST; + + if (wol->wolopts & WAKE_MCAST) + pdata->wol |= WAKE_MCAST; + + if (wol->wolopts & WAKE_BCAST) + pdata->wol |= WAKE_BCAST; + + if (wol->wolopts & WAKE_MAGIC) + pdata->wol |= WAKE_MAGIC; + + if (wol->wolopts & WAKE_PHY) + pdata->wol |= WAKE_PHY; + + if (wol->wolopts & WAKE_ARP) + pdata->wol |= WAKE_ARP; + + ret = fxgmac_set_pattern_data(pdata); + if (ret < 0) + return ret; + + ret = fxgmac_config_wol(pdata, (!!(pdata->wol))); + yt_dbg(pdata, "%s, opt=0x%x, wol=0x%x\n", __func__, wol->wolopts, + pdata->wol); + + return ret; +} + static int fxgmac_get_regs_len(struct net_device __always_unused *netdev) { return FXGMAC_PHY_REG_CNT * sizeof(u32); @@ -656,6 +823,8 @@ static const struct ethtool_ops fxgmac_ethtool_ops = { .set_rxnfc = fxgmac_set_rxnfc, .get_rxfh_indir_size = fxgmac_rss_indir_size, .get_rxfh_key_size = fxgmac_get_rxfh_key_size, + .get_wol = fxgmac_get_wol, + .set_wol = fxgmac_set_wol, .nway_reset = phy_ethtool_nway_reset, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c index a70fa4ede..bd3036625 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c @@ -1399,6 +1399,187 @@ static void fxgmac_config_rss(struct fxgmac_pdata *pdata) fxgmac_disable_rss(pdata); }
+static void fxgmac_update_aoe_ipv4addr(struct fxgmac_pdata *pdata, u8 *ip_addr) +{ + u32 val, ipval = 0; + + if (ip_addr) { + ipval = (ip_addr[0] << 24) | (ip_addr[1] << 16) | + (ip_addr[2] << 8) | (ip_addr[3] << 0); + yt_dbg(pdata, + "%s, covert IP dotted-addr %s to binary 0x%08x ok.\n", + __func__, ip_addr, cpu_to_be32(ipval)); + } else { + /* Get ipv4 addr from net device */ + ipval = fxgmac_get_netdev_ip4addr(pdata); + yt_dbg(pdata, "%s, Get net device binary IP ok, 0x%08x\n", + __func__, cpu_to_be32(ipval)); + + ipval = cpu_to_be32(ipval); + } + + val = rd32_mac(pdata, MAC_ARP_PROTO_ADDR); + if (val != (ipval)) { + wr32_mac(pdata, ipval, MAC_ARP_PROTO_ADDR); + yt_dbg(pdata, + "%s, update arp ipaddr reg from 0x%08x to 0x%08x\n", + __func__, val, ipval); + } +} + +static void fxgmac_enable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 1); + wr32_mac(pdata, val, MAC_CR); +} + +static void fxgmac_disable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, 0); + wr32_mac(pdata, val, MAC_CR); +} + +/** + * fxgmac_set_ns_offload - config register for NS offload function + * @pdata: board private structure + * @index: 0~1, index to NS look up table. one entry of the lut is like + * this |remote|solicited|target0|target1| + * @remote_addr: ipv6 addr where yt6801 gets the NS solicitation pkt(request). + * in common, it is 0 to match any remote machine. + * @solicited_addr: the solicited node multicast group address which + * yt6801 computes and joins. + * @target_addr1: it is the target address in NS solicitation pkt. + * @target_addr2:second target address, any address (with ast 6B same with + * target address) + * @mac_addr: it is the target address in NS solicitation pkt. + */ +static void +fxgmac_set_ns_offload(struct fxgmac_pdata *pdata, unsigned int index, + unsigned char *remote_addr, unsigned char *solicited_addr, + unsigned char *target_addr1, unsigned char *target_addr2, + unsigned char *mac_addr) +{ + u32 base = (NS_LUT_MAC_ADDR_CTL - NS_LUT_ROMOTE0 + 4) * index; + u32 address[4], mac_hi, mac_lo; + u8 remote_not_zero = 0; + u32 val; + + val = rd32_mem(pdata, NS_TPID_PRO); + fxgmac_set_bits(&val, NS_TPID_PRO_STPID_POS, NS_TPID_PRO_STPID_LEN, + 0X8100); + fxgmac_set_bits(&val, NS_TPID_PRO_CTPID_POS, NS_TPID_PRO_CTPID_LEN, + 0X9100); + wr32_mem(pdata, val, NS_TPID_PRO); + + val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL); + fxgmac_set_bits(&val, NS_LUT_DST_CMP_TYPE_POS, NS_LUT_DST_CMP_TYPE_LEN, + 1); + fxgmac_set_bits(&val, NS_LUT_DST_IGNORED_POS, NS_LUT_DST_IGNORED_LEN, + 1); + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + fxgmac_set_bits(&val, NS_LUT_TARGET_ISANY_POS, NS_LUT_TARGET_ISANY_LEN, + 0); + wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL); + + for (u32 i = 0; i < 16 / 4; i++) { + address[i] = (remote_addr[i * 4 + 0] << 24) | + (remote_addr[i * 4 + 1] << 16) | + (remote_addr[i * 4 + 2] << 8) | + (remote_addr[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_ROMOTE0 + 4 * i); + if (address[i]) + remote_not_zero = 1; + + address[i] = (target_addr1[i * 4 + 0] << 24) | + (target_addr1[i * 4 + 1] << 16) | + (target_addr1[i * 4 + 2] << 8) | + (target_addr1[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_TARGET0 + 4 * i); + address[i] = (solicited_addr[i * 4 + 0] << 24) | + (solicited_addr[i * 4 + 1] << 16) | + (solicited_addr[i * 4 + 2] << 8) | + (solicited_addr[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], base + NS_LUT_SOLICITED0 + 4 * i); + address[i] = (target_addr2[i * 4 + 0] << 24) | + (target_addr2[i * 4 + 1] << 16) | + (target_addr2[i * 4 + 2] << 8) | + (target_addr2[i * 4 + 3] << 0); + wr32_mem(pdata, address[i], + 0X10 * index + NS_LUT_TARGET4 + 4 * i); + } + mac_hi = (mac_addr[0] << 24) | (mac_addr[1] << 16) | + (mac_addr[2] << 8) | (mac_addr[3] << 0); + mac_lo = (mac_addr[4] << 8) | (mac_addr[5] << 0); + wr32_mem(pdata, mac_hi, base + NS_LUT_MAC_ADDR); + + val = rd32_mem(pdata, base + NS_LUT_MAC_ADDR_CTL); + fxgmac_set_bits(&val, NS_LUT_MAC_ADDR_LOW_POS, NS_LUT_MAC_ADDR_LOW_LEN, + mac_lo); + if (remote_not_zero == 0) + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 0); + else + fxgmac_set_bits(&val, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + wr32_mem(pdata, val, base + NS_LUT_MAC_ADDR_CTL); +} + +static void fxgmac_update_ns_offload_ipv6addr(struct fxgmac_pdata *pdata, + unsigned int param) +{ + struct net_device *netdev = pdata->netdev; + unsigned char addr_buf[5][16]; + static u8 ns_offload_tab_idx; + unsigned char *solicited_addr; + unsigned char *target_addr1; + unsigned char *remote_addr; + unsigned char *mac_addr; + + remote_addr = (unsigned char *)&addr_buf[0][0]; + solicited_addr = (unsigned char *)&addr_buf[1][0]; + target_addr1 = (unsigned char *)&addr_buf[2][0]; + mac_addr = (unsigned char *)&addr_buf[4][0]; + + param &= (FXGMAC_NS_IFA_LOCAL_LINK | FXGMAC_NS_IFA_GLOBAL_UNICAST); + /* Get ipv6 addr from net device */ + if (fxgmac_get_netdev_ip6addr(pdata, target_addr1, solicited_addr, + param) == NULL) { + yt_err(pdata, + "%s, get ipv6 addr with err and ignore NS offload.\n", + __func__); + return; + } + + yt_dbg(pdata, "%s, IPv6 local-link=%pI6, solicited =%pI6\n", __func__, + target_addr1, solicited_addr); + + memcpy(mac_addr, netdev->dev_addr, netdev->addr_len); + yt_dbg(pdata, "ns_tab idx=%d, %02x:%02x:%02x:%02x:%02x:%02x\n", + ns_offload_tab_idx, mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + memset(remote_addr, 0, 16); + fxgmac_set_ns_offload(pdata, ns_offload_tab_idx++, remote_addr, + solicited_addr, target_addr1, target_addr1, + mac_addr); + +#define NS_OFFLOAD_TAB_MAX_IDX 2 + if (ns_offload_tab_idx >= NS_OFFLOAD_TAB_MAX_IDX) + ns_offload_tab_idx = 0; +} + +static void fxgmac_enable_ns_offload(struct fxgmac_pdata *pdata) +{ + wr32_mem(pdata, 0X00000011, NS_OF_GLB_CTL); +} + static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata) { u32 val; @@ -1419,6 +1600,390 @@ static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata) return 0; }
+static int fxgmac_set_wake_pattern_mask(struct fxgmac_pdata *pdata, + u32 filter_index, u8 register_index, + u32 data) +{ + const u16 address_offset[16][3] = { + {0x1020, 0x1024, 0x1028}, + {0x102c, 0x1030, 0x1034}, + {0x1038, 0x103c, 0x1040}, + {0x1044, 0x1050, 0x1054}, + {0x1058, 0x105c, 0x1060}, + {0x1064, 0x1068, 0x106c}, + {0x1070, 0x1074, 0x1078}, + {0x107c, 0x1080, 0x1084}, + {0x1088, 0x108c, 0x1090}, + {0x1134, 0x113c, 0x1140}, + {0x1208, 0x1200, 0x1204}, + {0x1218, 0x1210, 0x1214}, + {0x1228, 0x1220, 0x1224}, + {0x1238, 0x1230, 0x1234}, + {0x1248, 0x1240, 0x1244}, + {0x1258, 0x1250, 0x1254}, + }; + if (filter_index > 15 || register_index > 2) { + yt_err(pdata, + "%s, xx is over range, filter:%d, register:0x%x\n", + __func__, filter_index, register_index); + return -EINVAL; + } + wr32_mem(pdata, data, address_offset[filter_index][register_index]); + return 0; +} + +union type16 { + u16 raw; + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + u16 bit_8 : 1; + u16 bit_9 : 1; + u16 bit_10 : 1; + u16 bit_11 : 1; + u16 bit_12 : 1; + u16 bit_13 : 1; + u16 bit_14 : 1; + u16 bit_15 : 1; + } bits; +}; + +union type8 { + u16 raw; + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + } bits; +}; + +static u16 wol_crc16(u8 *frame, u16 uslen) +{ + union type16 crc, crc_comb; + union type8 next_crc, rrpe_data; + + next_crc.raw = 0; + crc.raw = 0xffff; + for (u32 i = 0; i < uslen; i++) { + rrpe_data.raw = frame[i]; + next_crc.bits.bit_0 = crc.bits.bit_15 ^ rrpe_data.bits.bit_0; + next_crc.bits.bit_1 = crc.bits.bit_14 ^ next_crc.bits.bit_0 ^ + rrpe_data.bits.bit_1; + next_crc.bits.bit_2 = crc.bits.bit_13 ^ next_crc.bits.bit_1 ^ + rrpe_data.bits.bit_2; + next_crc.bits.bit_3 = crc.bits.bit_12 ^ next_crc.bits.bit_2 ^ + rrpe_data.bits.bit_3; + next_crc.bits.bit_4 = crc.bits.bit_11 ^ next_crc.bits.bit_3 ^ + rrpe_data.bits.bit_4; + next_crc.bits.bit_5 = crc.bits.bit_10 ^ next_crc.bits.bit_4 ^ + rrpe_data.bits.bit_5; + next_crc.bits.bit_6 = crc.bits.bit_9 ^ next_crc.bits.bit_5 ^ + rrpe_data.bits.bit_6; + next_crc.bits.bit_7 = crc.bits.bit_8 ^ next_crc.bits.bit_6 ^ + rrpe_data.bits.bit_7; + + crc_comb.bits.bit_15 = crc.bits.bit_7 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_14 = crc.bits.bit_6; + crc_comb.bits.bit_13 = crc.bits.bit_5; + crc_comb.bits.bit_12 = crc.bits.bit_4; + crc_comb.bits.bit_11 = crc.bits.bit_3; + crc_comb.bits.bit_10 = crc.bits.bit_2; + crc_comb.bits.bit_9 = crc.bits.bit_1 ^ next_crc.bits.bit_0; + crc_comb.bits.bit_8 = crc.bits.bit_0 ^ next_crc.bits.bit_1; + crc_comb.bits.bit_7 = next_crc.bits.bit_0 ^ next_crc.bits.bit_2; + crc_comb.bits.bit_6 = next_crc.bits.bit_1 ^ next_crc.bits.bit_3; + crc_comb.bits.bit_5 = next_crc.bits.bit_2 ^ next_crc.bits.bit_4; + crc_comb.bits.bit_4 = next_crc.bits.bit_3 ^ next_crc.bits.bit_5; + crc_comb.bits.bit_3 = next_crc.bits.bit_4 ^ next_crc.bits.bit_6; + crc_comb.bits.bit_2 = next_crc.bits.bit_5 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_1 = next_crc.bits.bit_6; + crc_comb.bits.bit_0 = next_crc.bits.bit_7; + crc.raw = crc_comb.raw; + } + return crc.raw; +} + +static void __write_filter(struct fxgmac_pdata *pdata, u8 i, u8 n) +{ + u8 *mask = &pdata->pattern[i * 4 + n].mask_info[0]; + u32 val; + + val = (mask[3] & 0x7f << 24) | (mask[2] << 16) | (mask[1] << 8) | + (mask[0] << 0); + + wr32_mac(pdata, val, MAC_RWK_PAC); +} + +static u32 __set_filter_addr_type(struct fxgmac_pdata *pdata, u8 i, u8 n, + u32 total_cnt, u32 pattern_cnt) +{ + struct wol_bitmap_pattern *pattern = &pdata->pattern[i * 4 + n]; + u32 val; + + /* Set filter enable bit. */ + val = ((i * 4 + n) < total_cnt) ? (0x1 << 8 * n) : 0x0; + + /* Set filter address type, 0- unicast, 1 - multicast. */ + val |= (i * 4 + n >= total_cnt) ? 0x0 : + (i * 4 + n >= pattern_cnt) ? (0x1 << (3 + 8 * n)) : + pattern->pattern_offset ? 0x0 : + !(pattern->mask_info[0] & 0x01) ? 0x0 : + (pattern->pattern_info[0] & 0x01) ? (0x1 << (3 + 8 * n)) : + 0x0; + return val; +} + +static u32 __wake_pattern_mask_val(struct wol_bitmap_pattern *pattern, u8 n) +{ + u8 *mask = &pattern->mask_info[n * 4]; + u32 val; + + val = ((mask[7] & 0x7f) << (24 + 1)) | (mask[6] << (16 + 1)) | + (mask[5] << (8 + 1)) | (mask[4] << (0 + 1)) | + ((mask[3] & 0x80) >> 7); + + return val; +} + +static int fxgmac_set_wake_pattern(struct fxgmac_pdata *pdata, + struct wol_bitmap_pattern *wol_pattern, + u32 pattern_cnt) +{ + u32 total_cnt = 0, inherited_cnt = 0, val; + struct wol_bitmap_pattern *pattern; + u16 map_index, mask_index; + u8 mask[MAX_PATTERN_SIZE]; + u32 i, j, z; + + if (pattern_cnt > MAX_PATTERN_COUNT) { + yt_err(pdata, "%s, %d patterns exceed %d, not supported!\n", + __func__, pattern_cnt, MAX_PATTERN_COUNT); + return -EINVAL; + } + + /* Reset the FIFO head pointer. */ + if (fxgmac_check_wake_pattern_fifo_pointer(pdata)) { + yt_err(pdata, "%s, remote pattern array pointer is not be 0\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < pattern_cnt; i++) { + pattern = &pdata->pattern[i]; + memcpy(pattern, wol_pattern + i, sizeof(wol_pattern[0])); + + if (pattern_cnt + inherited_cnt >= MAX_PATTERN_COUNT) + continue; + + if (wol_pattern[i].pattern_offset || + !(wol_pattern[i].mask_info[0] & 0x01)) { + pattern = &pdata->pattern[pattern_cnt + inherited_cnt]; + memcpy(pattern, wol_pattern + i, + sizeof(wol_pattern[0])); + inherited_cnt++; + } + } + total_cnt = pattern_cnt + inherited_cnt; + + /* Calculate the crc-16 of the mask pattern */ + for (i = 0; i < total_cnt; i++) { + /* Please program pattern[i] to NIC for pattern match wakeup. + * pattern_size, pattern_info, mask_info + */ + mask_index = 0; + map_index = 0; + pattern = &pdata->pattern[i]; + for (j = 0; j < pattern->mask_size; j++) { + for (z = 0; + z < ((j == (MAX_PATTERN_SIZE / 8 - 1)) ? 7 : 8); + z++) { + if (pattern->mask_info[j] & (0x01 << z)) { + mask[map_index] = + pattern->pattern_info + [pattern->pattern_offset + + mask_index]; + map_index++; + } + mask_index++; + } + } + /* Calculate the crc-16 of the mask pattern */ + pattern->pattern_crc = wol_crc16(mask, map_index); + memset(mask, 0, sizeof(mask)); + } + + /* Write patterns by FIFO block. */ + for (i = 0; i < (total_cnt + 3) / 4; i++) { + /* 1. Write the first 4Bytes of Filter. */ + __write_filter(pdata, i, 0); + __write_filter(pdata, i, 1); + __write_filter(pdata, i, 2); + __write_filter(pdata, i, 3); + + /* 2. Write the Filter Command. */ + val = 0; + val |= __set_filter_addr_type(pdata, i, 0, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 1, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 2, total_cnt, + pattern_cnt); + val |= __set_filter_addr_type(pdata, i, 3, total_cnt, + pattern_cnt); + wr32_mac(pdata, val, MAC_RWK_PAC); + + /* 3. Write the mask offset. */ + val = (pdata->pattern[i * 4 + 3].pattern_offset << 24) | + (pdata->pattern[i * 4 + 2].pattern_offset << 16) | + (pdata->pattern[i * 4 + 1].pattern_offset << 8) | + (pdata->pattern[i * 4 + 0].pattern_offset << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + + /* 4. Write the masked data CRC. */ + val = (pdata->pattern[i * 4 + 1].pattern_crc << 16) | + (pdata->pattern[i * 4 + 0].pattern_crc << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + val = (pdata->pattern[i * 4 + 3].pattern_crc << 16) | + (pdata->pattern[i * 4 + 2].pattern_crc << 0); + wr32_mac(pdata, val, MAC_RWK_PAC); + } + + for (i = 0; i < total_cnt; i++) { + /* Global manager regitster mask bit 31~62 */ + pattern = &pdata->pattern[i]; + val = __wake_pattern_mask_val(pattern, 0); + fxgmac_set_wake_pattern_mask(pdata, i, 0, val); + + /* Global manager regitster mask bit 63~94 */ + val = __wake_pattern_mask_val(pattern, 1); + fxgmac_set_wake_pattern_mask(pdata, i, 1, val); + + /* Global manager regitster mask bit 95~126 */ + val = __wake_pattern_mask_val(pattern, 2); + fxgmac_set_wake_pattern_mask(pdata, i, 2, val); + } + + return 0; +} + +static void fxgmac_enable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_disable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + fxgmac_set_bits(&val, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 0); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_enable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + struct pci_dev *pdev = to_pci_dev(pdata->dev); + u16 pm_ctrl; + u32 val; + int pos; + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); + + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (!pos) { + yt_err(pdata, "Unable to find Power Management Capabilities\n"); + return; + } + + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pm_ctrl); + pm_ctrl |= PCI_PM_CTRL_PME_ENABLE; + pci_write_config_word(pdev, pos + PCI_PM_CTRL, pm_ctrl); +} + +static void fxgmac_disable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); + + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 0); + wr32_mac(pdata, val, MAC_PMT_STA); +} + +static void fxgmac_enable_wake_packet_indication(struct fxgmac_pdata *pdata, + int en) +{ + u32 val_wpi_crtl0; + + rd32_mem(pdata, MGMT_WOL_CTRL); /* Read-clear WoL event. */ + + /* Prepare to write packet data by write wpi_mode to 1 */ + val_wpi_crtl0 = rd32_mem(pdata, MGMT_WPI_CTRL0); + fxgmac_set_bits(&val_wpi_crtl0, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, + (en ? MGMT_WPI_CTRL0_WPI_MODE_WR : + MGMT_WPI_CTRL0_WPI_MODE_NORMAL)); + wr32_mem(pdata, val_wpi_crtl0, MGMT_WPI_CTRL0); +} + +static void fxgmac_enable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 1); + wr32_mem(pdata, val, WOL_CTRL); +} + +static void fxgmac_disable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 val; + + val = rd32_mem(pdata, WOL_CTRL); + fxgmac_set_bits(&val, WOL_LINKCHG_EN_POS, WOL_LINKCHG_EN_LEN, 0); + wr32_mem(pdata, val, WOL_CTRL); +} + static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata) { struct fxgmac_channel *channel = pdata->channel_head; @@ -2686,4 +3251,15 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops) hw_ops->set_rss_options = fxgmac_write_rss_options; hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key; hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table; + + /* Wake */ + hw_ops->disable_arp_offload = fxgmac_disable_arp_offload; + hw_ops->enable_wake_magic_pattern = fxgmac_enable_wake_magic_pattern; + hw_ops->disable_wake_magic_pattern = fxgmac_disable_wake_magic_pattern; + hw_ops->enable_wake_link_change = fxgmac_enable_wake_link_change; + hw_ops->disable_wake_link_change = fxgmac_disable_wake_link_change; + hw_ops->set_wake_pattern = fxgmac_set_wake_pattern; + hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern; + hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern; + } diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index c5c13601e..6a3d1073c 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -14,6 +14,89 @@ const struct net_device_ops *fxgmac_get_netdev_ops(void); static void fxgmac_napi_enable(struct fxgmac_pdata *pdata);
+unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata) +{ + unsigned int ipval = 0xc0a801ca; /* 192.168.1.202 */ + struct net_device *netdev = pdata->netdev; + struct in_ifaddr *ifa; + + rcu_read_lock(); + + /* We only get the first IPv4 addr. */ + ifa = rcu_dereference(netdev->ip_ptr->ifa_list); + if (ifa) { + ipval = (unsigned int)ifa->ifa_address; + yt_dbg(pdata, "%s, netdev %s IPv4 address %pI4, mask: %pI4\n", + __func__, ifa->ifa_label, &ifa->ifa_address, + &ifa->ifa_mask); + } + + rcu_read_unlock(); + + return ipval; +} + +unsigned char *fxgmac_get_netdev_ip6addr(struct fxgmac_pdata *pdata, + unsigned char *ipval, + unsigned char *ip6addr_solicited, + unsigned int ifa_flag) +{ + struct net_device *netdev = pdata->netdev; + unsigned char solicited_ipval[16] = { 0 }; + unsigned char local_ipval[16] = { 0 }; + struct in6_addr *addr_ip6_solicited; + struct in6_addr *addr_ip6; + struct inet6_ifaddr *ifp; + int err = -EADDRNOTAVAIL; + struct inet6_dev *i6dev; + + if (!(ifa_flag & + (FXGMAC_NS_IFA_GLOBAL_UNICAST | FXGMAC_NS_IFA_LOCAL_LINK))) { + yt_err(pdata, "%s, ifa_flag :%d is err.\n", __func__, ifa_flag); + return NULL; + } + + addr_ip6 = (struct in6_addr *)local_ipval; + addr_ip6_solicited = (struct in6_addr *)solicited_ipval; + + if (ipval) + addr_ip6 = (struct in6_addr *)ipval; + + if (ip6addr_solicited) + addr_ip6_solicited = (struct in6_addr *)ip6addr_solicited; + + in6_pton("fe80::4808:8ffb:d93e:d753", -1, (u8 *)addr_ip6, -1, NULL); + + rcu_read_lock(); + i6dev = __in6_dev_get(netdev); + if (!i6dev) + goto err; + + read_lock_bh(&i6dev->lock); + list_for_each_entry(ifp, &i6dev->addr_list, if_list) { + if (((ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) && + ifp->scope != IFA_LINK) || + ((ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) && + ifp->scope == IFA_LINK)) { + memcpy(addr_ip6, &ifp->addr, 16); + addrconf_addr_solict_mult(addr_ip6, addr_ip6_solicited); + err = 0; + break; + } + } + read_unlock_bh(&i6dev->lock); + + if (err) + goto err; + + rcu_read_unlock(); + return ipval; +err: + rcu_read_unlock(); + yt_err(pdata, "%s, get ipv6 addr err, use default.\n", __func__); + return NULL; +} + static unsigned int fxgmac_desc_tx_avail(struct fxgmac_ring *ring) { unsigned int avail; @@ -903,6 +986,41 @@ static void fxgmac_restart_work(struct work_struct *work) rtnl_unlock(); }
+int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (!pdata->hw_feat.rwk) { + yt_err(pdata, "error configuring WOL - not supported.\n"); + return -EOPNOTSUPP; + } + + hw_ops->disable_wake_magic_pattern(pdata); + hw_ops->disable_wake_pattern(pdata); + hw_ops->disable_wake_link_change(pdata); + + if (en) { + /* Config mac address for rx of magic or ucast */ + hw_ops->set_mac_address(pdata, (u8 *)(pdata->netdev->dev_addr)); + + /* Enable Magic packet */ + if (pdata->wol & WAKE_MAGIC) + hw_ops->enable_wake_magic_pattern(pdata); + + /* Enable global unicast packet */ + if (pdata->wol & + (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP)) + hw_ops->enable_wake_pattern(pdata); + + /* Enable ephy link change */ + if (pdata->wol & WAKE_PHY) + hw_ops->enable_wake_link_change(pdata); + } + device_set_wakeup_enable((pdata->dev), en); + + return 0; +} + int fxgmac_net_powerdown(struct fxgmac_pdata *pdata, bool wake_en) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
driver inclusion category: feature
--------------------------------
Implement the pci_driver suspend function to enable the device to sleep, and implement the resume function to enable the device to resume operation.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 118 ++++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 28 +++++ .../ethernet/motorcomm/yt6801/yt6801_pci.c | 61 +++++++++ 3 files changed, 207 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c index bd3036625..25411f2dd 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c @@ -2424,6 +2424,56 @@ static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata) wr32_mac(pdata, val, DMA_SBMR); }
+static int fxgmac_pre_powerdown(struct fxgmac_pdata *pdata) +{ + u32 val = 0; + int ret; + + fxgmac_disable_rx(pdata); + + yt_dbg(pdata, "%s, phy and mac status update\n", __func__); + + if (device_may_wakeup(pdata->dev)) { + val = rd32_mem(pdata, EPHY_CTRL); + if (val & EPHY_CTRL_STA_LINKUP) { + ret = phy_speed_down(pdata->phydev, true); + if (ret < 0) { + yt_err(pdata, "%s, phy_speed_down err:%d\n", __func__, ret); + return ret; + } + + ret = phy_read_status(pdata->phydev); + if (ret < 0) { + yt_err(pdata, "%s, phy_read_status err:%d\n", + __func__, ret); + return ret; + } + + pdata->phy_speed = pdata->phydev->speed; + pdata->phy_duplex = pdata->phydev->duplex; + yt_dbg(pdata, "%s, speed :%d, duplex :%d\n", __func__, + pdata->phy_speed, pdata->phy_duplex); + + fxgmac_config_mac_speed(pdata); + } else { + yt_dbg(pdata, "%s link down, do nothing\n", __func__); + } + } + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, + * and lead to panic sometimes. So we should disable it from powerup, + * enable it from power down. + */ + val = rd32_mem(pdata, OOB_WOL_CTRL); + fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 0); + wr32_mem(pdata, val, OOB_WOL_CTRL); + fsleep(2000); + + fxgmac_set_mac_address(pdata, pdata->mac_addr); + + return 0; +} + static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata) { u32 stats_pre, stats; @@ -2458,6 +2508,70 @@ static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata) return -ETIMEDOUT; }
+static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata, + bool wol) +{ + u32 val = 0; + + /* Use default arp offloading feature */ + fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL); + fxgmac_enable_arp_offload(pdata); + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST); + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK); + fxgmac_enable_ns_offload(pdata); + fxgmac_enable_wake_packet_indication(pdata, 1); + + /* Enable MAC Rx TX */ + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1); + fxgmac_set_bits(&val, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1); + wr32_mac(pdata, val, MAC_CR); + + val = rd32_mem(pdata, LPW_CTRL); + fxgmac_set_bits(&val, LPW_CTRL_ASPM_LPW_EN_POS, + LPW_CTRL_ASPM_LPW_EN_LEN, 1); + wr32_mem(pdata, val, LPW_CTRL); + + /* Set gmac power down */ + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN, + 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, MGMT_SIGDET); + fxgmac_set_bits(&val, MGMT_SIGDET_POS, MGMT_SIGDET_LEN, + MGMT_SIGDET_55MV); + wr32_mem(pdata, val, MGMT_SIGDET); + fxgmac_phy_clear_interrupt(pdata); +} + +static void fxgmac_config_powerup(struct fxgmac_pdata *pdata) +{ + u32 val = 0; + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, + * and lead to panic sometimes. + * So we should disable it from powerup, enable it from power down. + */ + val = rd32_mem(pdata, OOB_WOL_CTRL); + fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 1); + wr32_mem(pdata, val, OOB_WOL_CTRL); + + /* Clear wpi mode whether or not waked by WOL, write reset value */ + val = rd32_mem(pdata, MGMT_WPI_CTRL0); + fxgmac_set_bits(&val, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, 0); + wr32_mem(pdata, val, MGMT_WPI_CTRL0); + + /* Read pmt_status register to De-assert the pmt_intr_o */ + val = rd32_mac(pdata, MAC_PMT_STA); + /* whether or not waked up by WOL, write reset value */ + fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN, + 0); + /* Write register to synchronized always-on block */ + wr32_mac(pdata, val, MAC_PMT_STA); +} + #define FXGMAC_WOL_WAIT_2_MS 2
static void fxgmac_config_wol_wait_time(struct fxgmac_pdata *pdata) @@ -3262,4 +3376,8 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops) hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern; hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern;
+ /* Power Management */ + hw_ops->pre_power_down = fxgmac_pre_powerdown; + hw_ops->config_power_down = fxgmac_config_powerdown; + hw_ops->config_power_up = fxgmac_config_powerup; } diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index 6a3d1073c..10c103e95 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -986,6 +986,34 @@ static void fxgmac_restart_work(struct work_struct *work) rtnl_unlock(); }
+int fxgmac_net_powerup(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret; + + /* Signal that we are up now */ + pdata->powerstate = 0; + if (__test_and_set_bit(FXGMAC_POWER_STATE_UP, &pdata->powerstate)) + return 0; /* do nothing if already up */ + + ret = fxgmac_start(pdata); + if (ret < 0) { + yt_err(pdata, "%s: fxgmac_start err: %d\n", __func__, ret); + return ret; + } + + /* Must call it after fxgmac_start,because it will be + * enable in fxgmac_start + */ + hw_ops->disable_arp_offload(pdata); + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s, powerstate :%ld.\n", __func__, + pdata->powerstate); + + return 0; +} + int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c index b2cd75b5c..860b79d13 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c @@ -104,6 +104,62 @@ static void fxgmac_shutdown(struct pci_dev *pcidev) (system_state == SYSTEM_POWER_OFF) ? 1 : 0); }
+static int fxgmac_suspend(struct device *device) +{ + struct fxgmac_pdata *pdata = dev_get_drvdata(device); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + mutex_lock(&pdata->mutex); + if (pdata->dev_state != FXGMAC_DEV_START) + goto unlock; + + if (netif_running(netdev)) { + ret = __fxgmac_shutdown(to_pci_dev(device), NULL); + if (ret < 0) + goto unlock; + } + + pdata->dev_state = FXGMAC_DEV_SUSPEND; +unlock: + mutex_unlock(&pdata->mutex); + + return ret; +} + +static int fxgmac_resume(struct device *device) +{ + struct fxgmac_pdata *pdata = dev_get_drvdata(device); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + mutex_lock(&pdata->mutex); + if (pdata->dev_state != FXGMAC_DEV_SUSPEND) + goto unlock; + + pdata->dev_state = FXGMAC_DEV_RESUME; + __clear_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate); + + rtnl_lock(); + if (netif_running(netdev)) { + ret = fxgmac_net_powerup(pdata); + if (ret < 0) { + dev_err(device, "%s, fxgmac_net_powerup err:%d\n", + __func__, ret); + goto unlock; + } + } + + netif_device_attach(netdev); + rtnl_unlock(); + + dev_dbg(device, "%s ok\n", __func__); +unlock: + mutex_unlock(&pdata->mutex); + + return ret; +} + #define MOTORCOMM_PCI_ID 0x1f0a #define YT6801_PCI_DEVICE_ID 0x6801
@@ -114,11 +170,16 @@ static const struct pci_device_id fxgmac_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, fxgmac_pci_tbl);
+static const struct dev_pm_ops fxgmac_pm_ops = { + SYSTEM_SLEEP_PM_OPS(fxgmac_suspend, fxgmac_resume) +}; + static struct pci_driver fxgmac_pci_driver = { .name = FXGMAC_DRV_NAME, .id_table = fxgmac_pci_tbl, .probe = fxgmac_probe, .remove = fxgmac_remove, + .driver.pm = pm_ptr(&fxgmac_pm_ops), .shutdown = fxgmac_shutdown, };
driver inclusion category: feature
--------------------------------
1. Add a Makefile in the motorcomm folder to build yt6801 driver. 2. Add the YT6801 and NET_VENDOR_MOTORCOMM entry in the Kconfig. 3. Add the CONFIG_YT6801 entry in the Makefile. 4. Add the motorcomm entry in the Kconfig. 5. Add the CONFIG_NET_VENDOR_MOTORCOMM entry in the Makefile. 6. Add myself as the maintainer for the motorcomm ethernet driver.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- MAINTAINERS | 8 ++++++ drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/motorcomm/Kconfig | 27 +++++++++++++++++++ drivers/net/ethernet/motorcomm/Makefile | 6 +++++ .../net/ethernet/motorcomm/yt6801/Makefile | 9 +++++++ 6 files changed, 52 insertions(+) create mode 100644 drivers/net/ethernet/motorcomm/Kconfig create mode 100644 drivers/net/ethernet/motorcomm/Makefile create mode 100644 drivers/net/ethernet/motorcomm/yt6801/Makefile
diff --git a/MAINTAINERS b/MAINTAINERS index bca43097c..b357b2bdb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14546,6 +14546,14 @@ F: drivers/most/ F: drivers/staging/most/ F: include/linux/most.h
+MOTORCOMM ETHERNET DRIVER +M: Frank Frank.Sae@motor-comm.com +L: netdev@vger.kernel.org +S: Maintained +W: https://www.motor-comm.com/ +F: Documentation/networking/device_drivers/ethernet/motorcomm/* +F: drivers/net/ethernet/motorcomm/* + MOTORCOMM PHY DRIVER M: Peter Geis pgwipeout@gmail.com M: Frank Frank.Sae@motor-comm.com diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index cce9d9c6c..a489829c1 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -132,6 +132,7 @@ source "drivers/net/ethernet/micrel/Kconfig" source "drivers/net/ethernet/microchip/Kconfig" source "drivers/net/ethernet/mscc/Kconfig" source "drivers/net/ethernet/microsoft/Kconfig" +source "drivers/net/ethernet/motorcomm/Kconfig" source "drivers/net/ethernet/moxa/Kconfig" source "drivers/net/ethernet/myricom/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 9ffae03b0..bddf845de 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/ obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/ obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/ +obj-$(CONFIG_NET_VENDOR_MOTORCOMM) += motorcomm/ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/ obj-$(CONFIG_FEALNX) += fealnx.o diff --git a/drivers/net/ethernet/motorcomm/Kconfig b/drivers/net/ethernet/motorcomm/Kconfig new file mode 100644 index 000000000..e85d11687 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Motorcomm network device configuration +# + +config NET_VENDOR_MOTORCOMM + bool "Motorcomm devices" + default y + help + If you have a network (Ethernet) device belonging to this class, + say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Synopsys devices. If you say Y, you will be asked + for your specific device in the following questions. + +if NET_VENDOR_MOTORCOMM + +config YT6801 + tristate "Motorcomm(R) 6801 PCI-Express Gigabit Ethernet support" + depends on PCI && NET + help + This driver supports Motorcomm(R) 6801 gigabit ethernet family of + adapters. + +endif # NET_VENDOR_MOTORCOMM diff --git a/drivers/net/ethernet/motorcomm/Makefile b/drivers/net/ethernet/motorcomm/Makefile new file mode 100644 index 000000000..511940680 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Motorcomm network device drivers. +# + +obj-$(CONFIG_YT6801) += yt6801/ diff --git a/drivers/net/ethernet/motorcomm/yt6801/Makefile b/drivers/net/ethernet/motorcomm/yt6801/Makefile new file mode 100644 index 000000000..72e0acd65 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2021 Motor-comm Corporation. +# +# Makefile for the Motorcomm(R) 6801 PCI-Express ethernet driver +# + +obj-$(CONFIG_YT6801) += yt6801.o +yt6801-objs := yt6801_desc.o yt6801_ethtool.o yt6801_hw.o \ + yt6801_net.o yt6801_pci.o