linux/arch/arm/mach-ns9xxx/clock.c
<<
>>
Prefs
   1/*
   2 * arch/arm/mach-ns9xxx/clock.c
   3 *
   4 * Copyright (C) 2007 by Digi International Inc.
   5 * All rights reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 */
  11#include <linux/err.h>
  12#include <linux/module.h>
  13#include <linux/list.h>
  14#include <linux/clk.h>
  15#include <linux/string.h>
  16#include <linux/platform_device.h>
  17#include <linux/semaphore.h>
  18
  19#include "clock.h"
  20
  21static LIST_HEAD(clocks);
  22static DEFINE_SPINLOCK(clk_lock);
  23
  24struct clk *clk_get(struct device *dev, const char *id)
  25{
  26        struct clk *p, *ret = NULL, *retgen = NULL;
  27        unsigned long flags;
  28        int idno;
  29
  30        if (dev == NULL || dev->bus != &platform_bus_type)
  31                idno = -1;
  32        else
  33                idno = to_platform_device(dev)->id;
  34
  35        spin_lock_irqsave(&clk_lock, flags);
  36        list_for_each_entry(p, &clocks, node) {
  37                if (strcmp(id, p->name) == 0) {
  38                        if (p->id == idno) {
  39                                if (!try_module_get(p->owner))
  40                                        continue;
  41                                ret = p;
  42                                break;
  43                        } else if (p->id == -1)
  44                                /* remember match with id == -1 in case there is
  45                                 * no clock for idno */
  46                                retgen = p;
  47                }
  48        }
  49
  50        if (!ret && retgen && try_module_get(retgen->owner))
  51                ret = retgen;
  52
  53        if (ret)
  54                ++ret->refcount;
  55
  56        spin_unlock_irqrestore(&clk_lock, flags);
  57
  58        return ret ? ret : ERR_PTR(-ENOENT);
  59}
  60EXPORT_SYMBOL(clk_get);
  61
  62void clk_put(struct clk *clk)
  63{
  64        module_put(clk->owner);
  65        --clk->refcount;
  66}
  67EXPORT_SYMBOL(clk_put);
  68
  69static int clk_enable_unlocked(struct clk *clk)
  70{
  71        int ret = 0;
  72        if (clk->parent) {
  73                ret = clk_enable_unlocked(clk->parent);
  74                if (ret)
  75                        return ret;
  76        }
  77
  78        if (clk->usage++ == 0 && clk->endisable)
  79                ret = clk->endisable(clk, 1);
  80
  81        return ret;
  82}
  83
  84int clk_enable(struct clk *clk)
  85{
  86        int ret;
  87        unsigned long flags;
  88
  89        spin_lock_irqsave(&clk_lock, flags);
  90
  91        ret = clk_enable_unlocked(clk);
  92
  93        spin_unlock_irqrestore(&clk_lock, flags);
  94
  95        return ret;
  96}
  97EXPORT_SYMBOL(clk_enable);
  98
  99static void clk_disable_unlocked(struct clk *clk)
 100{
 101        if (--clk->usage == 0 && clk->endisable)
 102                clk->endisable(clk, 0);
 103
 104        if (clk->parent)
 105                clk_disable_unlocked(clk->parent);
 106}
 107
 108void clk_disable(struct clk *clk)
 109{
 110        unsigned long flags;
 111
 112        spin_lock_irqsave(&clk_lock, flags);
 113
 114        clk_disable_unlocked(clk);
 115
 116        spin_unlock_irqrestore(&clk_lock, flags);
 117}
 118EXPORT_SYMBOL(clk_disable);
 119
 120unsigned long clk_get_rate(struct clk *clk)
 121{
 122        if (clk->get_rate)
 123                return clk->get_rate(clk);
 124
 125        if (clk->rate)
 126                return clk->rate;
 127
 128        if (clk->parent)
 129                return clk_get_rate(clk->parent);
 130
 131        return 0;
 132}
 133EXPORT_SYMBOL(clk_get_rate);
 134
 135int clk_register(struct clk *clk)
 136{
 137        unsigned long flags;
 138
 139        spin_lock_irqsave(&clk_lock, flags);
 140
 141        list_add(&clk->node, &clocks);
 142
 143        if (clk->parent)
 144                ++clk->parent->refcount;
 145
 146        spin_unlock_irqrestore(&clk_lock, flags);
 147
 148        return 0;
 149}
 150
 151int clk_unregister(struct clk *clk)
 152{
 153        int ret = 0;
 154        unsigned long flags;
 155
 156        spin_lock_irqsave(&clk_lock, flags);
 157
 158        if (clk->usage || clk->refcount)
 159                ret = -EBUSY;
 160        else
 161                list_del(&clk->node);
 162
 163        if (clk->parent)
 164                --clk->parent->refcount;
 165
 166        spin_unlock_irqrestore(&clk_lock, flags);
 167
 168        return ret;
 169}
 170
 171#if defined CONFIG_DEBUG_FS
 172
 173#include <linux/debugfs.h>
 174#include <linux/seq_file.h>
 175
 176static int clk_debugfs_show(struct seq_file *s, void *null)
 177{
 178        unsigned long flags;
 179        struct clk *p;
 180
 181        spin_lock_irqsave(&clk_lock, flags);
 182
 183        list_for_each_entry(p, &clocks, node)
 184                seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu\n",
 185                                p->name, p->id, p->usage, p->refcount,
 186                                p->usage ? clk_get_rate(p) : 0);
 187
 188        spin_unlock_irqrestore(&clk_lock, flags);
 189
 190        return 0;
 191}
 192
 193static int clk_debugfs_open(struct inode *inode, struct file *file)
 194{
 195        return single_open(file, clk_debugfs_show, NULL);
 196}
 197
 198static const struct file_operations clk_debugfs_operations = {
 199        .open = clk_debugfs_open,
 200        .read = seq_read,
 201        .llseek = seq_lseek,
 202        .release = single_release,
 203};
 204
 205static int __init clk_debugfs_init(void)
 206{
 207        struct dentry *dentry;
 208
 209        dentry = debugfs_create_file("clk", S_IFREG | S_IRUGO, NULL, NULL,
 210                        &clk_debugfs_operations);
 211        return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
 212}
 213subsys_initcall(clk_debugfs_init);
 214
 215#endif /* if defined CONFIG_DEBUG_FS */
 216