[PATCH] ep93xx: add ssp clocks | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] | |
Hello all,
I sent this patch previously to the wrong list (linux-arm). Hopefully
this is the correct list.
This patch adds the Synchronous Serial Port clocks to the clock control
for Cirrus EP93xx chips and initializes the rate for the base clock
based on the EP93xx silicon revision. It also adds the clk_set_rate
interface to allow setting the actual output SPI clock speed
(sspclkout).
A /proc/clocks interface is also added to output the clocks information.
Signed-off-by: H Hartley Sweeten <hsweeten@xxxxxxxxxxxxxxxxxxx>
--- orig/linux-2.6.25.10/include/asm-arm/arch-ep93xx/ep93xx-regs.h
2008-07-02 20:46:47.000000000 -0700
+++
/home/bigguiness/buildroot/project_build_arm/ep9307/linux-2.6.25.10/incl
ude/asm-arm/arch-ep93xx/ep93xx-regs.h 2008-07-23 09:48:30.000000000
-0700
@@ -92,6 +92,11 @@
#define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE +
0x00080000)
#define EP93XX_SPI_BASE (EP93XX_APB_VIRT_BASE +
0x000a0000)
+#define EP93XX_SPI_REG(x) (EP93XX_SPI_BASE + (x))
+#define EP93XX_SPI_SSPCR0 EP93XX_SPI_REG(0x00)
+#define EP93XX_SPI_SSPCR0_SCR_MASK (0x0000ff00)
+#define EP93XX_SPI_SSPCR0_SCR_SHIFT (8)
+#define EP93XX_SPI_SSPCPSR EP93XX_SPI_REG(0x10)
#define EP93XX_IRDA_BASE (EP93XX_APB_VIRT_BASE +
0x000b0000)
@@ -125,6 +130,7 @@
#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24)
#define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80)
#define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000
+#define EP93XX_SYSCON_CHIP_ID EP93XX_SYSCON_REG(0x94)
#define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0)
#define EP93XX_WATCHDOG_BASE (EP93XX_APB_VIRT_BASE +
0x00140000)
--- orig/linux-2.6.25.10/arch/arm/mach-ep93xx/clock.c 2008-07-02
20:46:47.000000000 -0700
+++
/home/bigguiness/buildroot/project_build_arm/ep9307/linux-2.6.25.10/arch
/arm/mach-ep93xx/clock.c 2008-07-23 09:40:01.000000000 -0700
@@ -15,10 +15,13 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/string.h>
+#include <linux/proc_fs.h>
#include <asm/div64.h>
#include <asm/hardware.h>
#include <asm/io.h>
+#define EP93XX_EXT_CLK_RATE 14745600
+
struct clk {
char *name;
unsigned long rate;
@@ -29,7 +32,7 @@ struct clk {
static struct clk clk_uart = {
.name = "UARTCLK",
- .rate = 14745600,
+ .rate = EP93XX_EXT_CLK_RATE,
};
static struct clk clk_pll1 = {
.name = "pll1",
@@ -51,6 +54,12 @@ static struct clk clk_usb_host = {
.enable_reg = EP93XX_SYSCON_CLOCK_CONTROL,
.enable_mask = EP93XX_SYSCON_CLOCK_USH_EN,
};
+static struct clk clk_ssp = {
+ .name = "sspclk",
+};
+static struct clk clk_sspclkout = {
+ .name = "sspclkout",
+};
static struct clk *clocks[] = {
@@ -61,6 +70,8 @@ static struct clk *clocks[] = {
&clk_p,
&clk_pll2,
&clk_usb_host,
+ &clk_ssp,
+ &clk_sspclkout,
};
struct clk *clk_get(struct device *dev, const char *id)
@@ -74,6 +85,7 @@ struct clk *clk_get(struct device *dev,
return ERR_PTR(-ENOENT);
}
+EXPORT_SYMBOL(clk_get);
int clk_enable(struct clk *clk)
{
@@ -86,6 +98,7 @@ int clk_enable(struct clk *clk)
return 0;
}
+EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
@@ -96,21 +109,96 @@ void clk_disable(struct clk *clk)
__raw_writel(value & ~clk->enable_mask,
clk->enable_reg);
}
}
+EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
+EXPORT_SYMBOL(clk_get_rate);
+
+static unsigned long
+clk_set_sclkout (unsigned long rate)
+{
+ unsigned long sspclk = clk_ssp.rate;
+ unsigned long sspclkout;
+ unsigned int cpsdvr, scr;
+ u32 value;
+
+ for (cpsdvr = 2; cpsdvr < 255; cpsdvr++) {
+ for (scr = 0; scr < 256; scr++) {
+ sspclkout = sspclk / (cpsdvr * (1 + scr));
+ if (sspclkout <= rate) {
+ __raw_writel(cpsdvr,
EP93XX_SPI_SSPCPSR);
+ value = __raw_readl(EP93XX_SPI_SSPCR0);
+ value &= EP93XX_SPI_SSPCR0_SCR_MASK;
+ value |= (scr <<
EP93XX_SPI_SSPCR0_SCR_SHIFT);
+ __raw_writel(value, EP93XX_SPI_SSPCR0);
+
+ return sspclkout;
+ }
+ }
+ }
+ return clk_sspclkout.rate;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if (strcmp(clk->name, "sspclkout") == 0) {
+ clk->rate = clk_set_sclkout(rate);
+ return 0;
+ }
+ return -1;
+}
+EXPORT_SYMBOL(clk_set_rate);
void clk_put(struct clk *clk)
{
}
+EXPORT_SYMBOL(clk_put);
+static int
+ep93xx_read_clocks_proc(char *buffer, char **start, off_t offset, int
size,
+ int *eof, void *data)
+{
+ int written = 0;
+ int i;
+
+ if (offset)
+ return 0;
+
+#define OUT(a,b...) written += snprintf(buffer + written, size -
written, a, ##b)
+
+ for (i = 0; i < ARRAY_SIZE(clocks); i++) {
+ struct clk *clk = clocks[i];
+ unsigned long rate = clk_get_rate(clk);
+
+ OUT("%3d %-12s\t: ", clk->users, clk->name);
+ if (rate > 1000000) {
+ OUT("%3ld.%06ld MHz\t",
+ rate / 1000000, rate % 1000000);
+ } else if (rate > 1000) {
+ OUT("%3ld.%03ld KHz\t\t",
+ rate / 1000, rate % 1000);
+ } else {
+ OUT("%3ld Hz\t\t", rate);
+ }
+ if (clk->enable_reg) {
+ if (!(__raw_readl(clk->enable_reg) &
clk->enable_mask))
+ OUT("(disabled)");
+ }
+ OUT("\n");
+ }
+
+ return written;
+}
+
static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
static char pclk_divisors[] = { 1, 2, 4, 8 };
+static char *ep93xx_rev[] = { "A", "B", "C", "D0", "D1", "E0", "E1",
"E2"};
/*
* PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) /
2^PS
@@ -120,7 +208,7 @@ static unsigned long calc_pll_rate(u32 c
unsigned long long rate;
int i;
- rate = 14745600;
+ rate = EP93XX_EXT_CLK_RATE;
rate *= ((config_word >> 11) & 0x1f) + 1; /* X1FBD
*/
rate *= ((config_word >> 5) & 0x3f) + 1; /* X2FBD
*/
do_div(rate, (config_word & 0x1f) + 1); /* X2IPD
*/
@@ -136,7 +224,7 @@ static int __init ep93xx_clock_init(void
value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1);
if (!(value & 0x00800000)) { /* PLL1
bypassed? */
- clk_pll1.rate = 14745600;
+ clk_pll1.rate = EP93XX_EXT_CLK_RATE;
} else {
clk_pll1.rate = calc_pll_rate(value);
}
@@ -146,7 +234,7 @@ static int __init ep93xx_clock_init(void
value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2);
if (!(value & 0x00080000)) { /* PLL2
bypassed? */
- clk_pll2.rate = 14745600;
+ clk_pll2.rate = EP93XX_EXT_CLK_RATE;
} else if (value & 0x00040000) { /* PLL2 enabled?
*/
clk_pll2.rate = calc_pll_rate(value);
} else {
@@ -154,11 +242,25 @@ static int __init ep93xx_clock_init(void
}
clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1);
+ /* See http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf */
+ value = __raw_readl(EP93XX_SYSCON_CHIP_ID);
+ value = (value >> 28) & 0xf;
+ if (value < 7)
+ clk_ssp.rate = EP93XX_EXT_CLK_RATE/2;
+ else
+ clk_ssp.rate = EP93XX_EXT_CLK_RATE;
+
printk(KERN_INFO "ep93xx: PLL1 running at %ld MHz, PLL2 at %ld
MHz\n",
clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
printk(KERN_INFO "ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld
MHz\n",
clk_f.rate / 1000000, clk_h.rate / 1000000,
clk_p.rate / 1000000);
+ if (value < 8)
+ printk(KERN_INFO "ep93xx: Silicon Rev %s\n",
ep93xx_rev[value]);
+ else
+ printk(KERN_INFO "ep93xx: Silicon Rev unknown (%d)\n",
value);
+
+ create_proc_read_entry("clocks", 0, 0, ep93xx_read_clocks_proc,
NULL);
return 0;
}
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
[Site Home] [Linux Arm] [Fedora ARM] [Gcc Help] [Git] [DCCP] [IETF Announce] [Security] [PDAs] [Linux] [Linux Book List] [Linux MIPS] [Yosemite Campsites] [Photos]
![]() |
|