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

Commit 7371a6df authored by Ramprasad Katkam's avatar Ramprasad Katkam Committed by Gerrit - the friendly Code Review server
Browse files

soc: swr-mstr: Add wake lock support to prevent suspend



Add lock and unlock sleep functions to master to prevent
system suspend during interrupts or slave wakeup
requests.

Change-Id: Icb4ea354ec6e2925f83c2380f30ea8e37aca7449
Signed-off-by: default avatarRamprasad Katkam <katkam@codeaurora.org>
parent 8af76620
Loading
Loading
Loading
Loading
+197 −18
Original line number Diff line number Diff line
@@ -33,7 +33,8 @@
#include "swr-mstr-ctrl.h"
#include "swrm_port_config.h"


#define SWRM_SYSTEM_RESUME_TIMEOUT_MS 700
#define SWRM_SYS_SUSPEND_WAIT 1
#define SWR_BROADCAST_CMD_ID            0x0F
#define SWR_AUTO_SUSPEND_DELAY          3 /* delay in sec */
#define SWR_DEV_ID_MASK			0xFFFFFFFF
@@ -89,6 +90,8 @@ static struct dentry *debugfs_poke;
static struct dentry *debugfs_reg_dump;
static unsigned int read_data;

static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm);
static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm);

static bool swrm_is_msm_variant(int val)
{
@@ -1269,6 +1272,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev)
	struct swr_device *swr_dev;
	struct swr_master *mstr = &swrm->master;

	if (unlikely(swrm_lock_sleep(swrm) == false)) {
		dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__);
		return IRQ_NONE;
	}

	mutex_lock(&swrm->reslock);
	swrm_clk_request(swrm, true);
@@ -1419,6 +1426,7 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev)
	mutex_lock(&swrm->reslock);
	swrm_clk_request(swrm, false);
	mutex_unlock(&swrm->reslock);
	swrm_unlock_sleep(swrm);
	return ret;
}

@@ -1462,12 +1470,19 @@ static void swrm_wakeup_work(struct work_struct *work)
	mutex_lock(&swrm->devlock);
	if (!swrm->dev_up) {
		mutex_unlock(&swrm->devlock);
		return;
		goto exit;
	}
	mutex_unlock(&swrm->devlock);
	if (unlikely(swrm_lock_sleep(swrm) == false)) {
		dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__);
		goto exit;
	}
	pm_runtime_get_sync(swrm->dev);
	pm_runtime_mark_last_busy(swrm->dev);
	pm_runtime_put_autosuspend(swrm->dev);
	swrm_unlock_sleep(swrm);
exit:
	pm_relax(swrm->dev);
}

static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum)
@@ -1553,6 +1568,10 @@ static void swrm_device_wakeup_vote(struct swr_master *mstr)
			__func__);
		return;
	}
	if (unlikely(swrm_lock_sleep(swrm) == false)) {
		dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__);
		return;
	}
	pm_runtime_get_sync(swrm->dev);
}

@@ -1567,6 +1586,7 @@ static void swrm_device_wakeup_unvote(struct swr_master *mstr)
	}
	pm_runtime_mark_last_busy(swrm->dev);
	pm_runtime_put_autosuspend(swrm->dev);
	swrm_unlock_sleep(swrm);
}

static int swrm_master_init(struct swr_mstr_ctrl *swrm)
@@ -1647,6 +1667,7 @@ static int swrm_event_notify(struct notifier_block *self,
	case SWR_WAKE_IRQ_EVENT:
		if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) {
			swrm->ipc_wakeup_triggered = true;
			pm_stay_awake(swrm->dev);
			schedule_work(&swrm->wakeup_work);
		}
		break;
@@ -1753,6 +1774,21 @@ static int swrm_probe(struct platform_device *pdev)
			&swrm->clk_stop_mode0_supp)) {
		swrm->clk_stop_mode0_supp = FALSE;
	}

	ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev",
				   &swrm->num_dev);
	if (ret) {
		dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n",
			__func__, "qcom,swr-num-dev");
	} else {
		if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) {
			dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n",
				__func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES);
			ret = -EINVAL;
			goto err_pdata_fail;
		}
	}

	/* Parse soundwire port mapping */
	ret = of_property_read_u32(pdev->dev.of_node, "qcom,swr-num-ports",
				&num_ports);
