322a1660d0c0d3748ecd83c8bc6f6c8432f8d748
[openwrt/openwrt.git] / target / linux / bcm53xx / patches-4.4 / 830-clk-bcm-Add-driver-for-BCM53573-ILP-clock.patch
1 From 1909c8bf322e0bbb068d57c370c5b64906a8c5a7 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
3 Date: Fri, 29 Jul 2016 14:48:19 +0200
4 Subject: [PATCH V6] clk: bcm: Add driver for BCM53573 ILP clock
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 This clock is present on BCM53573 devices (including BCM47189) that use
10 Cortex-A7. ILP is a part of PMU (Power Management Unit) and so it should
11 be defined as one of its subnodes (subdevices). For more details see
12 Documentation entry.
13
14 Unfortunately there isn't a set of registers related to ILP clock only.
15 We use registers 0x66c, 0x674 and 0x6dc and between them there are e.g.
16 "retention*" and "control_ext" regs. This is why this driver maps all
17 0x1000 B of space.
18
19 Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
20 ---
21 V2: Rebase on top of clk-next
22 Use ALP as parent clock
23 Improve comments
24 Switch from ioremap_nocache to ioremap
25 Check of_clk_add_provide result for error
26 V3: Drop #include <linux/moduleh>
27 Make ILP DT entry part of PMU
28 Describe ILP as subdevice of PMU in Documentation
29 V4: Use BCM53573 name as suggested by Jon and Ray. It seems "Northstar"
30 (even if used in some resources) should be used in relation to
31 Cortex-A9 devices only.
32 V5: Rename remaining "ns" references to "bcm53573", sorry, I sent V4 too
33 early.
34 V6: Drop #include <linux/clk.h>
35 Use "int" as type where it matches usage
36 Add cpu_relax() in the loop
37 Add disable callback
38 Use _hw_ functions for registering struct clk_hw (new API)
39
40 Thanks a lot Stephen!
41 ---
42 .../bindings/clock/brcm,bcm53573-ilp.txt | 40 ++++++
43 drivers/clk/bcm/Makefile | 1 +
44 drivers/clk/bcm/clk-bcm53573-ilp.c | 157 +++++++++++++++++++++
45 3 files changed, 198 insertions(+)
46 create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.txt
47 create mode 100644 drivers/clk/bcm/clk-bcm53573-ilp.c
48
49 --- /dev/null
50 +++ b/Documentation/devicetree/bindings/clock/brcm,bcm53573-ilp.txt
51 @@ -0,0 +1,40 @@
52 +Broadcom BCM53573 ILP clock
53 +===========================
54 +
55 +This binding uses the common clock binding:
56 + Documentation/devicetree/bindings/clock/clock-bindings.txt
57 +
58 +This binding is used for ILP clock (sometimes referred as "slow clock")
59 +on Broadcom BCM53573 devices using Cortex-A7 CPU.
60 +
61 +This clock is part of PMU (Power Management Unit), a Broadcom's device
62 +handing power-related aspects. Please note PMU contains more subdevices,
63 +ILP is only one of them.
64 +
65 +ILP's rate has to be calculated on runtime and it depends on ALP clock
66 +which has to be referenced.
67 +
68 +Required properties:
69 +- compatible: "brcm,bcm53573-ilp"
70 +- reg: iomem address range of PMU (Power Management Unit)
71 +- reg-names: "pmu", the only needed & supported reg right now
72 +- clocks: has to reference an ALP clock
73 +- #clock-cells: should be <0>
74 +
75 +Example:
76 +
77 +pmu@18012000 {
78 + compatible = "simple-bus";
79 + ranges = <0x00000000 0x18012000 0x00001000>;
80 + #address-cells = <1>;
81 + #size-cells = <1>;
82 +
83 + ilp: ilp@0 {
84 + compatible = "brcm,bcm53573-ilp";
85 + reg = <0 0x1000>;
86 + reg-names = "pmu";
87 + clocks = <&alp>;
88 + #clock-cells = <0>;
89 + clock-output-names = "ilp";
90 + };
91 +};
92 --- a/drivers/clk/bcm/Makefile
93 +++ b/drivers/clk/bcm/Makefile
94 @@ -8,3 +8,4 @@ obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns
95 obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o
96 obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o
97 obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o
98 +obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
99 --- /dev/null
100 +++ b/drivers/clk/bcm/clk-bcm53573-ilp.c
101 @@ -0,0 +1,157 @@
102 +/*
103 + * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
104 + *
105 + * This program is free software; you can redistribute it and/or modify
106 + * it under the terms of the GNU General Public License version 2 as
107 + * published by the Free Software Foundation.
108 + */
109 +
110 +#include <linux/clk-provider.h>
111 +#include <linux/err.h>
112 +#include <linux/io.h>
113 +#include <linux/of.h>
114 +#include <linux/of_address.h>
115 +#include <linux/slab.h>
116 +
117 +#define PMU_XTAL_FREQ_RATIO 0x66c
118 +#define XTAL_ALP_PER_4ILP 0x00001fff
119 +#define XTAL_CTL_EN 0x80000000
120 +#define PMU_SLOW_CLK_PERIOD 0x6dc
121 +
122 +struct bcm53573_ilp {
123 + struct clk_hw hw;
124 + void __iomem *pmu;
125 +};
126 +
127 +static int bcm53573_ilp_enable(struct clk_hw *hw)
128 +{
129 + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
130 +
131 + writel(0x10199, ilp->pmu + PMU_SLOW_CLK_PERIOD);
132 + writel(0x10000, ilp->pmu + 0x674);
133 +
134 + return 0;
135 +}
136 +
137 +static void bcm53573_ilp_disable(struct clk_hw *hw)
138 +{
139 + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
140 +
141 + writel(0, ilp->pmu + PMU_SLOW_CLK_PERIOD);
142 + writel(0, ilp->pmu + 0x674);
143 +}
144 +
145 +static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,
146 + unsigned long parent_rate)
147 +{
148 + struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
149 + void __iomem *pmu = ilp->pmu;
150 + u32 last_val, cur_val;
151 + int sum = 0, num = 0, loop_num = 0;
152 + int avg;
153 +
154 + /* Enable measurement */
155 + writel(XTAL_CTL_EN, pmu + PMU_XTAL_FREQ_RATIO);
156 +
157 + /* Read initial value */
158 + last_val = readl(pmu + PMU_XTAL_FREQ_RATIO) & XTAL_ALP_PER_4ILP;
159 +
160 + /*
161 + * At minimum we should loop for a bit to let hardware do the
162 + * measurement. This isn't very accurate however, so for a better
163 + * precision lets try getting 20 different values for and use average.
164 + */
165 + while (num < 20) {
166 + cur_val = readl(pmu + PMU_XTAL_FREQ_RATIO) & XTAL_ALP_PER_4ILP;
167 +
168 + if (cur_val != last_val) {
169 + /* Got different value, use it */
170 + sum += cur_val;
171 + num++;
172 + loop_num = 0;
173 + last_val = cur_val;
174 + } else if (++loop_num > 5000) {
175 + /* Same value over and over, give up */
176 + sum += cur_val;
177 + num++;
178 + break;
179 + }
180 +
181 + cpu_relax();
182 + }
183 +
184 + /* Disable measurement to save power */
185 + writel(0x0, pmu + PMU_XTAL_FREQ_RATIO);
186 +
187 + avg = sum / num;
188 +
189 + return parent_rate * 4 / avg;
190 +}
191 +
192 +static const struct clk_ops bcm53573_ilp_clk_ops = {
193 + .enable = bcm53573_ilp_enable,
194 + .disable = bcm53573_ilp_disable,
195 + .recalc_rate = bcm53573_ilp_recalc_rate,
196 +};
197 +
198 +static void bcm53573_ilp_init(struct device_node *np)
199 +{
200 + struct bcm53573_ilp *ilp;
201 + struct resource res;
202 + struct clk_init_data init = { 0 };
203 + const char *parent_name;
204 + int index;
205 + int err;
206 +
207 + ilp = kzalloc(sizeof(*ilp), GFP_KERNEL);
208 + if (!ilp)
209 + return;
210 +
211 + parent_name = of_clk_get_parent_name(np, 0);
212 + if (!parent_name) {
213 + err = -ENOENT;
214 + goto err_free_ilp;
215 + }
216 +
217 + /* TODO: This looks generic, try making it OF helper. */
218 + index = of_property_match_string(np, "reg-names", "pmu");
219 + if (index < 0) {
220 + err = index;
221 + goto err_free_ilp;
222 + }
223 + err = of_address_to_resource(np, index, &res);
224 + if (err)
225 + goto err_free_ilp;
226 + ilp->pmu = ioremap(res.start, resource_size(&res));
227 + if (IS_ERR(ilp->pmu)) {
228 + err = PTR_ERR(ilp->pmu);
229 + goto err_free_ilp;
230 + }
231 +
232 + init.name = np->name;
233 + init.ops = &bcm53573_ilp_clk_ops;
234 + init.parent_names = &parent_name;
235 + init.num_parents = 1;
236 +
237 + ilp->hw.init = &init;
238 + err = clk_hw_register(NULL, &ilp->hw);
239 + if (err)
240 + goto err_unmap_pmu;
241 +
242 + err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw);
243 + if (err)
244 + goto err_clk_hw_unregister;
245 +
246 + return;
247 +
248 +err_clk_hw_unregister:
249 + clk_hw_unregister(&ilp->hw);
250 +err_unmap_pmu:
251 + iounmap(ilp->pmu);
252 +err_free_ilp:
253 + kfree(ilp);
254 + pr_err("Failed to init ILP clock: %d\n", err);
255 +}
256 +
257 +/* We need it very early for arch code, before device model gets ready */
258 +CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init);