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