diff mbox

net: ethernet: stmmac: properly set PS bit in MII configurations during reset

Message ID 1493286329-24448-1-git-send-email-thomas.petazzoni@free-electrons.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Thomas Petazzoni April 27, 2017, 9:45 a.m. UTC
On the SPEAr600 SoC, which has the dwmac1000 variant of the IP block,
the DMA reset never succeeds when a MII PHY is used (no problem with a
GMII PHY). The dwmac_dma_reset() function sets the
DMA_BUS_MODE_SFT_RESET bit in the DMA_BUS_MODE register, and then
polls until this bit clears. When a MII PHY is used, with the current
driver, this bit never clears and the driver therefore doesn't work.

The reason is that the PS bit of the GMAC_CONTROL register should be
correctly configured for the DMA reset to work. When the PS bit is 0,
it tells the MAC we have a GMII PHY, when the PS bit is 1, it tells
the MAC we have a MII PHY.

Doing a DMA reset clears all registers, so the PS bit is cleared as
well. This makes the DMA reset work fine with a GMII PHY. However,
with MII PHY, the PS bit should be set.

We have identified this issue thanks to two SPEAr600 platform:

 - One equipped with a GMII PHY, with which the existing driver was
   working fine.

 - One equipped with a MII PHY, where the current driver fails because
   the DMA reset times out.

This patch fixes the problem for the MII PHY configuration, and has
been tested with a GMII PHY configuration as well.

In terms of implement, since the ->reset() hook is implemented in the
DMA related code, we do not want to touch directly from this function
the MAC registers. Therefore, a ->set_ps() hook has been added to
stmmac_ops, which gets called between the moment the reset is asserted
and the polling loop waiting for the reset bit to clear.

In order for this ->set_ps() hook to decide what to do, we pass it the
"struct mac_device_info" so it can access the MAC registers, and the
PHY interface type so it knows if we're using a MII PHY or not.

The ->set_ps() hook is only implemented for the dwmac1000 case.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: <stable@vger.kernel.org>
---
Do not hesitate to suggest ideas for alternative implementations, I'm
not sure if the current proposal is the one that fits best with the
current design of the driver.
---
 drivers/net/ethernet/stmicro/stmmac/common.h         | 12 +++++++++---
 drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 16 ++++++++++++++++
 drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h     |  3 ++-
 drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c     |  7 ++++++-
 drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h      |  3 ++-
 drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c      |  6 +++++-
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c    |  3 ++-
 7 files changed, 42 insertions(+), 8 deletions(-)

Comments

David Miller May 2, 2017, 7 p.m. UTC | #1
Someone needs to review this patch.
Giuseppe CAVALLARO May 3, 2017, 8:13 a.m. UTC | #2
Hello Thomas

this was initially set by using the hw->link.port; both the core_init 
and adjust callback
should invoke the hook and tuning the PS bit according to the speed and 
mode.
So maybe the ->set_ps is superfluous and you could reuse the existent hook

let me know

Regards
peppe

