1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Generic clock support for AT91 architectures.
4 *
5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
6 *
7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8 *
9 * Based on drivers/clk/at91/clk-generated.c from Linux.
10 */
11 #include <common.h>
12 #include <clk-uclass.h>
13 #include <dm.h>
14 #include <linux/io.h>
15 #include <linux/clk-provider.h>
16 #include <linux/clk/at91_pmc.h>
17
18 #include "pmc.h"
19
20 #define UBOOT_DM_CLK_AT91_GCK "at91-gck-clk"
21
22 #define GENERATED_MAX_DIV 255
23
24 struct clk_gck {
25 void __iomem *base;
26 const u32 *clk_mux_table;
27 const u32 *mux_table;
28 const struct clk_pcr_layout *layout;
29 struct clk_range range;
30 struct clk clk;
31 u32 num_parents;
32 u32 id;
33 };
34
35 #define to_clk_gck(_c) container_of(_c, struct clk_gck, clk)
36
clk_gck_enable(struct clk * clk)37 static int clk_gck_enable(struct clk *clk)
38 {
39 struct clk_gck *gck = to_clk_gck(clk);
40
41 pmc_write(gck->base, gck->layout->offset,
42 (gck->id & gck->layout->pid_mask));
43 pmc_update_bits(gck->base, gck->layout->offset,
44 gck->layout->cmd | AT91_PMC_PCR_GCKEN,
45 gck->layout->cmd | AT91_PMC_PCR_GCKEN);
46
47 return 0;
48 }
49
clk_gck_disable(struct clk * clk)50 static int clk_gck_disable(struct clk *clk)
51 {
52 struct clk_gck *gck = to_clk_gck(clk);
53
54 pmc_write(gck->base, gck->layout->offset,
55 (gck->id & gck->layout->pid_mask));
56 pmc_update_bits(gck->base, gck->layout->offset,
57 gck->layout->cmd | AT91_PMC_PCR_GCKEN,
58 gck->layout->cmd);
59
60 return 0;
61 }
62
clk_gck_set_parent(struct clk * clk,struct clk * parent)63 static int clk_gck_set_parent(struct clk *clk, struct clk *parent)
64 {
65 struct clk_gck *gck = to_clk_gck(clk);
66 int index;
67
68 index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents,
69 parent->id);
70 if (index < 0)
71 return index;
72
73 index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents,
74 index);
75 if (index < 0)
76 return index;
77
78 pmc_write(gck->base, gck->layout->offset,
79 (gck->id & gck->layout->pid_mask));
80 pmc_update_bits(gck->base, gck->layout->offset,
81 gck->layout->gckcss_mask | gck->layout->cmd,
82 (index << (ffs(gck->layout->gckcss_mask) - 1)) |
83 gck->layout->cmd);
84
85 return 0;
86 }
87
clk_gck_set_rate(struct clk * clk,ulong rate)88 static ulong clk_gck_set_rate(struct clk *clk, ulong rate)
89 {
90 struct clk_gck *gck = to_clk_gck(clk);
91 ulong parent_rate = clk_get_parent_rate(clk);
92 u32 div;
93
94 if (!rate || !parent_rate)
95 return 0;
96
97 if (gck->range.max && rate > gck->range.max)
98 return 0;
99
100 div = DIV_ROUND_CLOSEST(parent_rate, rate);
101 if (div > GENERATED_MAX_DIV + 1 || !div)
102 return 0;
103
104 pmc_write(gck->base, gck->layout->offset,
105 (gck->id & gck->layout->pid_mask));
106 pmc_update_bits(gck->base, gck->layout->offset,
107 AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd,
108 ((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) |
109 gck->layout->cmd);
110
111 return parent_rate / div;
112 }
113
clk_gck_get_rate(struct clk * clk)114 static ulong clk_gck_get_rate(struct clk *clk)
115 {
116 struct clk_gck *gck = to_clk_gck(clk);
117 ulong parent_rate = clk_get_parent_rate(clk);
118 u32 val, div;
119
120 if (!parent_rate)
121 return 0;
122
123 pmc_write(gck->base, gck->layout->offset,
124 (gck->id & gck->layout->pid_mask));
125 pmc_read(gck->base, gck->layout->offset, &val);
126
127 div = (val & AT91_PMC_PCR_GCKDIV_MASK) >>
128 (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1);
129
130 return parent_rate / (div + 1);
131 }
132
133 static const struct clk_ops gck_ops = {
134 .enable = clk_gck_enable,
135 .disable = clk_gck_disable,
136 .set_parent = clk_gck_set_parent,
137 .set_rate = clk_gck_set_rate,
138 .get_rate = clk_gck_get_rate,
139 };
140
141 struct clk *
at91_clk_register_generic(void __iomem * base,const struct clk_pcr_layout * layout,const char * name,const char * const * parent_names,const u32 * clk_mux_table,const u32 * mux_table,u8 num_parents,u8 id,const struct clk_range * range)142 at91_clk_register_generic(void __iomem *base,
143 const struct clk_pcr_layout *layout,
144 const char *name, const char * const *parent_names,
145 const u32 *clk_mux_table, const u32 *mux_table,
146 u8 num_parents, u8 id,
147 const struct clk_range *range)
148 {
149 struct clk_gck *gck;
150 struct clk *clk;
151 int ret, index;
152 u32 val;
153
154 if (!base || !layout || !name || !parent_names || !num_parents ||
155 !clk_mux_table || !mux_table || !range)
156 return ERR_PTR(-EINVAL);
157
158 gck = kzalloc(sizeof(*gck), GFP_KERNEL);
159 if (!gck)
160 return ERR_PTR(-ENOMEM);
161
162 gck->id = id;
163 gck->base = base;
164 gck->range = *range;
165 gck->layout = layout;
166 gck->clk_mux_table = clk_mux_table;
167 gck->mux_table = mux_table;
168 gck->num_parents = num_parents;
169
170 clk = &gck->clk;
171 clk->flags = CLK_GET_RATE_NOCACHE;
172
173 pmc_write(gck->base, gck->layout->offset,
174 (gck->id & gck->layout->pid_mask));
175 pmc_read(gck->base, gck->layout->offset, &val);
176
177 val = (val & gck->layout->gckcss_mask) >>
178 (ffs(gck->layout->gckcss_mask) - 1);
179
180 index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents,
181 val);
182 if (index < 0) {
183 kfree(gck);
184 return ERR_PTR(index);
185 }
186
187 ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name,
188 parent_names[index]);
189 if (ret) {
190 kfree(gck);
191 clk = ERR_PTR(ret);
192 }
193
194 return clk;
195 }
196
197 U_BOOT_DRIVER(at91_gck_clk) = {
198 .name = UBOOT_DM_CLK_AT91_GCK,
199 .id = UCLASS_CLK,
200 .ops = &gck_ops,
201 .flags = DM_FLAG_PRE_RELOC,
202 };
203