@@ -1835,24 +1871,17 @@ static int swrm_probe(struct platform_device *pdev)
	mutex_init(&swrm->iolock);
	mutex_init(&swrm->clklock);
	mutex_init(&swrm->devlock);
	mutex_init(&swrm->pm_lock);
	swrm->wlock_holders = 0;
	swrm->pm_state = SWRM_PM_SLEEPABLE;
	init_waitqueue_head(&swrm->pm_wq);
	pm_qos_add_request(&swrm->pm_qos_req,
			   PM_QOS_CPU_DMA_LATENCY,
			   PM_QOS_DEFAULT_VALUE);

	for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++)
		INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list);

	ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev",
				   &swrm->num_dev);
	if (ret) {
		dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n",
			__func__, "qcom,swr-num-dev");
	} else {
		if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) {
			dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n",
				__func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES);
			ret = -EINVAL;
			goto err_pdata_fail;
		}
	}

	if (swrm->reg_irq) {
		ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
			    SWR_IRQ_REGISTER);
@@ -1948,6 +1977,9 @@ static int swrm_probe(struct platform_device *pdev)
	mutex_destroy(&swrm->force_down_lock);
	mutex_destroy(&swrm->iolock);
	mutex_destroy(&swrm->clklock);
	mutex_destroy(&swrm->pm_lock);
	pm_qos_remove_request(&swrm->pm_qos_req);

err_pdata_fail:
err_memory_fail:
	return ret;
@@ -1964,7 +1996,7 @@ static int swrm_remove(struct platform_device *pdev)
		free_irq(swrm->irq, swrm);
	else if (swrm->wake_irq > 0)
		free_irq(swrm->wake_irq, swrm);

	cancel_work_sync(&swrm->wakeup_work);
	pm_runtime_disable(&pdev->dev);
	pm_runtime_set_suspended(&pdev->dev);
	swr_unregister_master(&swrm->master);
@@ -1974,6 +2006,8 @@ static int swrm_remove(struct platform_device *pdev)
	mutex_destroy(&swrm->iolock);
	mutex_destroy(&swrm->clklock);
	mutex_destroy(&swrm->force_down_lock);
	mutex_destroy(&swrm->pm_lock);
	pm_qos_remove_request(&swrm->pm_qos_req);
	devm_kfree(&pdev->dev, swrm);
	return 0;
}
@@ -2309,6 +2343,94 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data)
}
EXPORT_SYMBOL(swrm_wcd_notify);

/*
 * swrm_pm_cmpxchg:
 *      Check old state and exchange with pm new state
 *      if old state matches with current state
 *
 * @swrm: pointer to wcd core resource
 * @o: pm old state
 * @n: pm new state
 *
 * Returns old state
 */
static enum swrm_pm_state swrm_pm_cmpxchg(
				struct swr_mstr_ctrl *swrm,
				enum swrm_pm_state o,
				enum swrm_pm_state n)
{
	enum swrm_pm_state old;

	if (!swrm)
		return o;

	mutex_lock(&swrm->pm_lock);
	old = swrm->pm_state;
	if (old == o)
		swrm->pm_state = n;
	mutex_unlock(&swrm->pm_lock);

	return old;
}

static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm)
{
	enum swrm_pm_state os;

	/*
	 * swrm_{lock/unlock}_sleep will be called by swr irq handler
	 * and slave wake up requests..
	 *
	 * If system didn't resume, we can simply return false so
	 * IRQ handler can return without handling IRQ.
	 */
	mutex_lock(&swrm->pm_lock);
	if (swrm->wlock_holders++ == 0) {
		dev_dbg(swrm->dev, "%s: holding wake lock\n", __func__);
		pm_qos_update_request(&swrm->pm_qos_req,
					  msm_cpuidle_get_deep_idle_latency());
		pm_stay_awake(swrm->dev);
	}
	mutex_unlock(&swrm->pm_lock);

	if (!wait_event_timeout(swrm->pm_wq,
				((os =  swrm_pm_cmpxchg(swrm,
				  SWRM_PM_SLEEPABLE,
				  SWRM_PM_AWAKE)) ==
					SWRM_PM_SLEEPABLE ||
					(os == SWRM_PM_AWAKE)),
					msecs_to_jiffies(
					SWRM_SYSTEM_RESUME_TIMEOUT_MS))) {
		dev_err(swrm->dev, "%s: system didn't resume within %dms, s %d, w %d\n",
			__func__, SWRM_SYSTEM_RESUME_TIMEOUT_MS, swrm->pm_state,
				swrm->wlock_holders);
		swrm_unlock_sleep(swrm);
		return false;
	}
	wake_up_all(&swrm->pm_wq);
	return true;
}

static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm)
{
	mutex_lock(&swrm->pm_lock);
	if (--swrm->wlock_holders == 0) {
		dev_dbg(swrm->dev, "%s: releasing wake lock pm_state %d -> %d\n",
			 __func__, swrm->pm_state, SWRM_PM_SLEEPABLE);
		/*
		 * if swrm_lock_sleep failed, pm_state would be still
		 * swrm_PM_ASLEEP, don't overwrite
		 */
		if (likely(swrm->pm_state == SWRM_PM_AWAKE))
			swrm->pm_state = SWRM_PM_SLEEPABLE;
		pm_qos_update_request(&swrm->pm_qos_req,
				  PM_QOS_DEFAULT_VALUE);
		pm_relax(swrm->dev);
	}
	mutex_unlock(&swrm->pm_lock);
	wake_up_all(&swrm->pm_wq);
}

