summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-gpio-gate.c204
-rw-r--r--drivers/clk/mvebu/armada-370.c8
-rw-r--r--drivers/clk/mvebu/armada-375.c4
-rw-r--r--drivers/clk/mvebu/common.c82
-rw-r--r--drivers/clk/mvebu/common.h7
-rw-r--r--drivers/clk/qcom/clk-pll.c68
-rw-r--r--drivers/clk/qcom/clk-pll.h20
-rw-r--r--drivers/clk/qcom/clk-rcg.c115
-rw-r--r--drivers/clk/qcom/clk-rcg.h6
-rw-r--r--drivers/clk/qcom/clk-rcg2.c19
-rw-r--r--drivers/clk/qcom/common.c16
-rw-r--r--drivers/clk/qcom/common.h4
-rw-r--r--drivers/clk/qcom/gcc-ipq806x.c31
-rw-r--r--drivers/clk/qcom/mmcc-msm8960.c28
-rw-r--r--drivers/clk/tegra/clk-tegra124.c8
-rw-r--r--drivers/clk/tegra/clk.c9
17 files changed, 538 insertions, 92 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 27c542ba9e73..92a7f6c02394 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
+obj-$(CONFIG_COMMON_CLK) += clk-gpio-gate.o
ifeq ($(CONFIG_OF), y)
obj-$(CONFIG_COMMON_CLK) += clk-conf.o
endif
diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c
new file mode 100644
index 000000000000..9dde88533684
--- /dev/null
+++ b/drivers/clk/clk-gpio-gate.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Jyri Sarha <jsarha@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gpio gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+/**
+ * DOC: basic gpio gated clock which can be enabled and disabled
+ * with gpio output
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gpio
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw)
+
+static int clk_gpio_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 1);
+
+ return 0;
+}
+
+static void clk_gpio_gate_disable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 0);
+}
+
+static int clk_gpio_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ return gpiod_get_value(clk->gpiod);
+}
+
+const struct clk_ops clk_gpio_gate_ops = {
+ .enable = clk_gpio_gate_enable,
+ .disable = clk_gpio_gate_disable,
+ .is_enabled = clk_gpio_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gpio_gate_ops);
+
+/**
+ * clk_register_gpio - register a gpip clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of this clock's parent
+ * @gpiod: gpio descriptor to gate this clock
+ */
+struct clk *clk_register_gpio_gate(struct device *dev, const char *name,
+ const char *parent_name, struct gpio_desc *gpiod,
+ unsigned long flags)
+{
+ struct clk_gpio *clk_gpio = NULL;
+ struct clk *clk = ERR_PTR(-EINVAL);
+ struct clk_init_data init = { NULL };
+ unsigned long gpio_flags;
+ int err;
+
+ if (gpiod_is_active_low(gpiod))
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ if (dev)
+ err = devm_gpio_request_one(dev, desc_to_gpio(gpiod),
+ gpio_flags, name);
+ else
+ err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name);
+
+ if (err) {
+ pr_err("%s: %s: Error requesting clock control gpio %u\n",
+ __func__, name, desc_to_gpio(gpiod));
+ return ERR_PTR(err);
+ }
+
+ if (dev)
+ clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio),
+ GFP_KERNEL);
+ else
+ clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL);
+
+ if (!clk_gpio) {
+ clk = ERR_PTR(-ENOMEM);
+ goto clk_register_gpio_gate_err;
+ }
+
+ init.name = name;
+ init.ops = &clk_gpio_gate_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ clk_gpio->gpiod = gpiod;
+ clk_gpio->hw.init = &init;
+
+ clk = clk_register(dev, &clk_gpio->hw);
+
+ if (!IS_ERR(clk))
+ return clk;
+
+ if (!dev)
+ kfree(clk_gpio);
+
+clk_register_gpio_gate_err:
+ gpiod_put(gpiod);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_gpio_gate);
+
+#ifdef CONFIG_OF
+/**
+ * The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER
+ * can not be handled properly at of_clk_init() call time.
+ */
+
+struct clk_gpio_gate_delayed_register_data {
+ struct device_node *node;
+ struct mutex lock;
+ struct clk *clk;
+};
+
+static struct clk *of_clk_gpio_gate_delayed_register_get(
+ struct of_phandle_args *clkspec,
+ void *_data)
+{
+ struct clk_gpio_gate_delayed_register_data *data = _data;
+ struct clk *clk;
+ const char *clk_name = data->node->name;
+ const char *parent_name;
+ struct gpio_desc *gpiod;
+ int gpio;
+
+ mutex_lock(&data->lock);
+
+ if (data->clk) {
+ mutex_unlock(&data->lock);
+ return data->clk;
+ }
+
+ gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL);
+ if (gpio < 0) {
+ mutex_unlock(&data->lock);
+ if (gpio != -EPROBE_DEFER)
+ pr_err("%s: %s: Can't get 'enable-gpios' DT property\n",
+ __func__, clk_name);
+ return ERR_PTR(gpio);
+ }
+ gpiod = gpio_to_desc(gpio);
+
+ parent_name = of_clk_get_parent_name(data->node, 0);
+
+ clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0);
+ if (IS_ERR(clk)) {
+ mutex_unlock(&data->lock);
+ return clk;
+ }
+
+ data->clk = clk;
+ mutex_unlock(&data->lock);
+
+ return clk;
+}
+
+/**
+ * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock
+ */
+void __init of_gpio_gate_clk_setup(struct device_node *node)
+{
+ struct clk_gpio_gate_delayed_register_data *data;
+
+ data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data),
+ GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->node = node;
+ mutex_init(&data->lock);
+
+ of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data);
+}
+EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup);
+CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup);
+#endif
diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c
index bef198a83863..756f0f39d6a3 100644
--- a/drivers/clk/mvebu/armada-370.c
+++ b/drivers/clk/mvebu/armada-370.c
@@ -23,6 +23,7 @@
*/
#define SARL 0 /* Low part [0:31] */
+#define SARL_A370_SSCG_ENABLE BIT(10)
#define SARL_A370_PCLK_FREQ_OPT 11
#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
#define SARL_A370_FAB_FREQ_OPT 15
@@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio(
}
}
+static bool a370_is_sscg_enabled(void __iomem *sar)
+{
+ return !(readl(sar) & SARL_A370_SSCG_ENABLE);
+}
+
static const struct coreclk_soc_desc a370_coreclks = {
.get_tclk_freq = a370_get_tclk_freq,
.get_cpu_freq = a370_get_cpu_freq,
.get_clk_ratio = a370_get_clk_ratio,
+ .is_sscg_enabled = a370_is_sscg_enabled,
+ .fix_sscg_deviation = kirkwood_fix_sscg_deviation,
.ratios = a370_coreclk_ratios,
.num_ratios = ARRAY_SIZE(a370_coreclk_ratios),
};
diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c
index c991a4d95e10..c7af2242b796 100644
--- a/drivers/clk/mvebu/armada-375.c
+++ b/drivers/clk/mvebu/armada-375.c
@@ -27,14 +27,14 @@
* all modified at the same time, and not separately as for the Armada
* 370 or the Armada XP SoCs.
*
- * SAR0[21:17] : CPU frequency DDR frequency L2 frequency
+ * SAR1[21:17] : CPU frequency DDR frequency L2 frequency
* 6 = 400 MHz 400 MHz 200 MHz
* 15 = 600 MHz 600 MHz 300 MHz
* 21 = 800 MHz 534 MHz 400 MHz
* 25 = 1000 MHz 500 MHz 500 MHz
* others reserved.
*
- * SAR0[22] : TCLK frequency
+ * SAR1[22] : TCLK frequency
* 0 = 166 MHz
* 1 = 200 MHz
*/
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 8145c4efc381..7f8a33ab265b 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -26,8 +26,85 @@
* Core Clocks
*/
+#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3)
+#define SSCG_SPREAD_DOWN 0x0
+#define SSCG_SPREAD_UP 0x1
+#define SSCG_SPREAD_CENTRAL 0x2
+#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF)
+#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF)
+
static struct clk_onecell_data clk_data;
+/*
+ * This function can be used by the Kirkwood, the Armada 370, the
+ * Armada XP and the Armada 375 SoC. The name of the function was
+ * chosen following the dt convention: using the first known SoC
+ * compatible with it.
+ */
+u32 kirkwood_fix_sscg_deviation(struct device_node *np, u32 system_clk)
+{
+ struct device_node *sscg_np = NULL;
+ void __iomem *sscg_map;
+ u32 sscg_reg;
+ s32 low_bound, high_bound;
+ u64 freq_swing_half;
+
+ sscg_np = of_find_node_by_name(np, "sscg");
+ if (sscg_np == NULL) {
+ pr_err("cannot get SSCG register node\n");
+ return system_clk;
+ }
+
+ sscg_map = of_iomap(sscg_np, 0);
+ if (sscg_map == NULL) {
+ pr_err("cannot map SSCG register\n");
+ goto out;
+ }
+
+ sscg_reg = readl(sscg_map);
+ high_bound = SSCG_CONF_HIGH(sscg_reg);
+ low_bound = SSCG_CONF_LOW(sscg_reg);
+
+ if ((high_bound - low_bound) <= 0)
+ goto out;
+ /*
+ * From Marvell engineer we got the following formula (when
+ * this code was written, the datasheet was erroneous)
+ * Spread percentage = 1/96 * (H - L) / H
+ * H = SSCG_High_Boundary
+ * L = SSCG_Low_Boundary
+ *
+ * As the deviation is half of spread then it lead to the
+ * following formula in the code.
+ *
+ * To avoid an overflow and not lose any significant digit in
+ * the same time we have to use a 64 bit integer.
+ */
+
+ freq_swing_half = (((u64)high_bound - (u64)low_bound)
+ * (u64)system_clk);
+ do_div(freq_swing_half, (2 * 96 * high_bound));
+
+ switch (SSCG_CONF_MODE(sscg_reg)) {
+ case SSCG_SPREAD_DOWN:
+ system_clk -= freq_swing_half;
+ break;
+ case SSCG_SPREAD_UP:
+ system_clk += freq_swing_half;
+ break;
+ case SSCG_SPREAD_CENTRAL:
+ default:
+ break;
+ }
+
+ iounmap(sscg_map);
+
+out:
+ of_node_put(sscg_np);
+
+ return system_clk;
+}
+
void __init mvebu_coreclk_setup(struct device_node *np,
const struct coreclk_soc_desc *desc)
{
@@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np,
of_property_read_string_index(np, "clock-output-names", 1,
&cpuclk_name);
rate = desc->get_cpu_freq(base);
+
+ if (desc->is_sscg_enabled && desc->fix_sscg_deviation
+ && desc->is_sscg_enabled(base))
+ rate = desc->fix_sscg_deviation(np, rate);
+
clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
CLK_IS_ROOT, rate);
WARN_ON(IS_ERR(clk_data.clks[1]));
diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h
index 8cd28e47471c..8f8db7eac3f6 100644
--- a/drivers/clk/mvebu/common.h
+++ b/drivers/clk/mvebu/common.h
@@ -30,6 +30,8 @@ struct coreclk_soc_desc {
u32 (*get_tclk_freq)(void __iomem *sar);
u32 (*get_cpu_freq)(void __iomem *sar);
void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
+ bool (*is_sscg_enabled)(void __iomem *sar);
+ u32 (*fix_sscg_deviation)(struct device_node *np, u32 system_clk);
const struct coreclk_ratio *ratios;
int num_ratios;
};
@@ -47,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np,
void __init mvebu_clk_gating_setup(struct device_node *np,
const struct clk_gating_soc_desc *desc);
+/*
+ * This function is shared among the Kirkwood, Armada 370, Armada XP
+ * and Armada 375 SoC
+ */
+u32 kirkwood_fix_sscg_deviation(struct device_node *np, u32 system_clk);
#endif
diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c
index 9db03d3b1657..b823bc3b6250 100644
--- a/drivers/clk/qcom/clk-pll.c
+++ b/drivers/clk/qcom/clk-pll.c
@@ -97,7 +97,7 @@ 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;
+ u32 l, m, n, config;
unsigned long rate;
u64 tmp;
@@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
do_div(tmp, n);
rate += tmp;
}
+ if (pll->post_div_width) {
+ regmap_read(pll->clkr.regmap, pll->config_reg, &config);
+ config >>= pll->post_div_shift;
+ config &= BIT(pll->post_div_width) - 1;
+ rate /= config + 1;
+ }
+
return rate;
}
+static const
+struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
+{
+ if (!f)
+ return NULL;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ return NULL;
+}
+
+static long
+clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ const struct pll_freq_tbl *f;
+
+ f = find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return clk_pll_recalc_rate(hw, *p_rate);
+
+ return f->freq;
+}
+
+static int
+clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ const struct pll_freq_tbl *f;
+ bool enabled;
+ u32 mode;
+ u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
+
+ f = find_freq(pll->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
+ enabled = (mode & enable_mask) == enable_mask;
+
+ if (enabled)
+ clk_pll_disable(hw);
+
+ regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
+ regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
+ regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
+ regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
+
+ if (enabled)
+ clk_pll_enable(hw);
+
+ return 0;
+}
+
const struct clk_ops clk_pll_ops = {
.enable = clk_pll_enable,
.disable = clk_pll_disable,
.recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
};
EXPORT_SYMBOL_GPL(clk_pll_ops);
diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h
index 3003e9962472..c9c0cda306d0 100644
--- a/drivers/clk/qcom/clk-pll.h
+++ b/drivers/clk/qcom/clk-pll.h
@@ -18,6 +18,21 @@
#include "clk-regmap.h"
/**
+ * struct pll_freq_tbl - PLL frequency table
+ * @l: L value
+ * @m: M value
+ * @n: N value
+ * @ibits: internal values
+ */
+struct pll_freq_tbl {
+ unsigned long freq;
+ u16 l;
+ u16 m;
+ u16 n;
+ u32 ibits;
+};
+
+/**
* struct clk_pll - phase locked loop (PLL)
* @l_reg: L register
* @m_reg: M register
@@ -26,6 +41,7 @@
* @mode_reg: mode register
* @status_reg: status register
* @status_bit: ANDed with @status_reg to determine if PLL is enabled
+ * @freq_tbl: PLL frequency table
* @hw: handle between common and hardware-specific interfaces
*/
struct clk_pll {
@@ -36,6 +52,10 @@ struct clk_pll {
u32 mode_reg;
u32 status_reg;
u8 status_bit;
+ u8 post_div_width;
+ u8 post_div_shift;
+
+ const struct pll_freq_tbl *freq_tbl;
struct clk_regmap clkr;
};
diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c
index b638c5846dbf..b6e6959e89aa 100644
--- a/drivers/clk/qcom/clk-rcg.c
+++ b/drivers/clk/qcom/clk-rcg.c
@@ -21,6 +21,7 @@
#include <asm/div64.h>
#include "clk-rcg.h"
+#include "common.h"
static u32 ns_to_src(struct src_sel *s, u32 ns)
{
@@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
int num_parents = __clk_get_num_parents(hw->clk);
- u32 ns, ctl;
+ u32 ns, reg;
int bank;
int i;
struct src_sel *s;
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
- bank = reg_to_bank(rcg, ctl);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
s = &rcg->s[bank];
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
ns = ns_to_src(s, ns);
for (i = 0; i < num_parents; i++)
@@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
{
- u32 ns, md, ctl, *regp;
+ u32 ns, md, reg;
int bank, new_bank;
struct mn *mn;
struct pre_div *p;
struct src_sel *s;
bool enabled;
- u32 md_reg;
- u32 bank_reg;
+ u32 md_reg, ns_reg;
bool banked_mn = !!rcg->mn[1].width;
+ bool banked_p = !!rcg->p[1].pre_div_width;
struct clk_hw *hw = &rcg->clkr.hw;
enabled = __clk_is_enabled(hw->clk);
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
-
- if (banked_mn) {
- regp = &ctl;
- bank_reg = rcg->clkr.enable_reg;
- } else {
- regp = &ns;
- bank_reg = rcg->ns_reg;
- }
-
- bank = reg_to_bank(rcg, *regp);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ bank = reg_to_bank(rcg, reg);
new_bank = enabled ? !bank : bank;
+ ns_reg = rcg->ns_reg[new_bank];
+ regmap_read(rcg->clkr.regmap, ns_reg, &ns);
+
if (banked_mn) {
mn = &rcg->mn[new_bank];
md_reg = rcg->md_reg[new_bank];
ns |= BIT(mn->mnctr_reset_bit);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
regmap_read(rcg->clkr.regmap, md_reg, &md);
md = mn_to_md(mn, f->m, f->n, md);
regmap_write(rcg->clkr.regmap, md_reg, md);
ns = mn_to_ns(mn, f->m, f->n, ns);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
- ctl = mn_to_reg(mn, f->m, f->n, ctl);
- regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl);
+ /* Two NS registers means mode control is in NS register */
+ if (rcg->ns_reg[0] != rcg->ns_reg[1]) {
+ ns = mn_to_reg(mn, f->m, f->n, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+ } else {
+ reg = mn_to_reg(mn, f->m, f->n, reg);
+ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
+ }
ns &= ~BIT(mn->mnctr_reset_bit);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
- } else {
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
+ }
+
+ if (banked_p) {
p = &rcg->p[new_bank];
ns = pre_div_to_ns(p, f->pre_div - 1, ns);
}
s = &rcg->s[new_bank];
ns = src_to_ns(s, s->parent_map[f->src], ns);
- regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
+ regmap_write(rcg->clkr.regmap, ns_reg, ns);
if (enabled) {
- *regp ^= BIT(rcg->mux_sel_bit);
- regmap_write(rcg->clkr.regmap, bank_reg, *regp);
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
+ reg ^= BIT(rcg->mux_sel_bit);
+ regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg);
}
}
static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
- u32 ns, ctl, md, reg;
+ u32 ns, md, reg;
int bank;
struct freq_tbl f = { 0 };
bool banked_mn = !!rcg->mn[1].width;
+ bool banked_p = !!rcg->p[1].pre_div_width;
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl);
- reg = banked_mn ? ctl : ns;
-
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+
if (banked_mn) {
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
f.m = md_to_m(&rcg->mn[bank], md);
f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
- } else {
- f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
}
- f.src = index;
+ if (banked_p)
+ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
+
+ f.src = index;
configure_bank(rcg, &f);
return 0;
@@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
u32 m, n, pre_div, ns, md, mode, reg;
int bank;
struct mn *mn;
+ bool banked_p = !!rcg->p[1].pre_div_width;
bool banked_mn = !!rcg->mn[1].width;
- regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
-
- if (banked_mn)
- regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &reg);
- else
- reg = ns;
-
+ regmap_read(rcg->clkr.regmap, rcg->bank_reg, &reg);
bank = reg_to_bank(rcg, reg);
+ regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
+ m = n = pre_div = mode = 0;
+
if (banked_mn) {
mn = &rcg->mn[bank];
regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md);
m = md_to_m(mn, md);
n = ns_m_to_n(mn, ns, m);
+ /* Two NS registers means mode control is in NS register */
+ if (rcg->ns_reg[0] != rcg->ns_reg[1])
+ reg = ns;
mode = reg_to_mnctr_mode(mn, reg);
- return calc_rate(parent_rate, m, n, mode, 0);
- } else {
- pre_div = ns_to_pre_div(&rcg->p[bank], ns);
- return calc_rate(parent_rate, 0, 0, 0, pre_div);
}
-}
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
- if (!f)
- return NULL;
-
- for (; f->freq; f++)
- if (rate <= f->freq)
- return f;
+ if (banked_p)
+ pre_div = ns_to_pre_div(&rcg->p[bank], ns);
- return NULL;
+ return calc_rate(parent_rate, m, n, mode, pre_div);
}
static long _freq_tbl_determine_rate(struct clk_hw *hw,
@@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw,
{
unsigned long clk_flags;
- f = find_freq(f, rate);
+ f = qcom_find_freq(f, rate);
if (!f)
return -EINVAL;
@@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_rcg *rcg = to_clk_rcg(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
@@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index ba0523cefd2e..687e41f91d7c 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
* struct clk_dyn_rcg - root clock generator with glitch free mux
*
* @mux_sel_bit: bit to switch glitch free mux
- * @ns_reg: NS register
+ * @ns_reg: NS0 and NS1 register
* @md_reg: MD0 and MD1 register
+ * @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux
* @mn: mn counter (banked)
* @s: source selector (banked)
* @freq_tbl: frequency table
@@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops;
*
*/
struct clk_dyn_rcg {
- u32 ns_reg;
+ u32 ns_reg[2];
u32 md_reg[2];
+ u32 bank_reg;
u8 mux_sel_bit;
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index cd185d5cc67a..cfa9eb4fe9ca 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -24,6 +24,7 @@
#include <asm/div64.h>
#include "clk-rcg.h"
+#include "common.h"
#define CMD_REG 0x0
#define CMD_UPDATE BIT(0)
@@ -172,27 +173,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return calc_rate(parent_rate, m, n, mode, hid_div);
}
-static const
-struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
-{
- if (!f)
- return NULL;
-
- for (; f->freq; f++)
- if (rate <= f->freq)
- return f;
-
- /* Default to our fastest rate */
- return f - 1;
-}
-
static long _freq_tbl_determine_rate(struct clk_hw *hw,
const struct freq_tbl *f, unsigned long rate,
unsigned long *p_rate, struct clk **p)
{
unsigned long clk_flags;
- f = find_freq(f, rate);
+ f = qcom_find_freq(f, rate);
if (!f)
return -EINVAL;
@@ -268,7 +255,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f;
- f = find_freq(rcg->freq_tbl, rate);
+ f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index eeb3eea01f4c..e20d947db3e5 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -18,6 +18,7 @@
#include <linux/reset-controller.h>
#include "common.h"
+#include "clk-rcg.h"
#include "clk-regmap.h"
#include "reset.h"
@@ -27,6 +28,21 @@ struct qcom_cc {
struct clk *clks[];
};
+const
+struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
+{
+ if (!f)
+ return NULL;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ /* Default to our fastest rate */
+ return f - 1;
+}
+EXPORT_SYMBOL_GPL(qcom_find_freq);
+
struct regmap *
qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
{
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 2765e9d3da97..f519322acdf3 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -18,6 +18,7 @@ struct regmap_config;
struct clk_regmap;
struct qcom_reset_map;
struct regmap;
+struct freq_tbl;
struct qcom_cc_desc {
const struct regmap_config *config;
@@ -27,6 +28,9 @@ struct qcom_cc_desc {
size_t num_resets;
};
+extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
+ unsigned long rate);
+
extern struct regmap *qcom_cc_map(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
extern int qcom_cc_really_probe(struct platform_device *pdev,
diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
index 3b83b7dd78c7..5cd62a709ac7 100644
--- a/drivers/clk/qcom/gcc-ipq806x.c
+++ b/drivers/clk/qcom/gcc-ipq806x.c
@@ -32,6 +32,33 @@
#include "clk-branch.h"
#include "reset.h"
+static struct clk_pll pll0 = {
+ .l_reg = 0x30c4,
+ .m_reg = 0x30c8,
+ .n_reg = 0x30cc,
+ .config_reg = 0x30d4,
+ .mode_reg = 0x30c0,
+ .status_reg = 0x30d8,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll0",
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap pll0_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll0_vote",
+ .parent_names = (const char *[]){ "pll0" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
static struct clk_pll pll3 = {
.l_reg = 0x3164,
.m_reg = 0x3168,
@@ -154,7 +181,7 @@ static const u8 gcc_pxo_pll8_pll0[] = {
static const char *gcc_pxo_pll8_pll0_map[] = {
"pxo",
"pll8_vote",
- "pll0",
+ "pll0_vote",
};
static struct freq_tbl clk_tbl_gsbi_uart[] = {
@@ -2133,6 +2160,8 @@ static struct clk_branch usb_fs1_h_clk = {
};
static struct clk_regmap *gcc_ipq806x_clks[] = {
+ [PLL0] = &pll0.clkr,
+ [PLL0_VOTE] = &pll0_vote,
[PLL3] = &pll3.clkr,
[PLL8] = &pll8.clkr,
[PLL8_VOTE] = &pll8_vote,
diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c
index 68da36cd7ed0..e8b33bbc362f 100644
--- a/drivers/clk/qcom/mmcc-msm8960.c
+++ b/drivers/clk/qcom/mmcc-msm8960.c
@@ -773,9 +773,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = {
};
static struct clk_dyn_rcg gfx2d0_src = {
- .ns_reg = 0x0070,
+ .ns_reg[0] = 0x0070,
+ .ns_reg[1] = 0x0070,
.md_reg[0] = 0x0064,
.md_reg[1] = 0x0068,
+ .bank_reg = 0x0060,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -831,9 +833,11 @@ static struct clk_branch gfx2d0_clk = {
};
static struct clk_dyn_rcg gfx2d1_src = {
- .ns_reg = 0x007c,
+ .ns_reg[0] = 0x007c,
+ .ns_reg[1] = 0x007c,
.md_reg[0] = 0x0078,
.md_reg[1] = 0x006c,
+ .bank_reg = 0x0074,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -930,9 +934,11 @@ static struct freq_tbl clk_tbl_gfx3d_8064[] = {
};
static struct clk_dyn_rcg gfx3d_src = {
- .ns_reg = 0x008c,
+ .ns_reg[0] = 0x008c,
+ .ns_reg[1] = 0x008c,
.md_reg[0] = 0x0084,
.md_reg[1] = 0x0088,
+ .bank_reg = 0x0080,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 25,
@@ -1006,9 +1012,11 @@ static struct freq_tbl clk_tbl_vcap[] = {
};
static struct clk_dyn_rcg vcap_src = {
- .ns_reg = 0x021c,
+ .ns_reg[0] = 0x021c,
+ .ns_reg[1] = 0x021c,
.md_reg[0] = 0x01ec,
.md_reg[1] = 0x0218,
+ .bank_reg = 0x0178,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 23,
@@ -1211,9 +1219,11 @@ static struct freq_tbl clk_tbl_mdp[] = {
};
static struct clk_dyn_rcg mdp_src = {
- .ns_reg = 0x00d0,
+ .ns_reg[0] = 0x00d0,
+ .ns_reg[1] = 0x00d0,
.md_reg[0] = 0x00c4,
.md_reg[1] = 0x00c8,
+ .bank_reg = 0x00c0,
.mn[0] = {
.mnctr_en_bit = 8,
.mnctr_reset_bit = 31,
@@ -1318,7 +1328,9 @@ static struct freq_tbl clk_tbl_rot[] = {
};
static struct clk_dyn_rcg rot_src = {
- .ns_reg = 0x00e8,
+ .ns_reg[0] = 0x00e8,
+ .ns_reg[1] = 0x00e8,
+ .bank_reg = 0x00e8,
.p[0] = {
.pre_div_shift = 22,
.pre_div_width = 4,
@@ -1542,9 +1554,11 @@ static struct freq_tbl clk_tbl_vcodec[] = {
};
static struct clk_dyn_rcg vcodec_src = {
- .ns_reg = 0x0100,
+ .ns_reg[0] = 0x0100,
+ .ns_reg[1] = 0x0100,
.md_reg[0] = 0x00fc,
.md_reg[1] = 0x0128,
+ .bank_reg = 0x00f8,
.mn[0] = {
.mnctr_en_bit = 5,
.mnctr_reset_bit = 31,
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 9525c684d149..e3a85842ce0c 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -1166,6 +1166,12 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
clk_register_clkdev(clk, "pll_c_out1", NULL);
clks[TEGRA124_CLK_PLL_C_OUT1] = clk;
+ /* PLLC_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_c_ud", NULL);
+ clks[TEGRA124_CLK_PLL_C_UD] = clk;
+
/* PLLC2 */
clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0,
&pll_c2_params, NULL);
@@ -1198,6 +1204,8 @@ static void __init tegra124_pll_init(void __iomem *clk_base,
/* PLLM_UD */
clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_m_ud", NULL);
+ clks[TEGRA124_CLK_PLL_M_UD] = clk;
/* PLLU */
val = readl(clk_base + pll_u_params.base_reg);
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index f87c609e8f72..97dc8595c3cd 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -207,8 +207,13 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
for (; tbl->clk_id < clk_max; tbl++) {
clk = clks[tbl->clk_id];
- if (IS_ERR_OR_NULL(clk))
- return;
+ if (IS_ERR_OR_NULL(clk)) {
+ pr_err("%s: invalid entry %ld in clks array for id %d\n",
+ __func__, PTR_ERR(clk), tbl->clk_id);
+ WARN_ON(1);
+
+ continue;
+ }
if (tbl->parent_id < clk_max) {
struct clk *parent = clks[tbl->parent_id];