162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014 Marvell Technology Group Ltd. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Alexandre Belloni <alexandre.belloni@free-electrons.com> 662306a36Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/bitops.h> 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "berlin2-div.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * Clock dividers in Berlin2 SoCs comprise a complex cell to select 2062306a36Sopenharmony_ci * input pll and divider. The virtual structure as it is used in Marvell 2162306a36Sopenharmony_ci * BSP code can be seen as: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * +---+ 2462306a36Sopenharmony_ci * pll0 --------------->| 0 | +---+ 2562306a36Sopenharmony_ci * +---+ |(B)|--+--------------->| 0 | +---+ 2662306a36Sopenharmony_ci * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+ 2762306a36Sopenharmony_ci * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|-> 2862306a36Sopenharmony_ci * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+ 2962306a36Sopenharmony_ci * ... -->| | +-->|(D) 1:3 |----------+ +---+ 3062306a36Sopenharmony_ci * pll1.N -->| N | +--------- 3162306a36Sopenharmony_ci * +---+ 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * (A) input pll clock mux controlled by <PllSelect[1:n]> 3462306a36Sopenharmony_ci * (B) input pll bypass mux controlled by <PllSwitch> 3562306a36Sopenharmony_ci * (C) programmable clock divider controlled by <Select[1:n]> 3662306a36Sopenharmony_ci * (D) constant div-by-3 clock divider 3762306a36Sopenharmony_ci * (E) programmable clock divider bypass controlled by <Switch> 3862306a36Sopenharmony_ci * (F) constant div-by-3 clock mux controlled by <D3Switch> 3962306a36Sopenharmony_ci * (G) clock gate controlled by <Enable> 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * For whatever reason, above control signals come in two flavors: 4262306a36Sopenharmony_ci * - single register dividers with all bits in one register 4362306a36Sopenharmony_ci * - shared register dividers with bits spread over multiple registers 4462306a36Sopenharmony_ci * (including signals for the same cell spread over consecutive registers) 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Also, clock gate and pll mux is not available on every div cell, so 4762306a36Sopenharmony_ci * we have to deal with those, too. We reuse common clock composite driver 4862306a36Sopenharmony_ci * for it. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define PLL_SELECT_MASK 0x7 5262306a36Sopenharmony_ci#define DIV_SELECT_MASK 0x7 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct berlin2_div { 5562306a36Sopenharmony_ci struct clk_hw hw; 5662306a36Sopenharmony_ci void __iomem *base; 5762306a36Sopenharmony_ci struct berlin2_div_map map; 5862306a36Sopenharmony_ci spinlock_t *lock; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 }; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int berlin2_div_is_enabled(struct clk_hw *hw) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 6862306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 6962306a36Sopenharmony_ci u32 reg; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (div->lock) 7262306a36Sopenharmony_ci spin_lock(div->lock); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 7562306a36Sopenharmony_ci reg >>= map->gate_shift; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (div->lock) 7862306a36Sopenharmony_ci spin_unlock(div->lock); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return (reg & 0x1); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int berlin2_div_enable(struct clk_hw *hw) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 8662306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 8762306a36Sopenharmony_ci u32 reg; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (div->lock) 9062306a36Sopenharmony_ci spin_lock(div->lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 9362306a36Sopenharmony_ci reg |= BIT(map->gate_shift); 9462306a36Sopenharmony_ci writel_relaxed(reg, div->base + map->gate_offs); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (div->lock) 9762306a36Sopenharmony_ci spin_unlock(div->lock); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void berlin2_div_disable(struct clk_hw *hw) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 10562306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 10662306a36Sopenharmony_ci u32 reg; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (div->lock) 10962306a36Sopenharmony_ci spin_lock(div->lock); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 11262306a36Sopenharmony_ci reg &= ~BIT(map->gate_shift); 11362306a36Sopenharmony_ci writel_relaxed(reg, div->base + map->gate_offs); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (div->lock) 11662306a36Sopenharmony_ci spin_unlock(div->lock); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int berlin2_div_set_parent(struct clk_hw *hw, u8 index) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 12262306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 12362306a36Sopenharmony_ci u32 reg; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (div->lock) 12662306a36Sopenharmony_ci spin_lock(div->lock); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* index == 0 is PLL_SWITCH */ 12962306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_switch_offs); 13062306a36Sopenharmony_ci if (index == 0) 13162306a36Sopenharmony_ci reg &= ~BIT(map->pll_switch_shift); 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci reg |= BIT(map->pll_switch_shift); 13462306a36Sopenharmony_ci writel_relaxed(reg, div->base + map->pll_switch_offs); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* index > 0 is PLL_SELECT */ 13762306a36Sopenharmony_ci if (index > 0) { 13862306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_select_offs); 13962306a36Sopenharmony_ci reg &= ~(PLL_SELECT_MASK << map->pll_select_shift); 14062306a36Sopenharmony_ci reg |= (index - 1) << map->pll_select_shift; 14162306a36Sopenharmony_ci writel_relaxed(reg, div->base + map->pll_select_offs); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (div->lock) 14562306a36Sopenharmony_ci spin_unlock(div->lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic u8 berlin2_div_get_parent(struct clk_hw *hw) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 15362306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 15462306a36Sopenharmony_ci u32 reg; 15562306a36Sopenharmony_ci u8 index = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (div->lock) 15862306a36Sopenharmony_ci spin_lock(div->lock); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* PLL_SWITCH == 0 is index 0 */ 16162306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_switch_offs); 16262306a36Sopenharmony_ci reg &= BIT(map->pll_switch_shift); 16362306a36Sopenharmony_ci if (reg) { 16462306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_select_offs); 16562306a36Sopenharmony_ci reg >>= map->pll_select_shift; 16662306a36Sopenharmony_ci reg &= PLL_SELECT_MASK; 16762306a36Sopenharmony_ci index = 1 + reg; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (div->lock) 17162306a36Sopenharmony_ci spin_unlock(div->lock); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return index; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic unsigned long berlin2_div_recalc_rate(struct clk_hw *hw, 17762306a36Sopenharmony_ci unsigned long parent_rate) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 18062306a36Sopenharmony_ci struct berlin2_div_map *map = &div->map; 18162306a36Sopenharmony_ci u32 divsw, div3sw, divider = 1; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (div->lock) 18462306a36Sopenharmony_ci spin_lock(div->lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci divsw = readl_relaxed(div->base + map->div_switch_offs) & 18762306a36Sopenharmony_ci (1 << map->div_switch_shift); 18862306a36Sopenharmony_ci div3sw = readl_relaxed(div->base + map->div3_switch_offs) & 18962306a36Sopenharmony_ci (1 << map->div3_switch_shift); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* constant divide-by-3 (dominant) */ 19262306a36Sopenharmony_ci if (div3sw != 0) { 19362306a36Sopenharmony_ci divider = 3; 19462306a36Sopenharmony_ci /* divider can be bypassed with DIV_SWITCH == 0 */ 19562306a36Sopenharmony_ci } else if (divsw == 0) { 19662306a36Sopenharmony_ci divider = 1; 19762306a36Sopenharmony_ci /* clock divider determined by DIV_SELECT */ 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci u32 reg; 20062306a36Sopenharmony_ci reg = readl_relaxed(div->base + map->div_select_offs); 20162306a36Sopenharmony_ci reg >>= map->div_select_shift; 20262306a36Sopenharmony_ci reg &= DIV_SELECT_MASK; 20362306a36Sopenharmony_ci divider = clk_div[reg]; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (div->lock) 20762306a36Sopenharmony_ci spin_unlock(div->lock); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return parent_rate / divider; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic const struct clk_ops berlin2_div_rate_ops = { 21362306a36Sopenharmony_ci .determine_rate = clk_hw_determine_rate_no_reparent, 21462306a36Sopenharmony_ci .recalc_rate = berlin2_div_recalc_rate, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct clk_ops berlin2_div_gate_ops = { 21862306a36Sopenharmony_ci .is_enabled = berlin2_div_is_enabled, 21962306a36Sopenharmony_ci .enable = berlin2_div_enable, 22062306a36Sopenharmony_ci .disable = berlin2_div_disable, 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic const struct clk_ops berlin2_div_mux_ops = { 22462306a36Sopenharmony_ci .set_parent = berlin2_div_set_parent, 22562306a36Sopenharmony_ci .get_parent = berlin2_div_get_parent, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct clk_hw * __init 22962306a36Sopenharmony_ciberlin2_div_register(const struct berlin2_div_map *map, 23062306a36Sopenharmony_ci void __iomem *base, const char *name, u8 div_flags, 23162306a36Sopenharmony_ci const char **parent_names, int num_parents, 23262306a36Sopenharmony_ci unsigned long flags, spinlock_t *lock) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci const struct clk_ops *mux_ops = &berlin2_div_mux_ops; 23562306a36Sopenharmony_ci const struct clk_ops *rate_ops = &berlin2_div_rate_ops; 23662306a36Sopenharmony_ci const struct clk_ops *gate_ops = &berlin2_div_gate_ops; 23762306a36Sopenharmony_ci struct berlin2_div *div; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 24062306a36Sopenharmony_ci if (!div) 24162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* copy div_map to allow __initconst */ 24462306a36Sopenharmony_ci memcpy(&div->map, map, sizeof(*map)); 24562306a36Sopenharmony_ci div->base = base; 24662306a36Sopenharmony_ci div->lock = lock; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0) 24962306a36Sopenharmony_ci gate_ops = NULL; 25062306a36Sopenharmony_ci if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0) 25162306a36Sopenharmony_ci mux_ops = NULL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return clk_hw_register_composite(NULL, name, parent_names, num_parents, 25462306a36Sopenharmony_ci &div->hw, mux_ops, &div->hw, rate_ops, 25562306a36Sopenharmony_ci &div->hw, gate_ops, flags); 25662306a36Sopenharmony_ci} 257