1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright 2012 Freescale Semiconductor, Inc.
4 * Copyright 2012 Linaro Ltd.
5 */
6
7#include <linux/bits.h>
8#include <linux/clk.h>
9#include <linux/clk-provider.h>
10#include <linux/io.h>
11#include <linux/slab.h>
12#include <linux/jiffies.h>
13#include <linux/err.h>
14#include "clk.h"
15
16static int clk_busy_wait(void __iomem *reg, u8 shift)
17{
18	unsigned long timeout = jiffies + msecs_to_jiffies(10);
19
20	while (readl_relaxed(reg) & (1 << shift))
21		if (time_after(jiffies, timeout))
22			return -ETIMEDOUT;
23
24	return 0;
25}
26
27struct clk_busy_divider {
28	struct clk_divider div;
29	const struct clk_ops *div_ops;
30	void __iomem *reg;
31	u8 shift;
32};
33
34static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)
35{
36	struct clk_divider *div = to_clk_divider(hw);
37
38	return container_of(div, struct clk_busy_divider, div);
39}
40
41static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,
42						  unsigned long parent_rate)
43{
44	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
45
46	return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);
47}
48
49static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
50					unsigned long *prate)
51{
52	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
53
54	return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
55}
56
57static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
58		unsigned long parent_rate)
59{
60	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
61	int ret;
62
63	ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
64	if (!ret)
65		ret = clk_busy_wait(busy->reg, busy->shift);
66
67	return ret;
68}
69
70static const struct clk_ops clk_busy_divider_ops = {
71	.recalc_rate = clk_busy_divider_recalc_rate,
72	.round_rate = clk_busy_divider_round_rate,
73	.set_rate = clk_busy_divider_set_rate,
74};
75
76struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name,
77				 void __iomem *reg, u8 shift, u8 width,
78				 void __iomem *busy_reg, u8 busy_shift)
79{
80	struct clk_busy_divider *busy;
81	struct clk_hw *hw;
82	struct clk_init_data init;
83	int ret;
84
85	busy = kzalloc(sizeof(*busy), GFP_KERNEL);
86	if (!busy)
87		return ERR_PTR(-ENOMEM);
88
89	busy->reg = busy_reg;
90	busy->shift = busy_shift;
91
92	busy->div.reg = reg;
93	busy->div.shift = shift;
94	busy->div.width = width;
95	busy->div.lock = &imx_ccm_lock;
96	busy->div_ops = &clk_divider_ops;
97
98	init.name = name;
99	init.ops = &clk_busy_divider_ops;
100	init.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL;
101	init.parent_names = &parent_name;
102	init.num_parents = 1;
103
104	busy->div.hw.init = &init;
105
106	hw = &busy->div.hw;
107
108	ret = clk_hw_register(NULL, hw);
109	if (ret) {
110		kfree(busy);
111		return ERR_PTR(ret);
112	}
113
114	return hw;
115}
116
117struct clk_busy_mux {
118	struct clk_mux mux;
119	const struct clk_ops *mux_ops;
120	void __iomem *reg;
121	u8 shift;
122};
123
124static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw)
125{
126	struct clk_mux *mux = to_clk_mux(hw);
127
128	return container_of(mux, struct clk_busy_mux, mux);
129}
130
131static u8 clk_busy_mux_get_parent(struct clk_hw *hw)
132{
133	struct clk_busy_mux *busy = to_clk_busy_mux(hw);
134
135	return busy->mux_ops->get_parent(&busy->mux.hw);
136}
137
138static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
139{
140	struct clk_busy_mux *busy = to_clk_busy_mux(hw);
141	int ret;
142
143	ret = busy->mux_ops->set_parent(&busy->mux.hw, index);
144	if (!ret)
145		ret = clk_busy_wait(busy->reg, busy->shift);
146
147	return ret;
148}
149
150static const struct clk_ops clk_busy_mux_ops = {
151	.determine_rate = clk_hw_determine_rate_no_reparent,
152	.get_parent = clk_busy_mux_get_parent,
153	.set_parent = clk_busy_mux_set_parent,
154};
155
156struct clk_hw *imx_clk_hw_busy_mux(const char *name, void __iomem *reg, u8 shift,
157			     u8 width, void __iomem *busy_reg, u8 busy_shift,
158			     const char * const *parent_names, int num_parents)
159{
160	struct clk_busy_mux *busy;
161	struct clk_hw *hw;
162	struct clk_init_data init;
163	int ret;
164
165	busy = kzalloc(sizeof(*busy), GFP_KERNEL);
166	if (!busy)
167		return ERR_PTR(-ENOMEM);
168
169	busy->reg = busy_reg;
170	busy->shift = busy_shift;
171
172	busy->mux.reg = reg;
173	busy->mux.shift = shift;
174	busy->mux.mask = BIT(width) - 1;
175	busy->mux.lock = &imx_ccm_lock;
176	busy->mux_ops = &clk_mux_ops;
177
178	init.name = name;
179	init.ops = &clk_busy_mux_ops;
180	init.flags = CLK_IS_CRITICAL;
181	init.parent_names = parent_names;
182	init.num_parents = num_parents;
183
184	busy->mux.hw.init = &init;
185
186	hw = &busy->mux.hw;
187
188	ret = clk_hw_register(NULL, hw);
189	if (ret) {
190		kfree(busy);
191		return ERR_PTR(ret);
192	}
193
194	return hw;
195}
196