diff options
Diffstat (limited to 'arch/arm/mach-s3c24xx/clock-s3c2410.c')
| -rw-r--r-- | arch/arm/mach-s3c24xx/clock-s3c2410.c | 251 | 
1 files changed, 251 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2410.c b/arch/arm/mach-s3c24xx/clock-s3c2410.c new file mode 100644 index 00000000000..34fffdf6fc1 --- /dev/null +++ b/arch/arm/mach-s3c24xx/clock-s3c2410.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2006 Simtec Electronics + *	Ben Dooks <ben@simtec.co.uk> + * + * S3C2410,S3C2440,S3C2442 Clock control support + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/serial_core.h> +#include <linux/io.h> + +#include <asm/mach/map.h> + +#include <mach/hardware.h> + +#include <plat/regs-serial.h> +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> + +#include <plat/clock.h> +#include <plat/cpu.h> + +int s3c2410_clkcon_enable(struct clk *clk, int enable) +{ +	unsigned int clocks = clk->ctrlbit; +	unsigned long clkcon; + +	clkcon = __raw_readl(S3C2410_CLKCON); + +	if (enable) +		clkcon |= clocks; +	else +		clkcon &= ~clocks; + +	/* ensure none of the special function bits set */ +	clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + +	__raw_writel(clkcon, S3C2410_CLKCON); + +	return 0; +} + +static int s3c2410_upll_enable(struct clk *clk, int enable) +{ +	unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); +	unsigned long orig = clkslow; + +	if (enable) +		clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; +	else +		clkslow |= S3C2410_CLKSLOW_UCLK_OFF; + +	__raw_writel(clkslow, S3C2410_CLKSLOW); + +	/* if we started the UPLL, then allow to settle */ + +	if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) +		udelay(200); + +	return 0; +} + +/* standard clock definitions */ + +static struct clk init_clocks_off[] = { +	{ +		.name		= "nand", +		.parent		= &clk_h, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_NAND, +	}, { +		.name		= "sdi", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_SDI, +	}, { +		.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, +	} +}; + +static struct clk init_clocks[] = { +	{ +		.name		= "lcd", +		.parent		= &clk_h, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_LCDC, +	}, { +		.name		= "gpio", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_GPIO, +	}, { +		.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		= "uart", +		.devname	= "s3c2410-uart.0", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_UART0, +	}, { +		.name		= "uart", +		.devname	= "s3c2410-uart.1", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_UART1, +	}, { +		.name		= "uart", +		.devname	= "s3c2410-uart.2", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_UART2, +	}, { +		.name		= "rtc", +		.parent		= &clk_p, +		.enable		= s3c2410_clkcon_enable, +		.ctrlbit	= S3C2410_CLKCON_RTC, +	}, { +		.name		= "watchdog", +		.parent		= &clk_p, +		.ctrlbit	= 0, +	}, { +		.name		= "usb-bus-host", +		.parent		= &clk_usb_bus, +	}, { +		.name		= "usb-bus-gadget", +		.parent		= &clk_usb_bus, +	}, +}; + +/* s3c2410_baseclk_add() + * + * Add all the clocks used by the s3c2410 or compatible CPUs + * such as the S3C2440 and S3C2442. + * + * We cannot use a system device as we are needed before any + * of the init-calls that initialise the devices are actually + * done. +*/ + +int __init s3c2410_baseclk_add(void) +{ +	unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); +	unsigned long clkcon  = __raw_readl(S3C2410_CLKCON); +	struct clk *clkp; +	struct clk *xtal; +	int ret; +	int ptr; + +	clk_upll.enable = s3c2410_upll_enable; + +	if (s3c24xx_register_clock(&clk_usb_bus) < 0) +		printk(KERN_ERR "failed to register usb bus clock\n"); + +	/* register clocks from clock array */ + +	clkp = init_clocks; +	for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { +		/* ensure that we note the clock state */ + +		clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; + +		ret = s3c24xx_register_clock(clkp); +		if (ret < 0) { +			printk(KERN_ERR "Failed to register clock %s (%d)\n", +			       clkp->name, ret); +		} +	} + +	/* We must be careful disabling the clocks we are not intending to +	 * be using at boot time, as subsystems such as the LCD which do +	 * their own DMA requests to the bus can cause the system to lockup +	 * if they where in the middle of requesting bus access. +	 * +	 * 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. +	*/ + +	/* install (and disable) the clocks we do not need immediately */ + +	s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); +	s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + +	/* show the clock-slow value */ + +	xtal = clk_get(NULL, "xtal"); + +	printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", +	       print_mhz(clk_get_rate(xtal) / +			 ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), +	       (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", +	       (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", +	       (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); + +	s3c_pwmclk_init(); +	return 0; +}  |