diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index a9ab92e3d1a9..1e2302799f8f 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -70,9 +70,11 @@ /* Lane a Tx Reset Control Register */ #define LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) -#define LNaTRSTCTL_HLT_REQ BIT(27) -#define LNaTRSTCTL_RST_DONE BIT(30) #define LNaTRSTCTL_RST_REQ BIT(31) +#define LNaTRSTCTL_RST_DONE BIT(30) +#define LNaTRSTCTL_HLT_REQ BIT(27) +#define LNaTRSTCTL_STP_REQ BIT(26) +#define LNaTRSTCTL_DIS BIT(24) /* Lane a Tx General Control Register */ #define LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) @@ -98,9 +100,11 @@ /* Lane a Rx Reset Control Register */ #define LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) -#define LNaRRSTCTL_HLT_REQ BIT(27) -#define LNaRRSTCTL_RST_DONE BIT(30) #define LNaRRSTCTL_RST_REQ BIT(31) +#define LNaRRSTCTL_RST_DONE BIT(30) +#define LNaRRSTCTL_HLT_REQ BIT(27) +#define LNaRRSTCTL_STP_REQ BIT(26) +#define LNaRRSTCTL_DIS BIT(24) #define LNaRRSTCTL_CDR_LOCK BIT(12) /* Lane a Rx General Control Register */ @@ -255,6 +259,9 @@ #define LYNX_28G_LANE_RESET_SLEEP_US 100 #define LYNX_28G_LANE_RESET_TIMEOUT_US 1000000 +#define LYNX_28G_LANE_STOP_SLEEP_US 100 +#define LYNX_28G_LANE_STOP_TIMEOUT_US 1000000 + enum lynx_28g_eq_type { EQ_TYPE_NO_EQ = 0, EQ_TYPE_2TAP = 1, @@ -615,6 +622,15 @@ static bool lynx_28g_lane_halt_done(struct lynx_28g_lane *lane) !(rrstctl & LNaRRSTCTL_HLT_REQ); } +static bool lynx_28g_lane_stop_done(struct lynx_28g_lane *lane) +{ + u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + u32 rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + + return !(trstctl & LNaTRSTCTL_STP_REQ) && + !(rrstctl & LNaRRSTCTL_STP_REQ); +} + static bool lynx_28g_lane_reset_done(struct lynx_28g_lane *lane) { u32 trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); @@ -624,15 +640,13 @@ static bool lynx_28g_lane_reset_done(struct lynx_28g_lane *lane) (rrstctl & LNaRRSTCTL_RST_DONE); } -static int lynx_28g_power_off(struct phy *phy) +/* Halting puts the lane in a mode in which it can be reconfigured */ +static int lynx_28g_lane_halt(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); bool done; int err; - if (!lane->powered_up) - return 0; - /* Issue a halt request */ lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_HLT_REQ, LNaTRSTCTL_HLT_REQ); @@ -649,20 +663,15 @@ static int lynx_28g_power_off(struct phy *phy) 'A' + lane->id, ERR_PTR(err)); } - lane->powered_up = false; - - return 0; + return err; } -static int lynx_28g_power_on(struct phy *phy) +static int lynx_28g_lane_reset(struct phy *phy) { struct lynx_28g_lane *lane = phy_get_drvdata(phy); bool done; int err; - if (lane->powered_up) - return 0; - /* Issue a reset request on the lane */ lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_RST_REQ, LNaTRSTCTL_RST_REQ); @@ -679,6 +688,61 @@ static int lynx_28g_power_on(struct phy *phy) 'A' + lane->id, ERR_PTR(err)); } + return err; +} + +static int lynx_28g_power_off(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + bool done; + int err; + + if (!lane->powered_up) + return 0; + + /* Issue a stop request */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_STP_REQ, + LNaTRSTCTL_STP_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_STP_REQ, + LNaRRSTCTL_STP_REQ); + + /* Wait until the stop process is complete */ + err = read_poll_timeout(lynx_28g_lane_stop_done, done, done, + LYNX_28G_LANE_STOP_SLEEP_US, + LYNX_28G_LANE_STOP_TIMEOUT_US, + false, lane); + if (err) { + dev_err(&phy->dev, "Lane %c stop failed: %pe\n", + 'A' + lane->id, ERR_PTR(err)); + } + + /* Power down the RX and TX portions of the lane */ + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_DIS, + LNaRRSTCTL_DIS); + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_DIS, + LNaTRSTCTL_DIS); + + lane->powered_up = false; + + return 0; +} + +static int lynx_28g_power_on(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + int err; + + if (lane->powered_up) + return 0; + + /* Power up the RX and TX portions of the lane */ + lynx_28g_lane_rmw(lane, LNaRRSTCTL, 0, LNaRRSTCTL_DIS); + lynx_28g_lane_rmw(lane, LNaTRSTCTL, 0, LNaTRSTCTL_DIS); + + err = lynx_28g_lane_reset(phy); + if (err) + return err; + lane->powered_up = true; return 0; @@ -992,8 +1056,11 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) /* If the lane is powered up, put the lane into the halt state while * the reconfiguration is being done. */ - if (powered_up) - lynx_28g_power_off(phy); + if (powered_up) { + err = lynx_28g_lane_halt(phy); + if (err) + goto out; + } err = lynx_28g_lane_disable_pcvt(lane, lane->mode); if (err) @@ -1007,7 +1074,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) out: if (powered_up) - lynx_28g_power_on(phy); + lynx_28g_lane_reset(phy); return err; }