From: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
Mike Turquette
<mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [RFC/PATCH 05/13] clk: msm: Add support for phase locked loops (PLLs)
Date: Wed, 12 Jun 2013 18:49:01 -0700 [thread overview]
Message-ID: <1371088149-22562-6-git-send-email-sboyd@codeaurora.org> (raw)
In-Reply-To: <1371088149-22562-1-git-send-email-sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Add support for MSM's PLLs (phase locked loops). This is
sufficient enough to be able to determine the rate the PLL is
running at.
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Signed-off-by: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
Documentation/devicetree/bindings/clock/msm.txt | 40 ++++
drivers/clk/Kconfig | 2 +
drivers/clk/Makefile | 1 +
drivers/clk/msm/Kconfig | 4 +
drivers/clk/msm/Makefile | 3 +
drivers/clk/msm/clk-pll.c | 233 ++++++++++++++++++++++++
drivers/clk/msm/clk-pll.h | 43 +++++
7 files changed, 326 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/msm.txt
create mode 100644 drivers/clk/msm/Kconfig
create mode 100644 drivers/clk/msm/Makefile
create mode 100644 drivers/clk/msm/clk-pll.c
create mode 100644 drivers/clk/msm/clk-pll.h
diff --git a/Documentation/devicetree/bindings/clock/msm.txt b/Documentation/devicetree/bindings/clock/msm.txt
new file mode 100644
index 0000000..2192621
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/msm.txt
@@ -0,0 +1,40 @@
+Bindings for Qualcomm's clock controllers
+
+These bindings use the common clock binding
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+PLL Binding
+-----------
+
+Required properties:
+- compatible : shall be "qcom,pll".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the reference clock
+
+Example:
+ pll8: pll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+Voteable PLL Binding
+--------------------
+
+Voteable PLLs are PLLs surrounded by a voting wrapper that aggregates
+votes from multiple masters in the system and enables or disables the
+PLL according to the current vote.
+
+Required properties:
+- compatible: shall be "qcom,pll-vote"
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the pll that is wrapped
+ in voting logic
+
+Example:
+ vpll8: vpll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll-vote";
+ clocks = <&pll8>;
+ };
+
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 0357ac4..acdb826 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -81,6 +81,8 @@ config COMMON_CLK_AXI_CLKGEN
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
FPGAs. It is commonly used in Analog Devices' reference designs.
+source "drivers/clk/msm/Kconfig"
+
endmenu
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 137d3e7..c9e768b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
+obj-$(CONFIG_COMMON_CLK_MSM) += msm/
obj-$(CONFIG_PLAT_ORION) += mvebu/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
new file mode 100644
index 0000000..bf7e3d2
--- /dev/null
+++ b/drivers/clk/msm/Kconfig
@@ -0,0 +1,4 @@
+menuconfig COMMON_CLK_MSM
+ tristate "Support for Qualcomm's MSM designs"
+ depends on OF
+
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
new file mode 100644
index 0000000..16b750f
--- /dev/null
+++ b/drivers/clk/msm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_COMMON_CLK_MSM) += clk-msm.o
+
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
diff --git a/drivers/clk/msm/clk-pll.c b/drivers/clk/msm/clk-pll.c
new file mode 100644
index 0000000..03c2c41
--- /dev/null
+++ b/drivers/clk/msm/clk-pll.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <asm/div64.h>
+
+#include "clk-pll.h"
+
+/**
+ * struct clk_pll - phase locked loop (PLL)
+ * @l_reg: L register
+ * @m_reg: M register
+ * @n_reg: N register
+ * @config_reg: config register
+ * @mode_reg: mode register
+ * @status_reg: status register
+ * @status_bit: ANDed with @status_reg to determine if PLL is enabled
+ * @hw: handle between common and hardware-specific interfaces
+ */
+struct clk_pll {
+ void __iomem *l_reg;
+ void __iomem *m_reg;
+ void __iomem *n_reg;
+ void __iomem *config_reg;
+ void __iomem *mode_reg;
+ void __iomem *status_reg;
+ u8 status_bit;
+
+ struct clk_hw hw;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mode;
+
+ mode = readl_relaxed(pll->mode_reg);
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel(mode, pll->mode_reg);
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel(mode, pll->mode_reg);
+
+ /* Wait until PLL is locked. */
+ udelay(50);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel(mode, pll->mode_reg);
+
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mode;
+
+ mode = readl_relaxed(pll->mode_reg);
+ mode &= ~(PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL);
+ writel_relaxed(mode, pll->mode_reg);
+}
+
+static unsigned long
+clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 l, m, n;
+ unsigned long rate;
+ u64 tmp;
+
+ l = readl_relaxed(pll->l_reg) & 0x3ff;
+ m = readl_relaxed(pll->m_reg) & 0x7ffff;
+ n = readl_relaxed(pll->n_reg) & 0x7ffff;
+
+ rate = parent_rate * l;
+ if (n) {
+ tmp = parent_rate;
+ tmp *= m;
+ do_div(tmp, n);
+ rate += tmp;
+ }
+ return rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+struct clk *pll_clk_register(struct device *dev, struct pll_desc *desc,
+ struct clk_init_data *init)
+{
+ struct clk_pll *p;
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->l_reg = desc->base + desc->l_reg;
+ p->m_reg = desc->base + desc->m_reg;
+ p->n_reg = desc->base + desc->n_reg;
+ p->config_reg = desc->base + desc->config_reg;
+ p->mode_reg = desc->base + desc->mode_reg;
+ p->status_reg = desc->base + desc->status_reg;
+ p->status_bit = desc->status_bit;
+
+ init->ops = &clk_pll_ops;
+ p->hw.init = init;
+
+ return devm_clk_register(dev, &p->hw);
+}
+
+/**
+ * struct clk_pll_vote - phase locked loop (PLL) with hardware voting wrapper
+ * @vote_reg: Voting register
+ * @vote_bit: ORed into @vote_reg to enable PLL
+ * @hw: handle between common and hardware-specific interfaces
+ */
+struct clk_pll_vote {
+ void __iomem *vote_reg;
+ u8 vote_bit;
+ struct clk_hw hw;
+};
+
+#define to_clk_pll_vote(_hw) container_of(_hw, struct clk_pll_vote, hw)
+
+static DEFINE_SPINLOCK(pll_vote_lock);
+
+static int wait_for_pll(struct clk_pll *pll)
+{
+ int count;
+ const char *name = __clk_get_name(pll->hw.clk);
+
+ /* Wait for pll to enable. */
+ for (count = 200; count > 0; count--) {
+ if (readl_relaxed(pll->status_reg) & BIT(pll->status_bit))
+ return 0;
+ udelay(1);
+ }
+
+ WARN("%s didn't enable after voting for it!\n", name);
+ return -ETIMEDOUT;
+}
+
+static int clk_pll_vote_enable(struct clk_hw *hw)
+{
+ u32 val;
+ unsigned long flags;
+ struct clk_pll_vote *pll = to_clk_pll_vote(hw);
+ struct clk_pll *p = to_clk_pll(__clk_get_hw(__clk_get_parent(hw->clk)));
+
+ spin_lock_irqsave(&pll_vote_lock, flags);
+
+ val = readl_relaxed(pll->vote_reg);
+ val |= BIT(pll->vote_bit);
+ writel(val, pll->vote_reg);
+
+ spin_unlock_irqrestore(&pll_vote_lock, flags);
+
+ return wait_for_pll(p);
+}
+
+static void clk_pll_vote_disable(struct clk_hw *hw)
+{
+ u32 val;
+ unsigned long flags;
+ struct clk_pll_vote *pll = to_clk_pll_vote(hw);
+
+ spin_lock_irqsave(&pll_vote_lock, flags);
+
+ val = readl_relaxed(pll->vote_reg);
+ val &= ~BIT(pll->vote_bit);
+ writel_relaxed(val, pll->vote_reg);
+
+ spin_unlock_irqrestore(&pll_vote_lock, flags);
+}
+
+static const struct clk_ops clk_pll_vote_ops = {
+ .enable = clk_pll_vote_enable,
+ .disable = clk_pll_vote_disable,
+};
+
+struct clk *pll_vote_clk_register(struct device *dev,
+ struct pll_vote_desc *desc, struct clk_init_data *init)
+{
+ struct clk_pll_vote *p;
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->vote_reg = desc->base + desc->vote_reg;
+ p->vote_bit = desc->vote_bit;
+
+ init->ops = &clk_pll_vote_ops;
+ p->hw.init = init;
+
+ return devm_clk_register(dev, &p->hw);
+}
diff --git a/drivers/clk/msm/clk-pll.h b/drivers/clk/msm/clk-pll.h
new file mode 100644
index 0000000..4e63a5e
--- /dev/null
+++ b/drivers/clk/msm/clk-pll.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CLK_PLL_H__
+#define __MSM_CLK_PLL_H__
+
+struct device;
+struct clk;
+struct clk_init_data;
+
+struct pll_desc {
+ void __iomem *base;
+ u16 l_reg;
+ u16 m_reg;
+ u16 n_reg;
+ u16 config_reg;
+ u16 mode_reg;
+ u16 status_reg;
+ u8 status_bit;
+};
+
+struct pll_vote_desc {
+ void __iomem *base;
+ u16 vote_reg;
+ u8 vote_bit;
+};
+
+extern struct clk *pll_clk_register(struct device *dev, struct pll_desc *desc,
+ struct clk_init_data *init);
+extern struct clk *pll_vote_clk_register(struct device *dev,
+ struct pll_vote_desc *desc, struct clk_init_data *init);
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
next prev parent reply other threads:[~2013-06-13 1:49 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-06-13 1:48 [RFC/PATCH 00/13] Add support for MSM's mmio clocks Stephen Boyd
[not found] ` <1371088149-22562-1-git-send-email-sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2013-06-13 1:49 ` Stephen Boyd [this message]
2013-06-13 1:49 ` [RFC/PATCH 06/13] clk: msm: Add support for root clock generators (RCGs) Stephen Boyd
2013-06-13 1:49 ` [RFC/PATCH 07/13] clk: msm: Add support for branches/gate clocks Stephen Boyd
2013-06-13 1:49 ` [RFC/PATCH 09/13] clk: msm: Add support for MSM8960's global clock controller (GCC) Stephen Boyd
2013-06-13 1:49 ` [RFC/PATCH 10/13] clk: msm: Add support for MSM8960's multimedia clock controller (MMCC) Stephen Boyd
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1371088149-22562-6-git-send-email-sboyd@codeaurora.org \
--to=sboyd-sgv2jx0feol9jmxxk+q4oq@public.gmane.org \
--cc=devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org \
--cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
--cc=linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).