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);
/* 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 flags;
......@@ -72,11 +72,26 @@ void s3c2410_clk_enable(unsigned int clocks, unsigned int enable)
if (enable)
clkcon |= clocks;
/* ensure none of the special function bits set */
clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
__raw_writel(clkcon, S3C2410_CLKCON);
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 */
......@@ -105,15 +120,16 @@ void clk_put(struct clk *clk)
int clk_enable(struct clk *clk)
{
if (clk->ctrlbit != 0)
s3c2410_clk_enable(clk->ctrlbit, 1);
if (IS_ERR(clk))
return -EINVAL;
return 0;
return (clk->enable)(clk, 1);
}
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)
unsigned long clk_get_rate(struct clk *clk)
{
if (clk->parent != NULL)
return clk->parent->rate;
if (clk->rate != 0)
return clk->rate;
while (clk->parent != NULL && clk->rate == 0)
clk = clk->parent;
return clk->rate;
}
......@@ -186,67 +205,105 @@ static struct clk clk_p = {
.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 */
static struct clk init_clocks[] = {
{ .name = "nand",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_NAND
},
{ .name = "lcd",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_LCDC
},
{ .name = "usb-host",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBH
},
{ .name = "usb-device",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBD
},
{ .name = "timers",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_PWMT
},
{ .name = "sdi",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SDI
},
{ .name = "uart0",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART0
},
{ .name = "uart1",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART1
},
{ .name = "uart2",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_UART2
},
{ .name = "gpio",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_GPIO
},
{ .name = "rtc",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_RTC
},
{ .name = "adc",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_ADC
},
{ .name = "i2c",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIC
},
{ .name = "iis",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIS
},
{ .name = "spi",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SPI
},
{ .name = "watchdog",
......@@ -262,6 +319,9 @@ int s3c2410_register_clock(struct clk *clk)
clk->owner = THIS_MODULE;
atomic_set(&clk->used, 0);
if (clk->enable == NULL)
clk->enable = clk_null_enable;
/* add to the list of available clocks */
down(&clocks_sem);
......@@ -273,7 +333,7 @@ int s3c2410_register_clock(struct clk *clk)
/* initalise all the clocks */
static int __init s3c2410_init_clocks(void)
int __init s3c2410_init_clocks(void)
{
struct clk *clkp = init_clocks;
int ptr;
......@@ -287,8 +347,25 @@ static int __init s3c2410_init_clocks(void)
clk_p.rate = s3c24xx_pclk;
clk_f.rate = s3c24xx_fclk;
/* set the enabled clocks to a minimal (known) state */
__raw_writel(S3C2410_CLKCON_PWMT | S3C2410_CLKCON_UART0 | S3C2410_CLKCON_UART1 | S3C2410_CLKCON_UART2 | S3C2410_CLKCON_GPIO | S3C2410_CLKCON_RTC, S3C2410_CLKCON);
/* it looks like just setting the register here is not good
* 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 */
......@@ -312,5 +389,4 @@ static int __init s3c2410_init_clocks(void)
return 0;
}
arch_initcall(s3c2410_init_clocks);
......@@ -2,7 +2,7 @@
* linux/arch/arm/mach-s3c2410/clock.h
*
* 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
* it under the terms of the GNU General Public License version 2 as
......@@ -17,11 +17,30 @@ struct clk {
atomic_t used;
unsigned long rate;
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 */
extern unsigned long s3c24xx_xtal;
extern unsigned long s3c24xx_pclk;
extern unsigned long s3c24xx_hclk;
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 @@
#include <asm/arch/regs-gpio.h>
#include "cpu.h"
#include "clock.h"
#include "s3c2410.h"
#include "s3c2440.h"
......@@ -118,7 +119,16 @@ static struct s3c24xx_board *board;
void s3c24xx_set_board(struct s3c24xx_board *b)
{
int i;
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 */
......
......@@ -49,6 +49,9 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
struct s3c24xx_board {
struct platform_device **devices;
unsigned int devices_count;
struct clk **clocks;
unsigned int clocks_count;
};
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)
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_pclk));
/* initialise the clocks here, to allow other things like the
* console to use them
*/
s3c2410_init_clocks();
}
int __init s3c2410_init(void)
......
......@@ -120,6 +120,20 @@ static struct platform_device *uart_devices[] __initdata = {
&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)
{
unsigned long clkdiv;
......@@ -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",
print_mhz(s3c24xx_fclk), print_mhz(s3c24xx_hclk),
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