[PATCH 1/2] net: ti: cpsw-mdio: Add workaround for errata i2329

Ramon Fried rfried.dev at gmail.com
Wed Oct 5 04:06:42 CEST 2022


On Thu, Sep 22, 2022 at 12:51 PM Ravi Gunasekaran <r-gunasekaran at ti.com> wrote:
>
> In certain TI SoCs, on the CPSW and ICSS peripherals, there is
> a possibility that the MDIO interface returns corrupt data on
> MDIO reads or writes incorrect data on MDIO writes. There is also
> a possibility for the MDIO interface to become unavailable until
> the next peripheral reset.
>
> The workaround is to configure the MDIO in manual mode and disable the
> MDIO state machine and emulate the MDIO protocol by reading and writing
> appropriate fields in MDIO_MANUAL_IF_REG register of the MDIO controller
> to manipulate the MDIO clock and data pins.
>
> More details about the errata i2329 and the workaround is available in:
> https://www.ti.com/lit/er/sprz487a/sprz487a.pdf
>
> Add implementation to disable MDIO state machine, configure MDIO in manual
> mode and provide software MDIO read and writes via MDIO bitbanging. Allow
> the MDIO to be initialized based on the need for manual mode.
>
> Signed-off-by: Ravi Gunasekaran <r-gunasekaran at ti.com>
> ---
>  drivers/net/ti/am65-cpsw-nuss.c |   3 +-
>  drivers/net/ti/cpsw.c           |   3 +-
>  drivers/net/ti/cpsw_mdio.c      | 255 +++++++++++++++++++++++++++++++-
>  drivers/net/ti/cpsw_mdio.h      |   2 +-
>  drivers/net/ti/keystone_net.c   |   3 +-
>  5 files changed, 258 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c
> index 9580fa37ea..29c0422961 100644
> --- a/drivers/net/ti/am65-cpsw-nuss.c
> +++ b/drivers/net/ti/am65-cpsw-nuss.c
> @@ -552,7 +552,8 @@ static int am65_cpsw_mdio_init(struct udevice *dev)
>         cpsw_common->bus = cpsw_mdio_init(dev->name,
>                                           cpsw_common->mdio_base,
>                                           cpsw_common->bus_freq,
> -                                         clk_get_rate(&cpsw_common->fclk));
> +                                         clk_get_rate(&cpsw_common->fclk),
> +                                         false);
>         if (!cpsw_common->bus)
>                 return -EFAULT;
>
> diff --git a/drivers/net/ti/cpsw.c b/drivers/net/ti/cpsw.c
> index 8988c21e66..41cba7930d 100644
> --- a/drivers/net/ti/cpsw.c
> +++ b/drivers/net/ti/cpsw.c
> @@ -922,7 +922,8 @@ int _cpsw_register(struct cpsw_priv *priv)
>                 idx = idx + 1;
>         }
>
> -       priv->bus = cpsw_mdio_init(priv->dev->name, data->mdio_base, 0, 0);
> +       priv->bus = cpsw_mdio_init(priv->dev->name, data->mdio_base, 0, 0,
> +                                  false);
>         if (!priv->bus)
>                 return -EFAULT;
>
> diff --git a/drivers/net/ti/cpsw_mdio.c b/drivers/net/ti/cpsw_mdio.c
> index f4cb86d10a..a5ba73b739 100644
> --- a/drivers/net/ti/cpsw_mdio.c
> +++ b/drivers/net/ti/cpsw_mdio.c
> @@ -23,6 +23,11 @@ struct cpsw_mdio_regs {
>  #define CONTROL_FAULT_ENABLE   BIT(18)
>  #define CONTROL_DIV_MASK       GENMASK(15, 0)
>
> +#define MDIO_MAN_MDCLK_O        BIT(2)
> +#define MDIO_MAN_OE             BIT(1)
> +#define MDIO_MAN_PIN            BIT(0)
> +#define MDIO_MANUALMODE         BIT(31)
> +
>         u32     alive;
>         u32     link;
>         u32     linkintraw;
> @@ -32,7 +37,9 @@ struct cpsw_mdio_regs {
>         u32     userintmasked;
>         u32     userintmaskset;
>         u32     userintmaskclr;
> -       u32     __reserved_1[20];
> +       u32     manualif;
> +       u32     poll;
> +       u32     __reserved_1[18];
>
>         struct {
>                 u32             access;
> @@ -51,6 +58,13 @@ struct cpsw_mdio_regs {
>  #define PHY_REG_MASK           0x1f
>  #define PHY_ID_MASK            0x1f
>
> +#define MDIO_BITRANGE           0x8000
> +#define C22_READ_PATTERN        0x6
> +#define C22_WRITE_PATTERN       0x5
> +#define C22_BITRANGE            0x8
> +#define PHY_BITRANGE            0x10
> +#define PHY_DATA_BITRANGE       0x8000
> +
>  /*
>   * This timeout definition is a worst-case ultra defensive measure against
>   * unexpected controller lock ups.  Ideally, we should never ever hit this
> @@ -58,12 +72,239 @@ struct cpsw_mdio_regs {
>   */
>  #define CPSW_MDIO_TIMEOUT            100 /* msecs */
>
> +enum cpsw_mdio_manual {
> +       MDIO_PIN = 0,
> +       MDIO_OE,
> +       MDIO_MDCLK,
> +};
> +
>  struct cpsw_mdio {
>         struct cpsw_mdio_regs *regs;
>         struct mii_dev *bus;
>         int div;
>  };
>
> +static void cpsw_mdio_disable(struct cpsw_mdio *mdio)
> +{
> +       u32 reg;
> +       /* Disable MDIO state machine */
> +       reg = readl(&mdio->regs->control);
> +       reg &= ~CONTROL_ENABLE;
> +
> +       writel(reg, &mdio->regs->control);
> +}
> +
> +static void cpsw_mdio_enable_manual_mode(struct cpsw_mdio *mdio)
> +{
> +       u32 reg;
> +
> +       /* set manual mode */
> +       reg = readl(&mdio->regs->poll);
> +       reg |= MDIO_MANUALMODE;
> +
> +       writel(reg, &mdio->regs->poll);
> +}
> +
> +static void cpsw_mdio_sw_set_bit(struct cpsw_mdio *mdio,
> +                                enum cpsw_mdio_manual bit)
> +{
> +       u32 reg;
> +
> +       reg = readl(&mdio->regs->manualif);
> +
> +       switch (bit) {
> +       case MDIO_OE:
> +               reg |= MDIO_MAN_OE;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       case MDIO_PIN:
> +               reg |= MDIO_MAN_PIN;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       case MDIO_MDCLK:
> +               reg |= MDIO_MAN_MDCLK_O;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       default:
> +               break;
> +       };
> +}
> +
> +static void cpsw_mdio_sw_clr_bit(struct cpsw_mdio *mdio,
> +                                enum cpsw_mdio_manual bit)
> +{
> +       u32 reg;
> +
> +       reg = readl(&mdio->regs->manualif);
> +
> +       switch (bit) {
> +       case MDIO_OE:
> +               reg &= ~MDIO_MAN_OE;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       case MDIO_PIN:
> +               reg &= ~MDIO_MAN_PIN;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       case MDIO_MDCLK:
> +               reg = readl(&mdio->regs->manualif);
> +               reg &= ~MDIO_MAN_MDCLK_O;
> +               writel(reg, &mdio->regs->manualif);
> +               break;
> +       default:
> +               break;
> +       };
> +}
> +
> +static int cpsw_mdio_test_man_bit(struct cpsw_mdio *mdio,
> +                                 enum cpsw_mdio_manual bit)
> +{
> +       u32 reg;
> +
> +       reg = readl(&mdio->regs->manualif);
> +       return test_bit(bit, &reg);
> +}
> +
> +static void cpsw_mdio_toggle_man_bit(struct cpsw_mdio *mdio,
> +                                    enum cpsw_mdio_manual bit)
> +{
> +       cpsw_mdio_sw_clr_bit(mdio, bit);
> +       cpsw_mdio_sw_set_bit(mdio, bit);
> +}
> +
> +static void cpsw_mdio_man_send_pattern(struct cpsw_mdio *mdio,
> +                                      u32 bitrange, u32 val)
> +{
> +       u32 i;
> +
> +       for (i = bitrange; i; i = i >> 1) {
> +               if (i & val)
> +                       cpsw_mdio_sw_set_bit(mdio, MDIO_PIN);
> +               else
> +                       cpsw_mdio_sw_clr_bit(mdio, MDIO_PIN);
> +
> +               cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +       }
> +}
> +
> +static void cpsw_mdio_sw_preamble(struct cpsw_mdio *mdio)
> +{
> +       u32 i;
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
> +
> +       for (i = 0; i < 32; i++) {
> +               cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +               cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +               cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +               cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +       }
> +}
> +
> +static int cpsw_mdio_sw_read(struct mii_dev *bus, int phy_id,
> +                            int dev_addr, int phy_reg)
> +{
> +       struct cpsw_mdio *mdio = bus->priv;
> +       u32 reg, i;
> +       u8 ack;
> +
> +       if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
> +               return -EINVAL;
> +
> +       cpsw_mdio_disable(mdio);
> +       cpsw_mdio_enable_manual_mode(mdio);
> +       cpsw_mdio_sw_preamble(mdio);
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_OE);
> +
> +       /* Issue clause 22 MII read function {0,1,1,0} */
> +       cpsw_mdio_man_send_pattern(mdio, C22_BITRANGE, C22_READ_PATTERN);
> +
> +       /* Send the device number MSB first */
> +       cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_id);
> +
> +       /* Send the register number MSB first */
> +       cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_reg);
> +
> +       /* Send turn around cycles */
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
> +
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +       ack = cpsw_mdio_test_man_bit(mdio, MDIO_PIN);
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +       reg = 0;
> +       if (ack == 0) {
> +               for (i = MDIO_BITRANGE; i; i = i >> 1) {
> +                       if (cpsw_mdio_test_man_bit(mdio, MDIO_PIN))
> +                               reg |= i;
> +
> +                       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +               }
> +       } else {
> +               for (i = MDIO_BITRANGE; i; i = i >> 1)
> +                       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +               reg = 0xFFFF;
> +       }
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +       return reg;
> +}
> +
> +static int cpsw_mdio_sw_write(struct mii_dev *bus, int phy_id,
> +                             int dev_addr, int phy_reg, u16 phy_data)
> +{
> +       struct cpsw_mdio *mdio = bus->priv;
> +
> +       if ((phy_reg & ~PHY_REG_MASK) || (phy_id & ~PHY_ID_MASK))
> +               return -EINVAL;
> +
> +       cpsw_mdio_disable(mdio);
> +       cpsw_mdio_enable_manual_mode(mdio);
> +       cpsw_mdio_sw_preamble(mdio);
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_OE);
> +
> +       /* Issue clause 22 MII write function {0,1,0,1} */
> +       cpsw_mdio_man_send_pattern(mdio, C22_BITRANGE, C22_WRITE_PATTERN);
> +
> +       /* Send the device number MSB first */
> +       cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_id);
> +
> +       /* Send the register number MSB first */
> +       cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_reg);
> +
> +       /* set turn-around cycles */
> +       cpsw_mdio_sw_set_bit(mdio, MDIO_PIN);
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_PIN);
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +       /* Send Register data MSB first */
> +       cpsw_mdio_man_send_pattern(mdio, PHY_DATA_BITRANGE, phy_data);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
> +
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
> +       cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
> +
> +       return 0;
> +}
> +
>  /* wait until hardware is ready for another user access */
>  static int cpsw_mdio_wait_for_user_access(struct cpsw_mdio *mdio)
>  {
> @@ -130,7 +371,7 @@ u32 cpsw_mdio_get_alive(struct mii_dev *bus)
>  }
>
>  struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
> -                              u32 bus_freq, int fck_freq)
> +                              u32 bus_freq, int fck_freq, bool manual_mode)
>  {
>         struct cpsw_mdio *cpsw_mdio;
>         int ret;
> @@ -172,8 +413,14 @@ struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
>          */
>         mdelay(1);
>
> -       cpsw_mdio->bus->read = cpsw_mdio_read;
> -       cpsw_mdio->bus->write = cpsw_mdio_write;
> +       if (manual_mode) {
> +               cpsw_mdio->bus->read = cpsw_mdio_sw_read;
> +               cpsw_mdio->bus->write = cpsw_mdio_sw_write;
> +       } else {
> +               cpsw_mdio->bus->read = cpsw_mdio_read;
> +               cpsw_mdio->bus->write = cpsw_mdio_write;
> +       }
> +
>         cpsw_mdio->bus->priv = cpsw_mdio;
>         snprintf(cpsw_mdio->bus->name, sizeof(cpsw_mdio->bus->name), name);
>
> diff --git a/drivers/net/ti/cpsw_mdio.h b/drivers/net/ti/cpsw_mdio.h
> index dbf4a2dcac..9b98763656 100644
> --- a/drivers/net/ti/cpsw_mdio.h
> +++ b/drivers/net/ti/cpsw_mdio.h
> @@ -11,7 +11,7 @@
>  struct cpsw_mdio;
>
>  struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
> -                              u32 bus_freq, int fck_freq);
> +                              u32 bus_freq, int fck_freq, bool manual_mode);
>  void cpsw_mdio_free(struct mii_dev *bus);
>  u32 cpsw_mdio_get_alive(struct mii_dev *bus);
>
> diff --git a/drivers/net/ti/keystone_net.c b/drivers/net/ti/keystone_net.c
> index fbec69f571..1bdbd599d7 100644
> --- a/drivers/net/ti/keystone_net.c
> +++ b/drivers/net/ti/keystone_net.c
> @@ -571,7 +571,8 @@ static int ks2_eth_probe(struct udevice *dev)
>                 mdio_bus = cpsw_mdio_init("ethernet-mdio",
>                                           priv->mdio_base,
>                                           EMAC_MDIO_CLOCK_FREQ,
> -                                         EMAC_MDIO_BUS_FREQ);
> +                                         EMAC_MDIO_BUS_FREQ,
> +                                         false);
>                 if (!mdio_bus) {
>                         pr_err("MDIO alloc failed\n");
>                         return -ENOMEM;
> --
> 2.17.1
>
Reviewed-by: Ramon Fried <rfried.dev at gmail.com>


More information about the U-Boot mailing list