1 // 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 
24 struct 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 
clk_gck_enable(struct clk * clk)37 static 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 
clk_gck_disable(struct clk * clk)50 static 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 
clk_gck_set_parent(struct clk * clk,struct clk * parent)63 static 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 
clk_gck_set_rate(struct clk * clk,ulong rate)88 static 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 
clk_gck_get_rate(struct clk * clk)114 static 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 
133 static 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 
141 struct clk *
at91_clk_register_generic(void __iomem * base,const struct clk_pcr_layout * layout,const char * name,const char * const * parent_names,const u32 * clk_mux_table,const u32 * mux_table,u8 num_parents,u8 id,const struct clk_range * range)142 at91_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 
197 U_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 };
203