aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-10-15 03:46:37 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-10-15 03:46:37 -0400
commit28085bc5de19cad365bcff98e9c8785c397c7c36 (patch)
tree710d9b8e506cd408e8cf715ce0672f06a3375404 /drivers/sh
parenta80be1680502f99de5f9565c491208e90a9a3afe (diff)
sh: clkfwk: support clock remapping.
This implements support for ioremapping of register windows that encapsulate clock control registers used by a struct clk, with transparent sibling inheritance. Root clocks at the top of a given topology often encapsulate the entire register space of all of their sibling clocks, so this mapping can be done once and handed down. A given clock enable/disable case maps out to a single bit in a shared register, so this prevents creating multiple overlapping mappings. The mapping case breaks down in to a couple of different situations: - Sibling clocks without a specific mapping. - Root clocks without a specific mapping. - Any of sibling/root clocks with a specific mapping. Sibling clocks with no specified mapping will grovel up the clock chain and install the root clock mapping unconditionally at registration time. Root clocks without their own mappings have a dummy BSS-initialized mapping inserted that is handed down the chain just like any other mapping. This permits all of the sibling clock ops to read/write using the mapping offsets without any special configuration, enabling them to not care whether access ultimately goes through translatable or untranslatable memory. Any clock with its own mapping will have the window initialized at registration time and be ready for use by its clock ops. Failure to establish the mapping will prevent registration, so no additional sanity checks are needed. Sibling clocks that double as parents for the moment will not propagate their mapping down, but this is easily tunable if the need arises. All clock mappings are kref refcounted, with each instance of mapping inheritance incrementing the refcount. Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/sh')
-rw-r--r--drivers/sh/clk.c91
1 files changed, 89 insertions, 2 deletions
diff --git a/drivers/sh/clk.c b/drivers/sh/clk.c
index 661a801126ad..813d97cdad49 100644
--- a/drivers/sh/clk.c
+++ b/drivers/sh/clk.c
@@ -25,7 +25,7 @@
25#include <linux/sysdev.h> 25#include <linux/sysdev.h>
26#include <linux/seq_file.h> 26#include <linux/seq_file.h>
27#include <linux/err.h> 27#include <linux/err.h>
28#include <linux/platform_device.h> 28#include <linux/io.h>
29#include <linux/debugfs.h> 29#include <linux/debugfs.h>
30#include <linux/cpufreq.h> 30#include <linux/cpufreq.h>
31#include <linux/clk.h> 31#include <linux/clk.h>
@@ -251,8 +251,88 @@ void recalculate_root_clocks(void)
251 } 251 }
252} 252}
253 253
254static struct clk_mapping dummy_mapping;
255
256static struct clk *lookup_root_clock(struct clk *clk)
257{
258 while (clk->parent)
259 clk = clk->parent;
260
261 return clk;
262}
263
264static int clk_establish_mapping(struct clk *clk)
265{
266 struct clk_mapping *mapping = clk->mapping;
267
268 /*
269 * Propagate mappings.
270 */
271 if (!mapping) {
272 struct clk *clkp;
273
274 /*
275 * dummy mapping for root clocks with no specified ranges
276 */
277 if (!clk->parent) {
278 clk->mapping = &dummy_mapping;
279 return 0;
280 }
281
282 /*
283 * If we're on a child clock and it provides no mapping of its
284 * own, inherit the mapping from its root clock.
285 */
286 clkp = lookup_root_clock(clk);
287 mapping = clkp->mapping;
288 BUG_ON(!mapping);
289 }
290
291 /*
292 * Establish initial mapping.
293 */
294 if (!mapping->base && mapping->phys) {
295 kref_init(&mapping->ref);
296
297 mapping->base = ioremap_nocache(mapping->phys, mapping->len);
298 if (unlikely(!mapping->base))
299 return -ENXIO;
300 } else if (mapping->base) {
301 /*
302 * Bump the refcount for an existing mapping
303 */
304 kref_get(&mapping->ref);
305 }
306
307 clk->mapping = mapping;
308 return 0;
309}
310
311static void clk_destroy_mapping(struct kref *kref)
312{
313 struct clk_mapping *mapping;
314
315 mapping = container_of(kref, struct clk_mapping, ref);
316
317 iounmap(mapping->base);
318}
319
320static void clk_teardown_mapping(struct clk *clk)
321{
322 struct clk_mapping *mapping = clk->mapping;
323
324 /* Nothing to do */
325 if (mapping == &dummy_mapping)
326 return;
327
328 kref_put(&mapping->ref, clk_destroy_mapping);
329 clk->mapping = NULL;
330}
331
254int clk_register(struct clk *clk) 332int clk_register(struct clk *clk)
255{ 333{
334 int ret;
335
256 if (clk == NULL || IS_ERR(clk)) 336 if (clk == NULL || IS_ERR(clk))
257 return -EINVAL; 337 return -EINVAL;
258 338
@@ -267,6 +347,10 @@ int clk_register(struct clk *clk)
267 INIT_LIST_HEAD(&clk->children); 347 INIT_LIST_HEAD(&clk->children);
268 clk->usecount = 0; 348 clk->usecount = 0;
269 349
350 ret = clk_establish_mapping(clk);
351 if (unlikely(ret))
352 goto out_unlock;
353
270 if (clk->parent) 354 if (clk->parent)
271 list_add(&clk->sibling, &clk->parent->children); 355 list_add(&clk->sibling, &clk->parent->children);
272 else 356 else
@@ -275,9 +359,11 @@ int clk_register(struct clk *clk)
275 list_add(&clk->node, &clock_list); 359 list_add(&clk->node, &clock_list);
276 if (clk->ops && clk->ops->init) 360 if (clk->ops && clk->ops->init)
277 clk->ops->init(clk); 361 clk->ops->init(clk);
362
363out_unlock:
278 mutex_unlock(&clock_list_sem); 364 mutex_unlock(&clock_list_sem);
279 365
280 return 0; 366 return ret;
281} 367}
282EXPORT_SYMBOL_GPL(clk_register); 368EXPORT_SYMBOL_GPL(clk_register);
283 369
@@ -286,6 +372,7 @@ void clk_unregister(struct clk *clk)
286 mutex_lock(&clock_list_sem); 372 mutex_lock(&clock_list_sem);
287 list_del(&clk->sibling); 373 list_del(&clk->sibling);
288 list_del(&clk->node); 374 list_del(&clk->node);
375 clk_teardown_mapping(clk);
289 mutex_unlock(&clock_list_sem); 376 mutex_unlock(&clock_list_sem);
290} 377}
291EXPORT_SYMBOL_GPL(clk_unregister); 378EXPORT_SYMBOL_GPL(clk_unregister);