Blame view

drivers/clk/sunxi-ng/ccu_nk.c 3.71 KB
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * Copyright (C) 2016 Maxime Ripard
   * Maxime Ripard <maxime.ripard@free-electrons.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation; either version 2 of
   * the License, or (at your option) any later version.
   */
  
  #include <linux/clk-provider.h>
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
12
13
14
  
  #include "ccu_gate.h"
  #include "ccu_nk.h"
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
15
  struct _ccu_nk {
6e0d50daa   Maxime Ripard   clk: sunxi-ng: Ad...
16
17
  	unsigned long	n, min_n, max_n;
  	unsigned long	k, min_k, max_k;
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
18
  };
06421a782   Chen-Yu Tsai   clk: sunxi-ng: nk...
19
  static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
20
  			     struct _ccu_nk *nk)
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
21
22
23
24
  {
  	unsigned long best_rate = 0;
  	unsigned int best_k = 0, best_n = 0;
  	unsigned int _k, _n;
6e0d50daa   Maxime Ripard   clk: sunxi-ng: Ad...
25
26
  	for (_k = nk->min_k; _k <= nk->max_k; _k++) {
  		for (_n = nk->min_n; _n <= nk->max_n; _n++) {
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
27
28
29
30
31
32
33
34
35
36
37
38
  			unsigned long tmp_rate = parent * _n * _k;
  
  			if (tmp_rate > rate)
  				continue;
  
  			if ((rate - tmp_rate) < (rate - best_rate)) {
  				best_rate = tmp_rate;
  				best_k = _k;
  				best_n = _n;
  			}
  		}
  	}
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
39
40
  	nk->k = best_k;
  	nk->n = best_n;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  }
  
  static void ccu_nk_disable(struct clk_hw *hw)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
  
  	return ccu_gate_helper_disable(&nk->common, nk->enable);
  }
  
  static int ccu_nk_enable(struct clk_hw *hw)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
  
  	return ccu_gate_helper_enable(&nk->common, nk->enable);
  }
  
  static int ccu_nk_is_enabled(struct clk_hw *hw)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
  
  	return ccu_gate_helper_is_enabled(&nk->common, nk->enable);
  }
  
  static unsigned long ccu_nk_recalc_rate(struct clk_hw *hw,
  					unsigned long parent_rate)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
  	unsigned long rate, n, k;
  	u32 reg;
  
  	reg = readl(nk->common.base + nk->common.reg);
  
  	n = reg >> nk->n.shift;
  	n &= (1 << nk->n.width) - 1;
e66f81bbd   Maxime Ripard   clk: sunxi-ng: Im...
75
76
77
  	n += nk->n.offset;
  	if (!n)
  		n++;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
78
79
80
  
  	k = reg >> nk->k.shift;
  	k &= (1 << nk->k.width) - 1;
e66f81bbd   Maxime Ripard   clk: sunxi-ng: Im...
81
82
83
  	k += nk->k.offset;
  	if (!k)
  		k++;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
84

e66f81bbd   Maxime Ripard   clk: sunxi-ng: Im...
85
  	rate = parent_rate * n * k;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
86
87
88
89
90
91
92
93
94
95
  	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
  		rate /= nk->fixed_post_div;
  
  	return rate;
  }
  
  static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
  			      unsigned long *parent_rate)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
96
  	struct _ccu_nk _nk;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
97
98
99
  
  	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
  		rate *= nk->fixed_post_div;
4162c5ce5   Chen-Yu Tsai   clk: sunxi-ng: us...
100
  	_nk.min_n = nk->n.min ?: 1;
0c3c8e135   Maxime Ripard   clk: sunxi-ng: Im...
101
  	_nk.max_n = nk->n.max ?: 1 << nk->n.width;
4162c5ce5   Chen-Yu Tsai   clk: sunxi-ng: us...
102
  	_nk.min_k = nk->k.min ?: 1;
0c3c8e135   Maxime Ripard   clk: sunxi-ng: Im...
103
  	_nk.max_k = nk->k.max ?: 1 << nk->k.width;
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
104
105
106
  
  	ccu_nk_find_best(*parent_rate, rate, &_nk);
  	rate = *parent_rate * _nk.n * _nk.k;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
107

adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
108
109
110
111
112
113
114
115
116
117
118
  	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
  		rate = rate / nk->fixed_post_div;
  
  	return rate;
  }
  
  static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
  			   unsigned long parent_rate)
  {
  	struct ccu_nk *nk = hw_to_ccu_nk(hw);
  	unsigned long flags;
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
119
  	struct _ccu_nk _nk;
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
120
121
122
123
  	u32 reg;
  
  	if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
  		rate = rate * nk->fixed_post_div;
4162c5ce5   Chen-Yu Tsai   clk: sunxi-ng: us...
124
  	_nk.min_n = nk->n.min ?: 1;
0c3c8e135   Maxime Ripard   clk: sunxi-ng: Im...
125
  	_nk.max_n = nk->n.max ?: 1 << nk->n.width;
4162c5ce5   Chen-Yu Tsai   clk: sunxi-ng: us...
126
  	_nk.min_k = nk->k.min ?: 1;
0c3c8e135   Maxime Ripard   clk: sunxi-ng: Im...
127
  	_nk.max_k = nk->k.max ?: 1 << nk->k.width;
b8302c726   Maxime Ripard   clk: sunxi-ng: Fi...
128
129
  
  	ccu_nk_find_best(parent_rate, rate, &_nk);
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
130
131
132
133
134
135
  
  	spin_lock_irqsave(nk->common.lock, flags);
  
  	reg = readl(nk->common.base + nk->common.reg);
  	reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift);
  	reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift);
e66f81bbd   Maxime Ripard   clk: sunxi-ng: Im...
136
137
138
  	reg |= (_nk.k - nk->k.offset) << nk->k.shift;
  	reg |= (_nk.n - nk->n.offset) << nk->n.shift;
  	writel(reg, nk->common.base + nk->common.reg);
adbfb0056   Maxime Ripard   clk: sunxi-ng: Ad...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  
  	spin_unlock_irqrestore(nk->common.lock, flags);
  
  	ccu_helper_wait_for_lock(&nk->common, nk->lock);
  
  	return 0;
  }
  
  const struct clk_ops ccu_nk_ops = {
  	.disable	= ccu_nk_disable,
  	.enable		= ccu_nk_enable,
  	.is_enabled	= ccu_nk_is_enabled,
  
  	.recalc_rate	= ccu_nk_recalc_rate,
  	.round_rate	= ccu_nk_round_rate,
  	.set_rate	= ccu_nk_set_rate,
  };