18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018 MediaTek Inc. 48c2ecf20Sopenharmony_ci * Author: Owen Chen <owen.chen@mediatek.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/of.h> 88c2ecf20Sopenharmony_ci#include <linux/of_address.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "clk-mtk.h" 138c2ecf20Sopenharmony_ci#include "clk-mux.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci return container_of(hw, struct mtk_clk_mux, hw); 188c2ecf20Sopenharmony_ci} 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int mtk_clk_mux_enable(struct clk_hw *hw) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 238c2ecf20Sopenharmony_ci u32 mask = BIT(mux->data->gate_shift); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return regmap_update_bits(mux->regmap, mux->data->mux_ofs, 268c2ecf20Sopenharmony_ci mask, ~mask); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void mtk_clk_mux_disable(struct clk_hw *hw) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 328c2ecf20Sopenharmony_ci u32 mask = BIT(mux->data->gate_shift); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci regmap_update_bits(mux->regmap, mux->data->mux_ofs, mask, mask); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int mtk_clk_mux_enable_setclr(struct clk_hw *hw) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return regmap_write(mux->regmap, mux->data->clr_ofs, 428c2ecf20Sopenharmony_ci BIT(mux->data->gate_shift)); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void mtk_clk_mux_disable_setclr(struct clk_hw *hw) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci regmap_write(mux->regmap, mux->data->set_ofs, 508c2ecf20Sopenharmony_ci BIT(mux->data->gate_shift)); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int mtk_clk_mux_is_enabled(struct clk_hw *hw) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 568c2ecf20Sopenharmony_ci u32 val; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci regmap_read(mux->regmap, mux->data->mux_ofs, &val); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return (val & BIT(mux->data->gate_shift)) == 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic u8 mtk_clk_mux_get_parent(struct clk_hw *hw) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 668c2ecf20Sopenharmony_ci u32 mask = GENMASK(mux->data->mux_width - 1, 0); 678c2ecf20Sopenharmony_ci u32 val; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci regmap_read(mux->regmap, mux->data->mux_ofs, &val); 708c2ecf20Sopenharmony_ci val = (val >> mux->data->mux_shift) & mask; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return val; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int mtk_clk_mux_set_parent_lock(struct clk_hw *hw, u8 index) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 788c2ecf20Sopenharmony_ci u32 mask = GENMASK(mux->data->mux_width - 1, 0); 798c2ecf20Sopenharmony_ci unsigned long flags = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (mux->lock) 828c2ecf20Sopenharmony_ci spin_lock_irqsave(mux->lock, flags); 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci __acquire(mux->lock); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci regmap_update_bits(mux->regmap, mux->data->mux_ofs, mask, 878c2ecf20Sopenharmony_ci index << mux->data->mux_shift); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (mux->lock) 908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(mux->lock, flags); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci __release(mux->lock); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int mtk_clk_mux_set_parent_setclr_lock(struct clk_hw *hw, u8 index) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 1008c2ecf20Sopenharmony_ci u32 mask = GENMASK(mux->data->mux_width - 1, 0); 1018c2ecf20Sopenharmony_ci u32 val, orig; 1028c2ecf20Sopenharmony_ci unsigned long flags = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (mux->lock) 1058c2ecf20Sopenharmony_ci spin_lock_irqsave(mux->lock, flags); 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci __acquire(mux->lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci regmap_read(mux->regmap, mux->data->mux_ofs, &orig); 1108c2ecf20Sopenharmony_ci val = (orig & ~(mask << mux->data->mux_shift)) 1118c2ecf20Sopenharmony_ci | (index << mux->data->mux_shift); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (val != orig) { 1148c2ecf20Sopenharmony_ci regmap_write(mux->regmap, mux->data->clr_ofs, 1158c2ecf20Sopenharmony_ci mask << mux->data->mux_shift); 1168c2ecf20Sopenharmony_ci regmap_write(mux->regmap, mux->data->set_ofs, 1178c2ecf20Sopenharmony_ci index << mux->data->mux_shift); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (mux->data->upd_shift >= 0) 1208c2ecf20Sopenharmony_ci regmap_write(mux->regmap, mux->data->upd_ofs, 1218c2ecf20Sopenharmony_ci BIT(mux->data->upd_shift)); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (mux->lock) 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(mux->lock, flags); 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci __release(mux->lock); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciconst struct clk_ops mtk_mux_ops = { 1338c2ecf20Sopenharmony_ci .get_parent = mtk_clk_mux_get_parent, 1348c2ecf20Sopenharmony_ci .set_parent = mtk_clk_mux_set_parent_lock, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciconst struct clk_ops mtk_mux_clr_set_upd_ops = { 1388c2ecf20Sopenharmony_ci .get_parent = mtk_clk_mux_get_parent, 1398c2ecf20Sopenharmony_ci .set_parent = mtk_clk_mux_set_parent_setclr_lock, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciconst struct clk_ops mtk_mux_gate_ops = { 1438c2ecf20Sopenharmony_ci .enable = mtk_clk_mux_enable, 1448c2ecf20Sopenharmony_ci .disable = mtk_clk_mux_disable, 1458c2ecf20Sopenharmony_ci .is_enabled = mtk_clk_mux_is_enabled, 1468c2ecf20Sopenharmony_ci .get_parent = mtk_clk_mux_get_parent, 1478c2ecf20Sopenharmony_ci .set_parent = mtk_clk_mux_set_parent_lock, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciconst struct clk_ops mtk_mux_gate_clr_set_upd_ops = { 1518c2ecf20Sopenharmony_ci .enable = mtk_clk_mux_enable_setclr, 1528c2ecf20Sopenharmony_ci .disable = mtk_clk_mux_disable_setclr, 1538c2ecf20Sopenharmony_ci .is_enabled = mtk_clk_mux_is_enabled, 1548c2ecf20Sopenharmony_ci .get_parent = mtk_clk_mux_get_parent, 1558c2ecf20Sopenharmony_ci .set_parent = mtk_clk_mux_set_parent_setclr_lock, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistruct clk *mtk_clk_register_mux(const struct mtk_mux *mux, 1598c2ecf20Sopenharmony_ci struct regmap *regmap, 1608c2ecf20Sopenharmony_ci spinlock_t *lock) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct mtk_clk_mux *clk_mux; 1638c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 1648c2ecf20Sopenharmony_ci struct clk *clk; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci clk_mux = kzalloc(sizeof(*clk_mux), GFP_KERNEL); 1678c2ecf20Sopenharmony_ci if (!clk_mux) 1688c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci init.name = mux->name; 1718c2ecf20Sopenharmony_ci init.flags = mux->flags | CLK_SET_RATE_PARENT; 1728c2ecf20Sopenharmony_ci init.parent_names = mux->parent_names; 1738c2ecf20Sopenharmony_ci init.num_parents = mux->num_parents; 1748c2ecf20Sopenharmony_ci init.ops = mux->ops; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci clk_mux->regmap = regmap; 1778c2ecf20Sopenharmony_ci clk_mux->data = mux; 1788c2ecf20Sopenharmony_ci clk_mux->lock = lock; 1798c2ecf20Sopenharmony_ci clk_mux->hw.init = &init; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci clk = clk_register(NULL, &clk_mux->hw); 1828c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1838c2ecf20Sopenharmony_ci kfree(clk_mux); 1848c2ecf20Sopenharmony_ci return clk; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return clk; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ciint mtk_clk_register_muxes(const struct mtk_mux *muxes, 1918c2ecf20Sopenharmony_ci int num, struct device_node *node, 1928c2ecf20Sopenharmony_ci spinlock_t *lock, 1938c2ecf20Sopenharmony_ci struct clk_onecell_data *clk_data) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct regmap *regmap; 1968c2ecf20Sopenharmony_ci struct clk *clk; 1978c2ecf20Sopenharmony_ci int i; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(node); 2008c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 2018c2ecf20Sopenharmony_ci pr_err("Cannot find regmap for %pOF: %ld\n", node, 2028c2ecf20Sopenharmony_ci PTR_ERR(regmap)); 2038c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 2078c2ecf20Sopenharmony_ci const struct mtk_mux *mux = &muxes[i]; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(clk_data->clks[mux->id])) { 2108c2ecf20Sopenharmony_ci clk = mtk_clk_register_mux(mux, regmap, lock); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2138c2ecf20Sopenharmony_ci pr_err("Failed to register clk %s: %ld\n", 2148c2ecf20Sopenharmony_ci mux->name, PTR_ERR(clk)); 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci clk_data->clks[mux->id] = clk; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 224