linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: davidb@codeaurora.org (David Brown)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 18/34] msm: clock-local: Add support for 8x60 clock types
Date: Wed,  2 Nov 2011 11:36:15 -0700	[thread overview]
Message-ID: <1320258991-22325-19-git-send-email-davidb@codeaurora.org> (raw)
In-Reply-To: <1320258991-22325-1-git-send-email-davidb@codeaurora.org>

From: Matt Wagantall <mattw@codeaurora.org>

Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
---
 arch/arm/mach-msm/clock-local.c |  290 ++++++++++++++++++++++++++++++++++++---
 arch/arm/mach-msm/clock-local.h |   47 ++++++-
 2 files changed, 318 insertions(+), 19 deletions(-)

diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 7a6dc5d..3ee318d 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -81,11 +81,185 @@ void set_rate_nop(struct rcg_clk *clk, struct clk_freq_tbl *nf)
 	 */
 }
 
-int (*soc_update_sys_vdd)(enum sys_vdd_level level);
+void set_rate_mnd_8(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+{
+	u32 ctl_reg_val;
+
+	/* Assert MND reset. */
+	ctl_reg_val = readl_relaxed(clk->b.ctl_reg);
+	ctl_reg_val |= BIT(8);
+	writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+
+	/* Program M and D values. */
+	writel_relaxed(nf->md_val, clk->md_reg);
+
+	/* Program MN counter Enable and Mode. */
+	ctl_reg_val &= ~(clk->ctl_mask);
+	ctl_reg_val |= nf->ctl_val;
+	writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+
+	/* Deassert MND reset. */
+	ctl_reg_val &= ~BIT(8);
+	writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+}
+
+void set_rate_mnd_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+{
+	struct bank_masks *banks = clk->bank_masks;
+	const struct bank_mask_info *new_bank_masks;
+	const struct bank_mask_info *old_bank_masks;
+	u32 ns_reg_val, ctl_reg_val;
+	u32 bank_sel;
+
+	/*
+	 * Determine active bank and program the other one. If the clock is
+	 * off, program the active bank since bank switching won't work if
+	 * both banks aren't running.
+	 */
+	ctl_reg_val = readl_relaxed(clk->b.ctl_reg);
+	bank_sel = !!(ctl_reg_val & banks->bank_sel_mask);
+	 /* If clock isn't running, don't switch banks. */
+	bank_sel ^= (!clk->enabled || clk->current_freq->freq_hz == 0);
+	if (bank_sel == 0) {
+		new_bank_masks = &banks->bank1_mask;
+		old_bank_masks = &banks->bank0_mask;
+	} else {
+		new_bank_masks = &banks->bank0_mask;
+		old_bank_masks = &banks->bank1_mask;
+	}
+
+	ns_reg_val = readl_relaxed(clk->ns_reg);
+
+	/* Assert bank MND reset. */
+	ns_reg_val |= new_bank_masks->rst_mask;
+	writel_relaxed(ns_reg_val, clk->ns_reg);
+
+	/*
+	 * Program NS only if the clock is enabled, since the NS will be set
+	 * as part of the enable procedure and should remain with a low-power
+	 * MUX input selected until then.
+	 */
+	if (clk->enabled) {
+		ns_reg_val &= ~(new_bank_masks->ns_mask);
+		ns_reg_val |= (nf->ns_val & new_bank_masks->ns_mask);
+		writel_relaxed(ns_reg_val, clk->ns_reg);
+	}
+
+	writel_relaxed(nf->md_val, new_bank_masks->md_reg);
+
+	/* Enable counter only if clock is enabled. */
+	if (clk->enabled)
+		ctl_reg_val |= new_bank_masks->mnd_en_mask;
+	else
+		ctl_reg_val &= ~(new_bank_masks->mnd_en_mask);
+
+	ctl_reg_val &= ~(new_bank_masks->mode_mask);
+	ctl_reg_val |= (nf->ctl_val & new_bank_masks->mode_mask);
+	writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+
+	/* Deassert bank MND reset. */
+	ns_reg_val &= ~(new_bank_masks->rst_mask);
+	writel_relaxed(ns_reg_val, clk->ns_reg);
+
+	/*
+	 * Switch to the new bank if clock is running.  If it isn't, then
+	 * no switch is necessary since we programmed the active bank.
+	 */
+	if (clk->enabled && clk->current_freq->freq_hz) {
+		ctl_reg_val ^= banks->bank_sel_mask;
+		writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+		/*
+		 * Wait at least 6 cycles of slowest bank's clock
+		 * for the glitch-free MUX to fully switch sources.
+		 */
+		mb();
+		udelay(1);
+
+		/* Disable old bank's MN counter. */
+		ctl_reg_val &= ~(old_bank_masks->mnd_en_mask);
+		writel_relaxed(ctl_reg_val, clk->b.ctl_reg);
+
+		/* Program old bank to a low-power source and divider. */
+		ns_reg_val &= ~(old_bank_masks->ns_mask);
+		ns_reg_val |= (clk->freq_tbl->ns_val & old_bank_masks->ns_mask);
+		writel_relaxed(ns_reg_val, clk->ns_reg);
+	}
+
+	/*
+	 * If this freq requires the MN counter to be enabled,
+	 * update the enable mask to match the current bank.
+	 */
+	if (nf->mnd_en_mask)
+		nf->mnd_en_mask = new_bank_masks->mnd_en_mask;
+	/* Update the NS mask to match the current bank. */
+	clk->ns_mask = new_bank_masks->ns_mask;
+}
+
+void set_rate_div_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+{
+	struct bank_masks *banks = clk->bank_masks;
+	const struct bank_mask_info *new_bank_masks;
+	const struct bank_mask_info *old_bank_masks;
+	u32 ns_reg_val, bank_sel;
+
+	/*
+	 * Determine active bank and program the other one. If the clock is
+	 * off, program the active bank since bank switching won't work if
+	 * both banks aren't running.
+	 */
+	ns_reg_val = readl_relaxed(clk->ns_reg);
+	bank_sel = !!(ns_reg_val & banks->bank_sel_mask);
+	 /* If clock isn't running, don't switch banks. */
+	bank_sel ^= (!clk->enabled || clk->current_freq->freq_hz == 0);
+	if (bank_sel == 0) {
+		new_bank_masks = &banks->bank1_mask;
+		old_bank_masks = &banks->bank0_mask;
+	} else {
+		new_bank_masks = &banks->bank0_mask;
+		old_bank_masks = &banks->bank1_mask;
+	}
+
+	/*
+	 * Program NS only if the clock is enabled, since the NS will be set
+	 * as part of the enable procedure and should remain with a low-power
+	 * MUX input selected until then.
+	 */
+	if (clk->enabled) {
+		ns_reg_val &= ~(new_bank_masks->ns_mask);
+		ns_reg_val |= (nf->ns_val & new_bank_masks->ns_mask);
+		writel_relaxed(ns_reg_val, clk->ns_reg);
+	}
+
+	/*
+	 * Switch to the new bank if clock is running.  If it isn't, then
+	 * no switch is necessary since we programmed the active bank.
+	 */
+	if (clk->enabled && clk->current_freq->freq_hz) {
+		ns_reg_val ^= banks->bank_sel_mask;
+		writel_relaxed(ns_reg_val, clk->ns_reg);
+		/*
+		 * Wait at least 6 cycles of slowest bank's clock
+		 * for the glitch-free MUX to fully switch sources.
+		 */
+		mb();
+		udelay(1);
+
+		/* Program old bank to a low-power source and divider. */
+		ns_reg_val &= ~(old_bank_masks->ns_mask);
+		ns_reg_val |= (clk->freq_tbl->ns_val & old_bank_masks->ns_mask);
+		writel_relaxed(ns_reg_val, clk->ns_reg);
+	}
+
+	/* Update the NS mask to match the current bank. */
+	clk->ns_mask = new_bank_masks->ns_mask;
+}
+
 /*
  * SYS_VDD voting functions
  */
 
+int (*soc_update_sys_vdd)(enum sys_vdd_level level);
+
 /* Update system voltage level given the current votes. */
 static int local_update_sys_vdd(void)
 {
@@ -392,17 +566,22 @@ static int _rcg_clk_set_rate(struct rcg_clk *clk, struct clk_freq_tbl *nf)
 
 	spin_lock(&local_clock_reg_lock);
 
-	/* Disable all branches to prevent glitches. */
-	list_for_each_entry(chld, &clk->c.children, siblings) {
-		struct branch_clk *x = to_branch_clk(chld);
-		/* Don't bother turning off if it is already off.
-		 * Checking ch->enabled is cheaper (cache) than reading
-		 * and writing to a register (uncached/unbuffered). */
-		if (x->enabled)
-			__branch_clk_disable_reg(&x->b, x->c.dbg_name);
+	/* Disable branch if clock isn't dual-banked with a glitch-free MUX. */
+	if (clk->bank_masks == NULL) {
+		/* Disable all branches to prevent glitches. */
+		list_for_each_entry(chld, &clk->c.children, siblings) {
+			struct branch_clk *x = to_branch_clk(chld);
+			/*
+			 * We don't need to grab the child's lock because
+			 * we hold the local_clock_reg_lock and 'enabled' is
+			 * only modified within lock.
+			 */
+			if (x->enabled)
+				__branch_clk_disable_reg(&x->b, x->c.dbg_name);
+		}
+		if (clk->enabled)
+			__rcg_clk_disable_reg(clk);
 	}
-	if (clk->enabled)
-		__rcg_clk_disable_reg(clk);
 
 	/* Perform clock-specific frequency switch operations. */
 	BUG_ON(!clk->set_rate);
@@ -414,13 +593,16 @@ static int _rcg_clk_set_rate(struct rcg_clk *clk, struct clk_freq_tbl *nf)
 	 */
 	clk->current_freq = nf;
 
-	if (clk->enabled)
-		__rcg_clk_enable_reg(clk);
-	/* Enable only branches that were ON before. */
-	list_for_each_entry(chld, &clk->c.children, siblings) {
-		struct branch_clk *x = to_branch_clk(chld);
-		if (x->enabled)
-			__branch_clk_enable_reg(&x->b, x->c.dbg_name);
+	/* Enable any clocks that were disabled. */
+	if (clk->bank_masks == NULL) {
+		if (clk->enabled)
+			__rcg_clk_enable_reg(clk);
+		/* Enable only branches that were ON before. */
+		list_for_each_entry(chld, &clk->c.children, siblings) {
+			struct branch_clk *x = to_branch_clk(chld);
+			if (x->enabled)
+				__branch_clk_enable_reg(&x->b, x->c.dbg_name);
+		}
 	}
 
 	spin_unlock(&local_clock_reg_lock);
@@ -583,6 +765,78 @@ struct clk_ops clk_ops_pll_vote = {
 	.is_local = local_clk_is_local,
 };
 
+static int pll_clk_enable(struct clk *clk)
+{
+	u32 mode;
+	unsigned long flags;
+	struct pll_clk *pll = to_pll_clk(clk);
+
+	spin_lock_irqsave(&local_clock_reg_lock, flags);
+	mode = readl_relaxed(pll->mode_reg);
+	/* Disable PLL bypass mode. */
+	mode |= BIT(1);
+	writel_relaxed(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.
+	 */
+	mb();
+	udelay(10);
+
+	/* De-assert active-low PLL reset. */
+	mode |= BIT(2);
+	writel_relaxed(mode, pll->mode_reg);
+
+	/* Wait until PLL is locked. */
+	mb();
+	udelay(50);
+
+	/* Enable PLL output. */
+	mode |= BIT(0);
+	writel_relaxed(mode, pll->mode_reg);
+
+	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+	return 0;
+}
+
+static void pll_clk_disable(struct clk *clk)
+{
+	u32 mode;
+	unsigned long flags;
+	struct pll_clk *pll = to_pll_clk(clk);
+
+	/*
+	 * Disable the PLL output, disable test mode, enable
+	 * the bypass mode, and assert the reset.
+	 */
+	spin_lock_irqsave(&local_clock_reg_lock, flags);
+	mode = readl_relaxed(pll->mode_reg);
+	mode &= ~BM(3, 0);
+	writel_relaxed(mode, pll->mode_reg);
+	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static unsigned pll_clk_get_rate(struct clk *clk)
+{
+	struct pll_clk *pll = to_pll_clk(clk);
+	return pll->rate;
+}
+
+static struct clk *pll_clk_get_parent(struct clk *clk)
+{
+	struct pll_clk *pll = to_pll_clk(clk);
+	return pll->parent;
+}
+
+struct clk_ops clk_ops_pll = {
+	.enable = pll_clk_enable,
+	.disable = pll_clk_disable,
+	.get_rate = pll_clk_get_rate,
+	.get_parent = pll_clk_get_parent,
+	.is_local = local_clk_is_local,
+};
+
 struct clk_ops clk_ops_gnd = {
 	.get_rate = fixed_clk_get_rate,
 	.is_local = local_clk_is_local,
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index a7c9001..e004490 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -46,6 +46,23 @@ struct clk_freq_tbl {
 	void		*const extra_freq_data;
 };
 
+/* Some clocks have two banks to avoid glitches when switching frequencies.
+ * The unused bank is programmed while running on the other bank, and
+ * switched to afterwards. The following two structs describe the banks. */
+struct bank_mask_info {
+	void *const md_reg;
+	const uint32_t	ns_mask;
+	const uint32_t	rst_mask;
+	const uint32_t	mnd_en_mask;
+	const uint32_t	mode_mask;
+};
+
+struct bank_masks {
+	const uint32_t			bank_sel_mask;
+	const struct bank_mask_info	bank0_mask;
+	const struct bank_mask_info	bank1_mask;
+};
+
 #define F_RAW(f, sc, m_v, n_v, c_v, m_m, v, e) { \
 	.freq_hz = f, \
 	.src_clk = sc, \
@@ -98,8 +115,9 @@ struct rcg_clk {
 	const uint32_t	root_en_mask;
 	uint32_t	ns_mask;
 	const uint32_t	ctl_mask;
+	struct bank_masks *const bank_masks;
 	struct clk *depends;
-	void		(*set_rate)(struct clk_local *, struct clk_freq_tbl *);
+	void   (*set_rate)(struct rcg_clk *, struct clk_freq_tbl *);
 	struct clk_freq_tbl *const freq_tbl;
 	struct clk_freq_tbl *current_freq;
 
@@ -118,6 +136,7 @@ int rcg_clk_set_rate(struct clk *clk, unsigned rate);
 int rcg_clk_set_min_rate(struct clk *clk, unsigned rate);
 int rcg_clk_set_max_rate(struct clk *clk, unsigned rate);
 unsigned rcg_clk_get_rate(struct clk *clk);
+int rcg_clk_list_rate(struct clk *clk, unsigned n);
 unsigned rcg_clk_is_enabled(struct clk *clk);
 long rcg_clk_round_rate(struct clk *clk, unsigned rate);
 struct clk *rcg_clk_get_parent(struct clk *c);
@@ -184,6 +203,29 @@ static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *clk)
 }
 
 /**
+ * struct pll_clk - phase locked loop
+ * @rate: output rate
+ * @mode_reg: enable register
+ * @parent: clock source
+ * @c: clk
+ */
+struct pll_clk {
+	unsigned long rate;
+
+	void __iomem *const mode_reg;
+
+	struct clk *parent;
+	struct clk c;
+};
+
+extern struct clk_ops clk_ops_pll;
+
+static inline struct pll_clk *to_pll_clk(struct clk *clk)
+{
+	return container_of(clk, struct pll_clk, c);
+}
+
+/**
  * struct branch_clk - branch
  * @enabled: true if clock is on, false otherwise
  * @b: branch
@@ -237,6 +279,9 @@ extern int (*soc_update_sys_vdd)(enum sys_vdd_level level);
  */
 void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf);
 void set_rate_nop(struct rcg_clk *clk, struct clk_freq_tbl *nf);
+void set_rate_mnd_8(struct rcg_clk *clk, struct clk_freq_tbl *nf);
+void set_rate_mnd_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf);
+void set_rate_div_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf);
 
 #endif /* __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H */
 
-- 
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

  parent reply	other threads:[~2011-11-02 18:36 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-11-02 18:35 [RFC PATCH 00/34] msm: msm8660 and msm8960 clock support David Brown
2011-11-02 18:35 ` [RFC PATCH 01/34] msm: clock-pcom: Mark functions static David Brown
2011-11-02 18:35 ` [RFC PATCH 02/34] msm: clock: Always use an array to iterate over clocks David Brown
2011-11-02 19:45   ` Russell King - ARM Linux
2011-11-02 21:34     ` Stephen Boyd
2011-11-02 18:36 ` [RFC PATCH 03/34] msm: clock: Pass struct clk to the clk_ops David Brown
2011-11-02 18:36 ` [RFC PATCH 04/34] msm: clock: Support one lock per clock David Brown
2011-11-02 18:36 ` [RFC PATCH 05/34] msm: clock-pcom: Introduce a struct pcom_clk David Brown
2011-11-02 18:36 ` [RFC PATCH 06/34] msm: clock: Support clk_[s|g]et_parent() clk_ops David Brown
2011-11-02 18:36 ` [RFC PATCH 07/34] msm: clock-debug: Use clk_enable()/clk_disable() directly David Brown
2011-11-02 18:36 ` [RFC PATCH 08/34] msm: clock: Enable/disable parent clocks generically David Brown
2011-11-02 18:36 ` [RFC PATCH 09/34] msm: clock: Implement rate voting David Brown
2011-11-02 18:36 ` [RFC PATCH 10/34] msm: clock-pcom: Add pbus specific clock ops David Brown
2011-11-02 18:36 ` [RFC PATCH 11/34] msm: Migrate to clock rate voting David Brown
2011-11-02 18:36 ` [RFC PATCH 12/34] msm: clock: Make most clk_*() operations optional David Brown
2011-11-02 18:36 ` [RFC PATCH 13/34] msm: clock-debug: Implement a default is_enabled() David Brown
2011-11-02 18:36 ` [RFC PATCH 14/34] msm: proc_comm: Add CLKCTL_RPC_SRC_REQUEST David Brown
2011-11-02 18:36 ` [RFC PATCH 15/34] msm: clock: Add local clock control framework David Brown
2011-11-02 18:36 ` [RFC PATCH 16/34] msm: clock-pcom: Expose pc_clk_reset David Brown
2011-11-02 18:36 ` [RFC PATCH 17/34] msm: clock: Add 7x30 local clock support David Brown
2011-11-02 18:36 ` David Brown [this message]
2011-11-02 18:36 ` [RFC PATCH 20/34] msm: clock: Add list_rate debugfs nodes for locally-controlled clocks David Brown
2011-11-02 18:36 ` [RFC PATCH 21/34] msm: clock: Add debugfs interface to measure clock rates David Brown
2011-11-02 18:36 ` [RFC PATCH 22/34] msm: clock-8x60: Support measurement of CPU and L2 clocks David Brown
2011-11-02 18:36 ` [RFC PATCH 23/34] msm: Unify iomap for clock regions David Brown
2011-11-02 18:36 ` [RFC PATCH 24/34] msm: clock: Support dummy clocks David Brown
2011-11-02 18:36 ` [RFC PATCH 26/34] msm: 8660: Add FLUID support David Brown
2011-11-02 18:36 ` [RFC PATCH 27/34] msm-8x60: Add serial support David Brown
2011-11-02 18:36 ` [RFC PATCH 28/34] msm: clock: Invert CLKFLAG_AUTO_OFF David Brown
2011-11-02 18:36 ` [RFC PATCH 29/34] msm: clock: Expand CLK_MIN, CLK_MAX and CLK_MINMAX macros David Brown
2011-11-02 18:36 ` [RFC PATCH 30/34] msm: clock: Add EBI1 voter clocks for ADM on SoCs without them David Brown
2011-11-02 18:36 ` [RFC PATCH 31/34] msm: clock: Remove msm_clk_soc_init() David Brown
2011-11-02 18:36 ` [RFC PATCH 32/34] msm: clock-8x60: Add local control of vpe_axi_clk and vpe_axi_clk David Brown
2011-11-02 18:36 ` [RFC PATCH 33/34] ARM: msm: fix names of UART clocks David Brown
2011-11-02 18:36 ` [RFC PATCH 34/34] msm_serial: fix clock rate on DMA-based uarts David Brown

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=1320258991-22325-19-git-send-email-davidb@codeaurora.org \
    --to=davidb@codeaurora.org \
    --cc=linux-arm-kernel@lists.infradead.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).