On 4/27/2017 11:45 AM, Thomas Petazzoni wrote:
> On the SPEAr600 SoC, which has the dwmac1000 variant of the IP block,
> the DMA reset never succeeds when a MII PHY is used (no problem with a
> GMII PHY). The dwmac_dma_reset() function sets the
> DMA_BUS_MODE_SFT_RESET bit in the DMA_BUS_MODE register, and then
> polls until this bit clears. When a MII PHY is used, with the current
> driver, this bit never clears and the driver therefore doesn't work.
>
> The reason is that the PS bit of the GMAC_CONTROL register should be
> correctly configured for the DMA reset to work. When the PS bit is 0,
> it tells the MAC we have a GMII PHY, when the PS bit is 1, it tells
> the MAC we have a MII PHY.
>
> Doing a DMA reset clears all registers, so the PS bit is cleared as
> well. This makes the DMA reset work fine with a GMII PHY. However,
> with MII PHY, the PS bit should be set.
>
> We have identified this issue thanks to two SPEAr600 platform:
>
>   - One equipped with a GMII PHY, with which the existing driver was
>     working fine.
>
>   - One equipped with a MII PHY, where the current driver fails because
>     the DMA reset times out.
>
> This patch fixes the problem for the MII PHY configuration, and has
> been tested with a GMII PHY configuration as well.
>
> In terms of implement, since the ->reset() hook is implemented in the
> DMA related code, we do not want to touch directly from this function
> the MAC registers. Therefore, a ->set_ps() hook has been added to
> stmmac_ops, which gets called between the moment the reset is asserted
> and the polling loop waiting for the reset bit to clear.
>
> In order for this ->set_ps() hook to decide what to do, we pass it the
> "struct mac_device_info" so it can access the MAC registers, and the
> PHY interface type so it knows if we're using a MII PHY or not.
>
> The ->set_ps() hook is only implemented for the dwmac1000 case.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: <stable@vger.kernel.org>
> ---
> Do not hesitate to suggest ideas for alternative implementations, I'm
> not sure if the current proposal is the one that fits best with the
> current design of the driver.
> ---
>   drivers/net/ethernet/stmicro/stmmac/common.h         | 12 +++++++++---
>   drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 16 ++++++++++++++++
>   drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h     |  3 ++-
>   drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c     |  7 ++++++-
>   drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h      |  3 ++-
>   drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c      |  6 +++++-
>   drivers/net/ethernet/stmicro/stmmac/stmmac_main.c    |  3 ++-
>   7 files changed, 42 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
> index 04d9245..d576f95 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/common.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/common.h
> @@ -407,10 +407,13 @@ struct stmmac_desc_ops {
>   extern const struct stmmac_desc_ops enh_desc_ops;
>   extern const struct stmmac_desc_ops ndesc_ops;
>   
> +struct mac_device_info;
> +
>   /* Specific DMA helpers */
>   struct stmmac_dma_ops {
>   	/* DMA core initialization */
> -	int (*reset)(void __iomem *ioaddr);
> +	int (*reset)(void __iomem *ioaddr, struct mac_device_info *hw,
> +		     phy_interface_t interface);
>   	void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg,
>   		     u32 dma_tx, u32 dma_rx, int atds);
>   	/* Configure the AXI Bus Mode Register */
> @@ -445,12 +448,15 @@ struct stmmac_dma_ops {
>   	void (*enable_tso)(void __iomem *ioaddr, bool en, u32 chan);
>   };
>   
> -struct mac_device_info;
> -
>   /* Helpers to program the MAC core */
>   struct stmmac_ops {
>   	/* MAC core initialization */
>   	void (*core_init)(struct mac_device_info *hw, int mtu);
> +	/* Set port select. Called between asserting DMA reset and
> +	 * waiting for the reset bit to clear.
> +	 */
> +	void (*set_ps)(struct mac_device_info *hw,
> +		       phy_interface_t interface);
>   	/* Enable and verify that the IPC module is supported */
>   	int (*rx_ipc)(struct mac_device_info *hw);
>   	/* Enable RX Queues */
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> index 19b9b308..dfcbb5b 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> @@ -75,6 +75,21 @@ static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
>   #endif
>   }
>   
> +static void dwmac1000_set_ps(struct mac_device_info *hw,
> +			     phy_interface_t interface)
> +{
> +	void __iomem *ioaddr = hw->pcsr;
> +	u32 value = readl(ioaddr + GMAC_CONTROL);
> +
> +	/* When a MII PHY is used, we must set the PS bit for the DMA
> +	 * reset to succeed.
> +	 */
> +	if (interface == PHY_INTERFACE_MODE_MII)
> +		value |= GMAC_CONTROL_PS;
> +
> +	writel(value, ioaddr + GMAC_CONTROL);
> +}
> +
>   static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
>   {
>   	void __iomem *ioaddr = hw->pcsr;
> @@ -488,6 +503,7 @@ static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
>   
>   static const struct stmmac_ops dwmac1000_ops = {
>   	.core_init = dwmac1000_core_init,
> +	.set_ps = dwmac1000_set_ps,
>   	.rx_ipc = dwmac1000_rx_ipc_enable,
>   	.dump_regs = dwmac1000_dump_regs,
>   	.host_irq_status = dwmac1000_irq_status,
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> index 1b06df7..e9c6c49 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> @@ -183,7 +183,8 @@
>   #define DMA_CHAN0_DBG_STAT_RPS		GENMASK(11, 8)
>   #define DMA_CHAN0_DBG_STAT_RPS_SHIFT	8
>   
> -int dwmac4_dma_reset(void __iomem *ioaddr);
> +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> +		     phy_interface_t interface);
>   void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr);
>   void dwmac4_enable_dma_irq(void __iomem *ioaddr);
>   void dwmac410_enable_dma_irq(void __iomem *ioaddr);
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> index c7326d5..485eecb 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> @@ -14,7 +14,8 @@
>   #include "dwmac4_dma.h"
>   #include "dwmac4.h"
>   
> -int dwmac4_dma_reset(void __iomem *ioaddr)
> +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> +		     phy_interface_t interface)
>   {
>   	u32 value = readl(ioaddr + DMA_BUS_MODE);
>   	int limit;
> @@ -22,6 +23,10 @@ int dwmac4_dma_reset(void __iomem *ioaddr)
>   	/* DMA SW reset */
>   	value |= DMA_BUS_MODE_SFT_RESET;
>   	writel(value, ioaddr + DMA_BUS_MODE);
> +
> +	if (hw->mac->set_ps)
> +		hw->mac->set_ps(hw, interface);
> +
>   	limit = 10;
>   	while (limit--) {
>   		if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> index 56e485f..25ae028 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> @@ -144,6 +144,7 @@ void dwmac_dma_stop_tx(void __iomem *ioaddr);
>   void dwmac_dma_start_rx(void __iomem *ioaddr);
>   void dwmac_dma_stop_rx(void __iomem *ioaddr);
>   int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
> -int dwmac_dma_reset(void __iomem *ioaddr);
> +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> +		    phy_interface_t interface);
>   
>   #endif /* __DWMAC_DMA_H__ */
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> index e60bfca..1a17df5 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> @@ -23,7 +23,8 @@
>   
>   #define GMAC_HI_REG_AE		0x80000000
>   
> -int dwmac_dma_reset(void __iomem *ioaddr)
> +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> +		    phy_interface_t interface)
>   {
>   	u32 value = readl(ioaddr + DMA_BUS_MODE);
>   	int err;
> @@ -32,6 +33,9 @@ int dwmac_dma_reset(void __iomem *ioaddr)
>   	value |= DMA_BUS_MODE_SFT_RESET;
>   	writel(value, ioaddr + DMA_BUS_MODE);
>   
> +	if (hw->mac->set_ps)
> +		hw->mac->set_ps(hw, interface);
> +
>   	err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
>   				 !(value & DMA_BUS_MODE_SFT_RESET),
>   				 100000, 10000);
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 4498a38..66bc218 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -1585,7 +1585,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
>   	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
>   		atds = 1;
>   
> -	ret = priv->hw->dma->reset(priv->ioaddr);
> +	ret = priv->hw->dma->reset(priv->ioaddr, priv->hw,
> +				   priv->plat->interface);
>   	if (ret) {
>   		dev_err(priv->device, "Failed to reset the dma\n");
>   		return ret;
Corentin Labbe May 3, 2017, 2:30 p.m. UTC | #3
On Wed, May 03, 2017 at 10:13:56AM +0200, Giuseppe CAVALLARO wrote:
> Hello Thomas
> 
> this was initially set by using the hw->link.port; both the core_init 
> and adjust callback
> should invoke the hook and tuning the PS bit according to the speed and 
> mode.
> So maybe the ->set_ps is superfluous and you could reuse the existent hook
> 
> let me know
> 
> Regards
> peppe
> 

I just see that GMAC_CONTROL and MAC_CTRL_REG are the same, so why not create a custom adjust_link for each dwmac type ?
This will permit to call it instead of set_ps() and remove lots of if (has_gmac) and co in stmmac_adjust_link()
Basicly replace all between "ctrl = readl()... and writel(ctrl)" by a sot of priv->hw->mac->adjust_link()

It will also help a lot for my dwmac-sun8i inclusion (since I add some if has_sun8i:))

Regards

> On 4/27/2017 11:45 AM, Thomas Petazzoni wrote:
> > On the SPEAr600 SoC, which has the dwmac1000 variant of the IP block,
> > the DMA reset never succeeds when a MII PHY is used (no problem with a
> > GMII PHY). The dwmac_dma_reset() function sets the
> > DMA_BUS_MODE_SFT_RESET bit in the DMA_BUS_MODE register, and then
> > polls until this bit clears. When a MII PHY is used, with the current
> > driver, this bit never clears and the driver therefore doesn't work.
> >
> > The reason is that the PS bit of the GMAC_CONTROL register should be
> > correctly configured for the DMA reset to work. When the PS bit is 0,
> > it tells the MAC we have a GMII PHY, when the PS bit is 1, it tells
> > the MAC we have a MII PHY.
> >
> > Doing a DMA reset clears all registers, so the PS bit is cleared as
> > well. This makes the DMA reset work fine with a GMII PHY. However,
> > with MII PHY, the PS bit should be set.
> >
> > We have identified this issue thanks to two SPEAr600 platform:
> >
> >   - One equipped with a GMII PHY, with which the existing driver was
> >     working fine.
> >
> >   - One equipped with a MII PHY, where the current driver fails because
> >     the DMA reset times out.
> >
> > This patch fixes the problem for the MII PHY configuration, and has
> > been tested with a GMII PHY configuration as well.
> >
> > In terms of implement, since the ->reset() hook is implemented in the
> > DMA related code, we do not want to touch directly from this function
> > the MAC registers. Therefore, a ->set_ps() hook has been added to
> > stmmac_ops, which gets called between the moment the reset is asserted
> > and the polling loop waiting for the reset bit to clear.
> >
> > In order for this ->set_ps() hook to decide what to do, we pass it the
> > "struct mac_device_info" so it can access the MAC registers, and the
> > PHY interface type so it knows if we're using a MII PHY or not.
> >
> > The ->set_ps() hook is only implemented for the dwmac1000 case.
> >
> > Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> > Cc: <stable@vger.kernel.org>
> > ---
> > Do not hesitate to suggest ideas for alternative implementations, I'm
> > not sure if the current proposal is the one that fits best with the
> > current design of the driver.
> > ---
> >   drivers/net/ethernet/stmicro/stmmac/common.h         | 12 +++++++++---
> >   drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 16 ++++++++++++++++
> >   drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h     |  3 ++-
> >   drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c     |  7 ++++++-
> >   drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h      |  3 ++-
> >   drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c      |  6 +++++-
> >   drivers/net/ethernet/stmicro/stmmac/stmmac_main.c    |  3 ++-
> >   7 files changed, 42 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
> > index 04d9245..d576f95 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/common.h
> > +++ b/drivers/net/ethernet/stmicro/stmmac/common.h
> > @@ -407,10 +407,13 @@ struct stmmac_desc_ops {
> >   extern const struct stmmac_desc_ops enh_desc_ops;
> >   extern const struct stmmac_desc_ops ndesc_ops;
> >   
> > +struct mac_device_info;
> > +
> >   /* Specific DMA helpers */
> >   struct stmmac_dma_ops {
> >   	/* DMA core initialization */
> > -	int (*reset)(void __iomem *ioaddr);
> > +	int (*reset)(void __iomem *ioaddr, struct mac_device_info *hw,
> > +		     phy_interface_t interface);
> >   	void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg,
> >   		     u32 dma_tx, u32 dma_rx, int atds);
> >   	/* Configure the AXI Bus Mode Register */
> > @@ -445,12 +448,15 @@ struct stmmac_dma_ops {
> >   	void (*enable_tso)(void __iomem *ioaddr, bool en, u32 chan);
> >   };
> >   
> > -struct mac_device_info;
> > -
> >   /* Helpers to program the MAC core */
> >   struct stmmac_ops {
> >   	/* MAC core initialization */
> >   	void (*core_init)(struct mac_device_info *hw, int mtu);
> > +	/* Set port select. Called between asserting DMA reset and
> > +	 * waiting for the reset bit to clear.
> > +	 */
> > +	void (*set_ps)(struct mac_device_info *hw,
> > +		       phy_interface_t interface);
> >   	/* Enable and verify that the IPC module is supported */
> >   	int (*rx_ipc)(struct mac_device_info *hw);
> >   	/* Enable RX Queues */
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> > index 19b9b308..dfcbb5b 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
> > @@ -75,6 +75,21 @@ static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
> >   #endif
> >   }
> >   
> > +static void dwmac1000_set_ps(struct mac_device_info *hw,
> > +			     phy_interface_t interface)
> > +{
> > +	void __iomem *ioaddr = hw->pcsr;
> > +	u32 value = readl(ioaddr + GMAC_CONTROL);
> > +
> > +	/* When a MII PHY is used, we must set the PS bit for the DMA
> > +	 * reset to succeed.
> > +	 */
> > +	if (interface == PHY_INTERFACE_MODE_MII)
> > +		value |= GMAC_CONTROL_PS;
> > +
> > +	writel(value, ioaddr + GMAC_CONTROL);
> > +}
> > +
> >   static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
> >   {
> >   	void __iomem *ioaddr = hw->pcsr;
> > @@ -488,6 +503,7 @@ static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
> >   
> >   static const struct stmmac_ops dwmac1000_ops = {
> >   	.core_init = dwmac1000_core_init,
> > +	.set_ps = dwmac1000_set_ps,
> >   	.rx_ipc = dwmac1000_rx_ipc_enable,
> >   	.dump_regs = dwmac1000_dump_regs,
> >   	.host_irq_status = dwmac1000_irq_status,
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> > index 1b06df7..e9c6c49 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
> > @@ -183,7 +183,8 @@
> >   #define DMA_CHAN0_DBG_STAT_RPS		GENMASK(11, 8)
> >   #define DMA_CHAN0_DBG_STAT_RPS_SHIFT	8
> >   
> > -int dwmac4_dma_reset(void __iomem *ioaddr);
> > +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> > +		     phy_interface_t interface);
> >   void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr);
> >   void dwmac4_enable_dma_irq(void __iomem *ioaddr);
> >   void dwmac410_enable_dma_irq(void __iomem *ioaddr);
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> > index c7326d5..485eecb 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> > @@ -14,7 +14,8 @@
> >   #include "dwmac4_dma.h"
> >   #include "dwmac4.h"
> >   
> > -int dwmac4_dma_reset(void __iomem *ioaddr)
> > +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> > +		     phy_interface_t interface)
> >   {
> >   	u32 value = readl(ioaddr + DMA_BUS_MODE);
> >   	int limit;
> > @@ -22,6 +23,10 @@ int dwmac4_dma_reset(void __iomem *ioaddr)
> >   	/* DMA SW reset */
> >   	value |= DMA_BUS_MODE_SFT_RESET;
> >   	writel(value, ioaddr + DMA_BUS_MODE);
> > +
> > +	if (hw->mac->set_ps)
> > +		hw->mac->set_ps(hw, interface);
> > +
> >   	limit = 10;
> >   	while (limit--) {
> >   		if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> > index 56e485f..25ae028 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
> > @@ -144,6 +144,7 @@ void dwmac_dma_stop_tx(void __iomem *ioaddr);
> >   void dwmac_dma_start_rx(void __iomem *ioaddr);
> >   void dwmac_dma_stop_rx(void __iomem *ioaddr);
> >   int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
> > -int dwmac_dma_reset(void __iomem *ioaddr);
> > +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> > +		    phy_interface_t interface);
> >   
> >   #endif /* __DWMAC_DMA_H__ */
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> > index e60bfca..1a17df5 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
> > @@ -23,7 +23,8 @@
> >   
> >   #define GMAC_HI_REG_AE		0x80000000
> >   
> > -int dwmac_dma_reset(void __iomem *ioaddr)
> > +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
> > +		    phy_interface_t interface)
> >   {
> >   	u32 value = readl(ioaddr + DMA_BUS_MODE);
> >   	int err;
> > @@ -32,6 +33,9 @@ int dwmac_dma_reset(void __iomem *ioaddr)
> >   	value |= DMA_BUS_MODE_SFT_RESET;
> >   	writel(value, ioaddr + DMA_BUS_MODE);
> >   
> > +	if (hw->mac->set_ps)
> > +		hw->mac->set_ps(hw, interface);
> > +
> >   	err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
> >   				 !(value & DMA_BUS_MODE_SFT_RESET),
> >   				 100000, 10000);
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> > index 4498a38..66bc218 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> > @@ -1585,7 +1585,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
> >   	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
> >   		atds = 1;
> >   
> > -	ret = priv->hw->dma->reset(priv->ioaddr);
> > +	ret = priv->hw->dma->reset(priv->ioaddr, priv->hw,
> > +				   priv->plat->interface);
> >   	if (ret) {
> >   		dev_err(priv->device, "Failed to reset the dma\n");
> >   		return ret;
> 
>
Thomas Petazzoni May 3, 2017, 3:11 p.m. UTC | #4
Hello Giuseppe,

On Wed, 3 May 2017 10:13:56 +0200, Giuseppe CAVALLARO wrote:

> this was initially set by using the hw->link.port; both the core_init 
> and adjust callback
> should invoke the hook and tuning the PS bit according to the speed and 
> mode.

But this doesn't work: core_init and adjust callbacks are called too
late / not at the appropriate time.

Here, I need the PS to be set after asserting the DMA reset bit but
*before* polling for the DMA reset bit to clear.

I.e, I need:

int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
                    phy_interface_t interface)
{
        u32 value = readl(ioaddr + DMA_BUS_MODE);
        int limit;

        /* DMA SW reset */
        value |= DMA_BUS_MODE_SFT_RESET;
        writel(value, ioaddr + DMA_BUS_MODE);

        /* ===> PS must be set here when the PHY interface is MII */

        limit = 10;
        while (limit--) {
                if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
                        break;
                mdelay(10);
        }

        if (limit < 0)
                return -EBUSY;

        return 0;
}

