xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_mult.c (revision 93032e31)
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_mult.h"
15 
16 static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
17 			       unsigned int max_n, unsigned int *n)
18 {
19 	*n = rate / parent;
20 }
21 
22 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
23 					unsigned long parent_rate,
24 					unsigned long rate,
25 					void *data)
26 {
27 	struct ccu_mult *cm = data;
28 	unsigned int n;
29 
30 	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
31 
32 	return parent_rate * n;
33 }
34 
35 static void ccu_mult_disable(struct clk_hw *hw)
36 {
37 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
38 
39 	return ccu_gate_helper_disable(&cm->common, cm->enable);
40 }
41 
42 static int ccu_mult_enable(struct clk_hw *hw)
43 {
44 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
45 
46 	return ccu_gate_helper_enable(&cm->common, cm->enable);
47 }
48 
49 static int ccu_mult_is_enabled(struct clk_hw *hw)
50 {
51 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
52 
53 	return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
54 }
55 
56 static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
57 					unsigned long parent_rate)
58 {
59 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
60 	unsigned long val;
61 	u32 reg;
62 
63 	reg = readl(cm->common.base + cm->common.reg);
64 	val = reg >> cm->mult.shift;
65 	val &= (1 << cm->mult.width) - 1;
66 
67 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
68 						&parent_rate);
69 
70 	return parent_rate * (val + 1);
71 }
72 
73 static int ccu_mult_determine_rate(struct clk_hw *hw,
74 				struct clk_rate_request *req)
75 {
76 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
77 
78 	return ccu_mux_helper_determine_rate(&cm->common, &cm->mux,
79 					     req, ccu_mult_round_rate, cm);
80 }
81 
82 static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
83 			   unsigned long parent_rate)
84 {
85 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
86 	unsigned long flags;
87 	unsigned int n;
88 	u32 reg;
89 
90 	ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
91 						&parent_rate);
92 
93 	ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
94 
95 	spin_lock_irqsave(cm->common.lock, flags);
96 
97 	reg = readl(cm->common.base + cm->common.reg);
98 	reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
99 
100 	writel(reg | ((n - 1) << cm->mult.shift),
101 	       cm->common.base + cm->common.reg);
102 
103 	spin_unlock_irqrestore(cm->common.lock, flags);
104 
105 	return 0;
106 }
107 
108 static u8 ccu_mult_get_parent(struct clk_hw *hw)
109 {
110 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
111 
112 	return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
113 }
114 
115 static int ccu_mult_set_parent(struct clk_hw *hw, u8 index)
116 {
117 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
118 
119 	return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
120 }
121 
122 const struct clk_ops ccu_mult_ops = {
123 	.disable	= ccu_mult_disable,
124 	.enable		= ccu_mult_enable,
125 	.is_enabled	= ccu_mult_is_enabled,
126 
127 	.get_parent	= ccu_mult_get_parent,
128 	.set_parent	= ccu_mult_set_parent,
129 
130 	.determine_rate	= ccu_mult_determine_rate,
131 	.recalc_rate	= ccu_mult_recalc_rate,
132 	.set_rate	= ccu_mult_set_rate,
133 };
134