Lines Matching +full:composite +full:- +full:divider +full:- +full:clock
1 // SPDX-License-Identifier: GPL-2.0
5 * Alexandre Belloni <alexandre.belloni@free-electrons.com>
9 #include <linux/clk-provider.h>
16 #include "berlin2-div.h"
19 * Clock dividers in Berlin2 SoCs comprise a complex cell to select
20 * input pll and divider. The virtual structure as it is used in Marvell
23 * +---+
24 * pll0 --------------->| 0 | +---+
25 * +---+ |(B)|--+--------------->| 0 | +---+
26 * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
27 * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
28 * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
29 * ... -->| | +-->|(D) 1:3 |----------+ +---+
30 * pll1.N -->| N | +---------
31 * +---+
33 * (A) input pll clock mux controlled by <PllSelect[1:n]>
35 * (C) programmable clock divider controlled by <Select[1:n]>
36 * (D) constant div-by-3 clock divider
37 * (E) programmable clock divider bypass controlled by <Switch>
38 * (F) constant div-by-3 clock mux controlled by <D3Switch>
39 * (G) clock gate controlled by <Enable>
42 * - single register dividers with all bits in one register
43 * - shared register dividers with bits spread over multiple registers
46 * Also, clock gate and pll mux is not available on every div cell, so
47 * we have to deal with those, too. We reuse common clock composite driver
68 struct berlin2_div_map *map = &div->map; in berlin2_div_is_enabled()
71 if (div->lock) in berlin2_div_is_enabled()
72 spin_lock(div->lock); in berlin2_div_is_enabled()
74 reg = readl_relaxed(div->base + map->gate_offs); in berlin2_div_is_enabled()
75 reg >>= map->gate_shift; in berlin2_div_is_enabled()
77 if (div->lock) in berlin2_div_is_enabled()
78 spin_unlock(div->lock); in berlin2_div_is_enabled()
86 struct berlin2_div_map *map = &div->map; in berlin2_div_enable()
89 if (div->lock) in berlin2_div_enable()
90 spin_lock(div->lock); in berlin2_div_enable()
92 reg = readl_relaxed(div->base + map->gate_offs); in berlin2_div_enable()
93 reg |= BIT(map->gate_shift); in berlin2_div_enable()
94 writel_relaxed(reg, div->base + map->gate_offs); in berlin2_div_enable()
96 if (div->lock) in berlin2_div_enable()
97 spin_unlock(div->lock); in berlin2_div_enable()
105 struct berlin2_div_map *map = &div->map; in berlin2_div_disable()
108 if (div->lock) in berlin2_div_disable()
109 spin_lock(div->lock); in berlin2_div_disable()
111 reg = readl_relaxed(div->base + map->gate_offs); in berlin2_div_disable()
112 reg &= ~BIT(map->gate_shift); in berlin2_div_disable()
113 writel_relaxed(reg, div->base + map->gate_offs); in berlin2_div_disable()
115 if (div->lock) in berlin2_div_disable()
116 spin_unlock(div->lock); in berlin2_div_disable()
122 struct berlin2_div_map *map = &div->map; in berlin2_div_set_parent()
125 if (div->lock) in berlin2_div_set_parent()
126 spin_lock(div->lock); in berlin2_div_set_parent()
129 reg = readl_relaxed(div->base + map->pll_switch_offs); in berlin2_div_set_parent()
131 reg &= ~BIT(map->pll_switch_shift); in berlin2_div_set_parent()
133 reg |= BIT(map->pll_switch_shift); in berlin2_div_set_parent()
134 writel_relaxed(reg, div->base + map->pll_switch_offs); in berlin2_div_set_parent()
138 reg = readl_relaxed(div->base + map->pll_select_offs); in berlin2_div_set_parent()
139 reg &= ~(PLL_SELECT_MASK << map->pll_select_shift); in berlin2_div_set_parent()
140 reg |= (index - 1) << map->pll_select_shift; in berlin2_div_set_parent()
141 writel_relaxed(reg, div->base + map->pll_select_offs); in berlin2_div_set_parent()
144 if (div->lock) in berlin2_div_set_parent()
145 spin_unlock(div->lock); in berlin2_div_set_parent()
153 struct berlin2_div_map *map = &div->map; in berlin2_div_get_parent()
157 if (div->lock) in berlin2_div_get_parent()
158 spin_lock(div->lock); in berlin2_div_get_parent()
161 reg = readl_relaxed(div->base + map->pll_switch_offs); in berlin2_div_get_parent()
162 reg &= BIT(map->pll_switch_shift); in berlin2_div_get_parent()
164 reg = readl_relaxed(div->base + map->pll_select_offs); in berlin2_div_get_parent()
165 reg >>= map->pll_select_shift; in berlin2_div_get_parent()
170 if (div->lock) in berlin2_div_get_parent()
171 spin_unlock(div->lock); in berlin2_div_get_parent()
180 struct berlin2_div_map *map = &div->map; in berlin2_div_recalc_rate()
181 u32 divsw, div3sw, divider = 1; in berlin2_div_recalc_rate() local
183 if (div->lock) in berlin2_div_recalc_rate()
184 spin_lock(div->lock); in berlin2_div_recalc_rate()
186 divsw = readl_relaxed(div->base + map->div_switch_offs) & in berlin2_div_recalc_rate()
187 (1 << map->div_switch_shift); in berlin2_div_recalc_rate()
188 div3sw = readl_relaxed(div->base + map->div3_switch_offs) & in berlin2_div_recalc_rate()
189 (1 << map->div3_switch_shift); in berlin2_div_recalc_rate()
191 /* constant divide-by-3 (dominant) */ in berlin2_div_recalc_rate()
193 divider = 3; in berlin2_div_recalc_rate()
194 /* divider can be bypassed with DIV_SWITCH == 0 */ in berlin2_div_recalc_rate()
196 divider = 1; in berlin2_div_recalc_rate()
197 /* clock divider determined by DIV_SELECT */ in berlin2_div_recalc_rate()
200 reg = readl_relaxed(div->base + map->div_select_offs); in berlin2_div_recalc_rate()
201 reg >>= map->div_select_shift; in berlin2_div_recalc_rate()
203 divider = clk_div[reg]; in berlin2_div_recalc_rate()
206 if (div->lock) in berlin2_div_recalc_rate()
207 spin_unlock(div->lock); in berlin2_div_recalc_rate()
209 return parent_rate / divider; in berlin2_div_recalc_rate()
241 return ERR_PTR(-ENOMEM); in berlin2_div_register()
244 memcpy(&div->map, map, sizeof(*map)); in berlin2_div_register()
245 div->base = base; in berlin2_div_register()
246 div->lock = lock; in berlin2_div_register()
254 &div->hw, mux_ops, &div->hw, rate_ops, in berlin2_div_register()
255 &div->hw, gate_ops, flags); in berlin2_div_register()