1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * UTMI 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-utmi.c from Linux.
10 */
11#include <asm/processor.h>
12#include <common.h>
13#include <clk-uclass.h>
14#include <dm.h>
15#include <linux/clk-provider.h>
16#include <linux/clk/at91_pmc.h>
17#include <mach/at91_sfr.h>
18#include <regmap.h>
19#include <syscon.h>
20
21#include "pmc.h"
22
23#define UBOOT_DM_CLK_AT91_UTMI			"at91-utmi-clk"
24#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI		"at91-sama7g5-utmi-clk"
25
26/*
27 * The purpose of this clock is to generate a 480 MHz signal. A different
28 * rate can't be configured.
29 */
30#define UTMI_RATE	480000000
31
32struct clk_utmi {
33	void __iomem *base;
34	struct regmap *regmap_sfr;
35	struct clk clk;
36};
37
38#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk)
39
40static inline bool clk_utmi_ready(struct regmap *regmap)
41{
42	unsigned int status;
43
44	pmc_read(regmap, AT91_PMC_SR, &status);
45
46	return !!(status & AT91_PMC_LOCKU);
47}
48
49static int clk_utmi_enable(struct clk *clk)
50{
51	struct clk_utmi *utmi = to_clk_utmi(clk);
52	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
53			    AT91_PMC_BIASEN;
54	unsigned int utmi_ref_clk_freq;
55	ulong parent_rate = clk_get_parent_rate(clk);
56
57	/*
58	 * If mainck rate is different from 12 MHz, we have to configure the
59	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
60	 * the utmi clock.
61	 */
62	switch (parent_rate) {
63	case 12000000:
64		utmi_ref_clk_freq = 0;
65		break;
66	case 16000000:
67		utmi_ref_clk_freq = 1;
68		break;
69	case 24000000:
70		utmi_ref_clk_freq = 2;
71		break;
72	/*
73	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
74	 * maximum value is 24 MHz.
75	 */
76	case 48000000:
77		utmi_ref_clk_freq = 3;
78		break;
79	default:
80		debug("UTMICK: unsupported mainck rate\n");
81		return -EINVAL;
82	}
83
84	if (utmi->regmap_sfr) {
85		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
86				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
87	} else if (utmi_ref_clk_freq) {
88		debug("UTMICK: sfr node required\n");
89		return -EINVAL;
90	}
91
92	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr);
93
94	while (!clk_utmi_ready(utmi->base)) {
95		debug("waiting for utmi...\n");
96		cpu_relax();
97	}
98
99	return 0;
100}
101
102static int clk_utmi_disable(struct clk *clk)
103{
104	struct clk_utmi *utmi = to_clk_utmi(clk);
105
106	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
107
108	return 0;
109}
110
111static ulong clk_utmi_get_rate(struct clk *clk)
112{
113	/* UTMI clk rate is fixed. */
114	return UTMI_RATE;
115}
116
117static const struct clk_ops utmi_ops = {
118	.enable = clk_utmi_enable,
119	.disable = clk_utmi_disable,
120	.get_rate = clk_utmi_get_rate,
121};
122
123struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
124				   const char *name, const char *parent_name)
125{
126	struct udevice *syscon;
127	struct clk_utmi *utmi;
128	struct clk *clk;
129	int ret;
130
131	if (!base || !dev || !name || !parent_name)
132		return ERR_PTR(-EINVAL);
133
134	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
135					   "regmap-sfr", &syscon);
136	if (ret)
137		return ERR_PTR(ret);
138
139	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
140	if (!utmi)
141		return ERR_PTR(-ENOMEM);
142
143	utmi->base = base;
144	utmi->regmap_sfr = syscon_get_regmap(syscon);
145	if (!utmi->regmap_sfr) {
146		kfree(utmi);
147		return ERR_PTR(-ENODEV);
148	}
149
150	clk = &utmi->clk;
151	clk->flags = CLK_GET_RATE_NOCACHE;
152	ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name);
153	if (ret) {
154		kfree(utmi);
155		clk = ERR_PTR(ret);
156	}
157
158	return clk;
159}
160
161U_BOOT_DRIVER(at91_utmi_clk) = {
162	.name = UBOOT_DM_CLK_AT91_UTMI,
163	.id = UCLASS_CLK,
164	.ops = &utmi_ops,
165	.flags = DM_FLAG_PRE_RELOC,
166};
167
168static int clk_utmi_sama7g5_enable(struct clk *clk)
169{
170	struct clk_utmi *utmi = to_clk_utmi(clk);
171	ulong parent_rate = clk_get_parent_rate(clk);
172	unsigned int val;
173
174	switch (parent_rate) {
175	case 16000000:
176		val = 0;
177		break;
178	case 20000000:
179		val = 2;
180		break;
181	case 24000000:
182		val = 3;
183		break;
184	case 32000000:
185		val = 5;
186		break;
187	default:
188		debug("UTMICK: unsupported main_xtal rate\n");
189		return -EINVAL;
190	}
191
192	pmc_write(utmi->base, AT91_PMC_XTALF, val);
193
194	return 0;
195}
196
197static const struct clk_ops sama7g5_utmi_ops = {
198	.enable = clk_utmi_sama7g5_enable,
199	.get_rate = clk_utmi_get_rate,
200};
201
202struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base,
203		const char *name, const char *parent_name)
204{
205	struct clk_utmi *utmi;
206	struct clk *clk;
207	int ret;
208
209	if (!base || !name || !parent_name)
210		return ERR_PTR(-EINVAL);
211
212	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
213	if (!utmi)
214		return ERR_PTR(-ENOMEM);
215
216	utmi->base = base;
217
218	clk = &utmi->clk;
219	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name,
220			   parent_name);
221	if (ret) {
222		kfree(utmi);
223		clk = ERR_PTR(ret);
224	}
225
226	return clk;
227}
228
229U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = {
230	.name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI,
231	.id = UCLASS_CLK,
232	.ops = &sama7g5_utmi_ops,
233	.flags = DM_FLAG_PRE_RELOC,
234};
235