summaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-composite.c53
-rw-r--r--drivers/clk/clk-divider.c5
-rw-r--r--drivers/clk/clk-fixed-factor.c36
-rw-r--r--drivers/clk/clk-si5351.c1510
-rw-r--r--drivers/clk/clk-si5351.h155
-rw-r--r--drivers/clk/clk-vt8500.c2
-rw-r--r--drivers/clk/clk.c217
-rw-r--r--drivers/clk/mvebu/clk-core.c8
-rw-r--r--drivers/clk/mvebu/clk-cpu.c17
-rw-r--r--drivers/clk/mvebu/clk-cpu.h22
-rw-r--r--drivers/clk/mvebu/clk.c6
-rw-r--r--drivers/clk/mxs/clk-imx23.c42
-rw-r--r--drivers/clk/mxs/clk-imx28.c42
-rw-r--r--drivers/clk/socfpga/clk.c163
-rw-r--r--drivers/clk/spear/spear1310_clock.c64
-rw-r--r--drivers/clk/spear/spear1340_clock.c63
-rw-r--r--drivers/clk/spear/spear3xx_clock.c60
-rw-r--r--drivers/clk/spear/spear6xx_clock.c31
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c127
-rw-r--r--drivers/clk/tegra/clk-tegra20.c2
-rw-r--r--drivers/clk/tegra/clk-tegra30.c3
-rw-r--r--drivers/clk/ux500/Makefile1
-rw-r--r--drivers/clk/ux500/abx500-clk.c71
-rw-r--r--drivers/clk/ux500/clk-prcc.c1
-rw-r--r--drivers/clk/ux500/clk-sysctrl.c221
-rw-r--r--drivers/clk/ux500/clk.h34
-rw-r--r--drivers/clk/ux500/u8500_clk.c145
-rw-r--r--drivers/clk/versatile/Makefile2
-rw-r--r--drivers/clk/versatile/clk-sp810.c188
-rw-r--r--drivers/clk/versatile/clk-vexpress.c49
-rw-r--r--drivers/clk/x86/Makefile2
-rw-r--r--drivers/clk/x86/clk-lpss.c99
-rw-r--r--drivers/clk/x86/clk-lpss.h36
-rw-r--r--drivers/clk/x86/clk-lpt.c40
36 files changed, 2909 insertions, 619 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a64caefdba12..0357ac44638b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -55,6 +55,16 @@ config COMMON_CLK_MAX77686
---help---
This driver supports Maxim 77686 crystal oscillator clock.
+config COMMON_CLK_SI5351
+ tristate "Clock driver for SiLabs 5351A/B/C"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ select RATIONAL
+ ---help---
+ This driver supports Silicon Labs 5351A/B/C programmable clock
+ generators.
+
config CLK_TWL6040
tristate "External McPDM functional clock from twl6040"
depends on TWL6040_CORE
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 17e8dc4e417c..137d3e730f86 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 097dee4fd209..a33f46f20a41 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -47,36 +47,36 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
- const struct clk_ops *div_ops = composite->div_ops;
- struct clk_hw *div_hw = composite->div_hw;
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
- div_hw->clk = hw->clk;
+ rate_hw->clk = hw->clk;
- return div_ops->recalc_rate(div_hw, parent_rate);
+ return rate_ops->recalc_rate(rate_hw, parent_rate);
}
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_composite *composite = to_clk_composite(hw);
- const struct clk_ops *div_ops = composite->div_ops;
- struct clk_hw *div_hw = composite->div_hw;
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
- div_hw->clk = hw->clk;
+ rate_hw->clk = hw->clk;
- return div_ops->round_rate(div_hw, rate, prate);
+ return rate_ops->round_rate(rate_hw, rate, prate);
}
static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
- const struct clk_ops *div_ops = composite->div_ops;
- struct clk_hw *div_hw = composite->div_hw;
+ const struct clk_ops *rate_ops = composite->rate_ops;
+ struct clk_hw *rate_hw = composite->rate_hw;
- div_hw->clk = hw->clk;
+ rate_hw->clk = hw->clk;
- return div_ops->set_rate(div_hw, rate, parent_rate);
+ return rate_ops->set_rate(rate_hw, rate, parent_rate);
}
static int clk_composite_is_enabled(struct clk_hw *hw)
@@ -115,7 +115,7 @@ static void clk_composite_disable(struct clk_hw *hw)
struct clk *clk_register_composite(struct device *dev, const char *name,
const char **parent_names, int num_parents,
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
- struct clk_hw *div_hw, const struct clk_ops *div_ops,
+ struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags)
{
@@ -149,18 +149,27 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
clk_composite_ops->set_parent = clk_composite_set_parent;
}
- if (div_hw && div_ops) {
- if (!div_ops->recalc_rate || !div_ops->round_rate ||
- !div_ops->set_rate) {
+ if (rate_hw && rate_ops) {
+ if (!rate_ops->recalc_rate) {
clk = ERR_PTR(-EINVAL);
goto err;
}
- composite->div_hw = div_hw;
- composite->div_ops = div_ops;
+ /* .round_rate is a prerequisite for .set_rate */
+ if (rate_ops->round_rate) {
+ clk_composite_ops->round_rate = clk_composite_round_rate;
+ if (rate_ops->set_rate) {
+ clk_composite_ops->set_rate = clk_composite_set_rate;
+ }
+ } else {
+ WARN(rate_ops->set_rate,
+ "%s: missing round_rate op is required\n",
+ __func__);
+ }
+
+ composite->rate_hw = rate_hw;
+ composite->rate_ops = rate_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
- clk_composite_ops->round_rate = clk_composite_round_rate;
- clk_composite_ops->set_rate = clk_composite_set_rate;
}
if (gate_hw && gate_ops) {
@@ -187,8 +196,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
if (composite->mux_hw)
composite->mux_hw->clk = clk;
- if (composite->div_hw)
- composite->div_hw->clk = clk;
+ if (composite->rate_hw)
+ composite->rate_hw->clk = clk;
if (composite->gate_hw)
composite->gate_hw->clk = clk;
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 68b402101170..6d9674160430 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
div = _get_div(divider, val);
if (!div) {
- WARN(1, "%s: Invalid divisor for clock %s\n", __func__,
- __clk_get_name(hw->clk));
+ WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ __clk_get_name(hw->clk));
return parent_rate;
}
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 1ef271e47594..9ff7d510faa3 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -11,6 +11,7 @@
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/of.h>
/*
* DOC: basic fixed multiplier and divider clock that cannot gate
@@ -96,3 +97,38 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
return clk;
}
+#ifdef CONFIG_OF
+/**
+ * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
+ */
+void __init of_fixed_factor_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ u32 div, mult;
+
+ if (of_property_read_u32(node, "clock-div", &div)) {
+ pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
+ __func__, node->name);
+ return;
+ }
+
+ if (of_property_read_u32(node, "clock-mult", &mult)) {
+ pr_err("%s Fixed factor clock <%s> must have a clokc-mult property\n",
+ __func__, node->name);
+ return;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
+ mult, div);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
+CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
+ of_fixed_factor_clk_setup);
+#endif
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
new file mode 100644
index 000000000000..892728412e9d
--- /dev/null
+++ b/drivers/clk/clk-si5351.c
@@ -0,0 +1,1510 @@
+/*
+ * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * References:
+ * [1] "Si5351A/B/C Data Sheet"
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
+ * [2] "Manually Generating an Si5351 Register Map"
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/rational.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/si5351.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+
+#include "clk-si5351.h"
+
+struct si5351_driver_data;
+
+struct si5351_parameters {
+ unsigned long p1;
+ unsigned long p2;
+ unsigned long p3;
+ int valid;
+};
+
+struct si5351_hw_data {
+ struct clk_hw hw;
+ struct si5351_driver_data *drvdata;
+ struct si5351_parameters params;
+ unsigned char num;
+};
+
+struct si5351_driver_data {
+ enum si5351_variant variant;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct clk_onecell_data onecell;
+
+ struct clk *pxtal;
+ const char *pxtal_name;
+ struct clk_hw xtal;
+ struct clk *pclkin;
+ const char *pclkin_name;
+ struct clk_hw clkin;
+
+ struct si5351_hw_data pll[2];
+ struct si5351_hw_data *msynth;
+ struct si5351_hw_data *clkout;
+};
+
+static const char const *si5351_input_names[] = {
+ "xtal", "clkin"
+};
+static const char const *si5351_pll_names[] = {
+ "plla", "pllb", "vxco"
+};
+static const char const *si5351_msynth_names[] = {
+ "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
+};
+static const char const *si5351_clkout_names[] = {
+ "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
+};
+
+/*
+ * Si5351 i2c regmap
+ */
+static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(drvdata->regmap, reg, &val);
+ if (ret) {
+ dev_err(&drvdata->client->dev,
+ "unable to read from reg%02x\n", reg);
+ return 0;
+ }
+
+ return (u8)val;
+}
+
+static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
+ u8 reg, u8 count, u8 *buf)
+{
+ return regmap_bulk_read(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
+ u8 reg, u8 val)
+{
+ return regmap_write(drvdata->regmap, reg, val);
+}
+
+static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
+ u8 reg, u8 count, const u8 *buf)
+{
+ return regmap_raw_write(drvdata->regmap, reg, buf, count);
+}
+
+static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
+ u8 reg, u8 mask, u8 val)
+{
+ return regmap_update_bits(drvdata->regmap, reg, mask, val);
+}
+
+static inline u8 si5351_msynth_params_address(int num)
+{
+ if (num > 5)
+ return SI5351_CLK6_PARAMETERS + (num - 6);
+ return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
+}
+
+static void si5351_read_parameters(struct si5351_driver_data *drvdata,
+ u8 reg, struct si5351_parameters *params)
+{
+ u8 buf[SI5351_PARAMETERS_LENGTH];
+
+ switch (reg) {
+ case SI5351_CLK6_PARAMETERS:
+ case SI5351_CLK7_PARAMETERS:
+ buf[0] = si5351_reg_read(drvdata, reg);
+ params->p1 = buf[0];
+ params->p2 = 0;
+ params->p3 = 1;
+ break;
+ default:
+ si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+ params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
+ params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
+ params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
+ }
+ params->valid = 1;
+}
+
+static void si5351_write_parameters(struct si5351_driver_data *drvdata,
+ u8 reg, struct si5351_parameters *params)
+{
+ u8 buf[SI5351_PARAMETERS_LENGTH];
+
+ switch (reg) {
+ case SI5351_CLK6_PARAMETERS:
+ case SI5351_CLK7_PARAMETERS:
+ buf[0] = params->p1 & 0xff;
+ si5351_reg_write(drvdata, reg, buf[0]);
+ break;
+ default:
+ buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
+ buf[1] = params->p3 & 0xff;
+ /* save rdiv and divby4 */
+ buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
+ buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
+ buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
+ buf[4] = params->p1 & 0xff;
+ buf[5] = ((params->p3 & 0xf0000) >> 12) |
+ ((params->p2 & 0xf0000) >> 16);
+ buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
+ buf[7] = params->p2 & 0xff;
+ si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
+ }
+}
+
+static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SI5351_DEVICE_STATUS:
+ case SI5351_INTERRUPT_STATUS:
+ case SI5351_PLL_RESET:
+ return true;
+ }
+ return false;
+}
+
+static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+ /* reserved registers */
+ if (reg >= 4 && reg <= 8)
+ return false;
+ if (reg >= 10 && reg <= 14)
+ return false;
+ if (reg >= 173 && reg <= 176)
+ return false;
+ if (reg >= 178 && reg <= 182)
+ return false;
+ /* read-only */
+ if (reg == SI5351_DEVICE_STATUS)
+ return false;
+ return true;
+}
+
+static struct regmap_config si5351_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 187,
+ .writeable_reg = si5351_regmap_is_writeable,
+ .volatile_reg = si5351_regmap_is_volatile,
+};
+
+/*
+ * Si5351 xtal clock input
+ */
+static int si5351_xtal_prepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, xtal);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
+ return 0;
+}
+
+static void si5351_xtal_unprepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, xtal);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_XTAL_ENABLE, 0);
+}
+
+static const struct clk_ops si5351_xtal_ops = {
+ .prepare = si5351_xtal_prepare,
+ .unprepare = si5351_xtal_unprepare,
+};
+
+/*
+ * Si5351 clkin clock input (Si5351C only)
+ */
+static int si5351_clkin_prepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
+ return 0;
+}
+
+static void si5351_clkin_unprepare(struct clk_hw *hw)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
+ SI5351_CLKIN_ENABLE, 0);
+}
+
+/*
+ * CMOS clock source constraints:
+ * The input frequency range of the PLL is 10Mhz to 40MHz.
+ * If CLKIN is >40MHz, the input divider must be used.
+ */
+static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_driver_data *drvdata =
+ container_of(hw, struct si5351_driver_data, clkin);
+ unsigned long rate;
+ unsigned char idiv;
+
+ rate = parent_rate;
+ if (parent_rate > 160000000) {
+ idiv = SI5351_CLKIN_DIV_8;
+ rate /= 8;
+ } else if (parent_rate > 80000000) {
+ idiv = SI5351_CLKIN_DIV_4;
+ rate /= 4;
+ } else if (parent_rate > 40000000) {
+ idiv = SI5351_CLKIN_DIV_2;
+ rate /= 2;
+ } else {
+ idiv = SI5351_CLKIN_DIV_1;
+ }
+
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+ SI5351_CLKIN_DIV_MASK, idiv);
+
+ dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
+ __func__, (1 << (idiv >> 6)), rate);
+
+ return rate;
+}
+
+static const struct clk_ops si5351_clkin_ops = {
+ .prepare = si5351_clkin_prepare,
+ .unprepare = si5351_clkin_unprepare,
+ .recalc_rate = si5351_clkin_recalc_rate,
+};
+
+/*
+ * Si5351 vxco clock input (Si5351B only)
+ */
+
+static int si5351_vxco_prepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n");
+
+ return 0;
+}
+
+static void si5351_vxco_unprepare(struct clk_hw *hw)
+{
+}
+
+static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 0;
+}
+
+static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent)
+{
+ return 0;
+}
+
+static const struct clk_ops si5351_vxco_ops = {
+ .prepare = si5351_vxco_prepare,
+ .unprepare = si5351_vxco_unprepare,
+ .recalc_rate = si5351_vxco_recalc_rate,
+ .set_rate = si5351_vxco_set_rate,
+};
+
+/*
+ * Si5351 pll a/b
+ *
+ * Feedback Multisynth Divider Equations [2]
+ *
+ * fVCO = fIN * (a + b/c)
+ *
+ * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and
+ * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV
+ *
+ * Feedback Multisynth Register Equations
+ *
+ * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * (3) MSNx_P3[19:0] = c
+ *
+ * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c
+ *
+ * Using (4) on (1) yields:
+ * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512
+ * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c
+ *
+ * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128
+ * = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3)
+ *
+ */
+static int _si5351_pll_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_pll_src parent)
+{
+ u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+
+ if (parent == SI5351_PLL_SRC_DEFAULT)
+ return 0;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (drvdata->variant != SI5351_VARIANT_C &&
+ parent != SI5351_PLL_SRC_XTAL)
+ return -EINVAL;
+
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask,
+ (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask);
+ return 0;
+}
+
+static unsigned char si5351_pll_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE;
+ u8 val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE);
+
+ return (val & mask) ? 1 : 0;
+}
+
+static int si5351_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ if (hwdata->drvdata->variant != SI5351_VARIANT_C &&
+ index > 0)
+ return -EPERM;
+
+ if (index > 1)
+ return -EINVAL;
+
+ return _si5351_pll_reparent(hwdata->drvdata, hwdata->num,
+ (index == 0) ? SI5351_PLL_SRC_XTAL :
+ SI5351_PLL_SRC_CLKIN);
+}
+
+static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+ SI5351_PLLB_PARAMETERS;
+ unsigned long long rate;
+
+ if (!hwdata->params.valid)
+ si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (hwdata->params.p3 == 0)
+ return parent_rate;
+
+ /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */
+ rate = hwdata->params.p1 * hwdata->params.p3;
+ rate += 512 * hwdata->params.p3;
+ rate += hwdata->params.p2;
+ rate *= parent_rate;
+ do_div(rate, 128 * hwdata->params.p3);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ parent_rate, (unsigned long)rate);
+
+ return (unsigned long)rate;
+}
+
+static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long rfrac, denom, a, b, c;
+ unsigned long long lltmp;
+
+ if (rate < SI5351_PLL_VCO_MIN)
+ rate = SI5351_PLL_VCO_MIN;
+ if (rate > SI5351_PLL_VCO_MAX)
+ rate = SI5351_PLL_VCO_MAX;
+
+ /* determine integer part of feedback equation */
+ a = rate / *parent_rate;
+
+ if (a < SI5351_PLL_A_MIN)
+ rate = *parent_rate * SI5351_PLL_A_MIN;
+ if (a > SI5351_PLL_A_MAX)
+ rate = *parent_rate * SI5351_PLL_A_MAX;
+
+ /* find best approximation for b/c = fVCO mod fIN */
+ denom = 1000 * 1000;
+ lltmp = rate % (*parent_rate);
+ lltmp *= denom;
+ do_div(lltmp, *parent_rate);
+ rfrac = (unsigned long)lltmp;
+
+ b = 0;
+ c = 1;
+ if (rfrac)
+ rational_best_approximation(rfrac, denom,
+ SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c);
+
+ /* calculate parameters */
+ hwdata->params.p3 = c;
+ hwdata->params.p2 = (128 * b) % c;
+ hwdata->params.p1 = 128 * a;
+ hwdata->params.p1 += (128 * b / c);
+ hwdata->params.p1 -= 512;
+
+ /* recalculate rate by fIN * (a + b/c) */
+ lltmp = *parent_rate;
+ lltmp *= b;
+ do_div(lltmp, c);
+
+ rate = (unsigned long)lltmp;
+ rate += *parent_rate * a;
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), a, b, c,
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS :
+ SI5351_PLLB_PARAMETERS;
+
+ /* write multisynth parameters */
+ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ /* plla/pllb ctrl is in clk6/clk7 ctrl registers */
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num,
+ SI5351_CLK_INTEGER_MODE,
+ (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_pll_ops = {
+ .set_parent = si5351_pll_set_parent,
+ .get_parent = si5351_pll_get_parent,
+ .recalc_rate = si5351_pll_recalc_rate,
+ .round_rate = si5351_pll_round_rate,
+ .set_rate = si5351_pll_set_rate,
+};
+
+/*
+ * Si5351 multisync divider
+ *
+ * for fOUT <= 150 MHz:
+ *
+ * fOUT = (fIN * (a + b/c)) / CLKOUTDIV
+ *
+ * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and
+ * fIN = fVCO0, fVCO1
+ *
+ * Output Clock Multisynth Register Equations
+ *
+ * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512
+ * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
+ * MSx_P3[19:0] = c
+ *
+ * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
+ *
+ * for 150MHz < fOUT <= 160MHz:
+ *
+ * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b
+ */
+static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_multisynth_src parent)
+{
+ if (parent == SI5351_MULTISYNTH_SRC_DEFAULT)
+ return 0;
+
+ if (num > 8)
+ return -EINVAL;
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT,
+ (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 :
+ SI5351_CLK_PLL_SELECT);
+ return 0;
+}
+
+static unsigned char si5351_msynth_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+
+ return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0;
+}
+
+static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num,
+ (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 :
+ SI5351_MULTISYNTH_SRC_VCO1);
+}
+
+static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = si5351_msynth_params_address(hwdata->num);
+ unsigned long long rate;
+ unsigned long m;
+
+ if (!hwdata->params.valid)
+ si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (hwdata->params.p3 == 0)
+ return parent_rate;
+
+ /*
+ * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
+ * multisync6-7: fOUT = fIN / P1
+ */
+ rate = parent_rate;
+ if (hwdata->num > 5) {
+ m = hwdata->params.p1;
+ } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
+ SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
+ m = 4;
+ } else {
+ rate *= 128 * hwdata->params.p3;
+ m = hwdata->params.p1 * hwdata->params.p3;
+ m += hwdata->params.p2;
+ m += 512 * hwdata->params.p3;
+ }
+
+ if (m == 0)
+ return 0;
+ do_div(rate, m);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ m, parent_rate, (unsigned long)rate);
+
+ return (unsigned long)rate;
+}
+
+static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long long lltmp;
+ unsigned long a, b, c;
+ int divby4;
+
+ /* multisync6-7 can only handle freqencies < 150MHz */
+ if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ)
+ rate = SI5351_MULTISYNTH67_MAX_FREQ;
+
+ /* multisync frequency is 1MHz .. 160MHz */
+ if (rate > SI5351_MULTISYNTH_MAX_FREQ)
+ rate = SI5351_MULTISYNTH_MAX_FREQ;
+ if (rate < SI5351_MULTISYNTH_MIN_FREQ)
+ rate = SI5351_MULTISYNTH_MIN_FREQ;
+
+ divby4 = 0;
+ if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+ divby4 = 1;
+
+ /* multisync can set pll */
+ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+ /*
+ * find largest integer divider for max
+ * vco frequency and given target rate
+ */
+ if (divby4 == 0) {
+ lltmp = SI5351_PLL_VCO_MAX;
+ do_div(lltmp, rate);
+ a = (unsigned long)lltmp;
+ } else
+ a = 4;
+
+ b = 0;
+ c = 1;
+
+ *parent_rate = a * rate;
+ } else {
+ unsigned long rfrac, denom;
+
+ /* disable divby4 */
+ if (divby4) {
+ rate = SI5351_MULTISYNTH_DIVBY4_FREQ;
+ divby4 = 0;
+ }
+
+ /* determine integer part of divider equation */
+ a = *parent_rate / rate;
+ if (a < SI5351_MULTISYNTH_A_MIN)
+ a = SI5351_MULTISYNTH_A_MIN;
+ if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
+ a = SI5351_MULTISYNTH67_A_MAX;
+ else if (a > SI5351_MULTISYNTH_A_MAX)
+ a = SI5351_MULTISYNTH_A_MAX;
+
+ /* find best approximation for b/c = fVCO mod fOUT */
+ denom = 1000 * 1000;
+ lltmp = (*parent_rate) % rate;
+ lltmp *= denom;
+ do_div(lltmp, rate);
+ rfrac = (unsigned long)lltmp;
+
+ b = 0;
+ c = 1;
+ if (rfrac)
+ rational_best_approximation(rfrac, denom,
+ SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX,
+ &b, &c);
+ }
+
+ /* recalculate rate by fOUT = fIN / (a + b/c) */
+ lltmp = *parent_rate;
+ lltmp *= c;
+ do_div(lltmp, a * c + b);
+ rate = (unsigned long)lltmp;
+
+ /* calculate parameters */
+ if (divby4) {
+ hwdata->params.p3 = 1;
+ hwdata->params.p2 = 0;
+ hwdata->params.p1 = 0;
+ } else {
+ hwdata->params.p3 = c;
+ hwdata->params.p2 = (128 * b) % c;
+ hwdata->params.p1 = 128 * a;
+ hwdata->params.p1 += (128 * b / c);
+ hwdata->params.p1 -= 512;
+ }
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4,
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ u8 reg = si5351_msynth_params_address(hwdata->num);
+ int divby4 = 0;
+
+ /* write multisynth parameters */
+ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params);
+
+ if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ)
+ divby4 = 1;
+
+ /* enable/disable integer mode and divby4 on multisynth0-5 */
+ if (hwdata->num < 6) {
+ si5351_set_bits(hwdata->drvdata, reg + 2,
+ SI5351_OUTPUT_CLK_DIVBY4,
+ (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0);
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_INTEGER_MODE,
+ (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0);
+ }
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk),
+ hwdata->params.p1, hwdata->params.p2, hwdata->params.p3,
+ divby4, parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_msynth_ops = {
+ .set_parent = si5351_msynth_set_parent,
+ .get_parent = si5351_msynth_get_parent,
+ .recalc_rate = si5351_msynth_recalc_rate,
+ .round_rate = si5351_msynth_round_rate,
+ .set_rate = si5351_msynth_set_rate,
+};
+
+/*
+ * Si5351 clkout divider
+ */
+static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata,
+ int num, enum si5351_clkout_src parent)
+{
+ u8 val;
+
+ if (num > 8)
+ return -EINVAL;
+
+ switch (parent) {
+ case SI5351_CLKOUT_SRC_MSYNTH_N:
+ val = SI5351_CLK_INPUT_MULTISYNTH_N;
+ break;
+ case SI5351_CLKOUT_SRC_MSYNTH_0_4:
+ /* clk0/clk4 can only connect to its own multisync */
+ if (num == 0 || num == 4)
+ val = SI5351_CLK_INPUT_MULTISYNTH_N;
+ else
+ val = SI5351_CLK_INPUT_MULTISYNTH_0_4;
+ break;
+ case SI5351_CLKOUT_SRC_XTAL:
+ val = SI5351_CLK_INPUT_XTAL;
+ break;
+ case SI5351_CLKOUT_SRC_CLKIN:
+ if (drvdata->variant != SI5351_VARIANT_C)
+ return -EINVAL;
+
+ val = SI5351_CLK_INPUT_CLKIN;
+ break;
+ default:
+ return 0;
+ }
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+ SI5351_CLK_INPUT_MASK, val);
+ return 0;
+}
+
+static int _si5351_clkout_set_drive_strength(
+ struct si5351_driver_data *drvdata, int num,
+ enum si5351_drive_strength drive)
+{
+ u8 mask;
+
+ if (num > 8)
+ return -EINVAL;
+
+ switch (drive) {
+ case SI5351_DRIVE_2MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_2MA;
+ break;
+ case SI5351_DRIVE_4MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_4MA;
+ break;
+ case SI5351_DRIVE_6MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_6MA;
+ break;
+ case SI5351_DRIVE_8MA:
+ mask = SI5351_CLK_DRIVE_STRENGTH_8MA;
+ break;
+ default:
+ return 0;
+ }
+
+ si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num,
+ SI5351_CLK_DRIVE_STRENGTH_MASK, mask);
+ return 0;
+}
+
+static int si5351_clkout_prepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, 0);
+ si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+ (1 << hwdata->num), 0);
+ return 0;
+}
+
+static void si5351_clkout_unprepare(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN);
+ si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL,
+ (1 << hwdata->num), (1 << hwdata->num));
+}
+
+static u8 si5351_clkout_get_parent(struct clk_hw *hw)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ int index = 0;
+ unsigned char val;
+
+ val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num);
+ switch (val & SI5351_CLK_INPUT_MASK) {
+ case SI5351_CLK_INPUT_MULTISYNTH_N:
+ index = 0;
+ break;
+ case SI5351_CLK_INPUT_MULTISYNTH_0_4:
+ index = 1;
+ break;
+ case SI5351_CLK_INPUT_XTAL:
+ index = 2;
+ break;
+ case SI5351_CLK_INPUT_CLKIN:
+ index = 3;
+ break;
+ }
+
+ return index;
+}
+
+static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT;
+
+ switch (index) {
+ case 0:
+ parent = SI5351_CLKOUT_SRC_MSYNTH_N;
+ break;
+ case 1:
+ parent = SI5351_CLKOUT_SRC_MSYNTH_0_4;
+ break;
+ case 2:
+ parent = SI5351_CLKOUT_SRC_XTAL;
+ break;
+ case 3:
+ parent = SI5351_CLKOUT_SRC_CLKIN;
+ break;
+ }
+
+ return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent);
+}
+
+static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned char reg;
+ unsigned char rdiv;
+
+ if (hwdata->num > 5)
+ reg = si5351_msynth_params_address(hwdata->num) + 2;
+ else
+ reg = SI5351_CLK6_7_OUTPUT_DIVIDER;
+
+ rdiv = si5351_reg_read(hwdata->drvdata, reg);
+ if (hwdata->num == 6) {
+ rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK;
+ } else {
+ rdiv &= SI5351_OUTPUT_CLK_DIV_MASK;
+ rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT;
+ }
+
+ return parent_rate >> rdiv;
+}
+
+static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned char rdiv;
+
+ /* clkout6/7 can only handle output freqencies < 150MHz */
+ if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ)
+ rate = SI5351_CLKOUT67_MAX_FREQ;
+
+ /* clkout freqency is 8kHz - 160MHz */
+ if (rate > SI5351_CLKOUT_MAX_FREQ)
+ rate = SI5351_CLKOUT_MAX_FREQ;
+ if (rate < SI5351_CLKOUT_MIN_FREQ)
+ rate = SI5351_CLKOUT_MIN_FREQ;
+
+ /* request frequency if multisync master */
+ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) {
+ /* use r divider for frequencies below 1MHz */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ while (rate < SI5351_MULTISYNTH_MIN_FREQ &&
+ rdiv < SI5351_OUTPUT_CLK_DIV_128) {
+ rdiv += 1;
+ rate *= 2;
+ }
+ *parent_rate = rate;
+ } else {
+ unsigned long new_rate, new_err, err;
+
+ /* round to closed rdiv */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ new_rate = *parent_rate;
+ err = abs(new_rate - rate);
+ do {
+ new_rate >>= 1;
+ new_err = abs(new_rate - rate);
+ if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+ break;
+ rdiv++;
+ err = new_err;
+ } while (1);
+ }
+ rate = *parent_rate >> rdiv;
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+ *parent_rate, rate);
+
+ return rate;
+}
+
+static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct si5351_hw_data *hwdata =
+ container_of(hw, struct si5351_hw_data, hw);
+ unsigned long new_rate, new_err, err;
+ unsigned char rdiv;
+
+ /* round to closed rdiv */
+ rdiv = SI5351_OUTPUT_CLK_DIV_1;
+ new_rate = parent_rate;
+ err = abs(new_rate - rate);
+ do {
+ new_rate >>= 1;
+ new_err = abs(new_rate - rate);
+ if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128)
+ break;
+ rdiv++;
+ err = new_err;
+ } while (1);
+
+ /* write output divider */
+ switch (hwdata->num) {
+ case 6:
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+ SI5351_OUTPUT_CLK6_DIV_MASK, rdiv);
+ break;
+ case 7:
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER,
+ SI5351_OUTPUT_CLK_DIV_MASK,
+ rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+ break;
+ default:
+ si5351_set_bits(hwdata->drvdata,
+ si5351_msynth_params_address(hwdata->num) + 2,
+ SI5351_OUTPUT_CLK_DIV_MASK,
+ rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT);
+ }
+
+ /* powerup clkout */
+ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num,
+ SI5351_CLK_POWERDOWN, 0);
+
+ dev_dbg(&hwdata->drvdata->client->dev,
+ "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
+ __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv),
+ parent_rate, rate);
+
+ return 0;
+}
+
+static const struct clk_ops si5351_clkout_ops = {
+ .prepare = si5351_clkout_prepare,
+ .unprepare = si5351_clkout_unprepare,
+ .set_parent = si5351_clkout_set_parent,
+ .get_parent = si5351_clkout_get_parent,
+ .recalc_rate = si5351_clkout_recalc_rate,
+ .round_rate = si5351_clkout_round_rate,
+ .set_rate = si5351_clkout_set_rate,
+};
+
+/*
+ * Si5351 i2c probe and DT
+ */
+#ifdef CONFIG_OF
+static const struct of_device_id si5351_dt_ids[] = {
+ { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, },
+ { .compatible = "silabs,si5351a-msop",
+ .data = (void *)SI5351_VARIANT_A3, },
+ { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, },
+ { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, si5351_dt_ids);
+
+static int si5351_dt_parse(struct i2c_client *client)
+{
+ struct device_node *child, *np = client->dev.of_node;
+ struct si5351_platform_data *pdata;
+ const struct of_device_id *match;
+ struct property *prop;
+ const __be32 *p;
+ int num = 0;
+ u32 val;
+
+ if (np == NULL)
+ return 0;
+
+ match = of_match_node(si5351_dt_ids, np);
+ if (match == NULL)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->variant = (enum si5351_variant)match->data;
+ pdata->clk_xtal = of_clk_get(np, 0);
+ if (!IS_ERR(pdata->clk_xtal))
+ clk_put(pdata->clk_xtal);
+ pdata->clk_clkin = of_clk_get(np, 1);
+ if (!IS_ERR(pdata->clk_clkin))
+ clk_put(pdata->clk_clkin);
+
+ /*
+ * property silabs,pll-source : <num src>, [<..>]
+ * allow to selectively set pll source
+ */
+ of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
+ if (num >= 2) {
+ dev_err(&client->dev,
+ "invalid pll %d on pll-source prop\n", num);
+ return -EINVAL;
+ }
+
+ p = of_prop_next_u32(prop, p, &val);
+ if (!p) {
+ dev_err(&client->dev,
+ "missing pll-source for pll %d\n", num);
+ return -EINVAL;
+ }
+
+ switch (val) {
+ case 0:
+ pdata->pll_src[num] = SI5351_PLL_SRC_XTAL;
+ break;
+ case 1:
+ if (pdata->variant != SI5351_VARIANT_C) {
+ dev_err(&client->dev,
+ "invalid parent %d for pll %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for pll %d\n", val, num);
+ return -EINVAL;
+ }
+ }
+
+ /* per clkout properties */
+ for_each_child_of_node(np, child) {
+ if (of_property_read_u32(child, "reg", &num)) {
+ dev_err(&client->dev, "missing reg property of %s\n",
+ child->name);
+ return -EINVAL;
+ }
+
+ if (num >= 8 ||
+ (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) {
+ dev_err(&client->dev, "invalid clkout %d\n", num);
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(child, "silabs,multisynth-source",
+ &val)) {
+ switch (val) {
+ case 0:
+ pdata->clkout[num].multisynth_src =
+ SI5351_MULTISYNTH_SRC_VCO0;
+ break;
+ case 1:
+ pdata->clkout[num].multisynth_src =
+ SI5351_MULTISYNTH_SRC_VCO1;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for multisynth %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "silabs,clock-source", &val)) {
+ switch (val) {
+ case 0:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_MSYNTH_N;
+ break;
+ case 1:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_MSYNTH_0_4;
+ break;
+ case 2:
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_XTAL;
+ break;
+ case 3:
+ if (pdata->variant != SI5351_VARIANT_C) {
+ dev_err(&client->dev,
+ "invalid parent %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ pdata->clkout[num].clkout_src =
+ SI5351_CLKOUT_SRC_CLKIN;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid parent %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "silabs,drive-strength",
+ &val)) {
+ switch (val) {
+ case SI5351_DRIVE_2MA:
+ case SI5351_DRIVE_4MA:
+ case SI5351_DRIVE_6MA:
+ case SI5351_DRIVE_8MA:
+ pdata->clkout[num].drive = val;
+ break;
+ default:
+ dev_err(&client->dev,
+ "invalid drive strength %d for clkout %d\n",
+ val, num);
+ return -EINVAL;
+ }
+ }
+
+ if (!of_property_read_u32(child, "clock-frequency", &val))
+ pdata->clkout[num].rate = val;
+
+ pdata->clkout[num].pll_master =
+ of_property_read_bool(child, "silabs,pll-master");
+ }
+ client->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int si5351_dt_parse(struct i2c_client *client)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int si5351_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct si5351_platform_data *pdata;
+ struct si5351_driver_data *drvdata;
+ struct clk_init_data init;
+ struct clk *clk;
+ const char *parent_names[4];
+ u8 num_parents, num_clocks;
+ int ret, n;
+
+ ret = si5351_dt_parse(client);
+ if (ret)
+ return ret;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL) {
+ dev_err(&client->dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, drvdata);
+ drvdata->client = client;
+ drvdata->variant = pdata->variant;
+ drvdata->pxtal = pdata->clk_xtal;
+ drvdata->pclkin = pdata->clk_clkin;
+
+ drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
+ if (IS_ERR(drvdata->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(drvdata->regmap);
+ }
+
+ /* Disable interrupts */
+ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0);
+ /* Set disabled output drivers to drive low */
+ si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00);
+ si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00);
+ /* Ensure pll select is on XTAL for Si5351A/B */
+ if (drvdata->variant != SI5351_VARIANT_C)
+ si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
+ SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0);
+
+ /* setup clock configuration */
+ for (n = 0; n < 2; n++) {
+ ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent pll %d to %d\n",
+ n, pdata->pll_src[n]);
+ return ret;
+ }
+ }
+
+ for (n = 0; n < 8; n++) {
+ ret = _si5351_msynth_reparent(drvdata, n,
+ pdata->clkout[n].multisynth_src);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent multisynth %d to %d\n",
+ n, pdata->clkout[n].multisynth_src);
+ return ret;
+ }
+
+ ret = _si5351_clkout_reparent(drvdata, n,
+ pdata->clkout[n].clkout_src);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to reparent clkout %d to %d\n",
+ n, pdata->clkout[n].clkout_src);
+ return ret;
+ }
+
+ ret = _si5351_clkout_set_drive_strength(drvdata, n,
+ pdata->clkout[n].drive);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed set drive strength of clkout%d to %d\n",
+ n, pdata->clkout[n].drive);
+ return ret;
+ }
+ }
+
+ /* register xtal input clock gate */
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_input_names[0];
+ init.ops = &si5351_xtal_ops;
+ init.flags = 0;
+ if (!IS_ERR(drvdata->pxtal)) {
+ drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
+ init.parent_names = &drvdata->pxtal_name;
+ init.num_parents = 1;
+ }
+ drvdata->xtal.init = &init;
+ clk = devm_clk_register(&client->dev, &drvdata->xtal);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return PTR_ERR(clk);
+ }
+
+ /* register clkin input clock gate */
+ if (drvdata->variant == SI5351_VARIANT_C) {
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_input_names[1];
+ init.ops = &si5351_clkin_ops;
+ if (!IS_ERR(drvdata->pclkin)) {
+ drvdata->pclkin_name = __clk_get_name(drvdata->pclkin);
+ init.parent_names = &drvdata->pclkin_name;
+ init.num_parents = 1;
+ }
+ drvdata->clkin.init = &init;
+ clk = devm_clk_register(&client->dev, &drvdata->clkin);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return PTR_ERR(clk);
+ }
+ }
+
+ /* Si5351C allows to mux either xtal or clkin to PLL input */
+ num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1;
+ parent_names[0] = si5351_input_names[0];
+ parent_names[1] = si5351_input_names[1];
+
+ /* register PLLA */
+ drvdata->pll[0].num = 0;
+ drvdata->pll[0].drvdata = drvdata;
+ drvdata->pll[0].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_pll_names[0];
+ init.ops = &si5351_pll_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return -EINVAL;
+ }
+
+ /* register PLLB or VXCO (Si5351B) */
+ drvdata->pll[1].num = 1;
+ drvdata->pll[1].drvdata = drvdata;
+ drvdata->pll[1].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ if (drvdata->variant == SI5351_VARIANT_B) {
+ init.name = si5351_pll_names[2];
+ init.ops = &si5351_vxco_ops;
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ } else {
+ init.name = si5351_pll_names[1];
+ init.ops = &si5351_pll_ops;
+ init.flags = 0;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ }
+ clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n", init.name);
+ return -EINVAL;
+ }
+
+ /* register clk multisync and clk out divider */
+ num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8;
+ parent_names[0] = si5351_pll_names[0];
+ if (drvdata->variant == SI5351_VARIANT_B)
+ parent_names[1] = si5351_pll_names[2];
+ else
+ parent_names[1] = si5351_pll_names[1];
+
+ drvdata->msynth = devm_kzalloc(&client->dev, num_clocks *
+ sizeof(*drvdata->msynth), GFP_KERNEL);
+ drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
+ sizeof(*drvdata->clkout), GFP_KERNEL);
+
+ drvdata->onecell.clk_num = num_clocks;
+ drvdata->onecell.clks = devm_kzalloc(&client->dev,
+ num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
+
+ if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
+ !drvdata->onecell.clks))
+ return -ENOMEM;
+
+ for (n = 0; n < num_clocks; n++) {
+ drvdata->msynth[n].num = n;
+ drvdata->msynth[n].drvdata = drvdata;
+ drvdata->msynth[n].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_msynth_names[n];
+ init.ops = &si5351_msynth_ops;
+ init.flags = 0;
+ if (pdata->clkout[n].pll_master)
+ init.flags |= CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = 2;
+ clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return -EINVAL;
+ }
+ }
+
+ num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3;
+ parent_names[2] = si5351_input_names[0];
+ parent_names[3] = si5351_input_names[1];
+ for (n = 0; n < num_clocks; n++) {
+ parent_names[0] = si5351_msynth_names[n];
+ parent_names[1] = (n < 4) ? si5351_msynth_names[0] :
+ si5351_msynth_names[4];
+
+ drvdata->clkout[n].num = n;
+ drvdata->clkout[n].drvdata = drvdata;
+ drvdata->clkout[n].hw.init = &init;
+ memset(&init, 0, sizeof(init));
+ init.name = si5351_clkout_names[n];
+ init.ops = &si5351_clkout_ops;
+ init.flags = 0;
+ if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N)
+ init.flags |= CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "unable to register %s\n",
+ init.name);
+ return -EINVAL;
+ }
+ drvdata->onecell.clks[n] = clk;
+ }
+
+ ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
+ &drvdata->onecell);
+ if (ret) {
+ dev_err(&client->dev, "unable to add clk provider\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id si5351_i2c_ids[] = {
+ { "silabs,si5351", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
+
+static struct i2c_driver si5351_driver = {
+ .driver = {
+ .name = "si5351",
+ .of_match_table = of_match_ptr(si5351_dt_ids),
+ },
+ .probe = si5351_i2c_probe,
+ .id_table = si5351_i2c_ids,
+};
+module_i2c_driver(si5351_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com");
+MODULE_DESCRIPTION("Silicon Labs Si5351A/B/C clock generator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h
new file mode 100644
index 000000000000..af41b5080f43
--- /dev/null
+++ b/drivers/clk/clk-si5351.h
@@ -0,0 +1,155 @@
+/*
+ * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Rabeeh Khoury <rabeeh@solid-run.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _CLK_SI5351_H_
+#define _CLK_SI5351_H_
+
+#define SI5351_BUS_BASE_ADDR 0x60
+
+#define SI5351_PLL_VCO_MIN 600000000
+#define SI5351_PLL_VCO_MAX 900000000
+#define SI5351_MULTISYNTH_MIN_FREQ 1000000
+#define SI5351_MULTISYNTH_DIVBY4_FREQ 150000000
+#define SI5351_MULTISYNTH_MAX_FREQ 160000000
+#define SI5351_MULTISYNTH67_MAX_FREQ SI5351_MULTISYNTH_DIVBY4_FREQ
+#define SI5351_CLKOUT_MIN_FREQ 8000
+#define SI5351_CLKOUT_MAX_FREQ SI5351_MULTISYNTH_MAX_FREQ
+#define SI5351_CLKOUT67_MAX_FREQ SI5351_MULTISYNTH67_MAX_FREQ
+
+#define SI5351_PLL_A_MIN 15
+#define SI5351_PLL_A_MAX 90
+#define SI5351_PLL_B_MAX (SI5351_PLL_C_MAX-1)
+#define SI5351_PLL_C_MAX 1048575
+#define SI5351_MULTISYNTH_A_MIN 6
+#define SI5351_MULTISYNTH_A_MAX 1800
+#define SI5351_MULTISYNTH67_A_MAX 254
+#define SI5351_MULTISYNTH_B_MAX (SI5351_MULTISYNTH_C_MAX-1)
+#define SI5351_MULTISYNTH_C_MAX 1048575
+#define SI5351_MULTISYNTH_P1_MAX ((1<<18)-1)
+#define SI5351_MULTISYNTH_P2_MAX ((1<<20)-1)
+#define SI5351_MULTISYNTH_P3_MAX ((1<<20)-1)
+
+#define SI5351_DEVICE_STATUS 0
+#define SI5351_INTERRUPT_STATUS 1
+#define SI5351_INTERRUPT_MASK 2
+#define SI5351_STATUS_SYS_INIT (1<<7)
+#define SI5351_STATUS_LOL_B (1<<6)
+#define SI5351_STATUS_LOL_A (1<<5)
+#define SI5351_STATUS_LOS (1<<4)
+#define SI5351_OUTPUT_ENABLE_CTRL 3
+#define SI5351_OEB_PIN_ENABLE_CTRL 9
+#define SI5351_PLL_INPUT_SOURCE 15
+#define SI5351_CLKIN_DIV_MASK (3<<6)
+#define SI5351_CLKIN_DIV_1 (0<<6)
+#define SI5351_CLKIN_DIV_2 (1<<6)
+#define SI5351_CLKIN_DIV_4 (2<<6)
+#define SI5351_CLKIN_DIV_8 (3<<6)
+#define SI5351_PLLB_SOURCE (1<<3)
+#define SI5351_PLLA_SOURCE (1<<2)
+
+#define SI5351_CLK0_CTRL 16
+#define SI5351_CLK1_CTRL 17
+#define SI5351_CLK2_CTRL 18
+#define SI5351_CLK3_CTRL 19
+#define SI5351_CLK4_CTRL 20
+#define SI5351_CLK5_CTRL 21
+#define SI5351_CLK6_CTRL 22
+#define SI5351_CLK7_CTRL 23
+#define SI5351_CLK_POWERDOWN (1<<7)
+#define SI5351_CLK_INTEGER_MODE (1<<6)
+#define SI5351_CLK_PLL_SELECT (1<<5)
+#define SI5351_CLK_INVERT (1<<4)
+#define SI5351_CLK_INPUT_MASK (3<<2)
+#define SI5351_CLK_INPUT_XTAL (0<<2)
+#define SI5351_CLK_INPUT_CLKIN (1<<2)
+#define SI5351_CLK_INPUT_MULTISYNTH_0_4 (2<<2)
+#define SI5351_CLK_INPUT_MULTISYNTH_N (3<<2)
+#define SI5351_CLK_DRIVE_STRENGTH_MASK (3<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_2MA (0<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_4MA (1<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_6MA (2<<0)
+#define SI5351_CLK_DRIVE_STRENGTH_8MA (3<<0)
+
+#define SI5351_CLK3_0_DISABLE_STATE 24
+#define SI5351_CLK7_4_DISABLE_STATE 25
+#define SI5351_CLK_DISABLE_STATE_LOW 0
+#define SI5351_CLK_DISABLE_STATE_HIGH 1
+#define SI5351_CLK_DISABLE_STATE_FLOAT 2
+#define SI5351_CLK_DISABLE_STATE_NEVER 3
+
+#define SI5351_PARAMETERS_LENGTH 8
+#define SI5351_PLLA_PARAMETERS 26
+#define SI5351_PLLB_PARAMETERS 34
+#define SI5351_CLK0_PARAMETERS 42
+#define SI5351_CLK1_PARAMETERS 50
+#define SI5351_CLK2_PARAMETERS 58
+#define SI5351_CLK3_PARAMETERS 66
+#define SI5351_CLK4_PARAMETERS 74
+#define SI5351_CLK5_PARAMETERS 82
+#define SI5351_CLK6_PARAMETERS 90
+#define SI5351_CLK7_PARAMETERS 91
+#define SI5351_CLK6_7_OUTPUT_DIVIDER 92
+#define SI5351_OUTPUT_CLK_DIV_MASK (7 << 4)
+#define SI5351_OUTPUT_CLK6_DIV_MASK (7 << 0)
+#define SI5351_OUTPUT_CLK_DIV_SHIFT 4
+#define SI5351_OUTPUT_CLK_DIV6_SHIFT 0
+#define SI5351_OUTPUT_CLK_DIV_1 0
+#define SI5351_OUTPUT_CLK_DIV_2 1
+#define SI5351_OUTPUT_CLK_DIV_4 2
+#define SI5351_OUTPUT_CLK_DIV_8 3
+#define SI5351_OUTPUT_CLK_DIV_16 4
+#define SI5351_OUTPUT_CLK_DIV_32 5
+#define SI5351_OUTPUT_CLK_DIV_64 6
+#define SI5351_OUTPUT_CLK_DIV_128 7
+#define SI5351_OUTPUT_CLK_DIVBY4 (3<<2)
+
+#define SI5351_SSC_PARAM0 149
+#define SI5351_SSC_PARAM1 150
+#define SI5351_SSC_PARAM2 151
+#define SI5351_SSC_PARAM3 152
+#define SI5351_SSC_PARAM4 153
+#define SI5351_SSC_PARAM5 154
+#define SI5351_SSC_PARAM6 155
+#define SI5351_SSC_PARAM7 156
+#define SI5351_SSC_PARAM8 157
+#define SI5351_SSC_PARAM9 158
+#define SI5351_SSC_PARAM10 159
+#define SI5351_SSC_PARAM11 160
+#define SI5351_SSC_PARAM12 161
+
+#define SI5351_VXCO_PARAMETERS_LOW 162
+#define SI5351_VXCO_PARAMETERS_MID 163
+#define SI5351_VXCO_PARAMETERS_HIGH 164
+
+#define SI5351_CLK0_PHASE_OFFSET 165
+#define SI5351_CLK1_PHASE_OFFSET 166
+#define SI5351_CLK2_PHASE_OFFSET 167
+#define SI5351_CLK3_PHASE_OFFSET 168
+#define SI5351_CLK4_PHASE_OFFSET 169
+#define SI5351_CLK5_PHASE_OFFSET 170
+
+#define SI5351_PLL_RESET 177
+#define SI5351_PLL_RESET_B (1<<7)
+#define SI5351_PLL_RESET_A (1<<5)
+
+#define SI5351_CRYSTAL_LOAD 183
+#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
+#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
+#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
+#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
+
+#define SI5351_FANOUT_ENABLE 187
+#define SI5351_CLKIN_ENABLE (1<<7)
+#define SI5351_XTAL_ENABLE (1<<6)
+#define SI5351_MULTISYNTH_ENABLE (1<<4)
+
+#endif
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index 09c63315e579..debf688afa8e 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -488,6 +488,7 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
case PLL_TYPE_WM8750:
wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2);
pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2);
+ break;
default:
pr_err("%s: invalid pll type\n", __func__);
return 0;
@@ -523,6 +524,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
case PLL_TYPE_WM8750:
wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2);
round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2);
+ break;
default:
round_rate = 0;
}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0230c9d95975..934cfd18f72d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -343,6 +343,39 @@ out:
}
/**
+ * clk_debug_reparent - reparent clk node in the debugfs clk tree
+ * @clk: the clk being reparented
+ * @new_parent: the new clk parent, may be NULL
+ *
+ * Rename clk entry in the debugfs clk tree if debugfs has been
+ * initialized. Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+ struct dentry *d;
+ struct dentry *new_parent_d;
+
+ if (!inited)
+ return;
+
+ if (new_parent)
+ new_parent_d = new_parent->dentry;
+ else
+ new_parent_d = orphandir;
+
+ d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
+ new_parent_d, clk->name);
+ if (d)
+ clk->dentry = d;
+ else
+ pr_debug("%s: failed to rename debugfs entry for %s\n",
+ __func__, clk->name);
+}
+
+/**
* clk_debug_init - lazily create the debugfs clk tree visualization
*
* clks are often initialized very early during boot before memory can
@@ -396,6 +429,9 @@ static int __init clk_debug_init(void)
late_initcall(clk_debug_init);
#else
static inline int clk_debug_register(struct clk *clk) { return 0; }
+static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
+{
+}
#endif
/* caller must hold prepare_lock */
@@ -422,6 +458,7 @@ static void clk_unprepare_unused_subtree(struct clk *clk)
clk->ops->unprepare(clk->hw);
}
}
+EXPORT_SYMBOL_GPL(__clk_get_flags);
/* caller must hold prepare_lock */
static void clk_disable_unused_subtree(struct clk *clk)
@@ -462,10 +499,23 @@ out:
return;
}
+static bool clk_ignore_unused;
+static int __init clk_ignore_unused_setup(char *__unused)
+{
+ clk_ignore_unused = true;
+ return 1;
+}
+__setup("clk_ignore_unused", clk_ignore_unused_setup);
+
static int clk_disable_unused(void)
{
struct clk *clk;
+ if (clk_ignore_unused) {
+ pr_warn("clk: Not disabling unused clocks\n");
+ return 0;
+ }
+
clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node)
@@ -991,16 +1041,16 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
else
new_rate = parent_rate;
- /* abort the rate change if a driver returns NOTIFY_BAD */
+ /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
if (clk->notifier_count)
ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
- if (ret == NOTIFY_BAD)
+ if (ret & NOTIFY_STOP_MASK)
goto out;
hlist_for_each_entry(child, &clk->children, child_node) {
ret = __clk_speculate_rates(child, new_rate);
- if (ret == NOTIFY_BAD)
+ if (ret & NOTIFY_STOP_MASK)
break;
}
@@ -1093,7 +1143,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
if (clk->notifier_count) {
ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
- if (ret == NOTIFY_BAD)
+ if (ret & NOTIFY_STOP_MASK)
fail_clk = clk;
}
@@ -1277,16 +1327,8 @@ out:
return ret;
}
-void __clk_reparent(struct clk *clk, struct clk *new_parent)
+static void clk_reparent(struct clk *clk, struct clk *new_parent)
{
-#ifdef CONFIG_COMMON_CLK_DEBUG
- struct dentry *d;
- struct dentry *new_parent_d;
-#endif
-
- if (!clk || !new_parent)
- return;
-
hlist_del(&clk->child_node);
if (new_parent)
@@ -1294,39 +1336,20 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
else
hlist_add_head(&clk->child_node, &clk_orphan_list);
-#ifdef CONFIG_COMMON_CLK_DEBUG
- if (!inited)
- goto out;
-
- if (new_parent)
- new_parent_d = new_parent->dentry;
- else
- new_parent_d = orphandir;
-
- d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
- new_parent_d, clk->name);
- if (d)
- clk->dentry = d;
- else
- pr_debug("%s: failed to rename debugfs entry for %s\n",
- __func__, clk->name);
-out:
-#endif
-
clk->parent = new_parent;
+}
+void __clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+ clk_reparent(clk, new_parent);
+ clk_debug_reparent(clk, new_parent);
__clk_recalc_rates(clk, POST_RATE_CHANGE);
}
-static int __clk_set_parent(struct clk *clk, struct clk *parent)
+static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
{
- struct clk *old_parent;
- unsigned long flags;
- int ret = -EINVAL;
u8 i;
- old_parent = clk->parent;
-
if (!clk->parents)
clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
GFP_KERNEL);
@@ -1346,36 +1369,79 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
}
}
- if (i == clk->num_parents) {
- pr_debug("%s: clock %s is not a possible parent of clock %s\n",
- __func__, parent->name, clk->name);
- goto out;
- }
+ return i;
+}
- /* migrate prepare and enable */
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct clk *old_parent = clk->parent;
+ bool migrated_enable = false;
+
+ /* migrate prepare */
if (clk->prepare_count)
__clk_prepare(parent);
- /* FIXME replace with clk_is_enabled(clk) someday */
flags = clk_enable_lock();
- if (clk->enable_count)
+
+ /* migrate enable */
+ if (clk->enable_count) {
__clk_enable(parent);
+ migrated_enable = true;
+ }
+
+ /* update the clk tree topology */
+ clk_reparent(clk, parent);
+
clk_enable_unlock(flags);
/* change clock input source */
- ret = clk->ops->set_parent(clk->hw, i);
+ if (parent && clk->ops->set_parent)
+ ret = clk->ops->set_parent(clk->hw, p_index);
- /* clean up old prepare and enable */
- flags = clk_enable_lock();
- if (clk->enable_count)
+ if (ret) {
+ /*
+ * The error handling is tricky due to that we need to release
+ * the spinlock while issuing the .set_parent callback. This
+ * means the new parent might have been enabled/disabled in
+ * between, which must be considered when doing rollback.
+ */
+ flags = clk_enable_lock();
+
+ clk_reparent(clk, old_parent);
+
+ if (migrated_enable && clk->enable_count) {
+ __clk_disable(parent);
+ } else if (migrated_enable && (clk->enable_count == 0)) {
+ __clk_disable(old_parent);
+ } else if (!migrated_enable && clk->enable_count) {
+ __clk_disable(parent);
+ __clk_enable(old_parent);
+ }
+
+ clk_enable_unlock(flags);
+
+ if (clk->prepare_count)
+ __clk_unprepare(parent);
+
+ return ret;
+ }
+
+ /* clean up enable for old parent if migration was done */
+ if (migrated_enable) {
+ flags = clk_enable_lock();
__clk_disable(old_parent);
- clk_enable_unlock(flags);
+ clk_enable_unlock(flags);
+ }
+ /* clean up prepare for old parent if migration was done */
if (clk->prepare_count)
__clk_unprepare(old_parent);
-out:
- return ret;
+ /* update debugfs with new clk tree topology */
+ clk_debug_reparent(clk, parent);
+ return 0;
}
/**
@@ -1393,11 +1459,14 @@ out:
int clk_set_parent(struct clk *clk, struct clk *parent)
{
int ret = 0;
+ u8 p_index = 0;
+ unsigned long p_rate = 0;
if (!clk || !clk->ops)
return -EINVAL;
- if (!clk->ops->set_parent)
+ /* verify ops for for multi-parent clks */
+ if ((clk->num_parents > 1) && (!clk->ops->set_parent))
return -ENOSYS;
/* prevent racing with updates to the clock topology */
@@ -1406,28 +1475,40 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
if (clk->parent == parent)
goto out;
+ /* check that we are allowed to re-parent if the clock is in use */
+ if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* try finding the new parent index */
+ if (parent) {
+ p_index = clk_fetch_parent_index(clk, parent);
+ p_rate = parent->rate;
+ if (p_index == clk->num_parents) {
+ pr_debug("%s: clk %s can not be parent of clk %s\n",
+ __func__, parent->name, clk->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
/* propagate PRE_RATE_CHANGE notifications */
if (clk->notifier_count)
- ret = __clk_speculate_rates(clk, parent->rate);
+ ret = __clk_speculate_rates(clk, p_rate);
/* abort if a driver objects */
- if (ret == NOTIFY_STOP)
+ if (ret & NOTIFY_STOP_MASK)
goto out;
- /* only re-parent if the clock is not in use */
- if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
- ret = -EBUSY;
- else
- ret = __clk_set_parent(clk, parent);
+ /* do the re-parent */
+ ret = __clk_set_parent(clk, parent, p_index);
- /* propagate ABORT_RATE_CHANGE if .set_parent failed */
- if (ret) {
+ /* propagate rate recalculation accordingly */
+ if (ret)
__clk_recalc_rates(clk, ABORT_RATE_CHANGE);
- goto out;
- }
-
- /* propagate rate recalculation downstream */
- __clk_reparent(clk, parent);
+ else
+ __clk_recalc_rates(clk, POST_RATE_CHANGE);
out:
clk_prepare_unlock();
diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c
index 69056a7479e8..0a53edbae8b8 100644
--- a/drivers/clk/mvebu/clk-core.c
+++ b/drivers/clk/mvebu/clk-core.c
@@ -156,8 +156,8 @@ static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
SARL_A370_PCLK_FREQ_OPT_MASK);
- if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) {
- pr_err("CPU freq select unsuported %d\n", cpu_freq_select);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_370_cpu_frequencies)) {
+ pr_err("CPU freq select unsupported %d\n", cpu_freq_select);
cpu_freq = 0;
} else
cpu_freq = armada_370_cpu_frequencies[cpu_freq_select];
@@ -278,8 +278,8 @@ static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
SARH_AXP_PCLK_FREQ_OPT_MASK)
<< SARH_AXP_PCLK_FREQ_OPT_SHIFT);
- if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) {
- pr_err("CPU freq select unsuported: %d\n", cpu_freq_select);
+ if (cpu_freq_select >= ARRAY_SIZE(armada_xp_cpu_frequencies)) {
+ pr_err("CPU freq select unsupported: %d\n", cpu_freq_select);
cpu_freq = 0;
} else
cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select];
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
index 9dd2551a0a41..b0fbc0715491 100644
--- a/drivers/clk/mvebu/clk-cpu.c
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -16,7 +16,6 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
-#include "clk-cpu.h"
#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
@@ -173,17 +172,5 @@ clks_out:
kfree(cpuclk);
}
-static const __initconst struct of_device_id clk_cpu_match[] = {
- {
- .compatible = "marvell,armada-xp-cpu-clock",
- .data = of_cpu_clk_setup,
- },
- {
- /* sentinel */
- },
-};
-
-void __init mvebu_cpu_clk_init(void)
-{
- of_clk_init(clk_cpu_match);
-}
+CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
+ of_cpu_clk_setup);
diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h
deleted file mode 100644
index 08e2affba4e6..000000000000
--- a/drivers/clk/mvebu/clk-cpu.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Marvell MVEBU CPU clock handling.
- *
- * Copyright (C) 2012 Marvell
- *
- * Gregory CLEMENT <gregory.clement@free-electrons.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MVEBU_CLK_CPU_H
-#define __MVEBU_CLK_CPU_H
-
-#ifdef CONFIG_MVEBU_CLK_CPU
-void __init mvebu_cpu_clk_init(void);
-#else
-static inline void mvebu_cpu_clk_init(void) {}
-#endif
-
-#endif
diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c
index 855681b8a9dc..29f10fb3006c 100644
--- a/drivers/clk/mvebu/clk.c
+++ b/drivers/clk/mvebu/clk.c
@@ -10,18 +10,14 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
-#include <linux/clk.h>
#include <linux/clk-provider.h>
-#include <linux/of_address.h>
-#include <linux/clk/mvebu.h>
#include <linux/of.h>
#include "clk-core.h"
-#include "clk-cpu.h"
#include "clk-gating-ctrl.h"
void __init mvebu_clocks_init(void)
{
mvebu_core_clk_init();
mvebu_gating_clk_init();
- mvebu_cpu_clk_init();
+ of_clk_init(NULL);
}
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
index b5c06f9766f6..f6a74872f14e 100644
--- a/drivers/clk/mxs/clk-imx23.c
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -15,12 +15,15 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <mach/common.h>
-#include <mach/mx23.h>
+#include <linux/of_address.h>
#include "clk.h"
-#define DIGCTRL MX23_IO_ADDRESS(MX23_DIGCTL_BASE_ADDR)
-#define CLKCTRL MX23_IO_ADDRESS(MX23_CLKCTRL_BASE_ADDR)
+static void __iomem *clkctrl;
+static void __iomem *digctrl;
+
+#define CLKCTRL clkctrl
+#define DIGCTRL digctrl
+
#define PLLCTRL0 (CLKCTRL + 0x0000)
#define CPU (CLKCTRL + 0x0020)
#define HBUS (CLKCTRL + 0x0030)
@@ -48,10 +51,10 @@ static void __init clk_misc_init(void)
u32 val;
/* Gate off cpu clock in WFI for power saving */
- __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU);
+ writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET);
/* Clear BYPASS for SAIF */
- __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ);
+ writel_relaxed(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ + CLR);
/* SAIF has to use frac div for functional operation */
val = readl_relaxed(SAIF);
@@ -62,14 +65,14 @@ static void __init clk_misc_init(void)
* Source ssp clock from ref_io than ref_xtal,
* as ref_xtal only provides 24 MHz as maximum.
*/
- __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ);
+ writel_relaxed(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ + CLR);
/*
* 480 MHz seems too high to be ssp clock source directly,
* so set frac to get a 288 MHz ref_io.
*/
- __mxs_clrl(0x3f << BP_FRAC_IOFRAC, FRAC);
- __mxs_setl(30 << BP_FRAC_IOFRAC, FRAC);
+ writel_relaxed(0x3f << BP_FRAC_IOFRAC, FRAC + CLR);
+ writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET);
}
static const char *sel_pll[] __initconst = { "pll", "ref_xtal", };
@@ -101,6 +104,14 @@ int __init mx23_clocks_init(void)
struct device_node *np;
u32 i;
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl");
+ digctrl = of_iomap(np, 0);
+ WARN_ON(!digctrl);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl");
+ clkctrl = of_iomap(np, 0);
+ WARN_ON(!clkctrl);
+
clk_misc_init();
clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000);
@@ -153,19 +164,12 @@ int __init mx23_clocks_init(void)
return PTR_ERR(clks[i]);
}
- np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl");
- if (np) {
- clk_data.clks = clks;
- clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
- }
-
- clk_register_clkdev(clks[clk32k], NULL, "timrot");
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
- mxs_timer_init();
-
return 0;
}
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index 76ce6c6d1113..d0e5eed146de 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -15,11 +15,12 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <mach/common.h>
-#include <mach/mx28.h>
+#include <linux/of_address.h>
#include "clk.h"
-#define CLKCTRL MX28_IO_ADDRESS(MX28_CLKCTRL_BASE_ADDR)
+static void __iomem *clkctrl;
+#define CLKCTRL clkctrl
+
#define PLL0CTRL0 (CLKCTRL + 0x0000)
#define PLL1CTRL0 (CLKCTRL + 0x0020)
#define PLL2CTRL0 (CLKCTRL + 0x0040)
@@ -53,7 +54,8 @@
#define BP_FRAC0_IO1FRAC 16
#define BP_FRAC0_IO0FRAC 24
-#define DIGCTRL MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR)
+static void __iomem *digctrl;
+#define DIGCTRL digctrl
#define BP_SAIF_CLKMUX 10
/*
@@ -72,8 +74,8 @@ int mxs_saif_clkmux_select(unsigned int clkmux)
if (clkmux > 0x3)
return -EINVAL;
- __mxs_clrl(0x3 << BP_SAIF_CLKMUX, DIGCTRL);
- __mxs_setl(clkmux << BP_SAIF_CLKMUX, DIGCTRL);
+ writel_relaxed(0x3 << BP_SAIF_CLKMUX, DIGCTRL + CLR);
+ writel_relaxed(clkmux << BP_SAIF_CLKMUX, DIGCTRL + SET);
return 0;
}
@@ -83,13 +85,13 @@ static void __init clk_misc_init(void)
u32 val;
/* Gate off cpu clock in WFI for power saving */
- __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU);
+ writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET);
/* 0 is a bad default value for a divider */
- __mxs_setl(1 << BP_ENET_DIV_TIME, ENET);
+ writel_relaxed(1 << BP_ENET_DIV_TIME, ENET + SET);
/* Clear BYPASS for SAIF */
- __mxs_clrl(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ);
+ writel_relaxed(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ + CLR);
/* SAIF has to use frac div for functional operation */
val = readl_relaxed(SAIF0);
@@ -109,7 +111,7 @@ static void __init clk_misc_init(void)
* Source ssp clock from ref_io than ref_xtal,
* as ref_xtal only provides 24 MHz as maximum.
*/
- __mxs_clrl(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ);
+ writel_relaxed(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ + CLR);
/*
* 480 MHz seems too high to be ssp clock source directly,
@@ -156,6 +158,14 @@ int __init mx28_clocks_init(void)
struct device_node *np;
u32 i;
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl");
+ digctrl = of_iomap(np, 0);
+ WARN_ON(!digctrl);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl");
+ clkctrl = of_iomap(np, 0);
+ WARN_ON(!clkctrl);
+
clk_misc_init();
clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000);
@@ -231,20 +241,14 @@ int __init mx28_clocks_init(void)
return PTR_ERR(clks[i]);
}
- np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl");
- if (np) {
- clk_data.clks = clks;
- clk_data.clk_num = ARRAY_SIZE(clks);
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
- }
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
- clk_register_clkdev(clks[xbus], NULL, "timrot");
clk_register_clkdev(clks[enet_out], NULL, "enet_out");
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
- mxs_timer_init();
-
return 0;
}
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
index 2c855a6394ff..bd11315cf5ab 100644
--- a/drivers/clk/socfpga/clk.c
+++ b/drivers/clk/socfpga/clk.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2012 Altera Corporation <www.altera.com>
+ * Copyright 2011-2012 Calxeda, Inc.
+ * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -11,41 +12,161 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
+ * Based from clk-highbank.c
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
-#define SOCFPGA_OSC1_CLK 10000000
-#define SOCFPGA_MPU_CLK 800000000
-#define SOCFPGA_MAIN_QSPI_CLK 432000000
-#define SOCFPGA_MAIN_NAND_SDMMC_CLK 250000000
-#define SOCFPGA_S2F_USR_CLK 125000000
+/* Clock Manager offsets */
+#define CLKMGR_CTRL 0x0
+#define CLKMGR_BYPASS 0x4
-void __init socfpga_init_clocks(void)
+/* Clock bypass bits */
+#define MAINPLL_BYPASS (1<<0)
+#define SDRAMPLL_BYPASS (1<<1)
+#define SDRAMPLL_SRC_BYPASS (1<<2)
+#define PERPLL_BYPASS (1<<3)
+#define PERPLL_SRC_BYPASS (1<<4)
+
+#define SOCFPGA_PLL_BG_PWRDWN 0
+#define SOCFPGA_PLL_EXT_ENA 1
+#define SOCFPGA_PLL_PWR_DOWN 2
+#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
+#define SOCFPGA_PLL_DIVF_SHIFT 3
+#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
+#define SOCFPGA_PLL_DIVQ_SHIFT 16
+
+extern void __iomem *clk_mgr_base_addr;
+
+struct socfpga_clk {
+ struct clk_gate hw;
+ char *parent_name;
+ char *clk_name;
+ u32 fixed_div;
+};
+#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
{
+ struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
+ unsigned long divf, divq, vco_freq, reg;
+ unsigned long bypass;
+
+ reg = readl(socfpgaclk->hw.reg);
+ bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
+ if (bypass & MAINPLL_BYPASS)
+ return parent_rate;
+
+ divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
+ divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
+ vco_freq = parent_rate * (divf + 1);
+ return vco_freq / (1 + divq);
+}
+
+
+static struct clk_ops clk_pll_ops = {
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
+ u32 div;
+
+ if (socfpgaclk->fixed_div)
+ div = socfpgaclk->fixed_div;
+ else
+ div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
+
+ return parent_rate / div;
+}
+
+static const struct clk_ops periclk_ops = {
+ .recalc_rate = clk_periclk_recalc_rate,
+};
+
+static __init struct clk *socfpga_clk_init(struct device_node *node,
+ const struct clk_ops *ops)
+{
+ u32 reg;
struct clk *clk;
+ struct socfpga_clk *socfpga_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ u32 fixed_div;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return NULL;
+
+ socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
+ if (WARN_ON(!socfpga_clk))
+ return NULL;
+
+ socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
+
+ rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
+ if (rc)
+ socfpga_clk->fixed_div = 0;
+ else
+ socfpga_clk->fixed_div = fixed_div;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
- clk = clk_register_fixed_rate(NULL, "osc1_clk", NULL, CLK_IS_ROOT, SOCFPGA_OSC1_CLK);
- clk_register_clkdev(clk, "osc1_clk", NULL);
+ socfpga_clk->hw.hw.init = &init;
- clk = clk_register_fixed_rate(NULL, "mpu_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK);
- clk_register_clkdev(clk, "mpu_clk", NULL);
+ if (strcmp(clk_name, "main_pll") || strcmp(clk_name, "periph_pll") ||
+ strcmp(clk_name, "sdram_pll")) {
+ socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
+ clk_pll_ops.enable = clk_gate_ops.enable;
+ clk_pll_ops.disable = clk_gate_ops.disable;
+ }
- clk = clk_register_fixed_rate(NULL, "main_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK/2);
- clk_register_clkdev(clk, "main_clk", NULL);
+ clk = clk_register(NULL, &socfpga_clk->hw.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(socfpga_clk);
+ return NULL;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return clk;
+}
- clk = clk_register_fixed_rate(NULL, "dbg_base_clk", NULL, CLK_IS_ROOT, SOCFPGA_MPU_CLK/2);
- clk_register_clkdev(clk, "dbg_base_clk", NULL);
+static void __init socfpga_pll_init(struct device_node *node)
+{
+ socfpga_clk_init(node, &clk_pll_ops);
+}
+CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init);
- clk = clk_register_fixed_rate(NULL, "main_qspi_clk", NULL, CLK_IS_ROOT, SOCFPGA_MAIN_QSPI_CLK);
- clk_register_clkdev(clk, "main_qspi_clk", NULL);
+static void __init socfpga_periph_init(struct device_node *node)
+{
+ socfpga_clk_init(node, &periclk_ops);
+}
+CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init);
- clk = clk_register_fixed_rate(NULL, "main_nand_sdmmc_clk", NULL, CLK_IS_ROOT, SOCFPGA_MAIN_NAND_SDMMC_CLK);
- clk_register_clkdev(clk, "main_nand_sdmmc_clk", NULL);
+void __init socfpga_init_clocks(void)
+{
+ struct clk *clk;
+ int ret;
- clk = clk_register_fixed_rate(NULL, "s2f_usr_clk", NULL, CLK_IS_ROOT, SOCFPGA_S2F_USR_CLK);
- clk_register_clkdev(clk, "s2f_usr_clk", NULL);
+ clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
+ ret = clk_register_clkdev(clk, NULL, "smp_twd");
+ if (ret)
+ pr_err("smp_twd alias not registered\n");
}
diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c
index ed9af4278619..aedbbe12f321 100644
--- a/drivers/clk/spear/spear1310_clock.c
+++ b/drivers/clk/spear/spear1310_clock.c
@@ -17,12 +17,10 @@
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/spinlock_types.h>
-#include <mach/spear.h>
#include "clk.h"
-#define VA_SPEAR1310_RAS_BASE IOMEM(UL(0xFA400000))
/* PLL related registers and bit values */
-#define SPEAR1310_PLL_CFG (VA_MISC_BASE + 0x210)
+#define SPEAR1310_PLL_CFG (misc_base + 0x210)
/* PLL_CFG bit values */
#define SPEAR1310_CLCD_SYNT_CLK_MASK 1
#define SPEAR1310_CLCD_SYNT_CLK_SHIFT 31
@@ -35,15 +33,15 @@
#define SPEAR1310_PLL2_CLK_SHIFT 22
#define SPEAR1310_PLL1_CLK_SHIFT 20
-#define SPEAR1310_PLL1_CTR (VA_MISC_BASE + 0x214)
-#define SPEAR1310_PLL1_FRQ (VA_MISC_BASE + 0x218)
-#define SPEAR1310_PLL2_CTR (VA_MISC_BASE + 0x220)
-#define SPEAR1310_PLL2_FRQ (VA_MISC_BASE + 0x224)
-#define SPEAR1310_PLL3_CTR (VA_MISC_BASE + 0x22C)
-#define SPEAR1310_PLL3_FRQ (VA_MISC_BASE + 0x230)
-#define SPEAR1310_PLL4_CTR (VA_MISC_BASE + 0x238)
-#define SPEAR1310_PLL4_FRQ (VA_MISC_BASE + 0x23C)
-#define SPEAR1310_PERIP_CLK_CFG (VA_MISC_BASE + 0x244)
+#define SPEAR1310_PLL1_CTR (misc_base + 0x214)
+#define SPEAR1310_PLL1_FRQ (misc_base + 0x218)
+#define SPEAR1310_PLL2_CTR (misc_base + 0x220)
+#define SPEAR1310_PLL2_FRQ (misc_base + 0x224)
+#define SPEAR1310_PLL3_CTR (misc_base + 0x22C)
+#define SPEAR1310_PLL3_FRQ (misc_base + 0x230)
+#define SPEAR1310_PLL4_CTR (misc_base + 0x238)
+#define SPEAR1310_PLL4_FRQ (misc_base + 0x23C)
+#define SPEAR1310_PERIP_CLK_CFG (misc_base + 0x244)
/* PERIP_CLK_CFG bit values */
#define SPEAR1310_GPT_OSC24_VAL 0
#define SPEAR1310_GPT_APB_VAL 1
@@ -65,7 +63,7 @@
#define SPEAR1310_C3_CLK_MASK 1
#define SPEAR1310_C3_CLK_SHIFT 1
-#define SPEAR1310_GMAC_CLK_CFG (VA_MISC_BASE + 0x248)
+#define SPEAR1310_GMAC_CLK_CFG (misc_base + 0x248)
#define SPEAR1310_GMAC_PHY_IF_SEL_MASK 3
#define SPEAR1310_GMAC_PHY_IF_SEL_SHIFT 4
#define SPEAR1310_GMAC_PHY_CLK_MASK 1
@@ -73,7 +71,7 @@
#define SPEAR1310_GMAC_PHY_INPUT_CLK_MASK 2
#define SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT 1
-#define SPEAR1310_I2S_CLK_CFG (VA_MISC_BASE + 0x24C)
+#define SPEAR1310_I2S_CLK_CFG (misc_base + 0x24C)
/* I2S_CLK_CFG register mask */
#define SPEAR1310_I2S_SCLK_X_MASK 0x1F
#define SPEAR1310_I2S_SCLK_X_SHIFT 27
@@ -91,21 +89,21 @@
#define SPEAR1310_I2S_SRC_CLK_MASK 2
#define SPEAR1310_I2S_SRC_CLK_SHIFT 0
-#define SPEAR1310_C3_CLK_SYNT (VA_MISC_BASE + 0x250)
-#define SPEAR1310_UART_CLK_SYNT (VA_MISC_BASE + 0x254)
-#define SPEAR1310_GMAC_CLK_SYNT (VA_MISC_BASE + 0x258)
-#define SPEAR1310_SDHCI_CLK_SYNT (VA_MISC_BASE + 0x25C)
-#define SPEAR1310_CFXD_CLK_SYNT (VA_MISC_BASE + 0x260)
-#define SPEAR1310_ADC_CLK_SYNT (VA_MISC_BASE + 0x264)
-#define SPEAR1310_AMBA_CLK_SYNT (VA_MISC_BASE + 0x268)
-#define SPEAR1310_CLCD_CLK_SYNT (VA_MISC_BASE + 0x270)
-#define SPEAR1310_RAS_CLK_SYNT0 (VA_MISC_BASE + 0x280)
-#define SPEAR1310_RAS_CLK_SYNT1 (VA_MISC_BASE + 0x288)
-#define SPEAR1310_RAS_CLK_SYNT2 (VA_MISC_BASE + 0x290)
-#define SPEAR1310_RAS_CLK_SYNT3 (VA_MISC_BASE + 0x298)
+#define SPEAR1310_C3_CLK_SYNT (misc_base + 0x250)
+#define SPEAR1310_UART_CLK_SYNT (misc_base + 0x254)
+#define SPEAR1310_GMAC_CLK_SYNT (misc_base + 0x258)
+#define SPEAR1310_SDHCI_CLK_SYNT (misc_base + 0x25C)
+#define SPEAR1310_CFXD_CLK_SYNT (misc_base + 0x260)
+#define SPEAR1310_ADC_CLK_SYNT (misc_base + 0x264)
+#define SPEAR1310_AMBA_CLK_SYNT (misc_base + 0x268)
+#define SPEAR1310_CLCD_CLK_SYNT (misc_base + 0x270)
+#define SPEAR1310_RAS_CLK_SYNT0 (misc_base + 0x280)
+#define SPEAR1310_RAS_CLK_SYNT1 (misc_base + 0x288)
+#define SPEAR1310_RAS_CLK_SYNT2 (misc_base + 0x290)
+#define SPEAR1310_RAS_CLK_SYNT3 (misc_base + 0x298)
/* Check Fractional synthesizer reg masks */
-#define SPEAR1310_PERIP1_CLK_ENB (VA_MISC_BASE + 0x300)
+#define SPEAR1310_PERIP1_CLK_ENB (misc_base + 0x300)
/* PERIP1_CLK_ENB register masks */
#define SPEAR1310_RTC_CLK_ENB 31
#define SPEAR1310_ADC_CLK_ENB 30
@@ -138,7 +136,7 @@
#define SPEAR1310_SYSROM_CLK_ENB 1
#define SPEAR1310_BUS_CLK_ENB 0
-#define SPEAR1310_PERIP2_CLK_ENB (VA_MISC_BASE + 0x304)
+#define SPEAR1310_PERIP2_CLK_ENB (misc_base + 0x304)
/* PERIP2_CLK_ENB register masks */
#define SPEAR1310_THSENS_CLK_ENB 8
#define SPEAR1310_I2S_REF_PAD_CLK_ENB 7
@@ -150,7 +148,7 @@
#define SPEAR1310_DDR_CORE_CLK_ENB 1
#define SPEAR1310_DDR_CTRL_CLK_ENB 0
-#define SPEAR1310_RAS_CLK_ENB (VA_MISC_BASE + 0x310)
+#define SPEAR1310_RAS_CLK_ENB (misc_base + 0x310)
/* RAS_CLK_ENB register masks */
#define SPEAR1310_SYNT3_CLK_ENB 17
#define SPEAR1310_SYNT2_CLK_ENB 16
@@ -172,7 +170,7 @@
#define SPEAR1310_ACLK_CLK_ENB 0
/* RAS Area Control Register */
-#define SPEAR1310_RAS_CTRL_REG0 (VA_SPEAR1310_RAS_BASE + 0x000)
+#define SPEAR1310_RAS_CTRL_REG0 (ras_base + 0x000)
#define SPEAR1310_SSP1_CLK_MASK 3
#define SPEAR1310_SSP1_CLK_SHIFT 26
#define SPEAR1310_TDM_CLK_MASK 1
@@ -197,12 +195,12 @@
#define SPEAR1310_PCI_CLK_MASK 1
#define SPEAR1310_PCI_CLK_SHIFT 0
-#define SPEAR1310_RAS_CTRL_REG1 (VA_SPEAR1310_RAS_BASE + 0x004)
+#define SPEAR1310_RAS_CTRL_REG1 (ras_base + 0x004)
#define SPEAR1310_PHY_CLK_MASK 0x3
#define SPEAR1310_RMII_PHY_CLK_SHIFT 0
#define SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT 2
-#define SPEAR1310_RAS_SW_CLK_CTRL (VA_SPEAR1310_RAS_BASE + 0x0148)
+#define SPEAR1310_RAS_SW_CLK_CTRL (ras_base + 0x0148)
#define SPEAR1310_CAN1_CLK_ENB 25
#define SPEAR1310_CAN0_CLK_ENB 24
#define SPEAR1310_GPT64_CLK_ENB 23
@@ -385,7 +383,7 @@ static const char *ssp1_parents[] = { "ras_apb_clk", "gen_syn1_clk",
static const char *pci_parents[] = { "ras_pll3_clk", "gen_syn2_clk", };
static const char *tdm_parents[] = { "ras_pll3_clk", "gen_syn1_clk", };
-void __init spear1310_clk_init(void)
+void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
{
struct clk *clk, *clk1;
diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c
index 35e7e2698e10..9d0b3949db30 100644
--- a/drivers/clk/spear/spear1340_clock.c
+++ b/drivers/clk/spear/spear1340_clock.c
@@ -17,18 +17,17 @@
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/spinlock_types.h>
-#include <mach/spear.h>
#include "clk.h"
/* Clock Configuration Registers */
-#define SPEAR1340_SYS_CLK_CTRL (VA_MISC_BASE + 0x200)
+#define SPEAR1340_SYS_CLK_CTRL (misc_base + 0x200)
#define SPEAR1340_HCLK_SRC_SEL_SHIFT 27
#define SPEAR1340_HCLK_SRC_SEL_MASK 1
#define SPEAR1340_SCLK_SRC_SEL_SHIFT 23
#define SPEAR1340_SCLK_SRC_SEL_MASK 3
/* PLL related registers and bit values */
-#define SPEAR1340_PLL_CFG (VA_MISC_BASE + 0x210)
+#define SPEAR1340_PLL_CFG (misc_base + 0x210)
/* PLL_CFG bit values */
#define SPEAR1340_CLCD_SYNT_CLK_MASK 1
#define SPEAR1340_CLCD_SYNT_CLK_SHIFT 31
@@ -40,15 +39,15 @@
#define SPEAR1340_PLL2_CLK_SHIFT 22
#define SPEAR1340_PLL1_CLK_SHIFT 20
-#define SPEAR1340_PLL1_CTR (VA_MISC_BASE + 0x214)
-#define SPEAR1340_PLL1_FRQ (VA_MISC_BASE + 0x218)
-#define SPEAR1340_PLL2_CTR (VA_MISC_BASE + 0x220)
-#define SPEAR1340_PLL2_FRQ (VA_MISC_BASE + 0x224)
-#define SPEAR1340_PLL3_CTR (VA_MISC_BASE + 0x22C)
-#define SPEAR1340_PLL3_FRQ (VA_MISC_BASE + 0x230)
-#define SPEAR1340_PLL4_CTR (VA_MISC_BASE + 0x238)
-#define SPEAR1340_PLL4_FRQ (VA_MISC_BASE + 0x23C)
-#define SPEAR1340_PERIP_CLK_CFG (VA_MISC_BASE + 0x244)
+#define SPEAR1340_PLL1_CTR (misc_base + 0x214)
+#define SPEAR1340_PLL1_FRQ (misc_base + 0x218)
+#define SPEAR1340_PLL2_CTR (misc_base + 0x220)
+#define SPEAR1340_PLL2_FRQ (misc_base + 0x224)
+#define SPEAR1340_PLL3_CTR (misc_base + 0x22C)
+#define SPEAR1340_PLL3_FRQ (misc_base + 0x230)
+#define SPEAR1340_PLL4_CTR (misc_base + 0x238)
+#define SPEAR1340_PLL4_FRQ (misc_base + 0x23C)
+#define SPEAR1340_PERIP_CLK_CFG (misc_base + 0x244)
/* PERIP_CLK_CFG bit values */
#define SPEAR1340_SPDIF_CLK_MASK 1
#define SPEAR1340_SPDIF_OUT_CLK_SHIFT 15
@@ -66,13 +65,13 @@
#define SPEAR1340_C3_CLK_MASK 1
#define SPEAR1340_C3_CLK_SHIFT 1
-#define SPEAR1340_GMAC_CLK_CFG (VA_MISC_BASE + 0x248)
+#define SPEAR1340_GMAC_CLK_CFG (misc_base + 0x248)
#define SPEAR1340_GMAC_PHY_CLK_MASK 1
#define SPEAR1340_GMAC_PHY_CLK_SHIFT 2
#define SPEAR1340_GMAC_PHY_INPUT_CLK_MASK 2
#define SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT 0
-#define SPEAR1340_I2S_CLK_CFG (VA_MISC_BASE + 0x24C)
+#define SPEAR1340_I2S_CLK_CFG (misc_base + 0x24C)
/* I2S_CLK_CFG register mask */
#define SPEAR1340_I2S_SCLK_X_MASK 0x1F
#define SPEAR1340_I2S_SCLK_X_SHIFT 27
@@ -90,21 +89,21 @@
#define SPEAR1340_I2S_SRC_CLK_MASK 2
#define SPEAR1340_I2S_SRC_CLK_SHIFT 0
-#define SPEAR1340_C3_CLK_SYNT (VA_MISC_BASE + 0x250)
-#define SPEAR1340_UART0_CLK_SYNT (VA_MISC_BASE + 0x254)
-#define SPEAR1340_UART1_CLK_SYNT (VA_MISC_BASE + 0x258)
-#define SPEAR1340_GMAC_CLK_SYNT (VA_MISC_BASE + 0x25C)
-#define SPEAR1340_SDHCI_CLK_SYNT (VA_MISC_BASE + 0x260)
-#define SPEAR1340_CFXD_CLK_SYNT (VA_MISC_BASE + 0x264)
-#define SPEAR1340_ADC_CLK_SYNT (VA_MISC_BASE + 0x270)
-#define SPEAR1340_AMBA_CLK_SYNT (VA_MISC_BASE + 0x274)
-#define SPEAR1340_CLCD_CLK_SYNT (VA_MISC_BASE + 0x27C)
-#define SPEAR1340_SYS_CLK_SYNT (VA_MISC_BASE + 0x284)
-#define SPEAR1340_GEN_CLK_SYNT0 (VA_MISC_BASE + 0x28C)
-#define SPEAR1340_GEN_CLK_SYNT1 (VA_MISC_BASE + 0x294)
-#define SPEAR1340_GEN_CLK_SYNT2 (VA_MISC_BASE + 0x29C)
-#define SPEAR1340_GEN_CLK_SYNT3 (VA_MISC_BASE + 0x304)
-#define SPEAR1340_PERIP1_CLK_ENB (VA_MISC_BASE + 0x30C)
+#define SPEAR1340_C3_CLK_SYNT (misc_base + 0x250)
+#define SPEAR1340_UART0_CLK_SYNT (misc_base + 0x254)
+#define SPEAR1340_UART1_CLK_SYNT (misc_base + 0x258)
+#define SPEAR1340_GMAC_CLK_SYNT (misc_base + 0x25C)
+#define SPEAR1340_SDHCI_CLK_SYNT (misc_base + 0x260)
+#define SPEAR1340_CFXD_CLK_SYNT (misc_base + 0x264)
+#define SPEAR1340_ADC_CLK_SYNT (misc_base + 0x270)
+#define SPEAR1340_AMBA_CLK_SYNT (misc_base + 0x274)
+#define SPEAR1340_CLCD_CLK_SYNT (misc_base + 0x27C)
+#define SPEAR1340_SYS_CLK_SYNT (misc_base + 0x284)
+#define SPEAR1340_GEN_CLK_SYNT0 (misc_base + 0x28C)
+#define SPEAR1340_GEN_CLK_SYNT1 (misc_base + 0x294)
+#define SPEAR1340_GEN_CLK_SYNT2 (misc_base + 0x29C)
+#define SPEAR1340_GEN_CLK_SYNT3 (misc_base + 0x304)
+#define SPEAR1340_PERIP1_CLK_ENB (misc_base + 0x30C)
#define SPEAR1340_RTC_CLK_ENB 31
#define SPEAR1340_ADC_CLK_ENB 30
#define SPEAR1340_C3_CLK_ENB 29
@@ -133,7 +132,7 @@
#define SPEAR1340_SYSROM_CLK_ENB 1
#define SPEAR1340_BUS_CLK_ENB 0
-#define SPEAR1340_PERIP2_CLK_ENB (VA_MISC_BASE + 0x310)
+#define SPEAR1340_PERIP2_CLK_ENB (misc_base + 0x310)
#define SPEAR1340_THSENS_CLK_ENB 8
#define SPEAR1340_I2S_REF_PAD_CLK_ENB 7
#define SPEAR1340_ACP_CLK_ENB 6
@@ -144,7 +143,7 @@
#define SPEAR1340_DDR_CORE_CLK_ENB 1
#define SPEAR1340_DDR_CTRL_CLK_ENB 0
-#define SPEAR1340_PERIP3_CLK_ENB (VA_MISC_BASE + 0x314)
+#define SPEAR1340_PERIP3_CLK_ENB (misc_base + 0x314)
#define SPEAR1340_PLGPIO_CLK_ENB 18
#define SPEAR1340_VIDEO_DEC_CLK_ENB 16
#define SPEAR1340_VIDEO_ENC_CLK_ENB 15
@@ -441,7 +440,7 @@ static const char *gen_synth0_1_parents[] = { "vco1div4_clk", "vco3div2_clk",
static const char *gen_synth2_3_parents[] = { "vco1div4_clk", "vco2div2_clk",
"pll2_clk", };
-void __init spear1340_clk_init(void)
+void __init spear1340_clk_init(void __iomem *misc_base)
{
struct clk *clk, *clk1;
diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c
index 33d3ac588da7..f9ec43fd1320 100644
--- a/drivers/clk/spear/spear3xx_clock.c
+++ b/drivers/clk/spear/spear3xx_clock.c
@@ -15,21 +15,20 @@
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/spinlock_types.h>
-#include <mach/misc_regs.h>
#include "clk.h"
static DEFINE_SPINLOCK(_lock);
-#define PLL1_CTR (MISC_BASE + 0x008)
-#define PLL1_FRQ (MISC_BASE + 0x00C)
-#define PLL2_CTR (MISC_BASE + 0x014)
-#define PLL2_FRQ (MISC_BASE + 0x018)
-#define PLL_CLK_CFG (MISC_BASE + 0x020)
+#define PLL1_CTR (misc_base + 0x008)
+#define PLL1_FRQ (misc_base + 0x00C)
+#define PLL2_CTR (misc_base + 0x014)
+#define PLL2_FRQ (misc_base + 0x018)
+#define PLL_CLK_CFG (misc_base + 0x020)
/* PLL_CLK_CFG register masks */
#define MCTR_CLK_SHIFT 28
#define MCTR_CLK_MASK 3
-#define CORE_CLK_CFG (MISC_BASE + 0x024)
+#define CORE_CLK_CFG (misc_base + 0x024)
/* CORE CLK CFG register masks */
#define GEN_SYNTH2_3_CLK_SHIFT 18
#define GEN_SYNTH2_3_CLK_MASK 1
@@ -39,7 +38,7 @@ static DEFINE_SPINLOCK(_lock);
#define PCLK_RATIO_SHIFT 8
#define PCLK_RATIO_MASK 2
-#define PERIP_CLK_CFG (MISC_BASE + 0x028)
+#define PERIP_CLK_CFG (misc_base + 0x028)
/* PERIP_CLK_CFG register masks */
#define UART_CLK_SHIFT 4
#define UART_CLK_MASK 1
@@ -50,7 +49,7 @@ static DEFINE_SPINLOCK(_lock);
#define GPT2_CLK_SHIFT 12
#define GPT_CLK_MASK 1
-#define PERIP1_CLK_ENB (MISC_BASE + 0x02C)
+#define PERIP1_CLK_ENB (misc_base + 0x02C)
/* PERIP1_CLK_ENB register masks */
#define UART_CLK_ENB 3
#define SSP_CLK_ENB 5
@@ -69,7 +68,7 @@ static DEFINE_SPINLOCK(_lock);
#define USBH_CLK_ENB 25
#define C3_CLK_ENB 31
-#define RAS_CLK_ENB (MISC_BASE + 0x034)
+#define RAS_CLK_ENB (misc_base + 0x034)
#define RAS_AHB_CLK_ENB 0
#define RAS_PLL1_CLK_ENB 1
#define RAS_APB_CLK_ENB 2
@@ -82,20 +81,20 @@ static DEFINE_SPINLOCK(_lock);
#define RAS_SYNT2_CLK_ENB 10
#define RAS_SYNT3_CLK_ENB 11
-#define PRSC0_CLK_CFG (MISC_BASE + 0x044)
-#define PRSC1_CLK_CFG (MISC_BASE + 0x048)
-#define PRSC2_CLK_CFG (MISC_BASE + 0x04C)
-#define AMEM_CLK_CFG (MISC_BASE + 0x050)
+#define PRSC0_CLK_CFG (misc_base + 0x044)
+#define PRSC1_CLK_CFG (misc_base + 0x048)
+#define PRSC2_CLK_CFG (misc_base + 0x04C)
+#define AMEM_CLK_CFG (misc_base + 0x050)
#define AMEM_CLK_ENB 0
-#define CLCD_CLK_SYNT (MISC_BASE + 0x05C)
-#define FIRDA_CLK_SYNT (MISC_BASE + 0x060)
-#define UART_CLK_SYNT (MISC_BASE + 0x064)
-#define GMAC_CLK_SYNT (MISC_BASE + 0x068)
-#define GEN0_CLK_SYNT (MISC_BASE + 0x06C)
-#define GEN1_CLK_SYNT (MISC_BASE + 0x070)
-#define GEN2_CLK_SYNT (MISC_BASE + 0x074)
-#define GEN3_CLK_SYNT (MISC_BASE + 0x078)
+#define CLCD_CLK_SYNT (misc_base + 0x05C)
+#define FIRDA_CLK_SYNT (misc_base + 0x060)
+#define UART_CLK_SYNT (misc_base + 0x064)
+#define GMAC_CLK_SYNT (misc_base + 0x068)
+#define GEN0_CLK_SYNT (misc_base + 0x06C)
+#define GEN1_CLK_SYNT (misc_base + 0x070)
+#define GEN2_CLK_SYNT (misc_base + 0x074)
+#define GEN3_CLK_SYNT (misc_base + 0x078)
/* pll rate configuration table, in ascending order of rates */
static struct pll_rate_tbl pll_rtbl[] = {
@@ -211,6 +210,17 @@ static inline void spear310_clk_init(void) { }
/* array of all spear 320 clock lookups */
#ifdef CONFIG_MACH_SPEAR320
+
+#define SPEAR320_CONTROL_REG (soc_config_base + 0x0000)
+#define SPEAR320_EXT_CTRL_REG (soc_config_base + 0x0018)
+
+ #define SPEAR320_UARTX_PCLK_MASK 0x1
+ #define SPEAR320_UART2_PCLK_SHIFT 8
+ #define SPEAR320_UART3_PCLK_SHIFT 9
+ #define SPEAR320_UART4_PCLK_SHIFT 10
+ #define SPEAR320_UART5_PCLK_SHIFT 11
+ #define SPEAR320_UART6_PCLK_SHIFT 12
+ #define SPEAR320_RS485_PCLK_SHIFT 13
#define SMII_PCLK_SHIFT 18
#define SMII_PCLK_MASK 2
#define SMII_PCLK_VAL_PAD 0x0
@@ -235,7 +245,7 @@ static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk",
"ras_syn0_gclk", };
static const char *uartx_parents[] = { "ras_syn1_gclk", "ras_apb_clk", };
-static void __init spear320_clk_init(void)
+static void __init spear320_clk_init(void __iomem *soc_config_base)
{
struct clk *clk;
@@ -362,7 +372,7 @@ static void __init spear320_clk_init(void)
static inline void spear320_clk_init(void) { }
#endif
-void __init spear3xx_clk_init(void)
+void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_base)
{
struct clk *clk, *clk1;
@@ -634,5 +644,5 @@ void __init spear3xx_clk_init(void)
else if (of_machine_is_compatible("st,spear310"))
spear310_clk_init();
else if (of_machine_is_compatible("st,spear320"))
- spear320_clk_init();
+ spear320_clk_init(soc_config_base);
}
diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c
index e862a333ad30..9406f2426d64 100644
--- a/drivers/clk/spear/spear6xx_clock.c
+++ b/drivers/clk/spear/spear6xx_clock.c
@@ -13,28 +13,27 @@
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/spinlock_types.h>
-#include <mach/misc_regs.h>
#include "clk.h"
static DEFINE_SPINLOCK(_lock);
-#define PLL1_CTR (MISC_BASE + 0x008)
-#define PLL1_FRQ (MISC_BASE + 0x00C)
-#define PLL2_CTR (MISC_BASE + 0x014)
-#define PLL2_FRQ (MISC_BASE + 0x018)
-#define PLL_CLK_CFG (MISC_BASE + 0x020)
+#define PLL1_CTR (misc_base + 0x008)
+#define PLL1_FRQ (misc_base + 0x00C)
+#define PLL2_CTR (misc_base + 0x014)
+#define PLL2_FRQ (misc_base + 0x018)
+#define PLL_CLK_CFG (misc_base + 0x020)
/* PLL_CLK_CFG register masks */
#define MCTR_CLK_SHIFT 28
#define MCTR_CLK_MASK 3
-#define CORE_CLK_CFG (MISC_BASE + 0x024)
+#define CORE_CLK_CFG (misc_base + 0x024)
/* CORE CLK CFG register masks */
#define HCLK_RATIO_SHIFT 10
#define HCLK_RATIO_MASK 2
#define PCLK_RATIO_SHIFT 8
#define PCLK_RATIO_MASK 2
-#define PERIP_CLK_CFG (MISC_BASE + 0x028)
+#define PERIP_CLK_CFG (misc_base + 0x028)
/* PERIP_CLK_CFG register masks */
#define CLCD_CLK_SHIFT 2
#define CLCD_CLK_MASK 2
@@ -48,7 +47,7 @@ static DEFINE_SPINLOCK(_lock);
#define GPT3_CLK_SHIFT 12
#define GPT_CLK_MASK 1
-#define PERIP1_CLK_ENB (MISC_BASE + 0x02C)
+#define PERIP1_CLK_ENB (misc_base + 0x02C)
/* PERIP1_CLK_ENB register masks */
#define UART0_CLK_ENB 3
#define UART1_CLK_ENB 4
@@ -74,13 +73,13 @@ static DEFINE_SPINLOCK(_lock);
#define USBH0_CLK_ENB 25
#define USBH1_CLK_ENB 26
-#define PRSC0_CLK_CFG (MISC_BASE + 0x044)
-#define PRSC1_CLK_CFG (MISC_BASE + 0x048)
-#define PRSC2_CLK_CFG (MISC_BASE + 0x04C)
+#define PRSC0_CLK_CFG (misc_base + 0x044)
+#define PRSC1_CLK_CFG (misc_base + 0x048)
+#define PRSC2_CLK_CFG (misc_base + 0x04C)
-#define CLCD_CLK_SYNT (MISC_BASE + 0x05C)
-#define FIRDA_CLK_SYNT (MISC_BASE + 0x060)
-#define UART_CLK_SYNT (MISC_BASE + 0x064)
+#define CLCD_CLK_SYNT (misc_base + 0x05C)
+#define FIRDA_CLK_SYNT (misc_base + 0x060)
+#define UART_CLK_SYNT (misc_base + 0x064)
/* vco rate configuration table, in ascending order of rates */
static struct pll_rate_tbl pll_rtbl[] = {
@@ -115,7 +114,7 @@ static struct gpt_rate_tbl gpt_rtbl[] = {
{.mscale = 1, .nscale = 0}, /* 83 MHz */
};
-void __init spear6xx_clk_init(void)
+void __init spear6xx_clk_init(void __iomem *misc_base)
{
struct clk *clk, *clk1;
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index d528a2496690..8492ad1d5360 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -33,16 +33,36 @@ static DEFINE_SPINLOCK(clk_lock);
static void __init sunxi_osc_clk_setup(struct device_node *node)
{
struct clk *clk;
+ struct clk_fixed_rate *fixed;
+ struct clk_gate *gate;
const char *clk_name = node->name;
- const char *parent;
- void *reg;
+ u32 rate;
- reg = of_iomap(node, 0);
+ /* allocate fixed-rate and gate clock structs */
+ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+ if (!fixed)
+ return;
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ if (!gate) {
+ kfree(fixed);
+ return;
+ }
- parent = of_clk_get_parent_name(node, 0);
+ if (of_property_read_u32(node, "clock-frequency", &rate))
+ return;
+
+ /* set up gate and fixed rate properties */
+ gate->reg = of_iomap(node, 0);
+ gate->bit_idx = SUNXI_OSC24M_GATE;
+ gate->lock = &clk_lock;
+ fixed->fixed_rate = rate;
- clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
- reg, SUNXI_OSC24M_GATE, 0, &clk_lock);
+ clk = clk_register_composite(NULL, clk_name,
+ NULL, 0,
+ NULL, NULL,
+ &fixed->hw, &clk_fixed_rate_ops,
+ &gate->hw, &clk_gate_ops,
+ CLK_IS_ROOT);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
@@ -198,8 +218,8 @@ static void __init sunxi_factors_clk_setup(struct device_node *node,
parent = of_clk_get_parent_name(node, 0);
- clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
- reg, data->table, data->getter, &clk_lock);
+ clk = clk_register_factors(NULL, clk_name, parent, 0, reg,
+ data->table, data->getter, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
@@ -232,7 +252,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
{
struct clk *clk;
const char *clk_name = node->name;
- const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
+ const char *parents[5];
void *reg;
int i = 0;
@@ -302,9 +322,84 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
}
+
+/**
+ * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
+ */
+
+#define SUNXI_GATES_MAX_SIZE 64
+
+struct gates_data {
+ DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+};
+
+static const __initconst struct gates_data axi_gates_data = {
+ .mask = {1},
+};
+
+static const __initconst struct gates_data ahb_gates_data = {
+ .mask = {0x7F77FFF, 0x14FB3F},
+};
+
+static const __initconst struct gates_data apb0_gates_data = {
+ .mask = {0x4EF},
+};
+
+static const __initconst struct gates_data apb1_gates_data = {
+ .mask = {0xFF00F7},
+};
+
+static void __init sunxi_gates_clk_setup(struct device_node *node,
+ struct gates_data *data)
+{
+ struct clk_onecell_data *clk_data;
+ const char *clk_parent;
+ const char *clk_name;
+ void *reg;
+ int qty;
+ int i = 0;
+ int j = 0;
+ int ignore;
+
+ reg = of_iomap(node, 0);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ /* Worst-case size approximation and memory allocation */
+ qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE);
+ clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+ clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) {
+ of_property_read_string_index(node, "clock-output-names",
+ j, &clk_name);
+
+ /* No driver claims this clock, but it should remain gated */
+ ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0;
+
+ clk_data->clks[i] = clk_register_gate(NULL, clk_name,
+ clk_parent, ignore,
+ reg + 4 * (i/32), i % 32,
+ 0, &clk_lock);
+ WARN_ON(IS_ERR(clk_data->clks[i]));
+
+ j++;
+ }
+
+ /* Adjust to the real max */
+ clk_data->clk_num = i;
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
/* Matches for of_clk_init */
static const __initconst struct of_device_id clk_match[] = {
- {.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
{.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
{}
};
@@ -331,6 +426,15 @@ static const __initconst struct of_device_id clk_mux_match[] = {
{}
};
+/* Matches for gate clocks */
+static const __initconst struct of_device_id clk_gates_match[] = {
+ {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,},
+ {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,},
+ {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,},
+ {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,},
+ {}
+};
+
static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
void *function)
{
@@ -359,4 +463,7 @@ void __init sunxi_init_clocks(void)
/* Register mux clocks */
of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
+
+ /* Register gate clocks */
+ of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
}
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index b0405b67f49c..8292a00c3de9 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -710,7 +710,7 @@ static void tegra20_pll_init(void)
clks[pll_a_out0] = clk;
/* PLLE */
- clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, NULL,
+ clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, pmc_base,
0, 100000000, &pll_e_params,
0, pll_e_freq_table, NULL);
clk_register_clkdev(clk, "pll_e", NULL);
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index 2dc0c5602613..c6921f538e28 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -22,8 +22,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/tegra.h>
-
-#include <mach/powergate.h>
+#include <linux/tegra-powergate.h>
#include "clk.h"
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile
index bcc0c11a507c..c6a806ed0e8c 100644
--- a/drivers/clk/ux500/Makefile
+++ b/drivers/clk/ux500/Makefile
@@ -5,6 +5,7 @@
# Clock types
obj-y += clk-prcc.o
obj-y += clk-prcmu.o
+obj-y += clk-sysctrl.o
# Clock definitions
obj-y += u8500_clk.o
diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c
index 9f7400d74fa7..a0fca004abc1 100644
--- a/drivers/clk/ux500/abx500-clk.c
+++ b/drivers/clk/ux500/abx500-clk.c
@@ -12,13 +12,78 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mfd/abx500/ab8500.h>
-
-/* TODO: Add clock implementations here */
-
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include "clk.h"
/* Clock definitions for ab8500 */
static int ab8500_reg_clks(struct device *dev)
{
+ int ret;
+ struct clk *clk;
+
+ const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"};
+ u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1};
+ u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK};
+ u8 intclk_reg_bits[] = {
+ 0 ,
+ (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT)
+ };
+
+ dev_info(dev, "register clocks for ab850x\n");
+
+ /* Enable SWAT */
+ ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
+ if (ret)
+ return ret;
+
+ /* ab8500_sysclk */
+ clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, "sysclk", "ab8500-usb.0");
+ clk_register_clkdev(clk, "sysclk", "ab-iddet.0");
+ clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0");
+ clk_register_clkdev(clk, "sysclk", "shrm_bus");
+
+ /* ab8500_sysclk2 */
+ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk",
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
+ AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0);
+ clk_register_clkdev(clk, "sysclk", "0-0070");
+
+ /* ab8500_sysclk3 */
+ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk",
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
+ AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0);
+ clk_register_clkdev(clk, "sysclk", "cg1960_core.0");
+
+ /* ab8500_sysclk4 */
+ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk",
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
+ AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0);
+
+ /* ab_ulpclk */
+ clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL,
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ,
+ 38400000, 9000, CLK_IS_ROOT);
+ clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0");
+
+ /* ab8500_intclk */
+ clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2,
+ intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0);
+ clk_register_clkdev(clk, "intclk", "ab85xx-codec.0");
+ clk_register_clkdev(clk, NULL, "ab8500-pwm.1");
+
+ /* ab8500_audioclk */
+ clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk",
+ AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA,
+ AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0);
+ clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0");
+
return 0;
}
diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c
index 7eee7f768355..bd4769a84485 100644
--- a/drivers/clk/ux500/clk-prcc.c
+++ b/drivers/clk/ux500/clk-prcc.c
@@ -13,7 +13,6 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/types.h>
-#include <mach/hardware.h>
#include "clk.h"
diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c
new file mode 100644
index 000000000000..bc7e9bde792b
--- /dev/null
+++ b/drivers/clk/ux500/clk-sysctrl.c
@@ -0,0 +1,221 @@
+/*
+ * Sysctrl clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define SYSCTRL_MAX_NUM_PARENTS 4
+
+#define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw)
+
+struct clk_sysctrl {
+ struct clk_hw hw;
+ struct device *dev;
+ u8 parent_index;
+ u16 reg_sel[SYSCTRL_MAX_NUM_PARENTS];
+ u8 reg_mask[SYSCTRL_MAX_NUM_PARENTS];
+ u8 reg_bits[SYSCTRL_MAX_NUM_PARENTS];
+ unsigned long rate;
+ unsigned long enable_delay_us;
+};
+
+/* Sysctrl clock operations. */
+
+static int clk_sysctrl_prepare(struct clk_hw *hw)
+{
+ int ret;
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+
+ ret = ab8500_sysctrl_write(clk->reg_sel[0], clk->reg_mask[0],
+ clk->reg_bits[0]);
+
+ if (!ret && clk->enable_delay_us)
+ usleep_range(clk->enable_delay_us, clk->enable_delay_us);
+
+ return ret;
+}
+
+static void clk_sysctrl_unprepare(struct clk_hw *hw)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ if (ab8500_sysctrl_clear(clk->reg_sel[0], clk->reg_mask[0]))
+ dev_err(clk->dev, "clk_sysctrl: %s fail to clear %s.\n",
+ __func__, __clk_get_name(hw->clk));
+}
+
+static unsigned long clk_sysctrl_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ return clk->rate;
+}
+
+static int clk_sysctrl_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ u8 old_index = clk->parent_index;
+ int ret = 0;
+
+ if (clk->reg_sel[old_index]) {
+ ret = ab8500_sysctrl_clear(clk->reg_sel[old_index],
+ clk->reg_mask[old_index]);
+ if (ret)
+ return ret;
+ }
+
+ if (clk->reg_sel[index]) {
+ ret = ab8500_sysctrl_write(clk->reg_sel[index],
+ clk->reg_mask[index],
+ clk->reg_bits[index]);
+ if (ret) {
+ if (clk->reg_sel[old_index])
+ ab8500_sysctrl_write(clk->reg_sel[old_index],
+ clk->reg_mask[old_index],
+ clk->reg_bits[old_index]);
+ return ret;
+ }
+ }
+ clk->parent_index = index;
+
+ return ret;
+}
+
+static u8 clk_sysctrl_get_parent(struct clk_hw *hw)
+{
+ struct clk_sysctrl *clk = to_clk_sysctrl(hw);
+ return clk->parent_index;
+}
+
+static struct clk_ops clk_sysctrl_gate_ops = {
+ .prepare = clk_sysctrl_prepare,
+ .unprepare = clk_sysctrl_unprepare,
+};
+
+static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
+ .prepare = clk_sysctrl_prepare,
+ .unprepare = clk_sysctrl_unprepare,
+ .recalc_rate = clk_sysctrl_recalc_rate,
+};
+
+static struct clk_ops clk_sysctrl_set_parent_ops = {
+ .set_parent = clk_sysctrl_set_parent,
+ .get_parent = clk_sysctrl_get_parent,
+};
+
+static struct clk *clk_reg_sysctrl(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags,
+ struct clk_ops *clk_sysctrl_ops)
+{
+ struct clk_sysctrl *clk;
+ struct clk_init_data clk_sysctrl_init;
+ struct clk *clk_reg;
+ int i;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ if (!name || (num_parents > SYSCTRL_MAX_NUM_PARENTS)) {
+ dev_err(dev, "clk_sysctrl: invalid arguments passed\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = devm_kzalloc(dev, sizeof(struct clk_sysctrl), GFP_KERNEL);
+ if (!clk) {
+ dev_err(dev, "clk_sysctrl: could not allocate clk\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ for (i = 0; i < num_parents; i++) {
+ clk->reg_sel[i] = reg_sel[i];
+ clk->reg_bits[i] = reg_bits[i];
+ clk->reg_mask[i] = reg_mask[i];
+ }
+
+ clk->parent_index = 0;
+ clk->rate = rate;
+ clk->enable_delay_us = enable_delay_us;
+ clk->dev = dev;
+
+ clk_sysctrl_init.name = name;
+ clk_sysctrl_init.ops = clk_sysctrl_ops;
+ clk_sysctrl_init.flags = flags;
+ clk_sysctrl_init.parent_names = parent_names;
+ clk_sysctrl_init.num_parents = num_parents;
+ clk->hw.init = &clk_sysctrl_init;
+
+ clk_reg = devm_clk_register(clk->dev, &clk->hw);
+ if (IS_ERR(clk_reg))
+ dev_err(dev, "clk_sysctrl: clk_register failed\n");
+
+ return clk_reg;
+}
+
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long enable_delay_us,
+ unsigned long flags)
+{
+ const char **parent_names = (parent_name ? &parent_name : NULL);
+ u8 num_parents = (parent_name ? 1 : 0);
+
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ &reg_sel, &reg_mask, &reg_bits, 0, enable_delay_us,
+ flags, &clk_sysctrl_gate_ops);
+}
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags)
+{
+ const char **parent_names = (parent_name ? &parent_name : NULL);
+ u8 num_parents = (parent_name ? 1 : 0);
+
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ &reg_sel, &reg_mask, &reg_bits,
+ rate, enable_delay_us, flags,
+ &clk_sysctrl_gate_fixed_rate_ops);
+}
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long flags)
+{
+ return clk_reg_sysctrl(dev, name, parent_names, num_parents,
+ reg_sel, reg_mask, reg_bits, 0, 0, flags,
+ &clk_sysctrl_set_parent_ops);
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
index c3e449169a83..a2bb92d85ee0 100644
--- a/drivers/clk/ux500/clk.h
+++ b/drivers/clk/ux500/clk.h
@@ -11,16 +11,18 @@
#define __UX500_CLK_H
#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
struct clk *clk_reg_prcc_pclk(const char *name,
const char *parent_name,
- unsigned int phy_base,
+ resource_size_t phy_base,
u32 cg_sel,
unsigned long flags);
struct clk *clk_reg_prcc_kclk(const char *name,
const char *parent_name,
- unsigned int phy_base,
+ resource_size_t phy_base,
u32 cg_sel,
unsigned long flags);
@@ -57,4 +59,32 @@ struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
unsigned long rate,
unsigned long flags);
+struct clk *clk_reg_sysctrl_gate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long enable_delay_us,
+ unsigned long flags);
+
+struct clk *clk_reg_sysctrl_gate_fixed_rate(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ u16 reg_sel,
+ u8 reg_mask,
+ u8 reg_bits,
+ unsigned long rate,
+ unsigned long enable_delay_us,
+ unsigned long flags);
+
+struct clk *clk_reg_sysctrl_set_parent(struct device *dev,
+ const char *name,
+ const char **parent_names,
+ u8 num_parents,
+ u16 *reg_sel,
+ u8 *reg_mask,
+ u8 *reg_bits,
+ unsigned long flags);
+
#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
index 6b889a0e90b3..0b4f35a5ffc2 100644
--- a/drivers/clk/ux500/u8500_clk.c
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -12,10 +12,10 @@
#include <linux/clk-provider.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/platform_data/clk-ux500.h>
-#include <mach/db8500-regs.h>
#include "clk.h"
-void u8500_clk_init(void)
+void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base,
+ u32 clkrst5_base, u32 clkrst6_base)
{
struct prcmu_fw_version *fw_version;
const char *sgaclk_parent = NULL;
@@ -215,147 +215,148 @@ void u8500_clk_init(void)
*/
/* PRCC P-clocks */
- clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base,
BIT(0), 0);
clk_register_clkdev(clk, "apb_pclk", "uart0");
- clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base,
BIT(1), 0);
clk_register_clkdev(clk, "apb_pclk", "uart1");
- clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base,
BIT(2), 0);
clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1");
- clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base,
BIT(3), 0);
clk_register_clkdev(clk, "apb_pclk", "msp0");
clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.0");
- clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base,
BIT(4), 0);
clk_register_clkdev(clk, "apb_pclk", "msp1");
clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.1");
- clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base,
BIT(5), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi0");
- clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base,
BIT(6), 0);
clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2");
- clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base,
BIT(7), 0);
clk_register_clkdev(clk, NULL, "spi3");
- clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base,
BIT(8), 0);
clk_register_clkdev(clk, "apb_pclk", "slimbus0");
- clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base,
BIT(9), 0);
clk_register_clkdev(clk, NULL, "gpio.0");
clk_register_clkdev(clk, NULL, "gpio.1");
clk_register_clkdev(clk, NULL, "gpioblock0");
- clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base,
BIT(10), 0);
clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4");
- clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", U8500_CLKRST1_BASE,
+ clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base,
BIT(11), 0);
clk_register_clkdev(clk, "apb_pclk", "msp3");
clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.3");
- clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base,
BIT(0), 0);
clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3");
- clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base,
BIT(1), 0);
clk_register_clkdev(clk, NULL, "spi2");
- clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base,
BIT(2), 0);
clk_register_clkdev(clk, NULL, "spi1");
- clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base,
BIT(3), 0);
clk_register_clkdev(clk, NULL, "pwl");
- clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base,
BIT(4), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi4");
- clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base,
BIT(5), 0);
clk_register_clkdev(clk, "apb_pclk", "msp2");
clk_register_clkdev(clk, "apb_pclk", "ux500-msp-i2s.2");
- clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base,
BIT(6), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi1");
- clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base,
BIT(7), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi3");
- clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base,
BIT(8), 0);
clk_register_clkdev(clk, NULL, "spi0");
- clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base,
BIT(9), 0);
clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0");
- clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base,
BIT(10), 0);
clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0");
- clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base,
BIT(11), 0);
clk_register_clkdev(clk, NULL, "gpio.6");
clk_register_clkdev(clk, NULL, "gpio.7");
clk_register_clkdev(clk, NULL, "gpioblock1");
- clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", U8500_CLKRST2_BASE,
+ clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base,
BIT(12), 0);
- clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base,
BIT(0), 0);
- clk_register_clkdev(clk, NULL, "fsmc");
+ clk_register_clkdev(clk, "fsmc", NULL);
+ clk_register_clkdev(clk, NULL, "smsc911x");
- clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base,
BIT(1), 0);
clk_register_clkdev(clk, "apb_pclk", "ssp0");
- clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base,
BIT(2), 0);
clk_register_clkdev(clk, "apb_pclk", "ssp1");
- clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base,
BIT(3), 0);
clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0");
- clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base,
BIT(4), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi2");
- clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base,
BIT(5), 0);
clk_register_clkdev(clk, "apb_pclk", "ske");
clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad");
- clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base,
BIT(6), 0);
clk_register_clkdev(clk, "apb_pclk", "uart2");
- clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base,
BIT(7), 0);
clk_register_clkdev(clk, "apb_pclk", "sdi5");
- clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", U8500_CLKRST3_BASE,
+ clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base,
BIT(8), 0);
clk_register_clkdev(clk, NULL, "gpio.2");
clk_register_clkdev(clk, NULL, "gpio.3");
@@ -363,45 +364,45 @@ void u8500_clk_init(void)
clk_register_clkdev(clk, NULL, "gpio.5");
clk_register_clkdev(clk, NULL, "gpioblock2");
- clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", U8500_CLKRST5_BASE,
+ clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base,
BIT(0), 0);
clk_register_clkdev(clk, "usb", "musb-ux500.0");
- clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", U8500_CLKRST5_BASE,
+ clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base,
BIT(1), 0);
clk_register_clkdev(clk, NULL, "gpio.8");
clk_register_clkdev(clk, NULL, "gpioblock3");
- clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base,
BIT(0), 0);
clk_register_clkdev(clk, "apb_pclk", "rng");
- clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base,
BIT(1), 0);
clk_register_clkdev(clk, NULL, "cryp0");
clk_register_clkdev(clk, NULL, "cryp1");
- clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base,
BIT(2), 0);
clk_register_clkdev(clk, NULL, "hash0");
- clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base,
BIT(3), 0);
clk_register_clkdev(clk, NULL, "pka");
- clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base,
BIT(4), 0);
clk_register_clkdev(clk, NULL, "hash1");
- clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base,
BIT(5), 0);
clk_register_clkdev(clk, NULL, "cfgreg");
- clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base,
BIT(6), 0);
clk_register_clkdev(clk, "apb_pclk", "mtu0");
- clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", U8500_CLKRST6_BASE,
+ clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base,
BIT(7), 0);
clk_register_clkdev(clk, "apb_pclk", "mtu1");
@@ -415,110 +416,110 @@ void u8500_clk_init(void)
/* Periph1 */
clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk",
- U8500_CLKRST1_BASE, BIT(0), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(0), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "uart0");
clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk",
- U8500_CLKRST1_BASE, BIT(1), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(1), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "uart1");
clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk",
- U8500_CLKRST1_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(2), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "nmk-i2c.1");
clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk",
- U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(3), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "msp0");
clk_register_clkdev(clk, NULL, "ux500-msp-i2s.0");
clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk",
- U8500_CLKRST1_BASE, BIT(4), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(4), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "msp1");
clk_register_clkdev(clk, NULL, "ux500-msp-i2s.1");
clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk",
- U8500_CLKRST1_BASE, BIT(5), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(5), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi0");
clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk",
- U8500_CLKRST1_BASE, BIT(6), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(6), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "nmk-i2c.2");
clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk",
- U8500_CLKRST1_BASE, BIT(8), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(8), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "slimbus0");
clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk",
- U8500_CLKRST1_BASE, BIT(9), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(9), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "nmk-i2c.4");
clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk",
- U8500_CLKRST1_BASE, BIT(10), CLK_SET_RATE_GATE);
+ clkrst1_base, BIT(10), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "msp3");
clk_register_clkdev(clk, NULL, "ux500-msp-i2s.3");
/* Periph2 */
clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk",
- U8500_CLKRST2_BASE, BIT(0), CLK_SET_RATE_GATE);
+ clkrst2_base, BIT(0), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "nmk-i2c.3");
clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk",
- U8500_CLKRST2_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clkrst2_base, BIT(2), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi4");
clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk",
- U8500_CLKRST2_BASE, BIT(3), CLK_SET_RATE_GATE);
+ clkrst2_base, BIT(3), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "msp2");
clk_register_clkdev(clk, NULL, "ux500-msp-i2s.2");
clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk",
- U8500_CLKRST2_BASE, BIT(4), CLK_SET_RATE_GATE);
+ clkrst2_base, BIT(4), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi1");
clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk",
- U8500_CLKRST2_BASE, BIT(5), CLK_SET_RATE_GATE);
+ clkrst2_base, BIT(5), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi3");
/* Note that rate is received from parent. */
clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk",
- U8500_CLKRST2_BASE, BIT(6),
+ clkrst2_base, BIT(6),
CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk",
- U8500_CLKRST2_BASE, BIT(7),
+ clkrst2_base, BIT(7),
CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
/* Periph3 */
clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk",
- U8500_CLKRST3_BASE, BIT(1), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(1), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "ssp0");
clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk",
- U8500_CLKRST3_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(2), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "ssp1");
clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk",
- U8500_CLKRST3_BASE, BIT(3), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(3), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "nmk-i2c.0");
clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk",
- U8500_CLKRST3_BASE, BIT(4), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(4), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi2");
clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k",
- U8500_CLKRST3_BASE, BIT(5), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(5), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "ske");
clk_register_clkdev(clk, NULL, "nmk-ske-keypad");
clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk",
- U8500_CLKRST3_BASE, BIT(6), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(6), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "uart2");
clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk",
- U8500_CLKRST3_BASE, BIT(7), CLK_SET_RATE_GATE);
+ clkrst3_base, BIT(7), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "sdi5");
/* Periph6 */
clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk",
- U8500_CLKRST6_BASE, BIT(0), CLK_SET_RATE_GATE);
+ clkrst6_base, BIT(0), CLK_SET_RATE_GATE);
clk_register_clkdev(clk, NULL, "rng");
}
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
index ec3b88fe3e6d..c16ca787170a 100644
--- a/drivers/clk/versatile/Makefile
+++ b/drivers/clk/versatile/Makefile
@@ -3,5 +3,5 @@ obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
-obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o
+obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o
obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o
diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c
new file mode 100644
index 000000000000..bf9b15a585e1
--- /dev/null
+++ b/drivers/clk/versatile/clk-sp810.c
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#include <linux/amba/sp810.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define to_clk_sp810_timerclken(_hw) \
+ container_of(_hw, struct clk_sp810_timerclken, hw)
+
+struct clk_sp810;
+
+struct clk_sp810_timerclken {
+ struct clk_hw hw;
+ struct clk *clk;
+ struct clk_sp810 *sp810;
+ int channel;
+};
+
+struct clk_sp810 {
+ struct device_node *node;
+ int refclk_index, timclk_index;
+ void __iomem *base;
+ spinlock_t lock;
+ struct clk_sp810_timerclken timerclken[4];
+ struct clk *refclk;
+ struct clk *timclk;
+};
+
+static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ u32 val = readl(timerclken->sp810->base + SCCTRL);
+
+ return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
+}
+
+static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+ u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
+ unsigned long flags = 0;
+
+ if (WARN_ON(index > 1))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sp810->lock, flags);
+
+ val = readl(sp810->base + SCCTRL);
+ val &= ~(1 << shift);
+ val |= index << shift;
+ writel(val, sp810->base + SCCTRL);
+
+ spin_unlock_irqrestore(&sp810->lock, flags);
+
+ return 0;
+}
+
+/*
+ * FIXME - setting the parent every time .prepare is invoked is inefficient.
+ * This is better handled by a dedicated clock tree configuration mechanism at
+ * init-time. Revisit this later when such a mechanism exists
+ */
+static int clk_sp810_timerclken_prepare(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+ struct clk *old_parent = __clk_get_parent(hw->clk);
+ struct clk *new_parent;
+
+ if (!sp810->refclk)
+ sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index);
+
+ if (!sp810->timclk)
+ sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index);
+
+ if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk)))
+ return -ENOENT;
+
+ /* Select fastest parent */
+ if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk))
+ new_parent = sp810->refclk;
+ else
+ new_parent = sp810->timclk;
+
+ /* Switch the parent if necessary */
+ if (old_parent != new_parent) {
+ clk_prepare(new_parent);
+ clk_set_parent(hw->clk, new_parent);
+ clk_unprepare(old_parent);
+ }
+
+ return 0;
+}
+
+static void clk_sp810_timerclken_unprepare(struct clk_hw *hw)
+{
+ struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
+ struct clk_sp810 *sp810 = timerclken->sp810;
+
+ clk_put(sp810->timclk);
+ clk_put(sp810->refclk);
+}
+
+static const struct clk_ops clk_sp810_timerclken_ops = {
+ .prepare = clk_sp810_timerclken_prepare,
+ .unprepare = clk_sp810_timerclken_unprepare,
+ .get_parent = clk_sp810_timerclken_get_parent,
+ .set_parent = clk_sp810_timerclken_set_parent,
+};
+
+struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct clk_sp810 *sp810 = data;
+
+ if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
+ ARRAY_SIZE(sp810->timerclken)))
+ return NULL;
+
+ return sp810->timerclken[clkspec->args[0]].clk;
+}
+
+void __init clk_sp810_of_setup(struct device_node *node)
+{
+ struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
+ const char *parent_names[2];
+ char name[12];
+ struct clk_init_data init;
+ int i;
+
+ if (!sp810) {
+ pr_err("Failed to allocate memory for SP810!\n");
+ return;
+ }
+
+ sp810->refclk_index = of_property_match_string(node, "clock-names",
+ "refclk");
+ parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index);
+
+ sp810->timclk_index = of_property_match_string(node, "clock-names",
+ "timclk");
+ parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index);
+
+ if (parent_names[0] <= 0 || parent_names[1] <= 0) {
+ pr_warn("Failed to obtain parent clocks for SP810!\n");
+ return;
+ }
+
+ sp810->node = node;
+ sp810->base = of_iomap(node, 0);
+ spin_lock_init(&sp810->lock);
+
+ init.name = name;
+ init.ops = &clk_sp810_timerclken_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = ARRAY_SIZE(parent_names);
+
+ for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
+ snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+
+ sp810->timerclken[i].sp810 = sp810;
+ sp810->timerclken[i].channel = i;
+ sp810->timerclken[i].hw.init = &init;
+
+ sp810->timerclken[i].clk = clk_register(NULL,
+ &sp810->timerclken[i].hw);
+ WARN_ON(IS_ERR(sp810->timerclken[i].clk));
+ }
+
+ of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
+}
+CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);
diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c
index 82b45aad8ccf..a4a728d05092 100644
--- a/drivers/clk/versatile/clk-vexpress.c
+++ b/drivers/clk/versatile/clk-vexpress.c
@@ -15,8 +15,6 @@
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/vexpress.h>
static struct clk *vexpress_sp810_timerclken[4];
@@ -86,50 +84,3 @@ void __init vexpress_clk_init(void __iomem *sp810_base)
WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
"v2m-timer1", "sp804"));
}
-
-#if defined(CONFIG_OF)
-
-struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data)
-{
- if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
- ARRAY_SIZE(vexpress_sp810_timerclken)))
- return NULL;
-
- return vexpress_sp810_timerclken[clkspec->args[0]];
-}
-
-void __init vexpress_clk_of_init(void)
-{
- struct device_node *node;
- struct clk *clk;
- struct clk *refclk, *timclk;
-
- of_clk_init(NULL);
-
- node = of_find_compatible_node(NULL, NULL, "arm,sp810");
- vexpress_sp810_init(of_iomap(node, 0));
- of_clk_add_provider(node, vexpress_sp810_of_get, NULL);
-
- /* Select "better" (faster) parent for SP804 timers */
- refclk = of_clk_get_by_name(node, "refclk");
- timclk = of_clk_get_by_name(node, "timclk");
- if (!WARN_ON(IS_ERR(refclk) || IS_ERR(timclk))) {
- int i = 0;
-
- if (clk_get_rate(refclk) > clk_get_rate(timclk))
- clk = refclk;
- else
- clk = timclk;
-
- for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
- WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i],
- clk));
- }
-
- WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
- "v2m-timer0", "sp804"));
- WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
- "v2m-timer1", "sp804"));
-}
-
-#endif
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
index f9ba4fab0ddc..04781389d0fb 100644
--- a/drivers/clk/x86/Makefile
+++ b/drivers/clk/x86/Makefile
@@ -1,2 +1,2 @@
-clk-x86-lpss-objs := clk-lpss.o clk-lpt.o
+clk-x86-lpss-objs := clk-lpt.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
diff --git a/drivers/clk/x86/clk-lpss.c b/drivers/clk/x86/clk-lpss.c
deleted file mode 100644
index b5e229f3c3d9..000000000000
--- a/drivers/clk/x86/clk-lpss.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Intel Low Power Subsystem clocks.
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
- * Heikki Krogerus <heikki.krogerus@linux.intel.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.
- */
-
-#include <linux/acpi.h>
-#include <linux/clk.h>
-#include <linux/clk-provider.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/module.h>
-
-static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data)
-{
- struct resource r;
- return !acpi_dev_resource_memory(res, &r);
-}
-
-static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level,
- void *data, void **retval)
-{
- struct resource_list_entry *rentry;
- struct list_head resource_list;
- struct acpi_device *adev;
- const char *uid = data;
- int ret;
-
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (uid) {
- if (!adev->pnp.unique_id)
- return AE_OK;
- if (strcmp(uid, adev->pnp.unique_id))
- return AE_OK;
- }
-
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list,
- clk_lpss_is_mmio_resource, NULL);
- if (ret < 0)
- return AE_NO_MEMORY;
-
- list_for_each_entry(rentry, &resource_list, node)
- if (resource_type(&rentry->res) == IORESOURCE_MEM) {
- *(struct resource *)retval = rentry->res;
- break;
- }
-
- acpi_dev_free_resource_list(&resource_list);
- return AE_OK;
-}
-
-/**
- * clk_register_lpss_gate - register LPSS clock gate
- * @name: name of this clock gate
- * @parent_name: parent clock name
- * @hid: ACPI _HID of the device
- * @uid: ACPI _UID of the device (optional)
- * @offset: LPSS PRV_CLOCK_PARAMS offset
- *
- * Creates and registers LPSS clock gate.
- */
-struct clk *clk_register_lpss_gate(const char *name, const char *parent_name,
- const char *hid, const char *uid,
- unsigned offset)
-{
- struct resource res = { };
- void __iomem *mmio_base;
- acpi_status status;
- struct clk *clk;
-
- /*
- * First try to look the device and its mmio resource from the
- * ACPI namespace.
- */
- status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid,
- (void **)&res);
- if (ACPI_FAILURE(status) || !res.start)
- return ERR_PTR(-ENODEV);
-
- mmio_base = ioremap(res.start, resource_size(&res));
- if (!mmio_base)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset,
- 0, 0, NULL);
- if (IS_ERR(clk))
- iounmap(mmio_base);
-
- return clk;
-}
diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h
deleted file mode 100644
index e9460f442297..000000000000
--- a/drivers/clk/x86/clk-lpss.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Intel Low Power Subsystem clock.
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
- * Heikki Krogerus <heikki.krogerus@linux.intel.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.
- */
-
-#ifndef __CLK_LPSS_H
-#define __CLK_LPSS_H
-
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-
-#ifdef CONFIG_ACPI
-extern struct clk *clk_register_lpss_gate(const char *name,
- const char *parent_name,
- const char *hid, const char *uid,
- unsigned offset);
-#else
-static inline struct clk *clk_register_lpss_gate(const char *name,
- const char *parent_name,
- const char *hid,
- const char *uid,
- unsigned offset)
-{
- return ERR_PTR(-ENODEV);
-}
-#endif
-
-#endif /* __CLK_LPSS_H */
diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c
index 81298aeef7e3..5cf4f4686406 100644
--- a/drivers/clk/x86/clk-lpt.c
+++ b/drivers/clk/x86/clk-lpt.c
@@ -10,7 +10,6 @@
* published by the Free Software Foundation.
*/
-#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
@@ -18,8 +17,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include "clk-lpss.h"
-
#define PRV_CLOCK_PARAMS 0x800
static int lpt_clk_probe(struct platform_device *pdev)
@@ -34,40 +31,6 @@ static int lpt_clk_probe(struct platform_device *pdev)
/* Shared DMA clock */
clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto");
-
- /* SPI clocks */
- clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C0:00");
-
- clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C1:00");
-
- /* I2C clocks */
- clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C2:00");
-
- clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C3:00");
-
- /* UART clocks */
- clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C4:00");
-
- clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C5:00");
-
return 0;
}
@@ -79,8 +42,7 @@ static struct platform_driver lpt_clk_driver = {
.probe = lpt_clk_probe,
};
-static int __init lpt_clk_init(void)
+int __init lpt_clk_init(void)
{
return platform_driver_register(&lpt_clk_driver);
}
-arch_initcall(lpt_clk_init);