18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Simple multiplexer clock implementation 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * DOC: basic adjustable multiplexer clock that cannot gate 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Traits of this clock: 208c2ecf20Sopenharmony_ci * prepare - clk_prepare only ensures that parents are prepared 218c2ecf20Sopenharmony_ci * enable - clk_enable only ensures that parents are enabled 228c2ecf20Sopenharmony_ci * rate - rate is only affected by parent switching. No clk_set_rate support 238c2ecf20Sopenharmony_ci * parent - parent is adjustable through clk_set_parent 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline u32 clk_mux_readl(struct clk_mux *mux) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci if (mux->flags & CLK_MUX_BIG_ENDIAN) 298c2ecf20Sopenharmony_ci return ioread32be(mux->reg); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci return readl(mux->reg); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline void clk_mux_writel(struct clk_mux *mux, u32 val) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci if (mux->flags & CLK_MUX_BIG_ENDIAN) 378c2ecf20Sopenharmony_ci iowrite32be(val, mux->reg); 388c2ecf20Sopenharmony_ci else 398c2ecf20Sopenharmony_ci writel(val, mux->reg); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciint clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags, 438c2ecf20Sopenharmony_ci unsigned int val) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int num_parents = clk_hw_get_num_parents(hw); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (table) { 488c2ecf20Sopenharmony_ci int i; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci for (i = 0; i < num_parents; i++) 518c2ecf20Sopenharmony_ci if (table[i] == val) 528c2ecf20Sopenharmony_ci return i; 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (val && (flags & CLK_MUX_INDEX_BIT)) 578c2ecf20Sopenharmony_ci val = ffs(val) - 1; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (val && (flags & CLK_MUX_INDEX_ONE)) 608c2ecf20Sopenharmony_ci val--; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (val >= num_parents) 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return val; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_mux_val_to_index); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciunsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci unsigned int val = index; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (table) { 748c2ecf20Sopenharmony_ci val = table[index]; 758c2ecf20Sopenharmony_ci } else { 768c2ecf20Sopenharmony_ci if (flags & CLK_MUX_INDEX_BIT) 778c2ecf20Sopenharmony_ci val = 1 << index; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (flags & CLK_MUX_INDEX_ONE) 808c2ecf20Sopenharmony_ci val++; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return val; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_mux_index_to_val); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic u8 clk_mux_get_parent(struct clk_hw *hw) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct clk_mux *mux = to_clk_mux(hw); 908c2ecf20Sopenharmony_ci u32 val; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci val = clk_mux_readl(mux) >> mux->shift; 938c2ecf20Sopenharmony_ci val &= mux->mask; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return clk_mux_val_to_index(hw, mux->table, mux->flags, val); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int clk_mux_set_parent(struct clk_hw *hw, u8 index) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct clk_mux *mux = to_clk_mux(hw); 1018c2ecf20Sopenharmony_ci u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); 1028c2ecf20Sopenharmony_ci unsigned long flags = 0; 1038c2ecf20Sopenharmony_ci u32 reg; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (mux->lock) 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(mux->lock, flags); 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci __acquire(mux->lock); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (mux->flags & CLK_MUX_HIWORD_MASK) { 1118c2ecf20Sopenharmony_ci reg = mux->mask << (mux->shift + 16); 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci reg = clk_mux_readl(mux); 1148c2ecf20Sopenharmony_ci reg &= ~(mux->mask << mux->shift); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci val = val << mux->shift; 1178c2ecf20Sopenharmony_ci reg |= val; 1188c2ecf20Sopenharmony_ci clk_mux_writel(mux, reg); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (mux->lock) 1218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(mux->lock, flags); 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci __release(mux->lock); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int clk_mux_determine_rate(struct clk_hw *hw, 1298c2ecf20Sopenharmony_ci struct clk_rate_request *req) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct clk_mux *mux = to_clk_mux(hw); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return clk_mux_determine_rate_flags(hw, req, mux->flags); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciconst struct clk_ops clk_mux_ops = { 1378c2ecf20Sopenharmony_ci .get_parent = clk_mux_get_parent, 1388c2ecf20Sopenharmony_ci .set_parent = clk_mux_set_parent, 1398c2ecf20Sopenharmony_ci .determine_rate = clk_mux_determine_rate, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_mux_ops); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciconst struct clk_ops clk_mux_ro_ops = { 1448c2ecf20Sopenharmony_ci .get_parent = clk_mux_get_parent, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_mux_ro_ops); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct clk_hw *__clk_hw_register_mux(struct device *dev, struct device_node *np, 1498c2ecf20Sopenharmony_ci const char *name, u8 num_parents, 1508c2ecf20Sopenharmony_ci const char * const *parent_names, 1518c2ecf20Sopenharmony_ci const struct clk_hw **parent_hws, 1528c2ecf20Sopenharmony_ci const struct clk_parent_data *parent_data, 1538c2ecf20Sopenharmony_ci unsigned long flags, void __iomem *reg, u8 shift, u32 mask, 1548c2ecf20Sopenharmony_ci u8 clk_mux_flags, u32 *table, spinlock_t *lock) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct clk_mux *mux; 1578c2ecf20Sopenharmony_ci struct clk_hw *hw; 1588c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 1598c2ecf20Sopenharmony_ci u8 width = 0; 1608c2ecf20Sopenharmony_ci int ret = -EINVAL; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { 1638c2ecf20Sopenharmony_ci width = fls(mask) - ffs(mask) + 1; 1648c2ecf20Sopenharmony_ci if (width + shift > 16) { 1658c2ecf20Sopenharmony_ci pr_err("mux value exceeds LOWORD field\n"); 1668c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* allocate the mux */ 1718c2ecf20Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (!mux) 1738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci init.name = name; 1768c2ecf20Sopenharmony_ci if (clk_mux_flags & CLK_MUX_READ_ONLY) 1778c2ecf20Sopenharmony_ci init.ops = &clk_mux_ro_ops; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci init.ops = &clk_mux_ops; 1808c2ecf20Sopenharmony_ci init.flags = flags; 1818c2ecf20Sopenharmony_ci init.parent_names = parent_names; 1828c2ecf20Sopenharmony_ci init.parent_data = parent_data; 1838c2ecf20Sopenharmony_ci init.parent_hws = parent_hws; 1848c2ecf20Sopenharmony_ci init.num_parents = num_parents; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* struct clk_mux assignments */ 1878c2ecf20Sopenharmony_ci mux->reg = reg; 1888c2ecf20Sopenharmony_ci mux->shift = shift; 1898c2ecf20Sopenharmony_ci mux->mask = mask; 1908c2ecf20Sopenharmony_ci mux->flags = clk_mux_flags; 1918c2ecf20Sopenharmony_ci mux->lock = lock; 1928c2ecf20Sopenharmony_ci mux->table = table; 1938c2ecf20Sopenharmony_ci mux->hw.init = &init; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci hw = &mux->hw; 1968c2ecf20Sopenharmony_ci if (dev || !np) 1978c2ecf20Sopenharmony_ci ret = clk_hw_register(dev, hw); 1988c2ecf20Sopenharmony_ci else if (np) 1998c2ecf20Sopenharmony_ci ret = of_clk_hw_register(np, hw); 2008c2ecf20Sopenharmony_ci if (ret) { 2018c2ecf20Sopenharmony_ci kfree(mux); 2028c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return hw; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__clk_hw_register_mux); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistruct clk *clk_register_mux_table(struct device *dev, const char *name, 2108c2ecf20Sopenharmony_ci const char * const *parent_names, u8 num_parents, 2118c2ecf20Sopenharmony_ci unsigned long flags, void __iomem *reg, u8 shift, u32 mask, 2128c2ecf20Sopenharmony_ci u8 clk_mux_flags, u32 *table, spinlock_t *lock) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct clk_hw *hw; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci hw = clk_hw_register_mux_table(dev, name, parent_names, 2178c2ecf20Sopenharmony_ci num_parents, flags, reg, shift, mask, 2188c2ecf20Sopenharmony_ci clk_mux_flags, table, lock); 2198c2ecf20Sopenharmony_ci if (IS_ERR(hw)) 2208c2ecf20Sopenharmony_ci return ERR_CAST(hw); 2218c2ecf20Sopenharmony_ci return hw->clk; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_register_mux_table); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_civoid clk_unregister_mux(struct clk *clk) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct clk_mux *mux; 2288c2ecf20Sopenharmony_ci struct clk_hw *hw; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci hw = __clk_get_hw(clk); 2318c2ecf20Sopenharmony_ci if (!hw) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci mux = to_clk_mux(hw); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci clk_unregister(clk); 2378c2ecf20Sopenharmony_ci kfree(mux); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_unregister_mux); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid clk_hw_unregister_mux(struct clk_hw *hw) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct clk_mux *mux; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mux = to_clk_mux(hw); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci clk_hw_unregister(hw); 2488c2ecf20Sopenharmony_ci kfree(mux); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_unregister_mux); 251