diff options
-rw-r--r-- | arch/arm/mach-s3c2410/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c2440/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/clock-dclk.c | 194 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/clock.c | 171 |
6 files changed, 204 insertions, 171 deletions
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 5453aff60486..63a30d1dd425 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -80,6 +80,7 @@ config ARCH_BAST select PM_SIMTEC if PM select SIMTEC_NOR select MACH_BAST_IDE + select S3C24XX_DCLK select ISA help Say Y here if you are using the Simtec Electronics EB2410ITX @@ -117,6 +118,7 @@ config MACH_TCT_HAMMER config MACH_VR1000 bool "Thorcom VR1000" select PM_SIMTEC if PM + select S3C24XX_DCLK select SIMTEC_NOR select MACH_BAST_IDE select CPU_S3C2410 diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index 0429d255b0d8..57b9c57ff2b4 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig @@ -29,6 +29,7 @@ menu "S3C2440 Machines" config MACH_ANUBIS bool "Simtec Electronics ANUBIS" select CPU_S3C2440 + select S3C24XX_DCLK select PM_SIMTEC if PM select HAVE_PATA_PLATFORM help @@ -38,6 +39,7 @@ config MACH_ANUBIS config MACH_OSIRIS bool "Simtec IM2440D20 (OSIRIS) module" select CPU_S3C2440 + select S3C24XX_DCLK select PM_SIMTEC if PM help Say Y here if you are using the Simtec IM2440D20 module, also diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 2bc4b10f5227..d3faf01bbfc2 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -23,6 +23,11 @@ config S3C2410_CLOCK Clock code for the S3C2410, and similar processors which is currently includes the S3C2410, S3C2440, S3C2442. +config S3C24XX_DCLK + bool + help + Clock code for supporting DCLK/CLKOUT on S3C24XX architectures + config CPU_S3C244X bool depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 095a6c2324da..80cf8eb211ff 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -20,6 +20,7 @@ obj-y += gpiolib.o obj-y += time.o obj-y += clock.o obj-y += pwm-clock.o +obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o # Architecture dependant builds diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c new file mode 100644 index 000000000000..5b75a797b5ab --- /dev/null +++ b/arch/arm/plat-s3c24xx/clock-dclk.c @@ -0,0 +1,194 @@ +/* linux/arch/arm/plat-s3c24xx/clock-dclk.c + * + * Copyright (c) 2004,2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.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 + * published by the Free Software Foundation. + * + * S3C24XX - definitions for DCLK and CLKOUT registers + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> + +#include <plat/clock.h> +#include <plat/cpu.h> + +/* clocks that could be registered by external code */ + +static int s3c24xx_dclk_enable(struct clk *clk, int enable) +{ + unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (enable) + dclkcon |= clk->ctrlbit; + else + dclkcon &= ~clk->ctrlbit; + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} + +static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long dclkcon; + unsigned int uclk; + + if (parent == &clk_upll) + uclk = 1; + else if (parent == &clk_p) + uclk = 0; + else + return -EINVAL; + + clk->parent = parent; + + dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; + } else { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; + } + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} +static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) +{ + unsigned long div; + + if ((rate == 0) || !clk->parent) + return 0; + + div = clk_get_rate(clk->parent) / rate; + if (div < 2) + div = 2; + else if (div > 16) + div = 16; + + return div; +} + +static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return 0; + + return clk_get_rate(clk->parent) / div; +} + +static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) +{ + unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return -EINVAL; + + if (clk == &s3c24xx_dclk0) { + mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | + S3C2410_DCLKCON_DCLK0_CMP_MASK; + data = S3C2410_DCLKCON_DCLK0_DIV(div) | + S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); + } else if (clk == &s3c24xx_dclk1) { + mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | + S3C2410_DCLKCON_DCLK1_CMP_MASK; + data = S3C2410_DCLKCON_DCLK1_DIV(div) | + S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); + } else + return -EINVAL; + + clk->rate = clk_get_rate(clk->parent) / div; + __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), + S3C24XX_DCLKCON); + return clk->rate; +} +static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long mask; + unsigned long source; + + /* calculate the MISCCR setting for the clock */ + + if (parent == &clk_xtal) + source = S3C2410_MISCCR_CLK0_MPLL; + else if (parent == &clk_upll) + source = S3C2410_MISCCR_CLK0_UPLL; + else if (parent == &clk_f) + source = S3C2410_MISCCR_CLK0_FCLK; + else if (parent == &clk_h) + source = S3C2410_MISCCR_CLK0_HCLK; + else if (parent == &clk_p) + source = S3C2410_MISCCR_CLK0_PCLK; + else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) + source = S3C2410_MISCCR_CLK0_DCLK0; + else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) + source = S3C2410_MISCCR_CLK0_DCLK0; + else + return -EINVAL; + + clk->parent = parent; + + if (clk == &s3c24xx_clkout0) + mask = S3C2410_MISCCR_CLK0_MASK; + else { + source <<= 4; + mask = S3C2410_MISCCR_CLK1_MASK; + } + + s3c2410_modify_misccr(mask, source); + return 0; +} + +/* external clock definitions */ + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK1EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index a005ddbd9ef3..bf2633bd3996 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -283,178 +283,7 @@ struct clk clk_usb_bus = { .parent = &clk_upll, }; -/* clocks that could be registered by external code */ -static int s3c24xx_dclk_enable(struct clk *clk, int enable) -{ - unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); - - if (enable) - dclkcon |= clk->ctrlbit; - else - dclkcon &= ~clk->ctrlbit; - - __raw_writel(dclkcon, S3C24XX_DCLKCON); - - return 0; -} - -static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) -{ - unsigned long dclkcon; - unsigned int uclk; - - if (parent == &clk_upll) - uclk = 1; - else if (parent == &clk_p) - uclk = 0; - else - return -EINVAL; - - clk->parent = parent; - - dclkcon = __raw_readl(S3C24XX_DCLKCON); - - if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { - if (uclk) - dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; - else - dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; - } else { - if (uclk) - dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; - else - dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; - } - - __raw_writel(dclkcon, S3C24XX_DCLKCON); - - return 0; -} - -static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) -{ - unsigned long div; - - if ((rate == 0) || !clk->parent) - return 0; - - div = clk_get_rate(clk->parent) / rate; - if (div < 2) - div = 2; - else if (div > 16) - div = 16; - - return div; -} - -static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, - unsigned long rate) -{ - unsigned long div = s3c24xx_calc_div(clk, rate); - - if (div == 0) - return 0; - - return clk_get_rate(clk->parent) / div; -} - -static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) -{ - unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); - - if (div == 0) - return -EINVAL; - - if (clk == &s3c24xx_dclk0) { - mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | - S3C2410_DCLKCON_DCLK0_CMP_MASK; - data = S3C2410_DCLKCON_DCLK0_DIV(div) | - S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); - } else if (clk == &s3c24xx_dclk1) { - mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | - S3C2410_DCLKCON_DCLK1_CMP_MASK; - data = S3C2410_DCLKCON_DCLK1_DIV(div) | - S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); - } else - return -EINVAL; - - clk->rate = clk_get_rate(clk->parent) / div; - __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), - S3C24XX_DCLKCON); - return clk->rate; -} - -static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) -{ - unsigned long mask; - unsigned long source; - - /* calculate the MISCCR setting for the clock */ - - if (parent == &clk_xtal) - source = S3C2410_MISCCR_CLK0_MPLL; - else if (parent == &clk_upll) - source = S3C2410_MISCCR_CLK0_UPLL; - else if (parent == &clk_f) - source = S3C2410_MISCCR_CLK0_FCLK; - else if (parent == &clk_h) - source = S3C2410_MISCCR_CLK0_HCLK; - else if (parent == &clk_p) - source = S3C2410_MISCCR_CLK0_PCLK; - else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) - source = S3C2410_MISCCR_CLK0_DCLK0; - else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) - source = S3C2410_MISCCR_CLK0_DCLK0; - else - return -EINVAL; - - clk->parent = parent; - - if (clk == &s3c24xx_clkout0) - mask = S3C2410_MISCCR_CLK0_MASK; - else { - source <<= 4; - mask = S3C2410_MISCCR_CLK1_MASK; - } - - s3c2410_modify_misccr(mask, source); - return 0; -} - -/* external clock definitions */ - -struct clk s3c24xx_dclk0 = { - .name = "dclk0", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK0EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, -}; - -struct clk s3c24xx_dclk1 = { - .name = "dclk1", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK1EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, -}; - -struct clk s3c24xx_clkout0 = { - .name = "clkout0", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, -}; - -struct clk s3c24xx_clkout1 = { - .name = "clkout1", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, -}; struct clk s3c24xx_uclk = { .name = "uclk", |