msm: pil-riva: Proxy vote for L23
L23 supplies the power for the PLL that RIVA uses during boot.
Proxy vote for the regulator until RIVA is up enough to put in
its own vote. Otherwise we may turn off L23 while RIVA is just
booting and kill the processor. Move to using a workqueue instead
of a timer for the proxy timeout so that regulator APIs can be
used.
Change-Id: I2303f623803b44e993db323ca08c5e87144d7403
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index 6848c59..f977897 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -18,6 +18,8 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <mach/msm_iomap.h>
#include <mach/msm_xo.h>
@@ -81,37 +83,36 @@
void __iomem *base;
unsigned long start_addr;
struct msm_xo_voter *xo;
- struct timer_list xo_timer;
+ bool use_cxo;
+ struct delayed_work work;
+ struct regulator *pll_supply;
};
-static void pil_riva_make_xo_proxy_votes(struct device *dev)
+static void pil_riva_make_proxy_votes(struct device *dev)
{
struct riva_data *drv = dev_get_drvdata(dev);
+ int ret;
- msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
- mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+ if (drv->use_cxo)
+ msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
+ ret = regulator_enable(drv->pll_supply);
+ if (ret)
+ dev_err(dev, "failed to enable pll supply\n");
+ schedule_delayed_work(&drv->work, msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
}
-static void pil_riva_remove_xo_proxy_votes(unsigned long data)
+static void pil_riva_remove_proxy_votes(struct work_struct *work)
{
- struct riva_data *drv = (struct riva_data *)data;
-
- msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
+ struct riva_data *drv = container_of(work, struct riva_data, work.work);
+ regulator_disable(drv->pll_supply);
+ if (drv->use_cxo)
+ msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
}
-static void pil_riva_remove_xo_proxy_votes_now(struct device *dev)
+static void pil_riva_remove_proxy_votes_now(struct device *dev)
{
struct riva_data *drv = dev_get_drvdata(dev);
-
- if (del_timer(&drv->xo_timer))
- pil_riva_remove_xo_proxy_votes((unsigned long)drv);
-}
-
-static bool cxo_is_needed(struct riva_data *drv)
-{
- u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
- return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
- != RIVA_PMU_CFG_IRIS_XO_MODE_48;
+ flush_delayed_work(&drv->work);
}
static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
@@ -128,10 +129,16 @@
return 0;
}
+static bool cxo_is_needed(struct riva_data *drv)
+{
+ u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
+ return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
+ != RIVA_PMU_CFG_IRIS_XO_MODE_48;
+}
+
static int pil_riva_reset(struct pil_desc *pil)
{
u32 reg, sel;
- bool use_cxo;
struct riva_data *drv = dev_get_drvdata(pil->dev);
void __iomem *base = drv->base;
unsigned long start_addr = drv->start_addr;
@@ -141,17 +148,15 @@
reg |= RIVA_PMU_A2XB_CFG_EN;
writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG);
- /* Proxy-vote for CXO if it's needed */
- use_cxo = cxo_is_needed(drv);
- if (use_cxo)
- pil_riva_make_xo_proxy_votes(pil->dev);
+ drv->use_cxo = cxo_is_needed(drv);
+ pil_riva_make_proxy_votes(pil->dev);
/* Program PLL 13 to 960 MHz */
reg = readl_relaxed(RIVA_PLL_MODE);
reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
writel_relaxed(reg, RIVA_PLL_MODE);
- if (use_cxo)
+ if (drv->use_cxo)
writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
else
writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
@@ -161,7 +166,7 @@
reg = readl_relaxed(RIVA_PLL_MODE);
reg &= ~(PLL_MODE_REF_XO_SEL);
- reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
+ reg |= drv->use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
writel_relaxed(reg, RIVA_PLL_MODE);
/* Enable PLL 13 */
@@ -253,7 +258,7 @@
writel_relaxed(0, RIVA_RESET);
mb();
- pil_riva_remove_xo_proxy_votes_now(pil->dev);
+ pil_riva_remove_proxy_votes_now(pil->dev);
return 0;
}
@@ -273,12 +278,8 @@
static int pil_riva_reset_trusted(struct pil_desc *pil)
{
- struct riva_data *drv = dev_get_drvdata(pil->dev);
-
- /* Proxy-vote for CXO if it's needed */
- if (cxo_is_needed(drv))
- pil_riva_make_xo_proxy_votes(pil->dev);
-
+ /* Proxy-vote for resources RIVA needs */
+ pil_riva_make_proxy_votes(pil->dev);
return pas_auth_and_reset(PAS_RIVA);
}
@@ -286,7 +287,7 @@
{
int ret = pas_shutdown(PAS_RIVA);
- pil_riva_remove_xo_proxy_votes_now(pil->dev);
+ pil_riva_remove_proxy_votes_now(pil->dev);
return ret;
}
@@ -303,6 +304,7 @@
struct riva_data *drv;
struct resource *res;
struct pil_desc *desc;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -321,6 +323,23 @@
if (!desc)
return -ENOMEM;
+ drv->pll_supply = regulator_get(&pdev->dev, "pll_vdd");
+ if (IS_ERR(drv->pll_supply)) {
+ dev_err(&pdev->dev, "failed to get pll supply\n");
+ return PTR_ERR(drv->pll_supply);
+ }
+ ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set pll supply voltage\n");
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set pll supply optimum mode\n");
+ goto err;
+ }
+
desc->name = "wcnss";
desc->dev = &pdev->dev;
@@ -332,17 +351,31 @@
dev_info(&pdev->dev, "using non-secure boot\n");
}
- setup_timer(&drv->xo_timer, pil_riva_remove_xo_proxy_votes,
- (unsigned long)drv);
drv->xo = msm_xo_get(MSM_XO_CXO, desc->name);
- if (IS_ERR(drv->xo))
- return PTR_ERR(drv->xo);
+ if (IS_ERR(drv->xo)) {
+ ret = PTR_ERR(drv->xo);
+ goto err;
+ }
+ INIT_DELAYED_WORK(&drv->work, pil_riva_remove_proxy_votes);
- return msm_pil_register(desc);
+ ret = msm_pil_register(desc);
+ if (ret)
+ goto err_register;
+ return 0;
+err_register:
+ cancel_delayed_work_sync(&drv->work);
+ msm_xo_put(drv->xo);
+err:
+ regulator_put(drv->pll_supply);
+ return ret;
}
static int __devexit pil_riva_remove(struct platform_device *pdev)
{
+ struct riva_data *drv = platform_get_drvdata(pdev);
+ cancel_delayed_work_sync(&drv->work);
+ msm_xo_put(drv->xo);
+ regulator_put(drv->pll_supply);
return 0;
}