Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8d3ee8b authored by Sujeet Kumar's avatar Sujeet Kumar
Browse files

USB: phy-msm-usb: Ensure strict execution order of phy event processing



Using system_nrt_wq does not guarantee the serial execution
of the multiple works. This can lead to parallel execution of
different tasks at the same time. It can lead to a situation
where state machine put device into low power mode and charger
detection still running and accessing registers. This will cause
unclocked access.

Use strict order workqueue to schedule state machine, charger
detection, ID detection and suspend work. workqueue is tunned to
run at max one task at any given point in time which helps avoid
parallel execution of different tasks.

CRs-Fixed: 765672
Change-Id: I02eb36dd4bcbea923638c6532fc4a94c3e5dd6b9
Signed-off-by: default avatarSujeet Kumar <ksujeet@codeaurora.org>
parent 3faf5fc4
Loading
Loading
Loading
Loading
+33 −22
Original line number Diff line number Diff line
@@ -756,7 +756,7 @@ static enum hrtimer_restart msm_otg_timer_func(struct hrtimer *hrtimer)
	}

	pr_debug("expired %s timer\n", timer_string(motg->active_tmout));
	queue_work(system_nrt_wq, &motg->sm_work);
	queue_work(motg->otg_wq, &motg->sm_work);
	return HRTIMER_NORESTART;
}

@@ -799,7 +799,7 @@ static int msm_otg_start_hnp(struct usb_otg *otg)

	pr_debug("A-Host: HNP initiated\n");
	clear_bit(A_BUS_REQ, &motg->inputs);
	queue_work(system_nrt_wq, &motg->sm_work);
	queue_work(motg->otg_wq, &motg->sm_work);
	return 0;
}

@@ -889,7 +889,7 @@ static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
			clear_bit(A_BUS_REQ, &motg->inputs);
			if (!atomic_read(&motg->in_lpm) &&
					!test_bit(ID, &motg->inputs)) {
				queue_work(system_nrt_wq, &motg->sm_work);
				queue_work(motg->otg_wq, &motg->sm_work);
				/* Flush sm_work to avoid it race with
				 * subsequent calls of set_suspend.
				 */
@@ -902,7 +902,7 @@ static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
				break;
			set_bit(A_BUS_SUSPEND, &motg->inputs);
			if (!atomic_read(&motg->in_lpm))
				queue_delayed_work(system_nrt_wq,
				queue_delayed_work(motg->otg_wq,
					&motg->suspend_work,
					USB_SUSPEND_DELAY_TIME);
			break;
@@ -934,7 +934,7 @@ static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
				break;
			clear_bit(A_BUS_SUSPEND, &motg->inputs);
			if (atomic_read(&motg->in_lpm))
				queue_work(system_nrt_wq, &motg->sm_work);
				queue_work(motg->otg_wq, &motg->sm_work);
			break;
		default:
			break;
@@ -1912,7 +1912,7 @@ static int msm_otg_usbdev_notify(struct notifier_block *self,
				udev->bus->otg_vbus_off = 0;
				set_bit(A_BUS_DROP, &motg->inputs);
			}
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
		}
	default:
		break;
@@ -1999,7 +1999,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
			msm_hsusb_vbus_power(motg, 0);
			otg->host = NULL;
			otg->phy->state = OTG_STATE_UNDEFINED;
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
		} else {
			otg->host = NULL;
		}
@@ -2024,7 +2024,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
	 */
	if (motg->pdata->mode == USB_HOST || otg->gadget) {
		pm_runtime_get_sync(otg->phy->dev);
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
	}

	return 0;
@@ -2116,7 +2116,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg,
			msm_otg_start_peripheral(otg, 0);
			otg->gadget = NULL;
			otg->phy->state = OTG_STATE_UNDEFINED;
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
		} else {
			otg->gadget = NULL;
		}
@@ -2132,7 +2132,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg,
	 */
	if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
		pm_runtime_get_sync(otg->phy->dev);
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
	}

	return 0;
@@ -2272,7 +2272,7 @@ static void msm_otg_chg_check_timer_func(unsigned long data)
	if ((readl_relaxed(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
		dev_dbg(otg->phy->dev, "DCP is detected as SDP\n");
		set_bit(B_FALSE_SDP, &motg->inputs);
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
	}
}

@@ -2443,7 +2443,7 @@ static void msm_otg_id_timer_func(unsigned long data)

	if (msm_chg_check_aca_intr(motg)) {
		dev_dbg(motg->phy.dev, "timer: aca work\n");
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
	}

out:
@@ -2687,6 +2687,8 @@ static void msm_chg_detect_work(struct work_struct *w)
		return;
	}

	/* resume the device first if at all it resumes */
	pm_runtime_resume(phy->dev);
	switch (motg->chg_state) {
	case USB_CHG_STATE_UNDEFINED:
		msm_chg_block_on(motg);
@@ -2701,7 +2703,7 @@ static void msm_chg_detect_work(struct work_struct *w)
			msm_chg_block_off(motg);
			motg->chg_state = USB_CHG_STATE_DETECTED;
			motg->chg_type = USB_INVALID_CHARGER;
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
			return;
		}
		is_aca = msm_chg_aca_detect(motg);
