2 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
3 * Author: Lin Huang <hl@rock-chips.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <linux/arm-smccc.h>
17 #include <linux/clk.h>
18 #include <linux/clk-provider.h>
20 #include <linux/slab.h>
21 #include <soc/rockchip/rockchip_sip.h>
24 struct rockchip_ddrclk
{
26 void __iomem
*reg_base
;
36 #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
38 static int rockchip_ddrclk_sip_set_rate(struct clk_hw
*hw
, unsigned long drate
,
41 struct rockchip_ddrclk
*ddrclk
= to_rockchip_ddrclk_hw(hw
);
43 struct arm_smccc_res res
;
45 spin_lock_irqsave(ddrclk
->lock
, flags
);
46 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ
, drate
, 0,
47 ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE
,
49 spin_unlock_irqrestore(ddrclk
->lock
, flags
);
55 rockchip_ddrclk_sip_recalc_rate(struct clk_hw
*hw
,
56 unsigned long parent_rate
)
58 struct arm_smccc_res res
;
60 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ
, 0, 0,
61 ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE
,
67 static long rockchip_ddrclk_sip_round_rate(struct clk_hw
*hw
,
71 struct arm_smccc_res res
;
73 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ
, rate
, 0,
74 ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE
,
80 static u8
rockchip_ddrclk_get_parent(struct clk_hw
*hw
)
82 struct rockchip_ddrclk
*ddrclk
= to_rockchip_ddrclk_hw(hw
);
83 int num_parents
= clk_hw_get_num_parents(hw
);
86 val
= clk_readl(ddrclk
->reg_base
+
87 ddrclk
->mux_offset
) >> ddrclk
->mux_shift
;
88 val
&= GENMASK(ddrclk
->mux_width
- 1, 0);
90 if (val
>= num_parents
)
96 static const struct clk_ops rockchip_ddrclk_sip_ops
= {
97 .recalc_rate
= rockchip_ddrclk_sip_recalc_rate
,
98 .set_rate
= rockchip_ddrclk_sip_set_rate
,
99 .round_rate
= rockchip_ddrclk_sip_round_rate
,
100 .get_parent
= rockchip_ddrclk_get_parent
,
103 struct clk
*rockchip_clk_register_ddrclk(const char *name
, int flags
,
104 const char *const *parent_names
,
105 u8 num_parents
, int mux_offset
,
106 int mux_shift
, int mux_width
,
107 int div_shift
, int div_width
,
108 int ddr_flag
, void __iomem
*reg_base
,
111 struct rockchip_ddrclk
*ddrclk
;
112 struct clk_init_data init
;
115 ddrclk
= kzalloc(sizeof(*ddrclk
), GFP_KERNEL
);
117 return ERR_PTR(-ENOMEM
);
120 init
.parent_names
= parent_names
;
121 init
.num_parents
= num_parents
;
124 init
.flags
|= CLK_SET_RATE_NO_REPARENT
;
127 case ROCKCHIP_DDRCLK_SIP
:
128 init
.ops
= &rockchip_ddrclk_sip_ops
;
131 pr_err("%s: unsupported ddrclk type %d\n", __func__
, ddr_flag
);
133 return ERR_PTR(-EINVAL
);
136 ddrclk
->reg_base
= reg_base
;
138 ddrclk
->hw
.init
= &init
;
139 ddrclk
->mux_offset
= mux_offset
;
140 ddrclk
->mux_shift
= mux_shift
;
141 ddrclk
->mux_width
= mux_width
;
142 ddrclk
->div_shift
= div_shift
;
143 ddrclk
->div_width
= div_width
;
144 ddrclk
->ddr_flag
= ddr_flag
;
146 clk
= clk_register(NULL
, &ddrclk
->hw
);