Skip to content

Commit

Permalink
ns9xxx: add clock api
Browse files Browse the repository at this point in the history
Signed-off-by: Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
  • Loading branch information
Uwe Kleine-König authored and Uwe Kleine-König committed Mar 31, 2008
1 parent ed6f598 commit 04c366f
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 1 deletion.
2 changes: 1 addition & 1 deletion arch/arm/mach-ns9xxx/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
obj-y := irq.o generic.o gpio.o
obj-y := clock.o generic.o gpio.o irq.o

obj-$(CONFIG_MACH_CC9P9360DEV) += mach-cc9p9360dev.o
obj-$(CONFIG_MACH_CC9P9360JS) += mach-cc9p9360js.o
Expand Down
215 changes: 215 additions & 0 deletions arch/arm/mach-ns9xxx/clock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* arch/arm/mach-ns9xxx/clock.c
*
* Copyright (C) 2007 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/string.h>
#include <linux/platform_device.h>

#include <asm/semaphore.h>
#include "clock.h"

static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clk_lock);

struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p, *ret = NULL, *retgen = NULL;
unsigned long flags;
int idno;

if (dev == NULL || dev->bus != &platform_bus_type)
idno = -1;
else
idno = to_platform_device(dev)->id;

spin_lock_irqsave(&clk_lock, flags);
list_for_each_entry(p, &clocks, node) {
if (strcmp(id, p->name) == 0) {
if (p->id == idno) {
if (!try_module_get(p->owner))
continue;
ret = p;
break;
} else if (p->id == -1)
/* remember match with id == -1 in case there is
* no clock for idno */
retgen = p;
}
}

if (!ret && retgen && try_module_get(retgen->owner))
ret = retgen;

if (ret)
++ret->refcount;

spin_unlock_irqrestore(&clk_lock, flags);

return ret ? ret : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);

void clk_put(struct clk *clk)
{
module_put(clk->owner);
--clk->refcount;
}
EXPORT_SYMBOL(clk_put);

static int clk_enable_unlocked(struct clk *clk)
{
int ret = 0;
if (clk->parent) {
ret = clk_enable_unlocked(clk->parent);
if (ret)
return ret;
}

if (clk->usage++ == 0 && clk->endisable)
ret = clk->endisable(clk, 1);

return ret;
}

int clk_enable(struct clk *clk)
{
int ret;
unsigned long flags;

spin_lock_irqsave(&clk_lock, flags);

ret = clk_enable_unlocked(clk);

spin_unlock_irqrestore(&clk_lock, flags);

return ret;
}
EXPORT_SYMBOL(clk_enable);

static void clk_disable_unlocked(struct clk *clk)
{
if (--clk->usage == 0 && clk->endisable)
clk->endisable(clk, 0);

if (clk->parent)
clk_disable_unlocked(clk->parent);
}

void clk_disable(struct clk *clk)
{
unsigned long flags;

spin_lock_irqsave(&clk_lock, flags);

clk_disable_unlocked(clk);

spin_unlock_irqrestore(&clk_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

unsigned long clk_get_rate(struct clk *clk)
{
if (clk->get_rate)
return clk->get_rate(clk);

if (clk->rate)
return clk->rate;

if (clk->parent)
return clk_get_rate(clk->parent);

return 0;
}
EXPORT_SYMBOL(clk_get_rate);

int clk_register(struct clk *clk)
{
unsigned long flags;

spin_lock_irqsave(&clk_lock, flags);

list_add(&clk->node, &clocks);

if (clk->parent)
++clk->parent->refcount;

spin_unlock_irqrestore(&clk_lock, flags);

return 0;
}

int clk_unregister(struct clk *clk)
{
int ret = 0;
unsigned long flags;

spin_lock_irqsave(&clk_lock, flags);

if (clk->usage || clk->refcount)
ret = -EBUSY;
else
list_del(&clk->node);

if (clk->parent)
--clk->parent->refcount;

spin_unlock_irqrestore(&clk_lock, flags);

return ret;
}

#if defined CONFIG_DEBUG_FS

#include <linux/debugfs.h>
#include <linux/seq_file.h>

static int clk_debugfs_show(struct seq_file *s, void *null)
{
unsigned long flags;
struct clk *p;

spin_lock_irqsave(&clk_lock, flags);

list_for_each_entry(p, &clocks, node)
seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu\n",
p->name, p->id, p->usage, p->refcount,
p->usage ? clk_get_rate(p) : 0);

spin_unlock_irqrestore(&clk_lock, flags);

return 0;
}

static int clk_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_debugfs_show, NULL);
}

static struct file_operations clk_debugfs_operations = {
.open = clk_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

static int __init clk_debugfs_init(void)
{
struct dentry *dentry;

dentry = debugfs_create_file("clk", S_IFREG | S_IRUGO, NULL, NULL,
&clk_debugfs_operations);
return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
}
subsys_initcall(clk_debugfs_init);

#endif /* if defined CONFIG_DEBUG_FS */
35 changes: 35 additions & 0 deletions arch/arm/mach-ns9xxx/clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* arch/arm/mach-ns9xxx/clock.h
*
* Copyright (C) 2007 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef __NS9XXX_CLOCK_H
#define __NS9XXX_CLOCK_H

#include <linux/list.h>

struct clk {
struct module *owner;
const char *name;
int id;

struct clk *parent;

unsigned long rate;
int (*endisable)(struct clk *, int enable);
unsigned long (*get_rate)(struct clk *);

struct list_head node;
unsigned long refcount;
unsigned long usage;
};

int clk_register(struct clk *clk);
int clk_unregister(struct clk *clk);

#endif /* ifndef __NS9XXX_CLOCK_H */

0 comments on commit 04c366f

Please sign in to comment.