This function adds a notifier callback run before a driver is bound to
its driver. It will configure parent clock and clock frequencies based
on [clk-name]-clk-parent and [clk-name]-clk-rate' DT properties.

Signed-off-by: Sylwester Nawrocki <[email protected]>
Acked-by: Kyungmin Park <[email protected]>
---
 .../devicetree/bindings/clock/clock-bindings.txt   |   24 +++++
 drivers/clk/clk.c                                  |   92 ++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt 
b/Documentation/devicetree/bindings/clock/clock-bindings.txt
index 7c52c29..d618498 100644
--- a/Documentation/devicetree/bindings/clock/clock-bindings.txt
+++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt
@@ -115,3 +115,27 @@ clock signal, and a UART.
   ("pll" and "pll-switched").
 * The UART has its baud clock connected the external oscillator and its
   register clock connected to the PLL clock (the "pll-switched" signal)
+
+==Static initial configuration of clock parent and clock frequency==
+
+Some platforms require static configuration of (parts of) the clock controller
+often determined by the board design. Such a configuration can be specified in
+a clock consumer node through [clk-name]-clk-parent and [clk-name]-clk-rate DT
+properties. The former should contain phandle and clock specifier of the parent
+clock, the latter the required clock's frequency value (one cell). "clk-name"
+should be listed in the clock-names property and a phandle and a clock 
specifier
+pair corresponding to it should be present in the clocks property.
+
+    uart@a000 {
+        compatible = "fsl,imx-uart";
+        reg = <0xa000 0x1000>;
+       ...
+        clocks = <&clkcon 0>, <&clkcon 3>;
+        clock-names = "baud", "mux";
+
+       mux-clk-parent = <&pll 1>;
+       baud-clk-rate = <460800>;
+    };
+
+In this example the pll is set as parent of "mux" clock and frequency of "baud"
+clock is specified as 460800 Hz.
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 19f6f3f..9238e08 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -19,6 +19,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 #include <linux/sched.h>
 
 #include "clk.h"
@@ -2527,6 +2528,97 @@ const char *of_clk_get_parent_name(struct device_node 
*np, int index)
 }
 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
 
+static void __of_clk_assigned_config_set(struct clk *clk, struct clk *pclk,
+                                        u32 rate)
+{
+       int rc;
+
+       if (rate) {
+               rc = clk_set_rate(clk, rate);
+               if (rc < 0)
+                       pr_err("clk: couldn't set rate of clock %s (%d)\n",
+                              __clk_get_name(clk), rc);
+               else
+                       pr_debug("clk: set rate of clock %s to %u\n",
+                                __clk_get_name(clk), rate);
+       }
+
+       if (!IS_ERR(pclk)) {
+               rc = clk_set_parent(clk, pclk);
+               if (rc < 0)
+                       pr_err("clk: couldn't set %s as parent of %s (%d)\n",
+                              __clk_get_name(pclk), __clk_get_name(clk), rc);
+               else
+                       pr_debug("clk: set %s as parent of %s\n",
+                                __clk_get_name(pclk), __clk_get_name(clk));
+       }
+}
+
+static void of_clk_assigned_config_parse(struct device_node *node)
+{
+       char prop_name[OF_PROP_NAME_MAXLEN];
+       struct property *prop;
+       const char *clk_name;
+       int rc, index = 0;
+
+       of_property_for_each_string(node, "clock-names", prop, clk_name) {
+               struct clk *clk, *pclk;
+               u32 rate = 0;
+
+               snprintf(prop_name, OF_PROP_NAME_MAXLEN,
+                                       "%s-clk-parent", clk_name);
+               pclk = of_clk_get_list_entry(node, prop_name, 0);
+
+               snprintf(prop_name, OF_PROP_NAME_MAXLEN,
+                                       "%s-clk-rate", clk_name);
+               rc = of_property_read_u32(node, prop_name, &rate);
+
+               if (!rc || !IS_ERR(pclk)) {
+                       /*
+                        * Assuming here of_property_for_each_string() returns
+                        * consecutive values of a DT property in ascending
+                        * index order.
+                        */
+                       clk = of_clk_get(node, index);
+
+                       if (!IS_ERR(clk))
+                               __of_clk_assigned_config_set(clk, pclk, rate);
+                       else
+                               pr_err("clk: couldn't get clk %s\n", clk_name);
+               }
+               index++;
+       }
+}
+
+
+static int of_clk_setup_notifier_call(struct notifier_block *nb,
+                                       unsigned long event, void *data)
+{
+       struct device *dev = data;
+
+       if (!dev->of_node)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case BUS_NOTIFY_BIND_DRIVER:
+               /* Parse and configure DT assigned clock parents and rates */
+               of_clk_assigned_config_parse(dev->of_node);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block of_clk_setup_nb = {
+       .notifier_call = of_clk_setup_notifier_call,
+};
+
+int __init of_clk_setup_notifier_init(void)
+{
+       return bus_register_notifier(&platform_bus_type, &of_clk_setup_nb);
+}
+subsys_initcall(of_clk_setup_notifier_init);
+
 /**
  * of_clk_init() - Scan and init clock providers from the DT
  * @matches: array of compatible values and init functions for providers.
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to