1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2012 Freescale Semiconductor, Inc.
4 * Copyright 2012 Linaro Ltd.
8 #include <linux/clk-provider.h>
10 #include <linux/slab.h>
11 #include <linux/jiffies.h>
12 #include <linux/err.h>
15 static int clk_busy_wait(void __iomem
*reg
, u8 shift
)
17 unsigned long timeout
= jiffies
+ msecs_to_jiffies(10);
19 while (readl_relaxed(reg
) & (1 << shift
))
20 if (time_after(jiffies
, timeout
))
26 struct clk_busy_divider
{
27 struct clk_divider div
;
28 const struct clk_ops
*div_ops
;
33 static inline struct clk_busy_divider
*to_clk_busy_divider(struct clk_hw
*hw
)
35 struct clk_divider
*div
= to_clk_divider(hw
);
37 return container_of(div
, struct clk_busy_divider
, div
);
40 static unsigned long clk_busy_divider_recalc_rate(struct clk_hw
*hw
,
41 unsigned long parent_rate
)
43 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
45 return busy
->div_ops
->recalc_rate(&busy
->div
.hw
, parent_rate
);
48 static long clk_busy_divider_round_rate(struct clk_hw
*hw
, unsigned long rate
,
51 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
53 return busy
->div_ops
->round_rate(&busy
->div
.hw
, rate
, prate
);
56 static int clk_busy_divider_set_rate(struct clk_hw
*hw
, unsigned long rate
,
57 unsigned long parent_rate
)
59 struct clk_busy_divider
*busy
= to_clk_busy_divider(hw
);
62 ret
= busy
->div_ops
->set_rate(&busy
->div
.hw
, rate
, parent_rate
);
64 ret
= clk_busy_wait(busy
->reg
, busy
->shift
);
69 static const struct clk_ops clk_busy_divider_ops
= {
70 .recalc_rate
= clk_busy_divider_recalc_rate
,
71 .round_rate
= clk_busy_divider_round_rate
,
72 .set_rate
= clk_busy_divider_set_rate
,
75 struct clk_hw
*imx_clk_hw_busy_divider(const char *name
, const char *parent_name
,
76 void __iomem
*reg
, u8 shift
, u8 width
,
77 void __iomem
*busy_reg
, u8 busy_shift
)
79 struct clk_busy_divider
*busy
;
81 struct clk_init_data init
;
84 busy
= kzalloc(sizeof(*busy
), GFP_KERNEL
);
86 return ERR_PTR(-ENOMEM
);
89 busy
->shift
= busy_shift
;
92 busy
->div
.shift
= shift
;
93 busy
->div
.width
= width
;
94 busy
->div
.lock
= &imx_ccm_lock
;
95 busy
->div_ops
= &clk_divider_ops
;
98 init
.ops
= &clk_busy_divider_ops
;
99 init
.flags
= CLK_SET_RATE_PARENT
| CLK_IS_CRITICAL
;
100 init
.parent_names
= &parent_name
;
101 init
.num_parents
= 1;
103 busy
->div
.hw
.init
= &init
;
107 ret
= clk_hw_register(NULL
, hw
);
116 struct clk_busy_mux
{
118 const struct clk_ops
*mux_ops
;
123 static inline struct clk_busy_mux
*to_clk_busy_mux(struct clk_hw
*hw
)
125 struct clk_mux
*mux
= to_clk_mux(hw
);
127 return container_of(mux
, struct clk_busy_mux
, mux
);
130 static u8
clk_busy_mux_get_parent(struct clk_hw
*hw
)
132 struct clk_busy_mux
*busy
= to_clk_busy_mux(hw
);
134 return busy
->mux_ops
->get_parent(&busy
->mux
.hw
);
137 static int clk_busy_mux_set_parent(struct clk_hw
*hw
, u8 index
)
139 struct clk_busy_mux
*busy
= to_clk_busy_mux(hw
);
142 ret
= busy
->mux_ops
->set_parent(&busy
->mux
.hw
, index
);
144 ret
= clk_busy_wait(busy
->reg
, busy
->shift
);
149 static const struct clk_ops clk_busy_mux_ops
= {
150 .get_parent
= clk_busy_mux_get_parent
,
151 .set_parent
= clk_busy_mux_set_parent
,
154 struct clk_hw
*imx_clk_hw_busy_mux(const char *name
, void __iomem
*reg
, u8 shift
,
155 u8 width
, void __iomem
*busy_reg
, u8 busy_shift
,
156 const char * const *parent_names
, int num_parents
)
158 struct clk_busy_mux
*busy
;
160 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
;
178 init
.flags
= CLK_IS_CRITICAL
;
179 init
.parent_names
= parent_names
;
180 init
.num_parents
= num_parents
;
182 busy
->mux
.hw
.init
= &init
;
186 ret
= clk_hw_register(NULL
, hw
);