Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation; either version 2 of the License, or
  7 * (at your option) any later version.
  8 *
  9 */
 10
 11#include <linux/clk-provider.h>
 12#include <linux/clkdev.h>
 13#include <linux/clk/at91_pmc.h>
 14#include <linux/of.h>
 15#include <linux/mfd/syscon.h>
 16#include <linux/regmap.h>
 17
 18#include "pmc.h"
 19
 20#define MASTER_SOURCE_MAX	4
 21
 22#define MASTER_PRES_MASK	0x7
 23#define MASTER_PRES_MAX		MASTER_PRES_MASK
 24#define MASTER_DIV_SHIFT	8
 25#define MASTER_DIV_MASK		0x3
 26
 27struct clk_master_characteristics {
 28	struct clk_range output;
 29	u32 divisors[4];
 30	u8 have_div3_pres;
 31};
 32
 33struct clk_master_layout {
 34	u32 mask;
 35	u8 pres_shift;
 36};
 37
 38#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
 39
 40struct clk_master {
 41	struct clk_hw hw;
 42	struct regmap *regmap;
 43	const struct clk_master_layout *layout;
 44	const struct clk_master_characteristics *characteristics;
 45};
 46
 47static inline bool clk_master_ready(struct regmap *regmap)
 48{
 49	unsigned int status;
 50
 51	regmap_read(regmap, AT91_PMC_SR, &status);
 52
 53	return status & AT91_PMC_MCKRDY ? 1 : 0;
 54}
 55
 56static int clk_master_prepare(struct clk_hw *hw)
 57{
 58	struct clk_master *master = to_clk_master(hw);
 59
 60	while (!clk_master_ready(master->regmap))
 61		cpu_relax();
 62
 63	return 0;
 64}
 65
 66static int clk_master_is_prepared(struct clk_hw *hw)
 67{
 68	struct clk_master *master = to_clk_master(hw);
 69
 70	return clk_master_ready(master->regmap);
 71}
 72
 73static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
 74					    unsigned long parent_rate)
 75{
 76	u8 pres;
 77	u8 div;
 78	unsigned long rate = parent_rate;
 79	struct clk_master *master = to_clk_master(hw);
 80	const struct clk_master_layout *layout = master->layout;
 81	const struct clk_master_characteristics *characteristics =
 82						master->characteristics;
 83	unsigned int mckr;
 84
 85	regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
 86	mckr &= layout->mask;
 87
 88	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
 89	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
 90
 91	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
 92		rate /= 3;
 93	else
 94		rate >>= pres;
 95
 96	rate /= characteristics->divisors[div];
 97
 98	if (rate < characteristics->output.min)
 99		pr_warn("master clk is underclocked");
100	else if (rate > characteristics->output.max)
101		pr_warn("master clk is overclocked");
102
103	return rate;
104}
105
106static u8 clk_master_get_parent(struct clk_hw *hw)
107{
108	struct clk_master *master = to_clk_master(hw);
109	unsigned int mckr;
110
111	regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
112
113	return mckr & AT91_PMC_CSS;
114}
115
116static const struct clk_ops master_ops = {
117	.prepare = clk_master_prepare,
118	.is_prepared = clk_master_is_prepared,
119	.recalc_rate = clk_master_recalc_rate,
120	.get_parent = clk_master_get_parent,
121};
122
123static struct clk_hw * __init
124at91_clk_register_master(struct regmap *regmap,
125		const char *name, int num_parents,
126		const char **parent_names,
127		const struct clk_master_layout *layout,
128		const struct clk_master_characteristics *characteristics)
129{
130	struct clk_master *master;
131	struct clk_init_data init;
132	struct clk_hw *hw;
133	int ret;
134
135	if (!name || !num_parents || !parent_names)
136		return ERR_PTR(-EINVAL);
137
138	master = kzalloc(sizeof(*master), GFP_KERNEL);
139	if (!master)
140		return ERR_PTR(-ENOMEM);
141
142	init.name = name;
143	init.ops = &master_ops;
144	init.parent_names = parent_names;
145	init.num_parents = num_parents;
146	init.flags = 0;
147
148	master->hw.init = &init;
149	master->layout = layout;
150	master->characteristics = characteristics;
151	master->regmap = regmap;
152
153	hw = &master->hw;
154	ret = clk_hw_register(NULL, &master->hw);
155	if (ret) {
156		kfree(master);
157		hw = ERR_PTR(ret);
158	}
159
160	return hw;
161}
162
163
164static const struct clk_master_layout at91rm9200_master_layout = {
165	.mask = 0x31F,
166	.pres_shift = 2,
167};
168
169static const struct clk_master_layout at91sam9x5_master_layout = {
170	.mask = 0x373,
171	.pres_shift = 4,
172};
173
174
175static struct clk_master_characteristics * __init
176of_at91_clk_master_get_characteristics(struct device_node *np)
177{
178	struct clk_master_characteristics *characteristics;
179
180	characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
181	if (!characteristics)
182		return NULL;
183
184	if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
185		goto out_free_characteristics;
186
187	of_property_read_u32_array(np, "atmel,clk-divisors",
188				   characteristics->divisors, 4);
189
190	characteristics->have_div3_pres =
191		of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
192
193	return characteristics;
194
195out_free_characteristics:
196	kfree(characteristics);
197	return NULL;
198}
199
200static void __init
201of_at91_clk_master_setup(struct device_node *np,
202			 const struct clk_master_layout *layout)
203{
204	struct clk_hw *hw;
205	unsigned int num_parents;
206	const char *parent_names[MASTER_SOURCE_MAX];
207	const char *name = np->name;
208	struct clk_master_characteristics *characteristics;
209	struct regmap *regmap;
210
211	num_parents = of_clk_get_parent_count(np);
212	if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
213		return;
214
215	of_clk_parent_fill(np, parent_names, num_parents);
216
217	of_property_read_string(np, "clock-output-names", &name);
218
219	characteristics = of_at91_clk_master_get_characteristics(np);
220	if (!characteristics)
221		return;
222
223	regmap = syscon_node_to_regmap(of_get_parent(np));
224	if (IS_ERR(regmap))
225		return;
226
227	hw = at91_clk_register_master(regmap, name, num_parents,
228				       parent_names, layout,
229				       characteristics);
230	if (IS_ERR(hw))
231		goto out_free_characteristics;
232
233	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
234	return;
235
236out_free_characteristics:
237	kfree(characteristics);
238}
239
240static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
241{
242	of_at91_clk_master_setup(np, &at91rm9200_master_layout);
243}
244CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
245	       of_at91rm9200_clk_master_setup);
246
247static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
248{
249	of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
250}
251CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
252	       of_at91sam9x5_clk_master_setup);