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

Commit b1933b90 authored by Tarun Gupta's avatar Tarun Gupta Committed by Gerrit - the friendly Code Review server
Browse files

USB: ice40: Add pm_qos requirements when uicc tranfers in progress



CPU enters idle power collapse numerous times during UICC transfers,
latency of CPU for coming out of power collapse is impacting UICC
throughput. Add pm_qos requirement to prevent CPU from going into
ideal power collapse mode when UICC transfers are in progress.

Also add a module parameter pm_qos delay to input period of the timer in
milliseconds that start after the last access of the hardware to de-vote
the DMA latency vote. Its value is interpreted in following manner
  pm_qos_delay = -1: Never vote for QOS
  pm_qos_delay = 0: Always vote for QOS
  pm_qos_delay > 0: delay before devote for QOS

Also add a debug facility to get current pm_qos_stats for UICC. The below
command needs to be executed to get these stats.

echo -n "pm_qos_stat" > /sys/kernel/debug/ice40_hcd/command

Change-Id: I629b7db07785a550b6fc7d938203a191a0783b9d
Signed-off-by: default avatarTarun Gupta <tarung@codeaurora.org>
parent 3511ae2a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ Optional properties:
- <supply-name>-supply: handle to the regulator device tree node
  Optional "supply-name" is "gpio" used to power up the gpio bank
  used by this device
- qcom,pm-qos-latency : value in microseconds to be used by device to vote for
  DMA latency when UICC transfers are in progress.

	spi@f9923000 {
		lattice,spi-usb@3 {
@@ -42,6 +44,7 @@ Optional properties:
			interrupts = <121 0x8>;
			core-vcc-supply = <&pm8226_l2>;
			spi-vcc-supply = <&pm8226_l5>;
			qcom,pm-qos-latency = <2>;
			lattice,reset-gpio = <&msmgpio 114 0>;
			lattice,config-done-gpio = <&msmgpio 115 0>;
			lattice,vcc-en-gpio = <&msmgpio 117 0>;
+89 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/ktime.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
@@ -151,6 +152,11 @@ struct ice40_hcd {
	struct workqueue_struct *wq;
	struct work_struct async_work;

	struct delayed_work ice40_pm_qos_work;
	struct pm_qos_request ice40_pm_qos_req_dma;
	unsigned pm_qos_latency_us;
	bool pm_qos_voted;

	struct clk *xo_clk;

	struct pinctrl *pinctrl;
@@ -216,6 +222,28 @@ static bool uicc_card_present;
module_param(uicc_card_present, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(uicc_card_present, "UICC card is inserted");

/*
 * pm_qos delay is used to input period of the timer in miliseconds that start
 * after the last access of the hardware to de-vote the latency vote.
 * Its value is interpreted in following manner
 * pm_qos_delay_ms = -1: Never vote for QOS
 * pm_qos_delay_ms = 0: Always vote for QOS
 * pm_qos_delay_ms > 0: delay before devote for QOS
 */

/*
 * pm_qos_delay_ms default value is choosen as 500ms due to following reasons:
 * (1) to avoid multiple vote and de-vote during TUR (TEST_UNIT_READY)
 *     polling which happens with 1 sec period if enabled.
 * (2) to avoid multiple vote and de-vote during continous mass storage
 *     transfers
 */

#define ICE40_PM_QOS_DELAY 500
static int pm_qos_delay_ms = ICE40_PM_QOS_DELAY;
module_param(pm_qos_delay_ms, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pm_qos_delay_ms, "Set delay for workqueue ");

static inline struct ice40_hcd *hcd_to_ihcd(struct usb_hcd *hcd)
{
	return *((struct ice40_hcd **) hcd->hcd_priv);
@@ -415,6 +443,8 @@ static void ice40_stop(struct usb_hcd *hcd)
	struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);

	cancel_work_sync(&ihcd->async_work);
	if (ihcd->pm_qos_latency_us)
		cancel_delayed_work_sync(&ihcd->ice40_pm_qos_work);
}

/*
@@ -1055,6 +1085,16 @@ static void ice40_async_work(struct work_struct *work)
	 * if a URB is retired with -EPIPE/-EPROTO errors.
	 */

	if (pm_qos_delay_ms != -1 && ihcd->pm_qos_latency_us) {
		cancel_delayed_work_sync(&ihcd->ice40_pm_qos_work);
		if (!ihcd->pm_qos_voted) {
			pm_qos_update_request(&ihcd->ice40_pm_qos_req_dma,
					ihcd->pm_qos_latency_us);
			ihcd->pm_qos_voted = true;
			pr_debug("pm_qos voted\n");
		}
	}

	spin_lock_irqsave(&ihcd->lock, flags);

	if (list_empty(&ihcd->async_list))
@@ -1102,6 +1142,29 @@ static void ice40_async_work(struct work_struct *work)
	}
out:
	spin_unlock_irqrestore(&ihcd->lock, flags);

	if (pm_qos_delay_ms != 0 && ihcd->pm_qos_voted &&
			ihcd->pm_qos_latency_us) {
		if (pm_qos_delay_ms == -1)
			queue_delayed_work(ihcd->wq, &ihcd->ice40_pm_qos_work,
					msecs_to_jiffies(0));
		else
			queue_delayed_work(ihcd->wq, &ihcd->ice40_pm_qos_work,
					msecs_to_jiffies(pm_qos_delay_ms));
	}
}

static void ice40_pm_qos_work_f(struct work_struct *work)
{
	struct ice40_hcd *ihcd = container_of((struct delayed_work *)work,
			struct ice40_hcd, ice40_pm_qos_work);

	WARN_ON(!ihcd->pm_qos_latency_us);
	pm_qos_update_request(&ihcd->ice40_pm_qos_req_dma,
			PM_QOS_DEFAULT_VALUE);
	ihcd->pm_qos_voted = false;
	pr_debug("pm_qos devoted\n");

}

static int
@@ -1405,6 +1468,13 @@ static int ice40_bus_suspend(struct usb_hcd *hcd)
	if (!IS_ERR(s))
		pinctrl_select_state(ihcd->pinctrl, s);

	if (ihcd->pm_qos_latency_us) {
		cancel_delayed_work_sync(&ihcd->ice40_pm_qos_work);
		pm_qos_update_request(&ihcd->ice40_pm_qos_req_dma,
				PM_QOS_DEFAULT_VALUE);
		ihcd->pm_qos_voted = false;
	}

	trace_ice40_bus_suspend(1); /* successful */
	pm_relax(&ihcd->spi->dev);
	return 0;
@@ -2231,6 +2301,10 @@ static ssize_t ice40_dbg_cmd_write(struct file *file, const char __user *ubuf,
			pr_err("config load failed\n");
			goto out;
		}
	} else if (!strcmp(buf, "pm_qos_stat")) {
		pr_info("pm_qos_stat: delay %d, vote %d, latency %d\n",
				pm_qos_delay_ms, ihcd->pm_qos_voted,
				ihcd->pm_qos_latency_us);
	} else {
		ret = -EINVAL;
		goto out;
@@ -2318,9 +2392,18 @@ static int ice40_spi_probe(struct spi_device *spi)
		goto out;
	}

	if (of_property_read_u32(ihcd->spi->dev.of_node, "qcom,pm-qos-latency",
			&ihcd->pm_qos_latency_us))
		ihcd->pm_qos_latency_us = 0;

	spin_lock_init(&ihcd->lock);
	INIT_LIST_HEAD(&ihcd->async_list);
	INIT_WORK(&ihcd->async_work, ice40_async_work);

	if (ihcd->pm_qos_latency_us)
		INIT_DELAYED_WORK(&ihcd->ice40_pm_qos_work,
				ice40_pm_qos_work_f);

	mutex_init(&ihcd->wlock);
	mutex_init(&ihcd->rlock);

@@ -2398,6 +2481,10 @@ static int ice40_spi_probe(struct spi_device *spi)
	device_init_wakeup(&spi->dev, 1);
	pm_stay_awake(&spi->dev);

	if (ihcd->pm_qos_latency_us)
		pm_qos_add_request(&ihcd->ice40_pm_qos_req_dma,
				PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);

	pr_debug("success\n");

	return 0;
@@ -2423,6 +2510,8 @@ static int ice40_spi_remove(struct spi_device *spi)
	debugfs_remove_recursive(ihcd->dbg_root);

	usb_remove_hcd(hcd);
	if (ihcd->pm_qos_latency_us)
		pm_qos_remove_request(&ihcd->ice40_pm_qos_req_dma);
	usb_put_hcd(hcd);
	destroy_workqueue(ihcd->wq);
	ice40_spi_power_off(ihcd);