xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_div.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e9b93213SMaxime Ripard /*
3e9b93213SMaxime Ripard  * Copyright (C) 2016 Maxime Ripard
4e9b93213SMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
5e9b93213SMaxime Ripard  */
6e9b93213SMaxime Ripard 
7e9b93213SMaxime Ripard #include <linux/clk-provider.h>
862e59c4eSStephen Boyd #include <linux/io.h>
9e9b93213SMaxime Ripard 
10e9b93213SMaxime Ripard #include "ccu_gate.h"
11e9b93213SMaxime Ripard #include "ccu_div.h"
12e9b93213SMaxime Ripard 
ccu_div_round_rate(struct ccu_mux_internal * mux,struct clk_hw * parent,unsigned long * parent_rate,unsigned long rate,void * data)13e9b93213SMaxime Ripard static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
1410a8d9b9SMaxime Ripard 					struct clk_hw *parent,
1510a8d9b9SMaxime Ripard 					unsigned long *parent_rate,
16e9b93213SMaxime Ripard 					unsigned long rate,
17e9b93213SMaxime Ripard 					void *data)
18e9b93213SMaxime Ripard {
19e9b93213SMaxime Ripard 	struct ccu_div *cd = data;
20e9b93213SMaxime Ripard 
21721353c0SPriit Laes 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
22721353c0SPriit Laes 		rate *= cd->fixed_post_div;
23721353c0SPriit Laes 
24721353c0SPriit Laes 	rate = divider_round_rate_parent(&cd->common.hw, parent,
25e69b2afaSMaxime Ripard 					 rate, parent_rate,
26e69b2afaSMaxime Ripard 					 cd->div.table, cd->div.width,
27e9b93213SMaxime Ripard 					 cd->div.flags);
28721353c0SPriit Laes 
29721353c0SPriit Laes 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
30721353c0SPriit Laes 		rate /= cd->fixed_post_div;
31721353c0SPriit Laes 
32721353c0SPriit Laes 	return rate;
33e9b93213SMaxime Ripard }
34e9b93213SMaxime Ripard 
ccu_div_disable(struct clk_hw * hw)35e9b93213SMaxime Ripard static void ccu_div_disable(struct clk_hw *hw)
36e9b93213SMaxime Ripard {
37e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
38e9b93213SMaxime Ripard 
39e9b93213SMaxime Ripard 	return ccu_gate_helper_disable(&cd->common, cd->enable);
40e9b93213SMaxime Ripard }
41e9b93213SMaxime Ripard 
ccu_div_enable(struct clk_hw * hw)42e9b93213SMaxime Ripard static int ccu_div_enable(struct clk_hw *hw)
43e9b93213SMaxime Ripard {
44e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
45e9b93213SMaxime Ripard 
46e9b93213SMaxime Ripard 	return ccu_gate_helper_enable(&cd->common, cd->enable);
47e9b93213SMaxime Ripard }
48e9b93213SMaxime Ripard 
ccu_div_is_enabled(struct clk_hw * hw)49e9b93213SMaxime Ripard static int ccu_div_is_enabled(struct clk_hw *hw)
50e9b93213SMaxime Ripard {
51e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
52e9b93213SMaxime Ripard 
53e9b93213SMaxime Ripard 	return ccu_gate_helper_is_enabled(&cd->common, cd->enable);
54e9b93213SMaxime Ripard }
55e9b93213SMaxime Ripard 
ccu_div_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)56e9b93213SMaxime Ripard static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
57e9b93213SMaxime Ripard 					unsigned long parent_rate)
58e9b93213SMaxime Ripard {
59e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
60e9b93213SMaxime Ripard 	unsigned long val;
61e9b93213SMaxime Ripard 	u32 reg;
62e9b93213SMaxime Ripard 
63e9b93213SMaxime Ripard 	reg = readl(cd->common.base + cd->common.reg);
64e9b93213SMaxime Ripard 	val = reg >> cd->div.shift;
65e9b93213SMaxime Ripard 	val &= (1 << cd->div.width) - 1;
66e9b93213SMaxime Ripard 
67d754b159SMaxime Ripard 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
68d754b159SMaxime Ripard 						  parent_rate);
69e9b93213SMaxime Ripard 
70721353c0SPriit Laes 	val = divider_recalc_rate(hw, parent_rate, val, cd->div.table,
7112a26c29SJerome Brunet 				  cd->div.flags, cd->div.width);
72721353c0SPriit Laes 
73721353c0SPriit Laes 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
74721353c0SPriit Laes 		val /= cd->fixed_post_div;
75721353c0SPriit Laes 
76721353c0SPriit Laes 	return val;
77e9b93213SMaxime Ripard }
78e9b93213SMaxime Ripard 
ccu_div_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)79e9b93213SMaxime Ripard static int ccu_div_determine_rate(struct clk_hw *hw,
80e9b93213SMaxime Ripard 				struct clk_rate_request *req)
81e9b93213SMaxime Ripard {
82e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
83e9b93213SMaxime Ripard 
84e9b93213SMaxime Ripard 	return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
85e9b93213SMaxime Ripard 					     req, ccu_div_round_rate, cd);
86e9b93213SMaxime Ripard }
87e9b93213SMaxime Ripard 
ccu_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)88e9b93213SMaxime Ripard static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
89e9b93213SMaxime Ripard 			   unsigned long parent_rate)
90e9b93213SMaxime Ripard {
91e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
92e9b93213SMaxime Ripard 	unsigned long flags;
93e9b93213SMaxime Ripard 	unsigned long val;
94e9b93213SMaxime Ripard 	u32 reg;
95e9b93213SMaxime Ripard 
96d754b159SMaxime Ripard 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
97d754b159SMaxime Ripard 						  parent_rate);
98e9b93213SMaxime Ripard 
99721353c0SPriit Laes 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
100721353c0SPriit Laes 		rate *= cd->fixed_post_div;
101721353c0SPriit Laes 
102e9b93213SMaxime Ripard 	val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
103e9b93213SMaxime Ripard 			      cd->div.flags);
104e9b93213SMaxime Ripard 
105e9b93213SMaxime Ripard 	spin_lock_irqsave(cd->common.lock, flags);
106e9b93213SMaxime Ripard 
107e9b93213SMaxime Ripard 	reg = readl(cd->common.base + cd->common.reg);
108e9b93213SMaxime Ripard 	reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
109e9b93213SMaxime Ripard 
110e9b93213SMaxime Ripard 	writel(reg | (val << cd->div.shift),
111e9b93213SMaxime Ripard 	       cd->common.base + cd->common.reg);
112e9b93213SMaxime Ripard 
113e9b93213SMaxime Ripard 	spin_unlock_irqrestore(cd->common.lock, flags);
114e9b93213SMaxime Ripard 
115e9b93213SMaxime Ripard 	return 0;
116e9b93213SMaxime Ripard }
117e9b93213SMaxime Ripard 
ccu_div_get_parent(struct clk_hw * hw)118e9b93213SMaxime Ripard static u8 ccu_div_get_parent(struct clk_hw *hw)
119e9b93213SMaxime Ripard {
120e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
121e9b93213SMaxime Ripard 
122e9b93213SMaxime Ripard 	return ccu_mux_helper_get_parent(&cd->common, &cd->mux);
123e9b93213SMaxime Ripard }
124e9b93213SMaxime Ripard 
ccu_div_set_parent(struct clk_hw * hw,u8 index)125e9b93213SMaxime Ripard static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
126e9b93213SMaxime Ripard {
127e9b93213SMaxime Ripard 	struct ccu_div *cd = hw_to_ccu_div(hw);
128e9b93213SMaxime Ripard 
129e9b93213SMaxime Ripard 	return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index);
130e9b93213SMaxime Ripard }
131e9b93213SMaxime Ripard 
132e9b93213SMaxime Ripard const struct clk_ops ccu_div_ops = {
133e9b93213SMaxime Ripard 	.disable	= ccu_div_disable,
134e9b93213SMaxime Ripard 	.enable		= ccu_div_enable,
135e9b93213SMaxime Ripard 	.is_enabled	= ccu_div_is_enabled,
136e9b93213SMaxime Ripard 
137e9b93213SMaxime Ripard 	.get_parent	= ccu_div_get_parent,
138e9b93213SMaxime Ripard 	.set_parent	= ccu_div_set_parent,
139e9b93213SMaxime Ripard 
140e9b93213SMaxime Ripard 	.determine_rate	= ccu_div_determine_rate,
141e9b93213SMaxime Ripard 	.recalc_rate	= ccu_div_recalc_rate,
142e9b93213SMaxime Ripard 	.set_rate	= ccu_div_set_rate,
143e9b93213SMaxime Ripard };
144*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_div_ops, SUNXI_CCU);
145