blob: 87738b7b5bffba9b3a9201d2a6476d1d3a046514 [file] [log] [blame]
Claudiu Beznea36a96302020-09-07 17:46:50 +03001// 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
24struct 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
37static 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
50static 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
63static 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
88static 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
114static 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
133static 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
141struct clk *
142at91_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
197U_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};