msm: pil-q6v3: Move to prepare()/unprepare() clock APIs

The clock driver is moving to prepare/unprepare apis. Make sure
to call prepare/unprepare in non-atomic context, therefore
replace the timer with a workqueue and call the combined
prepare/enable apis appropriately.

Change-Id: Ib2616a9dcb8b88cdf59e72e72d79f584aca80210
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index 7354d93..06b98e5 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,7 @@
 #include <linux/elf.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/workqueue.h>
 
 #include <mach/msm_iomap.h>
 
@@ -66,7 +67,7 @@
 	void __iomem *base;
 	unsigned long start_addr;
 	struct clk *pll;
-	struct timer_list timer;
+	struct delayed_work work;
 };
 
 static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
@@ -83,36 +84,40 @@
 	return 0;
 }
 
-static void q6v3_remove_proxy_votes(unsigned long data)
+static void q6v3_remove_proxy_votes(struct work_struct *work)
 {
-	struct q6v3_data *drv = (struct q6v3_data *)data;
-	clk_disable(drv->pll);
+	struct q6v3_data *drv = container_of(work, struct q6v3_data, work.work);
+	clk_disable_unprepare(drv->pll);
 }
 
-static void q6v3_make_proxy_votes(struct device *dev)
+static int q6v3_make_proxy_votes(struct device *dev)
 {
 	int ret;
 	struct q6v3_data *drv = dev_get_drvdata(dev);
 
-	ret = clk_enable(drv->pll);
-	if (ret)
+	ret = clk_prepare_enable(drv->pll);
+	if (ret) {
 		dev_err(dev, "Failed to enable PLL\n");
-	mod_timer(&drv->timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+		return ret;
+	}
+	schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+	return 0;
 }
 
 static void q6v3_remove_proxy_votes_now(struct q6v3_data *drv)
 {
-	/* If the proxy vote hasn't been removed yet, remove it immediately. */
-	if (del_timer(&drv->timer))
-		q6v3_remove_proxy_votes((unsigned long)drv);
+	flush_delayed_work(&drv->work);
 }
 
 static int pil_q6v3_reset(struct pil_desc *pil)
 {
 	u32 reg;
+	int ret;
 	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
 
-	q6v3_make_proxy_votes(pil->dev);
+	ret = q6v3_make_proxy_votes(pil->dev);
+	if (ret)
+		return ret;
 
 	/* Put Q6 into reset */
 	reg = readl_relaxed(LCC_Q6_FUNC);
@@ -198,7 +203,10 @@
 
 static int pil_q6v3_reset_trusted(struct pil_desc *pil)
 {
-	q6v3_make_proxy_votes(pil->dev);
+	int ret;
+	ret = q6v3_make_proxy_votes(pil->dev);
+	if (ret)
+		return ret;
 	return pas_auth_and_reset(PAS_Q6);
 }
 
@@ -250,7 +258,6 @@
 	if (IS_ERR(drv->pll))
 		return PTR_ERR(drv->pll);
 
-	setup_timer(&drv->timer, q6v3_remove_proxy_votes, (unsigned long)drv);
 	desc->name = "q6";
 	desc->dev = &pdev->dev;
 
@@ -262,15 +269,19 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	if (msm_pil_register(desc))
+	INIT_DELAYED_WORK(&drv->work, q6v3_remove_proxy_votes);
+
+	if (msm_pil_register(desc)) {
+		flush_delayed_work_sync(&drv->work);
 		return -EINVAL;
+	}
 	return 0;
 }
 
 static int __devexit pil_q6v3_driver_exit(struct platform_device *pdev)
 {
 	struct q6v3_data *drv = platform_get_drvdata(pdev);
-	del_timer_sync(&drv->timer);
+	flush_delayed_work_sync(&drv->work);
 	return 0;
 }