1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2016 Maxime Ripard
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
9 #include <linux/clk-provider.h>
10 #include <linux/device.h>
11 #include <linux/iopoll.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
15 #include "ccu_common.h"
17 #include "ccu_reset.h"
20 const struct sunxi_ccu_desc
*desc
;
22 struct ccu_reset reset
;
25 void ccu_helper_wait_for_lock(struct ccu_common
*common
, u32 lock
)
33 if (common
->features
& CCU_FEATURE_LOCK_REG
)
34 addr
= common
->base
+ common
->lock_reg
;
36 addr
= common
->base
+ common
->reg
;
38 WARN_ON(readl_relaxed_poll_timeout(addr
, reg
, reg
& lock
, 100, 70000));
40 EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock
, "SUNXI_CCU");
42 bool ccu_is_better_rate(struct ccu_common
*common
,
43 unsigned long target_rate
,
44 unsigned long current_rate
,
45 unsigned long best_rate
)
47 unsigned long min_rate
, max_rate
;
49 clk_hw_get_rate_range(&common
->hw
, &min_rate
, &max_rate
);
51 if (current_rate
> max_rate
)
54 if (current_rate
< min_rate
)
57 if (common
->features
& CCU_FEATURE_CLOSEST_RATE
)
58 return abs(current_rate
- target_rate
) < abs(best_rate
- target_rate
);
60 return current_rate
<= target_rate
&& current_rate
> best_rate
;
62 EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate
, "SUNXI_CCU");
65 * This clock notifier is called when the frequency of a PLL clock is
66 * changed. In common PLL designs, changes to the dividers take effect
67 * almost immediately, while changes to the multipliers (implemented
68 * as dividers in the feedback loop) take a few cycles to work into
69 * the feedback loop for the PLL to stablize.
71 * Sometimes when the PLL clock rate is changed, the decrease in the
72 * divider is too much for the decrease in the multiplier to catch up.
73 * The PLL clock rate will spike, and in some cases, might lock up
76 * This notifier callback will gate and then ungate the clock,
77 * effectively resetting it, so it proceeds to work. Care must be
78 * taken to reparent consumers to other temporary clocks during the
79 * rate change, and that this notifier callback must be the first
82 static int ccu_pll_notifier_cb(struct notifier_block
*nb
,
83 unsigned long event
, void *data
)
85 struct ccu_pll_nb
*pll
= to_ccu_pll_nb(nb
);
88 if (event
!= POST_RATE_CHANGE
)
91 ccu_gate_helper_disable(pll
->common
, pll
->enable
);
93 ret
= ccu_gate_helper_enable(pll
->common
, pll
->enable
);
97 ccu_helper_wait_for_lock(pll
->common
, pll
->lock
);
100 return notifier_from_errno(ret
);
103 int ccu_pll_notifier_register(struct ccu_pll_nb
*pll_nb
)
105 pll_nb
->clk_nb
.notifier_call
= ccu_pll_notifier_cb
;
107 return clk_notifier_register(pll_nb
->common
->hw
.clk
,
110 EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register
, "SUNXI_CCU");
112 static int sunxi_ccu_probe(struct sunxi_ccu
*ccu
, struct device
*dev
,
113 struct device_node
*node
, void __iomem
*reg
,
114 const struct sunxi_ccu_desc
*desc
)
116 struct ccu_reset
*reset
;
121 spin_lock_init(&ccu
->lock
);
123 for (i
= 0; i
< desc
->num_ccu_clks
; i
++) {
124 struct ccu_common
*cclk
= desc
->ccu_clks
[i
];
130 cclk
->lock
= &ccu
->lock
;
133 for (i
= 0; i
< desc
->hw_clks
->num
; i
++) {
134 struct clk_hw
*hw
= desc
->hw_clks
->hws
[i
];
140 name
= hw
->init
->name
;
142 ret
= clk_hw_register(dev
, hw
);
144 ret
= of_clk_hw_register(node
, hw
);
146 pr_err("Couldn't register clock %d - %s\n", i
, name
);
151 for (i
= 0; i
< desc
->num_ccu_clks
; i
++) {
152 struct ccu_common
*cclk
= desc
->ccu_clks
[i
];
158 clk_hw_set_rate_range(&cclk
->hw
, cclk
->min_rate
,
162 "No max_rate, ignoring min_rate of clock %d - %s\n",
163 i
, clk_hw_get_name(&cclk
->hw
));
166 ret
= of_clk_add_hw_provider(node
, of_clk_hw_onecell_get
,
172 reset
->rcdev
.of_node
= node
;
173 reset
->rcdev
.ops
= &ccu_reset_ops
;
174 reset
->rcdev
.owner
= dev
? dev
->driver
->owner
: THIS_MODULE
;
175 reset
->rcdev
.nr_resets
= desc
->num_resets
;
177 reset
->lock
= &ccu
->lock
;
178 reset
->reset_map
= desc
->resets
;
180 ret
= reset_controller_register(&reset
->rcdev
);
182 goto err_del_provider
;
187 of_clk_del_provider(node
);
190 struct clk_hw
*hw
= desc
->hw_clks
->hws
[i
];
194 clk_hw_unregister(hw
);
199 static void devm_sunxi_ccu_release(struct device
*dev
, void *res
)
201 struct sunxi_ccu
*ccu
= res
;
202 const struct sunxi_ccu_desc
*desc
= ccu
->desc
;
205 reset_controller_unregister(&ccu
->reset
.rcdev
);
206 of_clk_del_provider(dev
->of_node
);
208 for (i
= 0; i
< desc
->hw_clks
->num
; i
++) {
209 struct clk_hw
*hw
= desc
->hw_clks
->hws
[i
];
213 clk_hw_unregister(hw
);
217 int devm_sunxi_ccu_probe(struct device
*dev
, void __iomem
*reg
,
218 const struct sunxi_ccu_desc
*desc
)
220 struct sunxi_ccu
*ccu
;
223 ccu
= devres_alloc(devm_sunxi_ccu_release
, sizeof(*ccu
), GFP_KERNEL
);
227 ret
= sunxi_ccu_probe(ccu
, dev
, dev
->of_node
, reg
, desc
);
233 devres_add(dev
, ccu
);
237 EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe
, "SUNXI_CCU");
239 void of_sunxi_ccu_probe(struct device_node
*node
, void __iomem
*reg
,
240 const struct sunxi_ccu_desc
*desc
)
242 struct sunxi_ccu
*ccu
;
245 ccu
= kzalloc(sizeof(*ccu
), GFP_KERNEL
);
249 ret
= sunxi_ccu_probe(ccu
, NULL
, node
, reg
, desc
);
251 pr_err("%pOF: probing clocks failed: %d\n", node
, ret
);
256 MODULE_DESCRIPTION("Common clock support for Allwinner SoCs");
257 MODULE_LICENSE("GPL");