mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			418 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			418 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From c2858487072a27598f8d5a9ce85255ff200b4cae Mon Sep 17 00:00:00 2001
 | |
| From: Phil Elwell <phil@raspberrypi.com>
 | |
| Date: Fri, 10 Sep 2021 17:20:45 +0100
 | |
| Subject: [PATCH] net: macb: Also set DMA coherent mask
 | |
| 
 | |
| macb: Add device tree properties that allow configuration of the AXI max pipeline register
 | |
| 
 | |
| net: macb: add support for ethtool interrupt moderation configuration
 | |
| 
 | |
| Only global throttling of rx or tx by time quanta is supported.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| 
 | |
| macb: add platform device shutdown function. Prevents AXI master over PCIE from hanging when the host is rebooted.
 | |
| 
 | |
| net: macb: increase polling interval for MDIO completion
 | |
| 
 | |
| MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
 | |
| is a bit aggressive, so increase to 100us as the transaction
 | |
| usually takes 100-200us to complete.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| 
 | |
| net: macb: Several patches for RP1
 | |
| 
 | |
| 64-bit RX fix
 | |
| 
 | |
| Also set DMA coherent mask
 | |
| 
 | |
| Add device tree properties that allow configuration of the AXI max
 | |
| pipeline register
 | |
| 
 | |
| Add support for ethtool interrupt moderation configuration
 | |
| 
 | |
| Only global throttling of rx or tx by time quanta is supported.
 | |
| 
 | |
| Add platform device shutdown function. Prevents AXI master over PCIE
 | |
| from hanging when the host is rebooted.
 | |
| 
 | |
| Increase polling interval for MDIO completion
 | |
| 
 | |
| MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
 | |
| is a bit aggressive, so increase to 100us as the transaction
 | |
| usually takes 100-200us to complete.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| 
 | |
| net: macb: Support the phy-reset-gpios property
 | |
| 
 | |
| Allow a PHY to be reset with an optional GPIO. The reset duration can
 | |
| be specified in milliseconds - the default is 10ms.
 | |
| 
 | |
| Signed-off-by: Phil Elwell <phil@raspberrypi.com>
 | |
| 
 | |
| drivers: net: macb: close device on driver shutdown
 | |
| 
 | |
| Fix some suspicious locking and instead call into macb_close, which
 | |
| deregisters and frees all resources the corresponding macb_open
 | |
| claimed.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| 
 | |
| net: macb: add hack to prevent TX stalls in a quiet system
 | |
| 
 | |
| See https://github.com/raspberrypi/linux-2712/issues/89
 | |
| 
 | |
| There is some critical window during TX where a further write to the
 | |
| TSTART bit while TX is active does not cause newly queued TX descriptors
 | |
| to be consumed.
 | |
| 
 | |
| For now "wait a bit, then try anyway" seems to work.
 | |
| 
 | |
| Requires further investigation, but this unsticks NFS reliably.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| 
 | |
| net: macb: set default interrupt moderation for GEM hardware
 | |
| 
 | |
| Defaulting to intmod = 0 is antisocial, as the MAC can generate over
 | |
| 130,000 interrupts per second. 50us is a sensible default.
 | |
| 
 | |
| Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
 | |
| ---
 | |
|  drivers/net/ethernet/cadence/macb.h      |  25 ++++
 | |
|  drivers/net/ethernet/cadence/macb_main.c | 151 ++++++++++++++++++++++-
 | |
|  2 files changed, 174 insertions(+), 2 deletions(-)
 | |
| 
 | |
| --- a/drivers/net/ethernet/cadence/macb.h
 | |
| +++ b/drivers/net/ethernet/cadence/macb.h
 | |
| @@ -84,6 +84,8 @@
 | |
|  #define GEM_DMACFG		0x0010 /* DMA Configuration */
 | |
|  #define GEM_JML			0x0048 /* Jumbo Max Length */
 | |
|  #define GEM_HS_MAC_CONFIG	0x0050 /* GEM high speed config */
 | |
| +#define GEM_AMP			0x0054 /* AXI Max Pipeline */
 | |
| +#define GEM_INTMOD		0x005c /* Interrupt moderation */
 | |
|  #define GEM_HRB			0x0080 /* Hash Bottom */
 | |
