18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Spreadtrum multiplexer clock driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2017 Spreadtrum, Inc. 68c2ecf20Sopenharmony_ci// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/regmap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "mux.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ciu8 sprd_mux_helper_get_parent(const struct sprd_clk_common *common, 158c2ecf20Sopenharmony_ci const struct sprd_mux_ssel *mux) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci unsigned int reg; 188c2ecf20Sopenharmony_ci u8 parent; 198c2ecf20Sopenharmony_ci int num_parents; 208c2ecf20Sopenharmony_ci int i; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci regmap_read(common->regmap, common->reg, ®); 238c2ecf20Sopenharmony_ci parent = reg >> mux->shift; 248c2ecf20Sopenharmony_ci parent &= (1 << mux->width) - 1; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!mux->table) 278c2ecf20Sopenharmony_ci return parent; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci num_parents = clk_hw_get_num_parents(&common->hw); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci for (i = 0; i < num_parents - 1; i++) 328c2ecf20Sopenharmony_ci if (parent >= mux->table[i] && parent < mux->table[i + 1]) 338c2ecf20Sopenharmony_ci return i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return num_parents - 1; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_mux_helper_get_parent); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic u8 sprd_mux_get_parent(struct clk_hw *hw) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct sprd_mux *cm = hw_to_sprd_mux(hw); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return sprd_mux_helper_get_parent(&cm->common, &cm->mux); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint sprd_mux_helper_set_parent(const struct sprd_clk_common *common, 478c2ecf20Sopenharmony_ci const struct sprd_mux_ssel *mux, 488c2ecf20Sopenharmony_ci u8 index) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci unsigned int reg; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (mux->table) 538c2ecf20Sopenharmony_ci index = mux->table[index]; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci regmap_read(common->regmap, common->reg, ®); 568c2ecf20Sopenharmony_ci reg &= ~GENMASK(mux->width + mux->shift - 1, mux->shift); 578c2ecf20Sopenharmony_ci regmap_write(common->regmap, common->reg, 588c2ecf20Sopenharmony_ci reg | (index << mux->shift)); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_mux_helper_set_parent); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int sprd_mux_set_parent(struct clk_hw *hw, u8 index) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct sprd_mux *cm = hw_to_sprd_mux(hw); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return sprd_mux_helper_set_parent(&cm->common, &cm->mux, index); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciconst struct clk_ops sprd_mux_ops = { 728c2ecf20Sopenharmony_ci .get_parent = sprd_mux_get_parent, 738c2ecf20Sopenharmony_ci .set_parent = sprd_mux_set_parent, 748c2ecf20Sopenharmony_ci .determine_rate = __clk_mux_determine_rate, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sprd_mux_ops); 77