1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Programmable 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-programmable.c from Linux.
10 */
11 #include <common.h>
12 #include <clk-uclass.h>
13 #include <dm.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_PROG "at91-prog-clk"
20
21 #define PROG_ID_MAX 7
22
23 #define PROG_STATUS_MASK(id) (1 << ((id) + 8))
24 #define PROG_PRES(_l, _p) (((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
25 #define PROG_MAX_RM9200_CSS 3
26
27 struct clk_programmable {
28 void __iomem *base;
29 const u32 *clk_mux_table;
30 const u32 *mux_table;
31 const struct clk_programmable_layout *layout;
32 u32 num_parents;
33 struct clk clk;
34 u8 id;
35 };
36
37 #define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
38
clk_programmable_get_rate(struct clk * clk)39 static ulong clk_programmable_get_rate(struct clk *clk)
40 {
41 struct clk_programmable *prog = to_clk_programmable(clk);
42 const struct clk_programmable_layout *layout = prog->layout;
43 ulong rate, parent_rate = clk_get_parent_rate(clk);
44 unsigned int pckr;
45
46 pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
47
48 if (layout->is_pres_direct)
49 rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
50 else
51 rate = parent_rate >> PROG_PRES(layout, pckr);
52
53 return rate;
54 }
55
clk_programmable_set_parent(struct clk * clk,struct clk * parent)56 static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
57 {
58 struct clk_programmable *prog = to_clk_programmable(clk);
59 const struct clk_programmable_layout *layout = prog->layout;
60 unsigned int mask = layout->css_mask;
61 int index;
62
63 index = at91_clk_mux_val_to_index(prog->clk_mux_table,
64 prog->num_parents, parent->id);
65 if (index < 0)
66 return index;
67
68 index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
69 index);
70 if (index < 0)
71 return index;
72
73 if (layout->have_slck_mck)
74 mask |= AT91_PMC_CSSMCK_MCK;
75
76 if (index > layout->css_mask) {
77 if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
78 return -EINVAL;
79
80 index |= AT91_PMC_CSSMCK_MCK;
81 }
82
83 pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
84
85 return 0;
86 }
87
clk_programmable_set_rate(struct clk * clk,ulong rate)88 static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
89 {
90 struct clk_programmable *prog = to_clk_programmable(clk);
91 const struct clk_programmable_layout *layout = prog->layout;
92 ulong parent_rate = clk_get_parent_rate(clk);
93 ulong div = parent_rate / rate;
94 int shift = 0;
95
96 if (!parent_rate || !div)
97 return -EINVAL;
98
99 if (layout->is_pres_direct) {
100 shift = div - 1;
101
102 if (shift > layout->pres_mask)
103 return -EINVAL;
104 } else {
105 shift = fls(div) - 1;
106
107 if (div != (1 << shift))
108 return -EINVAL;
109
110 if (shift >= layout->pres_mask)
111 return -EINVAL;
112 }
113
114 pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
115 layout->pres_mask << layout->pres_shift,
116 shift << layout->pres_shift);
117
118 if (layout->is_pres_direct)
119 return (parent_rate / shift + 1);
120
121 return parent_rate >> shift;
122 }
123
124 static const struct clk_ops programmable_ops = {
125 .get_rate = clk_programmable_get_rate,
126 .set_parent = clk_programmable_set_parent,
127 .set_rate = clk_programmable_set_rate,
128 };
129
at91_clk_register_programmable(void __iomem * base,const char * name,const char * const * parent_names,u8 num_parents,u8 id,const struct clk_programmable_layout * layout,const u32 * clk_mux_table,const u32 * mux_table)130 struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
131 const char *const *parent_names, u8 num_parents, u8 id,
132 const struct clk_programmable_layout *layout,
133 const u32 *clk_mux_table, const u32 *mux_table)
134 {
135 struct clk_programmable *prog;
136 struct clk *clk;
137 u32 val, tmp;
138 int ret;
139
140 if (!base || !name || !parent_names || !num_parents ||
141 !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
142 return ERR_PTR(-EINVAL);
143
144 prog = kzalloc(sizeof(*prog), GFP_KERNEL);
145 if (!prog)
146 return ERR_PTR(-ENOMEM);
147
148 prog->id = id;
149 prog->layout = layout;
150 prog->base = base;
151 prog->clk_mux_table = clk_mux_table;
152 prog->mux_table = mux_table;
153 prog->num_parents = num_parents;
154
155 pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
156 val = tmp & prog->layout->css_mask;
157 if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
158 ret = PROG_MAX_RM9200_CSS + 1;
159 else
160 ret = at91_clk_mux_val_to_index(prog->mux_table,
161 prog->num_parents, val);
162 if (ret < 0) {
163 kfree(prog);
164 return ERR_PTR(ret);
165 }
166
167 clk = &prog->clk;
168 clk->flags = CLK_GET_RATE_NOCACHE;
169 ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
170 parent_names[ret]);
171 if (ret) {
172 kfree(prog);
173 clk = ERR_PTR(ret);
174 }
175
176 return clk;
177 }
178
179 U_BOOT_DRIVER(at91_prog_clk) = {
180 .name = UBOOT_DM_CLK_AT91_PROG,
181 .id = UCLASS_CLK,
182 .ops = &programmable_ops,
183 .flags = DM_FLAG_PRE_RELOC,
184 };
185
186 const struct clk_programmable_layout at91rm9200_programmable_layout = {
187 .pres_mask = 0x7,
188 .pres_shift = 2,
189 .css_mask = 0x3,
190 .have_slck_mck = 0,
191 .is_pres_direct = 0,
192 };
193
194 const struct clk_programmable_layout at91sam9g45_programmable_layout = {
195 .pres_mask = 0x7,
196 .pres_shift = 2,
197 .css_mask = 0x3,
198 .have_slck_mck = 1,
199 .is_pres_direct = 0,
200 };
201
202 const struct clk_programmable_layout at91sam9x5_programmable_layout = {
203 .pres_mask = 0x7,
204 .pres_shift = 4,
205 .css_mask = 0x7,
206 .have_slck_mck = 0,
207 .is_pres_direct = 0,
208 };
209