Commit 4767b7e2 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-realtek-phy-read-corruption'

Alvin Šipraga says:

====================
net: dsa: realtek: fix PHY register read corruption

These two patches fix the issue reported by Arınç where PHY register
reads sometimes return garbage data.

v1 -> v2:

- no code changes
- just update the commit message of patch 2 to reflect the conclusion
  of further investigation requested by Vladimir
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents acd8df58 27967284
...@@ -98,6 +98,20 @@ static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) ...@@ -98,6 +98,20 @@ static int realtek_mdio_read(void *ctx, u32 reg, u32 *val)
return ret; return ret;
} }
static void realtek_mdio_lock(void *ctx)
{
struct realtek_priv *priv = ctx;
mutex_lock(&priv->map_lock);
}
static void realtek_mdio_unlock(void *ctx)
{
struct realtek_priv *priv = ctx;
mutex_unlock(&priv->map_lock);
}
static const struct regmap_config realtek_mdio_regmap_config = { static const struct regmap_config realtek_mdio_regmap_config = {
.reg_bits = 10, /* A4..A0 R4..R0 */ .reg_bits = 10, /* A4..A0 R4..R0 */
.val_bits = 16, .val_bits = 16,
...@@ -108,6 +122,21 @@ static const struct regmap_config realtek_mdio_regmap_config = { ...@@ -108,6 +122,21 @@ static const struct regmap_config realtek_mdio_regmap_config = {
.reg_read = realtek_mdio_read, .reg_read = realtek_mdio_read,
.reg_write = realtek_mdio_write, .reg_write = realtek_mdio_write,
.cache_type = REGCACHE_NONE, .cache_type = REGCACHE_NONE,
.lock = realtek_mdio_lock,
.unlock = realtek_mdio_unlock,
};
static const struct regmap_config realtek_mdio_nolock_regmap_config = {
.reg_bits = 10, /* A4..A0 R4..R0 */
.val_bits = 16,
.reg_stride = 1,
/* PHY regs are at 0x8000 */
.max_register = 0xffff,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.reg_read = realtek_mdio_read,
.reg_write = realtek_mdio_write,
.cache_type = REGCACHE_NONE,
.disable_locking = true,
}; };
static int realtek_mdio_probe(struct mdio_device *mdiodev) static int realtek_mdio_probe(struct mdio_device *mdiodev)
...@@ -115,8 +144,9 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) ...@@ -115,8 +144,9 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev)
struct realtek_priv *priv; struct realtek_priv *priv;
struct device *dev = &mdiodev->dev; struct device *dev = &mdiodev->dev;
const struct realtek_variant *var; const struct realtek_variant *var;
int ret; struct regmap_config rc;
struct device_node *np; struct device_node *np;
int ret;
var = of_device_get_match_data(dev); var = of_device_get_match_data(dev);
if (!var) if (!var)
...@@ -126,13 +156,25 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) ...@@ -126,13 +156,25 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->map = devm_regmap_init(dev, NULL, priv, &realtek_mdio_regmap_config); mutex_init(&priv->map_lock);
rc = realtek_mdio_regmap_config;
rc.lock_arg = priv;
priv->map = devm_regmap_init(dev, NULL, priv, &rc);
if (IS_ERR(priv->map)) { if (IS_ERR(priv->map)) {
ret = PTR_ERR(priv->map); ret = PTR_ERR(priv->map);
dev_err(dev, "regmap init failed: %d\n", ret); dev_err(dev, "regmap init failed: %d\n", ret);
return ret; return ret;
} }
rc = realtek_mdio_nolock_regmap_config;
priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
if (IS_ERR(priv->map_nolock)) {
ret = PTR_ERR(priv->map_nolock);
dev_err(dev, "regmap init failed: %d\n", ret);
return ret;
}
priv->mdio_addr = mdiodev->addr; priv->mdio_addr = mdiodev->addr;
priv->bus = mdiodev->bus; priv->bus = mdiodev->bus;
priv->dev = &mdiodev->dev; priv->dev = &mdiodev->dev;
......
...@@ -311,7 +311,21 @@ static int realtek_smi_read(void *ctx, u32 reg, u32 *val) ...@@ -311,7 +311,21 @@ static int realtek_smi_read(void *ctx, u32 reg, u32 *val)
return realtek_smi_read_reg(priv, reg, val); return realtek_smi_read_reg(priv, reg, val);
} }
static const struct regmap_config realtek_smi_mdio_regmap_config = { static void realtek_smi_lock(void *ctx)
{
struct realtek_priv *priv = ctx;
mutex_lock(&priv->map_lock);
}
static void realtek_smi_unlock(void *ctx)
{
struct realtek_priv *priv = ctx;
mutex_unlock(&priv->map_lock);
}
static const struct regmap_config realtek_smi_regmap_config = {
.reg_bits = 10, /* A4..A0 R4..R0 */ .reg_bits = 10, /* A4..A0 R4..R0 */
.val_bits = 16, .val_bits = 16,
.reg_stride = 1, .reg_stride = 1,
...@@ -321,6 +335,21 @@ static const struct regmap_config realtek_smi_mdio_regmap_config = { ...@@ -321,6 +335,21 @@ static const struct regmap_config realtek_smi_mdio_regmap_config = {
.reg_read = realtek_smi_read, .reg_read = realtek_smi_read,
.reg_write = realtek_smi_write, .reg_write = realtek_smi_write,
.cache_type = REGCACHE_NONE, .cache_type = REGCACHE_NONE,
.lock = realtek_smi_lock,
.unlock = realtek_smi_unlock,
};
static const struct regmap_config realtek_smi_nolock_regmap_config = {
.reg_bits = 10, /* A4..A0 R4..R0 */
.val_bits = 16,
.reg_stride = 1,
/* PHY regs are at 0x8000 */
.max_register = 0xffff,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.reg_read = realtek_smi_read,
.reg_write = realtek_smi_write,
.cache_type = REGCACHE_NONE,
.disable_locking = true,
}; };
static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum)
...@@ -385,6 +414,7 @@ static int realtek_smi_probe(struct platform_device *pdev) ...@@ -385,6 +414,7 @@ static int realtek_smi_probe(struct platform_device *pdev)
const struct realtek_variant *var; const struct realtek_variant *var;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct realtek_priv *priv; struct realtek_priv *priv;
struct regmap_config rc;
struct device_node *np; struct device_node *np;
int ret; int ret;
...@@ -395,14 +425,26 @@ static int realtek_smi_probe(struct platform_device *pdev) ...@@ -395,14 +425,26 @@ static int realtek_smi_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->chip_data = (void *)priv + sizeof(*priv); priv->chip_data = (void *)priv + sizeof(*priv);
priv->map = devm_regmap_init(dev, NULL, priv,
&realtek_smi_mdio_regmap_config); mutex_init(&priv->map_lock);
rc = realtek_smi_regmap_config;
rc.lock_arg = priv;
priv->map = devm_regmap_init(dev, NULL, priv, &rc);
if (IS_ERR(priv->map)) { if (IS_ERR(priv->map)) {
ret = PTR_ERR(priv->map); ret = PTR_ERR(priv->map);
dev_err(dev, "regmap init failed: %d\n", ret); dev_err(dev, "regmap init failed: %d\n", ret);
return ret; return ret;
} }
rc = realtek_smi_nolock_regmap_config;
priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc);
if (IS_ERR(priv->map_nolock)) {
ret = PTR_ERR(priv->map_nolock);
dev_err(dev, "regmap init failed: %d\n", ret);
return ret;
}
/* Link forward and backward */ /* Link forward and backward */
priv->dev = dev; priv->dev = dev;
priv->clk_delay = var->clk_delay; priv->clk_delay = var->clk_delay;
......
...@@ -52,6 +52,8 @@ struct realtek_priv { ...@@ -52,6 +52,8 @@ struct realtek_priv {
struct gpio_desc *mdc; struct gpio_desc *mdc;
struct gpio_desc *mdio; struct gpio_desc *mdio;
struct regmap *map; struct regmap *map;
struct regmap *map_nolock;
struct mutex map_lock;
struct mii_bus *slave_mii_bus; struct mii_bus *slave_mii_bus;
struct mii_bus *bus; struct mii_bus *bus;
int mdio_addr; int mdio_addr;
......
...@@ -590,7 +590,7 @@ static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) ...@@ -590,7 +590,7 @@ static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv)
{ {
u32 val; u32 val;
return regmap_read_poll_timeout(priv->map, return regmap_read_poll_timeout(priv->map_nolock,
RTL8365MB_INDIRECT_ACCESS_STATUS_REG, RTL8365MB_INDIRECT_ACCESS_STATUS_REG,
val, !val, 10, 100); val, !val, 10, 100);
} }
...@@ -604,7 +604,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, ...@@ -604,7 +604,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy,
/* Set OCP prefix */ /* Set OCP prefix */
val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr);
ret = regmap_update_bits( ret = regmap_update_bits(
priv->map, RTL8365MB_GPHY_OCP_MSB_0_REG, priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG,
RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK,
FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val));
if (ret) if (ret)
...@@ -617,8 +617,8 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, ...@@ -617,8 +617,8 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy,
ocp_addr >> 1); ocp_addr >> 1);
val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK,
ocp_addr >> 6); ocp_addr >> 6);
ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, ret = regmap_write(priv->map_nolock,
val); RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val);
if (ret) if (ret)
return ret; return ret;
...@@ -631,36 +631,42 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, ...@@ -631,36 +631,42 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy,
u32 val; u32 val;
int ret; int ret;
mutex_lock(&priv->map_lock);
ret = rtl8365mb_phy_poll_busy(priv); ret = rtl8365mb_phy_poll_busy(priv);
if (ret) if (ret)
return ret; goto out;
ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr);
if (ret) if (ret)
return ret; goto out;
/* Execute read operation */ /* Execute read operation */
val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ);
ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
val);
if (ret) if (ret)
return ret; goto out;
ret = rtl8365mb_phy_poll_busy(priv); ret = rtl8365mb_phy_poll_busy(priv);
if (ret) if (ret)
return ret; goto out;
/* Get PHY register data */ /* Get PHY register data */
ret = regmap_read(priv->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, ret = regmap_read(priv->map_nolock,
&val); RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val);
if (ret) if (ret)
return ret; goto out;
*data = val & 0xFFFF; *data = val & 0xFFFF;
return 0; out:
mutex_unlock(&priv->map_lock);
return ret;
} }
static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
...@@ -669,32 +675,38 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, ...@@ -669,32 +675,38 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
u32 val; u32 val;
int ret; int ret;
mutex_lock(&priv->map_lock);
ret = rtl8365mb_phy_poll_busy(priv); ret = rtl8365mb_phy_poll_busy(priv);
if (ret) if (ret)
return ret; goto out;
ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr);
if (ret) if (ret)
return ret; goto out;
/* Set PHY register data */ /* Set PHY register data */
ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, ret = regmap_write(priv->map_nolock,
data); RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data);
if (ret) if (ret)
return ret; goto out;
/* Execute write operation */ /* Execute write operation */
val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE);
ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
val);
if (ret) if (ret)
return ret; goto out;
ret = rtl8365mb_phy_poll_busy(priv); ret = rtl8365mb_phy_poll_busy(priv);
if (ret) if (ret)
return ret; goto out;
out:
mutex_unlock(&priv->map_lock);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment