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