Setting PS *before* asserting the DMA reset bit doesn't work, because
asserting the DMA reset bit clears all bits in all registers.

Setting PS *after* waiting for the DMA reset bit to clear doesn't work,
because this bit never clears if the PS configuration is not correct
with the regard to the PHY being used (which was my original problem).

Am I missing something here?

Best regards,

Thomas Petazzoni
Giuseppe CAVALLARO May 8, 2017, 2:28 p.m. UTC | #5
Hello

On 5/3/2017 4:30 PM, Corentin Labbe wrote:
> On Wed, May 03, 2017 at 10:13:56AM +0200, Giuseppe CAVALLARO wrote:
>> Hello Thomas
>>
>> this was initially set by using the hw->link.port; both the core_init
>> and adjust callback
>> should invoke the hook and tuning the PS bit according to the speed and
>> mode.
>> So maybe the ->set_ps is superfluous and you could reuse the existent hook
>>
>> let me know
>>
>> Regards
>> peppe
>>
> I just see that GMAC_CONTROL and MAC_CTRL_REG are the same, so why not create a custom adjust_link for each dwmac type ?
> This will permit to call it instead of set_ps() and remove lots of if (has_gmac) and co in stmmac_adjust_link()
> Basicly replace all between "ctrl = readl()... and writel(ctrl)" by a sot of priv->hw->mac->adjust_link()
>
> It will also help a lot for my dwmac-sun8i inclusion (since I add some if has_sun8i:))

Corentin, I think this is a good idea and maybe necessary now that the 
driver is supporting a lot of chips.
In the past it was sufficient to have a adjust link function and a 
stmmac_hw_fix_mac_speed
to invoke dedicated hook shared between MAC10/100 and GMAC inside STM 
platforms.

Thomas, I wonder if you could take a look at the 
priv->plat->fix_mac_speed. This can be used
for setting internal registers too.

Regards
Peppe

>
> Regards
>
>> On 4/27/2017 11:45 AM, Thomas Petazzoni wrote:
>>> On the SPEAr600 SoC, which has the dwmac1000 variant of the IP block,
>>> the DMA reset never succeeds when a MII PHY is used (no problem with a
>>> GMII PHY). The dwmac_dma_reset() function sets the
>>> DMA_BUS_MODE_SFT_RESET bit in the DMA_BUS_MODE register, and then
>>> polls until this bit clears. When a MII PHY is used, with the current
>>> driver, this bit never clears and the driver therefore doesn't work.
>>>
>>> The reason is that the PS bit of the GMAC_CONTROL register should be
>>> correctly configured for the DMA reset to work. When the PS bit is 0,
>>> it tells the MAC we have a GMII PHY, when the PS bit is 1, it tells
>>> the MAC we have a MII PHY.
>>>
>>> Doing a DMA reset clears all registers, so the PS bit is cleared as
>>> well. This makes the DMA reset work fine with a GMII PHY. However,
>>> with MII PHY, the PS bit should be set.
>>>
>>> We have identified this issue thanks to two SPEAr600 platform:
>>>
>>>    - One equipped with a GMII PHY, with which the existing driver was
>>>      working fine.
>>>
>>>    - One equipped with a MII PHY, where the current driver fails because
>>>      the DMA reset times out.
>>>
>>> This patch fixes the problem for the MII PHY configuration, and has
>>> been tested with a GMII PHY configuration as well.
>>>
>>> In terms of implement, since the ->reset() hook is implemented in the
>>> DMA related code, we do not want to touch directly from this function
>>> the MAC registers. Therefore, a ->set_ps() hook has been added to
>>> stmmac_ops, which gets called between the moment the reset is asserted
>>> and the polling loop waiting for the reset bit to clear.
>>>
>>> In order for this ->set_ps() hook to decide what to do, we pass it the
>>> "struct mac_device_info" so it can access the MAC registers, and the
>>> PHY interface type so it knows if we're using a MII PHY or not.
>>>
>>> The ->set_ps() hook is only implemented for the dwmac1000 case.
>>>
>>> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>> Cc: <stable@vger.kernel.org>
>>> ---
>>> Do not hesitate to suggest ideas for alternative implementations, I'm
>>> not sure if the current proposal is the one that fits best with the
>>> current design of the driver.
>>> ---
>>>    drivers/net/ethernet/stmicro/stmmac/common.h         | 12 +++++++++---
>>>    drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 16 ++++++++++++++++
>>>    drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h     |  3 ++-
>>>    drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c     |  7 ++++++-
>>>    drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h      |  3 ++-
>>>    drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c      |  6 +++++-
>>>    drivers/net/ethernet/stmicro/stmmac/stmmac_main.c    |  3 ++-
>>>    7 files changed, 42 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
>>> index 04d9245..d576f95 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/common.h
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/common.h
>>> @@ -407,10 +407,13 @@ struct stmmac_desc_ops {
>>>    extern const struct stmmac_desc_ops enh_desc_ops;
>>>    extern const struct stmmac_desc_ops ndesc_ops;
>>>    
>>> +struct mac_device_info;
>>> +
>>>    /* Specific DMA helpers */
>>>    struct stmmac_dma_ops {
>>>    	/* DMA core initialization */
>>> -	int (*reset)(void __iomem *ioaddr);
>>> +	int (*reset)(void __iomem *ioaddr, struct mac_device_info *hw,
>>> +		     phy_interface_t interface);
>>>    	void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg,
>>>    		     u32 dma_tx, u32 dma_rx, int atds);
>>>    	/* Configure the AXI Bus Mode Register */
>>> @@ -445,12 +448,15 @@ struct stmmac_dma_ops {
>>>    	void (*enable_tso)(void __iomem *ioaddr, bool en, u32 chan);
>>>    };
>>>    
>>> -struct mac_device_info;
>>> -
>>>    /* Helpers to program the MAC core */
>>>    struct stmmac_ops {
>>>    	/* MAC core initialization */
>>>    	void (*core_init)(struct mac_device_info *hw, int mtu);
>>> +	/* Set port select. Called between asserting DMA reset and
>>> +	 * waiting for the reset bit to clear.
>>> +	 */
>>> +	void (*set_ps)(struct mac_device_info *hw,
>>> +		       phy_interface_t interface);
>>>    	/* Enable and verify that the IPC module is supported */
>>>    	int (*rx_ipc)(struct mac_device_info *hw);
>>>    	/* Enable RX Queues */
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
>>> index 19b9b308..dfcbb5b 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
>>> @@ -75,6 +75,21 @@ static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
>>>    #endif
>>>    }
>>>    
>>> +static void dwmac1000_set_ps(struct mac_device_info *hw,
>>> +			     phy_interface_t interface)
>>> +{
>>> +	void __iomem *ioaddr = hw->pcsr;
>>> +	u32 value = readl(ioaddr + GMAC_CONTROL);
>>> +
>>> +	/* When a MII PHY is used, we must set the PS bit for the DMA
>>> +	 * reset to succeed.
>>> +	 */
>>> +	if (interface == PHY_INTERFACE_MODE_MII)
>>> +		value |= GMAC_CONTROL_PS;
>>> +
>>> +	writel(value, ioaddr + GMAC_CONTROL);
>>> +}
>>> +
>>>    static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
>>>    {
>>>    	void __iomem *ioaddr = hw->pcsr;
>>> @@ -488,6 +503,7 @@ static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
>>>    
>>>    static const struct stmmac_ops dwmac1000_ops = {
>>>    	.core_init = dwmac1000_core_init,
>>> +	.set_ps = dwmac1000_set_ps,
>>>    	.rx_ipc = dwmac1000_rx_ipc_enable,
>>>    	.dump_regs = dwmac1000_dump_regs,
>>>    	.host_irq_status = dwmac1000_irq_status,
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>>> index 1b06df7..e9c6c49 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>>> @@ -183,7 +183,8 @@
>>>    #define DMA_CHAN0_DBG_STAT_RPS		GENMASK(11, 8)
>>>    #define DMA_CHAN0_DBG_STAT_RPS_SHIFT	8
>>>    
>>> -int dwmac4_dma_reset(void __iomem *ioaddr);
>>> +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
>>> +		     phy_interface_t interface);
>>>    void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr);
>>>    void dwmac4_enable_dma_irq(void __iomem *ioaddr);
>>>    void dwmac410_enable_dma_irq(void __iomem *ioaddr);
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
>>> index c7326d5..485eecb 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
>>> @@ -14,7 +14,8 @@
>>>    #include "dwmac4_dma.h"
>>>    #include "dwmac4.h"
>>>    
>>> -int dwmac4_dma_reset(void __iomem *ioaddr)
>>> +int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
>>> +		     phy_interface_t interface)
>>>    {
>>>    	u32 value = readl(ioaddr + DMA_BUS_MODE);
>>>    	int limit;
>>> @@ -22,6 +23,10 @@ int dwmac4_dma_reset(void __iomem *ioaddr)
>>>    	/* DMA SW reset */
>>>    	value |= DMA_BUS_MODE_SFT_RESET;
>>>    	writel(value, ioaddr + DMA_BUS_MODE);
>>> +
>>> +	if (hw->mac->set_ps)
>>> +		hw->mac->set_ps(hw, interface);
>>> +
>>>    	limit = 10;
>>>    	while (limit--) {
>>>    		if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
>>> index 56e485f..25ae028 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
>>> @@ -144,6 +144,7 @@ void dwmac_dma_stop_tx(void __iomem *ioaddr);
>>>    void dwmac_dma_start_rx(void __iomem *ioaddr);
>>>    void dwmac_dma_stop_rx(void __iomem *ioaddr);
>>>    int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
>>> -int dwmac_dma_reset(void __iomem *ioaddr);
>>> +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
>>> +		    phy_interface_t interface);
>>>    
>>>    #endif /* __DWMAC_DMA_H__ */
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
>>> index e60bfca..1a17df5 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
>>> @@ -23,7 +23,8 @@
>>>    
>>>    #define GMAC_HI_REG_AE		0x80000000
>>>    
>>> -int dwmac_dma_reset(void __iomem *ioaddr)
>>> +int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
>>> +		    phy_interface_t interface)
>>>    {
>>>    	u32 value = readl(ioaddr + DMA_BUS_MODE);
>>>    	int err;
>>> @@ -32,6 +33,9 @@ int dwmac_dma_reset(void __iomem *ioaddr)
>>>    	value |= DMA_BUS_MODE_SFT_RESET;
>>>    	writel(value, ioaddr + DMA_BUS_MODE);
>>>    
>>> +	if (hw->mac->set_ps)
>>> +		hw->mac->set_ps(hw, interface);
>>> +
>>>    	err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
>>>    				 !(value & DMA_BUS_MODE_SFT_RESET),
>>>    				 100000, 10000);
>>> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
>>> index 4498a38..66bc218 100644
>>> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
>>> @@ -1585,7 +1585,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
>>>    	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
>>>    		atds = 1;
>>>    
>>> -	ret = priv->hw->dma->reset(priv->ioaddr);
>>> +	ret = priv->hw->dma->reset(priv->ioaddr, priv->hw,
>>> +				   priv->plat->interface);
>>>    	if (ret) {
>>>    		dev_err(priv->device, "Failed to reset the dma\n");
>>>    		return ret;
>>
Thomas Petazzoni May 8, 2017, 7:12 p.m. UTC | #6
Hello,

On Mon, 8 May 2017 16:28:21 +0200, Giuseppe CAVALLARO wrote:

> > I just see that GMAC_CONTROL and MAC_CTRL_REG are the same, so why not create a custom adjust_link for each dwmac type ?
> > This will permit to call it instead of set_ps() and remove lots of if (has_gmac) and co in stmmac_adjust_link()
> > Basicly replace all between "ctrl = readl()... and writel(ctrl)" by a sot of priv->hw->mac->adjust_link()
> >
> > It will also help a lot for my dwmac-sun8i inclusion (since I add some if has_sun8i:))  
> 
> Corentin, I think this is a good idea and maybe necessary now that the 
> driver is supporting a lot of chips.
> In the past it was sufficient to have a adjust link function and a 
> stmmac_hw_fix_mac_speed
> to invoke dedicated hook shared between MAC10/100 and GMAC inside STM 
> platforms.
> 
> Thomas, I wonder if you could take a look at the 
> priv->plat->fix_mac_speed. This can be used
> for setting internal registers too.

