diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c index 64d0c3dba1cbb9..1178801fe5dcb1 100644 --- a/drivers/phy/apple/atc.c +++ b/drivers/phy/apple/atc.c @@ -922,7 +922,7 @@ static void atcphy_apply_tunables(struct apple_atcphy *atcphy, enum atcphy_mode static int atcphy_pipehandler_lock(struct apple_atcphy *atcphy) { - int ret; + int ret, retries = 10; u32 reg; if (readl(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ) & PIPEHANDLER_LOCK_EN) { @@ -930,15 +930,17 @@ static int atcphy_pipehandler_lock(struct apple_atcphy *atcphy) return 0; } - set32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, PIPEHANDLER_LOCK_EN); - - ret = readl_poll_timeout(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_ACK, reg, - reg & PIPEHANDLER_LOCK_EN, 10, PIPEHANDLER_LOCK_ACK_TIMEOUT_US); - if (ret) { - clear32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, 1); - dev_warn(atcphy->dev, "Pipehandler lock not acked.\n"); - } + do { + set32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, PIPEHANDLER_LOCK_EN); + ret = readl_poll_timeout(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_ACK, reg, + reg & PIPEHANDLER_LOCK_EN, 10, PIPEHANDLER_LOCK_ACK_TIMEOUT_US); + if (!ret) + return 0; + clear32(atcphy->regs.pipehandler + PIPEHANDLER_LOCK_REQ, PIPEHANDLER_LOCK_EN); + usleep_range(5000, 10000); + } while (--retries); + dev_warn(atcphy->dev, "Pipehandler lock not acked.\n"); return ret; } @@ -2081,6 +2083,7 @@ static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *sta { struct apple_atcphy *atcphy = typec_mux_get_drvdata(mux); enum atcphy_mode target_mode; + int ret; guard(mutex)(&atcphy->lock); @@ -2138,11 +2141,31 @@ static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *sta return 0; /* - * If the pipehandler is still/already up here there's a bug somewhere so make sure to - * complain loudly. We can still try to switch modes and hope for the best though, - * in the worst case the hardware will fall back to USB2-only. + * If the pipehandler is still up and the target mode uses the dummy PIPE + * state (DP, USB2, TBT, OFF), tear it down before reconfiguring the lanes. + * Normally atcphy_usb3_power_off() does this, but a TypeC mux switch to + * DP alt mode can race the DWC3 PHY teardown on hotplug. By the time the + * kernel TypeC stack reaches here the USB PD Enter_Mode handshake is + * complete and the SS lanes are already being repurposed, so switching the + * pipehandler to dummy is safe even if DWC3 has not yet called phy_power_off. + */ + if (atcphy->pipehandler_up && + atcphy_modes[target_mode].pipehandler_state == ATCPHY_PIPEHANDLER_STATE_DUMMY) { + ret = atcphy_configure_pipehandler_dummy(atcphy); + if (ret) + dev_warn(atcphy->dev, + "Failed to clear pipehandler before mode switch: %d\n", ret); + else + atcphy->pipehandler_up = false; + } + + /* + * For modes that keep the pipehandler in USB3 state (e.g. USB3_DP), + * pipehandler_up being true here is expected and correct. Only warn + * if we tried to tear down the pipehandler above but failed. */ - WARN_ON_ONCE(atcphy->pipehandler_up); + WARN_ON_ONCE(atcphy->pipehandler_up && + atcphy_modes[target_mode].pipehandler_state == ATCPHY_PIPEHANDLER_STATE_DUMMY); return atcphy_configure(atcphy, target_mode); }