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

Commit 0925d44f authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 31435db6 b1933b90
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);