Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
phy: lynx-28g: serialize concurrent phy_set_mode_ext() calls to share…
Browse files Browse the repository at this point in the history
…d registers

The protocol converter configuration registers PCC8, PCCC, PCCD
(implemented by the driver), as well as others, control protocol
converters from multiple lanes (each represented as a different
struct phy). So, if there are simultaneous calls to phy_set_mode_ext()
to lanes sharing the same PCC register (either for the "old" or for the
"new" protocol), corruption of the values programmed to hardware is
possible, because lynx_28g_rmw() has no locking.

Add a spinlock in the struct lynx_28g_priv shared by all lanes, and take
the global spinlock from the phy_ops :: set_mode() implementation. There
are no other callers which modify PCC registers.

Fixes: 8f73b37 ("phy: add support for the Layerscape SerDes 28G")
Signed-off-by: Vladimir Oltean <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
vladimiroltean authored and davem330 committed Oct 6, 2023
1 parent 0ac87fe commit 139ad11
Showing 1 changed file with 9 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/phy/freescale/phy-fsl-lynx-28g.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ struct lynx_28g_lane {
struct lynx_28g_priv {
void __iomem *base;
struct device *dev;
/* Serialize concurrent access to registers shared between lanes,
* like PCCn
*/
spinlock_t pcc_lock;
struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];

Expand Down Expand Up @@ -397,6 +401,8 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
if (powered_up)
lynx_28g_power_off(phy);

spin_lock(&priv->pcc_lock);

switch (submode) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
Expand All @@ -413,6 +419,8 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
lane->interface = submode;

out:
spin_unlock(&priv->pcc_lock);

/* Power up the lane if necessary */
if (powered_up)
lynx_28g_power_on(phy);
Expand Down Expand Up @@ -596,6 +604,7 @@ static int lynx_28g_probe(struct platform_device *pdev)

dev_set_drvdata(dev, priv);

spin_lock_init(&priv->pcc_lock);
INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);

queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
Expand Down

0 comments on commit 139ad11

Please sign in to comment.