162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell Dove PMU Core PLL divider driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Cleaned up by substantially rewriting, and converted to DT by 662306a36Sopenharmony_ci * Russell King. Origin is not known. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "dove-divider.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct dove_clk { 1862306a36Sopenharmony_ci const char *name; 1962306a36Sopenharmony_ci struct clk_hw hw; 2062306a36Sopenharmony_ci void __iomem *base; 2162306a36Sopenharmony_ci spinlock_t *lock; 2262306a36Sopenharmony_ci u8 div_bit_start; 2362306a36Sopenharmony_ci u8 div_bit_end; 2462306a36Sopenharmony_ci u8 div_bit_load; 2562306a36Sopenharmony_ci u8 div_bit_size; 2662306a36Sopenharmony_ci u32 *divider_table; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cienum { 3062306a36Sopenharmony_ci DIV_CTRL0 = 0, 3162306a36Sopenharmony_ci DIV_CTRL1 = 4, 3262306a36Sopenharmony_ci DIV_CTRL1_N_RESET_MASK = BIT(10), 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define to_dove_clk(hw) container_of(hw, struct dove_clk, hw) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u32 v; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK; 4262306a36Sopenharmony_ci writel_relaxed(v, base + DIV_CTRL1); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val; 4562306a36Sopenharmony_ci writel_relaxed(v, base + DIV_CTRL0); 4662306a36Sopenharmony_ci writel_relaxed(v | load, base + DIV_CTRL0); 4762306a36Sopenharmony_ci ndelay(250); 4862306a36Sopenharmony_ci writel_relaxed(v, base + DIV_CTRL0); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic unsigned int dove_get_divider(struct dove_clk *dc) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci unsigned int divider; 5462306a36Sopenharmony_ci u32 val; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci val = readl_relaxed(dc->base + DIV_CTRL0); 5762306a36Sopenharmony_ci val >>= dc->div_bit_start; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci divider = val & ~(~0 << dc->div_bit_size); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (dc->divider_table) 6262306a36Sopenharmony_ci divider = dc->divider_table[divider]; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return divider; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int dove_calc_divider(const struct dove_clk *dc, unsigned long rate, 6862306a36Sopenharmony_ci unsigned long parent_rate, bool set) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci unsigned int divider, max; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci divider = DIV_ROUND_CLOSEST(parent_rate, rate); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (dc->divider_table) { 7562306a36Sopenharmony_ci unsigned int i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci for (i = 0; dc->divider_table[i]; i++) 7862306a36Sopenharmony_ci if (divider == dc->divider_table[i]) { 7962306a36Sopenharmony_ci divider = i; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!dc->divider_table[i]) 8462306a36Sopenharmony_ci return -EINVAL; 8562306a36Sopenharmony_ci } else { 8662306a36Sopenharmony_ci max = 1 << dc->div_bit_size; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (set && (divider == 0 || divider >= max)) 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci if (divider >= max) 9162306a36Sopenharmony_ci divider = max - 1; 9262306a36Sopenharmony_ci else if (divider == 0) 9362306a36Sopenharmony_ci divider = 1; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return divider; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct dove_clk *dc = to_dove_clk(hw); 10262306a36Sopenharmony_ci unsigned int divider = dove_get_divider(dc); 10362306a36Sopenharmony_ci unsigned long rate = DIV_ROUND_CLOSEST(parent, divider); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", 10662306a36Sopenharmony_ci __func__, dc->name, divider, parent, rate); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return rate; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic long dove_round_rate(struct clk_hw *hw, unsigned long rate, 11262306a36Sopenharmony_ci unsigned long *parent) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct dove_clk *dc = to_dove_clk(hw); 11562306a36Sopenharmony_ci unsigned long parent_rate = *parent; 11662306a36Sopenharmony_ci int divider; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci divider = dove_calc_divider(dc, rate, parent_rate, false); 11962306a36Sopenharmony_ci if (divider < 0) 12062306a36Sopenharmony_ci return divider; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci rate = DIV_ROUND_CLOSEST(parent_rate, divider); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", 12562306a36Sopenharmony_ci __func__, dc->name, divider, parent_rate, rate); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return rate; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int dove_set_clock(struct clk_hw *hw, unsigned long rate, 13162306a36Sopenharmony_ci unsigned long parent_rate) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct dove_clk *dc = to_dove_clk(hw); 13462306a36Sopenharmony_ci u32 mask, load, div; 13562306a36Sopenharmony_ci int divider; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci divider = dove_calc_divider(dc, rate, parent_rate, true); 13862306a36Sopenharmony_ci if (divider < 0) 13962306a36Sopenharmony_ci return divider; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", 14262306a36Sopenharmony_ci __func__, dc->name, divider, parent_rate, rate); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci div = (u32)divider << dc->div_bit_start; 14562306a36Sopenharmony_ci mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start; 14662306a36Sopenharmony_ci load = BIT(dc->div_bit_load); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_lock(dc->lock); 14962306a36Sopenharmony_ci dove_load_divider(dc->base, div, mask, load); 15062306a36Sopenharmony_ci spin_unlock(dc->lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct clk_ops dove_divider_ops = { 15662306a36Sopenharmony_ci .set_rate = dove_set_clock, 15762306a36Sopenharmony_ci .round_rate = dove_round_rate, 15862306a36Sopenharmony_ci .recalc_rate = dove_recalc_rate, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct clk *clk_register_dove_divider(struct device *dev, 16262306a36Sopenharmony_ci struct dove_clk *dc, const char **parent_names, size_t num_parents, 16362306a36Sopenharmony_ci void __iomem *base) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci char name[32]; 16662306a36Sopenharmony_ci struct clk_init_data init = { 16762306a36Sopenharmony_ci .name = name, 16862306a36Sopenharmony_ci .ops = &dove_divider_ops, 16962306a36Sopenharmony_ci .parent_names = parent_names, 17062306a36Sopenharmony_ci .num_parents = num_parents, 17162306a36Sopenharmony_ci }; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci strscpy(name, dc->name, sizeof(name)); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci dc->hw.init = &init; 17662306a36Sopenharmony_ci dc->base = base; 17762306a36Sopenharmony_ci dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return clk_register(dev, &dc->hw); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(dove_divider_lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct dove_clk dove_hw_clocks[4] = { 18762306a36Sopenharmony_ci { 18862306a36Sopenharmony_ci .name = "axi", 18962306a36Sopenharmony_ci .lock = &dove_divider_lock, 19062306a36Sopenharmony_ci .div_bit_start = 1, 19162306a36Sopenharmony_ci .div_bit_end = 6, 19262306a36Sopenharmony_ci .div_bit_load = 7, 19362306a36Sopenharmony_ci .divider_table = axi_divider, 19462306a36Sopenharmony_ci }, { 19562306a36Sopenharmony_ci .name = "gpu", 19662306a36Sopenharmony_ci .lock = &dove_divider_lock, 19762306a36Sopenharmony_ci .div_bit_start = 8, 19862306a36Sopenharmony_ci .div_bit_end = 13, 19962306a36Sopenharmony_ci .div_bit_load = 14, 20062306a36Sopenharmony_ci }, { 20162306a36Sopenharmony_ci .name = "vmeta", 20262306a36Sopenharmony_ci .lock = &dove_divider_lock, 20362306a36Sopenharmony_ci .div_bit_start = 15, 20462306a36Sopenharmony_ci .div_bit_end = 20, 20562306a36Sopenharmony_ci .div_bit_load = 21, 20662306a36Sopenharmony_ci }, { 20762306a36Sopenharmony_ci .name = "lcd", 20862306a36Sopenharmony_ci .lock = &dove_divider_lock, 20962306a36Sopenharmony_ci .div_bit_start = 22, 21062306a36Sopenharmony_ci .div_bit_end = 27, 21162306a36Sopenharmony_ci .div_bit_load = 28, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const char *core_pll[] = { 21662306a36Sopenharmony_ci "core-pll", 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int dove_divider_init(struct device *dev, void __iomem *base, 22062306a36Sopenharmony_ci struct clk **clks) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct clk *clk; 22362306a36Sopenharmony_ci int i; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * Create the core PLL clock. We treat this as a fixed rate 22762306a36Sopenharmony_ci * clock as we don't know any better, and documentation is sparse. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci clk = clk_register_fixed_rate(dev, core_pll[0], NULL, 0, 2000000000UL); 23062306a36Sopenharmony_ci if (IS_ERR(clk)) 23162306a36Sopenharmony_ci return PTR_ERR(clk); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++) 23462306a36Sopenharmony_ci clks[i] = clk_register_dove_divider(dev, &dove_hw_clocks[i], 23562306a36Sopenharmony_ci core_pll, 23662306a36Sopenharmony_ci ARRAY_SIZE(core_pll), base); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic struct clk *dove_divider_clocks[4]; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic struct clk_onecell_data dove_divider_data = { 24462306a36Sopenharmony_ci .clks = dove_divider_clocks, 24562306a36Sopenharmony_ci .clk_num = ARRAY_SIZE(dove_divider_clocks), 24662306a36Sopenharmony_ci}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_civoid __init dove_divider_clk_init(struct device_node *np) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci void __iomem *base; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci base = of_iomap(np, 0); 25362306a36Sopenharmony_ci if (WARN_ON(!base)) 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) { 25762306a36Sopenharmony_ci iounmap(base); 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, &dove_divider_data); 26262306a36Sopenharmony_ci} 263