blob: bb5e49e11425c95f7e957cfb53fcc652acd3f63c [file] [log] [blame]
Dario Binacchida3b0202020-12-30 00:06:32 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI multiplexer clock support
4 *
5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
6 *
7 * Based on Linux kernel drivers/clk/ti/mux.c
8 */
9
10#include <common.h>
11#include <dm.h>
12#include <dm/device_compat.h>
13#include <clk-uclass.h>
14#include <asm/io.h>
15#include <linux/clk-provider.h>
Dario Binacchid2841572020-12-30 00:06:35 +010016#include "clk.h"
Dario Binacchida3b0202020-12-30 00:06:32 +010017
18struct clk_ti_mux_priv {
19 struct clk_bulk parents;
20 fdt_addr_t reg;
21 u32 flags;
22 u32 mux_flags;
23 u32 mask;
24 u32 shift;
25 s32 latch;
26};
27
Dario Binacchida3b0202020-12-30 00:06:32 +010028static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents,
29 int index)
30{
31 if (index < 0 || !parents)
32 return ERR_PTR(-EINVAL);
33
34 if (index >= parents->count)
35 return ERR_PTR(-ENODEV);
36
37 return &parents->clks[index];
38}
39
40static int clk_ti_mux_get_parent_index(struct clk_bulk *parents,
41 struct clk *parent)
42{
43 int i;
44
45 if (!parents || !parent)
46 return -EINVAL;
47
48 for (i = 0; i < parents->count; i++) {
49 if (parents->clks[i].dev == parent->dev)
50 return i;
51 }
52
53 return -ENODEV;
54}
55
56static int clk_ti_mux_get_index(struct clk *clk)
57{
58 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
59 u32 val;
60
61 val = readl(priv->reg);
62 val >>= priv->shift;
63 val &= priv->mask;
64
65 if (val && (priv->flags & CLK_MUX_INDEX_BIT))
66 val = ffs(val) - 1;
67
68 if (val && (priv->flags & CLK_MUX_INDEX_ONE))
69 val--;
70
71 if (val >= priv->parents.count)
72 return -EINVAL;
73
74 return val;
75}
76
77static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent)
78{
79 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
80 int index;
81 u32 val;
82
83 index = clk_ti_mux_get_parent_index(&priv->parents, parent);
84 if (index < 0) {
85 dev_err(clk->dev, "failed to get parent clock\n");
86 return index;
87 }
88
89 index = clk_mux_index_to_val(NULL, priv->flags, index);
90
91 if (priv->flags & CLK_MUX_HIWORD_MASK) {
92 val = priv->mask << (priv->shift + 16);
93 } else {
94 val = readl(priv->reg);
95 val &= ~(priv->mask << priv->shift);
96 }
97
98 val |= index << priv->shift;
99 writel(val, priv->reg);
Dario Binacchid2841572020-12-30 00:06:35 +0100100 clk_ti_latch(priv->reg, priv->latch);
Dario Binacchida3b0202020-12-30 00:06:32 +0100101 return 0;
102}
103
104static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate)
105{
106 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
107 struct clk *parent;
108 int index;
109
110 if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
111 return -ENOSYS;
112
113 index = clk_ti_mux_get_index(clk);
114 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
115 if (IS_ERR(parent))
116 return PTR_ERR(parent);
117
118 rate = clk_set_rate(parent, rate);
119 dev_dbg(clk->dev, "rate=%ld\n", rate);
120 return rate;
121}
122
123static ulong clk_ti_mux_get_rate(struct clk *clk)
124{
125 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
126 int index;
127 struct clk *parent;
128 ulong rate;
129
130 index = clk_ti_mux_get_index(clk);
131 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
132 if (IS_ERR(parent))
133 return PTR_ERR(parent);
134
135 rate = clk_get_rate(parent);
136 dev_dbg(clk->dev, "rate=%ld\n", rate);
137 return rate;
138}
139
140static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate)
141{
142 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
143 struct clk *parent;
144 int index;
145
146 if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
147 return -ENOSYS;
148
149 index = clk_ti_mux_get_index(clk);
150 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
151 if (IS_ERR(parent))
152 return PTR_ERR(parent);
153
154 rate = clk_round_rate(parent, rate);
155 dev_dbg(clk->dev, "rate=%ld\n", rate);
156 return rate;
157}
158
159static int clk_ti_mux_request(struct clk *clk)
160{
161 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
162 struct clk *parent;
163 int index;
164
165 clk->flags = priv->flags;
166
167 index = clk_ti_mux_get_index(clk);
168 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
169 if (IS_ERR(parent))
170 return PTR_ERR(parent);
171
172 return clk_ti_mux_set_parent(clk, parent);
173}
174
175static struct clk_ops clk_ti_mux_ops = {
176 .request = clk_ti_mux_request,
177 .round_rate = clk_ti_mux_round_rate,
178 .get_rate = clk_ti_mux_get_rate,
179 .set_rate = clk_ti_mux_set_rate,
180 .set_parent = clk_ti_mux_set_parent,
181};
182
183static int clk_ti_mux_remove(struct udevice *dev)
184{
185 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
186 int err;
187
188 err = clk_release_all(priv->parents.clks, priv->parents.count);
189 if (err)
190 dev_dbg(dev, "could not release all parents' clocks\n");
191
192 return err;
193}
194
195static int clk_ti_mux_probe(struct udevice *dev)
196{
197 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
198 int err;
199
200 err = clk_get_bulk(dev, &priv->parents);
201 if (err || priv->parents.count < 2) {
202 dev_err(dev, "mux-clock must have parents\n");
203 return err ? err : -EFAULT;
204 }
205
206 /* Generate bit-mask based on parents info */
207 priv->mask = priv->parents.count;
208 if (!(priv->mux_flags & CLK_MUX_INDEX_ONE))
209 priv->mask--;
210
211 priv->mask = (1 << fls(priv->mask)) - 1;
212 return 0;
213}
214
215static int clk_ti_mux_of_to_plat(struct udevice *dev)
216{
217 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
218
219 priv->reg = dev_read_addr(dev);
220 if (priv->reg == FDT_ADDR_T_NONE) {
221 dev_err(dev, "failed to get register\n");
222 return -EINVAL;
223 }
224
225 dev_dbg(dev, "reg=0x%08lx\n", priv->reg);
226 priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0);
227 priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL);
228
229 priv->flags = CLK_SET_RATE_NO_REPARENT;
230 if (dev_read_bool(dev, "ti,set-rate-parent"))
231 priv->flags |= CLK_SET_RATE_PARENT;
232
233 if (dev_read_bool(dev, "ti,index-starts-at-one"))
234 priv->mux_flags |= CLK_MUX_INDEX_ONE;
235
236 return 0;
237}
238
239static const struct udevice_id clk_ti_mux_of_match[] = {
240 {.compatible = "ti,mux-clock"},
241 {},
242};
243
244U_BOOT_DRIVER(clk_ti_mux) = {
245 .name = "ti_mux_clock",
246 .id = UCLASS_CLK,
247 .of_match = clk_ti_mux_of_match,
Dario Binacchif686e4d2021-01-15 09:10:26 +0100248 .of_to_plat = clk_ti_mux_of_to_plat,
Dario Binacchida3b0202020-12-30 00:06:32 +0100249 .probe = clk_ti_mux_probe,
250 .remove = clk_ti_mux_remove,
251 .priv_auto = sizeof(struct clk_ti_mux_priv),
252 .ops = &clk_ti_mux_ops,
253};