18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Rockchip Electronics Co. Ltd. 48c2ecf20Sopenharmony_ci * Author: Lin Huang <hl@rock-chips.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <soc/rockchip/rockchip_sip.h> 138c2ecf20Sopenharmony_ci#include "clk.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct rockchip_ddrclk { 168c2ecf20Sopenharmony_ci struct clk_hw hw; 178c2ecf20Sopenharmony_ci void __iomem *reg_base; 188c2ecf20Sopenharmony_ci int mux_offset; 198c2ecf20Sopenharmony_ci int mux_shift; 208c2ecf20Sopenharmony_ci int mux_width; 218c2ecf20Sopenharmony_ci int div_shift; 228c2ecf20Sopenharmony_ci int div_width; 238c2ecf20Sopenharmony_ci int ddr_flag; 248c2ecf20Sopenharmony_ci spinlock_t *lock; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, 308c2ecf20Sopenharmony_ci unsigned long prate) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); 338c2ecf20Sopenharmony_ci unsigned long flags; 348c2ecf20Sopenharmony_ci struct arm_smccc_res res; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci spin_lock_irqsave(ddrclk->lock, flags); 378c2ecf20Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, 388c2ecf20Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, 398c2ecf20Sopenharmony_ci 0, 0, 0, 0, &res); 408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(ddrclk->lock, flags); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return res.a0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic unsigned long 468c2ecf20Sopenharmony_cirockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, 478c2ecf20Sopenharmony_ci unsigned long parent_rate) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct arm_smccc_res res; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, 528c2ecf20Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, 538c2ecf20Sopenharmony_ci 0, 0, 0, 0, &res); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return res.a0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, 598c2ecf20Sopenharmony_ci unsigned long rate, 608c2ecf20Sopenharmony_ci unsigned long *prate) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct arm_smccc_res res; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, 658c2ecf20Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, 668c2ecf20Sopenharmony_ci 0, 0, 0, 0, &res); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return res.a0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); 748c2ecf20Sopenharmony_ci u32 val; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci val = readl(ddrclk->reg_base + 778c2ecf20Sopenharmony_ci ddrclk->mux_offset) >> ddrclk->mux_shift; 788c2ecf20Sopenharmony_ci val &= GENMASK(ddrclk->mux_width - 1, 0); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return val; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_ddrclk_sip_ops = { 848c2ecf20Sopenharmony_ci .recalc_rate = rockchip_ddrclk_sip_recalc_rate, 858c2ecf20Sopenharmony_ci .set_rate = rockchip_ddrclk_sip_set_rate, 868c2ecf20Sopenharmony_ci .round_rate = rockchip_ddrclk_sip_round_rate, 878c2ecf20Sopenharmony_ci .get_parent = rockchip_ddrclk_get_parent, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct clk *rockchip_clk_register_ddrclk(const char *name, int flags, 918c2ecf20Sopenharmony_ci const char *const *parent_names, 928c2ecf20Sopenharmony_ci u8 num_parents, int mux_offset, 938c2ecf20Sopenharmony_ci int mux_shift, int mux_width, 948c2ecf20Sopenharmony_ci int div_shift, int div_width, 958c2ecf20Sopenharmony_ci int ddr_flag, void __iomem *reg_base, 968c2ecf20Sopenharmony_ci spinlock_t *lock) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct rockchip_ddrclk *ddrclk; 998c2ecf20Sopenharmony_ci struct clk_init_data init; 1008c2ecf20Sopenharmony_ci struct clk *clk; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL); 1038c2ecf20Sopenharmony_ci if (!ddrclk) 1048c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci init.name = name; 1078c2ecf20Sopenharmony_ci init.parent_names = parent_names; 1088c2ecf20Sopenharmony_ci init.num_parents = num_parents; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci init.flags = flags; 1118c2ecf20Sopenharmony_ci init.flags |= CLK_SET_RATE_NO_REPARENT; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci switch (ddr_flag) { 1148c2ecf20Sopenharmony_ci case ROCKCHIP_DDRCLK_SIP: 1158c2ecf20Sopenharmony_ci init.ops = &rockchip_ddrclk_sip_ops; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci default: 1188c2ecf20Sopenharmony_ci pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag); 1198c2ecf20Sopenharmony_ci kfree(ddrclk); 1208c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ddrclk->reg_base = reg_base; 1248c2ecf20Sopenharmony_ci ddrclk->lock = lock; 1258c2ecf20Sopenharmony_ci ddrclk->hw.init = &init; 1268c2ecf20Sopenharmony_ci ddrclk->mux_offset = mux_offset; 1278c2ecf20Sopenharmony_ci ddrclk->mux_shift = mux_shift; 1288c2ecf20Sopenharmony_ci ddrclk->mux_width = mux_width; 1298c2ecf20Sopenharmony_ci ddrclk->div_shift = div_shift; 1308c2ecf20Sopenharmony_ci ddrclk->div_width = div_width; 1318c2ecf20Sopenharmony_ci ddrclk->ddr_flag = ddr_flag; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci clk = clk_register(NULL, &ddrclk->hw); 1348c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 1358c2ecf20Sopenharmony_ci kfree(ddrclk); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return clk; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk); 140