Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  4 */
  5
  6#include <linux/clk-provider.h>
  7#include <linux/clkdev.h>
  8#include <linux/clk/at91_pmc.h>
  9#include <linux/of.h>
 10#include <linux/mfd/syscon.h>
 11#include <linux/regmap.h>
 12#include <soc/at91/atmel-sfr.h>
 13
 14#include "pmc.h"
 15
 16/*
 17 * The purpose of this clock is to generate a 480 MHz signal. A different
 18 * rate can't be configured.
 19 */
 20#define UTMI_RATE	480000000
 21
 22struct clk_utmi {
 23	struct clk_hw hw;
 24	struct regmap *regmap_pmc;
 25	struct regmap *regmap_sfr;
 26};
 27
 28#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
 29
 30static inline bool clk_utmi_ready(struct regmap *regmap)
 31{
 32	unsigned int status;
 33
 34	regmap_read(regmap, AT91_PMC_SR, &status);
 35
 36	return status & AT91_PMC_LOCKU;
 37}
 38
 39static int clk_utmi_prepare(struct clk_hw *hw)
 40{
 41	struct clk_hw *hw_parent;
 42	struct clk_utmi *utmi = to_clk_utmi(hw);
 43	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
 44			    AT91_PMC_BIASEN;
 45	unsigned int utmi_ref_clk_freq;
 46	unsigned long parent_rate;
 47
 48	/*
 49	 * If mainck rate is different from 12 MHz, we have to configure the
 50	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
 51	 * the utmi clock.
 52	 */
 53	hw_parent = clk_hw_get_parent(hw);
 54	parent_rate = clk_hw_get_rate(hw_parent);
 55
 56	switch (parent_rate) {
 57	case 12000000:
 58		utmi_ref_clk_freq = 0;
 59		break;
 60	case 16000000:
 61		utmi_ref_clk_freq = 1;
 62		break;
 63	case 24000000:
 64		utmi_ref_clk_freq = 2;
 65		break;
 66	/*
 67	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
 68	 * maximum value is 24 MHz.
 69	 */
 70	case 48000000:
 71		utmi_ref_clk_freq = 3;
 72		break;
 73	default:
 74		pr_err("UTMICK: unsupported mainck rate\n");
 75		return -EINVAL;
 76	}
 77
 78	if (utmi->regmap_sfr) {
 79		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
 80				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
 81	} else if (utmi_ref_clk_freq) {
 82		pr_err("UTMICK: sfr node required\n");
 83		return -EINVAL;
 84	}
 85
 86	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
 87
 88	while (!clk_utmi_ready(utmi->regmap_pmc))
 89		cpu_relax();
 90
 91	return 0;
 92}
 93
 94static int clk_utmi_is_prepared(struct clk_hw *hw)
 95{
 96	struct clk_utmi *utmi = to_clk_utmi(hw);
 97
 98	return clk_utmi_ready(utmi->regmap_pmc);
 99}
100
101static void clk_utmi_unprepare(struct clk_hw *hw)
102{
103	struct clk_utmi *utmi = to_clk_utmi(hw);
104
105	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
106			   AT91_PMC_UPLLEN, 0);
107}
108
109static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
110					  unsigned long parent_rate)
111{
112	/* UTMI clk rate is fixed. */
113	return UTMI_RATE;
114}
115
116static const struct clk_ops utmi_ops = {
117	.prepare = clk_utmi_prepare,
118	.unprepare = clk_utmi_unprepare,
119	.is_prepared = clk_utmi_is_prepared,
120	.recalc_rate = clk_utmi_recalc_rate,
121};
122
123static struct clk_hw * __init
124at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
125				struct regmap *regmap_sfr,
126				const char *name, const char *parent_name,
127				const struct clk_ops *ops, unsigned long flags)
128{
129	struct clk_utmi *utmi;
130	struct clk_hw *hw;
131	struct clk_init_data init;
132	int ret;
133
134	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
135	if (!utmi)
136		return ERR_PTR(-ENOMEM);
137
138	init.name = name;
139	init.ops = ops;
140	init.parent_names = parent_name ? &parent_name : NULL;
141	init.num_parents = parent_name ? 1 : 0;
142	init.flags = flags;
143
144	utmi->hw.init = &init;
145	utmi->regmap_pmc = regmap_pmc;
146	utmi->regmap_sfr = regmap_sfr;
147
148	hw = &utmi->hw;
149	ret = clk_hw_register(NULL, &utmi->hw);
150	if (ret) {
151		kfree(utmi);
152		hw = ERR_PTR(ret);
153	}
154
155	return hw;
156}
157
158struct clk_hw * __init
159at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
160		       const char *name, const char *parent_name)
161{
162	return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
163			parent_name, &utmi_ops, CLK_SET_RATE_GATE);
164}
165
166static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
167{
168	struct clk_utmi *utmi = to_clk_utmi(hw);
169	struct clk_hw *hw_parent;
170	unsigned long parent_rate;
171	unsigned int val;
172
173	hw_parent = clk_hw_get_parent(hw);
174	parent_rate = clk_hw_get_rate(hw_parent);
175
176	switch (parent_rate) {
177	case 16000000:
178		val = 0;
179		break;
180	case 20000000:
181		val = 2;
182		break;
183	case 24000000:
184		val = 3;
185		break;
186	case 32000000:
187		val = 5;
188		break;
189	default:
190		pr_err("UTMICK: unsupported main_xtal rate\n");
191		return -EINVAL;
192	}
193
194	regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
195
196	return 0;
197
198}
199
200static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
201{
202	struct clk_utmi *utmi = to_clk_utmi(hw);
203	struct clk_hw *hw_parent;
204	unsigned long parent_rate;
205	unsigned int val;
206
207	hw_parent = clk_hw_get_parent(hw);
208	parent_rate = clk_hw_get_rate(hw_parent);
209
210	regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
211	switch (val & 0x7) {
212	case 0:
213		if (parent_rate == 16000000)
214			return 1;
215		break;
216	case 2:
217		if (parent_rate == 20000000)
218			return 1;
219		break;
220	case 3:
221		if (parent_rate == 24000000)
222			return 1;
223		break;
224	case 5:
225		if (parent_rate == 32000000)
226			return 1;
227		break;
228	default:
229		break;
230	}
231
232	return 0;
233}
234
235static const struct clk_ops sama7g5_utmi_ops = {
236	.prepare = clk_utmi_sama7g5_prepare,
237	.is_prepared = clk_utmi_sama7g5_is_prepared,
238	.recalc_rate = clk_utmi_recalc_rate,
239};
240
241struct clk_hw * __init
242at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
243			       const char *parent_name)
244{
245	return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
246			parent_name, &sama7g5_utmi_ops, 0);
247}