123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- /*
- * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
- * Author: Lin Huang <hl@rock-chips.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/arm-smccc.h>
- #include <linux/clk.h>
- #include <linux/clk-provider.h>
- #include <linux/io.h>
- #include <linux/slab.h>
- #include <soc/rockchip/rockchip_sip.h>
- #include "clk.h"
- struct rockchip_ddrclk {
- struct clk_hw hw;
- void __iomem *reg_base;
- int mux_offset;
- int mux_shift;
- int mux_width;
- int div_shift;
- int div_width;
- int ddr_flag;
- spinlock_t *lock;
- };
- #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
- static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
- unsigned long prate)
- {
- struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
- unsigned long flags;
- struct arm_smccc_res res;
- spin_lock_irqsave(ddrclk->lock, flags);
- arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
- ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
- 0, 0, 0, 0, &res);
- spin_unlock_irqrestore(ddrclk->lock, flags);
- return res.a0;
- }
- static unsigned long
- rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct arm_smccc_res res;
- arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
- ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
- 0, 0, 0, 0, &res);
- return res.a0;
- }
- static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long *prate)
- {
- struct arm_smccc_res res;
- arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
- ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
- 0, 0, 0, 0, &res);
- return res.a0;
- }
- static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
- {
- struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
- int num_parents = clk_hw_get_num_parents(hw);
- u32 val;
- val = clk_readl(ddrclk->reg_base +
- ddrclk->mux_offset) >> ddrclk->mux_shift;
- val &= GENMASK(ddrclk->mux_width - 1, 0);
- if (val >= num_parents)
- return -EINVAL;
- return val;
- }
- static const struct clk_ops rockchip_ddrclk_sip_ops = {
- .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
- .set_rate = rockchip_ddrclk_sip_set_rate,
- .round_rate = rockchip_ddrclk_sip_round_rate,
- .get_parent = rockchip_ddrclk_get_parent,
- };
- struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
- const char *const *parent_names,
- u8 num_parents, int mux_offset,
- int mux_shift, int mux_width,
- int div_shift, int div_width,
- int ddr_flag, void __iomem *reg_base,
- spinlock_t *lock)
- {
- struct rockchip_ddrclk *ddrclk;
- struct clk_init_data init;
- struct clk *clk;
- ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
- if (!ddrclk)
- return ERR_PTR(-ENOMEM);
- init.name = name;
- init.parent_names = parent_names;
- init.num_parents = num_parents;
- init.flags = flags;
- init.flags |= CLK_SET_RATE_NO_REPARENT;
- switch (ddr_flag) {
- case ROCKCHIP_DDRCLK_SIP:
- init.ops = &rockchip_ddrclk_sip_ops;
- break;
- default:
- pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
- kfree(ddrclk);
- return ERR_PTR(-EINVAL);
- }
- ddrclk->reg_base = reg_base;
- ddrclk->lock = lock;
- ddrclk->hw.init = &init;
- ddrclk->mux_offset = mux_offset;
- ddrclk->mux_shift = mux_shift;
- ddrclk->mux_width = mux_width;
- ddrclk->div_shift = div_shift;
- ddrclk->div_width = div_width;
- ddrclk->ddr_flag = ddr_flag;
- clk = clk_register(NULL, &ddrclk->hw);
- if (IS_ERR(clk))
- kfree(ddrclk);
- return clk;
- }
|