blob: 641266f3d152011d35530d9194c2c219af977217 [file] [log] [blame]
Kukjin Kim09ec1d72013-01-31 16:54:38 -08001/*
Ben Dooksa21765a2007-02-11 18:31:01 +01002 * Copyright (c) 2006 Simtec Electronics
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Ben Dooks <ben@simtec.co.uk>
4 *
Ben Dooksa21765a2007-02-11 18:31:01 +01005 * S3C2410,S3C2440,S3C2442 Clock control support
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20*/
21
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/list.h>
26#include <linux/errno.h>
27#include <linux/err.h>
Kay Sieversedbaa602011-12-21 16:26:03 -080028#include <linux/device.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000029#include <linux/clk.h>
Arjan van de Ven00431702006-01-12 18:42:23 +000030#include <linux/mutex.h>
Ben Dooks8e40a2f2006-03-20 17:10:04 +000031#include <linux/delay.h>
Ben Dooksa21765a2007-02-11 18:31:01 +010032#include <linux/serial_core.h>
Russell Kingfced80c2008-09-06 12:10:45 +010033#include <linux/io.h>
Ben Dooksa21765a2007-02-11 18:31:01 +010034
35#include <asm/mach/map.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Russell Kinga09e64f2008-08-05 16:14:15 +010037#include <mach/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Ben Dooksa2b7ba92008-10-07 22:26:09 +010039#include <plat/regs-serial.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010040#include <mach/regs-clock.h>
41#include <mach/regs-gpio.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Ben Dooksa2b7ba92008-10-07 22:26:09 +010043#include <plat/s3c2410.h>
Ben Dooksd5120ae2008-10-07 23:09:51 +010044#include <plat/clock.h>
Ben Dooksa2b7ba92008-10-07 22:26:09 +010045#include <plat/cpu.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
Ben Dooksa21765a2007-02-11 18:31:01 +010047int s3c2410_clkcon_enable(struct clk *clk, int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -070048{
Ben Dooksa21765a2007-02-11 18:31:01 +010049 unsigned int clocks = clk->ctrlbit;
50 unsigned long clkcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Ben Dooksa21765a2007-02-11 18:31:01 +010052 clkcon = __raw_readl(S3C2410_CLKCON);
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000053
54 if (enable)
Ben Dooksa21765a2007-02-11 18:31:01 +010055 clkcon |= clocks;
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000056 else
Ben Dooksa21765a2007-02-11 18:31:01 +010057 clkcon &= ~clocks;
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000058
Ben Dooksa21765a2007-02-11 18:31:01 +010059 /* ensure none of the special function bits set */
60 clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
61
62 __raw_writel(clkcon, S3C2410_CLKCON);
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000063
64 return 0;
65}
66
Ben Dooksa21765a2007-02-11 18:31:01 +010067static int s3c2410_upll_enable(struct clk *clk, int enable)
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000068{
Ben Dooksa21765a2007-02-11 18:31:01 +010069 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
70 unsigned long orig = clkslow;
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000071
Ben Dooksa21765a2007-02-11 18:31:01 +010072 if (enable)
73 clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000074 else
Ben Dooksa21765a2007-02-11 18:31:01 +010075 clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000076
Ben Dooksa21765a2007-02-11 18:31:01 +010077 __raw_writel(clkslow, S3C2410_CLKSLOW);
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000078
Ben Dooksa21765a2007-02-11 18:31:01 +010079 /* if we started the UPLL, then allow to settle */
Ben Dooks3fc3e1c2006-03-20 17:10:07 +000080
Ben Dooksa21765a2007-02-11 18:31:01 +010081 if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
82 udelay(200);
83
84 return 0;
85}
86
87/* standard clock definitions */
88
Ben Dooks4e046912010-04-28 12:58:13 +090089static struct clk init_clocks_off[] = {
Ben Dooksa21765a2007-02-11 18:31:01 +010090 {
91 .name = "nand",
Ben Dooksa21765a2007-02-11 18:31:01 +010092 .parent = &clk_h,
93 .enable = s3c2410_clkcon_enable,
94 .ctrlbit = S3C2410_CLKCON_NAND,
95 }, {
96 .name = "sdi",
Ben Dooksa21765a2007-02-11 18:31:01 +010097 .parent = &clk_p,
98 .enable = s3c2410_clkcon_enable,
99 .ctrlbit = S3C2410_CLKCON_SDI,
100 }, {
101 .name = "adc",
Ben Dooksa21765a2007-02-11 18:31:01 +0100102 .parent = &clk_p,
103 .enable = s3c2410_clkcon_enable,
104 .ctrlbit = S3C2410_CLKCON_ADC,
105 }, {
106 .name = "i2c",
Ben Dooksa21765a2007-02-11 18:31:01 +0100107 .parent = &clk_p,
108 .enable = s3c2410_clkcon_enable,
109 .ctrlbit = S3C2410_CLKCON_IIC,
110 }, {
111 .name = "iis",
Ben Dooksa21765a2007-02-11 18:31:01 +0100112 .parent = &clk_p,
113 .enable = s3c2410_clkcon_enable,
114 .ctrlbit = S3C2410_CLKCON_IIS,
115 }, {
116 .name = "spi",
Ben Dooksa21765a2007-02-11 18:31:01 +0100117 .parent = &clk_p,
118 .enable = s3c2410_clkcon_enable,
119 .ctrlbit = S3C2410_CLKCON_SPI,
120 }
121};
122
123static struct clk init_clocks[] = {
124 {
125 .name = "lcd",
Ben Dooksa21765a2007-02-11 18:31:01 +0100126 .parent = &clk_h,
127 .enable = s3c2410_clkcon_enable,
128 .ctrlbit = S3C2410_CLKCON_LCDC,
129 }, {
130 .name = "gpio",
Ben Dooksa21765a2007-02-11 18:31:01 +0100131 .parent = &clk_p,
132 .enable = s3c2410_clkcon_enable,
133 .ctrlbit = S3C2410_CLKCON_GPIO,
134 }, {
135 .name = "usb-host",
Ben Dooksa21765a2007-02-11 18:31:01 +0100136 .parent = &clk_h,
137 .enable = s3c2410_clkcon_enable,
138 .ctrlbit = S3C2410_CLKCON_USBH,
139 }, {
140 .name = "usb-device",
Ben Dooksa21765a2007-02-11 18:31:01 +0100141 .parent = &clk_h,
142 .enable = s3c2410_clkcon_enable,
143 .ctrlbit = S3C2410_CLKCON_USBD,
144 }, {
145 .name = "timers",
Ben Dooksa21765a2007-02-11 18:31:01 +0100146 .parent = &clk_p,
147 .enable = s3c2410_clkcon_enable,
148 .ctrlbit = S3C2410_CLKCON_PWMT,
149 }, {
150 .name = "uart",
Thomas Abrahame83626f2011-06-14 19:12:26 +0900151 .devname = "s3c2410-uart.0",
Ben Dooksa21765a2007-02-11 18:31:01 +0100152 .parent = &clk_p,
153 .enable = s3c2410_clkcon_enable,
154 .ctrlbit = S3C2410_CLKCON_UART0,
155 }, {
156 .name = "uart",
Thomas Abrahame83626f2011-06-14 19:12:26 +0900157 .devname = "s3c2410-uart.1",
Ben Dooksa21765a2007-02-11 18:31:01 +0100158 .parent = &clk_p,
159 .enable = s3c2410_clkcon_enable,
160 .ctrlbit = S3C2410_CLKCON_UART1,
161 }, {
162 .name = "uart",
Thomas Abrahame83626f2011-06-14 19:12:26 +0900163 .devname = "s3c2410-uart.2",
Ben Dooksa21765a2007-02-11 18:31:01 +0100164 .parent = &clk_p,
165 .enable = s3c2410_clkcon_enable,
166 .ctrlbit = S3C2410_CLKCON_UART2,
167 }, {
168 .name = "rtc",
Ben Dooksa21765a2007-02-11 18:31:01 +0100169 .parent = &clk_p,
170 .enable = s3c2410_clkcon_enable,
171 .ctrlbit = S3C2410_CLKCON_RTC,
172 }, {
173 .name = "watchdog",
Ben Dooksa21765a2007-02-11 18:31:01 +0100174 .parent = &clk_p,
175 .ctrlbit = 0,
176 }, {
177 .name = "usb-bus-host",
Ben Dooksa21765a2007-02-11 18:31:01 +0100178 .parent = &clk_usb_bus,
179 }, {
180 .name = "usb-bus-gadget",
Ben Dooksa21765a2007-02-11 18:31:01 +0100181 .parent = &clk_usb_bus,
182 },
183};
184
185/* s3c2410_baseclk_add()
186 *
187 * Add all the clocks used by the s3c2410 or compatible CPUs
188 * such as the S3C2440 and S3C2442.
189 *
190 * We cannot use a system device as we are needed before any
191 * of the init-calls that initialise the devices are actually
192 * done.
193*/
194
195int __init s3c2410_baseclk_add(void)
196{
197 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
198 unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
199 struct clk *clkp;
200 struct clk *xtal;
201 int ret;
202 int ptr;
203
204 clk_upll.enable = s3c2410_upll_enable;
205
206 if (s3c24xx_register_clock(&clk_usb_bus) < 0)
207 printk(KERN_ERR "failed to register usb bus clock\n");
208
209 /* register clocks from clock array */
210
211 clkp = init_clocks;
212 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
213 /* ensure that we note the clock state */
214
215 clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
216
217 ret = s3c24xx_register_clock(clkp);
218 if (ret < 0) {
219 printk(KERN_ERR "Failed to register clock %s (%d)\n",
220 clkp->name, ret);
221 }
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000222 }
223
Ben Dooksa21765a2007-02-11 18:31:01 +0100224 /* We must be careful disabling the clocks we are not intending to
Robert P. J. Day3a4fa0a2007-10-19 23:10:43 +0200225 * be using at boot time, as subsystems such as the LCD which do
Ben Dooksa21765a2007-02-11 18:31:01 +0100226 * their own DMA requests to the bus can cause the system to lockup
227 * if they where in the middle of requesting bus access.
228 *
229 * Disabling the LCD clock if the LCD is active is very dangerous,
230 * and therefore the bootloader should be careful to not enable
231 * the LCD clock if it is not needed.
232 */
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000233
Ben Dooksa21765a2007-02-11 18:31:01 +0100234 /* install (and disable) the clocks we do not need immediately */
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000235
Ben Dooks4e046912010-04-28 12:58:13 +0900236 s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
237 s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000238
Ben Dooksa21765a2007-02-11 18:31:01 +0100239 /* show the clock-slow value */
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000240
Ben Dooksa21765a2007-02-11 18:31:01 +0100241 xtal = clk_get(NULL, "xtal");
Ben Dooks3fc3e1c2006-03-20 17:10:07 +0000242
Ben Dooksa21765a2007-02-11 18:31:01 +0100243 printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
244 print_mhz(clk_get_rate(xtal) /
245 ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
246 (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
247 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
248 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Ben Dooks9d325f22008-11-21 10:36:05 +0000250 s3c_pwmclk_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 return 0;
252}