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
24struct 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
37static 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
50static 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
63static 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
88static 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
114static 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
133static 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
141struct clk *
142at91_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
197U_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