186be408bSSylwester Nawrocki /* 286be408bSSylwester Nawrocki * Copyright (C) 2014 Samsung Electronics Co., Ltd. 386be408bSSylwester Nawrocki * Sylwester Nawrocki <s.nawrocki@samsung.com> 486be408bSSylwester Nawrocki * 586be408bSSylwester Nawrocki * This program is free software; you can redistribute it and/or modify 686be408bSSylwester Nawrocki * it under the terms of the GNU General Public License version 2 as 786be408bSSylwester Nawrocki * published by the Free Software Foundation. 886be408bSSylwester Nawrocki */ 986be408bSSylwester Nawrocki 1086be408bSSylwester Nawrocki #include <linux/clk.h> 1186be408bSSylwester Nawrocki #include <linux/clk-provider.h> 1286be408bSSylwester Nawrocki #include <linux/clk/clk-conf.h> 1386be408bSSylwester Nawrocki #include <linux/device.h> 1486be408bSSylwester Nawrocki #include <linux/of.h> 1586be408bSSylwester Nawrocki #include <linux/printk.h> 1686be408bSSylwester Nawrocki 1786be408bSSylwester Nawrocki static int __set_clk_parents(struct device_node *node, bool clk_supplier) 1886be408bSSylwester Nawrocki { 1986be408bSSylwester Nawrocki struct of_phandle_args clkspec; 2086be408bSSylwester Nawrocki int index, rc, num_parents; 2186be408bSSylwester Nawrocki struct clk *clk, *pclk; 2286be408bSSylwester Nawrocki 2386be408bSSylwester Nawrocki num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", 2486be408bSSylwester Nawrocki "#clock-cells"); 2586be408bSSylwester Nawrocki if (num_parents == -EINVAL) 2686be408bSSylwester Nawrocki pr_err("clk: invalid value of clock-parents property at %s\n", 2786be408bSSylwester Nawrocki node->full_name); 2886be408bSSylwester Nawrocki 2986be408bSSylwester Nawrocki for (index = 0; index < num_parents; index++) { 3086be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clock-parents", 3186be408bSSylwester Nawrocki "#clock-cells", index, &clkspec); 3286be408bSSylwester Nawrocki if (rc < 0) { 3386be408bSSylwester Nawrocki /* skip empty (null) phandles */ 3486be408bSSylwester Nawrocki if (rc == -ENOENT) 3586be408bSSylwester Nawrocki continue; 3686be408bSSylwester Nawrocki else 3786be408bSSylwester Nawrocki return rc; 3886be408bSSylwester Nawrocki } 3986be408bSSylwester Nawrocki if (clkspec.np == node && !clk_supplier) 4086be408bSSylwester Nawrocki return 0; 41306c342fSStephen Boyd pclk = of_clk_get_from_provider(&clkspec); 4286be408bSSylwester Nawrocki if (IS_ERR(pclk)) { 4386be408bSSylwester Nawrocki pr_warn("clk: couldn't get parent clock %d for %s\n", 4486be408bSSylwester Nawrocki index, node->full_name); 4586be408bSSylwester Nawrocki return PTR_ERR(pclk); 4686be408bSSylwester Nawrocki } 4786be408bSSylwester Nawrocki 4886be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clocks", 4986be408bSSylwester Nawrocki "#clock-cells", index, &clkspec); 5086be408bSSylwester Nawrocki if (rc < 0) 5186be408bSSylwester Nawrocki goto err; 5286be408bSSylwester Nawrocki if (clkspec.np == node && !clk_supplier) { 5386be408bSSylwester Nawrocki rc = 0; 5486be408bSSylwester Nawrocki goto err; 5586be408bSSylwester Nawrocki } 56306c342fSStephen Boyd clk = of_clk_get_from_provider(&clkspec); 576ba19bf0SDan Carpenter if (IS_ERR(clk)) { 5886be408bSSylwester Nawrocki pr_warn("clk: couldn't get parent clock %d for %s\n", 5986be408bSSylwester Nawrocki index, node->full_name); 606ba19bf0SDan Carpenter rc = PTR_ERR(clk); 6186be408bSSylwester Nawrocki goto err; 6286be408bSSylwester Nawrocki } 6386be408bSSylwester Nawrocki 6486be408bSSylwester Nawrocki rc = clk_set_parent(clk, pclk); 6586be408bSSylwester Nawrocki if (rc < 0) 6686be408bSSylwester Nawrocki pr_err("clk: failed to reparent %s to %s: %d\n", 6786be408bSSylwester Nawrocki __clk_get_name(clk), __clk_get_name(pclk), rc); 6886be408bSSylwester Nawrocki clk_put(clk); 6986be408bSSylwester Nawrocki clk_put(pclk); 7086be408bSSylwester Nawrocki } 7186be408bSSylwester Nawrocki return 0; 7286be408bSSylwester Nawrocki err: 7386be408bSSylwester Nawrocki clk_put(pclk); 7486be408bSSylwester Nawrocki return rc; 7586be408bSSylwester Nawrocki } 7686be408bSSylwester Nawrocki 7786be408bSSylwester Nawrocki static int __set_clk_rates(struct device_node *node, bool clk_supplier) 7886be408bSSylwester Nawrocki { 7986be408bSSylwester Nawrocki struct of_phandle_args clkspec; 8086be408bSSylwester Nawrocki struct property *prop; 8186be408bSSylwester Nawrocki const __be32 *cur; 8286be408bSSylwester Nawrocki int rc, index = 0; 8386be408bSSylwester Nawrocki struct clk *clk; 8486be408bSSylwester Nawrocki u32 rate; 8586be408bSSylwester Nawrocki 8686be408bSSylwester Nawrocki of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) { 8786be408bSSylwester Nawrocki if (rate) { 8886be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clocks", 8986be408bSSylwester Nawrocki "#clock-cells", index, &clkspec); 9086be408bSSylwester Nawrocki if (rc < 0) { 9186be408bSSylwester Nawrocki /* skip empty (null) phandles */ 9286be408bSSylwester Nawrocki if (rc == -ENOENT) 9386be408bSSylwester Nawrocki continue; 9486be408bSSylwester Nawrocki else 9586be408bSSylwester Nawrocki return rc; 9686be408bSSylwester Nawrocki } 9786be408bSSylwester Nawrocki if (clkspec.np == node && !clk_supplier) 9886be408bSSylwester Nawrocki return 0; 9986be408bSSylwester Nawrocki 100306c342fSStephen Boyd clk = of_clk_get_from_provider(&clkspec); 10186be408bSSylwester Nawrocki if (IS_ERR(clk)) { 10286be408bSSylwester Nawrocki pr_warn("clk: couldn't get clock %d for %s\n", 10386be408bSSylwester Nawrocki index, node->full_name); 10486be408bSSylwester Nawrocki return PTR_ERR(clk); 10586be408bSSylwester Nawrocki } 10686be408bSSylwester Nawrocki 10786be408bSSylwester Nawrocki rc = clk_set_rate(clk, rate); 10886be408bSSylwester Nawrocki if (rc < 0) 109*2885c3b2SChanwoo Choi pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n", 110*2885c3b2SChanwoo Choi __clk_get_name(clk), rate, rc, 111*2885c3b2SChanwoo Choi clk_get_rate(clk)); 11286be408bSSylwester Nawrocki clk_put(clk); 11386be408bSSylwester Nawrocki } 11486be408bSSylwester Nawrocki index++; 11586be408bSSylwester Nawrocki } 11686be408bSSylwester Nawrocki return 0; 11786be408bSSylwester Nawrocki } 11886be408bSSylwester Nawrocki 11986be408bSSylwester Nawrocki /** 12086be408bSSylwester Nawrocki * of_clk_set_defaults() - parse and set assigned clocks configuration 12186be408bSSylwester Nawrocki * @node: device node to apply clock settings for 12286be408bSSylwester Nawrocki * @clk_supplier: true if clocks supplied by @node should also be considered 12386be408bSSylwester Nawrocki * 12486be408bSSylwester Nawrocki * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties 12586be408bSSylwester Nawrocki * and sets any specified clock parents and rates. The @clk_supplier argument 12686be408bSSylwester Nawrocki * should be set to true if @node may be also a clock supplier of any clock 12786be408bSSylwester Nawrocki * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. 12886be408bSSylwester Nawrocki * If @clk_supplier is false the function exits returnning 0 as soon as it 12986be408bSSylwester Nawrocki * determines the @node is also a supplier of any of the clocks. 13086be408bSSylwester Nawrocki */ 13186be408bSSylwester Nawrocki int of_clk_set_defaults(struct device_node *node, bool clk_supplier) 13286be408bSSylwester Nawrocki { 13386be408bSSylwester Nawrocki int rc; 13486be408bSSylwester Nawrocki 13586be408bSSylwester Nawrocki if (!node) 13686be408bSSylwester Nawrocki return 0; 13786be408bSSylwester Nawrocki 13886be408bSSylwester Nawrocki rc = __set_clk_parents(node, clk_supplier); 13986be408bSSylwester Nawrocki if (rc < 0) 14086be408bSSylwester Nawrocki return rc; 14186be408bSSylwester Nawrocki 14286be408bSSylwester Nawrocki return __set_clk_rates(node, clk_supplier); 14386be408bSSylwester Nawrocki } 144b11a6facSSylwester Nawrocki EXPORT_SYMBOL_GPL(of_clk_set_defaults); 145