#ifdef CONFIG_PM_SLEEP
static int swrm_suspend(struct device *dev)
{
@@ -2317,7 +2439,49 @@ static int swrm_suspend(struct device *dev)
	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);

	dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state);
	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {

	mutex_lock(&swrm->pm_lock);

	if (swrm->pm_state == SWRM_PM_SLEEPABLE) {
		dev_dbg(swrm->dev, "%s: suspending system, state %d, wlock %d\n",
			 __func__, swrm->pm_state,
			swrm->wlock_holders);
		swrm->pm_state = SWRM_PM_ASLEEP;
	} else if (swrm->pm_state == SWRM_PM_AWAKE) {
		/*
		 * unlock to wait for pm_state == SWRM_PM_SLEEPABLE
		 * then set to SWRM_PM_ASLEEP
		 */
		dev_dbg(swrm->dev, "%s: waiting to suspend system, state %d, wlock %d\n",
			 __func__, swrm->pm_state,
			 swrm->wlock_holders);
		mutex_unlock(&swrm->pm_lock);
		if (!(wait_event_timeout(swrm->pm_wq, swrm_pm_cmpxchg(
					 swrm, SWRM_PM_SLEEPABLE,
						 SWRM_PM_ASLEEP) ==
						   SWRM_PM_SLEEPABLE,
						   msecs_to_jiffies(
						   SWRM_SYS_SUSPEND_WAIT)))) {
			dev_dbg(swrm->dev, "%s: suspend failed state %d, wlock %d\n",
				 __func__, swrm->pm_state,
				 swrm->wlock_holders);
			return -EBUSY;
		} else {
			dev_dbg(swrm->dev,
				"%s: done, state %d, wlock %d\n",
				__func__, swrm->pm_state,
				swrm->wlock_holders);
		}
		mutex_lock(&swrm->pm_lock);
	} else if (swrm->pm_state == SWRM_PM_ASLEEP) {
		dev_dbg(swrm->dev, "%s: system is already suspended, state %d, wlock %d\n",
			__func__, swrm->pm_state,
			swrm->wlock_holders);
	}

	mutex_unlock(&swrm->pm_lock);

	if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) {
		ret = swrm_runtime_suspend(dev);
		if (!ret) {
			/*
@@ -2363,6 +2527,21 @@ static int swrm_resume(struct device *dev)
			pm_request_autosuspend(dev);
		}
	}
	mutex_lock(&swrm->pm_lock);
	if (swrm->pm_state == SWRM_PM_ASLEEP) {
		dev_dbg(swrm->dev,
			"%s: resuming system, state %d, wlock %d\n",
			__func__, swrm->pm_state,
			swrm->wlock_holders);
		swrm->pm_state = SWRM_PM_SLEEPABLE;
	} else {
		dev_dbg(swrm->dev, "%s: system is already awake, state %d wlock %d\n",
			__func__, swrm->pm_state,
			swrm->wlock_holders);
	}
	mutex_unlock(&swrm->pm_lock);
	wake_up_all(&swrm->pm_wq);

	return ret;
}
#endif /* CONFIG_PM_SLEEP */
+13 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
#define _SWR_WCD_CTRL_H
#include <linux/module.h>
#include <soc/swr-wcd.h>
#include <linux/pm_qos.h>
#include <soc/qcom/pm.h>

#define SWR_ROW_48		0
#define SWR_ROW_50		1
@@ -42,6 +44,12 @@ enum {
	SWR_MSTR_SSR,
};

enum swrm_pm_state {
	SWRM_PM_SLEEPABLE,
	SWRM_PM_AWAKE,
	SWRM_PM_ASLEEP,
};

enum {
	SWR_IRQ_FREE,
	SWR_IRQ_REGISTER,
@@ -118,6 +126,7 @@ struct swr_mstr_ctrl {
	struct mutex devlock;
	struct mutex mlock;
	struct mutex reslock;
	struct mutex pm_lock;
	u32 swrm_base_reg;
	char __iomem *swrm_dig_base;
	u8 rcmd_id;
@@ -156,6 +165,10 @@ struct swr_mstr_ctrl {
	u32 ipc_wakeup;
	bool dev_up;
	bool ipc_wakeup_triggered;
	struct pm_qos_request pm_qos_req;
	enum swrm_pm_state pm_state;
	wait_queue_head_t pm_wq;
	int wlock_holders;
};

#endif /* _SWR_WCD_CTRL_H */