Once again, this is not called at the right time to fix the issue I'm
seeing with a MII PHY. I need to adjust the PS bit between asserting the
reset and polling for the reset bit to clear.

->fix_mac_speed() is called in the adjust_link() call-back, which is
called way too late.

Please, read again my patch and the description of the problem that I
have sent. But basically, any solution that does not allow to set the
PS bit between asserting the DMA reset bit and polling for it to clear
will not work for MII PHYs.

Best regards,

Thomas Petazzoni
Giuseppe CAVALLARO May 10, 2017, 7:03 a.m. UTC | #7
Hi Thomas

On 5/8/2017 9:12 PM, Thomas Petazzoni wrote:
> Hello,
>
> On Mon, 8 May 2017 16:28:21 +0200, Giuseppe CAVALLARO wrote:
>
>>> I just see that GMAC_CONTROL and MAC_CTRL_REG are the same, so why not create a custom adjust_link for each dwmac type ?
>>> This will permit to call it instead of set_ps() and remove lots of if (has_gmac) and co in stmmac_adjust_link()
>>> Basicly replace all between "ctrl = readl()... and writel(ctrl)" by a sot of priv->hw->mac->adjust_link()
>>>
>>> It will also help a lot for my dwmac-sun8i inclusion (since I add some if has_sun8i:))
>> Corentin, I think this is a good idea and maybe necessary now that the
>> driver is supporting a lot of chips.
>> In the past it was sufficient to have a adjust link function and a
>> stmmac_hw_fix_mac_speed
>> to invoke dedicated hook shared between MAC10/100 and GMAC inside STM
>> platforms.
>>
>> Thomas, I wonder if you could take a look at the
>> priv->plat->fix_mac_speed. This can be used
>> for setting internal registers too.
> Once again, this is not called at the right time to fix the issue I'm
> seeing with a MII PHY. I need to adjust the PS bit between asserting the
> reset and polling for the reset bit to clear.
>
> ->fix_mac_speed() is called in the adjust_link() call-back, which is
> called way too late.
>
> Please, read again my patch and the description of the problem that I
> have sent. But basically, any solution that does not allow to set the
> PS bit between asserting the DMA reset bit and polling for it to clear
> will not work for MII PHYs.

yes your point was clear to me, I was just wondering if we could find an 
easier way
to solve it w/o changing the API, adding  the set_ps and propagating the 
"interface"
inside the DMA reset.

Maybe this could be fixed in the glue-logic in some way. Let me know 
what do you think.

peppe

>
> Best regards,
>
> Thomas Petazzoni
Thomas Petazzoni May 10, 2017, 7:18 a.m. UTC | #8
Hello,

On Wed, 10 May 2017 09:03:12 +0200, Giuseppe CAVALLARO wrote:

> > Please, read again my patch and the description of the problem that I
> > have sent. But basically, any solution that does not allow to set the
> > PS bit between asserting the DMA reset bit and polling for it to clear
> > will not work for MII PHYs.  
> 
> yes your point was clear to me, I was just wondering if we could find an 
> easier way
> to solve it w/o changing the API, adding  the set_ps and propagating the 
> "interface"
> inside the DMA reset.
> 
> Maybe this could be fixed in the glue-logic in some way. Let me know 
> what do you think.

Well, it's more up to you to tell me how you would like this be solved.
We figured out what the problem was, but I don't know well enough the
architecture of the driver to decide how the solution to this problem
should be designed. I made an initial simple proposal to show what is
needed, but I'm definitely open to suggestions.

Best regards,

Thomas
Thomas Petazzoni May 15, 2017, 2:27 p.m. UTC | #9
Hello Giuseppe,

On Wed, 10 May 2017 09:18:17 +0200, Thomas Petazzoni wrote:

> On Wed, 10 May 2017 09:03:12 +0200, Giuseppe CAVALLARO wrote:
> 
> > > Please, read again my patch and the description of the problem that I
> > > have sent. But basically, any solution that does not allow to set the
> > > PS bit between asserting the DMA reset bit and polling for it to clear
> > > will not work for MII PHYs.    
> > 
> > yes your point was clear to me, I was just wondering if we could find an 
> > easier way
> > to solve it w/o changing the API, adding  the set_ps and propagating the 
> > "interface"
> > inside the DMA reset.
> > 
> > Maybe this could be fixed in the glue-logic in some way. Let me know 
> > what do you think.  
> 
> Well, it's more up to you to tell me how you would like this be solved.
> We figured out what the problem was, but I don't know well enough the
> architecture of the driver to decide how the solution to this problem
> should be designed. I made an initial simple proposal to show what is
> needed, but I'm definitely open to suggestions.

Do you have any suggestion on how to move forward with this?

Thanks a lot,

Thomas
Thomas Petazzoni June 25, 2017, 12:32 p.m. UTC | #10
Hello Giuseppe,

On Mon, 15 May 2017 16:27:34 +0200, Thomas Petazzoni wrote:

> On Wed, 10 May 2017 09:18:17 +0200, Thomas Petazzoni wrote:
> 
> > On Wed, 10 May 2017 09:03:12 +0200, Giuseppe CAVALLARO wrote:
> >   
> > > > Please, read again my patch and the description of the problem that I
> > > > have sent. But basically, any solution that does not allow to set the
> > > > PS bit between asserting the DMA reset bit and polling for it to clear
> > > > will not work for MII PHYs.      
> > > 
> > > yes your point was clear to me, I was just wondering if we could find an 
> > > easier way
> > > to solve it w/o changing the API, adding  the set_ps and propagating the 
> > > "interface"
> > > inside the DMA reset.
> > > 
> > > Maybe this could be fixed in the glue-logic in some way. Let me know 
> > > what do you think.    
> > 
> > Well, it's more up to you to tell me how you would like this be solved.
> > We figured out what the problem was, but I don't know well enough the
> > architecture of the driver to decide how the solution to this problem
> > should be designed. I made an initial simple proposal to show what is
> > needed, but I'm definitely open to suggestions.  
> 
> Do you have any suggestion on how to move forward with this?

Another kind ping on this topic. I really would like to have the
SPEAr600 network support work out of the box in mainline, which
currently isn't the case with an MII PHY.

I posted a patch that fixes the problem, see
https://patchwork.ozlabs.org/patch/755926/, but the feedback I got so
far does not give any direction on how to rework the patch to make it
acceptable. Would it be possible to get some more feedback?

Thanks a lot,

Thomas
Giuseppe CAVALLARO June 28, 2017, 2:40 p.m. UTC | #11
Hello Thomas

I do not want to change a critical reset function shared among different 
platforms where
this problem has never met but you are right that we have to find a way 
to proceed in order
to finalize your work. Let me elaborate your initial patch and I try to 
give you a proposal asap.
In my mind, we should have a dedicated spear_dma_reset for your case 
that should be used on
SPEAr platform driver (or by using st,spear600-gmac compatibility).
Also your patch did not consider the RMII and (R)GMII cases.

Regards
Peppe

On 6/25/2017 2:32 PM, Thomas Petazzoni wrote:
> Hello Giuseppe,
>
> On Mon, 15 May 2017 16:27:34 +0200, Thomas Petazzoni wrote:
>
>> On Wed, 10 May 2017 09:18:17 +0200, Thomas Petazzoni wrote:
>>
>>> On Wed, 10 May 2017 09:03:12 +0200, Giuseppe CAVALLARO wrote:
>>>    
>>>>> Please, read again my patch and the description of the problem that I
>>>>> have sent. But basically, any solution that does not allow to set the
>>>>> PS bit between asserting the DMA reset bit and polling for it to clear
>>>>> will not work for MII PHYs.
>>>> yes your point was clear to me, I was just wondering if we could find an
>>>> easier way
>>>> to solve it w/o changing the API, adding  the set_ps and propagating the
>>>> "interface"
>>>> inside the DMA reset.
>>>>
>>>> Maybe this could be fixed in the glue-logic in some way. Let me know
>>>> what do you think.
>>> Well, it's more up to you to tell me how you would like this be solved.
>>> We figured out what the problem was, but I don't know well enough the
>>> architecture of the driver to decide how the solution to this problem
>>> should be designed. I made an initial simple proposal to show what is
>>> needed, but I'm definitely open to suggestions.
>> Do you have any suggestion on how to move forward with this?
> Another kind ping on this topic. I really would like to have the
> SPEAr600 network support work out of the box in mainline, which
> currently isn't the case with an MII PHY.
>
> I posted a patch that fixes the problem, see
> https://patchwork.ozlabs.org/patch/755926/, but the feedback I got so
> far does not give any direction on how to rework the patch to make it
> acceptable. Would it be possible to get some more feedback?
Thomas Petazzoni July 29, 2017, 7:54 p.m. UTC | #12
Hello Giuseppe,

On Wed, 28 Jun 2017 16:40:51 +0200, Giuseppe CAVALLARO wrote:

> I do not want to change a critical reset function shared among
> different platforms where
> this problem has never met but you are right that we have to find a
> way to proceed in order
> to finalize your work. Let me elaborate your initial patch and I try
> to give you a proposal asap.
> In my mind, we should have a dedicated spear_dma_reset for your case 
> that should be used on
> SPEAr platform driver (or by using st,spear600-gmac compatibility).
> Also your patch did not consider the RMII and (R)GMII cases.

Have you had the chance to cook a different proposal? Alternatively, do
you have some specific hints to give me to make a new proposal that
would be acceptable for you ?

Thanks a lot,

Thomas Petazzoni
Giuseppe CAVALLARO Aug. 2, 2017, 12:33 p.m. UTC | #13
Hi Thomas

On 7/29/2017 9:54 PM, Thomas Petazzoni wrote:
> Hello Giuseppe,
>
> On Wed, 28 Jun 2017 16:40:51 +0200, Giuseppe CAVALLARO wrote:
>
>> I do not want to change a critical reset function shared among
>> different platforms where
>> this problem has never met but you are right that we have to find a
>> way to proceed in order
>> to finalize your work. Let me elaborate your initial patch and I try
>> to give you a proposal asap.
>> In my mind, we should have a dedicated spear_dma_reset for your case
>> that should be used on
>> SPEAr platform driver (or by using st,spear600-gmac compatibility).
>> Also your patch did not consider the RMII and (R)GMII cases.
> Have you had the chance to cook a different proposal? Alternatively, do
> you have some specific hints to give me to make a new proposal that
> would be acceptable for you ?

