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