diff options
Diffstat (limited to 'drivers/net/ethernet/cadence/macb_main.c')
-rw-r--r-- | drivers/net/ethernet/cadence/macb_main.c | 261 |
1 files changed, 220 insertions, 41 deletions
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index bebd9b1aeb64..5ca17e62dc3e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1,15 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Cadence MACB/GEM Ethernet Controller driver * * Copyright (C) 2004-2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/crc32.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -40,6 +38,13 @@ #include <linux/pm_runtime.h> #include "macb.h" +/* This structure is only used for MACB on SiFive FU540 devices */ +struct sifive_fu540_macb_mgmt { + void __iomem *reg; + unsigned long rate; + struct clk_hw hw; +}; + #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ @@ -984,7 +989,8 @@ static void discard_partial_frame(struct macb_queue *queue, unsigned int begin, */ } -static int gem_rx(struct macb_queue *queue, int budget) +static int gem_rx(struct macb_queue *queue, struct napi_struct *napi, + int budget) { struct macb *bp = queue->bp; unsigned int len; @@ -1066,7 +1072,7 @@ static int gem_rx(struct macb_queue *queue, int budget) skb->data, 32, true); #endif - netif_receive_skb(skb); + napi_gro_receive(napi, skb); } gem_rx_refill(queue); @@ -1074,8 +1080,8 @@ static int gem_rx(struct macb_queue *queue, int budget) return count; } -static int macb_rx_frame(struct macb_queue *queue, unsigned int first_frag, - unsigned int last_frag) +static int macb_rx_frame(struct macb_queue *queue, struct napi_struct *napi, + unsigned int first_frag, unsigned int last_frag) { unsigned int len; unsigned int frag; @@ -1151,7 +1157,7 @@ static int macb_rx_frame(struct macb_queue *queue, unsigned int first_frag, bp->dev->stats.rx_bytes += skb->len; netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", skb->len, skb->csum); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); return 0; } @@ -1174,7 +1180,8 @@ static inline void macb_init_rx_ring(struct macb_queue *queue) queue->rx_tail = 0; } -static int macb_rx(struct macb_queue *queue, int budget) +static int macb_rx(struct macb_queue *queue, struct napi_struct *napi, + int budget) { struct macb *bp = queue->bp; bool reset_rx_queue = false; @@ -1211,7 +1218,7 @@ static int macb_rx(struct macb_queue *queue, int budget) continue; } - dropped = macb_rx_frame(queue, first_frag, tail); + dropped = macb_rx_frame(queue, napi, first_frag, tail); first_frag = -1; if (unlikely(dropped < 0)) { reset_rx_queue = true; @@ -1265,7 +1272,7 @@ static int macb_poll(struct napi_struct *napi, int budget) netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n", (unsigned long)status, budget); - work_done = bp->macbgem_ops.mog_rx(queue, budget); + work_done = bp->macbgem_ops.mog_rx(queue, napi, budget); if (work_done < budget) { napi_complete_done(napi, work_done); @@ -2849,10 +2856,14 @@ static int macb_get_ts_info(struct net_device *netdev, static void gem_enable_flow_filters(struct macb *bp, bool enable) { + struct net_device *netdev = bp->dev; struct ethtool_rx_fs_item *item; u32 t2_scr; int num_t2_scr; + if (!(netdev->features & NETIF_F_NTUPLE)) + return; + num_t2_scr = GEM_BFEXT(T2SCR, gem_readl(bp, DCFG8)); list_for_each_entry(item, &bp->rx_fs_list.list, list) { @@ -3012,8 +3023,7 @@ static int gem_add_flow_filter(struct net_device *netdev, gem_prog_cmp_regs(bp, fs); bp->rx_fs_list.count++; /* enable filtering if NTUPLE on */ - if (netdev->features & NETIF_F_NTUPLE) - gem_enable_flow_filters(bp, 1); + gem_enable_flow_filters(bp, 1); spin_unlock_irqrestore(&bp->rx_fs_lock, flags); return 0; @@ -3201,6 +3211,50 @@ static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } } +static inline void macb_set_txcsum_feature(struct macb *bp, + netdev_features_t features) +{ + u32 val; + + if (!macb_is_gem(bp)) + return; + + val = gem_readl(bp, DMACFG); + if (features & NETIF_F_HW_CSUM) + val |= GEM_BIT(TXCOEN); + else + val &= ~GEM_BIT(TXCOEN); + + gem_writel(bp, DMACFG, val); +} + +static inline void macb_set_rxcsum_feature(struct macb *bp, + netdev_features_t features) +{ + struct net_device *netdev = bp->dev; + u32 val; + + if (!macb_is_gem(bp)) + return; + + val = gem_readl(bp, NCFGR); + if ((features & NETIF_F_RXCSUM) && !(netdev->flags & IFF_PROMISC)) + val |= GEM_BIT(RXCOEN); + else + val &= ~GEM_BIT(RXCOEN); + + gem_writel(bp, NCFGR, val); +} + +static inline void macb_set_rxflow_feature(struct macb *bp, + netdev_features_t features) +{ + if (!macb_is_gem(bp)) + return; + + gem_enable_flow_filters(bp, !!(features & NETIF_F_NTUPLE)); +} + static int macb_set_features(struct net_device *netdev, netdev_features_t features) { @@ -3208,39 +3262,35 @@ static int macb_set_features(struct net_device *netdev, netdev_features_t changed = features ^ netdev->features; /* TX checksum offload */ - if ((changed & NETIF_F_HW_CSUM) && macb_is_gem(bp)) { - u32 dmacfg; - - dmacfg = gem_readl(bp, DMACFG); - if (features & NETIF_F_HW_CSUM) - dmacfg |= GEM_BIT(TXCOEN); - else - dmacfg &= ~GEM_BIT(TXCOEN); - gem_writel(bp, DMACFG, dmacfg); - } + if (changed & NETIF_F_HW_CSUM) + macb_set_txcsum_feature(bp, features); /* RX checksum offload */ - if ((changed & NETIF_F_RXCSUM) && macb_is_gem(bp)) { - u32 netcfg; - - netcfg = gem_readl(bp, NCFGR); - if (features & NETIF_F_RXCSUM && - !(netdev->flags & IFF_PROMISC)) - netcfg |= GEM_BIT(RXCOEN); - else - netcfg &= ~GEM_BIT(RXCOEN); - gem_writel(bp, NCFGR, netcfg); - } + if (changed & NETIF_F_RXCSUM) + macb_set_rxcsum_feature(bp, features); /* RX Flow Filters */ - if ((changed & NETIF_F_NTUPLE) && macb_is_gem(bp)) { - bool turn_on = features & NETIF_F_NTUPLE; + if (changed & NETIF_F_NTUPLE) + macb_set_rxflow_feature(bp, features); - gem_enable_flow_filters(bp, turn_on); - } return 0; } +static void macb_restore_features(struct macb *bp) +{ + struct net_device *netdev = bp->dev; + netdev_features_t features = netdev->features; + + /* TX checksum offload */ + macb_set_txcsum_feature(bp, features); + + /* RX checksum offload */ + macb_set_rxcsum_feature(bp, features); + + /* RX Flow Filters */ + macb_set_rxflow_feature(bp, features); +} + static const struct net_device_ops macb_netdev_ops = { .ndo_open = macb_open, .ndo_stop = macb_close, @@ -3437,7 +3487,7 @@ static int macb_init(struct platform_device *pdev) queue = &bp->queues[q]; queue->bp = bp; - netif_napi_add(dev, &queue->napi, macb_poll, 64); + netif_napi_add(dev, &queue->napi, macb_poll, NAPI_POLL_WEIGHT); if (hw_q) { queue->ISR = GEM_ISR(hw_q - 1); queue->IER = GEM_IER(hw_q - 1); @@ -3576,6 +3626,8 @@ static int macb_init(struct platform_device *pdev) /* max number of receive buffers */ #define AT91ETHER_MAX_RX_DESCR 9 +static struct sifive_fu540_macb_mgmt *mgmt; + /* Initialize and start the Receiver and Transmit subsystems */ static int at91ether_start(struct net_device *dev) { @@ -3903,6 +3955,116 @@ static int at91ether_init(struct platform_device *pdev) return 0; } +static unsigned long fu540_macb_tx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return mgmt->rate; +} + +static long fu540_macb_tx_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + if (WARN_ON(rate < 2500000)) + return 2500000; + else if (rate == 2500000) + return 2500000; + else if (WARN_ON(rate < 13750000)) + return 2500000; + else if (WARN_ON(rate < 25000000)) + return 25000000; + else if (rate == 25000000) + return 25000000; + else if (WARN_ON(rate < 75000000)) + return 25000000; + else if (WARN_ON(rate < 125000000)) + return 125000000; + else if (rate == 125000000) + return 125000000; + + WARN_ON(rate > 125000000); + + return 125000000; +} + +static int fu540_macb_tx_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + rate = fu540_macb_tx_round_rate(hw, rate, &parent_rate); + if (rate != 125000000) + iowrite32(1, mgmt->reg); + else + iowrite32(0, mgmt->reg); + mgmt->rate = rate; + + return 0; +} + +static const struct clk_ops fu540_c000_ops = { + .recalc_rate = fu540_macb_tx_recalc_rate, + .round_rate = fu540_macb_tx_round_rate, + .set_rate = fu540_macb_tx_set_rate, +}; + +static int fu540_c000_clk_init(struct platform_device *pdev, struct clk **pclk, + struct clk **hclk, struct clk **tx_clk, + struct clk **rx_clk, struct clk **tsu_clk) +{ + struct clk_init_data init; + int err = 0; + + err = macb_clk_init(pdev, pclk, hclk, tx_clk, rx_clk, tsu_clk); + if (err) + return err; + + mgmt = devm_kzalloc(&pdev->dev, sizeof(*mgmt), GFP_KERNEL); + if (!mgmt) + return -ENOMEM; + + init.name = "sifive-gemgxl-mgmt"; + init.ops = &fu540_c000_ops; + init.flags = 0; + init.num_parents = 0; + + mgmt->rate = 0; + mgmt->hw.init = &init; + + *tx_clk = clk_register(NULL, &mgmt->hw); + if (IS_ERR(*tx_clk)) + return PTR_ERR(*tx_clk); + + err = clk_prepare_enable(*tx_clk); + if (err) + dev_err(&pdev->dev, "failed to enable tx_clk (%u)\n", err); + else + dev_info(&pdev->dev, "Registered clk switch '%s'\n", init.name); + + return 0; +} + +static int fu540_c000_init(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + + mgmt->reg = ioremap(res->start, resource_size(res)); + if (!mgmt->reg) + return -ENOMEM; + + return macb_init(pdev); +} + +static const struct macb_config fu540_c000_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP, + .dma_burst_length = 16, + .clk_init = fu540_c000_clk_init, + .init = fu540_c000_init, + .jumbo_max_len = 10240, +}; + static const struct macb_config at91sam9260_config = { .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII, .clk_init = macb_clk_init, @@ -3992,6 +4154,7 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,emac", .data = &emac_config }, { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config}, { .compatible = "cdns,zynq-gem", .data = &zynq_config }, + { .compatible = "sifive,fu540-macb", .data = &fu540_c000_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); @@ -4140,7 +4303,7 @@ static int macb_probe(struct platform_device *pdev) if (PTR_ERR(mac) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_out_free_netdev; - } else if (!IS_ERR(mac)) { + } else if (!IS_ERR_OR_NULL(mac)) { ether_addr_copy(bp->dev->dev_addr, mac); } else { macb_get_hwaddr(bp); @@ -4199,6 +4362,7 @@ err_out_free_netdev: err_disable_clocks: clk_disable_unprepare(tx_clk); + clk_unregister(tx_clk); clk_disable_unprepare(hclk); clk_disable_unprepare(pclk); clk_disable_unprepare(rx_clk); @@ -4233,6 +4397,7 @@ static int macb_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); if (!pm_runtime_suspended(&pdev->dev)) { clk_disable_unprepare(bp->tx_clk); + clk_unregister(bp->tx_clk); clk_disable_unprepare(bp->hclk); clk_disable_unprepare(bp->pclk); clk_disable_unprepare(bp->rx_clk); @@ -4273,6 +4438,12 @@ static int __maybe_unused macb_suspend(struct device *dev) spin_lock_irqsave(&bp->lock, flags); macb_reset_hw(bp); spin_unlock_irqrestore(&bp->lock, flags); + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + bp->pm_data.usrio = macb_or_gem_readl(bp, USRIO); + + if (netdev->hw_features & NETIF_F_NTUPLE) + bp->pm_data.scrt2 = gem_readl_n(bp, ETHT, SCRT2_ETHT); } netif_carrier_off(netdev); @@ -4301,6 +4472,13 @@ static int __maybe_unused macb_resume(struct device *dev) disable_irq_wake(bp->queues[0].irq); } else { macb_writel(bp, NCR, MACB_BIT(MPE)); + + if (netdev->hw_features & NETIF_F_NTUPLE) + gem_writel_n(bp, ETHT, SCRT2_ETHT, bp->pm_data.scrt2); + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + macb_or_gem_writel(bp, USRIO, bp->pm_data.usrio); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) napi_enable(&queue->napi); @@ -4312,6 +4490,7 @@ static int __maybe_unused macb_resume(struct device *dev) bp->macbgem_ops.mog_init_rings(bp); macb_init_hw(bp); macb_set_rx_mode(netdev); + macb_restore_features(bp); netif_device_attach(netdev); if (bp->ptp_info) bp->ptp_info->ptp_init(netdev); |