Reposting this RFC due to conflicts with v3.11-rc1 and because I haven't
gone through and generated all the data needed yet. This is just a few
clocks for now to give a general idea until I get some comments from DT
reviewers.
The first 4 patches are generic clock framework patches. They add support
for finding clocks in DT and parsing them generically. They also add support
for setting the rate and the parent at the same time based on patches from
James Hogan's remuxing set_rate series [1].
After that we add MSM clock hardware support and then fill in the DT and
data to actually register clocks. This is sufficient enough to get the UART
going on my msm8960 device.
If DT reviewers are ok I'll go ahead and generate the rest of the data
and post v2.
Stephen Boyd (14):
clk: fixed-rate: Export clk_fixed_rate_register()
clk: Add of_init_clk_data() to parse common clock bindings
clk: Add of_clk_match() for device drivers
clk: Add set_rate_and_parent() op
clk: msm: Add support for phase locked loops (PLLs)
clk: msm: Add support for root clock generators (RCGs)
clk: msm: Add support for branches/gate clocks
clk: msm: Add MSM clock driver
clk: msm: Add support for MSM8960's global clock controller (GCC)
clk: msm: Add support for MSM8960's multimedia clock controller (MMCC)
ARM: dts: msm: Add MSM8960 GCC DT nodes
ARM: dts: msm: Add MSM8960 MMCC DT nodes
clk: msm: Add MSM8974 GCC data
ARM: dts: msm: Add clock entries for MSM8960 uart device
Documentation/clk.txt | 3 +
Documentation/devicetree/bindings/clock/msm.txt | 99 +++
.../devicetree/bindings/clock/qcom,gcc.txt | 56 ++
.../devicetree/bindings/clock/qcom,mmcc.txt | 38 ++
arch/arm/boot/dts/msm8960-cdp.dts | 111 +++
drivers/clk/Kconfig | 2 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-fixed-rate.c | 1 +
drivers/clk/clk.c | 201 +++++-
drivers/clk/msm/Kconfig | 29 +
drivers/clk/msm/Makefile | 12 +
drivers/clk/msm/clk-branch.c | 190 ++++++
drivers/clk/msm/clk-branch.h | 48 ++
drivers/clk/msm/clk-pll.c | 233 +++++++
drivers/clk/msm/clk-pll.h | 43 ++
drivers/clk/msm/clk-rcg.c | 754 +++++++++++++++++++++
drivers/clk/msm/clk-rcg.h | 114 ++++
drivers/clk/msm/clk-rcg2.c | 320 +++++++++
drivers/clk/msm/core.c | 274 ++++++++
drivers/clk/msm/gcc-8960.c | 174 +++++
drivers/clk/msm/gcc-8974.c | 107 +++
drivers/clk/msm/internal.h | 28 +
drivers/clk/msm/mmcc-8960.c | 142 ++++
include/linux/clk-provider.h | 38 ++
24 files changed, 2998 insertions(+), 20 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/msm.txt
create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc.txt
create mode 100644 Documentation/devicetree/bindings/clock/qcom,mmcc.txt
create mode 100644 drivers/clk/msm/Kconfig
create mode 100644 drivers/clk/msm/Makefile
create mode 100644 drivers/clk/msm/clk-branch.c
create mode 100644 drivers/clk/msm/clk-branch.h
create mode 100644 drivers/clk/msm/clk-pll.c
create mode 100644 drivers/clk/msm/clk-pll.h
create mode 100644 drivers/clk/msm/clk-rcg.c
create mode 100644 drivers/clk/msm/clk-rcg.h
create mode 100644 drivers/clk/msm/clk-rcg2.c
create mode 100644 drivers/clk/msm/core.c
create mode 100644 drivers/clk/msm/gcc-8960.c
create mode 100644 drivers/clk/msm/gcc-8974.c
create mode 100644 drivers/clk/msm/internal.h
create mode 100644 drivers/clk/msm/mmcc-8960.c
[1] Subject: [PATCH v5 0/5] clk: implement remuxing during set_rate Message-ID: <[email protected]>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Export this symbol so that modules can register fixed rate
clocks.
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/clk/clk-fixed-rate.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index dc58fbd..1ed591a 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -80,6 +80,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
return clk;
}
+EXPORT_SYMBOL_GPL(clk_register_fixed_rate);
#ifdef CONFIG_OF
/**
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Some of Qualcomm's clocks can change their parent and rate at the
same time with a single register write. Add support for this
hardware to the common clock framework by adding a new
set_rate_and_parent() op. When the clock framework determines
that both the parent and the rate are going to change during
clk_set_rate() it will call the .set_rate_and_parent() op if
available and fall back to calling .set_parent() followed by
.set_rate() otherwise.
Cc: James Hogan <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
Documentation/clk.txt | 3 ++
drivers/clk/clk.c | 78 +++++++++++++++++++++++++++++++++-----------
include/linux/clk-provider.h | 15 +++++++++
3 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/Documentation/clk.txt b/Documentation/clk.txt
index 3aeb5c4..79700ea 100644
--- a/Documentation/clk.txt
+++ b/Documentation/clk.txt
@@ -77,6 +77,9 @@ the operations defined in clk.h:
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw, unsigned long);
+ int (*set_rate_and_parent)(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate, u8 index);
void (*init)(struct clk_hw *hw);
};
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1e3e0db..73de07c 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1121,10 +1121,9 @@ static void clk_reparent(struct clk *clk, struct clk *new_parent)
clk->parent = new_parent;
}
-static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+static struct clk *__clk_set_parent_before(struct clk *clk, struct clk *parent)
{
unsigned long flags;
- int ret = 0;
struct clk *old_parent = clk->parent;
/*
@@ -1155,6 +1154,34 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
clk_reparent(clk, parent);
clk_enable_unlock(flags);
+ return old_parent;
+}
+
+static void __clk_set_parent_after(struct clk *clk, struct clk *parent,
+ struct clk *old_parent)
+{
+ /*
+ * Finish the migration of prepare state and undo the changes done
+ * for preventing a race with clk_enable().
+ */
+ if (clk->prepare_count) {
+ clk_disable(clk);
+ clk_disable(old_parent);
+ __clk_unprepare(old_parent);
+ }
+
+ /* update debugfs with new clk tree topology */
+ clk_debug_reparent(clk, parent);
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct clk *old_parent;
+
+ old_parent = __clk_set_parent_before(clk, parent);
+
/* change clock input source */
if (parent && clk->ops->set_parent)
ret = clk->ops->set_parent(clk->hw, p_index);
@@ -1172,18 +1199,8 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
return ret;
}
- /*
- * Finish the migration of prepare state and undo the changes done
- * for preventing a race with clk_enable().
- */
- if (clk->prepare_count) {
- clk_disable(clk);
- clk_disable(old_parent);
- __clk_unprepare(old_parent);
- }
+ __clk_set_parent_after(clk, parent, old_parent);
- /* update debugfs with new clk tree topology */
- clk_debug_reparent(clk, parent);
return 0;
}
@@ -1368,17 +1385,32 @@ static void clk_change_rate(struct clk *clk)
struct clk *child;
unsigned long old_rate;
unsigned long best_parent_rate = 0;
+ bool skip_set_rate = false;
+ struct clk *old_parent;
old_rate = clk->rate;
- /* set parent */
- if (clk->new_parent && clk->new_parent != clk->parent)
- __clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
-
- if (clk->parent)
+ if (clk->new_parent)
+ best_parent_rate = clk->new_parent->rate;
+ else if (clk->parent)
best_parent_rate = clk->parent->rate;
- if (clk->ops->set_rate)
+ if (clk->new_parent && clk->new_parent != clk->parent) {
+ old_parent = __clk_set_parent_before(clk, clk->new_parent);
+
+ if (clk->ops->set_rate_and_parent) {
+ skip_set_rate = true;
+ clk->ops->set_rate_and_parent(clk->hw, clk->new_rate,
+ best_parent_rate,
+ clk->new_parent_index);
+ } else if (clk->ops->set_parent) {
+ clk->ops->set_parent(clk->hw, clk->new_parent_index);
+ }
+
+ __clk_set_parent_after(clk, clk->new_parent, old_parent);
+ }
+
+ if (!skip_set_rate && clk->ops->set_rate)
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
if (clk->ops->recalc_rate)
@@ -1664,6 +1696,14 @@ int __clk_init(struct device *dev, struct clk *clk)
goto out;
}
+ if (clk->ops->set_rate_and_parent &&
+ !(clk->ops->set_parent && clk->ops->set_rate)) {
+ pr_warning("%s: %s must implement .set_parent & .set_rate\n",
+ __func__, clk->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
/* throw a WARN if any entries in parent_names are NULL */
for (i = 0; i < clk->num_parents; i++)
WARN(!clk->parent_names[i],
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 484f8ad..1f7eabb 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -108,6 +108,18 @@ struct clk_hw;
* which is likely helpful for most .set_rate implementation.
* Returns 0 on success, -EERROR otherwise.
*
+ * @set_rate_and_parent: Change the rate and the parent of this clock. The
+ * requested rate is specified by the second argument, which
+ * should typically be the return of .round_rate call. The
+ * third argument gives the parent rate which is likely helpful
+ * for most .set_rate_and_parent implementation. The fourth
+ * argument gives the parent index. It is optional (and
+ * unnecessary) for clocks with 0 or 1 parents as well as
+ * for clocks that can tolerate switching the rate and the parent
+ * separately via calls to .set_parent and .set_rate.
+ * Returns 0 on success, -EERROR otherwise.
+ *
+ *
* The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
* implementations to split any work between atomic (enable) and sleepable
* (prepare) contexts. If enabling a clock requires code that might sleep,
@@ -139,6 +151,9 @@ struct clk_ops {
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw, unsigned long,
unsigned long);
+ int (*set_rate_and_parent)(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate, u8 index);
void (*init)(struct clk_hw *hw);
};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
In similar fashion as of_regulator_match() add an of_clk_match()
function that finds an initializes clock init_data structs from
devicetree. Drivers should use this API to find clocks that their
device is providing and then iterate over their match table
registering the clocks with the init data parsed.
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/clk/clk.c | 64 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk-provider.h | 16 +++++++++++
2 files changed, 80 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ea8e951b..1e3e0db 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2289,4 +2289,68 @@ err:
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(of_init_clk_data);
+
+/**
+ * of_clk_match() - Scan and match clock providers from the DT node of a device
+ * @dev: device initializing clock providers
+ * @matches: match table of clocks
+ * @num_matches: number of entries in @matches
+ *
+ * This function uses a match table specified by the clock driver to parse
+ * clock init data from the device tree. @dev is expected to be a device with
+ * a 'clocks' child node containing a set of child nodes, each providing the
+ * init data for one clock. The data parsed from a child node will be matched
+ * to a clock based on the child node's name. Note that the match table is
+ * modified in place.
+ *
+ * Returns the number of matches found or a negative error code on failure.
+ */
+int of_clk_match(struct device *dev, struct of_clk_match *matches,
+ int num_matches)
+{
+ int count = 0;
+ int err, i;
+ struct device_node *np, *clocks;
+ struct clk_init_data *init;
+
+ clocks = of_find_node_by_name(dev->of_node, "clocks");
+ if (!clocks)
+ return -EINVAL;
+
+ for (i = 0; i < num_matches; i++) {
+ struct of_clk_match *match = &matches[i];
+ match->init_data = NULL;
+ match->of_node = NULL;
+ }
+
+ for_each_available_child_of_node(clocks, np) {
+ for (i = 0; i < num_matches; i++) {
+ struct of_clk_match *match = &matches[i];
+ if (match->of_node)
+ continue;
+
+ if (strcmp(match->name, np->name))
+ continue;
+
+ init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL);
+ if (!init)
+ return -ENOMEM;
+
+ err = of_init_clk_data(np, init);
+ if (err) {
+ dev_err(dev,
+ "failed to parse DT for clock %s\n",
+ np->name);
+ return err;
+ }
+ match->of_node = np;
+ match->init_data = init;
+ count++;
+ break;
+ }
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(of_clk_match);
#endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 18d6362..484f8ad 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -461,6 +461,13 @@ struct clk_onecell_data {
__used __section(__clk_of_table) \
= { .compatible = compat, .data = fn };
+struct of_clk_match {
+ const char *name;
+ void *driver_data;
+ struct clk_init_data *init_data;
+ struct device_node *of_node;
+};
+
#ifdef CONFIG_OF
int of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *args,
@@ -475,6 +482,9 @@ const char *of_clk_get_parent_name(struct device_node *np, int index);
void of_clk_init(const struct of_device_id *matches);
int of_init_clk_data(struct device_node *np, struct clk_init_data *init);
+int of_clk_match(struct device *dev, struct of_clk_match *matches,
+ int num_matches);
+
#else /* !CONFIG_OF */
static inline int of_clk_add_provider(struct device_node *np,
@@ -508,6 +518,12 @@ of_init_clk_data(struct device_node *np, struct clk_init_data *init)
{
return 0;
}
+static inline int
+of_clk_match(struct device *dev, struct of_clk_match *matches, int num_matches)
+{
+
+ return 0;
+}
#endif /* CONFIG_OF */
#endif /* CONFIG_COMMON_CLK */
#endif /* CLK_PROVIDER_H */
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Fill in the data and wire up the global clock controller to the
MSM clock driver. This should allow most non-multimedia device
drivers to control their clocks on 8960 based platforms.
Cc: [email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
.../devicetree/bindings/clock/qcom,gcc.txt | 55 +++++++
drivers/clk/msm/Kconfig | 10 ++
drivers/clk/msm/Makefile | 2 +
drivers/clk/msm/core.c | 3 +
drivers/clk/msm/gcc-8960.c | 174 +++++++++++++++++++++
drivers/clk/msm/internal.h | 2 +
6 files changed, 246 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc.txt
create mode 100644 drivers/clk/msm/gcc-8960.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
new file mode 100644
index 0000000..2311e1a
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -0,0 +1,55 @@
+MSM Global Clock Controller Binding
+-----------------------------------
+
+Required properties :
+- compatible : shall contain at least "qcom,gcc" and only one of the
+ following:
+
+ "qcom,gcc-8660"
+ "qcom,gcc-8960"
+
+- reg : shall contain base register location and length
+- clocks : shall contain clocks supplied by the clock controller
+
+Example:
+ clock-controller@900000 {
+ compatible = "qcom,gcc-8960", "qcom,gcc";
+ reg = <0x900000 0x4000>;
+
+ clocks {
+ pxo: pxo {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <27000000>;
+ };
+
+ pll8: pll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+ vpll8: vpll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll-vote";
+ clocks = <&pll8>;
+ };
+
+ gsbi5_uart_rcg: gsbi5_uart_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p2-mn16-clock";
+ clocks = <&pxo>, <&vpll8>;
+ };
+
+ gsbi5_uart_clk: gsbi5_uart_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&gsbi5_uart_rcg>;
+ };
+
+ gsbi5_uart_ahb: gsbi5_uart_ahb {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-hg-clock";
+ };
+ };
+ };
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
index bf7e3d2..3eaffb6 100644
--- a/drivers/clk/msm/Kconfig
+++ b/drivers/clk/msm/Kconfig
@@ -2,3 +2,13 @@ menuconfig COMMON_CLK_MSM
tristate "Support for Qualcomm's MSM designs"
depends on OF
+if COMMON_CLK_MSM
+
+config MSM_GCC_8960
+ bool "MSM8960 Global Clock Controller"
+ help
+ Support for the global clock controller on msm8960 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, SATA, PCIe, etc.
+
+endif
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index 9cfd0d7..c785943 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -6,3 +6,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
+
+clk-msm-$(CONFIG_MSM_GCC_8960) += gcc-8960.o
diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
index b1904c0..b8e702b 100644
--- a/drivers/clk/msm/core.c
+++ b/drivers/clk/msm/core.c
@@ -173,6 +173,9 @@ typedef struct clk *
struct cc_data *cc);
static const struct of_device_id msm_cc_match_table[] = {
+#ifdef CONFIG_MSM_GCC_8960
+ { .compatible = "qcom,gcc-8960", .data = &msm_gcc_8960_matches },
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, msm_cc_match_table);
diff --git a/drivers/clk/msm/gcc-8960.c b/drivers/clk/msm/gcc-8960.c
new file mode 100644
index 0000000..b57d2dd
--- /dev/null
+++ b/drivers/clk/msm/gcc-8960.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+
+#include "internal.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+
+static struct pll_desc pll8_desc = {
+ .l_reg = 0x3144,
+ .m_reg = 0x3148,
+ .n_reg = 0x314c,
+ .config_reg = 0x3154,
+ .mode_reg = 0x3140,
+ .status_reg = 0x3158,
+ .status_bit = 16,
+};
+
+static struct pll_vote_desc pll8_vote_desc = {
+ .vote_reg = 0x34c0,
+ .vote_bit = 8,
+};
+
+#define PXO 0
+#define PLL8 1
+
+static u8 gcc_pxo_pll8_map[] = {
+ [PXO] = 0,
+ [PLL8] = 3,
+};
+
+static struct freq_tbl clk_tbl_gsbi_uart[] = {
+ { 1843200, PLL8, 2, 6, 625 },
+ { 3686400, PLL8, 2, 12, 625 },
+ { 7372800, PLL8, 2, 24, 625 },
+ { 14745600, PLL8, 2, 48, 625 },
+ { 16000000, PLL8, 4, 1, 6 },
+ { 24000000, PLL8, 4, 1, 4 },
+ { 32000000, PLL8, 4, 1, 3 },
+ { 40000000, PLL8, 1, 5, 48 },
+ { 46400000, PLL8, 1, 29, 240 },
+ { 48000000, PLL8, 4, 1, 2 },
+ { 51200000, PLL8, 1, 2, 15 },
+ { 56000000, PLL8, 1, 7, 48 },
+ { 58982400, PLL8, 1, 96, 625 },
+ { 64000000, PLL8, 2, 1, 3 },
+ { }
+};
+
+static struct rcg_desc gsbi5_uart_rcg = {
+ .ctl_reg = 0x2a54,
+ .ns_reg = 0x2a54,
+ .md_reg = 0x2a50,
+ .ctl_bit = 11,
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .pre_div_shift = 3,
+ .src_sel_shift = 0,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .parent_map = gcc_pxo_pll8_map,
+ .freq_tbl = clk_tbl_gsbi_uart,
+};
+
+static struct branch_desc gsbi5_uart_cxc = {
+ .ctl_reg = 0x2a54,
+ .halt_reg = 0x2fd0,
+ .ctl_bit = 9,
+ .halt_bit = 22,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct rcg_desc gsbi6_uart_rcg = {
+ .ctl_reg = 0x2a74,
+ .ns_reg = 0x2a74,
+ .md_reg = 0x2a70,
+ .ctl_bit = 11,
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .pre_div_shift = 3,
+ .src_sel_shift = 0,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .parent_map = gcc_pxo_pll8_map,
+ .freq_tbl = clk_tbl_gsbi_uart,
+};
+
+static struct branch_desc gsbi6_uart_cxc = {
+ .ctl_reg = 0x2a74,
+ .halt_reg = 0x2fd0,
+ .ctl_bit = 9,
+ .halt_bit = 18,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct branch_desc gsbi5_uart_ahb = {
+ .ctl_reg = 0x2a40,
+ .halt_reg = 0x2fd0,
+ .hwcg_reg = 0x2a40,
+ .ctl_bit = 4,
+ .hwcg_bit = 6,
+ .halt_bit = 23,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct freq_tbl clk_tbl_gsbi_qup[] = {
+ { 1100000, PXO, 1, 2, 49 },
+ { 5400000, PXO, 1, 1, 5 },
+ { 10800000, PXO, 1, 2, 5 },
+ { 15060000, PLL8, 1, 2, 51 },
+ { 24000000, PLL8, 4, 1, 4 },
+ { 25600000, PLL8, 1, 1, 15 },
+ { 27000000, PXO, 1, 0, 0 },
+ { 48000000, PLL8, 4, 1, 2 },
+ { 51200000, PLL8, 1, 2, 15 },
+ { }
+};
+
+static struct rcg_desc gsbi5_qup_rcg = {
+ .ctl_reg = 0x2a4c,
+ .ns_reg = 0x2a4c,
+ .md_reg = 0x2a48,
+ .ctl_bit = 11,
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .pre_div_shift = 3,
+ .src_sel_shift = 0,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .parent_map = gcc_pxo_pll8_map,
+ .freq_tbl = clk_tbl_gsbi_qup,
+};
+
+static struct branch_desc gsbi5_qup_cxc = {
+ .ctl_reg = 0x2a4c,
+ .halt_reg = 0x2fd0,
+ .ctl_bit = 9,
+ .halt_bit = 20,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct of_clk_match msm_gcc_clk_match[] = {
+ { .name = "cxo" },
+ { .name = "pxo" },
+ { .name = "pll8", .driver_data = &pll8_desc },
+ { .name = "vpll8", .driver_data = &pll8_vote_desc },
+ { .name = "gsbi5_uart_rcg", .driver_data = &gsbi5_uart_rcg },
+ { .name = "gsbi5_uart_cxc", .driver_data = &gsbi5_uart_cxc },
+ { .name = "gsbi6_uart_rcg", .driver_data = &gsbi6_uart_rcg },
+ { .name = "gsbi6_uart_cxc", .driver_data = &gsbi6_uart_cxc },
+ { .name = "gsbi5_uart_ahb", .driver_data = &gsbi5_uart_ahb },
+ { .name = "gsbi5_qup_rcg", .driver_data = &gsbi5_qup_rcg },
+ { .name = "gsbi5_qup_cxc", .driver_data = &gsbi5_qup_cxc },
+};
+
+const struct msm_clk_match msm_gcc_8960_matches = {
+ .matches = msm_gcc_clk_match,
+ .size = ARRAY_SIZE(msm_gcc_clk_match)
+};
diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
index 177bd3b..b0ffda7 100644
--- a/drivers/clk/msm/internal.h
+++ b/drivers/clk/msm/internal.h
@@ -21,4 +21,6 @@ struct msm_clk_match {
size_t size;
};
+extern const struct msm_clk_match msm_gcc_8960_matches;
+
#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Fill in the data and wire up the multimedia clock controller to
the MSM clock driver. This should allow multimedia device drivers
to control their clocks on 8960 based platforms.
Cc: [email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
.../devicetree/bindings/clock/qcom,mmcc.txt | 38 ++++++
drivers/clk/msm/Kconfig | 8 ++
drivers/clk/msm/Makefile | 1 +
drivers/clk/msm/core.c | 3 +
drivers/clk/msm/internal.h | 1 +
drivers/clk/msm/mmcc-8960.c | 142 +++++++++++++++++++++
6 files changed, 193 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,mmcc.txt
create mode 100644 drivers/clk/msm/mmcc-8960.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt
new file mode 100644
index 0000000..e06577e
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt
@@ -0,0 +1,38 @@
+MSM Multimedia Clock Controller Binding
+-----------------------------------------
+
+Required properties :
+- compatible : shall contain at least "qcom,mmcc" and only one of the
+ following:
+
+ "qcom,mmcc-8660"
+ "qcom,mmcc-8960"
+
+- reg : shall contain base register location and length
+- clocks : shall contain clocks supplied by the clock controller
+
+Example:
+ clock-controller@4000000 {
+ compatible = "qcom,mmcc-8960", "qcom,mmcc";
+ reg = <0x4000000 0x1000>;
+
+ clocks {
+ pll2: pll2 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+ mdp_rcg: mdp_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,mn8-dyn-clock";
+ clocks = <&pxo>, <&pll2>, <&vpll8>;
+ };
+
+ mdp_cxc: mdp_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&mdp_rcg>;
+ };
+ };
+ };
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
index 3eaffb6..6147380 100644
--- a/drivers/clk/msm/Kconfig
+++ b/drivers/clk/msm/Kconfig
@@ -11,4 +11,12 @@ config MSM_GCC_8960
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, SATA, PCIe, etc.
+config MSM_MMCC_8960
+ bool "MSM8960 Multimedia Clock Controller"
+ select MSM_GCC_8960
+ help
+ Support for the multimedia clock controller on msm8960 devices.
+ Say Y if you want to support multimedia devices such as display,
+ graphics, video encode/decode, camera, etc.
+
endif
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index c785943..ae199f5 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -8,3 +8,4 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
clk-msm-$(CONFIG_MSM_GCC_8960) += gcc-8960.o
+clk-msm-$(CONFIG_MSM_MMCC_8960) += mmcc-8960.o
diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
index b8e702b..4e8b8d0 100644
--- a/drivers/clk/msm/core.c
+++ b/drivers/clk/msm/core.c
@@ -176,6 +176,9 @@ static const struct of_device_id msm_cc_match_table[] = {
#ifdef CONFIG_MSM_GCC_8960
{ .compatible = "qcom,gcc-8960", .data = &msm_gcc_8960_matches },
#endif
+#ifdef CONFIG_MSM_MMCC_8960
+ { .compatible = "qcom,mmcc-8960", .data = &msm_mmcc_8960_matches },
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, msm_cc_match_table);
diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
index b0ffda7..45435a8 100644
--- a/drivers/clk/msm/internal.h
+++ b/drivers/clk/msm/internal.h
@@ -22,5 +22,6 @@ struct msm_clk_match {
};
extern const struct msm_clk_match msm_gcc_8960_matches;
+extern const struct msm_clk_match msm_mmcc_8960_matches;
#endif
diff --git a/drivers/clk/msm/mmcc-8960.c b/drivers/clk/msm/mmcc-8960.c
new file mode 100644
index 0000000..e7b7867
--- /dev/null
+++ b/drivers/clk/msm/mmcc-8960.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+
+#include "internal.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+
+static struct pll_desc pll2_desc = {
+ .l_reg = 0x320,
+ .m_reg = 0x324,
+ .n_reg = 0x328,
+ .config_reg = 0x32c,
+ .mode_reg = 0x31c,
+ .status_reg = 0x334,
+ .status_bit = 16,
+};
+
+#define PXO 0
+#define PLL2 1
+#define PLL8 2
+
+static u8 mmcc_pxo_pll2_pll8_map[] = {
+ [PXO] = 0,
+ [PLL2] = 1,
+ [PLL8] = 2,
+};
+
+static struct freq_tbl clk_tbl_mdp[] = {
+ { 9600000, PLL8, 0, 1, 40 },
+ { 13710000, PLL8, 0, 1, 28 },
+ { 27000000, PXO, 0, 0, 0 },
+ { 29540000, PLL8, 0, 1, 13 },
+ { 34910000, PLL8, 0, 1, 11 },
+ { 38400000, PLL8, 0, 1, 10 },
+ { 59080000, PLL8, 0, 2, 13 },
+ { 76800000, PLL8, 0, 1, 5 },
+ { 85330000, PLL8, 0, 2, 9 },
+ { 96000000, PLL8, 0, 1, 4 },
+ { 128000000, PLL8, 0, 1, 3 },
+ { 160000000, PLL2, 0, 1, 5 },
+ { 177780000, PLL2, 0, 2, 9 },
+ { 200000000, PLL2, 0, 1, 4 },
+ { 228571000, PLL2, 0, 2, 7 },
+ { 266667000, PLL2, 0, 1, 3 },
+ { }
+};
+
+static struct rcg_dyn_desc mdp_rcg = {
+ .ctl_reg = 0xc0,
+ .ns_reg = 0xd0,
+ .md0_reg = 0xc4,
+ .md1_reg = 0xc8,
+ .ctl_bit = 2,
+ .mnctr0_en_bit = 8,
+ .mnctr1_en_bit = 5,
+ .mnctr0_reset_bit = 31,
+ .mnctr1_reset_bit = 30,
+ .mnctr0_mode_shift = 9,
+ .mnctr1_mode_shift = 6,
+ .src0_sel_shift = 3,
+ .src1_sel_shift = 0,
+ .n0_val_shift = 22,
+ .n1_val_shift = 14,
+ .m0_val_shift = 8,
+ .m1_val_shift = 8,
+ .mux_sel_bit = 11,
+ .parent_map = mmcc_pxo_pll2_pll8_map,
+ .freq_tbl = clk_tbl_mdp,
+};
+
+static struct branch_desc mdp_cxc = {
+ .ctl_reg = 0xc0,
+ .halt_reg = 0x1d0,
+ .ctl_bit = 0,
+ .halt_bit = 10,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct freq_tbl clk_tbl_rot[] = {
+ { 27000000, PXO, 1 },
+ { 29540000, PLL8, 13 },
+ { 32000000, PLL8, 12 },
+ { 38400000, PLL8, 10 },
+ { 48000000, PLL8, 8 },
+ { 54860000, PLL8, 7 },
+ { 64000000, PLL8, 6 },
+ { 76800000, PLL8, 5 },
+ { 96000000, PLL8, 4 },
+ { 100000000, PLL2, 8 },
+ { 114290000, PLL2, 7 },
+ { 133330000, PLL2, 6 },
+ { 160000000, PLL2, 5 },
+ { 200000000, PLL2, 4 },
+ { }
+};
+
+static struct rcg_dyn_desc rot_rcg = {
+ .ctl_reg = 0xe0,
+ .ns_reg = 0xe8,
+ .ctl_bit = 2,
+ .pre_div0_shift = 22,
+ .pre_div1_shift = 26,
+ .src0_sel_shift = 16,
+ .src1_sel_shift = 19,
+ .mux_sel_bit = 30,
+ .parent_map = mmcc_pxo_pll2_pll8_map,
+ .freq_tbl = clk_tbl_rot,
+};
+
+static struct branch_desc rot_cxc = {
+ .ctl_reg = 0xe0,
+ .halt_reg = 0x1d0,
+ .ctl_bit = 0,
+ .halt_bit = 15,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct of_clk_match msm_mmcc_clk_match[] = {
+ { .name = "pll2", .driver_data = &pll2_desc },
+ { .name = "mdp_rcg", .driver_data = &mdp_rcg },
+ { .name = "mdp_cxc", .driver_data = &mdp_cxc },
+ { .name = "rot_rcg", .driver_data = &rot_rcg },
+ { .name = "rot_cxc", .driver_data = &rot_cxc },
+};
+
+const struct msm_clk_match msm_mmcc_8960_matches = {
+ .matches = msm_mmcc_clk_match,
+ .size = ARRAY_SIZE(msm_mmcc_clk_match)
+};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Add the necessary DT data to probe the multimedia clock controller
on MSM8960 devices.
Signed-off-by: Stephen Boyd <[email protected]>
---
arch/arm/boot/dts/msm8960-cdp.dts | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts
index 76a1817..be8d947 100644
--- a/arch/arm/boot/dts/msm8960-cdp.dts
+++ b/arch/arm/boot/dts/msm8960-cdp.dts
@@ -109,6 +109,43 @@
};
};
+ clock-controller@4000000 {
+ compatible = "qcom,mmcc-8960", "qcom,mmcc";
+ reg = <0x4000000 0x1000>;
+
+ clocks {
+ pll2: pll2 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+ mdp_rcg: mdp_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,mn8-dyn-clock";
+ clocks = <&pxo>, <&pll2>, <&vpll8>;
+ };
+
+ mdp_cxc: mdp_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&mdp_rcg>;
+ };
+
+ rot_rcg: rot_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p4-dyn-clock";
+ clocks = <&pxo>, <&pll2>, <&vpll8>;
+ };
+
+ rot_cxc: rot_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&rot_rcg>;
+ };
+ };
+ };
+
serial@16440000 {
compatible = "qcom,msm-hsuart", "qcom,msm-uart";
reg = <0x16440000 0x1000>,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Add the necessary data to probe an 8974 gcc device.
Signed-off-by: Stephen Boyd <[email protected]>
---
.../devicetree/bindings/clock/qcom,gcc.txt | 1 +
drivers/clk/msm/Kconfig | 7 ++
drivers/clk/msm/Makefile | 1 +
drivers/clk/msm/core.c | 3 +
drivers/clk/msm/gcc-8974.c | 107 +++++++++++++++++++++
drivers/clk/msm/internal.h | 1 +
6 files changed, 120 insertions(+)
create mode 100644 drivers/clk/msm/gcc-8974.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index 2311e1a..5c7ebcf 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -7,6 +7,7 @@ Required properties :
"qcom,gcc-8660"
"qcom,gcc-8960"
+ "qcom,gcc-8974"
- reg : shall contain base register location and length
- clocks : shall contain clocks supplied by the clock controller
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
index 6147380..b200554 100644
--- a/drivers/clk/msm/Kconfig
+++ b/drivers/clk/msm/Kconfig
@@ -19,4 +19,11 @@ config MSM_MMCC_8960
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
+config MSM_GCC_8974
+ bool "MSM8974 Global Clock Controller"
+ help
+ Support for the global clock controller on msm8974 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, SATA, PCIe, etc.
+
endif
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index ae199f5..6cd9f73 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -8,4 +8,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
clk-msm-$(CONFIG_MSM_GCC_8960) += gcc-8960.o
+clk-msm-$(CONFIG_MSM_GCC_8974) += gcc-8974.o
clk-msm-$(CONFIG_MSM_MMCC_8960) += mmcc-8960.o
diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
index 4e8b8d0..f2d0c0d 100644
--- a/drivers/clk/msm/core.c
+++ b/drivers/clk/msm/core.c
@@ -176,6 +176,9 @@ static const struct of_device_id msm_cc_match_table[] = {
#ifdef CONFIG_MSM_GCC_8960
{ .compatible = "qcom,gcc-8960", .data = &msm_gcc_8960_matches },
#endif
+#ifdef CONFIG_MSM_GCC_8974
+ { .compatible = "qcom,gcc-8974", .data = &msm_gcc_8974_matches },
+#endif
#ifdef CONFIG_MSM_MMCC_8960
{ .compatible = "qcom,mmcc-8960", .data = &msm_mmcc_8960_matches },
#endif
diff --git a/drivers/clk/msm/gcc-8974.c b/drivers/clk/msm/gcc-8974.c
new file mode 100644
index 0000000..e88f6ad
--- /dev/null
+++ b/drivers/clk/msm/gcc-8974.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+
+#include "internal.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+
+static struct pll_desc gpll0 = {
+ .l_reg = 0x4,
+ .m_reg = 0x8,
+ .n_reg = 0xc,
+ .config_reg = 0x14,
+ .mode_reg = 0x0,
+ .status_reg = 0x1c,
+ .status_bit = 17,
+};
+
+static struct pll_vote_desc vgpll0 = {
+ .vote_reg = 0x1480,
+ .vote_bit = 0,
+};
+
+#define CXO 0
+#define GPLL0 1
+#define GPLL1 2
+
+static u8 gcc_cxo_gpll0_gpll1_map[] = {
+ [CXO] = 0,
+ [GPLL0] = 1,
+ [GPLL1] = 2,
+};
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+static struct freq_tbl ftbl_gcc_blsp1_2_uart1_6_apps_clk[] = {
+ F( 3686400, GPLL0, 1, 96, 15625),
+ F( 7372800, GPLL0, 1, 192, 15625),
+ F(14745600, GPLL0, 1, 384, 15625),
+ F(16000000, GPLL0, 5, 2, 15),
+ F(19200000, CXO, 1, 0, 0),
+ F(24000000, GPLL0, 5, 1, 5),
+ F(32000000, GPLL0, 1, 4, 75),
+ F(40000000, GPLL0, 15, 0, 0),
+ F(46400000, GPLL0, 1, 29, 375),
+ F(48000000, GPLL0, 12.5, 0, 0),
+ F(51200000, GPLL0, 1, 32, 375),
+ F(56000000, GPLL0, 1, 7, 75),
+ F(58982400, GPLL0, 1, 1536, 15625),
+ F(60000000, GPLL0, 10, 0, 0),
+ F(63160000, GPLL0, 9.5, 0, 0),
+ { }
+};
+
+static struct rcg2_desc blsp1_uart2_apps_rcg = {
+ .cmd_rcgr = 0x070c,
+ .parent_map = gcc_cxo_gpll0_gpll1_map,
+ .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk,
+};
+
+static struct branch_desc blsp1_uart2_apps_cxc = {
+ .ctl_reg = 0x704,
+ .halt_reg = 0x704,
+ .ctl_bit = 0,
+ .halt_bit = 31,
+ .halt_check = BRANCH_HALT,
+};
+
+static struct branch_desc blsp1_ahb_cxc = {
+ .ctl_reg = 0x1484,
+ .halt_reg = 0x5c4,
+ .ctl_bit = 17,
+ .halt_bit = 31,
+ .halt_check = BRANCH_HALT_VOTED,
+};
+
+static struct of_clk_match msm_gcc_8974_match[] = {
+ { .name = "cxo" },
+ { .name = "gpll0", .driver_data = &gpll0 },
+ { .name = "vgpll0", .driver_data = &vgpll0 },
+ {
+ .name = "blsp1_uart2_apps_rcg",
+ .driver_data = &blsp1_uart2_apps_rcg,
+ },
+ {
+ .name = "blsp1_uart2_apps_cxc",
+ .driver_data = &blsp1_uart2_apps_cxc,
+ },
+ { .name = "blsp1_ahb_cxc", .driver_data = &blsp1_ahb_cxc },
+};
+
+const struct msm_clk_match msm_gcc_8974_matches = {
+ .matches = msm_gcc_8974_match,
+ .size = ARRAY_SIZE(msm_gcc_8974_match)
+};
diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
index 45435a8..ae252ec 100644
--- a/drivers/clk/msm/internal.h
+++ b/drivers/clk/msm/internal.h
@@ -22,6 +22,7 @@ struct msm_clk_match {
};
extern const struct msm_clk_match msm_gcc_8960_matches;
+extern const struct msm_clk_match msm_gcc_8974_matches;
extern const struct msm_clk_match msm_mmcc_8960_matches;
#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Add the necessary DT data to probe the global clock controller
on MSM8960 devices.
Signed-off-by: Stephen Boyd <[email protected]>
---
arch/arm/boot/dts/msm8960-cdp.dts | 72 +++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts
index db2060c..76a1817 100644
--- a/arch/arm/boot/dts/msm8960-cdp.dts
+++ b/arch/arm/boot/dts/msm8960-cdp.dts
@@ -37,6 +37,78 @@
reg = <0xfd510000 0x4000>;
};
+ clock-controller@900000 {
+ compatible = "qcom,gcc-8960", "qcom,gcc";
+ reg = <0x900000 0x4000>;
+
+ clocks {
+ cxo: cxo {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <19200000>;
+ };
+
+ pxo: pxo {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <27000000>;
+ };
+
+ pll8: pll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+ vpll8: vpll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll-vote";
+ clocks = <&pll8>;
+ };
+
+ gsbi5_uart_rcg: gsbi5_uart_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p2-mn16-clock";
+ clocks = <&pxo>, <&vpll8>;
+ };
+
+ gsbi5_uart_clk: gsbi5_uart_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&gsbi5_uart_rcg>;
+ };
+
+ gsbi6_uart_rcg: gsbi6_uart_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p2-mn16-clock";
+ clocks = <&pxo>, <&vpll8>;
+ };
+
+ gsbi6_uart_clk: gsbi6_uart_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&gsbi6_uart_rcg>;
+ };
+
+ gsbi5_uart_ahb: gsbi5_uart_ahb {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-hg-clock";
+ };
+
+ gsbi5_qup_rcg: gsbi5_qup_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p2-mn8-clock";
+ clocks = <&pxo>, <&vpll8>;
+ };
+
+ gsbi5_qup_clk: gsbi5_qup_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&gsbi5_qup_rcg>;
+ };
+ };
+ };
+
serial@16440000 {
compatible = "qcom,msm-hsuart", "qcom,msm-uart";
reg = <0x16440000 0x1000>,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Signed-off-by: Stephen Boyd <[email protected]>
---
arch/arm/boot/dts/msm8960-cdp.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts
index be8d947..e10fba6 100644
--- a/arch/arm/boot/dts/msm8960-cdp.dts
+++ b/arch/arm/boot/dts/msm8960-cdp.dts
@@ -151,6 +151,8 @@
reg = <0x16440000 0x1000>,
<0x16400000 0x1000>;
interrupts = <0 154 0x0>;
+ clocks = <&gsbi5_uart_clk>, <&gsbi5_uart_ahb>;
+ clock-names = "gsbi_uart_clk", "gsbi_pclk";
};
qcom,ssbi@500000 {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Cc: [email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
Documentation/devicetree/bindings/clock/msm.txt | 31 +
drivers/clk/msm/Makefile | 2 +
drivers/clk/msm/clk-rcg.c | 754 ++++++++++++++++++++++++
drivers/clk/msm/clk-rcg.h | 114 ++++
drivers/clk/msm/clk-rcg2.c | 320 ++++++++++
5 files changed, 1221 insertions(+)
create mode 100644 drivers/clk/msm/clk-rcg.c
create mode 100644 drivers/clk/msm/clk-rcg.h
create mode 100644 drivers/clk/msm/clk-rcg2.c
diff --git a/Documentation/devicetree/bindings/clock/msm.txt b/Documentation/devicetree/bindings/clock/msm.txt
index 2192621..f4595fa 100644
--- a/Documentation/devicetree/bindings/clock/msm.txt
+++ b/Documentation/devicetree/bindings/clock/msm.txt
@@ -38,3 +38,34 @@ Example:
clocks = <&pll8>;
};
+M/N:D Binding
+-------------
+
+Required properties:
+- compatible : shall be one of "qcom,p2-mn16-clock" or "qcom,p2-mn8-clock"
+- #clock-cells : from common clock binding; shall be set to 0
+- clocks : from common clock binding; shall be set of muxed input sources
+
+Example:
+ gsbi5_uart_rcg: gsbi5_uart_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,p2-mn16-clock";
+ clocks = <&pxo>, <&pll8>;
+ };
+
+M/N:D Dynamic Binding
+---------------------
+
+Required properties:
+- compatible : shall be one of "qcom,mn4-dyn-clock", "qcom,mn8-dyn-clock" or
+ "qcom,p4-dyn-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set of muxed input sources
+
+Example:
+ gfx2d_rcg : gfx2d_rcg {
+ #clock-cells = <0>;
+ compatible = "qcom,mn4-dyn-clock";
+ clocks = <&pxo>, <&pll2>, <&pll8>;
+ };
+
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index 16b750f..fb78ac9 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -1,3 +1,5 @@
obj-$(CONFIG_COMMON_CLK_MSM) += clk-msm.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg.o
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
diff --git a/drivers/clk/msm/clk-rcg.c b/drivers/clk/msm/clk-rcg.c
new file mode 100644
index 0000000..3400fee
--- /dev/null
+++ b/drivers/clk/msm/clk-rcg.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <asm/div64.h>
+
+#include "clk-rcg.h"
+
+/**
+ * struct mn - M/N:D counter
+ * @mnctr_en_bit: bit to enable mn counter
+ * @mnctr_reset_bit: bit to assert mn counter reset
+ * @mnctr_mode_shift: lowest bit of mn counter mode field
+ * @n_val_shift: lowest bit of n value field
+ * @m_val_shift: lowest bit of m value field
+ * @width: number of bits in m/n/d values
+ */
+struct mn {
+ u8 mnctr_en_bit;
+ u8 mnctr_reset_bit;
+ u8 mnctr_mode_shift;
+#define MNCTR_MODE_DUAL 0x2
+#define MNCTR_MODE_MASK 0x3
+ u8 n_val_shift;
+ u8 m_val_shift;
+ u8 width;
+};
+
+/**
+ * struct pre_div - pre-divider
+ * @pre_div_shift: lowest bit of pre divider field
+ * @pre_div_width: number of bits in predivider
+ */
+struct pre_div {
+ u8 pre_div_shift;
+ u8 pre_div_width;
+};
+
+/**
+ * struct src_sel - source selector
+ * @src_sel_shift: lowest bit of source selection field
+ * @parent_map: map from software's parent index to hardware's src_sel field
+ */
+struct src_sel {
+ u8 src_sel_shift;
+#define SRC_SEL_MASK 0x7
+ u8 *parent_map;
+};
+
+/**
+ * struct clk_rcg - root clock generator
+ *
+ * @ctl_reg: clock control register
+ * @ctl_bit: ORed with @ctl_reg to enable the clock
+ * @ns_reg: NS register
+ * @md_reg: MD register
+ * @mn: mn counter
+ * @p: pre divider
+ * @s: source selector
+ * @freq_tbl: Frequency table
+ * @hw: handle between common and hardware-specific interfaces
+ * @lock: register lock
+ *
+ */
+struct clk_rcg {
+ void __iomem *ctl_reg;
+ void __iomem *ns_reg;
+ void __iomem *md_reg;
+
+ u8 ctl_bit;
+ struct mn mn;
+ struct pre_div p;
+ struct src_sel s;
+
+ const struct freq_tbl *freq_tbl;
+
+ struct clk_hw hw;
+ spinlock_t *lock;
+};
+
+#define to_clk_rcg(_hw) container_of(_hw, struct clk_rcg, hw)
+
+/**
+ * struct clk_dyn_rcg - root clock generator with glitch free mux
+ *
+ * @ctl_reg: clock control register
+ * @ctl_bit: ORed with @ctl_reg to enable the clock
+ * @mux_sel_bit: Bit to switch glitch free mux
+ * @ns_reg: NS register
+ * @md_reg: MD0 and MD1 register
+ * @mn: mn counter (banked)
+ * @s: source selector (banked)
+ * @freq_tbl: Frequency table
+ * @hw: handle between common and hardware-specific interfaces
+ * @lock: register lock
+ *
+ */
+struct clk_dyn_rcg {
+ void __iomem *ctl_reg;
+ void __iomem *ns_reg;
+ void __iomem *md_reg[2];
+
+ u8 ctl_bit;
+ u8 mux_sel_bit;
+ struct mn mn[2];
+ struct pre_div p[2];
+ struct src_sel s[2];
+
+ const struct freq_tbl *freq_tbl;
+
+ struct clk_hw hw;
+ spinlock_t *lock;
+};
+
+#define to_clk_dyn_rcg(_hw) container_of(_hw, struct clk_dyn_rcg, hw)
+
+static int clk_rcg_toggle(void __iomem *ctl_reg, u8 ctl_bit,
+ spinlock_t *lock, bool en)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(lock, flags);
+
+ val = readl_relaxed(ctl_reg);
+ if (en)
+ val |= BIT(ctl_bit);
+ else
+ val &= ~BIT(ctl_bit);
+ writel(val, ctl_reg);
+
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+static int clk_rcg_enable(struct clk_hw *hw)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+
+ return clk_rcg_toggle(rcg->ctl_reg, rcg->ctl_bit, rcg->lock, true);
+}
+
+static void clk_rcg_disable(struct clk_hw *hw)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+
+ clk_rcg_toggle(rcg->ctl_reg, rcg->ctl_bit, rcg->lock, false);
+}
+
+static int clk_dyn_rcg_enable(struct clk_hw *hw)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+
+ return clk_rcg_toggle(rcg->ctl_reg, rcg->ctl_bit, rcg->lock, true);
+}
+
+static void clk_dyn_rcg_disable(struct clk_hw *hw)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+
+ clk_rcg_toggle(rcg->ctl_reg, rcg->ctl_bit, rcg->lock, false);
+}
+
+static int clk_dyn_rcg_is_enabled(struct clk_hw *hw)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ u32 val;
+
+ val = readl_relaxed(rcg->ctl_reg);
+ val &= BIT(rcg->ctl_bit);
+
+ return val ? 1 : 0;
+}
+
+static u32 ns_to_src(struct src_sel *s, u32 ns)
+{
+ ns >>= s->src_sel_shift;
+ ns &= SRC_SEL_MASK;
+ return ns;
+}
+
+static u32 src_to_ns(struct src_sel *s, u8 src, u32 ns)
+{
+ u32 mask;
+
+ mask = SRC_SEL_MASK;
+ mask <<= s->src_sel_shift;
+ ns &= ~mask;
+
+ ns |= src << s->src_sel_shift;
+ return ns;
+}
+
+static u8 clk_rcg_get_parent(struct clk_hw *hw)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+ u32 ns;
+ int i;
+
+ ns = readl_relaxed(rcg->ns_reg);
+ ns = ns_to_src(&rcg->s, ns);
+
+ for (i = 0; i < num_parents; i++)
+ if (ns == rcg->s.parent_map[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int reg_to_bank(struct clk_dyn_rcg *rcg, u32 bank)
+{
+ bank &= BIT(rcg->mux_sel_bit);
+ return !!bank;
+}
+
+static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+ u32 ns, ctl;
+ int bank;
+ int i;
+ struct src_sel *s;
+
+ ctl = readl_relaxed(rcg->ctl_reg);
+ bank = reg_to_bank(rcg, ctl);
+ s = &rcg->s[bank];
+
+ ns = readl_relaxed(rcg->ns_reg);
+ ns = ns_to_src(s, ns);
+
+ for (i = 0; i < num_parents; i++)
+ if (ns == s->parent_map[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int clk_rcg_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+ unsigned long flags;
+ u32 ns;
+
+ spin_lock_irqsave(rcg->lock, flags);
+
+ ns = readl_relaxed(rcg->ns_reg);
+ ns = src_to_ns(&rcg->s, rcg->s.parent_map[index], ns);
+ writel(ns, rcg->ns_reg);
+
+ spin_unlock_irqrestore(rcg->lock, flags);
+
+ return 0;
+}
+
+static u32 md_to_m(struct mn *mn, u32 md)
+{
+ md >>= mn->m_val_shift;
+ md &= BIT(mn->width) - 1;
+ return md;
+}
+
+static u32 ns_to_pre_div(struct pre_div *p, u32 ns)
+{
+ ns >>= p->pre_div_shift;
+ ns &= BIT(p->pre_div_width) - 1;
+ return ns;
+}
+
+static u32 pre_div_to_ns(struct pre_div *p, u8 pre_div, u32 ns)
+{
+ u32 mask;
+
+ mask = BIT(p->pre_div_width) - 1;
+ mask <<= p->pre_div_shift;
+ ns &= ~mask;
+
+ ns |= pre_div << p->pre_div_shift;
+ return ns;
+}
+
+static u32 mn_to_md(struct mn *mn, u32 m, u32 n, u32 md)
+{
+ u32 mask, mask_w;
+
+ mask_w = BIT(mn->width) - 1;
+ mask = (mask_w << mn->m_val_shift) | mask_w;
+ md &= ~mask;
+
+ if (n) {
+ m <<= mn->m_val_shift;
+ md |= m;
+ md |= ~n & mask_w;
+ }
+
+ return md;
+}
+
+static u32 ns_m_to_n(struct mn *mn, u32 ns, u32 m)
+{
+ ns = ~ns >> mn->n_val_shift;
+ ns &= BIT(mn->width) - 1;
+ return ns + m;
+}
+
+static u32 reg_to_mnctr_mode(struct mn *mn, u32 val)
+{
+ val >>= mn->mnctr_mode_shift;
+ val &= MNCTR_MODE_MASK;
+ return val;
+}
+
+static u32 mn_to_ns(struct mn *mn, u32 m, u32 n, u32 ns)
+{
+ u32 mask;
+
+ mask = BIT(mn->width) - 1;
+ mask <<= mn->n_val_shift;
+ ns &= ~mask;
+
+ if (n) {
+ n = n - m;
+ n = ~n;
+ n &= BIT(mn->width) - 1;
+ n <<= mn->n_val_shift;
+ ns |= n;
+ }
+
+ return ns;
+}
+
+static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val)
+{
+ u32 mask;
+
+ mask = MNCTR_MODE_MASK << mn->mnctr_mode_shift;
+ mask |= BIT(mn->mnctr_en_bit);
+ val &= ~mask;
+
+ if (n) {
+ val |= BIT(mn->mnctr_en_bit);
+ val |= MNCTR_MODE_DUAL << mn->mnctr_mode_shift;
+ }
+
+ return val;
+}
+
+static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f)
+{
+ unsigned long flags;
+ u32 ns, md, ctl, *regp;
+ int bank, new_bank;
+ struct mn *mn;
+ struct pre_div *p;
+ struct src_sel *s;
+ bool enabled;
+ void __iomem *md_reg;
+ void __iomem *bank_reg;
+ bool banked_mn = !!rcg->md_reg[0];
+
+ spin_lock_irqsave(rcg->lock, flags);
+
+ enabled = __clk_is_enabled(rcg->hw.clk);
+
+ ns = readl_relaxed(rcg->ns_reg);
+ ctl = readl_relaxed(rcg->ctl_reg);
+
+ if (banked_mn) {
+ regp = &ctl;
+ bank_reg = rcg->ctl_reg;
+ } else {
+ regp = &ns;
+ bank_reg = rcg->ns_reg;
+ }
+
+ bank = reg_to_bank(rcg, *regp);
+ new_bank = enabled ? !bank : bank;
+
+ if (banked_mn) {
+ mn = &rcg->mn[new_bank];
+ md_reg = rcg->md_reg[new_bank];
+
+ ns |= BIT(mn->mnctr_reset_bit);
+ writel_relaxed(ns, rcg->ns_reg);
+
+ md = readl_relaxed(md_reg);
+ md = mn_to_md(mn, f->m, f->n, md);
+ writel_relaxed(md, md_reg);
+
+ ns = mn_to_ns(mn, f->m, f->n, ns);
+ writel_relaxed(ns, rcg->ns_reg);
+
+ ctl = mn_to_reg(mn, f->m, f->n, ctl);
+ writel_relaxed(ctl, rcg->ctl_reg);
+
+ ns &= ~BIT(mn->mnctr_reset_bit);
+ writel_relaxed(ns, rcg->ns_reg);
+ } else {
+ p = &rcg->p[new_bank];
+ ns = pre_div_to_ns(p, f->pre_div - 1, ns);
+ }
+
+ s = &rcg->s[new_bank];
+ ns = src_to_ns(s, s->parent_map[f->src], ns);
+ writel_relaxed(ns, rcg->ns_reg);
+
+ if (enabled) {
+ *regp ^= BIT(rcg->mux_sel_bit);
+ writel_relaxed(*regp, bank_reg);
+ }
+
+ /* Ensure parent switch is completed */
+ mb();
+ spin_unlock_irqrestore(rcg->lock, flags);
+}
+
+static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ u32 ns, ctl, md, reg;
+ int bank;
+ struct freq_tbl f = { 0 };
+ bool banked_mn = !!rcg->md_reg[0];
+
+ ctl = readl_relaxed(rcg->ctl_reg);
+ ns = readl_relaxed(rcg->ns_reg);
+ reg = banked_mn ? ctl : ns;
+
+ bank = reg_to_bank(rcg, reg);
+
+ if (banked_mn) {
+ md = readl_relaxed(rcg->md_reg[bank]);
+ f.m = md_to_m(&rcg->mn[bank], md);
+ f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m);
+ } else {
+ f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1;
+ }
+ f.src = index;
+
+ configure_bank(rcg, &f);
+
+ return 0;
+}
+
+/*
+ * Calculate m/n:d rate
+ *
+ * parent_rate m
+ * rate = ----------- x ---
+ * pre_div n
+ */
+static unsigned long
+calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 pre_div)
+{
+ if (pre_div)
+ rate /= pre_div + 1;
+
+ if (mode) {
+ u64 tmp = rate;
+ tmp *= m;
+ do_div(tmp, n);
+ rate = tmp;
+ }
+
+ return rate;
+}
+
+static unsigned long
+clk_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+ u32 pre_div, m, n, ns, md, mode;
+ struct mn *mn = &rcg->mn;
+
+ ns = readl_relaxed(rcg->ns_reg);
+ md = readl_relaxed(rcg->md_reg);
+
+ pre_div = ns_to_pre_div(&rcg->p, ns);
+ m = md_to_m(mn, md);
+ n = ns_m_to_n(mn, ns, m);
+ /* MN counter mode is in ctl_reg sometimes */
+ if (rcg->ctl_reg != rcg->ns_reg)
+ mode = readl_relaxed(rcg->ctl_reg);
+ else
+ mode = ns;
+ mode = reg_to_mnctr_mode(mn, mode);
+
+ return calc_rate(parent_rate, m, n, mode, pre_div);
+}
+
+static unsigned long
+clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ u32 m, n, pre_div, ns, md, mode, reg;
+ int bank;
+ struct mn *mn;
+ bool banked_mn = !!rcg->md_reg[0];
+
+ ns = readl_relaxed(rcg->ns_reg);
+
+ if (banked_mn)
+ reg = readl_relaxed(rcg->ctl_reg);
+ else
+ reg = ns;
+
+ bank = reg_to_bank(rcg, reg);
+
+ if (banked_mn) {
+ mn = &rcg->mn[bank];
+ md = readl_relaxed(rcg->md_reg[bank]);
+ m = md_to_m(mn, md);
+ n = ns_m_to_n(mn, ns, m);
+ mode = reg_to_mnctr_mode(mn, reg);
+ return calc_rate(parent_rate, m, n, mode, 0);
+ } else {
+ pre_div = ns_to_pre_div(&rcg->p[bank], ns);
+ return calc_rate(parent_rate, 0, 0, 0, pre_div);
+ }
+}
+
+static const
+struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
+{
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ return NULL;
+}
+
+static long _freq_tbl_determine_rate(struct clk_hw *hw,
+ const struct freq_tbl *f, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ f = find_freq(f, rate);
+ if (!f)
+ return -EINVAL;
+
+ *p = clk_get_parent_by_index(hw->clk, f->src);
+ *p_rate = __clk_get_rate(*p);
+
+ return f->freq;
+}
+
+static long clk_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+
+ return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
+}
+
+static long clk_dyn_rcg_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+
+ return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
+}
+
+static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg *rcg = to_clk_rcg(hw);
+ unsigned long flags;
+ const struct freq_tbl *f;
+ u32 ns, md, ctl;
+ struct mn *mn = &rcg->mn;
+
+ f = find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ spin_lock_irqsave(rcg->lock, flags);
+
+ ns = readl_relaxed(rcg->ns_reg);
+ ns |= BIT(mn->mnctr_reset_bit);
+ writel_relaxed(ns, rcg->ns_reg);
+
+ md = readl_relaxed(rcg->md_reg);
+ md = mn_to_md(mn, f->m, f->n, md);
+ writel_relaxed(md, rcg->md_reg);
+
+ /* MN counter mode is in ctl_reg sometimes */
+ if (rcg->ctl_reg != rcg->ns_reg) {
+ ctl = readl_relaxed(rcg->ctl_reg);
+ ctl = mn_to_reg(mn, f->m, f->n, ctl);
+ writel_relaxed(ctl, rcg->ctl_reg);
+ } else {
+ ns = mn_to_reg(mn, f->m, f->n, ns);
+ }
+ ns = mn_to_ns(mn, f->m, f->n, ns);
+ ns = pre_div_to_ns(&rcg->p, f->pre_div - 1, ns);
+ writel_relaxed(ns, rcg->ns_reg);
+
+ ns &= ~BIT(mn->mnctr_reset_bit);
+ writel(ns, rcg->ns_reg);
+
+ spin_unlock_irqrestore(rcg->lock, flags);
+
+ return 0;
+}
+
+static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
+ const struct freq_tbl *f;
+
+ f = find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ configure_bank(rcg, f);
+
+ return 0;
+}
+
+static int clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return __clk_dyn_rcg_set_rate(hw, rate);
+}
+
+static int clk_dyn_rcg_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ return __clk_dyn_rcg_set_rate(hw, rate);
+}
+
+static const struct clk_ops clk_rcg_ops = {
+ .enable = clk_rcg_enable,
+ .disable = clk_rcg_disable,
+ .get_parent = clk_rcg_get_parent,
+ .set_parent = clk_rcg_set_parent,
+ .recalc_rate = clk_rcg_recalc_rate,
+ .determine_rate = clk_rcg_determine_rate,
+ .set_rate = clk_rcg_set_rate,
+};
+
+struct clk *rcg_clk_register(struct device *dev, struct rcg_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init,
+ u8 pre_div_width, u8 mnd_width)
+{
+ struct clk_rcg *r;
+
+ r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return ERR_PTR(-ENOMEM);
+
+ r->ctl_reg = desc->base + desc->ctl_reg;
+ r->ns_reg = desc->base + desc->ns_reg;
+ r->md_reg = desc->base + desc->md_reg;
+ r->ctl_bit = desc->ctl_bit;
+ r->mn.mnctr_en_bit = desc->mnctr_en_bit;
+ r->mn.mnctr_reset_bit = desc->mnctr_reset_bit;
+ r->mn.mnctr_mode_shift = desc->mnctr_mode_shift;
+ r->mn.n_val_shift = desc->n_val_shift;
+ r->mn.m_val_shift = desc->m_val_shift;
+ r->p.pre_div_shift = desc->pre_div_shift;
+ r->p.pre_div_width = pre_div_width;
+ r->s.src_sel_shift = desc->src_sel_shift;
+ r->s.parent_map = desc->parent_map;
+ r->freq_tbl = desc->freq_tbl;
+ r->lock = lock;
+ r->mn.width = mnd_width;
+
+ init->ops = &clk_rcg_ops;
+ init->flags |= CLK_SET_RATE_GATE;
+ r->hw.init = init;
+
+ return devm_clk_register(dev, &r->hw);
+}
+
+static const struct clk_ops clk_dyn_rcg_ops = {
+ .enable = clk_dyn_rcg_enable,
+ .is_enabled = clk_dyn_rcg_is_enabled,
+ .disable = clk_dyn_rcg_disable,
+ .get_parent = clk_dyn_rcg_get_parent,
+ .set_parent = clk_dyn_rcg_set_parent,
+ .recalc_rate = clk_dyn_rcg_recalc_rate,
+ .determine_rate = clk_dyn_rcg_determine_rate,
+ .set_rate = clk_dyn_rcg_set_rate,
+ .set_rate_and_parent = clk_dyn_rcg_set_rate_and_parent,
+};
+
+struct clk *
+rcg_dyn_clk_register(struct device *dev, struct rcg_dyn_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init, u8 pre_div_width,
+ u8 mnd_width)
+{
+ struct clk_dyn_rcg *r;
+
+ r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return ERR_PTR(-ENOMEM);
+
+ r->ctl_reg = desc->base + desc->ctl_reg;
+ r->ns_reg = desc->base + desc->ns_reg;
+ r->ctl_bit = desc->ctl_bit;
+ if (mnd_width) {
+ r->md_reg[0] = desc->base + desc->md0_reg;
+ r->md_reg[1] = desc->base + desc->md1_reg;
+ r->mn[0].mnctr_en_bit = desc->mnctr0_en_bit;
+ r->mn[0].mnctr_reset_bit = desc->mnctr0_reset_bit;
+ r->mn[0].mnctr_mode_shift = desc->mnctr0_mode_shift;
+ r->mn[0].n_val_shift = desc->n0_val_shift;
+ r->mn[0].m_val_shift = desc->m0_val_shift;
+ r->mn[0].width = mnd_width;
+ r->mn[1].mnctr_en_bit = desc->mnctr1_en_bit;
+ r->mn[1].mnctr_reset_bit = desc->mnctr1_reset_bit;
+ r->mn[1].mnctr_mode_shift = desc->mnctr1_mode_shift;
+ r->mn[1].n_val_shift = desc->n1_val_shift;
+ r->mn[1].m_val_shift = desc->m1_val_shift;
+ r->mn[1].width = mnd_width;
+ }
+ if (pre_div_width) {
+ r->p[0].pre_div_shift = desc->pre_div0_shift;
+ r->p[0].pre_div_width = pre_div_width;
+ r->p[1].pre_div_shift = desc->pre_div1_shift;
+ r->p[1].pre_div_width = pre_div_width;
+ }
+ r->s[0].src_sel_shift = desc->src0_sel_shift;
+ r->s[0].parent_map = desc->parent_map;
+ r->s[1].src_sel_shift = desc->src1_sel_shift;
+ r->s[1].parent_map = desc->parent_map;
+ r->mux_sel_bit = desc->mux_sel_bit;
+ r->freq_tbl = desc->freq_tbl;
+ r->lock = lock;
+
+ init->ops = &clk_dyn_rcg_ops;
+ r->hw.init = init;
+
+ return devm_clk_register(dev, &r->hw);
+}
diff --git a/drivers/clk/msm/clk-rcg.h b/drivers/clk/msm/clk-rcg.h
new file mode 100644
index 0000000..9cc572d
--- /dev/null
+++ b/drivers/clk/msm/clk-rcg.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CLK_RCG_H__
+#define __MSM_CLK_RCG_H__
+
+struct device;
+struct clk;
+struct clk_init_data;
+
+struct freq_tbl {
+ unsigned long freq;
+ u8 src;
+ u8 pre_div;
+ u16 m;
+ u16 n;
+};
+
+struct rcg_desc {
+ void __iomem *base;
+ u32 ctl_reg;
+ u32 ns_reg;
+ u32 md_reg;
+
+ u8 ctl_bit;
+ u8 mnctr_en_bit;
+ u8 mnctr_reset_bit;
+ u8 mnctr_mode_shift;
+ u8 pre_div_shift;
+ u8 src_sel_shift;
+ u8 n_val_shift;
+ u8 m_val_shift;
+
+ u8 *parent_map;
+ struct freq_tbl *freq_tbl;
+};
+
+extern struct clk *rcg_clk_register(struct device *dev, struct rcg_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init,
+ u8 pre_div_width, u8 mnd_width);
+
+#define rcg_p2mn8_clk_register(dev, desc, lock, init) \
+ rcg_clk_register(dev, desc, lock, init, 2, 8)
+#define rcg_p2mn16_clk_register(dev, desc, lock, init) \
+ rcg_clk_register(dev, desc, lock, init, 2, 16)
+
+struct rcg_dyn_desc {
+ void __iomem *base;
+ u32 ctl_reg;
+ u32 ns_reg;
+ u32 md0_reg;
+ u32 md1_reg;
+
+ u8 ctl_bit;
+ u8 mnctr0_en_bit;
+ u8 mnctr1_en_bit;
+ u8 mnctr0_reset_bit;
+ u8 mnctr1_reset_bit;
+ u8 mnctr0_mode_shift;
+ u8 mnctr1_mode_shift;
+ u8 pre_div0_shift;
+ u8 pre_div1_shift;
+ u8 src0_sel_shift;
+ u8 src1_sel_shift;
+ u8 n0_val_shift;
+ u8 n1_val_shift;
+ u8 m0_val_shift;
+ u8 m1_val_shift;
+ u8 mux_sel_bit;
+
+ u8 *parent_map;
+ struct freq_tbl *freq_tbl;
+};
+
+struct clk *rcg_dyn_clk_register(struct device *dev,
+ struct rcg_dyn_desc *desc, spinlock_t *lock,
+ struct clk_init_data *init, u8 pre_div_width, u8 mnd_width);
+
+#define rcg_mn4_dyn_clk_register(dev, desc, lock, init) \
+ rcg_dyn_clk_register(dev, desc, lock, init, 0, 4)
+#define rcg_mn8_dyn_clk_register(dev, desc, lock, init) \
+ rcg_dyn_clk_register(dev, desc, lock, init, 0, 8)
+#define rcg_p4_dyn_clk_register(dev, desc, lock, init) \
+ rcg_dyn_clk_register(dev, desc, lock, init, 4, 0)
+
+struct rcg2_desc {
+ void __iomem *base;
+ u32 cmd_rcgr;
+ u8 *parent_map;
+ struct freq_tbl *freq_tbl;
+};
+
+extern struct clk *rcg2_clk_register(struct device *dev, struct rcg2_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init,
+ u8 hid_width, u8 mnd_width);
+
+#define rcg_h5_clk_register(dev, desc, lock, init) \
+ rcg2_clk_register(dev, desc, lock, init, 5, 0)
+#define rcg_h5mn8_clk_register(dev, desc, lock, init) \
+ rcg2_clk_register(dev, desc, lock, init, 5, 8)
+#define rcg_h5mn16_clk_register(dev, desc, lock, init) \
+ rcg2_clk_register(dev, desc, lock, init, 5, 16)
+
+#endif
diff --git a/drivers/clk/msm/clk-rcg2.c b/drivers/clk/msm/clk-rcg2.c
new file mode 100644
index 0000000..b8daf87
--- /dev/null
+++ b/drivers/clk/msm/clk-rcg2.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <asm/div64.h>
+
+#include "clk-rcg.h"
+
+#define CMD_REG 0x0
+#define CMD_UPDATE BIT(0)
+#define CMD_ROOT_EN BIT(1)
+#define CMD_DIRTY_CFG BIT(4)
+#define CMD_DIRTY_N BIT(5)
+#define CMD_DIRTY_M BIT(6)
+#define CMD_DIRTY_D BIT(7)
+#define CMD_ROOT_OFF BIT(31)
+
+#define CFG_REG 0x4
+#define CFG_SRC_DIV_SHIFT 0
+#define CFG_SRC_SEL_SHIFT 8
+#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
+#define CFG_MODE_SHIFT 12
+#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
+#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
+
+#define M_REG 0x8
+#define N_REG 0xc
+#define D_REG 0x10
+
+/**
+ * struct clk_rcg2 - root clock generator
+ *
+ * @base: corresponds to *_CMD_RCGR
+ * @mnd_width: number of bits in m/n/d values
+ * @hid_width: number of bits in half integer divider
+ * @parent_map: map from software's parent index to hardware's src_sel field
+ * @freq_tbl: Frequency table
+ * @hw: handle between common and hardware-specific interfaces
+ * @lock: register lock
+ *
+ */
+struct clk_rcg2 {
+ void __iomem *base;
+ u8 mnd_width;
+ u8 hid_width;
+ u8 *parent_map;
+ const struct freq_tbl *freq_tbl;
+ struct clk_hw hw;
+ spinlock_t *lock;
+};
+
+#define to_clk_rcg2(_hw) container_of(_hw, struct clk_rcg2, hw)
+
+static int clk_rcg2_is_enabled(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ u32 cmd;
+
+ cmd = readl_relaxed(rcg->base + CMD_REG);
+ cmd &= CMD_ROOT_OFF;
+
+ return cmd ? 0 : 1;
+}
+
+static u8 clk_rcg2_get_parent(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+ u32 cfg;
+ int i;
+
+ cfg = readl_relaxed(rcg->base + CFG_REG);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++)
+ if (cfg == rcg->parent_map[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static void update_config(struct clk_rcg2 *rcg)
+{
+ int count;
+ u32 cmd;
+ const char *name = __clk_get_name(rcg->hw.clk);
+
+ cmd = readl_relaxed(rcg->base + CMD_REG);
+ cmd |= CMD_UPDATE;
+ writel_relaxed(cmd, rcg->base + CMD_REG);
+
+ /* Wait for update to take effect */
+ for (count = 500; count > 0; count--) {
+ if (!(readl_relaxed(rcg->base + CMD_REG) & CMD_UPDATE))
+ return;
+ udelay(1);
+ }
+
+ WARN(1, "%s: rcg didn't update its configuration.", name);
+}
+
+static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long flags;
+ u32 cfg;
+
+ spin_lock_irqsave(rcg->lock, flags);
+
+ cfg = readl_relaxed(rcg->base + CFG_REG);
+ cfg &= ~CFG_SRC_SEL_MASK;
+ cfg |= rcg->parent_map[index] << CFG_SRC_SEL_SHIFT;
+ writel(cfg, rcg->base + CFG_REG);
+
+ update_config(rcg);
+
+ spin_unlock_irqrestore(rcg->lock, flags);
+
+ return 0;
+}
+
+/*
+ * Calculate m/n:d rate
+ *
+ * parent_rate m
+ * rate = ----------- x ---
+ * hid_div n
+ */
+static unsigned long
+calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
+{
+ if (hid_div) {
+ rate *= 2;
+ rate /= hid_div + 1;
+ }
+
+ if (mode) {
+ u64 tmp = rate;
+ tmp *= m;
+ do_div(tmp, n);
+ rate = tmp;
+ }
+
+ return rate;
+}
+
+static unsigned long
+clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
+
+ cfg = readl_relaxed(rcg->base + CFG_REG);
+
+ if (rcg->mnd_width) {
+ mask = BIT(rcg->mnd_width) - 1;
+ m = readl_relaxed(rcg->base + M_REG);
+ m &= mask;
+ n = readl_relaxed(rcg->base + N_REG);
+ n = ~n;
+ n &= mask;
+ n += m;
+ mode = cfg & CFG_MODE_MASK;
+ mode >>= CFG_MODE_SHIFT;
+ }
+
+ mask = BIT(rcg->hid_width) - 1;
+ hid_div = cfg >> CFG_SRC_DIV_SHIFT;
+ hid_div &= mask;
+
+ return calc_rate(parent_rate, m, n, mode, hid_div);
+}
+
+static const
+struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate)
+{
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ return NULL;
+}
+
+static long _freq_tbl_determine_rate(struct clk_hw *hw,
+ const struct freq_tbl *f, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ f = find_freq(f, rate);
+ if (!f)
+ return -EINVAL;
+
+ *p = clk_get_parent_by_index(hw->clk, f->src);
+ *p_rate = __clk_get_rate(*p);
+
+ return f->freq;
+}
+
+static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
+}
+
+static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long flags;
+ const struct freq_tbl *f;
+ u32 cfg, m, n, d, mask, val;
+
+ f = find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ spin_lock_irqsave(rcg->lock, flags);
+
+ cfg = readl_relaxed(rcg->base + CFG_REG);
+ mask = BIT(rcg->hid_width) - 1;
+ mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
+ cfg &= ~mask;
+
+ if (rcg->mnd_width && f->n) {
+ mask = BIT(rcg->mnd_width) - 1;
+ m = readl_relaxed(rcg->base + M_REG);
+ m &= ~mask;
+ m |= f->m;
+ writel_relaxed(m, rcg->base + M_REG);
+
+ val = readl_relaxed(rcg->base + M_REG);
+ val &= ~mask;
+ n = f->n - f->m;
+ n = ~n;
+ n &= mask;
+ val |= n;
+ writel_relaxed(val, rcg->base + N_REG);
+
+ d = readl_relaxed(rcg->base + D_REG);
+ d &= ~mask;
+ d |= ~f->n & mask;
+ writel_relaxed(d, rcg->base + D_REG);
+
+ cfg |= CFG_MODE_DUAL_EDGE;
+ }
+
+ cfg |= f->pre_div << CFG_SRC_DIV_SHIFT;
+ cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT;
+ writel_relaxed(cfg, rcg->base + CFG_REG);
+
+ update_config(rcg);
+
+ spin_unlock_irqrestore(rcg->lock, flags);
+
+ return 0;
+}
+
+static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return __clk_rcg2_set_rate(hw, rate);
+}
+
+static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ return __clk_rcg2_set_rate(hw, rate);
+}
+
+static const struct clk_ops clk_rcg2_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .determine_rate = clk_rcg2_determine_rate,
+ .set_rate = clk_rcg2_set_rate,
+ .set_rate_and_parent = clk_rcg2_set_rate_and_parent,
+};
+
+struct clk *rcg2_clk_register(struct device *dev, struct rcg2_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init,
+ u8 hid_width, u8 mnd_width)
+{
+ struct clk_rcg2 *r;
+
+ r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return ERR_PTR(-ENOMEM);
+
+ r->base = desc->base + desc->cmd_rcgr;
+ r->parent_map = desc->parent_map;
+ r->freq_tbl = desc->freq_tbl;
+ r->lock = lock;
+ r->mnd_width = mnd_width;
+ r->hid_width = hid_width;
+
+ init->ops = &clk_rcg2_ops;
+ r->hw.init = init;
+
+ return devm_clk_register(dev, &r->hw);
+}
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Add a clock driver that registers clocks from a DT node's
'clocks' child. Each new SoC will add a file describing the
software interface and frequency plan to drivers/clk/msm/ and
then hook that into the msm_cc_match_table by means of a
compatible string and an msm_clk_match table.
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/clk/msm/Makefile | 2 +
drivers/clk/msm/core.c | 265 +++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/msm/internal.h | 24 ++++
3 files changed, 291 insertions(+)
create mode 100644 drivers/clk/msm/core.c
create mode 100644 drivers/clk/msm/internal.h
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index e1cee29..9cfd0d7 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -4,3 +4,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
+
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
new file mode 100644
index 0000000..b1904c0
--- /dev/null
+++ b/drivers/clk/msm/core.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+
+#include "internal.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+
+struct cc_data {
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+static struct clk *
+dispatch_fixed_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ u32 rate;
+ const char *name = m->init_data->name;
+
+ if (of_property_read_u32(m->of_node, "clock-frequency", &rate))
+ return ERR_PTR(-EINVAL);
+
+ return clk_register_fixed_rate(dev, name, NULL, CLK_IS_ROOT, rate);
+}
+
+static struct clk *
+dispatch_pll_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct pll_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return pll_clk_register(dev, desc, m->init_data);
+}
+
+static struct clk *
+dispatch_pll_vote_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct pll_vote_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return pll_vote_clk_register(dev, desc, m->init_data);
+}
+
+static struct clk *
+dispatch_rcg_p2mn16_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_p2mn16_clk_register(dev, desc, &cc->lock, m->init_data);
+}
+
+static struct clk *
+dispatch_rcg_p2mn8_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_p2mn8_clk_register(dev, desc, &cc->lock, m->init_data);
+}
+
+static struct clk *
+dispatch_rcg_mn8_dyn_clk(struct of_clk_match *match, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg_dyn_desc *desc = match->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_mn8_dyn_clk_register(dev, desc, &cc->lock,
+ match->init_data);
+}
+
+static struct clk *
+dispatch_rcg_p4_dyn_clk(struct of_clk_match *match, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg_dyn_desc *desc = match->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_p4_dyn_clk_register(dev, desc, &cc->lock,
+ match->init_data);
+}
+
+static struct clk *
+dispatch_rcg_h5mn8_clk(struct of_clk_match *match, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg2_desc *desc = match->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_h5mn8_clk_register(dev, desc, &cc->lock, match->init_data);
+}
+
+static struct clk *
+dispatch_rcg_h5mn16_clk(struct of_clk_match *match, struct device *dev,
+ struct cc_data *cc)
+{
+ struct rcg2_desc *desc = match->driver_data;
+
+ desc->base = cc->base;
+
+ return rcg_h5mn16_clk_register(dev, desc, &cc->lock, match->init_data);
+}
+
+static struct clk *
+dispatch_branch_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct branch_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return branch_clk_register(dev, desc, &cc->lock, m->init_data);
+}
+
+static struct clk *
+dispatch_branch_hg_clk(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc)
+{
+ struct branch_desc *desc = m->driver_data;
+
+ desc->base = cc->base;
+
+ return branch_hg_clk_register(dev, desc, &cc->lock, m->init_data);
+}
+
+static const struct of_device_id dispatch_table[] = {
+ { .compatible = "fixed-clock", .data = dispatch_fixed_clk },
+ { .compatible = "qcom,pll", .data = dispatch_pll_clk },
+ { .compatible = "qcom,pll-vote", .data = dispatch_pll_vote_clk },
+ { .compatible = "qcom,p2-mn8-clock", .data = dispatch_rcg_p2mn8_clk },
+ { .compatible = "qcom,p2-mn16-clock", .data = dispatch_rcg_p2mn16_clk },
+ { .compatible = "qcom,mn8-dyn-clock", .data = dispatch_rcg_mn8_dyn_clk },
+ { .compatible = "qcom,p4-dyn-clock", .data = dispatch_rcg_p4_dyn_clk },
+ { .compatible = "qcom,h5-mn8-clock", .data = dispatch_rcg_h5mn8_clk },
+ { .compatible = "qcom,h5-mn16-clock", .data = dispatch_rcg_h5mn16_clk },
+ { .compatible = "qcom,cxc-clock", .data = dispatch_branch_clk },
+ { .compatible = "qcom,cxc-hg-clock", .data = dispatch_branch_hg_clk },
+};
+
+typedef struct clk *
+(*clk_dispatch_fn)(struct of_clk_match *m, struct device *dev,
+ struct cc_data *cc);
+
+static const struct of_device_id msm_cc_match_table[] = {
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_cc_match_table);
+
+static int msm_cc_probe(struct platform_device *pdev)
+{
+ struct cc_data *cc;
+ void __iomem *base;
+ struct resource *res;
+ int count, i;
+ const struct of_device_id *id_entry;
+ const struct msm_clk_match *m;
+ struct of_clk_match *matches;
+ size_t size;
+
+ id_entry = of_match_device(msm_cc_match_table, &pdev->dev);
+ m = id_entry->data;
+ matches = m->matches;
+ size = m->size;
+
+ cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ cc->base = base;
+ spin_lock_init(&cc->lock);
+
+ count = of_clk_match(&pdev->dev, matches, size);
+ if (count < 0)
+ return count;
+
+ for (i = 0; i < size; i++) {
+ struct of_clk_match *match;
+ const struct of_device_id *id;
+ struct device_node *np;
+ clk_dispatch_fn fn;
+ struct clk *clk;
+ int err;
+
+ match = &matches[i];
+ np = match->of_node;
+ if (!np)
+ continue;
+
+ id = of_match_node(dispatch_table, match->of_node);
+ if (!id)
+ continue;
+
+ fn = id->data;
+ clk = fn(match, &pdev->dev, cc);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static struct platform_driver msm_cc_driver = {
+ .probe = msm_cc_probe,
+ .driver = {
+ .name = "msm-clock",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cc_match_table,
+ },
+};
+
+static int __init msm_cc_init(void)
+{
+ return platform_driver_register(&msm_cc_driver);
+}
+core_initcall(msm_cc_init);
+
+static void __exit msm_cc_exit(void)
+{
+ platform_driver_unregister(&msm_cc_driver);
+}
+module_exit(msm_cc_exit);
+
+MODULE_DESCRIPTION("MSM Clock Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msm-cc");
diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
new file mode 100644
index 0000000..177bd3b
--- /dev/null
+++ b/drivers/clk/msm/internal.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CLK_INTERNAL_H__
+#define __MSM_CLK_INTERNAL_H__
+
+struct of_clk_match;
+
+struct msm_clk_match {
+ struct of_clk_match *matches;
+ size_t size;
+};
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Consolidate DT parsing for the common bits of a clock binding in
one place to simplify clock drivers. This also has the added
benefit of standardizing how the clock names used by the common
clock framework are generated from the DT bindings. We always use
the first clock-output-names string if it exists, otherwise we
fall back to the node name.
To be slightly more efficient and make the caller's life easier,
we introduce a shallow copy flag so that the clock core knows to
just copy the pointers to the strings and not the string
contents. Otherwise the callers of this function would have to
free the strings allocated here which could be cumbersome.
Cc: Rob Herring <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
drivers/clk/clk.c | 59 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/clk-provider.h | 7 ++++++
2 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1ed9bdd..ea8e951b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1809,6 +1809,10 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
{
int i, ret;
+ hw->clk = clk;
+ if (hw->init->flags & CLK_SHALLOW_COPY)
+ return PTR_RET(__clk_register(dev, hw));
+
clk->name = kstrdup(hw->init->name, GFP_KERNEL);
if (!clk->name) {
pr_err("%s: could not allocate clk->name\n", __func__);
@@ -1819,7 +1823,6 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
clk->hw = hw;
clk->flags = hw->init->flags;
clk->num_parents = hw->init->num_parents;
- hw->clk = clk;
/* allocate local copy in case parent_names is __initdata */
clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents),
@@ -2232,4 +2235,58 @@ void __init of_clk_init(const struct of_device_id *matches)
clk_init_cb(np);
}
}
+
+/**
+ * of_init_clk_data() - Initialize a clk_init_data struct from a DT node
+ * @np: node to initialize struct from
+ * @init: struct to initialize
+ *
+ * Populates the clk_init_data struct by parsing the device node for
+ * properties matching the common clock binding. Returns 0 on success
+ * and a negative error code on failure.
+ */
+int of_init_clk_data(struct device_node *np, struct clk_init_data *init)
+{
+ struct of_phandle_args s;
+ const char **names = NULL, **p;
+ const char *name;
+ int i;
+
+ if (of_property_read_string(np, "clock-output-names", &name) < 0)
+ name = np->name;
+ init->name = kstrdup(name, GFP_KERNEL);
+ if (!init->name)
+ return -ENOMEM;
+
+ for (i = 0; of_parse_phandle_with_args(np, "clocks", "#clock-cells",
+ i, &s) == 0; i++) {
+ p = krealloc(names, sizeof(*names) * (i + 1), GFP_KERNEL);
+ if (!p)
+ goto err;
+ names = p;
+
+ if (of_property_read_string(s.np, "clock-output-names",
+ &name) < 0)
+ name = s.np->name;
+ names[i] = kstrdup(name, GFP_KERNEL);
+ if (!names[i])
+ goto err;
+ of_node_put(s.np);
+ }
+
+ init->parent_names = names;
+ init->num_parents = i;
+ init->flags = init->num_parents ? 0 : CLK_IS_ROOT;
+ init->flags |= CLK_SHALLOW_COPY;
+
+ return 0;
+err:
+ of_node_put(s.np);
+ while (--i >= 0)
+ kfree(names[i]);
+ kfree(names);
+ kfree(init->name);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(of_init_clk_data);
#endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 9861cee..18d6362 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -28,6 +28,7 @@
#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */
#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
+#define CLK_SHALLOW_COPY BIT(8) /* don't copy the initdata strings */
struct clk_hw;
@@ -472,6 +473,7 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
const char *of_clk_get_parent_name(struct device_node *np, int index);
void of_clk_init(const struct of_device_id *matches);
+int of_init_clk_data(struct device_node *np, struct clk_init_data *init);
#else /* !CONFIG_OF */
@@ -501,6 +503,11 @@ static inline const char *of_clk_get_parent_name(struct device_node *np,
}
#define of_clk_init(matches) \
{ while (0); }
+static inline int
+of_init_clk_data(struct device_node *np, struct clk_init_data *init)
+{
+ return 0;
+}
#endif /* CONFIG_OF */
#endif /* CONFIG_COMMON_CLK */
#endif /* CLK_PROVIDER_H */
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Cc: [email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
Documentation/devicetree/bindings/clock/msm.txt | 28 ++++
drivers/clk/msm/Makefile | 1 +
drivers/clk/msm/clk-branch.c | 190 ++++++++++++++++++++++++
drivers/clk/msm/clk-branch.h | 48 ++++++
4 files changed, 267 insertions(+)
create mode 100644 drivers/clk/msm/clk-branch.c
create mode 100644 drivers/clk/msm/clk-branch.h
diff --git a/Documentation/devicetree/bindings/clock/msm.txt b/Documentation/devicetree/bindings/clock/msm.txt
index f4595fa..6840398 100644
--- a/Documentation/devicetree/bindings/clock/msm.txt
+++ b/Documentation/devicetree/bindings/clock/msm.txt
@@ -69,3 +69,31 @@ Example:
clocks = <&pxo>, <&pll2>, <&pll8>;
};
+CXC Binding
+-----------
+
+Required properties:
+- compatible : shall be "qcom,cxc-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the source being gated
+
+Example:
+ gsbi5_uart_clk: gsbi5_uart_cxc {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-clock";
+ clocks = <&gsbi5_uart_rcg>;
+ };
+
+CXC With Hardware Gating Binding
+--------------------------------
+
+Required properties:
+- compatible : shall be "qcom,cxc-hg-clock".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the source being gated
+
+ gsbi5_uart_ahb: gsbi5_uart_ahb {
+ #clock-cells = <0>;
+ compatible = "qcom,cxc-hg-clock";
+ clocks = <&cfpb_clk>;
+ };
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index fb78ac9..e1cee29 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_COMMON_CLK_MSM) += clk-msm.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg.o
clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
diff --git a/drivers/clk/msm/clk-branch.c b/drivers/clk/msm/clk-branch.c
new file mode 100644
index 0000000..ff2e599
--- /dev/null
+++ b/drivers/clk/msm/clk-branch.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include "clk-branch.h"
+
+/**
+ * struct clk_branch - gating clock with status bit and dynamic hardware gating
+ *
+ * @ctl_reg: clock control register
+ * @ctl_bit: ORed with @ctl_reg to enable the clock
+ * @hwcg_reg: dynamic hardware clock gating register
+ * @hwcg_bit: ORed with @hwcg_reg to enable dynamic hardware clock gating
+ * @halt_reg: halt register
+ * @halt_bit: ANDed with @halt_reg to test for clock halted
+ * @halt_check: type of halt checking to perform
+ * @hw: handle between common and hardware-specific interfaces
+ * @lock: register lock
+ *
+ * Clock which can gate its output.
+ */
+struct clk_branch {
+ void __iomem *ctl_reg;
+ void __iomem *hwcg_reg;
+ void __iomem *halt_reg;
+
+ u8 ctl_bit;
+ u8 hwcg_bit;
+ u8 halt_bit;
+ u8 halt_check;
+
+ struct clk_hw hw;
+ spinlock_t *lock;
+};
+
+#define to_clk_branch(_hw) container_of(_hw, struct clk_branch, hw)
+
+static bool clk_branch_is_halted(const struct clk_branch *br)
+{
+ bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
+ u32 status_bit = readl_relaxed(br->halt_reg) & BIT(br->halt_bit);
+ return invert ? !status_bit : status_bit;
+}
+
+static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
+{
+ if (!br->hwcg_reg)
+ return 0;
+
+ return !!(readl_relaxed(br->hwcg_reg) & BIT(br->hwcg_bit));
+}
+
+static int clk_branch_wait(const struct clk_branch *br, bool enabling)
+{
+ bool voted = br->halt_check & BRANCH_VOTED;
+ const char *name = __clk_get_name(br->hw.clk);
+
+ /* Skip checking halt bit if the clock is in hardware gated mode */
+ if (clk_branch_in_hwcg_mode(br))
+ return 0;
+
+ if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
+ udelay(10);
+ } else if (br->halt_check == BRANCH_HALT_ENABLE ||
+ br->halt_check == BRANCH_HALT ||
+ (enabling && voted)) {
+ int count = 200;
+
+ while (count-- > 0) {
+ if (clk_branch_is_halted(br) == !enabling)
+ return 0;
+ udelay(1);
+ }
+ WARN("%s status stuck at 'o%s'", name, enabling ? "ff" : "n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int clk_branch_toggle(const struct clk_hw *hw, bool en)
+{
+ struct clk_branch *br = to_clk_branch(hw);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(br->lock, flags);
+
+ val = readl_relaxed(br->ctl_reg);
+ if (en)
+ val |= BIT(br->ctl_bit);
+ else
+ val &= ~BIT(br->ctl_bit);
+ writel(val, br->ctl_reg);
+
+ spin_unlock_irqrestore(br->lock, flags);
+
+ return clk_branch_wait(br, en);
+}
+
+static int clk_branch_enable(struct clk_hw *hw)
+{
+ return clk_branch_toggle(hw, true);
+}
+
+static void clk_branch_disable(struct clk_hw *hw)
+{
+ clk_branch_toggle(hw, false);
+}
+
+static int clk_branch_is_enabled(struct clk_hw *hw)
+{
+ struct clk_branch *br = to_clk_branch(hw);
+ u32 val;
+
+ val = readl_relaxed(br->ctl_reg);
+ val &= BIT(br->ctl_bit);
+
+ return val ? 1 : 0;
+}
+
+static const struct clk_ops clk_branch_ops = {
+ .enable = clk_branch_enable,
+ .is_enabled = clk_branch_is_enabled,
+ .disable = clk_branch_disable,
+};
+
+struct clk *branch_clk_register(struct device *dev, struct branch_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init)
+{
+ struct clk_branch *b;
+
+ b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
+ if (!b)
+ return ERR_PTR(-ENOMEM);
+
+ b->ctl_reg = desc->base + desc->ctl_reg;
+ b->halt_reg = desc->base + desc->halt_reg;
+ b->ctl_bit = desc->ctl_bit;
+ b->halt_bit = desc->halt_bit;
+ b->halt_check = desc->halt_check;
+ b->lock = lock;
+
+ init->ops = &clk_branch_ops;
+ init->flags |= CLK_SET_RATE_PARENT;
+ b->hw.init = init;
+
+ return devm_clk_register(dev, &b->hw);
+}
+
+struct clk *branch_hg_clk_register(struct device *dev, struct branch_desc *desc,
+ spinlock_t *lock, struct clk_init_data *init)
+{
+ struct clk_branch *b;
+
+ b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
+ if (!b)
+ return ERR_PTR(-ENOMEM);
+
+ b->ctl_reg = desc->base + desc->ctl_reg;
+ b->halt_reg = desc->base + desc->halt_reg;
+ b->hwcg_reg = desc->base + desc->hwcg_reg;
+ b->ctl_bit = desc->ctl_bit;
+ b->halt_bit = desc->halt_bit;
+ b->halt_check = desc->halt_check;
+ b->lock = lock;
+
+ init->ops = &clk_branch_ops;
+ init->flags |= CLK_SET_RATE_PARENT;
+ b->hw.init = init;
+
+ return devm_clk_register(dev, &b->hw);
+}
diff --git a/drivers/clk/msm/clk-branch.h b/drivers/clk/msm/clk-branch.h
new file mode 100644
index 0000000..bcb7345
--- /dev/null
+++ b/drivers/clk/msm/clk-branch.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CLK_BRANCH_H__
+#define __MSM_CLK_BRANCH_H__
+
+#include <linux/spinlock.h>
+
+struct device;
+struct clk;
+struct clk_init_data;
+
+struct branch_desc {
+ void __iomem *base;
+ u32 ctl_reg;
+ u32 hwcg_reg;
+ u32 halt_reg;
+
+ u8 ctl_bit;
+ u8 hwcg_bit;
+ u8 halt_bit;
+ u8 halt_check;
+#define BRANCH_VOTED BIT(7) /* Delay on disable */
+#define BRANCH_HALT 0 /* pol: 1 = halt */
+#define BRANCH_HALT_VOTED (BRANCH_HALT | BRANCH_VOTED)
+#define BRANCH_HALT_ENABLE 1 /* pol: 0 = halt */
+#define BRANCH_HALT_ENABLE_VOTED (BRANCH_HALT_ENABLE | BRANCH_VOTED)
+#define BRANCH_HALT_DELAY 2 /* No bit to check; just delay */
+};
+
+extern struct clk *branch_clk_register(struct device *dev,
+ struct branch_desc *desc, spinlock_t *lock,
+ struct clk_init_data *init);
+extern struct clk *branch_hg_clk_register(struct device *dev,
+ struct branch_desc *desc, spinlock_t *lock,
+ struct clk_init_data *init);
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Add support for MSM's PLLs (phase locked loops). This is
sufficient enough to be able to determine the rate the PLL is
running at. We can add rate setting support later when it's
needed.
Cc: [email protected]
Signed-off-by: Stephen Boyd <[email protected]>
---
Documentation/devicetree/bindings/clock/msm.txt | 40 ++++
drivers/clk/Kconfig | 2 +
drivers/clk/Makefile | 1 +
drivers/clk/msm/Kconfig | 4 +
drivers/clk/msm/Makefile | 3 +
drivers/clk/msm/clk-pll.c | 233 ++++++++++++++++++++++++
drivers/clk/msm/clk-pll.h | 43 +++++
7 files changed, 326 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/msm.txt
create mode 100644 drivers/clk/msm/Kconfig
create mode 100644 drivers/clk/msm/Makefile
create mode 100644 drivers/clk/msm/clk-pll.c
create mode 100644 drivers/clk/msm/clk-pll.h
diff --git a/Documentation/devicetree/bindings/clock/msm.txt b/Documentation/devicetree/bindings/clock/msm.txt
new file mode 100644
index 0000000..2192621
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/msm.txt
@@ -0,0 +1,40 @@
+Bindings for Qualcomm's clock controllers
+
+These bindings use the common clock binding
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+PLL Binding
+-----------
+
+Required properties:
+- compatible : shall be "qcom,pll".
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the reference clock
+
+Example:
+ pll8: pll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll";
+ clocks = <&pxo>;
+ };
+
+Voteable PLL Binding
+--------------------
+
+Voteable PLLs are PLLs surrounded by a voting wrapper that aggregates
+votes from multiple masters in the system and enables or disables the
+PLL according to the current vote.
+
+Required properties:
+- compatible: shall be "qcom,pll-vote"
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : from common clock binding; shall be set to the pll that is wrapped
+ in voting logic
+
+Example:
+ vpll8: vpll8 {
+ #clock-cells = <0>;
+ compatible = "qcom,pll-vote";
+ clocks = <&pll8>;
+ };
+
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 51380d6..fad596b8 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -87,6 +87,8 @@ config CLK_PPC_CORENET
This adds the clock driver support for Freescale PowerPC corenet
platforms using common clock framework.
+source "drivers/clk/msm/Kconfig"
+
endmenu
source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4038c2b..67df66f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o
+obj-$(CONFIG_COMMON_CLK_MSM) += msm/
obj-$(CONFIG_PLAT_ORION) += mvebu/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
new file mode 100644
index 0000000..bf7e3d2
--- /dev/null
+++ b/drivers/clk/msm/Kconfig
@@ -0,0 +1,4 @@
+menuconfig COMMON_CLK_MSM
+ tristate "Support for Qualcomm's MSM designs"
+ depends on OF
+
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
new file mode 100644
index 0000000..16b750f
--- /dev/null
+++ b/drivers/clk/msm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_COMMON_CLK_MSM) += clk-msm.o
+
+clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
diff --git a/drivers/clk/msm/clk-pll.c b/drivers/clk/msm/clk-pll.c
new file mode 100644
index 0000000..03c2c41
--- /dev/null
+++ b/drivers/clk/msm/clk-pll.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <asm/div64.h>
+
+#include "clk-pll.h"
+
+/**
+ * struct clk_pll - phase locked loop (PLL)
+ * @l_reg: L register
+ * @m_reg: M register
+ * @n_reg: N register
+ * @config_reg: config register
+ * @mode_reg: mode register
+ * @status_reg: status register
+ * @status_bit: ANDed with @status_reg to determine if PLL is enabled
+ * @hw: handle between common and hardware-specific interfaces
+ */
+struct clk_pll {
+ void __iomem *l_reg;
+ void __iomem *m_reg;
+ void __iomem *n_reg;
+ void __iomem *config_reg;
+ void __iomem *mode_reg;
+ void __iomem *status_reg;
+ u8 status_bit;
+
+ struct clk_hw hw;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mode;
+
+ mode = readl_relaxed(pll->mode_reg);
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel(mode, pll->mode_reg);
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel(mode, pll->mode_reg);
+
+ /* Wait until PLL is locked. */
+ udelay(50);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel(mode, pll->mode_reg);
+
+ return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mode;
+
+ mode = readl_relaxed(pll->mode_reg);
+ mode &= ~(PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL);
+ writel_relaxed(mode, pll->mode_reg);
+}
+
+static unsigned long
+clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 l, m, n;
+ unsigned long rate;
+ u64 tmp;
+
+ l = readl_relaxed(pll->l_reg) & 0x3ff;
+ m = readl_relaxed(pll->m_reg) & 0x7ffff;
+ n = readl_relaxed(pll->n_reg) & 0x7ffff;
+
+ rate = parent_rate * l;
+ if (n) {
+ tmp = parent_rate;
+ tmp *= m;
+ do_div(tmp, n);
+ rate += tmp;
+ }
+ return rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+struct clk *pll_clk_register(struct device *dev, struct pll_desc *desc,
+ struct clk_init_data *init)
+{
+ struct clk_pll *p;
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->l_reg = desc->base + desc->l_reg;
+ p->m_reg = desc->base + desc->m_reg;
+ p->n_reg = desc->base + desc->n_reg;
+ p->config_reg = desc->base + desc->config_reg;
+ p->mode_reg = desc->base + desc->mode_reg;
+ p->status_reg = desc->base + desc->status_reg;
+ p->status_bit = desc->status_bit;
+
+ init->ops = &clk_pll_ops;
+ p->hw.init = init;
+
+ return devm_clk_register(dev, &p->hw);
+}
+
+/**
+ * struct clk_pll_vote - phase locked loop (PLL) with hardware voting wrapper
+ * @vote_reg: Voting register
+ * @vote_bit: ORed into @vote_reg to enable PLL
+ * @hw: handle between common and hardware-specific interfaces
+ */
+struct clk_pll_vote {
+ void __iomem *vote_reg;
+ u8 vote_bit;
+ struct clk_hw hw;
+};
+
+#define to_clk_pll_vote(_hw) container_of(_hw, struct clk_pll_vote, hw)
+
+static DEFINE_SPINLOCK(pll_vote_lock);
+
+static int wait_for_pll(struct clk_pll *pll)
+{
+ int count;
+ const char *name = __clk_get_name(pll->hw.clk);
+
+ /* Wait for pll to enable. */
+ for (count = 200; count > 0; count--) {
+ if (readl_relaxed(pll->status_reg) & BIT(pll->status_bit))
+ return 0;
+ udelay(1);
+ }
+
+ WARN("%s didn't enable after voting for it!\n", name);
+ return -ETIMEDOUT;
+}
+
+static int clk_pll_vote_enable(struct clk_hw *hw)
+{
+ u32 val;
+ unsigned long flags;
+ struct clk_pll_vote *pll = to_clk_pll_vote(hw);
+ struct clk_pll *p = to_clk_pll(__clk_get_hw(__clk_get_parent(hw->clk)));
+
+ spin_lock_irqsave(&pll_vote_lock, flags);
+
+ val = readl_relaxed(pll->vote_reg);
+ val |= BIT(pll->vote_bit);
+ writel(val, pll->vote_reg);
+
+ spin_unlock_irqrestore(&pll_vote_lock, flags);
+
+ return wait_for_pll(p);
+}
+
+static void clk_pll_vote_disable(struct clk_hw *hw)
+{
+ u32 val;
+ unsigned long flags;
+ struct clk_pll_vote *pll = to_clk_pll_vote(hw);
+
+ spin_lock_irqsave(&pll_vote_lock, flags);
+
+ val = readl_relaxed(pll->vote_reg);
+ val &= ~BIT(pll->vote_bit);
+ writel_relaxed(val, pll->vote_reg);
+
+ spin_unlock_irqrestore(&pll_vote_lock, flags);
+}
+
+static const struct clk_ops clk_pll_vote_ops = {
+ .enable = clk_pll_vote_enable,
+ .disable = clk_pll_vote_disable,
+};
+
+struct clk *pll_vote_clk_register(struct device *dev,
+ struct pll_vote_desc *desc, struct clk_init_data *init)
+{
+ struct clk_pll_vote *p;
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->vote_reg = desc->base + desc->vote_reg;
+ p->vote_bit = desc->vote_bit;
+
+ init->ops = &clk_pll_vote_ops;
+ p->hw.init = init;
+
+ return devm_clk_register(dev, &p->hw);
+}
diff --git a/drivers/clk/msm/clk-pll.h b/drivers/clk/msm/clk-pll.h
new file mode 100644
index 0000000..4e63a5e
--- /dev/null
+++ b/drivers/clk/msm/clk-pll.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CLK_PLL_H__
+#define __MSM_CLK_PLL_H__
+
+struct device;
+struct clk;
+struct clk_init_data;
+
+struct pll_desc {
+ void __iomem *base;
+ u16 l_reg;
+ u16 m_reg;
+ u16 n_reg;
+ u16 config_reg;
+ u16 mode_reg;
+ u16 status_reg;
+ u8 status_bit;
+};
+
+struct pll_vote_desc {
+ void __iomem *base;
+ u16 vote_reg;
+ u8 vote_bit;
+};
+
+extern struct clk *pll_clk_register(struct device *dev, struct pll_desc *desc,
+ struct clk_init_data *init);
+extern struct clk *pll_vote_clk_register(struct device *dev,
+ struct pll_vote_desc *desc, struct clk_init_data *init);
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
Hi Stephen,
On Wednesday 24 of July 2013 17:43:31 Stephen Boyd wrote:
> In similar fashion as of_regulator_match() add an of_clk_match()
> function that finds an initializes clock init_data structs from
> devicetree. Drivers should use this API to find clocks that their
> device is providing and then iterate over their match table
> registering the clocks with the init data parsed.
I think all you need here is declaring all your clock drivers using
CLK_OF_DECLARE() macro. Then they will be automatically probed by
of_clk_init(). See last lines of drivers/clk/clk-fixed-rate.c for an
example.
Best regards,
Tomasz
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> drivers/clk/clk.c | 64
> ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/clk-provider.h | 16 +++++++++++
> 2 files changed, 80 insertions(+)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index ea8e951b..1e3e0db 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -2289,4 +2289,68 @@ err:
> return -ENOMEM;
> }
> EXPORT_SYMBOL_GPL(of_init_clk_data);
> +
> +/**
> + * of_clk_match() - Scan and match clock providers from the DT node of
> a device + * @dev: device initializing clock providers
> + * @matches: match table of clocks
> + * @num_matches: number of entries in @matches
> + *
> + * This function uses a match table specified by the clock driver to
> parse + * clock init data from the device tree. @dev is expected to be
> a device with + * a 'clocks' child node containing a set of child
> nodes, each providing the + * init data for one clock. The data parsed
> from a child node will be matched + * to a clock based on the child
> node's name. Note that the match table is + * modified in place.
> + *
> + * Returns the number of matches found or a negative error code on
> failure. + */
> +int of_clk_match(struct device *dev, struct of_clk_match *matches,
> + int num_matches)
> +{
> + int count = 0;
> + int err, i;
> + struct device_node *np, *clocks;
> + struct clk_init_data *init;
> +
> + clocks = of_find_node_by_name(dev->of_node, "clocks");
> + if (!clocks)
> + return -EINVAL;
> +
> + for (i = 0; i < num_matches; i++) {
> + struct of_clk_match *match = &matches[i];
> + match->init_data = NULL;
> + match->of_node = NULL;
> + }
> +
> + for_each_available_child_of_node(clocks, np) {
> + for (i = 0; i < num_matches; i++) {
> + struct of_clk_match *match = &matches[i];
> + if (match->of_node)
> + continue;
> +
> + if (strcmp(match->name, np->name))
> + continue;
> +
> + init = devm_kzalloc(dev, sizeof(*init),
GFP_KERNEL);
> + if (!init)
> + return -ENOMEM;
> +
> + err = of_init_clk_data(np, init);
> + if (err) {
> + dev_err(dev,
> + "failed to parse DT for clock
%s\n",
> + np->name);
> + return err;
> + }
> + match->of_node = np;
> + match->init_data = init;
> + count++;
> + break;
> + }
> + }
> +
> + return count;
> +}
> +EXPORT_SYMBOL_GPL(of_clk_match);
> #endif
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 18d6362..484f8ad 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -461,6 +461,13 @@ struct clk_onecell_data {
> __used __section(__clk_of_table) \
> = { .compatible = compat, .data = fn };
>
> +struct of_clk_match {
> + const char *name;
> + void *driver_data;
> + struct clk_init_data *init_data;
> + struct device_node *of_node;
> +};
> +
> #ifdef CONFIG_OF
> int of_clk_add_provider(struct device_node *np,
> struct clk *(*clk_src_get)(struct of_phandle_args
*args,
> @@ -475,6 +482,9 @@ const char *of_clk_get_parent_name(struct
> device_node *np, int index); void of_clk_init(const struct of_device_id
> *matches);
> int of_init_clk_data(struct device_node *np, struct clk_init_data
> *init);
>
> +int of_clk_match(struct device *dev, struct of_clk_match *matches,
> + int num_matches);
> +
> #else /* !CONFIG_OF */
>
> static inline int of_clk_add_provider(struct device_node *np,
> @@ -508,6 +518,12 @@ of_init_clk_data(struct device_node *np, struct
> clk_init_data *init) {
> return 0;
> }
> +static inline int
> +of_clk_match(struct device *dev, struct of_clk_match *matches, int
> num_matches) +{
> +
> + return 0;
> +}
> #endif /* CONFIG_OF */
> #endif /* CONFIG_COMMON_CLK */
> #endif /* CLK_PROVIDER_H */
Hi Stephen,
Pretty good idea, but I have some comments inline.
On Wednesday 24 of July 2013 17:43:30 Stephen Boyd wrote:
> Consolidate DT parsing for the common bits of a clock binding in
> one place to simplify clock drivers. This also has the added
> benefit of standardizing how the clock names used by the common
> clock framework are generated from the DT bindings. We always use
> the first clock-output-names string if it exists, otherwise we
> fall back to the node name.
>
> To be slightly more efficient and make the caller's life easier,
> we introduce a shallow copy flag so that the clock core knows to
> just copy the pointers to the strings and not the string
> contents. Otherwise the callers of this function would have to
> free the strings allocated here which could be cumbersome.
>
> Cc: Rob Herring <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> drivers/clk/clk.c | 59
> +++++++++++++++++++++++++++++++++++++++++++-
> include/linux/clk-provider.h | 7 ++++++
> 2 files changed, 65 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 1ed9bdd..ea8e951b 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1809,6 +1809,10 @@ static int _clk_register(struct device *dev,
> struct clk_hw *hw, struct clk *clk) {
> int i, ret;
>
> + hw->clk = clk;
> + if (hw->init->flags & CLK_SHALLOW_COPY)
> + return PTR_RET(__clk_register(dev, hw));
> +
> clk->name = kstrdup(hw->init->name, GFP_KERNEL);
> if (!clk->name) {
> pr_err("%s: could not allocate clk->name\n", __func__);
> @@ -1819,7 +1823,6 @@ static int _clk_register(struct device *dev,
> struct clk_hw *hw, struct clk *clk) clk->hw = hw;
> clk->flags = hw->init->flags;
> clk->num_parents = hw->init->num_parents;
> - hw->clk = clk;
>
> /* allocate local copy in case parent_names is __initdata */
> clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents),
> @@ -2232,4 +2235,58 @@ void __init of_clk_init(const struct of_device_id
> *matches) clk_init_cb(np);
> }
> }
> +
> +/**
> + * of_init_clk_data() - Initialize a clk_init_data struct from a DT
> node + * @np: node to initialize struct from
> + * @init: struct to initialize
> + *
> + * Populates the clk_init_data struct by parsing the device node for
> + * properties matching the common clock binding. Returns 0 on success
> + * and a negative error code on failure.
> + */
> +int of_init_clk_data(struct device_node *np, struct clk_init_data
> *init) +{
> + struct of_phandle_args s;
> + const char **names = NULL, **p;
> + const char *name;
> + int i;
> +
> + if (of_property_read_string(np, "clock-output-names", &name) < 0)
> + name = np->name;
> + init->name = kstrdup(name, GFP_KERNEL);
> + if (!init->name)
> + return -ENOMEM;
> +
> + for (i = 0; of_parse_phandle_with_args(np, "clocks", "#clock-
cells",
> + i, &s) == 0; i++) {
> + p = krealloc(names, sizeof(*names) * (i + 1), GFP_KERNEL);
> + if (!p)
> + goto err;
What about using of_count_phandle_with_args() first to get the parent
count?
> + names = p;
> +
> + if (of_property_read_string(s.np, "clock-output-names",
> + &name) < 0)
> + name = s.np->name;
You should be able to use of_clk_get_parent_name() here, instead of
parsing the properties directly.
Best regards,
Tomasz
On Wednesday 24 of July 2013 17:43:32 Stephen Boyd wrote:
> Some of Qualcomm's clocks can change their parent and rate at the
> same time with a single register write. Add support for this
> hardware to the common clock framework by adding a new
> set_rate_and_parent() op. When the clock framework determines
> that both the parent and the rate are going to change during
> clk_set_rate() it will call the .set_rate_and_parent() op if
> available and fall back to calling .set_parent() followed by
> .set_rate() otherwise.
This is strange. Does you hardware support switching parent and rate
separately or you always need to set both and so all the fuss here?
If the latter is the case, then maybe you can simply keep parent index and
rate cached inside driver data of your clock driver and use them on any
.set_rate() or .set_parent() calls?
I'm not really sure if we want such oddities to be handled inside of
common clock framework. Mike, what do you think?
Best regards,
Tomasz
> Cc: James Hogan <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> Documentation/clk.txt | 3 ++
> drivers/clk/clk.c | 78
> +++++++++++++++++++++++++++++++++-----------
> include/linux/clk-provider.h | 15 +++++++++
> 3 files changed, 77 insertions(+), 19 deletions(-)
>
> diff --git a/Documentation/clk.txt b/Documentation/clk.txt
> index 3aeb5c4..79700ea 100644
> --- a/Documentation/clk.txt
> +++ b/Documentation/clk.txt
> @@ -77,6 +77,9 @@ the operations defined in clk.h:
> int (*set_parent)(struct clk_hw *hw, u8
index);
> u8 (*get_parent)(struct clk_hw *hw);
> int (*set_rate)(struct clk_hw *hw, unsigned
long);
> + int (*set_rate_and_parent)(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate, u8
index);
> void (*init)(struct clk_hw *hw);
> };
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 1e3e0db..73de07c 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1121,10 +1121,9 @@ static void clk_reparent(struct clk *clk, struct
> clk *new_parent) clk->parent = new_parent;
> }
>
> -static int __clk_set_parent(struct clk *clk, struct clk *parent, u8
> p_index) +static struct clk *__clk_set_parent_before(struct clk *clk,
> struct clk *parent) {
> unsigned long flags;
> - int ret = 0;
> struct clk *old_parent = clk->parent;
>
> /*
> @@ -1155,6 +1154,34 @@ static int __clk_set_parent(struct clk *clk,
> struct clk *parent, u8 p_index) clk_reparent(clk, parent);
> clk_enable_unlock(flags);
>
> + return old_parent;
> +}
> +
> +static void __clk_set_parent_after(struct clk *clk, struct clk *parent,
> + struct clk *old_parent)
> +{
> + /*
> + * Finish the migration of prepare state and undo the changes done
> + * for preventing a race with clk_enable().
> + */
> + if (clk->prepare_count) {
> + clk_disable(clk);
> + clk_disable(old_parent);
> + __clk_unprepare(old_parent);
> + }
> +
> + /* update debugfs with new clk tree topology */
> + clk_debug_reparent(clk, parent);
> +}
> +
> +static int __clk_set_parent(struct clk *clk, struct clk *parent, u8
> p_index) +{
> + unsigned long flags;
> + int ret = 0;
> + struct clk *old_parent;
> +
> + old_parent = __clk_set_parent_before(clk, parent);
> +
> /* change clock input source */
> if (parent && clk->ops->set_parent)
> ret = clk->ops->set_parent(clk->hw, p_index);
> @@ -1172,18 +1199,8 @@ static int __clk_set_parent(struct clk *clk,
> struct clk *parent, u8 p_index) return ret;
> }
>
> - /*
> - * Finish the migration of prepare state and undo the changes done
> - * for preventing a race with clk_enable().
> - */
> - if (clk->prepare_count) {
> - clk_disable(clk);
> - clk_disable(old_parent);
> - __clk_unprepare(old_parent);
> - }
> + __clk_set_parent_after(clk, parent, old_parent);
>
> - /* update debugfs with new clk tree topology */
> - clk_debug_reparent(clk, parent);
> return 0;
> }
>
> @@ -1368,17 +1385,32 @@ static void clk_change_rate(struct clk *clk)
> struct clk *child;
> unsigned long old_rate;
> unsigned long best_parent_rate = 0;
> + bool skip_set_rate = false;
> + struct clk *old_parent;
>
> old_rate = clk->rate;
>
> - /* set parent */
> - if (clk->new_parent && clk->new_parent != clk->parent)
> - __clk_set_parent(clk, clk->new_parent, clk-
>new_parent_index);
> -
> - if (clk->parent)
> + if (clk->new_parent)
> + best_parent_rate = clk->new_parent->rate;
> + else if (clk->parent)
> best_parent_rate = clk->parent->rate;
>
> - if (clk->ops->set_rate)
> + if (clk->new_parent && clk->new_parent != clk->parent) {
> + old_parent = __clk_set_parent_before(clk, clk-
>new_parent);
> +
> + if (clk->ops->set_rate_and_parent) {
> + skip_set_rate = true;
> + clk->ops->set_rate_and_parent(clk->hw, clk-
>new_rate,
> + best_parent_rate,
> + clk->new_parent_index);
> + } else if (clk->ops->set_parent) {
> + clk->ops->set_parent(clk->hw, clk-
>new_parent_index);
> + }
> +
> + __clk_set_parent_after(clk, clk->new_parent, old_parent);
> + }
> +
> + if (!skip_set_rate && clk->ops->set_rate)
> clk->ops->set_rate(clk->hw, clk->new_rate,
best_parent_rate);
>
> if (clk->ops->recalc_rate)
> @@ -1664,6 +1696,14 @@ int __clk_init(struct device *dev, struct clk
> *clk) goto out;
> }
>
> + if (clk->ops->set_rate_and_parent &&
> + !(clk->ops->set_parent && clk->ops->set_rate)) {
> + pr_warning("%s: %s must implement .set_parent &
.set_rate\n",
> + __func__, clk->name);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> /* throw a WARN if any entries in parent_names are NULL */
> for (i = 0; i < clk->num_parents; i++)
> WARN(!clk->parent_names[i],
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 484f8ad..1f7eabb 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -108,6 +108,18 @@ struct clk_hw;
> * which is likely helpful for most .set_rate implementation.
> * Returns 0 on success, -EERROR otherwise.
> *
> + * @set_rate_and_parent: Change the rate and the parent of this clock.
> The + * requested rate is specified by the second
argument, which +
> * should typically be the return of .round_rate call. The
> + * third argument gives the parent rate which is likely
helpful
> + * for most .set_rate_and_parent implementation. The fourth
> + * argument gives the parent index. It is optional (and
> + * unnecessary) for clocks with 0 or 1 parents as well as
> + * for clocks that can tolerate switching the rate and the
parent
> + * separately via calls to .set_parent and .set_rate.
> + * Returns 0 on success, -EERROR otherwise.
> + *
> + *
> * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
> * implementations to split any work between atomic (enable) and
> sleepable * (prepare) contexts. If enabling a clock requires code that
> might sleep, @@ -139,6 +151,9 @@ struct clk_ops {
> u8 (*get_parent)(struct clk_hw *hw);
> int (*set_rate)(struct clk_hw *hw, unsigned long,
> unsigned long);
> + int (*set_rate_and_parent)(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate, u8 index);
> void (*init)(struct clk_hw *hw);
> };
Hi Stephen,
On Wednesday 24 of July 2013 17:43:33 Stephen Boyd wrote:
> Add support for MSM's PLLs (phase locked loops). This is
> sufficient enough to be able to determine the rate the PLL is
> running at. We can add rate setting support later when it's
> needed.
>
> Cc: [email protected]
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> Documentation/devicetree/bindings/clock/msm.txt | 40 ++++
> drivers/clk/Kconfig | 2 +
> drivers/clk/Makefile | 1 +
> drivers/clk/msm/Kconfig | 4 +
> drivers/clk/msm/Makefile | 3 +
> drivers/clk/msm/clk-pll.c | 233
> ++++++++++++++++++++++++ drivers/clk/msm/clk-pll.h
> | 43 +++++
> 7 files changed, 326 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/msm.txt
> create mode 100644 drivers/clk/msm/Kconfig
> create mode 100644 drivers/clk/msm/Makefile
> create mode 100644 drivers/clk/msm/clk-pll.c
> create mode 100644 drivers/clk/msm/clk-pll.h
[snip]
> diff --git a/drivers/clk/msm/clk-pll.h b/drivers/clk/msm/clk-pll.h
> new file mode 100644
> index 0000000..4e63a5e
> --- /dev/null
> +++ b/drivers/clk/msm/clk-pll.h
> @@ -0,0 +1,43 @@
> +/*
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms. + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __MSM_CLK_PLL_H__
> +#define __MSM_CLK_PLL_H__
> +
> +struct device;
> +struct clk;
> +struct clk_init_data;
> +
> +struct pll_desc {
> + void __iomem *base;
> + u16 l_reg;
> + u16 m_reg;
> + u16 n_reg;
> + u16 config_reg;
> + u16 mode_reg;
> + u16 status_reg;
> + u8 status_bit;
> +};
> +
> +struct pll_vote_desc {
> + void __iomem *base;
> + u16 vote_reg;
> + u8 vote_bit;
> +};
> +
> +extern struct clk *pll_clk_register(struct device *dev, struct pll_desc
> *desc, + struct clk_init_data *init);
> +extern struct clk *pll_vote_clk_register(struct device *dev,
> + struct pll_vote_desc *desc, struct clk_init_data *init);
I don't think such generic name is good here. What about prefixing them
with msm_ or qcom_ string?
Best regards,
Tomasz
Hi Stephen,
On Wednesday 24 of July 2013 17:43:36 Stephen Boyd wrote:
> Add a clock driver that registers clocks from a DT node's
> 'clocks' child. Each new SoC will add a file describing the
> software interface and frequency plan to drivers/clk/msm/ and
> then hook that into the msm_cc_match_table by means of a
> compatible string and an msm_clk_match table.
>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> drivers/clk/msm/Makefile | 2 +
> drivers/clk/msm/core.c | 265
> +++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/msm/internal.h | 24 ++++
> 3 files changed, 291 insertions(+)
> create mode 100644 drivers/clk/msm/core.c
> create mode 100644 drivers/clk/msm/internal.h
>
> diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
> index e1cee29..9cfd0d7 100644
> --- a/drivers/clk/msm/Makefile
> +++ b/drivers/clk/msm/Makefile
> @@ -4,3 +4,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg.o
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
> +
> +clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
> diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
> new file mode 100644
> index 0000000..b1904c0
> --- /dev/null
> +++ b/drivers/clk/msm/core.c
> @@ -0,0 +1,265 @@
> +/*
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms. + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/spinlock.h>
> +
> +#include "internal.h"
> +#include "clk-pll.h"
> +#include "clk-rcg.h"
> +#include "clk-branch.h"
> +
> +struct cc_data {
> + void __iomem *base;
> + spinlock_t lock;
> +};
> +
> +static struct clk *
> +dispatch_fixed_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + u32 rate;
> + const char *name = m->init_data->name;
> +
> + if (of_property_read_u32(m->of_node, "clock-frequency", &rate))
> + return ERR_PTR(-EINVAL);
> +
> + return clk_register_fixed_rate(dev, name, NULL, CLK_IS_ROOT,
rate);
> +}
> +
> +static struct clk *
> +dispatch_pll_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct pll_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return pll_clk_register(dev, desc, m->init_data);
> +}
> +
> +static struct clk *
> +dispatch_pll_vote_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct pll_vote_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return pll_vote_clk_register(dev, desc, m->init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_p2mn16_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct rcg_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_p2mn16_clk_register(dev, desc, &cc->lock, m-
>init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_p2mn8_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct rcg_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_p2mn8_clk_register(dev, desc, &cc->lock, m->init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_mn8_dyn_clk(struct of_clk_match *match, struct device
> *dev, + struct cc_data *cc)
> +{
> + struct rcg_dyn_desc *desc = match->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_mn8_dyn_clk_register(dev, desc, &cc->lock,
> + match->init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_p4_dyn_clk(struct of_clk_match *match, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct rcg_dyn_desc *desc = match->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_p4_dyn_clk_register(dev, desc, &cc->lock,
> + match->init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_h5mn8_clk(struct of_clk_match *match, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct rcg2_desc *desc = match->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_h5mn8_clk_register(dev, desc, &cc->lock, match-
>init_data);
> +}
> +
> +static struct clk *
> +dispatch_rcg_h5mn16_clk(struct of_clk_match *match, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct rcg2_desc *desc = match->driver_data;
> +
> + desc->base = cc->base;
> +
> + return rcg_h5mn16_clk_register(dev, desc, &cc->lock,
> match->init_data); +}
> +
> +static struct clk *
> +dispatch_branch_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct branch_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return branch_clk_register(dev, desc, &cc->lock, m->init_data);
> +}
> +
> +static struct clk *
> +dispatch_branch_hg_clk(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc)
> +{
> + struct branch_desc *desc = m->driver_data;
> +
> + desc->base = cc->base;
> +
> + return branch_hg_clk_register(dev, desc, &cc->lock, m->init_data);
> +}
> +
> +static const struct of_device_id dispatch_table[] = {
> + { .compatible = "fixed-clock", .data = dispatch_fixed_clk },
This is already handled by the fixed-rate-clock driver and of_clk_init().
> + { .compatible = "qcom,pll", .data = dispatch_pll_clk },
> + { .compatible = "qcom,pll-vote", .data = dispatch_pll_vote_clk },
> + { .compatible = "qcom,p2-mn8-clock", .data =
dispatch_rcg_p2mn8_clk },
> + { .compatible = "qcom,p2-mn16-clock", .data =
dispatch_rcg_p2mn16_clk
> }, + { .compatible = "qcom,mn8-dyn-clock", .data =
> dispatch_rcg_mn8_dyn_clk }, + { .compatible = "qcom,p4-dyn-clock",
> .data = dispatch_rcg_p4_dyn_clk }, + { .compatible =
> "qcom,h5-mn8-clock", .data = dispatch_rcg_h5mn8_clk }, + {
.compatible
> = "qcom,h5-mn16-clock", .data = dispatch_rcg_h5mn16_clk }, + {
> .compatible = "qcom,cxc-clock", .data = dispatch_branch_clk }, + {
> .compatible = "qcom,cxc-hg-clock", .data = dispatch_branch_hg_clk },
> +};
You can avoid all the code above by using OF_CLK_DECLARE() and
of_clk_init().
> +typedef struct clk *
> +(*clk_dispatch_fn)(struct of_clk_match *m, struct device *dev,
> + struct cc_data *cc);
> +
> +static const struct of_device_id msm_cc_match_table[] = {
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, msm_cc_match_table);
> +
> +static int msm_cc_probe(struct platform_device *pdev)
> +{
> + struct cc_data *cc;
> + void __iomem *base;
> + struct resource *res;
> + int count, i;
> + const struct of_device_id *id_entry;
> + const struct msm_clk_match *m;
> + struct of_clk_match *matches;
> + size_t size;
> +
> + id_entry = of_match_device(msm_cc_match_table, &pdev->dev);
> + m = id_entry->data;
> + matches = m->matches;
> + size = m->size;
> +
> + cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
> + if (!cc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + cc->base = base;
> + spin_lock_init(&cc->lock);
> +
> + count = of_clk_match(&pdev->dev, matches, size);
> + if (count < 0)
> + return count;
> +
> + for (i = 0; i < size; i++) {
> + struct of_clk_match *match;
> + const struct of_device_id *id;
> + struct device_node *np;
> + clk_dispatch_fn fn;
> + struct clk *clk;
> + int err;
> +
> + match = &matches[i];
> + np = match->of_node;
> + if (!np)
> + continue;
> +
> + id = of_match_node(dispatch_table, match->of_node);
> + if (!id)
> + continue;
> +
> + fn = id->data;
> + clk = fn(match, &pdev->dev, cc);
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + err = of_clk_add_provider(np, of_clk_src_simple_get, clk);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static struct platform_driver msm_cc_driver = {
> + .probe = msm_cc_probe,
> + .driver = {
> + .name = "msm-clock",
> + .owner = THIS_MODULE,
> + .of_match_table = msm_cc_match_table,
> + },
> +};
> +
> +static int __init msm_cc_init(void)
> +{
> + return platform_driver_register(&msm_cc_driver);
> +}
> +core_initcall(msm_cc_init);
I might be saying something stupid, but is it okay for a removable module
to have a core_initcall?
Best regards,
Tomasz
On 07/25, Tomasz Figa wrote:
> Hi Stephen,
>
> On Wednesday 24 of July 2013 17:43:31 Stephen Boyd wrote:
> > In similar fashion as of_regulator_match() add an of_clk_match()
> > function that finds an initializes clock init_data structs from
> > devicetree. Drivers should use this API to find clocks that their
> > device is providing and then iterate over their match table
> > registering the clocks with the init data parsed.
>
> I think all you need here is declaring all your clock drivers using
> CLK_OF_DECLARE() macro. Then they will be automatically probed by
> of_clk_init(). See last lines of drivers/clk/clk-fixed-rate.c for an
> example.
>
CLK_OF_DECLARE() will not work because it doesn't give the
caller a struct device. I want that struct device to use managed
allocations and clk registrations.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 07/25, Tomasz Figa wrote:
> On Wednesday 24 of July 2013 17:43:30 Stephen Boyd wrote:
> > Consolidate DT parsing for the common bits of a clock binding in
> > one place to simplify clock drivers. This also has the added
> > benefit of standardizing how the clock names used by the common
> > clock framework are generated from the DT bindings. We always use
> > the first clock-output-names string if it exists, otherwise we
> > fall back to the node name.
> >
> > To be slightly more efficient and make the caller's life easier,
> > we introduce a shallow copy flag so that the clock core knows to
> > just copy the pointers to the strings and not the string
> > contents. Otherwise the callers of this function would have to
> > free the strings allocated here which could be cumbersome.
> >
> > Cc: Rob Herring <[email protected]>
> > Signed-off-by: Stephen Boyd <[email protected]>
> > ---
> > drivers/clk/clk.c | 59
> > +++++++++++++++++++++++++++++++++++++++++++-
> > include/linux/clk-provider.h | 7 ++++++
> > 2 files changed, 65 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index 1ed9bdd..ea8e951b 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -1809,6 +1809,10 @@ static int _clk_register(struct device *dev,
> > struct clk_hw *hw, struct clk *clk) {
> > int i, ret;
> >
> > + hw->clk = clk;
> > + if (hw->init->flags & CLK_SHALLOW_COPY)
> > + return PTR_RET(__clk_register(dev, hw));
> > +
> > clk->name = kstrdup(hw->init->name, GFP_KERNEL);
> > if (!clk->name) {
> > pr_err("%s: could not allocate clk->name\n", __func__);
> > @@ -1819,7 +1823,6 @@ static int _clk_register(struct device *dev,
> > struct clk_hw *hw, struct clk *clk) clk->hw = hw;
> > clk->flags = hw->init->flags;
> > clk->num_parents = hw->init->num_parents;
> > - hw->clk = clk;
> >
> > /* allocate local copy in case parent_names is __initdata */
> > clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents),
> > @@ -2232,4 +2235,58 @@ void __init of_clk_init(const struct of_device_id
> > *matches) clk_init_cb(np);
> > }
> > }
> > +
> > +/**
> > + * of_init_clk_data() - Initialize a clk_init_data struct from a DT
> > node + * @np: node to initialize struct from
> > + * @init: struct to initialize
> > + *
> > + * Populates the clk_init_data struct by parsing the device node for
> > + * properties matching the common clock binding. Returns 0 on success
> > + * and a negative error code on failure.
> > + */
> > +int of_init_clk_data(struct device_node *np, struct clk_init_data
> > *init) +{
> > + struct of_phandle_args s;
> > + const char **names = NULL, **p;
> > + const char *name;
> > + int i;
> > +
> > + if (of_property_read_string(np, "clock-output-names", &name) < 0)
> > + name = np->name;
> > + init->name = kstrdup(name, GFP_KERNEL);
> > + if (!init->name)
> > + return -ENOMEM;
> > +
> > + for (i = 0; of_parse_phandle_with_args(np, "clocks", "#clock-
> cells",
> > + i, &s) == 0; i++) {
> > + p = krealloc(names, sizeof(*names) * (i + 1), GFP_KERNEL);
> > + if (!p)
> > + goto err;
>
> What about using of_count_phandle_with_args() first to get the parent
> count?
Sure that simplifies things.
>
> > + names = p;
> > +
> > + if (of_property_read_string(s.np, "clock-output-names",
> > + &name) < 0)
> > + name = s.np->name;
>
> You should be able to use of_clk_get_parent_name() here, instead of
> parsing the properties directly.
This must be new too. Will do in v2.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 07/25, Tomasz Figa wrote:
> On Wednesday 24 of July 2013 17:43:33 Stephen Boyd wrote:
> > +extern struct clk *pll_clk_register(struct device *dev, struct pll_desc
> > *desc, + struct clk_init_data *init);
> > +extern struct clk *pll_vote_clk_register(struct device *dev,
> > + struct pll_vote_desc *desc, struct clk_init_data *init);
>
> I don't think such generic name is good here. What about prefixing them
> with msm_ or qcom_ string?
>
Sure.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 07/25, Tomasz Figa wrote:
> On Wednesday 24 of July 2013 17:43:36 Stephen Boyd wrote:
> > Add a clock driver that registers clocks from a DT node's
> > 'clocks' child. Each new SoC will add a file describing the
> > software interface and frequency plan to drivers/clk/msm/ and
> > then hook that into the msm_cc_match_table by means of a
> > compatible string and an msm_clk_match table.
> >
> > Signed-off-by: Stephen Boyd <[email protected]>
> > ---
> > drivers/clk/msm/Makefile | 2 +
> > drivers/clk/msm/core.c | 265
> > +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/clk/msm/internal.h | 24 ++++
> > 3 files changed, 291 insertions(+)
> > create mode 100644 drivers/clk/msm/core.c
> > create mode 100644 drivers/clk/msm/internal.h
> >
> > diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
> > index e1cee29..9cfd0d7 100644
> > --- a/drivers/clk/msm/Makefile
> > +++ b/drivers/clk/msm/Makefile
> > @@ -4,3 +4,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-pll.o
> > clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg.o
> > clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
> > clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
> > +
> > +clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
> > diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
> > new file mode 100644
> > index 0000000..b1904c0
> > --- /dev/null
> > +++ b/drivers/clk/msm/core.c
> > @@ -0,0 +1,265 @@
> > +/*
> > + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms. + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/module.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/spinlock.h>
> > +
> > +#include "internal.h"
> > +#include "clk-pll.h"
> > +#include "clk-rcg.h"
> > +#include "clk-branch.h"
> > +
> > +struct cc_data {
> > + void __iomem *base;
> > + spinlock_t lock;
> > +};
> > +
> > +static struct clk *
> > +dispatch_fixed_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + u32 rate;
> > + const char *name = m->init_data->name;
> > +
> > + if (of_property_read_u32(m->of_node, "clock-frequency", &rate))
> > + return ERR_PTR(-EINVAL);
> > +
> > + return clk_register_fixed_rate(dev, name, NULL, CLK_IS_ROOT,
> rate);
> > +}
> > +
> > +static struct clk *
> > +dispatch_pll_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct pll_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return pll_clk_register(dev, desc, m->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_pll_vote_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct pll_vote_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return pll_vote_clk_register(dev, desc, m->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_p2mn16_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct rcg_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_p2mn16_clk_register(dev, desc, &cc->lock, m-
> >init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_p2mn8_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct rcg_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_p2mn8_clk_register(dev, desc, &cc->lock, m->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_mn8_dyn_clk(struct of_clk_match *match, struct device
> > *dev, + struct cc_data *cc)
> > +{
> > + struct rcg_dyn_desc *desc = match->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_mn8_dyn_clk_register(dev, desc, &cc->lock,
> > + match->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_p4_dyn_clk(struct of_clk_match *match, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct rcg_dyn_desc *desc = match->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_p4_dyn_clk_register(dev, desc, &cc->lock,
> > + match->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_h5mn8_clk(struct of_clk_match *match, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct rcg2_desc *desc = match->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_h5mn8_clk_register(dev, desc, &cc->lock, match-
> >init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_rcg_h5mn16_clk(struct of_clk_match *match, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct rcg2_desc *desc = match->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return rcg_h5mn16_clk_register(dev, desc, &cc->lock,
> > match->init_data); +}
> > +
> > +static struct clk *
> > +dispatch_branch_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct branch_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return branch_clk_register(dev, desc, &cc->lock, m->init_data);
> > +}
> > +
> > +static struct clk *
> > +dispatch_branch_hg_clk(struct of_clk_match *m, struct device *dev,
> > + struct cc_data *cc)
> > +{
> > + struct branch_desc *desc = m->driver_data;
> > +
> > + desc->base = cc->base;
> > +
> > + return branch_hg_clk_register(dev, desc, &cc->lock, m->init_data);
> > +}
> > +
> > +static const struct of_device_id dispatch_table[] = {
> > + { .compatible = "fixed-clock", .data = dispatch_fixed_clk },
>
> This is already handled by the fixed-rate-clock driver and of_clk_init().
I don't plan to use of_clk_init() because I have actual device
drivers.
>
> > + { .compatible = "qcom,pll", .data = dispatch_pll_clk },
> > + { .compatible = "qcom,pll-vote", .data = dispatch_pll_vote_clk },
> > + { .compatible = "qcom,p2-mn8-clock", .data =
> dispatch_rcg_p2mn8_clk },
> > + { .compatible = "qcom,p2-mn16-clock", .data =
> dispatch_rcg_p2mn16_clk
> > }, + { .compatible = "qcom,mn8-dyn-clock", .data =
> > dispatch_rcg_mn8_dyn_clk }, + { .compatible = "qcom,p4-dyn-clock",
> > .data = dispatch_rcg_p4_dyn_clk }, + { .compatible =
> > "qcom,h5-mn8-clock", .data = dispatch_rcg_h5mn8_clk }, + {
> .compatible
> > = "qcom,h5-mn16-clock", .data = dispatch_rcg_h5mn16_clk }, + {
> > .compatible = "qcom,cxc-clock", .data = dispatch_branch_clk }, + {
> > .compatible = "qcom,cxc-hg-clock", .data = dispatch_branch_hg_clk },
> > +};
>
> You can avoid all the code above by using OF_CLK_DECLARE() and
> of_clk_init().
As stated earlier that won't work because I want a struct device.
> > +
> > +static int __init msm_cc_init(void)
> > +{
> > + return platform_driver_register(&msm_cc_driver);
> > +}
> > +core_initcall(msm_cc_init);
>
> I might be saying something stupid, but is it okay for a removable module
> to have a core_initcall?
>
If its compiled as a module the core_initcall becomes a
module_initcall(). It's here because I need the clocks before the
serial driver probes. If clocks supported deferred probing this
wouldn't be necessary and we could use module_platform_driver().
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 07/25, Tomasz Figa wrote:
> On Wednesday 24 of July 2013 17:43:32 Stephen Boyd wrote:
> > Some of Qualcomm's clocks can change their parent and rate at the
> > same time with a single register write. Add support for this
> > hardware to the common clock framework by adding a new
> > set_rate_and_parent() op. When the clock framework determines
> > that both the parent and the rate are going to change during
> > clk_set_rate() it will call the .set_rate_and_parent() op if
> > available and fall back to calling .set_parent() followed by
> > .set_rate() otherwise.
>
> This is strange. Does you hardware support switching parent and rate
> separately or you always need to set both and so all the fuss here?
It supports setting the parent or setting the rate, or setting
both at the same time.
>
> If the latter is the case, then maybe you can simply keep parent index and
> rate cached inside driver data of your clock driver and use them on any
> .set_rate() or .set_parent() calls?
This will not work. In fact, doing that would cause us to
overclock hardware for a short time between switching the parent
and the rate.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
Quoting Stephen Boyd (2013-07-25 09:36:56)
> On 07/25, Tomasz Figa wrote:
> > On Wednesday 24 of July 2013 17:43:30 Stephen Boyd wrote:
> > > Consolidate DT parsing for the common bits of a clock binding in
> > > one place to simplify clock drivers. This also has the added
> > > benefit of standardizing how the clock names used by the common
> > > clock framework are generated from the DT bindings. We always use
> > > the first clock-output-names string if it exists, otherwise we
> > > fall back to the node name.
> > >
> > > To be slightly more efficient and make the caller's life easier,
> > > we introduce a shallow copy flag so that the clock core knows to
> > > just copy the pointers to the strings and not the string
> > > contents. Otherwise the callers of this function would have to
> > > free the strings allocated here which could be cumbersome.
> > >
> > > Cc: Rob Herring <[email protected]>
> > > Signed-off-by: Stephen Boyd <[email protected]>
> > > ---
> > > drivers/clk/clk.c | 59
> > > +++++++++++++++++++++++++++++++++++++++++++-
> > > include/linux/clk-provider.h | 7 ++++++
> > > 2 files changed, 65 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > > index 1ed9bdd..ea8e951b 100644
> > > --- a/drivers/clk/clk.c
> > > +++ b/drivers/clk/clk.c
> > > @@ -1809,6 +1809,10 @@ static int _clk_register(struct device *dev,
> > > struct clk_hw *hw, struct clk *clk) {
> > > int i, ret;
> > >
> > > + hw->clk = clk;
> > > + if (hw->init->flags & CLK_SHALLOW_COPY)
> > > + return PTR_RET(__clk_register(dev, hw));
> > > +
> > > clk->name = kstrdup(hw->init->name, GFP_KERNEL);
> > > if (!clk->name) {
> > > pr_err("%s: could not allocate clk->name\n", __func__);
> > > @@ -1819,7 +1823,6 @@ static int _clk_register(struct device *dev,
> > > struct clk_hw *hw, struct clk *clk) clk->hw = hw;
> > > clk->flags = hw->init->flags;
> > > clk->num_parents = hw->init->num_parents;
> > > - hw->clk = clk;
> > >
> > > /* allocate local copy in case parent_names is __initdata */
> > > clk->parent_names = kzalloc((sizeof(char*) * clk->num_parents),
> > > @@ -2232,4 +2235,58 @@ void __init of_clk_init(const struct of_device_id
> > > *matches) clk_init_cb(np);
> > > }
> > > }
> > > +
> > > +/**
> > > + * of_init_clk_data() - Initialize a clk_init_data struct from a DT
> > > node + * @np: node to initialize struct from
> > > + * @init: struct to initialize
> > > + *
> > > + * Populates the clk_init_data struct by parsing the device node for
> > > + * properties matching the common clock binding. Returns 0 on success
> > > + * and a negative error code on failure.
> > > + */
> > > +int of_init_clk_data(struct device_node *np, struct clk_init_data
> > > *init) +{
> > > + struct of_phandle_args s;
> > > + const char **names = NULL, **p;
> > > + const char *name;
> > > + int i;
> > > +
> > > + if (of_property_read_string(np, "clock-output-names", &name) < 0)
> > > + name = np->name;
> > > + init->name = kstrdup(name, GFP_KERNEL);
> > > + if (!init->name)
> > > + return -ENOMEM;
> > > +
> > > + for (i = 0; of_parse_phandle_with_args(np, "clocks", "#clock-
> > cells",
> > > + i, &s) == 0; i++) {
> > > + p = krealloc(names, sizeof(*names) * (i + 1), GFP_KERNEL);
> > > + if (!p)
> > > + goto err;
> >
> > What about using of_count_phandle_with_args() first to get the parent
> > count?
>
> Sure that simplifies things.
I made a simple wrapper around of_count_phandle_with_args for the basic
DT bindings series:
http://lists.infradead.org/pipermail/linux-arm-kernel/2013-June/178836.html
I still need to make a couple of changes to that before I merge it. If
you want to base on top of the v3 version then you can use it, otherwise
just use of_count_phandle_with_args.
Regards,
Mike
>
> >
> > > + names = p;
> > > +
> > > + if (of_property_read_string(s.np, "clock-output-names",
> > > + &name) < 0)
> > > + name = s.np->name;
> >
> > You should be able to use of_clk_get_parent_name() here, instead of
> > parsing the properties directly.
>
> This must be new too. Will do in v2.
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> hosted by The Linux Foundation
Hi Stephen,
On Thu, Jul 25, 2013 at 01:43:37AM +0100, Stephen Boyd wrote:
> Fill in the data and wire up the global clock controller to the
> MSM clock driver. This should allow most non-multimedia device
> drivers to control their clocks on 8960 based platforms.
>
> Cc: [email protected]
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> .../devicetree/bindings/clock/qcom,gcc.txt | 55 +++++++
> drivers/clk/msm/Kconfig | 10 ++
> drivers/clk/msm/Makefile | 2 +
> drivers/clk/msm/core.c | 3 +
> drivers/clk/msm/gcc-8960.c | 174 +++++++++++++++++++++
> drivers/clk/msm/internal.h | 2 +
> 6 files changed, 246 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc.txt
> create mode 100644 drivers/clk/msm/gcc-8960.c
>
> diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> new file mode 100644
> index 0000000..2311e1a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> @@ -0,0 +1,55 @@
> +MSM Global Clock Controller Binding
> +-----------------------------------
> +
> +Required properties :
> +- compatible : shall contain at least "qcom,gcc" and only one of the
> + following:
> +
> + "qcom,gcc-8660"
> + "qcom,gcc-8960"
> +
> +- reg : shall contain base register location and length
> +- clocks : shall contain clocks supplied by the clock controller
> +
> +Example:
> + clock-controller@900000 {
> + compatible = "qcom,gcc-8960", "qcom,gcc";
> + reg = <0x900000 0x4000>;
> +
> + clocks {
> + pxo: pxo {
> + #clock-cells = <0>;
> + compatible = "fixed-clock";
> + clock-frequency = <27000000>;
> + };
> +
> + pll8: pll8 {
> + #clock-cells = <0>;
> + compatible = "qcom,pll";
> + clocks = <&pxo>;
> + };
> +
> + vpll8: vpll8 {
> + #clock-cells = <0>;
> + compatible = "qcom,pll-vote";
> + clocks = <&pll8>;
> + };
> +
> + gsbi5_uart_rcg: gsbi5_uart_rcg {
> + #clock-cells = <0>;
> + compatible = "qcom,p2-mn16-clock";
> + clocks = <&pxo>, <&vpll8>;
> + };
> +
> + gsbi5_uart_clk: gsbi5_uart_cxc {
> + #clock-cells = <0>;
> + compatible = "qcom,cxc-clock";
> + clocks = <&gsbi5_uart_rcg>;
> + };
> +
> + gsbi5_uart_ahb: gsbi5_uart_ahb {
> + #clock-cells = <0>;
> + compatible = "qcom,cxc-hg-clock";
> + };
> + };
> + };
I'm slightly confused by this. How is each of the clocks described in
the clocks node related to a portion of the register set?
If the set of clocks is fixed, surely the gcc node gives you enough
information alone, and the whole block can be modelled as a single
provider of multiple clock outputs, or it's not fixed, and some linkage
needs to be defined?
The code seems to imply the former, unless only a subset of clocks may
be present? In that case, the set of clocks which might be present
should be described in the binding.
Thanks,
Mark.
> diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
> index bf7e3d2..3eaffb6 100644
> --- a/drivers/clk/msm/Kconfig
> +++ b/drivers/clk/msm/Kconfig
> @@ -2,3 +2,13 @@ menuconfig COMMON_CLK_MSM
> tristate "Support for Qualcomm's MSM designs"
> depends on OF
>
> +if COMMON_CLK_MSM
> +
> +config MSM_GCC_8960
> + bool "MSM8960 Global Clock Controller"
> + help
> + Support for the global clock controller on msm8960 devices.
> + Say Y if you want to use peripheral devices such as UART, SPI,
> + i2c, USB, SD/eMMC, SATA, PCIe, etc.
> +
> +endif
> diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
> index 9cfd0d7..c785943 100644
> --- a/drivers/clk/msm/Makefile
> +++ b/drivers/clk/msm/Makefile
> @@ -6,3 +6,5 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-rcg2.o
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
>
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
> +
> +clk-msm-$(CONFIG_MSM_GCC_8960) += gcc-8960.o
> diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
> index b1904c0..b8e702b 100644
> --- a/drivers/clk/msm/core.c
> +++ b/drivers/clk/msm/core.c
> @@ -173,6 +173,9 @@ typedef struct clk *
> struct cc_data *cc);
>
> static const struct of_device_id msm_cc_match_table[] = {
> +#ifdef CONFIG_MSM_GCC_8960
> + { .compatible = "qcom,gcc-8960", .data = &msm_gcc_8960_matches },
> +#endif
> { }
> };
> MODULE_DEVICE_TABLE(of, msm_cc_match_table);
> diff --git a/drivers/clk/msm/gcc-8960.c b/drivers/clk/msm/gcc-8960.c
> new file mode 100644
> index 0000000..b57d2dd
> --- /dev/null
> +++ b/drivers/clk/msm/gcc-8960.c
> @@ -0,0 +1,174 @@
> +/*
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk-provider.h>
> +
> +#include "internal.h"
> +#include "clk-pll.h"
> +#include "clk-rcg.h"
> +#include "clk-branch.h"
> +
> +static struct pll_desc pll8_desc = {
> + .l_reg = 0x3144,
> + .m_reg = 0x3148,
> + .n_reg = 0x314c,
> + .config_reg = 0x3154,
> + .mode_reg = 0x3140,
> + .status_reg = 0x3158,
> + .status_bit = 16,
> +};
> +
> +static struct pll_vote_desc pll8_vote_desc = {
> + .vote_reg = 0x34c0,
> + .vote_bit = 8,
> +};
> +
> +#define PXO 0
> +#define PLL8 1
> +
> +static u8 gcc_pxo_pll8_map[] = {
> + [PXO] = 0,
> + [PLL8] = 3,
> +};
> +
> +static struct freq_tbl clk_tbl_gsbi_uart[] = {
> + { 1843200, PLL8, 2, 6, 625 },
> + { 3686400, PLL8, 2, 12, 625 },
> + { 7372800, PLL8, 2, 24, 625 },
> + { 14745600, PLL8, 2, 48, 625 },
> + { 16000000, PLL8, 4, 1, 6 },
> + { 24000000, PLL8, 4, 1, 4 },
> + { 32000000, PLL8, 4, 1, 3 },
> + { 40000000, PLL8, 1, 5, 48 },
> + { 46400000, PLL8, 1, 29, 240 },
> + { 48000000, PLL8, 4, 1, 2 },
> + { 51200000, PLL8, 1, 2, 15 },
> + { 56000000, PLL8, 1, 7, 48 },
> + { 58982400, PLL8, 1, 96, 625 },
> + { 64000000, PLL8, 2, 1, 3 },
> + { }
> +};
> +
> +static struct rcg_desc gsbi5_uart_rcg = {
> + .ctl_reg = 0x2a54,
> + .ns_reg = 0x2a54,
> + .md_reg = 0x2a50,
> + .ctl_bit = 11,
> + .mnctr_en_bit = 8,
> + .mnctr_reset_bit = 7,
> + .mnctr_mode_shift = 5,
> + .pre_div_shift = 3,
> + .src_sel_shift = 0,
> + .n_val_shift = 16,
> + .m_val_shift = 16,
> + .parent_map = gcc_pxo_pll8_map,
> + .freq_tbl = clk_tbl_gsbi_uart,
> +};
> +
> +static struct branch_desc gsbi5_uart_cxc = {
> + .ctl_reg = 0x2a54,
> + .halt_reg = 0x2fd0,
> + .ctl_bit = 9,
> + .halt_bit = 22,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct rcg_desc gsbi6_uart_rcg = {
> + .ctl_reg = 0x2a74,
> + .ns_reg = 0x2a74,
> + .md_reg = 0x2a70,
> + .ctl_bit = 11,
> + .mnctr_en_bit = 8,
> + .mnctr_reset_bit = 7,
> + .mnctr_mode_shift = 5,
> + .pre_div_shift = 3,
> + .src_sel_shift = 0,
> + .n_val_shift = 16,
> + .m_val_shift = 16,
> + .parent_map = gcc_pxo_pll8_map,
> + .freq_tbl = clk_tbl_gsbi_uart,
> +};
> +
> +static struct branch_desc gsbi6_uart_cxc = {
> + .ctl_reg = 0x2a74,
> + .halt_reg = 0x2fd0,
> + .ctl_bit = 9,
> + .halt_bit = 18,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct branch_desc gsbi5_uart_ahb = {
> + .ctl_reg = 0x2a40,
> + .halt_reg = 0x2fd0,
> + .hwcg_reg = 0x2a40,
> + .ctl_bit = 4,
> + .hwcg_bit = 6,
> + .halt_bit = 23,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct freq_tbl clk_tbl_gsbi_qup[] = {
> + { 1100000, PXO, 1, 2, 49 },
> + { 5400000, PXO, 1, 1, 5 },
> + { 10800000, PXO, 1, 2, 5 },
> + { 15060000, PLL8, 1, 2, 51 },
> + { 24000000, PLL8, 4, 1, 4 },
> + { 25600000, PLL8, 1, 1, 15 },
> + { 27000000, PXO, 1, 0, 0 },
> + { 48000000, PLL8, 4, 1, 2 },
> + { 51200000, PLL8, 1, 2, 15 },
> + { }
> +};
> +
> +static struct rcg_desc gsbi5_qup_rcg = {
> + .ctl_reg = 0x2a4c,
> + .ns_reg = 0x2a4c,
> + .md_reg = 0x2a48,
> + .ctl_bit = 11,
> + .mnctr_en_bit = 8,
> + .mnctr_reset_bit = 7,
> + .mnctr_mode_shift = 5,
> + .pre_div_shift = 3,
> + .src_sel_shift = 0,
> + .n_val_shift = 16,
> + .m_val_shift = 16,
> + .parent_map = gcc_pxo_pll8_map,
> + .freq_tbl = clk_tbl_gsbi_qup,
> +};
> +
> +static struct branch_desc gsbi5_qup_cxc = {
> + .ctl_reg = 0x2a4c,
> + .halt_reg = 0x2fd0,
> + .ctl_bit = 9,
> + .halt_bit = 20,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct of_clk_match msm_gcc_clk_match[] = {
> + { .name = "cxo" },
> + { .name = "pxo" },
> + { .name = "pll8", .driver_data = &pll8_desc },
> + { .name = "vpll8", .driver_data = &pll8_vote_desc },
> + { .name = "gsbi5_uart_rcg", .driver_data = &gsbi5_uart_rcg },
> + { .name = "gsbi5_uart_cxc", .driver_data = &gsbi5_uart_cxc },
> + { .name = "gsbi6_uart_rcg", .driver_data = &gsbi6_uart_rcg },
> + { .name = "gsbi6_uart_cxc", .driver_data = &gsbi6_uart_cxc },
> + { .name = "gsbi5_uart_ahb", .driver_data = &gsbi5_uart_ahb },
> + { .name = "gsbi5_qup_rcg", .driver_data = &gsbi5_qup_rcg },
> + { .name = "gsbi5_qup_cxc", .driver_data = &gsbi5_qup_cxc },
> +};
> +
> +const struct msm_clk_match msm_gcc_8960_matches = {
> + .matches = msm_gcc_clk_match,
> + .size = ARRAY_SIZE(msm_gcc_clk_match)
> +};
> diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
> index 177bd3b..b0ffda7 100644
> --- a/drivers/clk/msm/internal.h
> +++ b/drivers/clk/msm/internal.h
> @@ -21,4 +21,6 @@ struct msm_clk_match {
> size_t size;
> };
>
> +extern const struct msm_clk_match msm_gcc_8960_matches;
> +
> #endif
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Hi Stephen,
On Thu, Jul 25, 2013 at 01:43:38AM +0100, Stephen Boyd wrote:
> Fill in the data and wire up the multimedia clock controller to
> the MSM clock driver. This should allow multimedia device drivers
> to control their clocks on 8960 based platforms.
>
> Cc: [email protected]
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> .../devicetree/bindings/clock/qcom,mmcc.txt | 38 ++++++
> drivers/clk/msm/Kconfig | 8 ++
> drivers/clk/msm/Makefile | 1 +
> drivers/clk/msm/core.c | 3 +
> drivers/clk/msm/internal.h | 1 +
> drivers/clk/msm/mmcc-8960.c | 142 +++++++++++++++++++++
> 6 files changed, 193 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/qcom,mmcc.txt
> create mode 100644 drivers/clk/msm/mmcc-8960.c
>
> diff --git a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt
> new file mode 100644
> index 0000000..e06577e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt
> @@ -0,0 +1,38 @@
> +MSM Multimedia Clock Controller Binding
> +-----------------------------------------
> +
> +Required properties :
> +- compatible : shall contain at least "qcom,mmcc" and only one of the
> + following:
> +
> + "qcom,mmcc-8660"
> + "qcom,mmcc-8960"
> +
> +- reg : shall contain base register location and length
> +- clocks : shall contain clocks supplied by the clock controller
> +
> +Example:
> + clock-controller@4000000 {
> + compatible = "qcom,mmcc-8960", "qcom,mmcc";
> + reg = <0x4000000 0x1000>;
> +
> + clocks {
> + pll2: pll2 {
> + #clock-cells = <0>;
> + compatible = "qcom,pll";
> + clocks = <&pxo>;
> + };
> +
> + mdp_rcg: mdp_rcg {
> + #clock-cells = <0>;
> + compatible = "qcom,mn8-dyn-clock";
> + clocks = <&pxo>, <&pll2>, <&vpll8>;
> + };
> +
> + mdp_cxc: mdp_cxc {
> + #clock-cells = <0>;
> + compatible = "qcom,cxc-clock";
> + clocks = <&mdp_rcg>;
> + };
> + };
> + };
Similar to my comments on the previous patch, this seems odd to me.
There's either not enough information in the binding, or there's not
enough in the documentation.
Thanks,
Mark.
> diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
> index 3eaffb6..6147380 100644
> --- a/drivers/clk/msm/Kconfig
> +++ b/drivers/clk/msm/Kconfig
> @@ -11,4 +11,12 @@ config MSM_GCC_8960
> Say Y if you want to use peripheral devices such as UART, SPI,
> i2c, USB, SD/eMMC, SATA, PCIe, etc.
>
> +config MSM_MMCC_8960
> + bool "MSM8960 Multimedia Clock Controller"
> + select MSM_GCC_8960
> + help
> + Support for the multimedia clock controller on msm8960 devices.
> + Say Y if you want to support multimedia devices such as display,
> + graphics, video encode/decode, camera, etc.
> +
> endif
> diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
> index c785943..ae199f5 100644
> --- a/drivers/clk/msm/Makefile
> +++ b/drivers/clk/msm/Makefile
> @@ -8,3 +8,4 @@ clk-msm-$(CONFIG_COMMON_CLK_MSM) += clk-branch.o
> clk-msm-$(CONFIG_COMMON_CLK_MSM) += core.o
>
> clk-msm-$(CONFIG_MSM_GCC_8960) += gcc-8960.o
> +clk-msm-$(CONFIG_MSM_MMCC_8960) += mmcc-8960.o
> diff --git a/drivers/clk/msm/core.c b/drivers/clk/msm/core.c
> index b8e702b..4e8b8d0 100644
> --- a/drivers/clk/msm/core.c
> +++ b/drivers/clk/msm/core.c
> @@ -176,6 +176,9 @@ static const struct of_device_id msm_cc_match_table[] = {
> #ifdef CONFIG_MSM_GCC_8960
> { .compatible = "qcom,gcc-8960", .data = &msm_gcc_8960_matches },
> #endif
> +#ifdef CONFIG_MSM_MMCC_8960
> + { .compatible = "qcom,mmcc-8960", .data = &msm_mmcc_8960_matches },
> +#endif
> { }
> };
> MODULE_DEVICE_TABLE(of, msm_cc_match_table);
> diff --git a/drivers/clk/msm/internal.h b/drivers/clk/msm/internal.h
> index b0ffda7..45435a8 100644
> --- a/drivers/clk/msm/internal.h
> +++ b/drivers/clk/msm/internal.h
> @@ -22,5 +22,6 @@ struct msm_clk_match {
> };
>
> extern const struct msm_clk_match msm_gcc_8960_matches;
> +extern const struct msm_clk_match msm_mmcc_8960_matches;
>
> #endif
> diff --git a/drivers/clk/msm/mmcc-8960.c b/drivers/clk/msm/mmcc-8960.c
> new file mode 100644
> index 0000000..e7b7867
> --- /dev/null
> +++ b/drivers/clk/msm/mmcc-8960.c
> @@ -0,0 +1,142 @@
> +/*
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk-provider.h>
> +
> +#include "internal.h"
> +#include "clk-pll.h"
> +#include "clk-rcg.h"
> +#include "clk-branch.h"
> +
> +static struct pll_desc pll2_desc = {
> + .l_reg = 0x320,
> + .m_reg = 0x324,
> + .n_reg = 0x328,
> + .config_reg = 0x32c,
> + .mode_reg = 0x31c,
> + .status_reg = 0x334,
> + .status_bit = 16,
> +};
> +
> +#define PXO 0
> +#define PLL2 1
> +#define PLL8 2
> +
> +static u8 mmcc_pxo_pll2_pll8_map[] = {
> + [PXO] = 0,
> + [PLL2] = 1,
> + [PLL8] = 2,
> +};
> +
> +static struct freq_tbl clk_tbl_mdp[] = {
> + { 9600000, PLL8, 0, 1, 40 },
> + { 13710000, PLL8, 0, 1, 28 },
> + { 27000000, PXO, 0, 0, 0 },
> + { 29540000, PLL8, 0, 1, 13 },
> + { 34910000, PLL8, 0, 1, 11 },
> + { 38400000, PLL8, 0, 1, 10 },
> + { 59080000, PLL8, 0, 2, 13 },
> + { 76800000, PLL8, 0, 1, 5 },
> + { 85330000, PLL8, 0, 2, 9 },
> + { 96000000, PLL8, 0, 1, 4 },
> + { 128000000, PLL8, 0, 1, 3 },
> + { 160000000, PLL2, 0, 1, 5 },
> + { 177780000, PLL2, 0, 2, 9 },
> + { 200000000, PLL2, 0, 1, 4 },
> + { 228571000, PLL2, 0, 2, 7 },
> + { 266667000, PLL2, 0, 1, 3 },
> + { }
> +};
> +
> +static struct rcg_dyn_desc mdp_rcg = {
> + .ctl_reg = 0xc0,
> + .ns_reg = 0xd0,
> + .md0_reg = 0xc4,
> + .md1_reg = 0xc8,
> + .ctl_bit = 2,
> + .mnctr0_en_bit = 8,
> + .mnctr1_en_bit = 5,
> + .mnctr0_reset_bit = 31,
> + .mnctr1_reset_bit = 30,
> + .mnctr0_mode_shift = 9,
> + .mnctr1_mode_shift = 6,
> + .src0_sel_shift = 3,
> + .src1_sel_shift = 0,
> + .n0_val_shift = 22,
> + .n1_val_shift = 14,
> + .m0_val_shift = 8,
> + .m1_val_shift = 8,
> + .mux_sel_bit = 11,
> + .parent_map = mmcc_pxo_pll2_pll8_map,
> + .freq_tbl = clk_tbl_mdp,
> +};
> +
> +static struct branch_desc mdp_cxc = {
> + .ctl_reg = 0xc0,
> + .halt_reg = 0x1d0,
> + .ctl_bit = 0,
> + .halt_bit = 10,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct freq_tbl clk_tbl_rot[] = {
> + { 27000000, PXO, 1 },
> + { 29540000, PLL8, 13 },
> + { 32000000, PLL8, 12 },
> + { 38400000, PLL8, 10 },
> + { 48000000, PLL8, 8 },
> + { 54860000, PLL8, 7 },
> + { 64000000, PLL8, 6 },
> + { 76800000, PLL8, 5 },
> + { 96000000, PLL8, 4 },
> + { 100000000, PLL2, 8 },
> + { 114290000, PLL2, 7 },
> + { 133330000, PLL2, 6 },
> + { 160000000, PLL2, 5 },
> + { 200000000, PLL2, 4 },
> + { }
> +};
> +
> +static struct rcg_dyn_desc rot_rcg = {
> + .ctl_reg = 0xe0,
> + .ns_reg = 0xe8,
> + .ctl_bit = 2,
> + .pre_div0_shift = 22,
> + .pre_div1_shift = 26,
> + .src0_sel_shift = 16,
> + .src1_sel_shift = 19,
> + .mux_sel_bit = 30,
> + .parent_map = mmcc_pxo_pll2_pll8_map,
> + .freq_tbl = clk_tbl_rot,
> +};
> +
> +static struct branch_desc rot_cxc = {
> + .ctl_reg = 0xe0,
> + .halt_reg = 0x1d0,
> + .ctl_bit = 0,
> + .halt_bit = 15,
> + .halt_check = BRANCH_HALT,
> +};
> +
> +static struct of_clk_match msm_mmcc_clk_match[] = {
> + { .name = "pll2", .driver_data = &pll2_desc },
> + { .name = "mdp_rcg", .driver_data = &mdp_rcg },
> + { .name = "mdp_cxc", .driver_data = &mdp_cxc },
> + { .name = "rot_rcg", .driver_data = &rot_rcg },
> + { .name = "rot_cxc", .driver_data = &rot_cxc },
> +};
> +
> +const struct msm_clk_match msm_mmcc_8960_matches = {
> + .matches = msm_mmcc_clk_match,
> + .size = ARRAY_SIZE(msm_mmcc_clk_match)
> +};
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Quoting Stephen Boyd (2013-07-25 09:45:42)
> On 07/25, Tomasz Figa wrote:
> > On Wednesday 24 of July 2013 17:43:32 Stephen Boyd wrote:
> > > Some of Qualcomm's clocks can change their parent and rate at the
> > > same time with a single register write. Add support for this
> > > hardware to the common clock framework by adding a new
> > > set_rate_and_parent() op. When the clock framework determines
> > > that both the parent and the rate are going to change during
> > > clk_set_rate() it will call the .set_rate_and_parent() op if
> > > available and fall back to calling .set_parent() followed by
> > > .set_rate() otherwise.
> >
> > This is strange. Does you hardware support switching parent and rate
> > separately or you always need to set both and so all the fuss here?
>
> It supports setting the parent or setting the rate, or setting
> both at the same time.
I think that setting parent and rate at the same time is a common enough
case to merit handling it in the clock core. Probably this design will
become more common in time.
Regards,
Mike
>
> >
> > If the latter is the case, then maybe you can simply keep parent index and
> > rate cached inside driver data of your clock driver and use them on any
> > .set_rate() or .set_parent() calls?
>
> This will not work. In fact, doing that would cause us to
> overclock hardware for a short time between switching the parent
> and the rate.
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> hosted by The Linux Foundation
Hi Stephen,
On 25/07/13 01:43, Stephen Boyd wrote:
> Some of Qualcomm's clocks can change their parent and rate at the
> same time with a single register write. Add support for this
> hardware to the common clock framework by adding a new
> set_rate_and_parent() op. When the clock framework determines
> that both the parent and the rate are going to change during
> clk_set_rate() it will call the .set_rate_and_parent() op if
> available and fall back to calling .set_parent() followed by
> .set_rate() otherwise.
>
> Cc: James Hogan <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
Aside from the nit below, I can't see anything wrong with this patch.
Reviewed-by: James Hogan <[email protected]>
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 484f8ad..1f7eabb 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -108,6 +108,18 @@ struct clk_hw;
> * which is likely helpful for most .set_rate implementation.
> * Returns 0 on success, -EERROR otherwise.
> *
> + * @set_rate_and_parent: Change the rate and the parent of this clock. The
> + * requested rate is specified by the second argument, which
> + * should typically be the return of .round_rate call. The
> + * third argument gives the parent rate which is likely helpful
> + * for most .set_rate_and_parent implementation. The fourth
> + * argument gives the parent index. It is optional (and
nit: s/It/This callback/ or add newline or something - I completely
misread it the first time, thinking you were referring to the parent
index argument :)
> + * unnecessary) for clocks with 0 or 1 parents as well as
> + * for clocks that can tolerate switching the rate and the parent
> + * separately via calls to .set_parent and .set_rate.
> + * Returns 0 on success, -EERROR otherwise.
> + *
> + *
> * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
> * implementations to split any work between atomic (enable) and sleepable
> * (prepare) contexts. If enabling a clock requires code that might sleep,
Thanks
James
On 08/08, Mark Rutland wrote:
> Hi Stephen,
>
> On Thu, Jul 25, 2013 at 01:43:37AM +0100, Stephen Boyd wrote:
> > Fill in the data and wire up the global clock controller to the
> > MSM clock driver. This should allow most non-multimedia device
> > drivers to control their clocks on 8960 based platforms.
> >
> > Cc: [email protected]
> > Signed-off-by: Stephen Boyd <[email protected]>
> > ---
> > .../devicetree/bindings/clock/qcom,gcc.txt | 55 +++++++
> > drivers/clk/msm/Kconfig | 10 ++
> > drivers/clk/msm/Makefile | 2 +
> > drivers/clk/msm/core.c | 3 +
> > drivers/clk/msm/gcc-8960.c | 174 +++++++++++++++++++++
> > drivers/clk/msm/internal.h | 2 +
> > 6 files changed, 246 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > create mode 100644 drivers/clk/msm/gcc-8960.c
> >
> > diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > new file mode 100644
> > index 0000000..2311e1a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > @@ -0,0 +1,55 @@
> > +MSM Global Clock Controller Binding
> > +-----------------------------------
> > +
> > +Required properties :
> > +- compatible : shall contain at least "qcom,gcc" and only one of the
> > + following:
> > +
> > + "qcom,gcc-8660"
> > + "qcom,gcc-8960"
> > +
> > +- reg : shall contain base register location and length
> > +- clocks : shall contain clocks supplied by the clock controller
> > +
> > +Example:
> > + clock-controller@900000 {
> > + compatible = "qcom,gcc-8960", "qcom,gcc";
> > + reg = <0x900000 0x4000>;
> > +
> > + clocks {
> > + pxo: pxo {
> > + #clock-cells = <0>;
> > + compatible = "fixed-clock";
> > + clock-frequency = <27000000>;
> > + };
> > +
> > + pll8: pll8 {
> > + #clock-cells = <0>;
> > + compatible = "qcom,pll";
> > + clocks = <&pxo>;
> > + };
> > +
> > + vpll8: vpll8 {
> > + #clock-cells = <0>;
> > + compatible = "qcom,pll-vote";
> > + clocks = <&pll8>;
> > + };
> > +
> > + gsbi5_uart_rcg: gsbi5_uart_rcg {
> > + #clock-cells = <0>;
> > + compatible = "qcom,p2-mn16-clock";
> > + clocks = <&pxo>, <&vpll8>;
> > + };
> > +
> > + gsbi5_uart_clk: gsbi5_uart_cxc {
> > + #clock-cells = <0>;
> > + compatible = "qcom,cxc-clock";
> > + clocks = <&gsbi5_uart_rcg>;
> > + };
> > +
> > + gsbi5_uart_ahb: gsbi5_uart_ahb {
> > + #clock-cells = <0>;
> > + compatible = "qcom,cxc-hg-clock";
> > + };
> > + };
> > + };
>
> I'm slightly confused by this. How is each of the clocks described in
> the clocks node related to a portion of the register set?
The registers to control clocks and determine their state are
scattered throughout the registers in the gcc (in this example
from 0x900000 to 0x903fff). If you match up gsbi5_uart_rcg with
its C struct counterpart you'll notice that there are multiple
registers used to configure the clock. It isn't as simple as one
reg property per clock even for the case where we're just
toggling a bit to turn a clock on and off either. And it isn't as
simple as saying the clock has a base register that we can offset
from because offsets are almost always different (we've tried
to correct this in future chip versions).
>
> If the set of clocks is fixed, surely the gcc node gives you enough
> information alone, and the whole block can be modelled as a single
> provider of multiple clock outputs, or it's not fixed, and some linkage
> needs to be defined?
>
> The code seems to imply the former, unless only a subset of clocks may
> be present? In that case, the set of clocks which might be present
> should be described in the binding.
The clock controller is hardware and the number of clock outputs
is fixed. Isn't all hardware fixed until you start talking about
FPGAs? The next minor revision of the clock controller may add
more clocks or remove clocks from that base design, but otherwise
the two are 90% the same and generally software compatible. It
isn't until we start a new generation of chips that we make major
changes to the design. Is that loose enough to qualify?
These bindings attempt to follow the regulator bindings. With
regulators there is a node for each regulator and we describe
physical characteristics of those regulators within the nodes but
we don't describe the software interface (bits, masks, shifts,
etc). I imagine we could extend these clock nodes to describe
physical characteristics such as min/max frequency or if the
bootloader has left the clocks on. Right now we're using the
nodes to describe what types of clocks there are and how the
clock tree is layed out.
Or perhaps you're talking about clock sharing? We share the clock
controller with multiple masters (processors running other OSes)
and the partitioning of the clocks is mostly predefined. We just
won't use some clocks because they're reserved for other
processors. They're still part of the same clock controller
hardware block but we don't want to control them on Linux because
we'll trample over other processors and most likely hang the
system. I wonder how this would work for hexagon and krait both
running linux on the same SoC. If all DT says is that there is a
gcc here at this address how are we supposed to know that we
shouldn't use some clock? In fact we have some clocks that are
"voteable" in the sense that each master has its own register to
vote for a clock to be on or off. The registers are all ORed
together by hardware to determine if the clock should be on or
not. I should probably rename those clocks to have a _krait or
_apps at the end so that it's clear we want to instantiate the
krait version of the clock and not the hexagon version. I suppose
the other solution there is to say we have gcc-8960-krait and
gcc-8960-hexagon so we know which voting registers to use or put
an ifdef ARCH_HEXAGON/ARCH_ARM. Is that the right solution?
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 08/12, Mike Turquette wrote:
> Quoting Stephen Boyd (2013-07-24 17:43:31)
> > In similar fashion as of_regulator_match() add an of_clk_match()
> > function that finds an initializes clock init_data structs from
> > devicetree. Drivers should use this API to find clocks that their
> > device is providing and then iterate over their match table
> > registering the clocks with the init data parsed.
> >
> > Signed-off-by: Stephen Boyd <[email protected]>
>
> Stephen,
>
> In general I like this approach. Writing real device drivers for clock
> controllers is The Right Way and of_clk_match helps.
>
> Am I reading this correctly that the base register addresses/offsets for
> the clock nodes still come from C files? For example I still see
> pll8_desc defining reg stuff in drivers/clk/msm/gcc-8960.c.
I think we may be able to put the registers in DT but I don't
know why we need to do that if we're already matching up nodes
with C structs. It also made me want to introduce devm_of_iomap()
which just seemed wrong (if you have a dev struct why can't you
use devm_ioremap()).
>
> What do you think about fetching this data from DT? My thinking here is
> that the definition of that PLL structure would be in C, as would all of
> the control logic. But any per-clock data whatsoever should live in DTS.
> This means the clock data you supply in the DTS files in patches #9 and
> #10 would have base addresses or offsets per-clock. I think this echoes
> Mark R's concerns as well.
>
> In the future if new chips have more of that type of PLL it would not
> require changes to your clock driver, only new DTS data for the new
> chip.
>
> I could have that wrong though, there is a fair amount of indirection in
> this series...
Let's take the PLL example and see if I follow what would be in
DT and what would be in C.
Right now we have
pll8: pll8 {
#clock-cells = <0>;
compatible = "qcom,pll";
clocks = <&pxo>;
};
in DT and
static struct pll_desc pll8_desc = {
.l_reg = 0x3144,
.m_reg = 0x3148,
.n_reg = 0x314c,
.config_reg = 0x3154,
.mode_reg = 0x3140,
.status_reg = 0x3158,
.status_bit = 16,
};
in C. Do you want everything to be in DT? Something like:
pll8: pll8@3140 {
#clock-cells = <0>;
compatible = "qcom,pll";
clocks = <&pxo>;
reg = <0x3140 0x20>;
};
and then assume that all those registers are offset from the base
register and that the status bit is 16 (it usually is but not
always)?
The problem I see is this quickly breaks down with more
complicated clocks like the RCGs.
We have
gsbi5_uart_rcg: gsbi5_uart_rcg {
#clock-cells = <0>;
compatible = "qcom,p2-mn16-clock";
clocks = <&pxo>, <&vpll8>;
};
in DT and
static struct freq_tbl clk_tbl_gsbi_uart[] = {
{ 1843200, PLL8, 2, 6, 625 },
{ 3686400, PLL8, 2, 12, 625 },
{ 7372800, PLL8, 2, 24, 625 },
{ 14745600, PLL8, 2, 48, 625 },
{ 16000000, PLL8, 4, 1, 6 },
{ 24000000, PLL8, 4, 1, 4 },
{ 32000000, PLL8, 4, 1, 3 },
{ 40000000, PLL8, 1, 5, 48 },
{ 46400000, PLL8, 1, 29, 240 },
{ 48000000, PLL8, 4, 1, 2 },
{ 51200000, PLL8, 1, 2, 15 },
{ 56000000, PLL8, 1, 7, 48 },
{ 58982400, PLL8, 1, 96, 625 },
{ 64000000, PLL8, 2, 1, 3 },
{ }
};
static struct rcg_desc gsbi5_uart_rcg = {
.ctl_reg = 0x2a54,
.ns_reg = 0x2a54,
.md_reg = 0x2a50,
.ctl_bit = 11,
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
.pre_div_shift = 3,
.src_sel_shift = 0,
.n_val_shift = 16,
.m_val_shift = 16,
.parent_map = gcc_pxo_pll8_map,
.freq_tbl = clk_tbl_gsbi_uart,
};
in C. It starts to get pretty unwieldy when you put this all in
DT, plus you'll notice that the ns_reg and ctl_reg are the same
here because we've generalized the code to work with different
types of software interfaces (technically this clock has no ctl
register, just an NS and MD register). Our multimedia clock
controllers don't follow any standard base/offset pair and so the
ctl_reg can be a different offset from the md_reg depending on
which clock we're talking about. My initial try at translating
this into DT pretty much just made every struct member into a
property, including the duplicate register, expect for the
frequency table, which could probably also be DT-ified with some
work.
gsbi5_uart_rcg: gsbi5_uart_rcg@2a54 {
#clock-cells = <0>;
compatible = "qcom,p2-mn16-clock";
clocks = <&pxo>, <&vpll8>;
reg = <0x2a54 0x4>,
<0x2a54 0x4>,
<0x2a50 0x4>;
ctl_bit = <11>;
mnctr_en_bit = <8>;
mnctr_reset_bit = <7>;
mnctr_mode_shift = <5>;
pre_div_shift = <3>;
src_sel_shift = <0>;
n_val_shift = <16>;
m_val_shift = <16>;
};
This is great for making the kernel DT-data-driven, but I
couldn't find any other driver that was describing register level
details in DT.
The good news is that newer clock controllers follow a standard
and so we can specify one or two register properties and the type
of clock and we're pretty much done. The software interface
hasn't been randomized like on earlier controllers and bits
within registers are always the same. We still have some clocks
that are just on/off switches though and so we'll have to put
register level details like which bit turns that clock on in DT
(which I believe is not preferred/allowed?). I don't see any way
to avoid that if we want it to be entirely DT driven.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
Quoting Stephen Boyd (2013-08-12 22:03:34)
> On 08/08, Mark Rutland wrote:
> > Hi Stephen,
> >
> > On Thu, Jul 25, 2013 at 01:43:37AM +0100, Stephen Boyd wrote:
> > > Fill in the data and wire up the global clock controller to the
> > > MSM clock driver. This should allow most non-multimedia device
> > > drivers to control their clocks on 8960 based platforms.
> > >
> > > Cc: [email protected]
> > > Signed-off-by: Stephen Boyd <[email protected]>
> > > ---
> > > .../devicetree/bindings/clock/qcom,gcc.txt | 55 +++++++
> > > drivers/clk/msm/Kconfig | 10 ++
> > > drivers/clk/msm/Makefile | 2 +
> > > drivers/clk/msm/core.c | 3 +
> > > drivers/clk/msm/gcc-8960.c | 174 +++++++++++++++++++++
> > > drivers/clk/msm/internal.h | 2 +
> > > 6 files changed, 246 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > > create mode 100644 drivers/clk/msm/gcc-8960.c
> > >
> > > diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > > new file mode 100644
> > > index 0000000..2311e1a
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
> > > @@ -0,0 +1,55 @@
> > > +MSM Global Clock Controller Binding
> > > +-----------------------------------
> > > +
> > > +Required properties :
> > > +- compatible : shall contain at least "qcom,gcc" and only one of the
> > > + following:
> > > +
> > > + "qcom,gcc-8660"
> > > + "qcom,gcc-8960"
> > > +
> > > +- reg : shall contain base register location and length
> > > +- clocks : shall contain clocks supplied by the clock controller
> > > +
> > > +Example:
> > > + clock-controller@900000 {
> > > + compatible = "qcom,gcc-8960", "qcom,gcc";
> > > + reg = <0x900000 0x4000>;
> > > +
> > > + clocks {
> > > + pxo: pxo {
> > > + #clock-cells = <0>;
> > > + compatible = "fixed-clock";
> > > + clock-frequency = <27000000>;
> > > + };
> > > +
> > > + pll8: pll8 {
> > > + #clock-cells = <0>;
> > > + compatible = "qcom,pll";
> > > + clocks = <&pxo>;
> > > + };
> > > +
> > > + vpll8: vpll8 {
> > > + #clock-cells = <0>;
> > > + compatible = "qcom,pll-vote";
> > > + clocks = <&pll8>;
> > > + };
> > > +
> > > + gsbi5_uart_rcg: gsbi5_uart_rcg {
> > > + #clock-cells = <0>;
> > > + compatible = "qcom,p2-mn16-clock";
> > > + clocks = <&pxo>, <&vpll8>;
> > > + };
> > > +
> > > + gsbi5_uart_clk: gsbi5_uart_cxc {
> > > + #clock-cells = <0>;
> > > + compatible = "qcom,cxc-clock";
> > > + clocks = <&gsbi5_uart_rcg>;
> > > + };
> > > +
> > > + gsbi5_uart_ahb: gsbi5_uart_ahb {
> > > + #clock-cells = <0>;
> > > + compatible = "qcom,cxc-hg-clock";
> > > + };
> > > + };
> > > + };
> >
> > I'm slightly confused by this. How is each of the clocks described in
> > the clocks node related to a portion of the register set?
>
> The registers to control clocks and determine their state are
> scattered throughout the registers in the gcc (in this example
> from 0x900000 to 0x903fff). If you match up gsbi5_uart_rcg with
> its C struct counterpart you'll notice that there are multiple
> registers used to configure the clock. It isn't as simple as one
> reg property per clock even for the case where we're just
> toggling a bit to turn a clock on and off either. And it isn't as
> simple as saying the clock has a base register that we can offset
> from because offsets are almost always different (we've tried
> to correct this in future chip versions).
>
> >
> > If the set of clocks is fixed, surely the gcc node gives you enough
> > information alone, and the whole block can be modelled as a single
> > provider of multiple clock outputs, or it's not fixed, and some linkage
> > needs to be defined?
> >
> > The code seems to imply the former, unless only a subset of clocks may
> > be present? In that case, the set of clocks which might be present
> > should be described in the binding.
>
> The clock controller is hardware and the number of clock outputs
> is fixed. Isn't all hardware fixed until you start talking about
> FPGAs? The next minor revision of the clock controller may add
> more clocks or remove clocks from that base design, but otherwise
> the two are 90% the same and generally software compatible. It
> isn't until we start a new generation of chips that we make major
> changes to the design. Is that loose enough to qualify?
>
> These bindings attempt to follow the regulator bindings. With
> regulators there is a node for each regulator and we describe
> physical characteristics of those regulators within the nodes but
> we don't describe the software interface (bits, masks, shifts,
> etc). I imagine we could extend these clock nodes to describe
> physical characteristics such as min/max frequency or if the
> bootloader has left the clocks on. Right now we're using the
> nodes to describe what types of clocks there are and how the
> clock tree is layed out.
>
> Or perhaps you're talking about clock sharing? We share the clock
> controller with multiple masters (processors running other OSes)
> and the partitioning of the clocks is mostly predefined. We just
> won't use some clocks because they're reserved for other
> processors. They're still part of the same clock controller
> hardware block but we don't want to control them on Linux because
> we'll trample over other processors and most likely hang the
> system. I wonder how this would work for hexagon and krait both
> running linux on the same SoC. If all DT says is that there is a
> gcc here at this address how are we supposed to know that we
> shouldn't use some clock?
Do Krait and Hexagon have the same register map? On the ARM SoCs I am
familiar with the masters have differing views of register addresses for
the same peripherals and hardware blocks. So you couldn't use the same
DTS in a straightforward way if this is true for your system.
Regards,
Mike
> In fact we have some clocks that are
> "voteable" in the sense that each master has its own register to
> vote for a clock to be on or off. The registers are all ORed
> together by hardware to determine if the clock should be on or
> not. I should probably rename those clocks to have a _krait or
> _apps at the end so that it's clear we want to instantiate the
> krait version of the clock and not the hexagon version. I suppose
> the other solution there is to say we have gcc-8960-krait and
> gcc-8960-hexagon so we know which voting registers to use or put
> an ifdef ARCH_HEXAGON/ARCH_ARM. Is that the right solution?
>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> hosted by The Linux Foundation
On 08/13, Mike Turquette wrote:
> Quoting Stephen Boyd (2013-08-12 22:03:34)
> > The clock controller is hardware and the number of clock outputs
> > is fixed. Isn't all hardware fixed until you start talking about
> > FPGAs? The next minor revision of the clock controller may add
> > more clocks or remove clocks from that base design, but otherwise
> > the two are 90% the same and generally software compatible. It
> > isn't until we start a new generation of chips that we make major
> > changes to the design. Is that loose enough to qualify?
> >
> > These bindings attempt to follow the regulator bindings. With
> > regulators there is a node for each regulator and we describe
> > physical characteristics of those regulators within the nodes but
> > we don't describe the software interface (bits, masks, shifts,
> > etc). I imagine we could extend these clock nodes to describe
> > physical characteristics such as min/max frequency or if the
> > bootloader has left the clocks on. Right now we're using the
> > nodes to describe what types of clocks there are and how the
> > clock tree is layed out.
> >
> > Or perhaps you're talking about clock sharing? We share the clock
> > controller with multiple masters (processors running other OSes)
> > and the partitioning of the clocks is mostly predefined. We just
> > won't use some clocks because they're reserved for other
> > processors. They're still part of the same clock controller
> > hardware block but we don't want to control them on Linux because
> > we'll trample over other processors and most likely hang the
> > system. I wonder how this would work for hexagon and krait both
> > running linux on the same SoC. If all DT says is that there is a
> > gcc here at this address how are we supposed to know that we
> > shouldn't use some clock?
>
> Do Krait and Hexagon have the same register map? On the ARM SoCs I am
> familiar with the masters have differing views of register addresses for
> the same peripherals and hardware blocks. So you couldn't use the same
> DTS in a straightforward way if this is true for your system.
>
They both have the same view of the register map.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On 08/14, Mike Turquette wrote:
> Quoting Stephen Boyd (2013-08-12 22:48:39)
> > On 08/12, Mike Turquette wrote:
> > > Quoting Stephen Boyd (2013-07-24 17:43:31)
> > > > In similar fashion as of_regulator_match() add an of_clk_match()
> > > > function that finds an initializes clock init_data structs from
> > > > devicetree. Drivers should use this API to find clocks that their
> > > > device is providing and then iterate over their match table
> > > > registering the clocks with the init data parsed.
> > > >
> > > > Signed-off-by: Stephen Boyd <[email protected]>
> > >
> > > Stephen,
> > >
> > > In general I like this approach. Writing real device drivers for clock
> > > controllers is The Right Way and of_clk_match helps.
> > >
> > > Am I reading this correctly that the base register addresses/offsets for
> > > the clock nodes still come from C files? For example I still see
> > > pll8_desc defining reg stuff in drivers/clk/msm/gcc-8960.c.
> >
> > I think we may be able to put the registers in DT but I don't
> > know why we need to do that if we're already matching up nodes
> > with C structs.
>
> The reason to do so is to remove the per-clock data from the kernel
> sources entirely. Separating logic and data.
Ok.
>
> > It also made me want to introduce devm_of_iomap()
> > which just seemed wrong (if you have a dev struct why can't you
> > use devm_ioremap()).
> >
[snip]
> >
> > This is great for making the kernel DT-data-driven, but I
> > couldn't find any other driver that was describing register level
> > details in DT.
>
> Yeah, this sucks. Building a binding from a C struct is a bad idea. How
> many permutations are there? Hopefully some clocks use the same bit
> shifts and have reliable register offsets, with the only difference
> being base address. If this is the case then a compatible string could
> be done for each permutation assuming that number is low and manageable.
In the multimedia controller almost every clock has a different
register layout. Making a compatible string for each clock is
pretty much what I've done if you consider that I match up the
name of the node to a struct instead of matching a compatible
string to a struct. In the global controller it's more sane,
following a single register layout per compatible string.
Remember though, all the single bit clocks (gates or what we call
branches) have a completely random location for their on/off bit
and status bit.
>
> >
> > The good news is that newer clock controllers follow a standard
> > and so we can specify one or two register properties and the type
> > of clock and we're pretty much done. The software interface
> > hasn't been randomized like on earlier controllers and bits
> > within registers are always the same.
>
> This does not suck. Just for the sake of argument let's say that you
> only had to deal with this new and improved register layout and not the
> old (current) stuff. Do you still see a reason to match DT data up with
> C struct data objects in the kernel?
I would still need to match up frequency tables unless I figure
out a way to put that in DT too.
>
> > We still have some clocks
> > that are just on/off switches though and so we'll have to put
> > register level details like which bit turns that clock on in DT
> > (which I believe is not preferred/allowed?). I don't see any way
> > to avoid that if we want it to be entirely DT driven.
>
> This is what I'm doing for the generic clock bindings. No one has
> screamed over that stuff so I guess rules were meant to be broken.
>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
On Aug 15, 2013, at 12:02 AM, Mike Turquette wrote:
>> Right now we have
>>
>> pll8: pll8 {
>> #clock-cells = <0>;
>> compatible = "qcom,pll";
>> clocks = <&pxo>;
>> };
>>
>> in DT and
>>
>> static struct pll_desc pll8_desc = {
>> .l_reg = 0x3144,
>> .m_reg = 0x3148,
>> .n_reg = 0x314c,
>> .config_reg = 0x3154,
>> .mode_reg = 0x3140,
>> .status_reg = 0x3158,
>> .status_bit = 16,
>> };
>>
>> in C. Do you want everything to be in DT? Something like:
>>
>> pll8: pll8@3140 {
>> #clock-cells = <0>;
>> compatible = "qcom,pll";
>> clocks = <&pxo>;
>> reg = <0x3140 0x20>;
>> };
>>
>> and then assume that all those registers are offset from the base
>> register and that the status bit is 16 (it usually is but not
>> always)?
I think its reasonable to put the various regs associated with a clock in the .dts like the example you show, but we should be going down to bit level details. If we think of each clock as its own device its reasonable that the clock would have some set of registers associated with it.
-k
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
On Aug 16, 2013, at 11:43 AM, Kumar Gala wrote:
>
> On Aug 15, 2013, at 12:02 AM, Mike Turquette wrote:
>
>>> Right now we have
>>>
>>> pll8: pll8 {
>>> #clock-cells = <0>;
>>> compatible = "qcom,pll";
>>> clocks = <&pxo>;
>>> };
>>>
>>> in DT and
>>>
>>> static struct pll_desc pll8_desc = {
>>> .l_reg = 0x3144,
>>> .m_reg = 0x3148,
>>> .n_reg = 0x314c,
>>> .config_reg = 0x3154,
>>> .mode_reg = 0x3140,
>>> .status_reg = 0x3158,
>>> .status_bit = 16,
>>> };
>>>
>>> in C. Do you want everything to be in DT? Something like:
>>>
>>> pll8: pll8@3140 {
>>> #clock-cells = <0>;
>>> compatible = "qcom,pll";
>>> clocks = <&pxo>;
>>> reg = <0x3140 0x20>;
>>> };
>>>
>>> and then assume that all those registers are offset from the base
>>> register and that the status bit is 16 (it usually is but not
>>> always)?
>
> I think its reasonable to put the various regs associated with a clock in the .dts like the example you show, but we should be going down to bit level details. If we think of each clock as its own device its reasonable that the clock would have some set of registers associated with it.
oops, we should NOT be going down to bit level.
- k
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation