18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Marvell Technology Group Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Alexandre Belloni <alexandre.belloni@free-electrons.com> 68c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "berlin2-div.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * Clock dividers in Berlin2 SoCs comprise a complex cell to select 208c2ecf20Sopenharmony_ci * input pll and divider. The virtual structure as it is used in Marvell 218c2ecf20Sopenharmony_ci * BSP code can be seen as: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * +---+ 248c2ecf20Sopenharmony_ci * pll0 --------------->| 0 | +---+ 258c2ecf20Sopenharmony_ci * +---+ |(B)|--+--------------->| 0 | +---+ 268c2ecf20Sopenharmony_ci * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+ 278c2ecf20Sopenharmony_ci * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|-> 288c2ecf20Sopenharmony_ci * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+ 298c2ecf20Sopenharmony_ci * ... -->| | +-->|(D) 1:3 |----------+ +---+ 308c2ecf20Sopenharmony_ci * pll1.N -->| N | +--------- 318c2ecf20Sopenharmony_ci * +---+ 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * (A) input pll clock mux controlled by <PllSelect[1:n]> 348c2ecf20Sopenharmony_ci * (B) input pll bypass mux controlled by <PllSwitch> 358c2ecf20Sopenharmony_ci * (C) programmable clock divider controlled by <Select[1:n]> 368c2ecf20Sopenharmony_ci * (D) constant div-by-3 clock divider 378c2ecf20Sopenharmony_ci * (E) programmable clock divider bypass controlled by <Switch> 388c2ecf20Sopenharmony_ci * (F) constant div-by-3 clock mux controlled by <D3Switch> 398c2ecf20Sopenharmony_ci * (G) clock gate controlled by <Enable> 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * For whatever reason, above control signals come in two flavors: 428c2ecf20Sopenharmony_ci * - single register dividers with all bits in one register 438c2ecf20Sopenharmony_ci * - shared register dividers with bits spread over multiple registers 448c2ecf20Sopenharmony_ci * (including signals for the same cell spread over consecutive registers) 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * Also, clock gate and pll mux is not available on every div cell, so 478c2ecf20Sopenharmony_ci * we have to deal with those, too. We reuse common clock composite driver 488c2ecf20Sopenharmony_ci * for it. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define PLL_SELECT_MASK 0x7 528c2ecf20Sopenharmony_ci#define DIV_SELECT_MASK 0x7 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct berlin2_div { 558c2ecf20Sopenharmony_ci struct clk_hw hw; 568c2ecf20Sopenharmony_ci void __iomem *base; 578c2ecf20Sopenharmony_ci struct berlin2_div_map map; 588c2ecf20Sopenharmony_ci spinlock_t *lock; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 }; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int berlin2_div_is_enabled(struct clk_hw *hw) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 688c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 698c2ecf20Sopenharmony_ci u32 reg; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (div->lock) 728c2ecf20Sopenharmony_ci spin_lock(div->lock); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 758c2ecf20Sopenharmony_ci reg >>= map->gate_shift; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (div->lock) 788c2ecf20Sopenharmony_ci spin_unlock(div->lock); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return (reg & 0x1); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int berlin2_div_enable(struct clk_hw *hw) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 868c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 878c2ecf20Sopenharmony_ci u32 reg; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (div->lock) 908c2ecf20Sopenharmony_ci spin_lock(div->lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 938c2ecf20Sopenharmony_ci reg |= BIT(map->gate_shift); 948c2ecf20Sopenharmony_ci writel_relaxed(reg, div->base + map->gate_offs); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (div->lock) 978c2ecf20Sopenharmony_ci spin_unlock(div->lock); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void berlin2_div_disable(struct clk_hw *hw) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 1058c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 1068c2ecf20Sopenharmony_ci u32 reg; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (div->lock) 1098c2ecf20Sopenharmony_ci spin_lock(div->lock); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->gate_offs); 1128c2ecf20Sopenharmony_ci reg &= ~BIT(map->gate_shift); 1138c2ecf20Sopenharmony_ci writel_relaxed(reg, div->base + map->gate_offs); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (div->lock) 1168c2ecf20Sopenharmony_ci spin_unlock(div->lock); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int berlin2_div_set_parent(struct clk_hw *hw, u8 index) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 1228c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 1238c2ecf20Sopenharmony_ci u32 reg; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (div->lock) 1268c2ecf20Sopenharmony_ci spin_lock(div->lock); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* index == 0 is PLL_SWITCH */ 1298c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_switch_offs); 1308c2ecf20Sopenharmony_ci if (index == 0) 1318c2ecf20Sopenharmony_ci reg &= ~BIT(map->pll_switch_shift); 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci reg |= BIT(map->pll_switch_shift); 1348c2ecf20Sopenharmony_ci writel_relaxed(reg, div->base + map->pll_switch_offs); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* index > 0 is PLL_SELECT */ 1378c2ecf20Sopenharmony_ci if (index > 0) { 1388c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_select_offs); 1398c2ecf20Sopenharmony_ci reg &= ~(PLL_SELECT_MASK << map->pll_select_shift); 1408c2ecf20Sopenharmony_ci reg |= (index - 1) << map->pll_select_shift; 1418c2ecf20Sopenharmony_ci writel_relaxed(reg, div->base + map->pll_select_offs); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (div->lock) 1458c2ecf20Sopenharmony_ci spin_unlock(div->lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic u8 berlin2_div_get_parent(struct clk_hw *hw) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 1538c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 1548c2ecf20Sopenharmony_ci u32 reg; 1558c2ecf20Sopenharmony_ci u8 index = 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (div->lock) 1588c2ecf20Sopenharmony_ci spin_lock(div->lock); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* PLL_SWITCH == 0 is index 0 */ 1618c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_switch_offs); 1628c2ecf20Sopenharmony_ci reg &= BIT(map->pll_switch_shift); 1638c2ecf20Sopenharmony_ci if (reg) { 1648c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->pll_select_offs); 1658c2ecf20Sopenharmony_ci reg >>= map->pll_select_shift; 1668c2ecf20Sopenharmony_ci reg &= PLL_SELECT_MASK; 1678c2ecf20Sopenharmony_ci index = 1 + reg; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (div->lock) 1718c2ecf20Sopenharmony_ci spin_unlock(div->lock); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return index; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic unsigned long berlin2_div_recalc_rate(struct clk_hw *hw, 1778c2ecf20Sopenharmony_ci unsigned long parent_rate) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct berlin2_div *div = to_berlin2_div(hw); 1808c2ecf20Sopenharmony_ci struct berlin2_div_map *map = &div->map; 1818c2ecf20Sopenharmony_ci u32 divsw, div3sw, divider = 1; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (div->lock) 1848c2ecf20Sopenharmony_ci spin_lock(div->lock); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci divsw = readl_relaxed(div->base + map->div_switch_offs) & 1878c2ecf20Sopenharmony_ci (1 << map->div_switch_shift); 1888c2ecf20Sopenharmony_ci div3sw = readl_relaxed(div->base + map->div3_switch_offs) & 1898c2ecf20Sopenharmony_ci (1 << map->div3_switch_shift); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* constant divide-by-3 (dominant) */ 1928c2ecf20Sopenharmony_ci if (div3sw != 0) { 1938c2ecf20Sopenharmony_ci divider = 3; 1948c2ecf20Sopenharmony_ci /* divider can be bypassed with DIV_SWITCH == 0 */ 1958c2ecf20Sopenharmony_ci } else if (divsw == 0) { 1968c2ecf20Sopenharmony_ci divider = 1; 1978c2ecf20Sopenharmony_ci /* clock divider determined by DIV_SELECT */ 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci u32 reg; 2008c2ecf20Sopenharmony_ci reg = readl_relaxed(div->base + map->div_select_offs); 2018c2ecf20Sopenharmony_ci reg >>= map->div_select_shift; 2028c2ecf20Sopenharmony_ci reg &= DIV_SELECT_MASK; 2038c2ecf20Sopenharmony_ci divider = clk_div[reg]; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (div->lock) 2078c2ecf20Sopenharmony_ci spin_unlock(div->lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return parent_rate / divider; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic const struct clk_ops berlin2_div_rate_ops = { 2138c2ecf20Sopenharmony_ci .recalc_rate = berlin2_div_recalc_rate, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const struct clk_ops berlin2_div_gate_ops = { 2178c2ecf20Sopenharmony_ci .is_enabled = berlin2_div_is_enabled, 2188c2ecf20Sopenharmony_ci .enable = berlin2_div_enable, 2198c2ecf20Sopenharmony_ci .disable = berlin2_div_disable, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct clk_ops berlin2_div_mux_ops = { 2238c2ecf20Sopenharmony_ci .set_parent = berlin2_div_set_parent, 2248c2ecf20Sopenharmony_ci .get_parent = berlin2_div_get_parent, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistruct clk_hw * __init 2288c2ecf20Sopenharmony_ciberlin2_div_register(const struct berlin2_div_map *map, 2298c2ecf20Sopenharmony_ci void __iomem *base, const char *name, u8 div_flags, 2308c2ecf20Sopenharmony_ci const char **parent_names, int num_parents, 2318c2ecf20Sopenharmony_ci unsigned long flags, spinlock_t *lock) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci const struct clk_ops *mux_ops = &berlin2_div_mux_ops; 2348c2ecf20Sopenharmony_ci const struct clk_ops *rate_ops = &berlin2_div_rate_ops; 2358c2ecf20Sopenharmony_ci const struct clk_ops *gate_ops = &berlin2_div_gate_ops; 2368c2ecf20Sopenharmony_ci struct berlin2_div *div; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!div) 2408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* copy div_map to allow __initconst */ 2438c2ecf20Sopenharmony_ci memcpy(&div->map, map, sizeof(*map)); 2448c2ecf20Sopenharmony_ci div->base = base; 2458c2ecf20Sopenharmony_ci div->lock = lock; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0) 2488c2ecf20Sopenharmony_ci gate_ops = NULL; 2498c2ecf20Sopenharmony_ci if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0) 2508c2ecf20Sopenharmony_ci mux_ops = NULL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return clk_hw_register_composite(NULL, name, parent_names, num_parents, 2538c2ecf20Sopenharmony_ci &div->hw, mux_ops, &div->hw, rate_ops, 2548c2ecf20Sopenharmony_ci &div->hw, gate_ops, flags); 2558c2ecf20Sopenharmony_ci} 256