blob: 868de4b1774b3b97b24cb486cacc9c1e9dbe0bb1 [file] [log] [blame]
Claudiu Beznea2a1a5792020-09-07 17:46:47 +03001// 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
27struct 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
39static 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
56static 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
88static 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
124static 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
130struct 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
179U_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
186const 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
194const 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
202const 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};