|  #define GEM_HRT			0x0084 /* Hash Top */
 | |
|  #define GEM_SA1B		0x0088 /* Specific1 Bottom */
 | |
| @@ -346,6 +348,21 @@
 | |
|  #define GEM_ADDR64_OFFSET	30 /* Address bus width - 64b or 32b */
 | |
|  #define GEM_ADDR64_SIZE		1
 | |
|  
 | |
| +/* Bitfields in AMP */
 | |
| +#define GEM_AR2R_MAX_PIPE_OFFSET	0  /* Maximum number of outstanding AXI read requests */
 | |
| +#define GEM_AR2R_MAX_PIPE_SIZE		8
 | |
| +#define GEM_AW2W_MAX_PIPE_OFFSET	8  /* Maximum number of outstanding AXI write requests */
 | |
| +#define GEM_AW2W_MAX_PIPE_SIZE		8
 | |
| +#define GEM_AW2B_FILL_OFFSET		16 /* Select wether the max AW2W transactions operates between: */
 | |
| +#define GEM_AW2B_FILL_AW2W		0  /*   0: the AW to W AXI channel */
 | |
| +#define GEM_AW2B_FILL_AW2B		1  /*   1: AW to B channel */
 | |
| +#define GEM_AW2B_FILL_SIZE              1
 | |
| +
 | |
| +/* Bitfields in INTMOD */
 | |
| +#define GEM_RX_MODERATION_OFFSET	0  /* RX interrupt moderation */
 | |
| +#define GEM_RX_MODERATION_SIZE		8
 | |
| +#define GEM_TX_MODERATION_OFFSET	16 /* TX interrupt moderation */
 | |
| +#define GEM_TX_MODERATION_SIZE		8
 | |
|  
 | |
|  /* Bitfields in NSR */
 | |
|  #define MACB_NSR_LINK_OFFSET	0 /* pcs_link_state */
 | |
| @@ -798,6 +815,7 @@
 | |
|  	})
 | |
|  
 | |
|  #define MACB_READ_NSR(bp)	macb_readl(bp, NSR)
 | |
| +#define MACB_READ_TSR(bp)	macb_readl(bp, TSR)
 | |
|  
 | |
|  /* struct macb_dma_desc - Hardware DMA descriptor
 | |
|   * @addr: DMA address of data buffer
 | |
| @@ -1217,6 +1235,7 @@ struct macb_queue {
 | |
|  	dma_addr_t		tx_ring_dma;
 | |
|  	struct work_struct	tx_error_task;
 | |
|  	bool			txubr_pending;
 | |
| +	bool			tx_pending;
 | |
|  	struct napi_struct	napi_tx;
 | |
|  
 | |
|  	dma_addr_t		rx_ring_dma;
 | |
| @@ -1286,9 +1305,15 @@ struct macb {
 | |
|  
 | |
|  	u32			caps;
 | |
|  	unsigned int		dma_burst_length;
 | |
| +	u8			aw2w_max_pipe;
 | |
| +	u8			ar2r_max_pipe;
 | |
| +	bool			use_aw2b_fill;
 | |
|  
 | |
|  	phy_interface_t		phy_interface;
 | |
|  
 | |
| +	struct gpio_desc	*phy_reset_gpio;
 | |
| +	int			phy_reset_ms;
 | |
| +
 | |
|  	/* AT91RM9200 transmit queue (1 on wire + 1 queued) */
 | |
|  	struct macb_tx_skb	rm9200_txq[2];
 | |
|  	unsigned int		max_tx_length;
 | |
| --- a/drivers/net/ethernet/cadence/macb_main.c
 | |
| +++ b/drivers/net/ethernet/cadence/macb_main.c
 | |
| @@ -41,6 +41,9 @@
 | |
|  #include <linux/firmware/xlnx-zynqmp.h>
 | |
|  #include "macb.h"
 | |
|  
 | |
| +static unsigned int txdelay = 35;
 | |
| +module_param(txdelay, uint, 0644);
 | |
| +
 | |
|  /* This structure is only used for MACB on SiFive FU540 devices */
 | |
