clock.c 6.58 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/* linux/arch/arm/mach-s3c2410/clock.c
 *
3
 * Copyright (c) 2006 Simtec Electronics
Linus Torvalds's avatar
Linus Torvalds committed
4 5
 *	Ben Dooks <ben@simtec.co.uk>
 *
6
 * S3C2410,S3C2440,S3C2442 Clock control support
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * 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/sysdev.h>
30
#include <linux/clk.h>
31
#include <linux/mutex.h>
32
#include <linux/delay.h>
33
#include <linux/serial_core.h>
34
#include <linux/io.h>
35 36

#include <asm/mach/map.h>
Linus Torvalds's avatar
Linus Torvalds committed
37

38
#include <mach/hardware.h>
Linus Torvalds's avatar
Linus Torvalds committed
39

40
#include <asm/plat-s3c/regs-serial.h>
41 42
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
Linus Torvalds's avatar
Linus Torvalds committed
43

44 45 46
#include <asm/plat-s3c24xx/s3c2410.h>
#include <asm/plat-s3c24xx/clock.h>
#include <asm/plat-s3c24xx/cpu.h>
Linus Torvalds's avatar
Linus Torvalds committed
47

48
int s3c2410_clkcon_enable(struct clk *clk, int enable)
Linus Torvalds's avatar
Linus Torvalds committed
49
{
50 51
	unsigned int clocks = clk->ctrlbit;
	unsigned long clkcon;
Linus Torvalds's avatar
Linus Torvalds committed
52

53
	clkcon = __raw_readl(S3C2410_CLKCON);
Linus Torvalds's avatar
Linus Torvalds committed
54

55 56
	if (enable)
		clkcon |= clocks;
57
	else
58
		clkcon &= ~clocks;
Linus Torvalds's avatar
Linus Torvalds committed
59

60 61
	/* ensure none of the special function bits set */
	clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
Linus Torvalds's avatar
Linus Torvalds committed
62

63
	__raw_writel(clkcon, S3C2410_CLKCON);
Linus Torvalds's avatar
Linus Torvalds committed
64

65
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
66 67
}

68
static int s3c2410_upll_enable(struct clk *clk, int enable)
Linus Torvalds's avatar
Linus Torvalds committed
69
{
70 71
	unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
	unsigned long orig = clkslow;
72 73

	if (enable)
74
		clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
75
	else
76
		clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
77

78
	__raw_writel(clkslow, S3C2410_CLKSLOW);
79

80
	/* if we started the UPLL, then allow to settle */
81

82 83
	if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
		udelay(200);
84 85 86 87

	return 0;
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
/* standard clock definitions */

static struct clk init_clocks_disable[] = {
	{
		.name		= "nand",
		.id		= -1,
		.parent		= &clk_h,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_NAND,
	}, {
		.name		= "sdi",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_SDI,
	}, {
		.name		= "adc",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_ADC,
	}, {
		.name		= "i2c",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_IIC,
	}, {
		.name		= "iis",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_IIS,
	}, {
		.name		= "spi",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_SPI,
127
	}
Linus Torvalds's avatar
Linus Torvalds committed
128 129
};

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
static struct clk init_clocks[] = {
	{
		.name		= "lcd",
		.id		= -1,
		.parent		= &clk_h,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_LCDC,
	}, {
		.name		= "gpio",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_GPIO,
	}, {
		.name		= "usb-host",
		.id		= -1,
		.parent		= &clk_h,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_USBH,
	}, {
		.name		= "usb-device",
		.id		= -1,
		.parent		= &clk_h,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_USBD,
	}, {
		.name		= "timers",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_PWMT,
	}, {
		.name		= "uart",
		.id		= 0,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_UART0,
	}, {
		.name		= "uart",
		.id		= 1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_UART1,
	}, {
		.name		= "uart",
		.id		= 2,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_UART2,
	}, {
		.name		= "rtc",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_RTC,
	}, {
		.name		= "watchdog",
		.id		= -1,
		.parent		= &clk_p,
		.ctrlbit	= 0,
	}, {
		.name		= "usb-bus-host",
		.id		= -1,
		.parent		= &clk_usb_bus,
	}, {
		.name		= "usb-bus-gadget",
		.id		= -1,
		.parent		= &clk_usb_bus,
	},
Linus Torvalds's avatar
Linus Torvalds committed
199 200
};

201 202 203 204 205 206 207 208 209
/* 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.
*/
Linus Torvalds's avatar
Linus Torvalds committed
210

211
int __init s3c2410_baseclk_add(void)
Linus Torvalds's avatar
Linus Torvalds committed
212
{
213 214 215 216 217 218
	unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
	unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
	struct clk *clkp;
	struct clk *xtal;
	int ret;
	int ptr;
Linus Torvalds's avatar
Linus Torvalds committed
219

220
	clk_upll.enable = s3c2410_upll_enable;
Linus Torvalds's avatar
Linus Torvalds committed
221

222 223
	if (s3c24xx_register_clock(&clk_usb_bus) < 0)
		printk(KERN_ERR "failed to register usb bus clock\n");
Linus Torvalds's avatar
Linus Torvalds committed
224

225
	/* register clocks from clock array */
Linus Torvalds's avatar
Linus Torvalds committed
226

227 228 229
	clkp = init_clocks;
	for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
		/* ensure that we note the clock state */
Linus Torvalds's avatar
Linus Torvalds committed
230

231
		clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
232

233 234 235 236 237 238
		ret = s3c24xx_register_clock(clkp);
		if (ret < 0) {
			printk(KERN_ERR "Failed to register clock %s (%d)\n",
			       clkp->name, ret);
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
239

240
	/* We must be careful disabling the clocks we are not intending to
241
	 * be using at boot time, as subsystems such as the LCD which do
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	 * 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 */

	clkp = init_clocks_disable;
	for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {

		ret = s3c24xx_register_clock(clkp);
		if (ret < 0) {
			printk(KERN_ERR "Failed to register clock %s (%d)\n",
			       clkp->name, ret);
		}
260

261 262
		s3c2410_clkcon_enable(clkp, 0);
	}
263

264
	/* show the clock-slow value */
Linus Torvalds's avatar
Linus Torvalds committed
265

266
	xtal = clk_get(NULL, "xtal");
Linus Torvalds's avatar
Linus Torvalds committed
267

268 269 270 271 272 273
	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");
Linus Torvalds's avatar
Linus Torvalds committed
274 275 276

	return 0;
}