From ff680e982b7155e8758afbbdb1bd8554699f85a8 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 19 Apr 2019 16:34:37 +0800 Subject: [PATCH] MLK-21496 usb: typec: tcpm: remove qos request when unregister port As the counter part of pm_qos_add_request(), we should do pm_qos_remove_request() and release high bus when unregister tcpm port, otherwise we will have below crash if tcpm register again. [ 4.229417] tcpm_register_port(5021): trace [ 4.233631] pm_qos_update_target(280): c=ffff000009685998 node=ffff80083a084fb0 [ 4.240960] pm_qos_update_target(301): trace [ 4.245228] pm_qos_update_target(303): trace [ 4.249512] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [ 4.258302] Mem abort info: [ 4.261089] ESR = 0x96000044 [ 4.264142] Exception class = DABT (current EL), IL = 32 bits [ 4.270063] SET = 0, FnV = 0 [ 4.273115] EA = 0, S1PTW = 0 [ 4.276258] Data abort info: [ 4.279135] ISV = 0, ISS = 0x00000044 [ 4.282973] CM = 0, WnR = 1 [ 4.285938] [0000000000000000] user address but active_mm is swapper [ 4.292300] Internal error: Oops: 96000044 [#1] PREEMPT SMP [ 4.297876] Modules linked in: [ 4.300934] Process kworker/0:2 (pid: 860, stack limit = 0x(____ptrval____)) [ 4.307992] CPU: 0 PID: 860 Comm: kworker/0:2 Not tainted 4.19.35-04375-gedbbcdc07bf1-dirty #3 [ 4.316604] Hardware name: Freescale i.MX8QXP MEK (DT) [ 4.321761] Workqueue: events deferred_probe_work_func [ 4.326901] pstate: 80000085 (Nzcv daIf -PAN -UAO) [ 4.331700] pc : plist_add+0x98/0xd8 [ 4.335279] lr : pm_qos_update_target+0x310/0x330 [ 4.339982] sp : ffff00000d603a00 [ 4.343290] x29: ffff00000d603a00 x28: 0000000000000000 [ 4.348607] x27: ffff00000936b538 x26: 0000000000000000 [ 4.353924] x25: ffff000008f425b0 x24: 0000000000000000 [ 4.359241] x23: ffff0000098d0000 x22: 0000000000000000 [ 4.364557] x21: 000000003af84fa8 x20: ffff80083a084fb0 [ 4.369874] x19: ffff000009685998 x18: ffffffffffffffff [ 4.375191] x17: 0000000000000000 x16: 0000000000000000 [ 4.380508] x15: ffff0000096686c8 x14: ffff0000898d0fef [ 4.385824] x13: ffff0000098d0ffd x12: ffff000009685000 [ 4.391141] x11: 0000000005f5e0ff x10: ffff000009668940 [ 4.396458] x9 : ffff000008681658 x8 : ffff80083a084fb8 [ 4.401775] x7 : ffff80083a084fc8 x6 : ffff80083af84fc8 [ 4.407091] x5 : 0000000000000000 x4 : 0000000000000000 [ 4.412408] x3 : ffff80083af84fb0 x2 : 0000000000000000 [ 4.417725] x1 : ffff80083af84fb8 x0 : ffff80083a084fb0 [ 4.423044] Call trace: [ 4.425497] plist_add+0x98/0xd8 [ 4.428728] pm_qos_add_request+0x88/0x170 [ 4.432831] tcpm_register_port+0x5ec/0x898 [ 4.437017] tcpci_register_port+0x154/0x1e0 [ 4.441291] tcpci_probe+0xa8/0x218 [ 4.444784] i2c_device_probe+0x2e4/0x2f8 [ 4.448796] really_probe+0x248/0x3b8 [ 4.452461] driver_probe_device+0x12c/0x148 [ 4.456735] __device_attach_driver+0xac/0x160 [ 4.461184] bus_for_each_drv+0x68/0xd0 [ 4.465022] __device_attach+0xd8/0x160 [ 4.468864] device_initial_probe+0x10/0x18 [ 4.473049] bus_probe_device+0x94/0xa0 [ 4.476890] deferred_probe_work_func+0x84/0xd8 [ 4.481427] process_one_work+0x1ec/0x460 [ 4.485438] worker_thread+0x224/0x448 [ 4.489194] kthread+0x12c/0x130 [ 4.492427] ret_from_fork+0x10/0x18 [ 4.496006] Code: f9400822 91002021 f9000428 a9008801 (f9000048) [ 4.502103] ---[ end trace 910526b6d64dab64 ]--- [ 4.506754] note: kworker/0:2[860] exited with preempt_count 1 Reported-by: Leonard Crestez Suggested-by: Li Jun Signed-off-by: Peter Chen --- drivers/usb/typec/tcpm.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index db3339ea581c..1e5db9fd0c22 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -888,18 +888,21 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } -static void tcpm_qos_handling(struct tcpm_port *port) +static enum tcpm_state tcpm_get_idle_state(struct tcpm_port *port) { - enum tcpm_state idle_state; - if (port->typec_caps.type == TYPEC_PORT_SNK) - idle_state = SNK_UNATTACHED; + return SNK_UNATTACHED; else if (port->typec_caps.type == TYPEC_PORT_SRC) - idle_state = SNK_UNATTACHED; + return SNK_UNATTACHED; else if (port->typec_caps.type == TYPEC_PORT_DRP) - idle_state = DRP_TOGGLING; + return DRP_TOGGLING; else - return; + return INVALID_STATE; +} + +static void tcpm_qos_handling(struct tcpm_port *port) +{ + enum tcpm_state idle_state = tcpm_get_idle_state(port); if ((port->prev_state == SNK_READY || port->prev_state == SRC_READY || port->prev_state == idle_state)) { @@ -5047,6 +5050,11 @@ void tcpm_unregister_port(struct tcpm_port *port) usb_role_switch_put(port->role_sw); tcpm_debugfs_exit(port); destroy_workqueue(port->wq); + if (!(port->state == SNK_READY || port->state == SRC_READY || + port->state == tcpm_get_idle_state(port))) { + pm_qos_remove_request(&port->pm_qos_req); + release_bus_freq(BUS_FREQ_HIGH); + } } EXPORT_SYMBOL_GPL(tcpm_unregister_port); -- 2.17.1