berlin2-div.c (6921B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2014 Marvell Technology Group Ltd. 4 * 5 * Alexandre Belloni <alexandre.belloni@free-electrons.com> 6 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 7 */ 8#include <linux/bitops.h> 9#include <linux/clk-provider.h> 10#include <linux/io.h> 11#include <linux/of.h> 12#include <linux/of_address.h> 13#include <linux/slab.h> 14#include <linux/spinlock.h> 15 16#include "berlin2-div.h" 17 18/* 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 21 * BSP code can be seen as: 22 * 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 * +---+ 32 * 33 * (A) input pll clock mux controlled by <PllSelect[1:n]> 34 * (B) input pll bypass mux controlled by <PllSwitch> 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> 40 * 41 * For whatever reason, above control signals come in two flavors: 42 * - single register dividers with all bits in one register 43 * - shared register dividers with bits spread over multiple registers 44 * (including signals for the same cell spread over consecutive registers) 45 * 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 48 * for it. 49 */ 50 51#define PLL_SELECT_MASK 0x7 52#define DIV_SELECT_MASK 0x7 53 54struct berlin2_div { 55 struct clk_hw hw; 56 void __iomem *base; 57 struct berlin2_div_map map; 58 spinlock_t *lock; 59}; 60 61#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw) 62 63static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 }; 64 65static int berlin2_div_is_enabled(struct clk_hw *hw) 66{ 67 struct berlin2_div *div = to_berlin2_div(hw); 68 struct berlin2_div_map *map = &div->map; 69 u32 reg; 70 71 if (div->lock) 72 spin_lock(div->lock); 73 74 reg = readl_relaxed(div->base + map->gate_offs); 75 reg >>= map->gate_shift; 76 77 if (div->lock) 78 spin_unlock(div->lock); 79 80 return (reg & 0x1); 81} 82 83static int berlin2_div_enable(struct clk_hw *hw) 84{ 85 struct berlin2_div *div = to_berlin2_div(hw); 86 struct berlin2_div_map *map = &div->map; 87 u32 reg; 88 89 if (div->lock) 90 spin_lock(div->lock); 91 92 reg = readl_relaxed(div->base + map->gate_offs); 93 reg |= BIT(map->gate_shift); 94 writel_relaxed(reg, div->base + map->gate_offs); 95 96 if (div->lock) 97 spin_unlock(div->lock); 98 99 return 0; 100} 101 102static void berlin2_div_disable(struct clk_hw *hw) 103{ 104 struct berlin2_div *div = to_berlin2_div(hw); 105 struct berlin2_div_map *map = &div->map; 106 u32 reg; 107 108 if (div->lock) 109 spin_lock(div->lock); 110 111 reg = readl_relaxed(div->base + map->gate_offs); 112 reg &= ~BIT(map->gate_shift); 113 writel_relaxed(reg, div->base + map->gate_offs); 114 115 if (div->lock) 116 spin_unlock(div->lock); 117} 118 119static int berlin2_div_set_parent(struct clk_hw *hw, u8 index) 120{ 121 struct berlin2_div *div = to_berlin2_div(hw); 122 struct berlin2_div_map *map = &div->map; 123 u32 reg; 124 125 if (div->lock) 126 spin_lock(div->lock); 127 128 /* index == 0 is PLL_SWITCH */ 129 reg = readl_relaxed(div->base + map->pll_switch_offs); 130 if (index == 0) 131 reg &= ~BIT(map->pll_switch_shift); 132 else 133 reg |= BIT(map->pll_switch_shift); 134 writel_relaxed(reg, div->base + map->pll_switch_offs); 135 136 /* index > 0 is PLL_SELECT */ 137 if (index > 0) { 138 reg = readl_relaxed(div->base + map->pll_select_offs); 139 reg &= ~(PLL_SELECT_MASK << map->pll_select_shift); 140 reg |= (index - 1) << map->pll_select_shift; 141 writel_relaxed(reg, div->base + map->pll_select_offs); 142 } 143 144 if (div->lock) 145 spin_unlock(div->lock); 146 147 return 0; 148} 149 150static u8 berlin2_div_get_parent(struct clk_hw *hw) 151{ 152 struct berlin2_div *div = to_berlin2_div(hw); 153 struct berlin2_div_map *map = &div->map; 154 u32 reg; 155 u8 index = 0; 156 157 if (div->lock) 158 spin_lock(div->lock); 159 160 /* PLL_SWITCH == 0 is index 0 */ 161 reg = readl_relaxed(div->base + map->pll_switch_offs); 162 reg &= BIT(map->pll_switch_shift); 163 if (reg) { 164 reg = readl_relaxed(div->base + map->pll_select_offs); 165 reg >>= map->pll_select_shift; 166 reg &= PLL_SELECT_MASK; 167 index = 1 + reg; 168 } 169 170 if (div->lock) 171 spin_unlock(div->lock); 172 173 return index; 174} 175 176static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw, 177 unsigned long parent_rate) 178{ 179 struct berlin2_div *div = to_berlin2_div(hw); 180 struct berlin2_div_map *map = &div->map; 181 u32 divsw, div3sw, divider = 1; 182 183 if (div->lock) 184 spin_lock(div->lock); 185 186 divsw = readl_relaxed(div->base + map->div_switch_offs) & 187 (1 << map->div_switch_shift); 188 div3sw = readl_relaxed(div->base + map->div3_switch_offs) & 189 (1 << map->div3_switch_shift); 190 191 /* constant divide-by-3 (dominant) */ 192 if (div3sw != 0) { 193 divider = 3; 194 /* divider can be bypassed with DIV_SWITCH == 0 */ 195 } else if (divsw == 0) { 196 divider = 1; 197 /* clock divider determined by DIV_SELECT */ 198 } else { 199 u32 reg; 200 reg = readl_relaxed(div->base + map->div_select_offs); 201 reg >>= map->div_select_shift; 202 reg &= DIV_SELECT_MASK; 203 divider = clk_div[reg]; 204 } 205 206 if (div->lock) 207 spin_unlock(div->lock); 208 209 return parent_rate / divider; 210} 211 212static const struct clk_ops berlin2_div_rate_ops = { 213 .recalc_rate = berlin2_div_recalc_rate, 214}; 215 216static const struct clk_ops berlin2_div_gate_ops = { 217 .is_enabled = berlin2_div_is_enabled, 218 .enable = berlin2_div_enable, 219 .disable = berlin2_div_disable, 220}; 221 222static const struct clk_ops berlin2_div_mux_ops = { 223 .set_parent = berlin2_div_set_parent, 224 .get_parent = berlin2_div_get_parent, 225}; 226 227struct clk_hw * __init 228berlin2_div_register(const struct berlin2_div_map *map, 229 void __iomem *base, const char *name, u8 div_flags, 230 const char **parent_names, int num_parents, 231 unsigned long flags, spinlock_t *lock) 232{ 233 const struct clk_ops *mux_ops = &berlin2_div_mux_ops; 234 const struct clk_ops *rate_ops = &berlin2_div_rate_ops; 235 const struct clk_ops *gate_ops = &berlin2_div_gate_ops; 236 struct berlin2_div *div; 237 238 div = kzalloc(sizeof(*div), GFP_KERNEL); 239 if (!div) 240 return ERR_PTR(-ENOMEM); 241 242 /* copy div_map to allow __initconst */ 243 memcpy(&div->map, map, sizeof(*map)); 244 div->base = base; 245 div->lock = lock; 246 247 if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0) 248 gate_ops = NULL; 249 if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0) 250 mux_ops = NULL; 251 252 return clk_hw_register_composite(NULL, name, parent_names, num_parents, 253 &div->hw, mux_ops, &div->hw, rate_ops, 254 &div->hw, gate_ops, flags); 255}