linux/arch/avr32/mach-at32ap/clock.c
<<
>>
Prefs
   1/*
   2 * Clock management for AT32AP CPUs
   3 *
   4 * Copyright (C) 2006 Atmel Corporation
   5 *
   6 * Based on arch/arm/mach-at91/clock.c
   7 *   Copyright (C) 2005 David Brownell
   8 *   Copyright (C) 2005 Ivan Kokshaysky
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14#include <linux/clk.h>
  15#include <linux/err.h>
  16#include <linux/export.h>
  17#include <linux/device.h>
  18#include <linux/string.h>
  19#include <linux/list.h>
  20
  21#include <mach/chip.h>
  22
  23#include "clock.h"
  24
  25/* at32 clock list */
  26static LIST_HEAD(at32_clock_list);
  27
  28static DEFINE_SPINLOCK(clk_lock);
  29static DEFINE_SPINLOCK(clk_list_lock);
  30
  31void at32_clk_register(struct clk *clk)
  32{
  33        spin_lock(&clk_list_lock);
  34        /* add the new item to the end of the list */
  35        list_add_tail(&clk->list, &at32_clock_list);
  36        spin_unlock(&clk_list_lock);
  37}
  38
  39static struct clk *__clk_get(struct device *dev, const char *id)
  40{
  41        struct clk *clk;
  42
  43        list_for_each_entry(clk, &at32_clock_list, list) {
  44                if (clk->dev == dev && strcmp(id, clk->name) == 0) {
  45                        return clk;
  46                }
  47        }
  48
  49        return ERR_PTR(-ENOENT);
  50}
  51
  52struct clk *clk_get(struct device *dev, const char *id)
  53{
  54        struct clk *clk;
  55
  56        spin_lock(&clk_list_lock);
  57        clk = __clk_get(dev, id);
  58        spin_unlock(&clk_list_lock);
  59
  60        return clk;
  61}
  62
  63EXPORT_SYMBOL(clk_get);
  64
  65void clk_put(struct clk *clk)
  66{
  67        /* clocks are static for now, we can't free them */
  68}
  69EXPORT_SYMBOL(clk_put);
  70
  71static void __clk_enable(struct clk *clk)
  72{
  73        if (clk->parent)
  74                __clk_enable(clk->parent);
  75        if (clk->users++ == 0 && clk->mode)
  76                clk->mode(clk, 1);
  77}
  78
  79int clk_enable(struct clk *clk)
  80{
  81        unsigned long flags;
  82
  83        spin_lock_irqsave(&clk_lock, flags);
  84        __clk_enable(clk);
  85        spin_unlock_irqrestore(&clk_lock, flags);
  86
  87        return 0;
  88}
  89EXPORT_SYMBOL(clk_enable);
  90
  91static void __clk_disable(struct clk *clk)
  92{
  93        if (clk->users == 0) {
  94                printk(KERN_ERR "%s: mismatched disable\n", clk->name);
  95                WARN_ON(1);
  96                return;
  97        }
  98
  99        if (--clk->users == 0 && clk->mode)
 100                clk->mode(clk, 0);
 101        if (clk->parent)
 102                __clk_disable(clk->parent);
 103}
 104
 105void clk_disable(struct clk *clk)
 106{
 107        unsigned long flags;
 108
 109        spin_lock_irqsave(&clk_lock, flags);
 110        __clk_disable(clk);
 111        spin_unlock_irqrestore(&clk_lock, flags);
 112}
 113EXPORT_SYMBOL(clk_disable);
 114
 115unsigned long clk_get_rate(struct clk *clk)
 116{
 117        unsigned long flags;
 118        unsigned long rate;
 119
 120        spin_lock_irqsave(&clk_lock, flags);
 121        rate = clk->get_rate(clk);
 122        spin_unlock_irqrestore(&clk_lock, flags);
 123
 124        return rate;
 125}
 126EXPORT_SYMBOL(clk_get_rate);
 127
 128long clk_round_rate(struct clk *clk, unsigned long rate)
 129{
 130        unsigned long flags, actual_rate;
 131
 132        if (!clk->set_rate)
 133                return -ENOSYS;
 134
 135        spin_lock_irqsave(&clk_lock, flags);
 136        actual_rate = clk->set_rate(clk, rate, 0);
 137        spin_unlock_irqrestore(&clk_lock, flags);
 138
 139        return actual_rate;
 140}
 141EXPORT_SYMBOL(clk_round_rate);
 142
 143int clk_set_rate(struct clk *clk, unsigned long rate)
 144{
 145        unsigned long flags;
 146        long ret;
 147
 148        if (!clk->set_rate)
 149                return -ENOSYS;
 150
 151        spin_lock_irqsave(&clk_lock, flags);
 152        ret = clk->set_rate(clk, rate, 1);
 153        spin_unlock_irqrestore(&clk_lock, flags);
 154
 155        return (ret < 0) ? ret : 0;
 156}
 157EXPORT_SYMBOL(clk_set_rate);
 158
 159int clk_set_parent(struct clk *clk, struct clk *parent)
 160{
 161        unsigned long flags;
 162        int ret;
 163
 164        if (!clk->set_parent)
 165                return -ENOSYS;
 166
 167        spin_lock_irqsave(&clk_lock, flags);
 168        ret = clk->set_parent(clk, parent);
 169        spin_unlock_irqrestore(&clk_lock, flags);
 170
 171        return ret;
 172}
 173EXPORT_SYMBOL(clk_set_parent);
 174
 175struct clk *clk_get_parent(struct clk *clk)
 176{
 177        return clk->parent;
 178}
 179EXPORT_SYMBOL(clk_get_parent);
 180
 181
 182
 183#ifdef CONFIG_DEBUG_FS
 184
 185/* /sys/kernel/debug/at32ap_clk */
 186
 187#include <linux/io.h>
 188#include <linux/debugfs.h>
 189#include <linux/seq_file.h>
 190#include "pm.h"
 191
 192
 193#define NEST_DELTA      2
 194#define NEST_MAX        6
 195
 196struct clkinf {
 197        struct seq_file *s;
 198        unsigned        nest;
 199};
 200
 201static void
 202dump_clock(struct clk *parent, struct clkinf *r)
 203{
 204        unsigned        nest = r->nest;
 205        char            buf[16 + NEST_MAX];
 206        struct clk      *clk;
 207        unsigned        i;
 208
 209        /* skip clocks coupled to devices that aren't registered */
 210        if (parent->dev && !dev_name(parent->dev) && !parent->users)
 211                return;
 212
 213        /* <nest spaces> name <pad to end> */
 214        memset(buf, ' ', sizeof(buf) - 1);
 215        buf[sizeof(buf) - 1] = 0;
 216        i = strlen(parent->name);
 217        memcpy(buf + nest, parent->name,
 218                        min(i, (unsigned)(sizeof(buf) - 1 - nest)));
 219
 220        seq_printf(r->s, "%s%c users=%2d %-3s %9ld Hz",
 221                buf, parent->set_parent ? '*' : ' ',
 222                parent->users,
 223                parent->users ? "on" : "off",   /* NOTE: not-paranoid!! */
 224                clk_get_rate(parent));
 225        if (parent->dev)
 226                seq_printf(r->s, ", for %s", dev_name(parent->dev));
 227        seq_printf(r->s, "\n");
 228
 229        /* cost of this scan is small, but not linear... */
 230        r->nest = nest + NEST_DELTA;
 231
 232        list_for_each_entry(clk, &at32_clock_list, list) {
 233                if (clk->parent == parent)
 234                        dump_clock(clk, r);
 235        }
 236        r->nest = nest;
 237}
 238
 239static int clk_show(struct seq_file *s, void *unused)
 240{
 241        struct clkinf   r;
 242        int             i;
 243        struct clk      *clk;
 244
 245        /* show all the power manager registers */
 246        seq_printf(s, "MCCTRL  = %8x\n", pm_readl(MCCTRL));
 247        seq_printf(s, "CKSEL   = %8x\n", pm_readl(CKSEL));
 248        seq_printf(s, "CPUMASK = %8x\n", pm_readl(CPU_MASK));
 249        seq_printf(s, "HSBMASK = %8x\n", pm_readl(HSB_MASK));
 250        seq_printf(s, "PBAMASK = %8x\n", pm_readl(PBA_MASK));
 251        seq_printf(s, "PBBMASK = %8x\n", pm_readl(PBB_MASK));
 252        seq_printf(s, "PLL0    = %8x\n", pm_readl(PLL0));
 253        seq_printf(s, "PLL1    = %8x\n", pm_readl(PLL1));
 254        seq_printf(s, "IMR     = %8x\n", pm_readl(IMR));
 255        for (i = 0; i < 8; i++) {
 256                if (i == 5)
 257                        continue;
 258                seq_printf(s, "GCCTRL%d = %8x\n", i, pm_readl(GCCTRL(i)));
 259        }
 260
 261        seq_printf(s, "\n");
 262
 263        r.s = s;
 264        r.nest = 0;
 265        /* protected from changes on the list while dumping */
 266        spin_lock(&clk_list_lock);
 267
 268        /* show clock tree as derived from the three oscillators */
 269        clk = __clk_get(NULL, "osc32k");
 270        dump_clock(clk, &r);
 271        clk_put(clk);
 272
 273        clk = __clk_get(NULL, "osc0");
 274        dump_clock(clk, &r);
 275        clk_put(clk);
 276
 277        clk = __clk_get(NULL, "osc1");
 278        dump_clock(clk, &r);
 279        clk_put(clk);
 280
 281        spin_unlock(&clk_list_lock);
 282
 283        return 0;
 284}
 285
 286static int clk_open(struct inode *inode, struct file *file)
 287{
 288        return single_open(file, clk_show, NULL);
 289}
 290
 291static const struct file_operations clk_operations = {
 292        .open           = clk_open,
 293        .read           = seq_read,
 294        .llseek         = seq_lseek,
 295        .release        = single_release,
 296};
 297
 298static int __init clk_debugfs_init(void)
 299{
 300        (void) debugfs_create_file("at32ap_clk", S_IFREG | S_IRUGO,
 301                        NULL, NULL, &clk_operations);
 302
 303        return 0;
 304}
 305postcore_initcall(clk_debugfs_init);
 306
 307#endif
 308