blob: 2ad682b8fe2c419f9af2e41d11a257f49ed100e0 [file] [log] [blame]
Lukasz Majewski4de44bb2019-06-24 15:50:45 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2019 DENX Software Engineering
4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
5 *
6 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
7 * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
8 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
9 *
10 */
11
Patrick Delaunay8767e792021-11-19 15:12:07 +010012#define LOG_CATEGORY UCLASS_CLK
13
Tom Riniabb9a042024-05-18 20:20:43 -060014#include <common.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020015#include <asm/io.h>
16#include <malloc.h>
17#include <clk-uclass.h>
Patrick Delaunay8767e792021-11-19 15:12:07 +010018#include <log.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020019#include <dm/device.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070020#include <dm/devres.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020021#include <dm/uclass.h>
22#include <dm/lists.h>
Patrick Delaunay8767e792021-11-19 15:12:07 +010023#include <dm/device_compat.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020024#include <dm/device-internal.h>
Simon Glassc06c1be2020-05-10 11:40:08 -060025#include <linux/bug.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020026#include <linux/clk-provider.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070027#include <linux/err.h>
Peng Fanfd67c632019-07-31 07:01:37 +000028#include <linux/log2.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020029#include <div64.h>
30#include <clk.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060031#include <linux/printk.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020032#include "clk.h"
33
34#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
35
Dario Binacchi39fc7a62020-12-30 00:06:27 +010036unsigned int clk_divider_get_table_div(const struct clk_div_table *table,
37 unsigned int val)
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020038{
39 const struct clk_div_table *clkt;
40
41 for (clkt = table; clkt->div; clkt++)
42 if (clkt->val == val)
43 return clkt->div;
44 return 0;
45}
46
47static unsigned int _get_div(const struct clk_div_table *table,
48 unsigned int val, unsigned long flags, u8 width)
49{
50 if (flags & CLK_DIVIDER_ONE_BASED)
51 return val;
52 if (flags & CLK_DIVIDER_POWER_OF_TWO)
53 return 1 << val;
54 if (flags & CLK_DIVIDER_MAX_AT_ZERO)
55 return val ? val : clk_div_mask(width) + 1;
56 if (table)
Dario Binacchi39fc7a62020-12-30 00:06:27 +010057 return clk_divider_get_table_div(table, val);
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020058 return val + 1;
59}
60
61unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
62 unsigned int val,
63 const struct clk_div_table *table,
64 unsigned long flags, unsigned long width)
65{
66 unsigned int div;
67
68 div = _get_div(table, val, flags, width);
69 if (!div) {
70 WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
71 "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
72 clk_hw_get_name(hw));
73 return parent_rate;
74 }
75
76 return DIV_ROUND_UP_ULL((u64)parent_rate, div);
77}
78
79static ulong clk_divider_recalc_rate(struct clk *clk)
80{
Sean Andersoncfc2f022020-06-24 06:41:06 -040081 struct clk_divider *divider = to_clk_divider(clk);
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020082 unsigned long parent_rate = clk_get_parent_rate(clk);
83 unsigned int val;
84
Simon Glass0a6a0c42023-02-05 15:40:43 -070085#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
Lukasz Majewskibb18f1b2019-06-24 15:50:48 +020086 val = divider->io_divider_val;
87#else
88 val = readl(divider->reg);
89#endif
90 val >>= divider->shift;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020091 val &= clk_div_mask(divider->width);
92
93 return divider_recalc_rate(clk, parent_rate, val, divider->table,
94 divider->flags, divider->width);
95}
96
Dario Binacchi39fc7a62020-12-30 00:06:27 +010097bool clk_divider_is_valid_table_div(const struct clk_div_table *table,
98 unsigned int div)
Peng Fanfd67c632019-07-31 07:01:37 +000099{
100 const struct clk_div_table *clkt;
101
102 for (clkt = table; clkt->div; clkt++)
103 if (clkt->div == div)
104 return true;
105 return false;
106}
107
Dario Binacchi39fc7a62020-12-30 00:06:27 +0100108bool clk_divider_is_valid_div(const struct clk_div_table *table,
109 unsigned int div, unsigned long flags)
Peng Fanfd67c632019-07-31 07:01:37 +0000110{
111 if (flags & CLK_DIVIDER_POWER_OF_TWO)
112 return is_power_of_2(div);
113 if (table)
Dario Binacchi39fc7a62020-12-30 00:06:27 +0100114 return clk_divider_is_valid_table_div(table, div);
Peng Fanfd67c632019-07-31 07:01:37 +0000115 return true;
116}
117
Dario Binacchi39fc7a62020-12-30 00:06:27 +0100118unsigned int clk_divider_get_table_val(const struct clk_div_table *table,
119 unsigned int div)
Peng Fanfd67c632019-07-31 07:01:37 +0000120{
121 const struct clk_div_table *clkt;
122
123 for (clkt = table; clkt->div; clkt++)
124 if (clkt->div == div)
125 return clkt->val;
126 return 0;
127}
128
129static unsigned int _get_val(const struct clk_div_table *table,
130 unsigned int div, unsigned long flags, u8 width)
131{
132 if (flags & CLK_DIVIDER_ONE_BASED)
133 return div;
134 if (flags & CLK_DIVIDER_POWER_OF_TWO)
135 return __ffs(div);
136 if (flags & CLK_DIVIDER_MAX_AT_ZERO)
137 return (div == clk_div_mask(width) + 1) ? 0 : div;
138 if (table)
Dario Binacchi39fc7a62020-12-30 00:06:27 +0100139 return clk_divider_get_table_val(table, div);
Peng Fanfd67c632019-07-31 07:01:37 +0000140 return div - 1;
141}
142int divider_get_val(unsigned long rate, unsigned long parent_rate,
143 const struct clk_div_table *table, u8 width,
144 unsigned long flags)
145{
146 unsigned int div, value;
147
148 div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
149
Dario Binacchi39fc7a62020-12-30 00:06:27 +0100150 if (!clk_divider_is_valid_div(table, div, flags))
Peng Fanfd67c632019-07-31 07:01:37 +0000151 return -EINVAL;
152
153 value = _get_val(table, div, flags, width);
154
155 return min_t(unsigned int, value, clk_div_mask(width));
156}
157
158static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate)
159{
Sean Andersoncfc2f022020-06-24 06:41:06 -0400160 struct clk_divider *divider = to_clk_divider(clk);
Peng Fanfd67c632019-07-31 07:01:37 +0000161 unsigned long parent_rate = clk_get_parent_rate(clk);
162 int value;
163 u32 val;
164
165 value = divider_get_val(rate, parent_rate, divider->table,
166 divider->width, divider->flags);
167 if (value < 0)
168 return value;
169
170 if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
171 val = clk_div_mask(divider->width) << (divider->shift + 16);
172 } else {
173 val = readl(divider->reg);
174 val &= ~(clk_div_mask(divider->width) << divider->shift);
175 }
176 val |= (u32)value << divider->shift;
177 writel(val, divider->reg);
178
179 return clk_get_rate(clk);
180}
181
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200182const struct clk_ops clk_divider_ops = {
183 .get_rate = clk_divider_recalc_rate,
Peng Fanfd67c632019-07-31 07:01:37 +0000184 .set_rate = clk_divider_set_rate,
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200185};
186
187static struct clk *_register_divider(struct device *dev, const char *name,
188 const char *parent_name, unsigned long flags,
189 void __iomem *reg, u8 shift, u8 width,
190 u8 clk_divider_flags, const struct clk_div_table *table)
191{
192 struct clk_divider *div;
193 struct clk *clk;
194 int ret;
195
196 if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
197 if (width + shift > 16) {
Patrick Delaunay8767e792021-11-19 15:12:07 +0100198 dev_warn(dev, "divider value exceeds LOWORD field\n");
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200199 return ERR_PTR(-EINVAL);
200 }
201 }
202
203 /* allocate the divider */
204 div = kzalloc(sizeof(*div), GFP_KERNEL);
205 if (!div)
206 return ERR_PTR(-ENOMEM);
207
208 /* struct clk_divider assignments */
209 div->reg = reg;
210 div->shift = shift;
211 div->width = width;
212 div->flags = clk_divider_flags;
213 div->table = table;
Simon Glass0a6a0c42023-02-05 15:40:43 -0700214#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
Lukasz Majewskibb18f1b2019-06-24 15:50:48 +0200215 div->io_divider_val = *(u32 *)reg;
216#endif
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200217
218 /* register the clock */
219 clk = &div->clk;
Dario Binacchi1a62dc12020-04-13 14:36:27 +0200220 clk->flags = flags;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200221
222 ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
223 if (ret) {
224 kfree(div);
225 return ERR_PTR(ret);
226 }
227
228 return clk;
229}
230
231struct clk *clk_register_divider(struct device *dev, const char *name,
232 const char *parent_name, unsigned long flags,
233 void __iomem *reg, u8 shift, u8 width,
234 u8 clk_divider_flags)
235{
236 struct clk *clk;
237
238 clk = _register_divider(dev, name, parent_name, flags, reg, shift,
239 width, clk_divider_flags, NULL);
240 if (IS_ERR(clk))
241 return ERR_CAST(clk);
242 return clk;
243}
244
245U_BOOT_DRIVER(ccf_clk_divider) = {
246 .name = UBOOT_DM_CLK_CCF_DIVIDER,
247 .id = UCLASS_CLK,
248 .ops = &clk_divider_ops,
249 .flags = DM_FLAG_PRE_RELOC,
250};