yes you are right and I had no chance to enter in this topic. :-(
We could follow one of the following approaches:

     - add a new small platform driver where you can add ad-hoc code for 
SPEAr.

          Today there is a compatibility for st,spear600-gmac inside the 
dwmac-generic.c

     - introduce a new DT parameter to set the PS bit when resetting the HW.

The latter should be quite easy to implement starting from your original 
patch,
this approach is not intrusive and can help others in case of the same 
behavior is found.

What do you think?

Regards
Peppe

>
> Thanks a lot,
>
> Thomas Petazzoni
diff mbox

Patch

diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 04d9245..d576f95 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -407,10 +407,13 @@  struct stmmac_desc_ops {
 extern const struct stmmac_desc_ops enh_desc_ops;
 extern const struct stmmac_desc_ops ndesc_ops;
 
+struct mac_device_info;
+
 /* Specific DMA helpers */
 struct stmmac_dma_ops {
 	/* DMA core initialization */
-	int (*reset)(void __iomem *ioaddr);
+	int (*reset)(void __iomem *ioaddr, struct mac_device_info *hw,
+		     phy_interface_t interface);
 	void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg,
 		     u32 dma_tx, u32 dma_rx, int atds);
 	/* Configure the AXI Bus Mode Register */
@@ -445,12 +448,15 @@  struct stmmac_dma_ops {
 	void (*enable_tso)(void __iomem *ioaddr, bool en, u32 chan);
 };
 
-struct mac_device_info;
-
 /* Helpers to program the MAC core */
 struct stmmac_ops {
 	/* MAC core initialization */
 	void (*core_init)(struct mac_device_info *hw, int mtu);
+	/* Set port select. Called between asserting DMA reset and
+	 * waiting for the reset bit to clear.
+	 */
+	void (*set_ps)(struct mac_device_info *hw,
+		       phy_interface_t interface);
 	/* Enable and verify that the IPC module is supported */
 	int (*rx_ipc)(struct mac_device_info *hw);
 	/* Enable RX Queues */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 19b9b308..dfcbb5b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -75,6 +75,21 @@  static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
 #endif
 }
 
+static void dwmac1000_set_ps(struct mac_device_info *hw,
+			     phy_interface_t interface)
+{
+	void __iomem *ioaddr = hw->pcsr;
+	u32 value = readl(ioaddr + GMAC_CONTROL);
+
+	/* When a MII PHY is used, we must set the PS bit for the DMA
+	 * reset to succeed.
+	 */
+	if (interface == PHY_INTERFACE_MODE_MII)
+		value |= GMAC_CONTROL_PS;
+
+	writel(value, ioaddr + GMAC_CONTROL);
+}
+
 static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
 {
 	void __iomem *ioaddr = hw->pcsr;
@@ -488,6 +503,7 @@  static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
 
 static const struct stmmac_ops dwmac1000_ops = {
 	.core_init = dwmac1000_core_init,
+	.set_ps = dwmac1000_set_ps,
 	.rx_ipc = dwmac1000_rx_ipc_enable,
 	.dump_regs = dwmac1000_dump_regs,
 	.host_irq_status = dwmac1000_irq_status,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 1b06df7..e9c6c49 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
@@ -183,7 +183,8 @@ 
 #define DMA_CHAN0_DBG_STAT_RPS		GENMASK(11, 8)
 #define DMA_CHAN0_DBG_STAT_RPS_SHIFT	8
 
-int dwmac4_dma_reset(void __iomem *ioaddr);
+int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
+		     phy_interface_t interface);
 void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr);
 void dwmac4_enable_dma_irq(void __iomem *ioaddr);
 void dwmac410_enable_dma_irq(void __iomem *ioaddr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index c7326d5..485eecb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -14,7 +14,8 @@ 
 #include "dwmac4_dma.h"
 #include "dwmac4.h"
 
-int dwmac4_dma_reset(void __iomem *ioaddr)
+int dwmac4_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
+		     phy_interface_t interface)
 {
 	u32 value = readl(ioaddr + DMA_BUS_MODE);
 	int limit;
@@ -22,6 +23,10 @@  int dwmac4_dma_reset(void __iomem *ioaddr)
 	/* DMA SW reset */
 	value |= DMA_BUS_MODE_SFT_RESET;
 	writel(value, ioaddr + DMA_BUS_MODE);
+
+	if (hw->mac->set_ps)
+		hw->mac->set_ps(hw, interface);
+
 	limit = 10;
 	while (limit--) {
 		if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index 56e485f..25ae028 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -144,6 +144,7 @@  void dwmac_dma_stop_tx(void __iomem *ioaddr);
 void dwmac_dma_start_rx(void __iomem *ioaddr);
 void dwmac_dma_stop_rx(void __iomem *ioaddr);
 int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
-int dwmac_dma_reset(void __iomem *ioaddr);
+int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
+		    phy_interface_t interface);
 
 #endif /* __DWMAC_DMA_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index e60bfca..1a17df5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -23,7 +23,8 @@ 
 
 #define GMAC_HI_REG_AE		0x80000000
 
-int dwmac_dma_reset(void __iomem *ioaddr)
+int dwmac_dma_reset(void __iomem *ioaddr, struct mac_device_info *hw,
+		    phy_interface_t interface)
 {
 	u32 value = readl(ioaddr + DMA_BUS_MODE);
 	int err;
@@ -32,6 +33,9 @@  int dwmac_dma_reset(void __iomem *ioaddr)
 	value |= DMA_BUS_MODE_SFT_RESET;
 	writel(value, ioaddr + DMA_BUS_MODE);
 
+	if (hw->mac->set_ps)
+		hw->mac->set_ps(hw, interface);
+
 	err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
 				 !(value & DMA_BUS_MODE_SFT_RESET),
 				 100000, 10000);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 4498a38..66bc218 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1585,7 +1585,8 @@  static int stmmac_init_dma_engine(struct stmmac_priv *priv)
 	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
 		atds = 1;
 
-	ret = priv->hw->dma->reset(priv->ioaddr);
+	ret = priv->hw->dma->reset(priv->ioaddr, priv->hw,
+				   priv->plat->interface);
 	if (ret) {
 		dev_err(priv->device, "Failed to reset the dma\n");
 		return ret;