2 * Copyright 2012 Freescale Semiconductor, Inc.
3 * Copyright 2012 Linaro Ltd.
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
13 #include <linux/clk.h>
14 #include <linux/clk-provider.h>
16 #include <linux/slab.h>
17 #include <linux/jiffies.h>
18 #include <linux/err.h>
21 static int clk_busy_wait(void __iomem
*reg
, u8 shift
)
23 unsigned long timeout
= jiffies
+ msecs_to_jiffies(10);
25 while (readl_relaxed(reg
) & (1 << shift
))
26 if (time_after(jiffies
, timeout
))
32 struct clk_busy_divider
{
33 struct clk_divider div
;
34 const struct clk_ops
*div_ops
;
39 static inline struct clk_busy_divider
*to_clk_busy_divider(struct clk_hw
*hw
)
41 struct clk_divider
*div
= to_clk_divider(hw
);
43 return container_of(div
, struct clk_busy_divider
, div
);
46 static unsigned long clk_busy_divider_recalc_rate(struct clk_hw
*hw
,
47 unsigned long parent_rate
)
49 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
51 return busy
->div_ops
->recalc_rate(&busy
->div
.hw
, parent_rate
);
54 static long clk_busy_divider_round_rate(struct clk_hw
*hw
, unsigned long rate
,
57 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
59 return busy
->div_ops
->round_rate(&busy
->div
.hw
, rate
, prate
);
62 static int clk_busy_divider_set_rate(struct clk_hw
*hw
, unsigned long rate
,
63 unsigned long parent_rate
)
65 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
68 ret
= busy
->div_ops
->set_rate(&busy
->div
.hw
, rate
, parent_rate
);
70 ret
= clk_busy_wait(busy
->reg
, busy
->shift
);
75 static struct clk_ops clk_busy_divider_ops
= {
76 .recalc_rate
= clk_busy_divider_recalc_rate
,
77 .round_rate
= clk_busy_divider_round_rate
,
78 .set_rate
= clk_busy_divider_set_rate
,
81 struct clk
*imx_clk_busy_divider(const char *name
, const char *parent_name
,
82 void __iomem
*reg
, u8 shift
, u8 width
,
83 void __iomem
*busy_reg
, u8 busy_shift
)
85 struct clk_busy_divider
*busy
;
87 struct clk_init_data init
;
89 busy
= kzalloc(sizeof(*busy
), GFP_KERNEL
);
91 return ERR_PTR(-ENOMEM
);
94 busy
->shift
= busy_shift
;
97 busy
->div
.shift
= shift
;
98 busy
->div
.width
= width
;
99 busy
->div
.lock
= &imx_ccm_lock
;
100 busy
->div_ops
= &clk_divider_ops
;
103 init
.ops
= &clk_busy_divider_ops
;
104 init
.flags
= CLK_SET_RATE_PARENT
;
105 init
.parent_names
= &parent_name
;
106 init
.num_parents
= 1;
108 busy
->div
.hw
.init
= &init
;
110 clk
= clk_register(NULL
, &busy
->div
.hw
);
117 struct clk_busy_mux
{
119 const struct clk_ops
*mux_ops
;
124 static inline struct clk_busy_mux
*to_clk_busy_mux(struct clk_hw
*hw
)
126 struct clk_mux
*mux
= to_clk_mux(hw
);
128 return container_of(mux
, struct clk_busy_mux
, mux
);
131 static u8
clk_busy_mux_get_parent(struct clk_hw
*hw
)
133 struct clk_busy_mux
*busy
= to_clk_busy_mux(hw
);
135 return busy
->mux_ops
->get_parent(&busy
->mux
.hw
);
138 static int clk_busy_mux_set_parent(struct clk_hw
*hw
, u8 index
)
140 struct clk_busy_mux
*busy
= to_clk_busy_mux(hw
);
143 ret
= busy
->mux_ops
->set_parent(&busy
->mux
.hw
, index
);
145 ret
= clk_busy_wait(busy
->reg
, busy
->shift
);
150 static struct clk_ops clk_busy_mux_ops
= {
151 .get_parent
= clk_busy_mux_get_parent
,
152 .set_parent
= clk_busy_mux_set_parent
,
155 struct clk
*imx_clk_busy_mux(const char *name
, void __iomem
*reg
, u8 shift
,
156 u8 width
, void __iomem
*busy_reg
, u8 busy_shift
,
157 const char **parent_names
, int num_parents
)
159 struct clk_busy_mux
*busy
;
161 struct clk_init_data init
;
163 busy
= kzalloc(sizeof(*busy
), GFP_KERNEL
);
165 return ERR_PTR(-ENOMEM
);
167 busy
->reg
= busy_reg
;
168 busy
->shift
= busy_shift
;
171 busy
->mux
.shift
= shift
;
172 busy
->mux
.mask
= BIT(width
) - 1;
173 busy
->mux
.lock
= &imx_ccm_lock
;
174 busy
->mux_ops
= &clk_mux_ops
;
177 init
.ops
= &clk_busy_mux_ops
;
179 init
.parent_names
= parent_names
;
180 init
.num_parents
= num_parents
;
182 busy
->mux
.hw
.init
= &init
;
184 clk
= clk_register(NULL
, &busy
->mux
.hw
);