@@ -2810,13 +2812,13 @@ static void msm_chg_detect_work(struct work_struct *w)

		dev_dbg(phy->dev, "chg_type = %s\n",
			chg_to_string(motg->chg_type));
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
		return;
	default:
		return;
	}

	queue_delayed_work(system_nrt_wq, &motg->chg_work, delay);
	queue_delayed_work(motg->otg_wq, &motg->chg_work, delay);
}

#define VBUS_INIT_TIMEOUT	msecs_to_jiffies(5000)
@@ -3562,7 +3564,7 @@ static void msm_otg_sm_work(struct work_struct *w)
		break;
	}
	if (work)
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
}

static void msm_otg_suspend_work(struct work_struct *w)
@@ -3733,7 +3735,7 @@ static irqreturn_t msm_otg_irq(int irq, void *data)
		ret = IRQ_HANDLED;
	}
	if (work)
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);

	return ret;
}
@@ -3787,7 +3789,7 @@ out:
		motg->sm_work_pending = true;
	} else if (!motg->sm_work_pending) {
		/* process event only if previous one is not pending */
		queue_work(system_nrt_wq, &motg->sm_work);
		queue_work(motg->otg_wq, &motg->sm_work);
	}
}

@@ -3823,7 +3825,7 @@ static void msm_id_status_w(struct work_struct *w)
			motg->sm_work_pending = true;
		} else if (!motg->sm_work_pending) {
			/* process event only if previous one is not pending */
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
		}
	}

@@ -3842,7 +3844,7 @@ static irqreturn_t msm_id_irq(int irq, void *data)

	if (!aca_id_turned_on)
		/*schedule delayed work for 5msec for ID line state to settle*/
		queue_delayed_work(system_nrt_wq, &motg->id_status_work,
		queue_delayed_work(motg->otg_wq, &motg->id_status_work,
				msecs_to_jiffies(MSM_ID_STATUS_DELAY));

	return IRQ_HANDLED;
@@ -3865,7 +3867,7 @@ int msm_otg_pm_notify(struct notifier_block *notify_block,
		/* Handle any deferred wakeup events from USB during suspend */
		if (motg->sm_work_pending) {
			motg->sm_work_pending = false;
			queue_work(system_nrt_wq, &motg->sm_work);
			queue_work(motg->otg_wq, &motg->sm_work);
		}
		break;

@@ -3974,7 +3976,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
	}

	pm_runtime_resume(phy->dev);
	queue_work(system_nrt_wq, &motg->sm_work);
	queue_work(motg->otg_wq, &motg->sm_work);
out:
	return status;
}
@@ -5202,6 +5204,13 @@ static int msm_otg_probe(struct platform_device *pdev)
				(unsigned long) motg);
	setup_timer(&motg->chg_check_timer, msm_otg_chg_check_timer_func,
				(unsigned long) motg);
	motg->otg_wq = alloc_ordered_workqueue("k_otg", 0);
	if (!motg->otg_wq) {
		pr_err("%s: Unable to create workqueue otg_wq\n",
			__func__);
		goto destroy_wlock;
	}

	ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
					"msm_otg", motg);
	if (ret) {
@@ -5396,6 +5405,7 @@ destroy_wlock:
	wake_lock_destroy(&motg->wlock);
	clk_disable_unprepare(motg->core_clk);
	msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
	destroy_workqueue(motg->otg_wq);
free_ldo_init:
	msm_hsusb_ldo_init(motg, 0);
free_hsusb_vdd:
@@ -5473,6 +5483,7 @@ static int msm_otg_remove(struct platform_device *pdev)
	cancel_delayed_work_sync(&motg->id_status_work);
	cancel_delayed_work_sync(&motg->suspend_work);
	cancel_work_sync(&motg->sm_work);
	destroy_workqueue(motg->otg_wq);

	pm_runtime_resume(&pdev->dev);

+2 −0
Original line number Diff line number Diff line
@@ -379,6 +379,7 @@ struct msm_otg_platform_data {
 * @in_lpm: indicates low power mode (LPM) state.
 * @async_int: IRQ line on which ASYNC interrupt arrived in LPM.
 * @cur_power: The amount of mA available from downstream port.
 * @otg_wq: Strict order otg workqueue for OTG works (SM/ID/SUSPEND).
 * @chg_work: Charger detection work.
 * @chg_state: The state of charger detection process.
 * @chg_type: The type of charger attached.
@@ -452,6 +453,7 @@ struct msm_otg {
	atomic_t set_fpr_with_lpm_exit;
	int async_int;
	unsigned cur_power;
	struct workqueue_struct *otg_wq;
	struct delayed_work chg_work;
	struct delayed_work id_status_work;
	struct delayed_work suspend_work;