-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ARM] 4430/1: davinci: clock control support
Support clock control driver for TI DaVinci SoC Signed-off-by: Vladimir Barinov <vbarinov@ru.mvista.com> Signed-off-by: Kevin Hilman <khilman@mvista.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
- Loading branch information
Vladimir Barinov
authored and
Russell King
committed
Jul 12, 2007
1 parent
7dcca30
commit 3e062b0
Showing
6 changed files
with
387 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,323 @@ | ||
/* | ||
* TI DaVinci clock config file | ||
* | ||
* Copyright (C) 2006 Texas Instruments. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
#include <linux/init.h> | ||
#include <linux/errno.h> | ||
#include <linux/err.h> | ||
#include <linux/mutex.h> | ||
#include <linux/platform_device.h> | ||
|
||
#include <asm/hardware.h> | ||
#include <asm/io.h> | ||
|
||
#include <asm/arch/psc.h> | ||
#include "clock.h" | ||
|
||
/* PLL/Reset register offsets */ | ||
#define PLLM 0x110 | ||
|
||
static LIST_HEAD(clocks); | ||
static DEFINE_MUTEX(clocks_mutex); | ||
static DEFINE_SPINLOCK(clockfw_lock); | ||
|
||
static unsigned int commonrate; | ||
static unsigned int armrate; | ||
static unsigned int fixedrate = 27000000; /* 27 MHZ */ | ||
|
||
extern void davinci_psc_config(unsigned int domain, unsigned int id, char enable); | ||
|
||
/* | ||
* Returns a clock. Note that we first try to use device id on the bus | ||
* and clock name. If this fails, we try to use clock name only. | ||
*/ | ||
struct clk *clk_get(struct device *dev, const char *id) | ||
{ | ||
struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
int idno; | ||
|
||
if (dev == NULL || dev->bus != &platform_bus_type) | ||
idno = -1; | ||
else | ||
idno = to_platform_device(dev)->id; | ||
|
||
mutex_lock(&clocks_mutex); | ||
|
||
list_for_each_entry(p, &clocks, node) { | ||
if (p->id == idno && | ||
strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
clk = p; | ||
goto found; | ||
} | ||
} | ||
|
||
list_for_each_entry(p, &clocks, node) { | ||
if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
clk = p; | ||
break; | ||
} | ||
} | ||
|
||
found: | ||
mutex_unlock(&clocks_mutex); | ||
|
||
return clk; | ||
} | ||
EXPORT_SYMBOL(clk_get); | ||
|
||
void clk_put(struct clk *clk) | ||
{ | ||
if (clk && !IS_ERR(clk)) | ||
module_put(clk->owner); | ||
} | ||
EXPORT_SYMBOL(clk_put); | ||
|
||
static int __clk_enable(struct clk *clk) | ||
{ | ||
if (clk->flags & ALWAYS_ENABLED) | ||
return 0; | ||
|
||
davinci_psc_config(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 1); | ||
return 0; | ||
} | ||
|
||
static void __clk_disable(struct clk *clk) | ||
{ | ||
if (clk->usecount) | ||
return; | ||
|
||
davinci_psc_config(DAVINCI_GPSC_ARMDOMAIN, clk->lpsc, 0); | ||
} | ||
|
||
int clk_enable(struct clk *clk) | ||
{ | ||
unsigned long flags; | ||
int ret = 0; | ||
|
||
if (clk == NULL || IS_ERR(clk)) | ||
return -EINVAL; | ||
|
||
if (clk->usecount++ == 0) { | ||
spin_lock_irqsave(&clockfw_lock, flags); | ||
ret = __clk_enable(clk); | ||
spin_unlock_irqrestore(&clockfw_lock, flags); | ||
} | ||
|
||
return ret; | ||
} | ||
EXPORT_SYMBOL(clk_enable); | ||
|
||
void clk_disable(struct clk *clk) | ||
{ | ||
unsigned long flags; | ||
|
||
if (clk == NULL || IS_ERR(clk)) | ||
return; | ||
|
||
if (clk->usecount > 0 && !(--clk->usecount)) { | ||
spin_lock_irqsave(&clockfw_lock, flags); | ||
__clk_disable(clk); | ||
spin_unlock_irqrestore(&clockfw_lock, flags); | ||
} | ||
} | ||
EXPORT_SYMBOL(clk_disable); | ||
|
||
unsigned long clk_get_rate(struct clk *clk) | ||
{ | ||
if (clk == NULL || IS_ERR(clk)) | ||
return -EINVAL; | ||
|
||
return *(clk->rate); | ||
} | ||
EXPORT_SYMBOL(clk_get_rate); | ||
|
||
long clk_round_rate(struct clk *clk, unsigned long rate) | ||
{ | ||
if (clk == NULL || IS_ERR(clk)) | ||
return -EINVAL; | ||
|
||
return *(clk->rate); | ||
} | ||
EXPORT_SYMBOL(clk_round_rate); | ||
|
||
int clk_set_rate(struct clk *clk, unsigned long rate) | ||
{ | ||
if (clk == NULL || IS_ERR(clk)) | ||
return -EINVAL; | ||
|
||
/* changing the clk rate is not supported */ | ||
return -EINVAL; | ||
} | ||
EXPORT_SYMBOL(clk_set_rate); | ||
|
||
int clk_register(struct clk *clk) | ||
{ | ||
if (clk == NULL || IS_ERR(clk)) | ||
return -EINVAL; | ||
|
||
mutex_lock(&clocks_mutex); | ||
list_add(&clk->node, &clocks); | ||
mutex_unlock(&clocks_mutex); | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL(clk_register); | ||
|
||
void clk_unregister(struct clk *clk) | ||
{ | ||
if (clk == NULL || IS_ERR(clk)) | ||
return; | ||
|
||
mutex_lock(&clocks_mutex); | ||
list_del(&clk->node); | ||
mutex_unlock(&clocks_mutex); | ||
} | ||
EXPORT_SYMBOL(clk_unregister); | ||
|
||
static struct clk davinci_clks[] = { | ||
{ | ||
.name = "ARMCLK", | ||
.rate = &armrate, | ||
.lpsc = -1, | ||
.flags = ALWAYS_ENABLED, | ||
}, | ||
{ | ||
.name = "UART", | ||
.rate = &fixedrate, | ||
.lpsc = DAVINCI_LPSC_UART0, | ||
}, | ||
{ | ||
.name = "EMACCLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_EMAC_WRAPPER, | ||
}, | ||
{ | ||
.name = "I2CCLK", | ||
.rate = &fixedrate, | ||
.lpsc = DAVINCI_LPSC_I2C, | ||
}, | ||
{ | ||
.name = "IDECLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_ATA, | ||
}, | ||
{ | ||
.name = "McBSPCLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_McBSP, | ||
}, | ||
{ | ||
.name = "MMCSDCLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_MMC_SD, | ||
}, | ||
{ | ||
.name = "SPICLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_SPI, | ||
}, | ||
{ | ||
.name = "gpio", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_GPIO, | ||
}, | ||
{ | ||
.name = "AEMIFCLK", | ||
.rate = &commonrate, | ||
.lpsc = DAVINCI_LPSC_AEMIF, | ||
.usecount = 1, | ||
} | ||
}; | ||
|
||
int __init davinci_clk_init(void) | ||
{ | ||
struct clk *clkp; | ||
int count = 0; | ||
u32 pll_mult; | ||
|
||
pll_mult = davinci_readl(DAVINCI_PLL_CNTRL0_BASE + PLLM); | ||
commonrate = ((pll_mult + 1) * 27000000) / 6; | ||
armrate = ((pll_mult + 1) * 27000000) / 2; | ||
|
||
for (clkp = davinci_clks; count < ARRAY_SIZE(davinci_clks); | ||
count++, clkp++) { | ||
clk_register(clkp); | ||
|
||
/* Turn on clocks that have been enabled in the | ||
* table above */ | ||
if (clkp->usecount) | ||
clk_enable(clkp); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
#ifdef CONFIG_PROC_FS | ||
#include <linux/proc_fs.h> | ||
#include <linux/seq_file.h> | ||
|
||
static void *davinci_ck_start(struct seq_file *m, loff_t *pos) | ||
{ | ||
return *pos < 1 ? (void *)1 : NULL; | ||
} | ||
|
||
static void *davinci_ck_next(struct seq_file *m, void *v, loff_t *pos) | ||
{ | ||
++*pos; | ||
return NULL; | ||
} | ||
|
||
static void davinci_ck_stop(struct seq_file *m, void *v) | ||
{ | ||
} | ||
|
||
static int davinci_ck_show(struct seq_file *m, void *v) | ||
{ | ||
struct clk *cp; | ||
|
||
list_for_each_entry(cp, &clocks, node) | ||
seq_printf(m,"%s %d %d\n", cp->name, *(cp->rate), cp->usecount); | ||
|
||
return 0; | ||
} | ||
|
||
static struct seq_operations davinci_ck_op = { | ||
.start = davinci_ck_start, | ||
.next = davinci_ck_next, | ||
.stop = davinci_ck_stop, | ||
.show = davinci_ck_show | ||
}; | ||
|
||
static int davinci_ck_open(struct inode *inode, struct file *file) | ||
{ | ||
return seq_open(file, &davinci_ck_op); | ||
} | ||
|
||
static struct file_operations proc_davinci_ck_operations = { | ||
.open = davinci_ck_open, | ||
.read = seq_read, | ||
.llseek = seq_lseek, | ||
.release = seq_release, | ||
}; | ||
|
||
static int __init davinci_ck_proc_init(void) | ||
{ | ||
struct proc_dir_entry *entry; | ||
|
||
entry = create_proc_entry("davinci_clocks", 0, NULL); | ||
if (entry) | ||
entry->proc_fops = &proc_davinci_ck_operations; | ||
return 0; | ||
|
||
} | ||
__initcall(davinci_ck_proc_init); | ||
#endif /* CONFIG_DEBUG_PROC_FS */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* TI DaVinci clock definitions | ||
* | ||
* Copyright (C) 2006 Texas Instruments. | ||
* | ||
* 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 __ARCH_ARM_DAVINCI_CLOCK_H | ||
#define __ARCH_ARM_DAVINCI_CLOCK_H | ||
|
||
struct clk { | ||
struct list_head node; | ||
struct module *owner; | ||
const char *name; | ||
unsigned int *rate; | ||
int id; | ||
__s8 usecount; | ||
__u8 flags; | ||
__u8 lpsc; | ||
}; | ||
|
||
/* Clock flags */ | ||
#define RATE_CKCTL 1 | ||
#define RATE_FIXED 2 | ||
#define RATE_PROPAGATES 4 | ||
#define VIRTUAL_CLOCK 8 | ||
#define ALWAYS_ENABLED 16 | ||
#define ENABLE_REG_32BIT 32 | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.