Commit 043230a4 authored by Ben Dooks's avatar Ben Dooks Committed by Russell King

[ARM PATCH] 2172/1: S3C2410 - clock updates

Patch from Ben Dooks

This patch does the following:

- move the clock initialisation to just after the cpu init
- add the two clocks for the s3c2440
- add dclk0/1 and clkout0/1 definitions
- add list of clocks to the board specific struct
- fix initialisation of the clock control
- update handling of parent clocks for >1 parent

this fixes the following problems:

- serial console with no clock sources
- clocks specific to various boards
- hang if LCD controller enabled at start time

Signed-off-by: Ben Dooks 
parent 3e669d8c
...@@ -59,7 +59,7 @@ static DECLARE_MUTEX(clocks_sem); ...@@ -59,7 +59,7 @@ static DECLARE_MUTEX(clocks_sem);
/* old functions */ /* old functions */
void s3c2410_clk_enable(unsigned int clocks, unsigned int enable) void inline s3c2410_clk_enable(unsigned int clocks, unsigned int enable)
{ {
unsigned long clkcon; unsigned long clkcon;
unsigned long flags; unsigned long flags;
...@@ -72,11 +72,26 @@ void s3c2410_clk_enable(unsigned int clocks, unsigned int enable) ...@@ -72,11 +72,26 @@ void s3c2410_clk_enable(unsigned int clocks, unsigned int enable)
if (enable) if (enable)
clkcon |= clocks; clkcon |= clocks;
/* ensure none of the special function bits set */
clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
__raw_writel(clkcon, S3C2410_CLKCON); __raw_writel(clkcon, S3C2410_CLKCON);
local_irq_restore(flags); local_irq_restore(flags);
} }
/* enable and disable calls for use with the clk struct */
static int clk_null_enable(struct clk *clk, int enable)
{
return 0;
}
int s3c2410_clkcon_enable(struct clk *clk, int enable)
{
s3c2410_clk_enable(clk->ctrlbit, enable);
return 0;
}
/* Clock API calls */ /* Clock API calls */
...@@ -105,15 +120,16 @@ void clk_put(struct clk *clk) ...@@ -105,15 +120,16 @@ void clk_put(struct clk *clk)
int clk_enable(struct clk *clk) int clk_enable(struct clk *clk)
{ {
if (clk->ctrlbit != 0) if (IS_ERR(clk))
s3c2410_clk_enable(clk->ctrlbit, 1); return -EINVAL;
return 0; return (clk->enable)(clk, 1);
} }
void clk_disable(struct clk *clk) void clk_disable(struct clk *clk)
{ {
s3c2410_clk_enable(clk->ctrlbit, 0); if (!IS_ERR(clk))
(clk->enable)(clk, 0);
} }
...@@ -131,8 +147,11 @@ void clk_unuse(struct clk *clk) ...@@ -131,8 +147,11 @@ void clk_unuse(struct clk *clk)
unsigned long clk_get_rate(struct clk *clk) unsigned long clk_get_rate(struct clk *clk)
{ {
if (clk->parent != NULL) if (clk->rate != 0)
return clk->parent->rate; return clk->rate;
while (clk->parent != NULL && clk->rate == 0)
clk = clk->parent;
return clk->rate; return clk->rate;
} }
...@@ -186,67 +205,105 @@ static struct clk clk_p = { ...@@ -186,67 +205,105 @@ static struct clk clk_p = {
.ctrlbit = 0 .ctrlbit = 0
}; };
/* clocks that could be registered by external code */
struct clk s3c24xx_dclk0 = {
.name = "dclk0",
};
struct clk s3c24xx_dclk1 = {
.name = "dclk1",
};
struct clk s3c24xx_clkout0 = {
.name = "clkout1",
};
struct clk s3c24xx_clkout1 = {
.name = "clkout1",
};
struct clk s3c24xx_uclk = {
.name = "uclk",
};
/* clock definitions */ /* clock definitions */
static struct clk init_clocks[] = { static struct clk init_clocks[] = {
{ .name = "nand", { .name = "nand",
.parent = &clk_h, .parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_NAND .ctrlbit = S3C2410_CLKCON_NAND
}, },
{ .name = "lcd", { .name = "lcd",
.parent = &clk_h, .parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_LCDC .ctrlbit = S3C2410_CLKCON_LCDC
}, },
{ .name = "usb-host", { .name = "usb-host",
.parent = &clk_h, .parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBH .ctrlbit = S3C2410_CLKCON_USBH
}, },
{ .name = "usb-device", { .name = "usb-device",
.parent = &clk_h, .parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBD .ctrlbit = S3C2410_CLKCON_USBD
}, },
{ .name = "timers", { .name = "timers",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_PWMT .ctrlbit = S3C2410_CLKCON_PWMT
}, },
{ .name = "sdi", { .name = "sdi",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SDI .ctrlbit = S3C2410_CLKCON_SDI
}, },
{ .name = "uart0", { .name = "uart0",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART0 .ctrlbit = S3C2410_CLKCON_UART0
}, },
{ .name = "uart1", { .name = "uart1",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART1 .ctrlbit = S3C2410_CLKCON_UART1
}, },
{ .name = "uart2", { .name = "uart2",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART2 .ctrlbit = S3C2410_CLKCON_UART2
}, },
{ .name = "gpio", { .name = "gpio",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_GPIO .ctrlbit = S3C2410_CLKCON_GPIO
}, },
{ .name = "rtc", { .name = "rtc",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_RTC .ctrlbit = S3C2410_CLKCON_RTC
}, },
{ .name = "adc", { .name = "adc",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_ADC .ctrlbit = S3C2410_CLKCON_ADC
}, },
{ .name = "i2c", { .name = "i2c",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIC .ctrlbit = S3C2410_CLKCON_IIC
}, },
{ .name = "iis", { .name = "iis",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIS .ctrlbit = S3C2410_CLKCON_IIS
}, },
{ .name = "spi", { .name = "spi",
.parent = &clk_p, .parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SPI .ctrlbit = S3C2410_CLKCON_SPI
}, },
{ .name = "watchdog", { .name = "watchdog",
...@@ -262,6 +319,9 @@ int s3c2410_register_clock(struct clk *clk) ...@@ -262,6 +319,9 @@ int s3c2410_register_clock(struct clk *clk)
clk->owner = THIS_MODULE; clk->owner = THIS_MODULE;
atomic_set(&clk->used, 0); atomic_set(&clk->used, 0);
if (clk->enable == NULL)
clk->enable = clk_null_enable;
/* add to the list of available clocks */ /* add to the list of available clocks */
down(&clocks_sem); down(&clocks_sem);
...@@ -273,7 +333,7 @@ int s3c2410_register_clock(struct clk *clk) ...@@ -273,7 +333,7 @@ int s3c2410_register_clock(struct clk *clk)
/* initalise all the clocks */ /* initalise all the clocks */
static int __init s3c2410_init_clocks(void) int __init s3c2410_init_clocks(void)
{ {
struct clk *clkp = init_clocks; struct clk *clkp = init_clocks;
int ptr; int ptr;
...@@ -287,8 +347,25 @@ static int __init s3c2410_init_clocks(void) ...@@ -287,8 +347,25 @@ static int __init s3c2410_init_clocks(void)
clk_p.rate = s3c24xx_pclk; clk_p.rate = s3c24xx_pclk;
clk_f.rate = s3c24xx_fclk; clk_f.rate = s3c24xx_fclk;
/* set the enabled clocks to a minimal (known) state */ /* it looks like just setting the register here is not good
__raw_writel(S3C2410_CLKCON_PWMT | S3C2410_CLKCON_UART0 | S3C2410_CLKCON_UART1 | S3C2410_CLKCON_UART2 | S3C2410_CLKCON_GPIO | S3C2410_CLKCON_RTC, S3C2410_CLKCON); * enough, and causes the odd hang at initial boot time, so
* do all of them indivdually.
*
* I think disabling the LCD clock if the LCD is active is
* very dangerous, and therefore the bootloader should be
* careful to not enable the LCD clock if it is not needed.
*
* and of course, this looks neater
*/
s3c2410_clk_enable(S3C2410_CLKCON_NAND, 0);
s3c2410_clk_enable(S3C2410_CLKCON_USBH, 0);
s3c2410_clk_enable(S3C2410_CLKCON_USBD, 0);
s3c2410_clk_enable(S3C2410_CLKCON_ADC, 0);
s3c2410_clk_enable(S3C2410_CLKCON_IIC, 0);
s3c2410_clk_enable(S3C2410_CLKCON_SPI, 0);
/* assume uart clocks are correctly setup */
/* register our clocks */ /* register our clocks */
...@@ -312,5 +389,4 @@ static int __init s3c2410_init_clocks(void) ...@@ -312,5 +389,4 @@ static int __init s3c2410_init_clocks(void)
return 0; return 0;
} }
arch_initcall(s3c2410_init_clocks);
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* linux/arch/arm/mach-s3c2410/clock.h * linux/arch/arm/mach-s3c2410/clock.h
* *
* Copyright (c) 2004 Simtec Electronics * Copyright (c) 2004 Simtec Electronics
* Written by Ben Dooks, <ben@simtec.co.uk> * Written by Ben Dooks, <ben@simtec.co.uk>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -17,11 +17,30 @@ struct clk { ...@@ -17,11 +17,30 @@ struct clk {
atomic_t used; atomic_t used;
unsigned long rate; unsigned long rate;
unsigned long ctrlbit; unsigned long ctrlbit;
int (*enable)(struct clk *, int enable);
}; };
/* other clocks which may be registered by board support */
extern struct clk s3c24xx_dclk0;
extern struct clk s3c24xx_dclk1;
extern struct clk s3c24xx_clkout0;
extern struct clk s3c24xx_clkout1;
extern struct clk s3c24xx_uclk;
/* processor clock settings, in Hz */ /* processor clock settings, in Hz */
extern unsigned long s3c24xx_xtal; extern unsigned long s3c24xx_xtal;
extern unsigned long s3c24xx_pclk; extern unsigned long s3c24xx_pclk;
extern unsigned long s3c24xx_hclk; extern unsigned long s3c24xx_hclk;
extern unsigned long s3c24xx_fclk; extern unsigned long s3c24xx_fclk;
/* exports for arch/arm/mach-s3c2410
*
* Please DO NOT use these outside of arch/arm/mach-s3c2410
*/
extern int s3c2410_clkcon_enable(struct clk *clk, int enable);
extern int s3c2410_register_clock(struct clk *clk);
extern int s3c2410_init_clocks(void);
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <asm/arch/regs-gpio.h> #include <asm/arch/regs-gpio.h>
#include "cpu.h" #include "cpu.h"
#include "clock.h"
#include "s3c2410.h" #include "s3c2410.h"
#include "s3c2440.h" #include "s3c2440.h"
...@@ -118,7 +119,16 @@ static struct s3c24xx_board *board; ...@@ -118,7 +119,16 @@ static struct s3c24xx_board *board;
void s3c24xx_set_board(struct s3c24xx_board *b) void s3c24xx_set_board(struct s3c24xx_board *b)
{ {
int i;
board = b; board = b;
if (b->clocks_count != 0) {
struct clk **ptr = b->clocks;;
for (i = b->clocks_count; i > 0; i--, ptr++)
s3c2410_register_clock(*ptr);
}
} }
/* cpu information */ /* cpu information */
......
...@@ -49,6 +49,9 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); ...@@ -49,6 +49,9 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
struct s3c24xx_board { struct s3c24xx_board {
struct platform_device **devices; struct platform_device **devices;
unsigned int devices_count; unsigned int devices_count;
struct clk **clocks;
unsigned int clocks_count;
}; };
extern void s3c24xx_set_board(struct s3c24xx_board *board); extern void s3c24xx_set_board(struct s3c24xx_board *board);
......
...@@ -189,6 +189,12 @@ void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size) ...@@ -189,6 +189,12 @@ void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size)
printk("S3C2410: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", printk("S3C2410: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
print_mhz(s3c24xx_fclk), print_mhz(s3c24xx_hclk), print_mhz(s3c24xx_fclk), print_mhz(s3c24xx_hclk),
print_mhz(s3c24xx_pclk)); print_mhz(s3c24xx_pclk));
/* initialise the clocks here, to allow other things like the
* console to use them
*/
s3c2410_init_clocks();
} }
int __init s3c2410_init(void) int __init s3c2410_init(void)
......
...@@ -120,6 +120,20 @@ static struct platform_device *uart_devices[] __initdata = { ...@@ -120,6 +120,20 @@ static struct platform_device *uart_devices[] __initdata = {
&s3c_uart2 &s3c_uart2
}; };
/* s3c2440 specific clock sources */
static struct clk s3c2440_clk_cam = {
.name = "camera",
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2440_CLKCON_CAMERA
};
static struct clk s3c2440_clk_ac97 = {
.name = "ac97",
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2440_CLKCON_CAMERA
};
void __init s3c2440_map_io(struct map_desc *mach_desc, int size) void __init s3c2440_map_io(struct map_desc *mach_desc, int size)
{ {
unsigned long clkdiv; unsigned long clkdiv;
...@@ -167,6 +181,23 @@ void __init s3c2440_map_io(struct map_desc *mach_desc, int size) ...@@ -167,6 +181,23 @@ void __init s3c2440_map_io(struct map_desc *mach_desc, int size)
printk("S3C2440: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", printk("S3C2440: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
print_mhz(s3c24xx_fclk), print_mhz(s3c24xx_hclk), print_mhz(s3c24xx_fclk), print_mhz(s3c24xx_hclk),
print_mhz(s3c24xx_pclk)); print_mhz(s3c24xx_pclk));
/* initialise the clocks here, to allow other things like the
* console to use them, and to add new ones after the initialisation
*/
s3c2410_init_clocks();
/* add s3c2440 specific clocks */
s3c2440_clk_cam.parent = clk_get(NULL, "hclk");
s3c2440_clk_ac97.parent = clk_get(NULL, "pclk");
s3c2410_register_clock(&clk_ac97);
s3c2410_register_clock(&clk_cam);
clk_disable(&clk_ac97);
clk_disable(&clk_cam);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment