162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Rockchip Electronics Co. Ltd. 462306a36Sopenharmony_ci * Author: Lin Huang <hl@rock-chips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/arm-smccc.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <soc/rockchip/rockchip_sip.h> 1362306a36Sopenharmony_ci#include "clk.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct rockchip_ddrclk { 1662306a36Sopenharmony_ci struct clk_hw hw; 1762306a36Sopenharmony_ci void __iomem *reg_base; 1862306a36Sopenharmony_ci int mux_offset; 1962306a36Sopenharmony_ci int mux_shift; 2062306a36Sopenharmony_ci int mux_width; 2162306a36Sopenharmony_ci int div_shift; 2262306a36Sopenharmony_ci int div_width; 2362306a36Sopenharmony_ci int ddr_flag; 2462306a36Sopenharmony_ci spinlock_t *lock; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, 3062306a36Sopenharmony_ci unsigned long prate) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); 3362306a36Sopenharmony_ci unsigned long flags; 3462306a36Sopenharmony_ci struct arm_smccc_res res; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci spin_lock_irqsave(ddrclk->lock, flags); 3762306a36Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, 3862306a36Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, 3962306a36Sopenharmony_ci 0, 0, 0, 0, &res); 4062306a36Sopenharmony_ci spin_unlock_irqrestore(ddrclk->lock, flags); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return res.a0; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic unsigned long 4662306a36Sopenharmony_cirockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, 4762306a36Sopenharmony_ci unsigned long parent_rate) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct arm_smccc_res res; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, 5262306a36Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, 5362306a36Sopenharmony_ci 0, 0, 0, 0, &res); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return res.a0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, 5962306a36Sopenharmony_ci unsigned long rate, 6062306a36Sopenharmony_ci unsigned long *prate) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct arm_smccc_res res; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, 6562306a36Sopenharmony_ci ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, 6662306a36Sopenharmony_ci 0, 0, 0, 0, &res); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return res.a0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); 7462306a36Sopenharmony_ci u32 val; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci val = readl(ddrclk->reg_base + 7762306a36Sopenharmony_ci ddrclk->mux_offset) >> ddrclk->mux_shift; 7862306a36Sopenharmony_ci val &= GENMASK(ddrclk->mux_width - 1, 0); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return val; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct clk_ops rockchip_ddrclk_sip_ops = { 8462306a36Sopenharmony_ci .recalc_rate = rockchip_ddrclk_sip_recalc_rate, 8562306a36Sopenharmony_ci .set_rate = rockchip_ddrclk_sip_set_rate, 8662306a36Sopenharmony_ci .round_rate = rockchip_ddrclk_sip_round_rate, 8762306a36Sopenharmony_ci .get_parent = rockchip_ddrclk_get_parent, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct clk *rockchip_clk_register_ddrclk(const char *name, int flags, 9162306a36Sopenharmony_ci const char *const *parent_names, 9262306a36Sopenharmony_ci u8 num_parents, int mux_offset, 9362306a36Sopenharmony_ci int mux_shift, int mux_width, 9462306a36Sopenharmony_ci int div_shift, int div_width, 9562306a36Sopenharmony_ci int ddr_flag, void __iomem *reg_base, 9662306a36Sopenharmony_ci spinlock_t *lock) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct rockchip_ddrclk *ddrclk; 9962306a36Sopenharmony_ci struct clk_init_data init; 10062306a36Sopenharmony_ci struct clk *clk; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL); 10362306a36Sopenharmony_ci if (!ddrclk) 10462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci init.name = name; 10762306a36Sopenharmony_ci init.parent_names = parent_names; 10862306a36Sopenharmony_ci init.num_parents = num_parents; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci init.flags = flags; 11162306a36Sopenharmony_ci init.flags |= CLK_SET_RATE_NO_REPARENT; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci switch (ddr_flag) { 11462306a36Sopenharmony_ci case ROCKCHIP_DDRCLK_SIP: 11562306a36Sopenharmony_ci init.ops = &rockchip_ddrclk_sip_ops; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag); 11962306a36Sopenharmony_ci kfree(ddrclk); 12062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ddrclk->reg_base = reg_base; 12462306a36Sopenharmony_ci ddrclk->lock = lock; 12562306a36Sopenharmony_ci ddrclk->hw.init = &init; 12662306a36Sopenharmony_ci ddrclk->mux_offset = mux_offset; 12762306a36Sopenharmony_ci ddrclk->mux_shift = mux_shift; 12862306a36Sopenharmony_ci ddrclk->mux_width = mux_width; 12962306a36Sopenharmony_ci ddrclk->div_shift = div_shift; 13062306a36Sopenharmony_ci ddrclk->div_width = div_width; 13162306a36Sopenharmony_ci ddrclk->ddr_flag = ddr_flag; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci clk = clk_register(NULL, &ddrclk->hw); 13462306a36Sopenharmony_ci if (IS_ERR(clk)) 13562306a36Sopenharmony_ci kfree(ddrclk); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return clk; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk); 140