xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_div.c (revision 4da722ca)
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  * Maxime Ripard <maxime.ripard@free-electrons.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  */
10 
11 #include <linux/clk-provider.h>
12 
13 #include "ccu_gate.h"
14 #include "ccu_div.h"
15 
16 static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
17 					struct clk_hw *parent,
18 					unsigned long *parent_rate,
19 					unsigned long rate,
20 					void *data)
21 {
22 	struct ccu_div *cd = data;
23 
24 	return divider_round_rate_parent(&cd->common.hw, parent,
25 					 rate, parent_rate,
26 					 cd->div.table, cd->div.width,
27 					 cd->div.flags);
28 }
29 
30 static void ccu_div_disable(struct clk_hw *hw)
31 {
32 	struct ccu_div *cd = hw_to_ccu_div(hw);
33 
34 	return ccu_gate_helper_disable(&cd->common, cd->enable);
35 }
36 
37 static int ccu_div_enable(struct clk_hw *hw)
38 {
39 	struct ccu_div *cd = hw_to_ccu_div(hw);
40 
41 	return ccu_gate_helper_enable(&cd->common, cd->enable);
42 }
43 
44 static int ccu_div_is_enabled(struct clk_hw *hw)
45 {
46 	struct ccu_div *cd = hw_to_ccu_div(hw);
47 
48 	return ccu_gate_helper_is_enabled(&cd->common, cd->enable);
49 }
50 
51 static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
52 					unsigned long parent_rate)
53 {
54 	struct ccu_div *cd = hw_to_ccu_div(hw);
55 	unsigned long val;
56 	u32 reg;
57 
58 	reg = readl(cd->common.base + cd->common.reg);
59 	val = reg >> cd->div.shift;
60 	val &= (1 << cd->div.width) - 1;
61 
62 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
63 						  parent_rate);
64 
65 	return divider_recalc_rate(hw, parent_rate, val, cd->div.table,
66 				   cd->div.flags);
67 }
68 
69 static int ccu_div_determine_rate(struct clk_hw *hw,
70 				struct clk_rate_request *req)
71 {
72 	struct ccu_div *cd = hw_to_ccu_div(hw);
73 
74 	return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
75 					     req, ccu_div_round_rate, cd);
76 }
77 
78 static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
79 			   unsigned long parent_rate)
80 {
81 	struct ccu_div *cd = hw_to_ccu_div(hw);
82 	unsigned long flags;
83 	unsigned long val;
84 	u32 reg;
85 
86 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
87 						  parent_rate);
88 
89 	val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
90 			      cd->div.flags);
91 
92 	spin_lock_irqsave(cd->common.lock, flags);
93 
94 	reg = readl(cd->common.base + cd->common.reg);
95 	reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
96 
97 	writel(reg | (val << cd->div.shift),
98 	       cd->common.base + cd->common.reg);
99 
100 	spin_unlock_irqrestore(cd->common.lock, flags);
101 
102 	return 0;
103 }
104 
105 static u8 ccu_div_get_parent(struct clk_hw *hw)
106 {
107 	struct ccu_div *cd = hw_to_ccu_div(hw);
108 
109 	return ccu_mux_helper_get_parent(&cd->common, &cd->mux);
110 }
111 
112 static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
113 {
114 	struct ccu_div *cd = hw_to_ccu_div(hw);
115 
116 	return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index);
117 }
118 
119 const struct clk_ops ccu_div_ops = {
120 	.disable	= ccu_div_disable,
121 	.enable		= ccu_div_enable,
122 	.is_enabled	= ccu_div_is_enabled,
123 
124 	.get_parent	= ccu_div_get_parent,
125 	.set_parent	= ccu_div_set_parent,
126 
127 	.determine_rate	= ccu_div_determine_rate,
128 	.recalc_rate	= ccu_div_recalc_rate,
129 	.set_rate	= ccu_div_set_rate,
130 };
131