123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- Content-Type: text/plain; charset="utf-8"
- MIME-Version: 1.0
- Content-Transfer-Encoding: 7bit
- Subject: [v3,09/13] clk: qcom: Add support for Krait clocks
- From: Stephen Boyd <sboyd@codeaurora.org>
- X-Patchwork-Id: 6063251
- Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org>
- To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
- Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
- linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
- Viresh Kumar <viresh.kumar@linaro.org>
- Date: Fri, 20 Mar 2015 23:45:28 -0700
- The Krait clocks are made up of a series of muxes and a divider
- that choose between a fixed rate clock and dedicated HFPLLs for
- each CPU. Instead of using mmio accesses to remux parents, the
- Krait implementation exposes the remux control via cp15
- registers. Support these clocks.
- Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
- ---
- drivers/clk/qcom/Kconfig | 4 ++
- drivers/clk/qcom/Makefile | 1 +
- drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++
- drivers/clk/qcom/clk-krait.h | 49 +++++++++++++
- 4 files changed, 220 insertions(+)
- create mode 100644 drivers/clk/qcom/clk-krait.c
- create mode 100644 drivers/clk/qcom/clk-krait.h
- --- a/drivers/clk/qcom/Kconfig
- +++ b/drivers/clk/qcom/Kconfig
- @@ -143,3 +143,7 @@ config QCOM_HFPLL
- Support for the high-frequency PLLs present on Qualcomm devices.
- Say Y if you want to support CPU frequency scaling on devices
- such as MSM8974, APQ8084, etc.
- +
- +config KRAIT_CLOCKS
- + bool
- + select KRAIT_L2_ACCESSORS
- --- a/drivers/clk/qcom/Makefile
- +++ b/drivers/clk/qcom/Makefile
- @@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
- clk-qcom-y += clk-branch.o
- clk-qcom-y += clk-regmap-divider.o
- clk-qcom-y += clk-regmap-mux.o
- +clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
- clk-qcom-y += clk-hfpll.o
- clk-qcom-y += reset.o
- clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
- --- /dev/null
- +++ b/drivers/clk/qcom/clk-krait.c
- @@ -0,0 +1,167 @@
- +/*
- + * Copyright (c) 2013-2014, The Linux Foundation. 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
- + * only version 2 as published by the Free Software Foundation.
- + *
- + * 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/module.h>
- +#include <linux/init.h>
- +#include <linux/io.h>
- +#include <linux/delay.h>
- +#include <linux/err.h>
- +#include <linux/clk-provider.h>
- +#include <linux/spinlock.h>
- +
- +#include <asm/krait-l2-accessors.h>
- +
- +#include "clk-krait.h"
- +
- +/* Secondary and primary muxes share the same cp15 register */
- +static DEFINE_SPINLOCK(krait_clock_reg_lock);
- +
- +#define LPL_SHIFT 8
- +static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
- +{
- + unsigned long flags;
- + u32 regval;
- +
- + spin_lock_irqsave(&krait_clock_reg_lock, flags);
- + regval = krait_get_l2_indirect_reg(mux->offset);
- + regval &= ~(mux->mask << mux->shift);
- + regval |= (sel & mux->mask) << mux->shift;
- + if (mux->lpl) {
- + regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
- + regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
- + }
- + krait_set_l2_indirect_reg(mux->offset, regval);
- + spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
- +
- + /* Wait for switch to complete. */
- + mb();
- + udelay(1);
- +}
- +
- +static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
- +{
- + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
- + u32 sel;
- +
- + sel = clk_mux_reindex(index, mux->parent_map, 0);
- + mux->en_mask = sel;
- + /* Don't touch mux if CPU is off as it won't work */
- + if (__clk_is_enabled(hw->clk))
- + __krait_mux_set_sel(mux, sel);
- + return 0;
- +}
- +
- +static u8 krait_mux_get_parent(struct clk_hw *hw)
- +{
- + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
- + u32 sel;
- +
- + sel = krait_get_l2_indirect_reg(mux->offset);
- + sel >>= mux->shift;
- + sel &= mux->mask;
- + mux->en_mask = sel;
- +
- + return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
- +}
- +
- +static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw,
- + unsigned long *safe_freq)
- +{
- + int i;
- + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
- + int num_parents = clk_hw_get_num_parents(hw);
- +
- + i = mux->safe_sel;
- + for (i = 0; i < num_parents; i++)
- + if (mux->safe_sel == mux->parent_map[i])
- + break;
- +
- + return clk_hw_get_parent_by_index(hw, i);
- +}
- +
- +static int krait_mux_enable(struct clk_hw *hw)
- +{
- + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
- +
- + __krait_mux_set_sel(mux, mux->en_mask);
- +
- + return 0;
- +}
- +
- +static void krait_mux_disable(struct clk_hw *hw)
- +{
- + struct krait_mux_clk *mux = to_krait_mux_clk(hw);
- +
- + __krait_mux_set_sel(mux, mux->safe_sel);
- +}
- +
- +const struct clk_ops krait_mux_clk_ops = {
- + .enable = krait_mux_enable,
- + .disable = krait_mux_disable,
- + .set_parent = krait_mux_set_parent,
- + .get_parent = krait_mux_get_parent,
- + .determine_rate = __clk_mux_determine_rate_closest,
- + .get_safe_parent = krait_mux_get_safe_parent,
- +};
- +EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
- +
- +/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
- +static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
- + unsigned long *parent_rate)
- +{
- + *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
- + return DIV_ROUND_UP(*parent_rate, 2);
- +}
- +
- +static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
- + unsigned long parent_rate)
- +{
- + struct krait_div2_clk *d = to_krait_div2_clk(hw);
- + unsigned long flags;
- + u32 val;
- + u32 mask = BIT(d->width) - 1;
- +
- + if (d->lpl)
- + mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
- +
- + spin_lock_irqsave(&krait_clock_reg_lock, flags);
- + val = krait_get_l2_indirect_reg(d->offset);
- + val &= ~mask;
- + krait_set_l2_indirect_reg(d->offset, val);
- + spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
- +
- + return 0;
- +}
- +
- +static unsigned long
- +krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
- +{
- + struct krait_div2_clk *d = to_krait_div2_clk(hw);
- + u32 mask = BIT(d->width) - 1;
- + u32 div;
- +
- + div = krait_get_l2_indirect_reg(d->offset);
- + div >>= d->shift;
- + div &= mask;
- + div = (div + 1) * 2;
- +
- + return DIV_ROUND_UP(parent_rate, div);
- +}
- +
- +const struct clk_ops krait_div2_clk_ops = {
- + .round_rate = krait_div2_round_rate,
- + .set_rate = krait_div2_set_rate,
- + .recalc_rate = krait_div2_recalc_rate,
- +};
- +EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
- --- /dev/null
- +++ b/drivers/clk/qcom/clk-krait.h
- @@ -0,0 +1,49 @@
- +/*
- + * Copyright (c) 2013, The Linux Foundation. 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
- + * only version 2 as published by the Free Software Foundation.
- + *
- + * 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 __QCOM_CLK_KRAIT_H
- +#define __QCOM_CLK_KRAIT_H
- +
- +#include <linux/clk-provider.h>
- +
- +struct krait_mux_clk {
- + unsigned int *parent_map;
- + bool has_safe_parent;
- + u8 safe_sel;
- + u32 offset;
- + u32 mask;
- + u32 shift;
- + u32 en_mask;
- + bool lpl;
- +
- + struct clk_hw hw;
- +};
- +
- +#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
- +
- +extern const struct clk_ops krait_mux_clk_ops;
- +
- +struct krait_div2_clk {
- + u32 offset;
- + u8 width;
- + u32 shift;
- + bool lpl;
- +
- + struct clk_hw hw;
- +};
- +
- +#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
- +
- +extern const struct clk_ops krait_div2_clk_ops;
- +
- +#endif
|