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