mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user