msm: pil-q6v5: Migrate to clock APIs
Rather than writing to clock controller registers directly, utilize
the clock driver and its recently-added support for LPASS and MSS
clocks needed by PIL.
Since clock hardware differences are hidden under the clock APIs,
clock control can be moved from pil-q6v5-lpass into the pil-q6v5
library and shared with other PIL drivers that will eventually use
it.
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
Change-Id: I70d67d6abded93bcc5119e5bcec4c0211f157ac0
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index 002431a..308f992 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -7,17 +7,15 @@
Required properties:
- compatible: Must be "qcom,pil-q6v5-lpass"
-- reg: Three pairs of physical base addresses and region sizes
+- reg: Two pairs of physical base addresses and region sizes
of memory mapped registers. The first region corresponds
- to QDSP6SS_PUB, the second corresponds to LPASS_CC, and
- the third to LPASS_HALTREQ.
+ to QDSP6SS_PUB, and the second to LPASS_HALTREQ.
- qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
Example:
qcom,lpass@fe200000 {
compatible = "qcom,pil-q6v5-lpass";
reg = <0xfe200000 0x00100>,
- <0xfe000000 0x40000>,
<0xfd485100 0x00010>;
qcom,firmware-name = "lpass";
diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi
index 69aff33..66f5fa1 100644
--- a/arch/arm/boot/dts/msmcopper.dtsi
+++ b/arch/arm/boot/dts/msmcopper.dtsi
@@ -276,7 +276,6 @@
qcom,lpass@fe200000 {
compatible = "qcom,pil-q6v5-lpass";
reg = <0xfe200000 0x00100>,
- <0xfe000000 0x40000>,
<0xfd485100 0x00010>;
qcom,firmware-name = "lpass";
diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c
index 03667d7..80403f8 100644
--- a/arch/arm/mach-msm/clock-copper.c
+++ b/arch/arm/mach-msm/clock-copper.c
@@ -4906,8 +4906,8 @@
CLK_LOOKUP("core_clk", mss_xo_q6_clk.c, ""),
CLK_LOOKUP("bus_clk", mss_bus_q6_clk.c, ""),
- CLK_LOOKUP("core_clk", q6ss_xo_clk.c, ""),
- CLK_LOOKUP("bus_clk", q6ss_ahb_lfabif_clk.c, ""),
+ CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "pil-q6v5-lpass"),
+ CLK_LOOKUP("bus_clk", q6ss_ahb_lfabif_clk.c, "pil-q6v5-lpass"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, ""),
CLK_LOOKUP("bus_clk", gcc_mss_cfg_ahb_clk.c, ""),
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index 5eac539..8691ac7 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -18,37 +18,19 @@
#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/clk.h>
#include "peripheral-loader.h"
#include "pil-q6v5.h"
/* Register Offsets */
#define QDSP6SS_RST_EVB 0x010
-#define LPASS_Q6SS_BCR 0x06000
-#define LPASS_Q6SS_AHB_LFABIF_CBCR 0x22000
-#define LPASS_Q6SS_XO_CBCR 0x26000
#define AXI_HALTREQ 0x0
#define AXI_HALTACK 0x4
#define AXI_IDLE 0x8
#define HALT_ACK_TIMEOUT_US 100000
-static void clk_reg_enable(void __iomem *reg)
-{
- u32 val;
- val = readl_relaxed(reg);
- val |= BIT(0);
- writel_relaxed(val, reg);
-}
-
-static void clk_reg_disable(void __iomem *reg)
-{
- u32 val;
- val = readl_relaxed(reg);
- val &= ~BIT(0);
- writel_relaxed(val, reg);
-}
-
static int pil_lpass_shutdown(struct pil_desc *pil)
{
struct q6v5_data *drv = dev_get_drvdata(pil->dev);
@@ -64,17 +46,18 @@
dev_err(pil->dev, "Port halt failed\n");
writel_relaxed(0, drv->axi_halt_base + AXI_HALTREQ);
- /* Make sure Q6 registers are accessible */
- writel_relaxed(0, drv->clk_base + LPASS_Q6SS_BCR);
- clk_reg_enable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
- mb();
+ /*
+ * If the shutdown function is called before the reset function, clocks
+ * will not be enabled yet. Enable them here so that register writes
+ * performed during the shutdown succeed.
+ */
+ if (drv->is_booted == false)
+ pil_q6v5_enable_clks(pil);
pil_q6v5_shutdown(pil);
+ pil_q6v5_disable_clks(pil);
- /* Disable clocks and assert subsystem resets. */
- clk_reg_disable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
- clk_reg_disable(drv->clk_base + LPASS_Q6SS_XO_CBCR);
- writel_relaxed(1, drv->clk_base + LPASS_Q6SS_BCR);
+ drv->is_booted = false;
return 0;
}
@@ -82,21 +65,25 @@
static int pil_lpass_reset(struct pil_desc *pil)
{
struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ int ret;
- /*
- * Bring subsystem out of reset and enable required
- * regulators and clocks.
- */
- writel_relaxed(0, drv->clk_base + LPASS_Q6SS_BCR);
- clk_reg_enable(drv->clk_base + LPASS_Q6SS_XO_CBCR);
- clk_reg_enable(drv->clk_base + LPASS_Q6SS_AHB_LFABIF_CBCR);
- mb();
+ ret = pil_q6v5_enable_clks(pil);
+ if (ret)
+ return ret;
/* Program Image Address */
writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
drv->reg_base + QDSP6SS_RST_EVB);
- return pil_q6v5_reset(pil);
+ ret = pil_q6v5_reset(pil);
+ if (ret) {
+ pil_q6v5_disable_clks(pil);
+ return ret;
+ }
+
+ drv->is_booted = true;
+
+ return 0;
}
static struct pil_reset_ops pil_lpass_ops = {
@@ -124,7 +111,7 @@
desc->ops = &pil_lpass_ops;
desc->owner = THIS_MODULE;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!drv->axi_halt_base)
diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c
index cd58a4c..3b9d542 100644
--- a/arch/arm/mach-msm/pil-q6v5.c
+++ b/arch/arm/mach-msm/pil-q6v5.c
@@ -20,6 +20,8 @@
#include <linux/of.h>
#include <linux/clk.h>
+#include <mach/clk.h>
+
#include "peripheral-loader.h"
#include "pil-q6v5.h"
@@ -76,6 +78,42 @@
}
EXPORT_SYMBOL(pil_q6v5_init_image);
+int pil_q6v5_enable_clks(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+ int ret;
+
+ ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT);
+ if (ret)
+ goto err_reset;
+ ret = clk_prepare_enable(drv->core_clk);
+ if (ret)
+ goto err_core_clk;
+ ret = clk_prepare_enable(drv->bus_clk);
+ if (ret)
+ goto err_bus_clk;
+
+ return 0;
+
+err_bus_clk:
+ clk_disable_unprepare(drv->core_clk);
+err_core_clk:
+ clk_reset(drv->core_clk, CLK_RESET_ASSERT);
+err_reset:
+ return ret;
+}
+EXPORT_SYMBOL(pil_q6v5_enable_clks);
+
+void pil_q6v5_disable_clks(struct pil_desc *pil)
+{
+ struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+
+ clk_disable_unprepare(drv->bus_clk);
+ clk_disable_unprepare(drv->core_clk);
+ clk_reset(drv->core_clk, CLK_RESET_ASSERT);
+}
+EXPORT_SYMBOL(pil_q6v5_disable_clks);
+
void pil_q6v5_shutdown(struct pil_desc *pil)
{
u32 val;
@@ -173,12 +211,6 @@
if (!drv->reg_base)
return ERR_PTR(-ENOMEM);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- drv->clk_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!drv->clk_base)
- return ERR_PTR(-ENOMEM);
-
desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
@@ -192,6 +224,14 @@
if (IS_ERR(drv->xo))
return ERR_CAST(drv->xo);
+ drv->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(drv->bus_clk))
+ return ERR_CAST(drv->bus_clk);
+
+ drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(drv->core_clk))
+ return ERR_CAST(drv->core_clk);
+
desc->dev = &pdev->dev;
return desc;
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index b17d4e7..5f283da 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -21,14 +21,15 @@
struct q6v5_data {
void __iomem *reg_base;
- void __iomem *clk_base;
+ struct clk *xo;
+ struct clk *bus_clk;
+ struct clk *core_clk;
void __iomem *axi_halt_base;
void __iomem *rmb_base;
unsigned long start_addr;
struct regulator *vreg;
- bool vreg_enabled;
+ bool is_booted;
int self_auth;
- struct clk *xo;
struct pil_device *pil;
};
@@ -38,6 +39,8 @@
size_t size);
void pil_q6v5_shutdown(struct pil_desc *pil);
int pil_q6v5_reset(struct pil_desc *pil);
+int pil_q6v5_enable_clks(struct pil_desc *pil);
+void pil_q6v5_disable_clks(struct pil_desc *pil);
struct pil_desc *pil_q6v5_init(struct platform_device *pdev);
#endif