phy: rockchip: inno-usb2: Fix mismatch id interrupt

Test on RK3588S Tablet, set the power supply of logic on
and set the power supply of usb2 phy off during deep sleep,
then the id falling edge interrupt will be triggered after
system resume, and kernel panic with the following log:

SError Interrupt on CPU0, code 0xbe000011 -- SError
CPU: 0 PID: 1946 Comm: kworker/0:0 Not tainted 5.10.110 #600
Hardware name: Rockchip RK3588S TABLET RK806 SINGLE Board (DT)
Workqueue: events rockchip_usb2phy_otg_sm_work
pstate: 20c00009 (nzCv daif +PAN +UAO -TCO BTYPE=--)
pc : _raw_spin_unlock_irqrestore+0x28/0x60
lr : regmap_unlock_spinlock+0x18/0x28
......
Kernel panic - not syncing: Asynchronous SError Interrupt
CPU: 0 PID: 1946 Comm: kworker/0:0 Not tainted 5.10.110 #600
Hardware name: Rockchip RK3588S TABLET RK806 SINGLE Board (DT)
Workqueue: events rockchip_usb2phy_otg_sm_work
Call trace:
 dump_backtrace+0x0/0x1c8
 show_stack+0x1c/0x2c
 dump_stack_lvl+0xdc/0x12c
 dump_stack+0x1c/0x64
 panic+0x150/0x3a4
 test_taint+0x0/0x30
 arm64_serror_panic+0x78/0x84
 do_serror+0xe0/0x100
 el1_error+0x94/0x118
 _raw_spin_unlock_irqrestore+0x28/0x60
 regmap_unlock_spinlock+0x18/0x28
 regmap_write+0x68/0x84
 rockchip_usb2phy_power_on+0x128/0x1f0
 rockchip_usb2phy_otg_sm_work+0x1d0/0x454
 process_one_work+0x1f4/0x490
 worker_thread+0x278/0x4dc
 kthread+0x13c/0x344
 ret_from_fork+0x10/0x30

In fact, there are two issues here.
1. The power of phy id belongs to the usb2 phy power supply.
And the id is pulled up to high level by default. So if we
power off usb2 phy supply during deep sleep, the id status
will fall to low level and trigger the falling edge interrupt.
In the id irq handler rockchip_usb2phy_id_irq(), it send Host
notification only depends the id falling edge irq status, it's
not enough in this case, it needs to check the iddig status
to make sure that the id status is indeed in low level.

2. For RK3588S, the pipe phystatus select register from the
usb grf, and the power domain of usb grf belongs to PD_USB.
So we must make sure the PD_USB is on when operate the pipe
phystatus select register. Originally, we operated the pipe
phystatus register in the phy ops of power_on, because we
expected that the phy ops of power_on called from the dwc3
controller pm runtime resume process which can power on the
PD_USB. However, in this test case, if the id falling edge
interrupt after system resume, the phy ops of power_on can
be called when PD_USB is off.

The call stack:
 rockchip_usb2phy_id_irq()
    -> send Host notification ->
 rockchip_otg_event()
    -> receive notification and schedule otg_sm_work ->
 rockchip_usb2phy_otg_sm_work()
    -> detect EXTCON_USB_HOST is set and call phy power_on ops
 rockchip_usb2phy_power_on()
    -> set the pipe phystatus select register

Change-Id: Ib7e5bc095ab1df2ca4c983d58a9f15720f0cccb9
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
William Wu
2023-04-06 11:03:47 +08:00
committed by Tao Huang
parent 9ea7c790e7
commit c3239eb297

View File

@@ -795,6 +795,10 @@ static int rockchip_usb2phy_init(struct phy *phy)
mutex_lock(&rport->mutex);
if (rport->sel_pipe_phystatus)
property_enable(rphy->usbctrl_grf,
&rport->port_cfg->pipe_phystatus, true);
if (rport->port_id == USB2PHY_PORT_OTG &&
(rport->mode == USB_DR_MODE_PERIPHERAL ||
rport->mode == USB_DR_MODE_OTG)) {
@@ -882,10 +886,6 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
if (ret)
goto unlock;
if (rport->sel_pipe_phystatus)
property_enable(rphy->usbctrl_grf,
&rport->port_cfg->pipe_phystatus, true);
ret = property_enable(base, &rport->port_cfg->phy_sus, false);
if (ret)
goto unlock;
@@ -1757,7 +1757,9 @@ static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data)
if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) {
property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr,
true);
cable_vbus_state = true;
/* switch to host if id fall det and iddig status is low */
if (!property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig))
cable_vbus_state = true;
} else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) {
property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr,
true);