1d3ff9728SAbel Vesa // SPDX-License-Identifier: GPL-2.0
2d3ff9728SAbel Vesa /*
3d3ff9728SAbel Vesa  * Copyright 2018 NXP
4d3ff9728SAbel Vesa  */
5d3ff9728SAbel Vesa 
6d3ff9728SAbel Vesa #include <linux/clk-provider.h>
762e59c4eSStephen Boyd #include <linux/errno.h>
862e59c4eSStephen Boyd #include <linux/io.h>
962e59c4eSStephen Boyd #include <linux/slab.h>
10d3ff9728SAbel Vesa 
11d3ff9728SAbel Vesa #include "clk.h"
12d3ff9728SAbel Vesa 
13d3ff9728SAbel Vesa #define PCG_PREDIV_SHIFT	16
14d3ff9728SAbel Vesa #define PCG_PREDIV_WIDTH	3
15d3ff9728SAbel Vesa #define PCG_PREDIV_MAX		8
16d3ff9728SAbel Vesa 
17d3ff9728SAbel Vesa #define PCG_DIV_SHIFT		0
18d3ff9728SAbel Vesa #define PCG_DIV_WIDTH		6
19d3ff9728SAbel Vesa #define PCG_DIV_MAX		64
20d3ff9728SAbel Vesa 
21d3ff9728SAbel Vesa #define PCG_PCS_SHIFT		24
22d3ff9728SAbel Vesa #define PCG_PCS_MASK		0x7
23d3ff9728SAbel Vesa 
24d3ff9728SAbel Vesa #define PCG_CGC_SHIFT		28
25d3ff9728SAbel Vesa 
26d3ff9728SAbel Vesa static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw,
27d3ff9728SAbel Vesa 						unsigned long parent_rate)
28d3ff9728SAbel Vesa {
29d3ff9728SAbel Vesa 	struct clk_divider *divider = to_clk_divider(hw);
30d3ff9728SAbel Vesa 	unsigned long prediv_rate;
31d3ff9728SAbel Vesa 	unsigned int prediv_value;
32d3ff9728SAbel Vesa 	unsigned int div_value;
33d3ff9728SAbel Vesa 
34d3ff9728SAbel Vesa 	prediv_value = readl(divider->reg) >> divider->shift;
35d3ff9728SAbel Vesa 	prediv_value &= clk_div_mask(divider->width);
36d3ff9728SAbel Vesa 
37d3ff9728SAbel Vesa 	prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value,
38d3ff9728SAbel Vesa 						NULL, divider->flags,
39d3ff9728SAbel Vesa 						divider->width);
40d3ff9728SAbel Vesa 
41d3ff9728SAbel Vesa 	div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
42d3ff9728SAbel Vesa 	div_value &= clk_div_mask(PCG_DIV_WIDTH);
43d3ff9728SAbel Vesa 
44d3ff9728SAbel Vesa 	return divider_recalc_rate(hw, prediv_rate, div_value, NULL,
45d3ff9728SAbel Vesa 				   divider->flags, PCG_DIV_WIDTH);
46d3ff9728SAbel Vesa }
47d3ff9728SAbel Vesa 
48d3ff9728SAbel Vesa static int imx8m_clk_composite_compute_dividers(unsigned long rate,
49d3ff9728SAbel Vesa 						unsigned long parent_rate,
50d3ff9728SAbel Vesa 						int *prediv, int *postdiv)
51d3ff9728SAbel Vesa {
52d3ff9728SAbel Vesa 	int div1, div2;
53d3ff9728SAbel Vesa 	int error = INT_MAX;
54d3ff9728SAbel Vesa 	int ret = -EINVAL;
55d3ff9728SAbel Vesa 
56d3ff9728SAbel Vesa 	*prediv = 1;
57d3ff9728SAbel Vesa 	*postdiv = 1;
58d3ff9728SAbel Vesa 
59d3ff9728SAbel Vesa 	for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
60d3ff9728SAbel Vesa 		for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
61d3ff9728SAbel Vesa 			int new_error = ((parent_rate / div1) / div2) - rate;
62d3ff9728SAbel Vesa 
63d3ff9728SAbel Vesa 			if (abs(new_error) < abs(error)) {
64d3ff9728SAbel Vesa 				*prediv = div1;
65d3ff9728SAbel Vesa 				*postdiv = div2;
66d3ff9728SAbel Vesa 				error = new_error;
67d3ff9728SAbel Vesa 				ret = 0;
68d3ff9728SAbel Vesa 			}
69d3ff9728SAbel Vesa 		}
70d3ff9728SAbel Vesa 	}
71d3ff9728SAbel Vesa 	return ret;
72d3ff9728SAbel Vesa }
73d3ff9728SAbel Vesa 
74d3ff9728SAbel Vesa static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw,
75d3ff9728SAbel Vesa 						unsigned long rate,
76d3ff9728SAbel Vesa 						unsigned long *prate)
77d3ff9728SAbel Vesa {
78d3ff9728SAbel Vesa 	int prediv_value;
79d3ff9728SAbel Vesa 	int div_value;
80d3ff9728SAbel Vesa 
81d3ff9728SAbel Vesa 	imx8m_clk_composite_compute_dividers(rate, *prate,
82d3ff9728SAbel Vesa 						&prediv_value, &div_value);
83d3ff9728SAbel Vesa 	rate = DIV_ROUND_UP(*prate, prediv_value);
84d3ff9728SAbel Vesa 
85d3ff9728SAbel Vesa 	return DIV_ROUND_UP(rate, div_value);
86d3ff9728SAbel Vesa 
87d3ff9728SAbel Vesa }
88d3ff9728SAbel Vesa 
89d3ff9728SAbel Vesa static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw,
90d3ff9728SAbel Vesa 					unsigned long rate,
91d3ff9728SAbel Vesa 					unsigned long parent_rate)
92d3ff9728SAbel Vesa {
93d3ff9728SAbel Vesa 	struct clk_divider *divider = to_clk_divider(hw);
94d3ff9728SAbel Vesa 	unsigned long flags = 0;
95d3ff9728SAbel Vesa 	int prediv_value;
96d3ff9728SAbel Vesa 	int div_value;
9733e7a842SColin Ian King 	int ret;
98d3ff9728SAbel Vesa 	u32 val;
99d3ff9728SAbel Vesa 
100d3ff9728SAbel Vesa 	ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
101d3ff9728SAbel Vesa 						&prediv_value, &div_value);
102d3ff9728SAbel Vesa 	if (ret)
103d3ff9728SAbel Vesa 		return -EINVAL;
104d3ff9728SAbel Vesa 
105d3ff9728SAbel Vesa 	spin_lock_irqsave(divider->lock, flags);
106d3ff9728SAbel Vesa 
107d3ff9728SAbel Vesa 	val = readl(divider->reg);
108d3ff9728SAbel Vesa 	val &= ~((clk_div_mask(divider->width) << divider->shift) |
109d3ff9728SAbel Vesa 			(clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
110d3ff9728SAbel Vesa 
111d3ff9728SAbel Vesa 	val |= (u32)(prediv_value  - 1) << divider->shift;
112d3ff9728SAbel Vesa 	val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
113d3ff9728SAbel Vesa 	writel(val, divider->reg);
114d3ff9728SAbel Vesa 
115d3ff9728SAbel Vesa 	spin_unlock_irqrestore(divider->lock, flags);
116d3ff9728SAbel Vesa 
117d3ff9728SAbel Vesa 	return ret;
118d3ff9728SAbel Vesa }
119d3ff9728SAbel Vesa 
120d3ff9728SAbel Vesa static const struct clk_ops imx8m_clk_composite_divider_ops = {
121d3ff9728SAbel Vesa 	.recalc_rate = imx8m_clk_composite_divider_recalc_rate,
122d3ff9728SAbel Vesa 	.round_rate = imx8m_clk_composite_divider_round_rate,
123d3ff9728SAbel Vesa 	.set_rate = imx8m_clk_composite_divider_set_rate,
124d3ff9728SAbel Vesa };
125d3ff9728SAbel Vesa 
126a4b431f8SPeng Fan struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
12765a6b7c5SAbel Vesa 					const char * const *parent_names,
128d3ff9728SAbel Vesa 					int num_parents, void __iomem *reg,
129d3ff9728SAbel Vesa 					unsigned long flags)
130d3ff9728SAbel Vesa {
131d3ff9728SAbel Vesa 	struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
132d3ff9728SAbel Vesa 	struct clk_hw *div_hw, *gate_hw;
133d3ff9728SAbel Vesa 	struct clk_divider *div = NULL;
134d3ff9728SAbel Vesa 	struct clk_gate *gate = NULL;
135d3ff9728SAbel Vesa 	struct clk_mux *mux = NULL;
136d3ff9728SAbel Vesa 
137d3ff9728SAbel Vesa 	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
138d3ff9728SAbel Vesa 	if (!mux)
139d3ff9728SAbel Vesa 		goto fail;
140d3ff9728SAbel Vesa 
141d3ff9728SAbel Vesa 	mux_hw = &mux->hw;
142d3ff9728SAbel Vesa 	mux->reg = reg;
143d3ff9728SAbel Vesa 	mux->shift = PCG_PCS_SHIFT;
144d3ff9728SAbel Vesa 	mux->mask = PCG_PCS_MASK;
145d3ff9728SAbel Vesa 
146d3ff9728SAbel Vesa 	div = kzalloc(sizeof(*div), GFP_KERNEL);
147d3ff9728SAbel Vesa 	if (!div)
148d3ff9728SAbel Vesa 		goto fail;
149d3ff9728SAbel Vesa 
150d3ff9728SAbel Vesa 	div_hw = &div->hw;
151d3ff9728SAbel Vesa 	div->reg = reg;
152d3ff9728SAbel Vesa 	div->shift = PCG_PREDIV_SHIFT;
153d3ff9728SAbel Vesa 	div->width = PCG_PREDIV_WIDTH;
154d3ff9728SAbel Vesa 	div->lock = &imx_ccm_lock;
155d3ff9728SAbel Vesa 	div->flags = CLK_DIVIDER_ROUND_CLOSEST;
156d3ff9728SAbel Vesa 
157d3ff9728SAbel Vesa 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
158d3ff9728SAbel Vesa 	if (!gate)
159d3ff9728SAbel Vesa 		goto fail;
160d3ff9728SAbel Vesa 
161d3ff9728SAbel Vesa 	gate_hw = &gate->hw;
162d3ff9728SAbel Vesa 	gate->reg = reg;
163d3ff9728SAbel Vesa 	gate->bit_idx = PCG_CGC_SHIFT;
164d3ff9728SAbel Vesa 
165d3ff9728SAbel Vesa 	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
166d3ff9728SAbel Vesa 			mux_hw, &clk_mux_ops, div_hw,
167d3ff9728SAbel Vesa 			&imx8m_clk_composite_divider_ops,
168d3ff9728SAbel Vesa 			gate_hw, &clk_gate_ops, flags);
169d3ff9728SAbel Vesa 	if (IS_ERR(hw))
170d3ff9728SAbel Vesa 		goto fail;
171d3ff9728SAbel Vesa 
172a4b431f8SPeng Fan 	return hw;
173d3ff9728SAbel Vesa 
174d3ff9728SAbel Vesa fail:
175d3ff9728SAbel Vesa 	kfree(gate);
176d3ff9728SAbel Vesa 	kfree(div);
177d3ff9728SAbel Vesa 	kfree(mux);
178d3ff9728SAbel Vesa 	return ERR_CAST(hw);
179d3ff9728SAbel Vesa }
180