|  struct sifive_fu540_macb_mgmt {
 | |
|  	void __iomem *reg;
 | |
| @@ -336,7 +339,7 @@ static int macb_mdio_wait_for_idle(struc
 | |
|  	u32 val;
 | |
|  
 | |
|  	return readx_poll_timeout(MACB_READ_NSR, bp, val, val & MACB_BIT(IDLE),
 | |
| -				  1, MACB_MDIO_TIMEOUT);
 | |
| +				  100, MACB_MDIO_TIMEOUT);
 | |
|  }
 | |
|  
 | |
|  static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 | |
| @@ -442,6 +445,19 @@ mdio_pm_exit:
 | |
|  	return status;
 | |
|  }
 | |
|  
 | |
| +static int macb_mdio_reset(struct mii_bus *bus)
 | |
| +{
 | |
| +	struct macb *bp = bus->priv;
 | |
| +
 | |
| +	if (bp->phy_reset_gpio) {
 | |
| +		gpiod_set_value_cansleep(bp->phy_reset_gpio, 1);
 | |
| +		msleep(bp->phy_reset_ms);
 | |
| +		gpiod_set_value_cansleep(bp->phy_reset_gpio, 0);
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  static void macb_init_buffers(struct macb *bp)
 | |
|  {
 | |
|  	struct macb_queue *queue;
 | |
| @@ -915,6 +931,7 @@ static int macb_mii_init(struct macb *bp
 | |
|  	bp->mii_bus->name = "MACB_mii_bus";
 | |
|  	bp->mii_bus->read = &macb_mdio_read;
 | |
|  	bp->mii_bus->write = &macb_mdio_write;
 | |
| +	bp->mii_bus->reset = &macb_mdio_reset;
 | |
|  	snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
 | |
|  		 bp->pdev->name, bp->pdev->id);
 | |
|  	bp->mii_bus->priv = bp;
 | |
| @@ -1584,6 +1601,11 @@ static int macb_rx(struct macb_queue *qu
 | |
|  
 | |
|  		macb_init_rx_ring(queue);
 | |
|  		queue_writel(queue, RBQP, queue->rx_ring_dma);
 | |
| +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
 | |
| +		if (bp->hw_dma_cap & HW_DMA_CAP_64B)
 | |
| +			macb_writel(bp, RBQPH,
 | |
| +				    upper_32_bits(queue->rx_ring_dma));
 | |
| +#endif
 | |
|  
 | |
|  		macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
 | |
|  
 | |
| @@ -1884,8 +1906,9 @@ static irqreturn_t macb_interrupt(int ir
 | |
|  				queue_writel(queue, ISR, MACB_BIT(TCOMP) |
 | |
|  							 MACB_BIT(TXUBR));
 | |
|  
 | |
| -			if (status & MACB_BIT(TXUBR)) {
 | |
| +			if (status & MACB_BIT(TXUBR) || queue->tx_pending) {
 | |
|  				queue->txubr_pending = true;
 | |
| +				queue->tx_pending = 0;
 | |
|  				wmb(); // ensure softirq can see update
 | |
|  			}
 | |
|  
 | |
| @@ -2332,6 +2355,11 @@ static netdev_tx_t macb_start_xmit(struc
 | |
|  	skb_tx_timestamp(skb);
 | |
|  
 | |
|  	spin_lock_irq(&bp->lock);
 | |
| +
 | |
| +	/* TSTART write might get dropped, so make the IRQ retrigger a buffer read */
 | |
| +	if (macb_readl(bp, TSR) & MACB_BIT(TGO))
 | |
| +		queue->tx_pending = 1;
 | |
| +
 | |
|  	macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
 | |
|  	spin_unlock_irq(&bp->lock);
 | |
|  
 | |
| @@ -2699,6 +2727,37 @@ static void macb_configure_dma(struct ma
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| +static void gem_init_axi(struct macb *bp)
 | |
| +{
 | |
| +	u32 amp;
 | |
| +
 | |
| +	/* AXI pipeline setup - don't touch values unless specified in device
 | |
| +	 * tree. Some hardware could have reset values > 1.
 | |
| +	 */
 | |
| +	amp = gem_readl(bp, AMP);
 | |
| +
 | |
| +	if (bp->use_aw2b_fill)
 | |
| +		amp = GEM_BFINS(AW2B_FILL, bp->use_aw2b_fill, amp);
 | |
| +	if (bp->aw2w_max_pipe)
 | |
| +		amp = GEM_BFINS(AW2W_MAX_PIPE, bp->aw2w_max_pipe, amp);
 | |
| +	if (bp->ar2r_max_pipe)
 | |
| +		amp = GEM_BFINS(AR2R_MAX_PIPE, bp->ar2r_max_pipe, amp);
 | |
| +
 | |
| +	gem_writel(bp, AMP, amp);
 | |
| +}
 | |
| +
 | |
| +static void gem_init_intmod(struct macb *bp)
 | |
| +{
 | |
| +	unsigned int throttle;
 | |
| +	u32 intmod = 0;
 | |
| +
 | |
| +	/* Use sensible interrupt moderation thresholds (50us rx and tx) */
 | |
| +	throttle = (1000 * 50) / 800;
 | |
| +	intmod = GEM_BFINS(TX_MODERATION, throttle, intmod);
 | |
| +	intmod = GEM_BFINS(RX_MODERATION, throttle, intmod);
 | |
| +	gem_writel(bp, INTMOD, intmod);
 | |
| +}
 | |
| +
 | |
|  static void macb_init_hw(struct macb *bp)
 | |
|  {
 | |
|  	u32 config;
 | |
| @@ -2727,6 +2786,11 @@ static void macb_init_hw(struct macb *bp
 | |
|  	if (bp->caps & MACB_CAPS_JUMBO)
 | |
|  		bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK;
 | |
|  
 | |
| +	if (macb_is_gem(bp)) {
 | |
| +		gem_init_axi(bp);
 | |
| +		gem_init_intmod(bp);
 | |
| +	}
 | |
| +
 | |
|  	macb_configure_dma(bp);
 | |
|  }
 | |
|  
 | |
| @@ -3072,6 +3136,52 @@ static void gem_get_ethtool_strings(stru
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| +static int gem_set_coalesce(struct net_device *dev,
 | |
| +			    struct ethtool_coalesce *ec,
 | |
| +			    struct kernel_ethtool_coalesce *kernel_coal,
 | |
| +			    struct netlink_ext_ack *extack)
 | |
| +{
 | |
| +	struct macb *bp = netdev_priv(dev);
 | |
| +	unsigned int tx_throttle;
 | |
| +	unsigned int rx_throttle;
 | |
| +	u32 intmod = 0;
 | |
| +
 | |
| +	/* GEM has simple IRQ throttling support. RX and TX interrupts
 | |
| +	 * are separately moderated on 800ns quantums, with no support
 | |
| +	 * for frame coalescing.
 | |
| +	 */
 | |
| +
 | |
| +	/* Max is 255 * 0.8us = 204us. Zero implies no moderation. */
 | |
| +	if (ec->rx_coalesce_usecs > 204 || ec->tx_coalesce_usecs > 204)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	tx_throttle = (1000 * ec->tx_coalesce_usecs) / 800;
 | |
| +	rx_throttle = (1000 * ec->rx_coalesce_usecs) / 800;
 | |
| +
 | |
| +	intmod = GEM_BFINS(TX_MODERATION, tx_throttle, intmod);
 | |
| +	intmod = GEM_BFINS(RX_MODERATION, rx_throttle, intmod);
 | |
| +
 | |
| +	gem_writel(bp, INTMOD, intmod);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int gem_get_coalesce(struct net_device *dev,
 | |
| +			    struct ethtool_coalesce *ec,
 | |
| +			    struct kernel_ethtool_coalesce *kernel_coal,
 | |
| +			    struct netlink_ext_ack *extack)
 | |
| +{
 | |
| +	struct macb *bp = netdev_priv(dev);
 | |
| +	u32 intmod;
 | |
| +
 | |
| +	intmod = gem_readl(bp, INTMOD);
 | |
| +
 | |
| +	ec->tx_coalesce_usecs = (GEM_BFEXT(TX_MODERATION, intmod) * 800) / 1000;
 | |
| +	ec->rx_coalesce_usecs = (GEM_BFEXT(RX_MODERATION, intmod) * 800) / 1000;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  static struct net_device_stats *macb_get_stats(struct net_device *dev)
 | |
|  {
 | |
|  	struct macb *bp = netdev_priv(dev);
 | |
| @@ -3664,6 +3774,8 @@ static const struct ethtool_ops macb_eth
 | |
|  };
 | |
|  
 | |
|  static const struct ethtool_ops gem_ethtool_ops = {
 | |
| +	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
 | |
| +				     ETHTOOL_COALESCE_TX_USECS,
 | |
|  	.get_regs_len		= macb_get_regs_len,
 | |
|  	.get_regs		= macb_get_regs,
 | |
|  	.get_wol		= macb_get_wol,
 | |
| @@ -3673,6 +3785,8 @@ static const struct ethtool_ops gem_etht
 | |
|  	.get_ethtool_stats	= gem_get_ethtool_stats,
 | |
|  	.get_strings		= gem_get_ethtool_strings,
 | |
|  	.get_sset_count		= gem_get_sset_count,
 | |
| +	.get_coalesce		= gem_get_coalesce,
 | |
| +	.set_coalesce		= gem_set_coalesce,
 | |
|  	.get_link_ksettings     = macb_get_link_ksettings,
 | |
|  	.set_link_ksettings     = macb_set_link_ksettings,
 | |
|  	.get_ringparam		= macb_get_ringparam,
 | |
| @@ -4940,6 +5054,10 @@ static int macb_probe(struct platform_de
 | |
|  
 | |
|  	bp->usrio = macb_config->usrio;
 | |
|  
 | |
| +	device_property_read_u8(&pdev->dev, "cdns,aw2w-max-pipe", &bp->aw2w_max_pipe);
 | |
| +	device_property_read_u8(&pdev->dev, "cdns,ar2r-max-pipe", &bp->ar2r_max_pipe);
 | |
| +	bp->use_aw2b_fill = device_property_read_bool(&pdev->dev, "cdns,use-aw2b-fill");
 | |
| +
 | |
|  	spin_lock_init(&bp->lock);
 | |
|  
 | |
|  	/* setup capabilities */
 | |
| @@ -4995,6 +5113,21 @@ static int macb_probe(struct platform_de
 | |
|  	else
 | |
|  		bp->phy_interface = interface;
 | |
|  
 | |
| +	/* optional PHY reset-related properties */
 | |
| +	bp->phy_reset_gpio = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
 | |
| +						     GPIOD_OUT_LOW);
 | |
| +	if (IS_ERR(bp->phy_reset_gpio)) {
 | |
| +		dev_err(&pdev->dev, "Failed to obtain phy-reset gpio\n");
 | |
| +		err = PTR_ERR(bp->phy_reset_gpio);
 | |
| +		goto err_out_free_netdev;
 | |
| +	}
 | |
| +
 | |
| +	bp->phy_reset_ms = 10;
 | |
| +	of_property_read_u32(np, "phy-reset-duration", &bp->phy_reset_ms);
 | |
| +	/* A sane reset duration should not be longer than 1s */
 | |
| +	if (bp->phy_reset_ms > 1000)
 | |
| +		bp->phy_reset_ms = 1000;
 | |
| +
 | |
|  	/* IP specific init */
 | |
|  	err = init(pdev);
 | |
|  	if (err)
 | |
| @@ -5071,6 +5204,19 @@ static int macb_remove(struct platform_d
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +static void macb_shutdown(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct net_device *dev;
 | |
| +
 | |
| +	dev = platform_get_drvdata(pdev);
 | |
| +
 | |
| +	rtnl_lock();
 | |
| +	netif_device_detach(dev);
 | |
| +	if (netif_running(dev))
 | |
| +		dev_close(dev);
 | |
| +	rtnl_unlock();
 | |
| +}
 | |
| +
 | |
|  static int __maybe_unused macb_suspend(struct device *dev)
 | |
|  {
 | |
|  	struct net_device *netdev = dev_get_drvdata(dev);
 | |
| @@ -5285,6 +5431,7 @@ static const struct dev_pm_ops macb_pm_o
 | |
|  static struct platform_driver macb_driver = {
 | |
|  	.probe		= macb_probe,
 | |
|  	.remove		= macb_remove,
 | |
| +	.shutdown	= macb_shutdown,
 | |
|  	.driver		= {
 | |
|  		.name		= "macb",
 | |
|  		.of_match_table	= of_match_ptr(macb_dt_ids),
 |