aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/arm/Samsung-S3C24XX/CPUfreq.txt75
-rw-r--r--arch/arm/Kconfig61
-rw-r--r--arch/arm/mach-s3c2410/Kconfig18
-rw-r--r--arch/arm/mach-s3c2410/Makefile2
-rw-r--r--arch/arm/mach-s3c2410/cpu-freq.c159
-rw-r--r--arch/arm/mach-s3c2410/dma.c11
-rw-r--r--arch/arm/mach-s3c2410/include/mach/map.h7
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-mem.h10
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h23
-rw-r--r--arch/arm/mach-s3c2410/irq.c15
-rw-r--r--arch/arm/mach-s3c2410/mach-bast.c9
-rw-r--r--arch/arm/mach-s3c2410/pll.c95
-rw-r--r--arch/arm/mach-s3c2410/pm.c12
-rw-r--r--arch/arm/mach-s3c2410/s3c2410.c29
-rw-r--r--arch/arm/mach-s3c2412/Kconfig9
-rw-r--r--arch/arm/mach-s3c2412/Makefile1
-rw-r--r--arch/arm/mach-s3c2412/cpu-freq.c257
-rw-r--r--arch/arm/mach-s3c2412/s3c2412.c12
-rw-r--r--arch/arm/mach-s3c2440/Kconfig7
-rw-r--r--arch/arm/mach-s3c2440/mach-osiris.c9
-rw-r--r--arch/arm/mach-s3c6400/include/mach/map.h4
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu-freq.h87
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu.h1
-rw-r--r--arch/arm/plat-s3c/include/plat/map-base.h8
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig59
-rw-r--r--arch/arm/plat-s3c24xx/Makefile10
-rw-r--r--arch/arm/plat-s3c24xx/cpu-freq-debugfs.c199
-rw-r--r--arch/arm/plat-s3c24xx/cpu-freq.c716
-rw-r--r--arch/arm/plat-s3c24xx/cpu.c2
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h282
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2410.h1
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c64
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-iotiming.c477
-rw-r--r--arch/arm/plat-s3c24xx/s3c2412-iotiming.c285
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-cpufreq.c311
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c97
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c127
-rw-r--r--include/linux/amba/pl093.h80
38 files changed, 3605 insertions, 26 deletions
diff --git a/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt b/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
new file mode 100644
index 000000000000..76b3a11e90be
--- /dev/null
+++ b/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
@@ -0,0 +1,75 @@
1 S3C24XX CPUfreq support
2 =======================
3
4Introduction
5------------
6
7 The S3C24XX series support a number of power saving systems, such as
8 the ability to change the core, memory and peripheral operating
9 frequencies. The core control is exported via the CPUFreq driver
10 which has a number of different manual or automatic controls over the
11 rate the core is running at.
12
13 There are two forms of the driver depending on the specific CPU and
14 how the clocks are arranged. The first implementation used as single
15 PLL to feed the ARM, memory and peripherals via a series of dividers
16 and muxes and this is the implementation that is documented here. A
17 newer version where there is a seperate PLL and clock divider for the
18 ARM core is available as a seperate driver.
19
20
21Layout
22------
23
24 The code core manages the CPU specific drivers, any data that they
25 need to register and the interface to the generic drivers/cpufreq
26 system. Each CPU registers a driver to control the PLL, clock dividers
27 and anything else associated with it. Any board that wants to use this
28 framework needs to supply at least basic details of what is required.
29
30 The core registers with drivers/cpufreq at init time if all the data
31 necessary has been supplied.
32
33
34CPU support
35-----------
36
37 The support for each CPU depends on the facilities provided by the
38 SoC and the driver as each device has different PLL and clock chains
39 associated with it.
40
41
42Slow Mode
43---------
44
45 The SLOW mode where the PLL is turned off altogether and the
46 system is fed by the external crystal input is currently not
47 supported.
48
49
50sysfs
51-----
52
53 The core code exports extra information via sysfs in the directory
54 devices/system/cpu/cpu0/arch-freq.
55
56
57Board Support
58-------------
59
60 Each board that wants to use the cpufreq code must register some basic
61 information with the core driver to provide information about what the
62 board requires and any restrictions being placed on it.
63
64 The board needs to supply information about whether it needs the IO bank
65 timings changing, any maximum frequency limits and information about the
66 SDRAM refresh rate.
67
68
69
70
71Document Author
72---------------
73
74Ben Dooks, Copyright 2009 Simtec Electronics
75Licensed under GPLv2
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index aef63c8e3d2d..f07a4ba281bc 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -126,6 +126,13 @@ config ARCH_HAS_ILOG2_U32
126config ARCH_HAS_ILOG2_U64 126config ARCH_HAS_ILOG2_U64
127 bool 127 bool
128 128
129config ARCH_HAS_CPUFREQ
130 bool
131 help
132 Internal node to signify that the ARCH has CPUFREQ support
133 and that the relevant menu configurations are displayed for
134 it.
135
129config GENERIC_HWEIGHT 136config GENERIC_HWEIGHT
130 bool 137 bool
131 default y 138 default y
@@ -203,6 +210,7 @@ config ARCH_AAEC2000
203config ARCH_INTEGRATOR 210config ARCH_INTEGRATOR
204 bool "ARM Ltd. Integrator family" 211 bool "ARM Ltd. Integrator family"
205 select ARM_AMBA 212 select ARM_AMBA
213 select ARCH_HAS_CPUFREQ
206 select HAVE_CLK 214 select HAVE_CLK
207 select COMMON_CLKDEV 215 select COMMON_CLKDEV
208 select ICST525 216 select ICST525
@@ -509,6 +517,7 @@ config ARCH_PXA
509 bool "PXA2xx/PXA3xx-based" 517 bool "PXA2xx/PXA3xx-based"
510 depends on MMU 518 depends on MMU
511 select ARCH_MTD_XIP 519 select ARCH_MTD_XIP
520 select ARCH_HAS_CPUFREQ
512 select GENERIC_GPIO 521 select GENERIC_GPIO
513 select HAVE_CLK 522 select HAVE_CLK
514 select COMMON_CLKDEV 523 select COMMON_CLKDEV
@@ -551,6 +560,7 @@ config ARCH_SA1100
551 select ISA 560 select ISA
552 select ARCH_SPARSEMEM_ENABLE 561 select ARCH_SPARSEMEM_ENABLE
553 select ARCH_MTD_XIP 562 select ARCH_MTD_XIP
563 select ARCH_HAS_CPUFREQ
554 select GENERIC_GPIO 564 select GENERIC_GPIO
555 select GENERIC_TIME 565 select GENERIC_TIME
556 select GENERIC_CLOCKEVENTS 566 select GENERIC_CLOCKEVENTS
@@ -563,6 +573,7 @@ config ARCH_SA1100
563config ARCH_S3C2410 573config ARCH_S3C2410
564 bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443" 574 bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443"
565 select GENERIC_GPIO 575 select GENERIC_GPIO
576 select ARCH_HAS_CPUFREQ
566 select HAVE_CLK 577 select HAVE_CLK
567 help 578 help
568 Samsung S3C2410X CPU based systems, such as the Simtec Electronics 579 Samsung S3C2410X CPU based systems, such as the Simtec Electronics
@@ -573,6 +584,7 @@ config ARCH_S3C64XX
573 bool "Samsung S3C64XX" 584 bool "Samsung S3C64XX"
574 select GENERIC_GPIO 585 select GENERIC_GPIO
575 select HAVE_CLK 586 select HAVE_CLK
587 select ARCH_HAS_CPUFREQ
576 help 588 help
577 Samsung S3C64XX series based systems 589 Samsung S3C64XX series based systems
578 590
@@ -632,6 +644,7 @@ config ARCH_OMAP
632 select GENERIC_GPIO 644 select GENERIC_GPIO
633 select HAVE_CLK 645 select HAVE_CLK
634 select ARCH_REQUIRE_GPIOLIB 646 select ARCH_REQUIRE_GPIOLIB
647 select ARCH_HAS_CPUFREQ
635 select GENERIC_TIME 648 select GENERIC_TIME
636 select GENERIC_CLOCKEVENTS 649 select GENERIC_CLOCKEVENTS
637 help 650 help
@@ -1241,7 +1254,7 @@ endmenu
1241 1254
1242menu "CPU Power Management" 1255menu "CPU Power Management"
1243 1256
1244if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX) 1257if ARCH_HAS_CPUFREQ
1245 1258
1246source "drivers/cpufreq/Kconfig" 1259source "drivers/cpufreq/Kconfig"
1247 1260
@@ -1276,6 +1289,52 @@ config CPU_FREQ_S3C64XX
1276 bool "CPUfreq support for Samsung S3C64XX CPUs" 1289 bool "CPUfreq support for Samsung S3C64XX CPUs"
1277 depends on CPU_FREQ && CPU_S3C6410 1290 depends on CPU_FREQ && CPU_S3C6410
1278 1291
1292config CPU_FREQ_S3C
1293 bool
1294 help
1295 Internal configuration node for common cpufreq on Samsung SoC
1296
1297config CPU_FREQ_S3C24XX
1298 bool "CPUfreq driver for Samsung S3C24XX series CPUs"
1299 depends on ARCH_S3C2410 && CPU_FREQ && EXPERIMENTAL
1300 select CPU_FREQ_S3C
1301 help
1302 This enables the CPUfreq driver for the Samsung S3C24XX family
1303 of CPUs.
1304
1305 For details, take a look at <file:Documentation/cpu-freq>.
1306
1307 If in doubt, say N.
1308
1309config CPU_FREQ_S3C24XX_PLL
1310 bool "Support CPUfreq changing of PLL frequency"
1311 depends on CPU_FREQ_S3C24XX && EXPERIMENTAL
1312 help
1313 Compile in support for changing the PLL frequency from the
1314 S3C24XX series CPUfreq driver. The PLL takes time to settle
1315 after a frequency change, so by default it is not enabled.
1316
1317 This also means that the PLL tables for the selected CPU(s) will
1318 be built which may increase the size of the kernel image.
1319
1320config CPU_FREQ_S3C24XX_DEBUG
1321 bool "Debug CPUfreq Samsung driver core"
1322 depends on CPU_FREQ_S3C24XX
1323 help
1324 Enable s3c_freq_dbg for the Samsung S3C CPUfreq core
1325
1326config CPU_FREQ_S3C24XX_IODEBUG
1327 bool "Debug CPUfreq Samsung driver IO timing"
1328 depends on CPU_FREQ_S3C24XX
1329 help
1330 Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core
1331
1332config CPU_FREQ_S3C24XX_DEBUGFS
1333 bool "Export debugfs for CPUFreq"
1334 depends on CPU_FREQ_S3C24XX && DEBUG_FS
1335 help
1336 Export status information via debugfs.
1337
1279endif 1338endif
1280 1339
1281source "drivers/cpuidle/Kconfig" 1340source "drivers/cpuidle/Kconfig"
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig
index 41bb65d5b91f..d8c023d4df30 100644
--- a/arch/arm/mach-s3c2410/Kconfig
+++ b/arch/arm/mach-s3c2410/Kconfig
@@ -12,6 +12,7 @@ config CPU_S3C2410
12 select S3C2410_GPIO 12 select S3C2410_GPIO
13 select CPU_LLSERIAL_S3C2410 13 select CPU_LLSERIAL_S3C2410
14 select S3C2410_PM if PM 14 select S3C2410_PM if PM
15 select S3C2410_CPUFREQ if CPU_FREQ_S3C24XX
15 help 16 help
16 Support for S3C2410 and S3C2410A family from the S3C24XX line 17 Support for S3C2410 and S3C2410A family from the S3C24XX line
17 of Samsung Mobile CPUs. 18 of Samsung Mobile CPUs.
@@ -45,6 +46,22 @@ config MACH_BAST_IDE
45 Internal node for machines with an BAST style IDE 46 Internal node for machines with an BAST style IDE
46 interface 47 interface
47 48
49# cpu frequency scaling support
50
51config S3C2410_CPUFREQ
52 bool
53 depends on CPU_FREQ_S3C24XX && CPU_S3C2410
54 select S3C2410_CPUFREQ_UTILS
55 help
56 CPU Frequency scaling support for S3C2410
57
58config S3C2410_PLLTABLE
59 bool
60 depends on S3C2410_CPUFREQ && CPU_FREQ_S3C24XX_PLL
61 default y
62 help
63 Select the PLL table for the S3C2410
64
48menu "S3C2410 Machines" 65menu "S3C2410 Machines"
49 66
50config ARCH_SMDK2410 67config ARCH_SMDK2410
@@ -79,6 +96,7 @@ config MACH_N30
79config ARCH_BAST 96config ARCH_BAST
80 bool "Simtec Electronics BAST (EB2410ITX)" 97 bool "Simtec Electronics BAST (EB2410ITX)"
81 select CPU_S3C2410 98 select CPU_S3C2410
99 select S3C2410_IOTIMING if S3C2410_CPUFREQ
82 select PM_SIMTEC if PM 100 select PM_SIMTEC if PM
83 select SIMTEC_NOR 101 select SIMTEC_NOR
84 select MACH_BAST_IDE 102 select MACH_BAST_IDE
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile
index fca02f82711c..2ab5ba4b266f 100644
--- a/arch/arm/mach-s3c2410/Makefile
+++ b/arch/arm/mach-s3c2410/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
15obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o 15obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
16obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o 16obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o
17obj-$(CONFIG_S3C2410_GPIO) += gpio.o 17obj-$(CONFIG_S3C2410_GPIO) += gpio.o
18obj-$(CONFIG_S3C2410_CPUFREQ) += cpu-freq.o
19obj-$(CONFIG_S3C2410_PLLTABLE) += pll.o
18 20
19# Machine support 21# Machine support
20 22
diff --git a/arch/arm/mach-s3c2410/cpu-freq.c b/arch/arm/mach-s3c2410/cpu-freq.c
new file mode 100644
index 000000000000..9d1186877d08
--- /dev/null
+++ b/arch/arm/mach-s3c2410/cpu-freq.c
@@ -0,0 +1,159 @@
1/* linux/arch/arm/mach-s3c2410/cpu-freq.c
2 *
3 * Copyright (c) 2006,2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C2410 CPU Frequency scaling
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/ioport.h>
18#include <linux/cpufreq.h>
19#include <linux/sysdev.h>
20#include <linux/clk.h>
21#include <linux/err.h>
22#include <linux/io.h>
23
24#include <asm/mach/arch.h>
25#include <asm/mach/map.h>
26
27#include <mach/regs-clock.h>
28
29#include <plat/cpu.h>
30#include <plat/clock.h>
31#include <plat/cpu-freq-core.h>
32
33/* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */
34
35static void s3c2410_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
36{
37 u32 clkdiv = 0;
38
39 if (cfg->divs.h_divisor == 2)
40 clkdiv |= S3C2410_CLKDIVN_HDIVN;
41
42 if (cfg->divs.p_divisor != cfg->divs.h_divisor)
43 clkdiv |= S3C2410_CLKDIVN_PDIVN;
44
45 __raw_writel(clkdiv, S3C2410_CLKDIVN);
46}
47
48static int s3c2410_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
49{
50 unsigned long hclk, fclk, pclk;
51 unsigned int hdiv, pdiv;
52 unsigned long hclk_max;
53
54 fclk = cfg->freq.fclk;
55 hclk_max = cfg->max.hclk;
56
57 cfg->freq.armclk = fclk;
58
59 s3c_freq_dbg("%s: fclk is %lu, max hclk %lu\n",
60 __func__, fclk, hclk_max);
61
62 hdiv = (fclk > cfg->max.hclk) ? 2 : 1;
63 hclk = fclk / hdiv;
64
65 if (hclk > cfg->max.hclk) {
66 s3c_freq_dbg("%s: hclk too big\n", __func__);
67 return -EINVAL;
68 }
69
70 pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
71 pclk = hclk / pdiv;
72
73 if (pclk > cfg->max.pclk) {
74 s3c_freq_dbg("%s: pclk too big\n", __func__);
75 return -EINVAL;
76 }
77
78 pdiv *= hdiv;
79
80 /* record the result */
81 cfg->divs.p_divisor = pdiv;
82 cfg->divs.h_divisor = hdiv;
83
84 return 0 ;
85}
86
87static struct s3c_cpufreq_info s3c2410_cpufreq_info = {
88 .max = {
89 .fclk = 200000000,
90 .hclk = 100000000,
91 .pclk = 50000000,
92 },
93
94 /* transition latency is about 5ms worst-case, so
95 * set 10ms to be sure */
96 .latency = 10000000,
97
98 .locktime_m = 150,
99 .locktime_u = 150,
100 .locktime_bits = 12,
101
102 .need_pll = 1,
103
104 .name = "s3c2410",
105 .calc_iotiming = s3c2410_iotiming_calc,
106 .set_iotiming = s3c2410_iotiming_set,
107 .get_iotiming = s3c2410_iotiming_get,
108 .resume_clocks = s3c2410_setup_clocks,
109
110 .set_fvco = s3c2410_set_fvco,
111 .set_refresh = s3c2410_cpufreq_setrefresh,
112 .set_divs = s3c2410_cpufreq_setdivs,
113 .calc_divs = s3c2410_cpufreq_calcdivs,
114
115 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
116};
117
118static int s3c2410_cpufreq_add(struct sys_device *sysdev)
119{
120 return s3c_cpufreq_register(&s3c2410_cpufreq_info);
121}
122
123static struct sysdev_driver s3c2410_cpufreq_driver = {
124 .add = s3c2410_cpufreq_add,
125};
126
127static int __init s3c2410_cpufreq_init(void)
128{
129 return sysdev_driver_register(&s3c2410_sysclass,
130 &s3c2410_cpufreq_driver);
131}
132
133arch_initcall(s3c2410_cpufreq_init);
134
135static int s3c2410a_cpufreq_add(struct sys_device *sysdev)
136{
137 /* alter the maximum freq settings for S3C2410A. If a board knows
138 * it only has a maximum of 200, then it should register its own
139 * limits. */
140
141 s3c2410_cpufreq_info.max.fclk = 266000000;
142 s3c2410_cpufreq_info.max.hclk = 133000000;
143 s3c2410_cpufreq_info.max.pclk = 66500000;
144 s3c2410_cpufreq_info.name = "s3c2410a";
145
146 return s3c2410_cpufreq_add(sysdev);
147}
148
149static struct sysdev_driver s3c2410a_cpufreq_driver = {
150 .add = s3c2410a_cpufreq_add,
151};
152
153static int __init s3c2410a_cpufreq_init(void)
154{
155 return sysdev_driver_register(&s3c2410a_sysclass,
156 &s3c2410a_cpufreq_driver);
157}
158
159arch_initcall(s3c2410a_cpufreq_init);
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c
index dbf96e60d992..63b753f56c64 100644
--- a/arch/arm/mach-s3c2410/dma.c
+++ b/arch/arm/mach-s3c2410/dma.c
@@ -164,6 +164,17 @@ static int __init s3c2410_dma_drvinit(void)
164} 164}
165 165
166arch_initcall(s3c2410_dma_drvinit); 166arch_initcall(s3c2410_dma_drvinit);
167
168static struct sysdev_driver s3c2410a_dma_driver = {
169 .add = s3c2410_dma_add,
170};
171
172static int __init s3c2410a_dma_drvinit(void)
173{
174 return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_dma_driver);
175}
176
177arch_initcall(s3c2410a_dma_drvinit);
167#endif 178#endif
168 179
169#if defined(CONFIG_CPU_S3C2442) 180#if defined(CONFIG_CPU_S3C2442)
diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index e99b212cb1ca..11cce0975502 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -67,6 +67,13 @@
67#define S3C2443_PA_HSMMC (0x4A800000) 67#define S3C2443_PA_HSMMC (0x4A800000)
68#define S3C2443_SZ_HSMMC (256) 68#define S3C2443_SZ_HSMMC (256)
69 69
70/* S3C2412 memory and IO controls */
71#define S3C2412_PA_SSMC (0x4F000000)
72#define S3C2412_VA_SSMC S3C_ADDR_CPU(0x00000000)
73
74#define S3C2412_PA_EBI (0x48800000)
75#define S3C2412_VA_EBI S3C_ADDR_CPU(0x00010000)
76
70/* physical addresses of all the chip-select areas */ 77/* physical addresses of all the chip-select areas */
71 78
72#define S3C2410_CS0 (0x00000000) 79#define S3C2410_CS0 (0x00000000)
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-mem.h b/arch/arm/mach-s3c2410/include/mach/regs-mem.h
index 57759804e2fa..7f7c52947963 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-mem.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-mem.h
@@ -73,6 +73,16 @@
73#define S3C2410_BWSCON_WS7 (1<<30) 73#define S3C2410_BWSCON_WS7 (1<<30)
74#define S3C2410_BWSCON_ST7 (1<<31) 74#define S3C2410_BWSCON_ST7 (1<<31)
75 75
76/* accesor functions for getting BANK(n) configuration. (n != 0) */
77
78#define S3C2410_BWSCON_GET(_bwscon, _bank) (((_bwscon) >> ((_bank) * 4)) & 0xf)
79
80#define S3C2410_BWSCON_DW8 (0)
81#define S3C2410_BWSCON_DW16 (1)
82#define S3C2410_BWSCON_DW32 (2)
83#define S3C2410_BWSCON_WS (1 << 2)
84#define S3C2410_BWSCON_ST (1 << 3)
85
76/* memory set (rom, ram) */ 86/* memory set (rom, ram) */
77#define S3C2410_BANKCON0 S3C2410_MEMREG(0x0004) 87#define S3C2410_BANKCON0 S3C2410_MEMREG(0x0004)
78#define S3C2410_BANKCON1 S3C2410_MEMREG(0x0008) 88#define S3C2410_BANKCON1 S3C2410_MEMREG(0x0008)
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
index a4bf27123170..fb6352515090 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
@@ -14,9 +14,11 @@
14#ifndef __ASM_ARM_REGS_S3C2412_MEM 14#ifndef __ASM_ARM_REGS_S3C2412_MEM
15#define __ASM_ARM_REGS_S3C2412_MEM 15#define __ASM_ARM_REGS_S3C2412_MEM
16 16
17#ifndef S3C2412_MEMREG
18#define S3C2412_MEMREG(x) (S3C24XX_VA_MEMCTRL + (x)) 17#define S3C2412_MEMREG(x) (S3C24XX_VA_MEMCTRL + (x))
19#endif 18#define S3C2412_EBIREG(x) (S3C2412_VA_EBI + (x))
19
20#define S3C2412_SSMCREG(x) (S3C2412_VA_SSMC + (x))
21#define S3C2412_SSMC(x, o) (S3C2412_SSMCREG((x * 0x20) + (o)))
20 22
21#define S3C2412_BANKCFG S3C2412_MEMREG(0x00) 23#define S3C2412_BANKCFG S3C2412_MEMREG(0x00)
22#define S3C2412_BANKCON1 S3C2412_MEMREG(0x04) 24#define S3C2412_BANKCON1 S3C2412_MEMREG(0x04)
@@ -26,4 +28,21 @@
26#define S3C2412_REFRESH S3C2412_MEMREG(0x10) 28#define S3C2412_REFRESH S3C2412_MEMREG(0x10)
27#define S3C2412_TIMEOUT S3C2412_MEMREG(0x14) 29#define S3C2412_TIMEOUT S3C2412_MEMREG(0x14)
28 30
31/* EBI control registers */
32
33#define S3C2412_EBI_PR S3C2412_EBIREG(0x00)
34#define S3C2412_EBI_BANKCFG S3C2412_EBIREG(0x04)
35
36/* SSMC control registers */
37
38#define S3C2412_SSMC_BANK(x) S3C2412_SSMC(x, 0x00)
39#define S3C2412_SMIDCYR(x) S3C2412_SSMC(x, 0x00)
40#define S3C2412_SMBWSTRD(x) S3C2412_SSMC(x, 0x04)
41#define S3C2412_SMBWSTWRR(x) S3C2412_SSMC(x, 0x08)
42#define S3C2412_SMBWSTOENR(x) S3C2412_SSMC(x, 0x0C)
43#define S3C2412_SMBWSTWENR(x) S3C2412_SSMC(x, 0x10)
44#define S3C2412_SMBCR(x) S3C2412_SSMC(x, 0x14)
45#define S3C2412_SMBSR(x) S3C2412_SSMC(x, 0x18)
46#define S3C2412_SMBWSTBRDR(x) S3C2412_SSMC(x, 0x1C)
47
29#endif /* __ASM_ARM_REGS_S3C2412_MEM */ 48#endif /* __ASM_ARM_REGS_S3C2412_MEM */
diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c
index 92150399563b..5e2f35332056 100644
--- a/arch/arm/mach-s3c2410/irq.c
+++ b/arch/arm/mach-s3c2410/irq.c
@@ -39,9 +39,22 @@ static struct sysdev_driver s3c2410_irq_driver = {
39 .resume = s3c24xx_irq_resume, 39 .resume = s3c24xx_irq_resume,
40}; 40};
41 41
42static int s3c2410_irq_init(void) 42static int __init s3c2410_irq_init(void)
43{ 43{
44 return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_irq_driver); 44 return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_irq_driver);
45} 45}
46 46
47arch_initcall(s3c2410_irq_init); 47arch_initcall(s3c2410_irq_init);
48
49static struct sysdev_driver s3c2410a_irq_driver = {
50 .add = s3c2410_irq_add,
51 .suspend = s3c24xx_irq_suspend,
52 .resume = s3c24xx_irq_resume,
53};
54
55static int __init s3c2410a_irq_init(void)
56{
57 return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_irq_driver);
58}
59
60arch_initcall(s3c2410a_irq_init);
diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c
index 06670bb89cab..647c9adb018f 100644
--- a/arch/arm/mach-s3c2410/mach-bast.c
+++ b/arch/arm/mach-s3c2410/mach-bast.c
@@ -60,6 +60,7 @@
60#include <plat/clock.h> 60#include <plat/clock.h>
61#include <plat/devs.h> 61#include <plat/devs.h>
62#include <plat/cpu.h> 62#include <plat/cpu.h>
63#include <plat/cpu-freq.h>
63 64
64#include "usb-simtec.h" 65#include "usb-simtec.h"
65#include "nor-simtec.h" 66#include "nor-simtec.h"
@@ -601,6 +602,12 @@ static struct clk *bast_clocks[] __initdata = {
601 &s3c24xx_uclk, 602 &s3c24xx_uclk,
602}; 603};
603 604
605static struct s3c_cpufreq_board __initdata bast_cpufreq = {
606 .refresh = 7800, /* 7.8usec */
607 .auto_io = 1,
608 .need_io = 1,
609};
610
604static void __init bast_map_io(void) 611static void __init bast_map_io(void)
605{ 612{
606 /* initialise the clocks */ 613 /* initialise the clocks */
@@ -640,6 +647,8 @@ static void __init bast_init(void)
640 647
641 usb_simtec_init(); 648 usb_simtec_init();
642 nor_simtec_init(); 649 nor_simtec_init();
650
651 s3c_cpufreq_setboard(&bast_cpufreq);
643} 652}
644 653
645MACHINE_START(BAST, "Simtec-BAST") 654MACHINE_START(BAST, "Simtec-BAST")
diff --git a/arch/arm/mach-s3c2410/pll.c b/arch/arm/mach-s3c2410/pll.c
new file mode 100644
index 000000000000..f178c2fd9d85
--- /dev/null
+++ b/arch/arm/mach-s3c2410/pll.c
@@ -0,0 +1,95 @@
1/* arch/arm/mach-s3c2410/pll.c
2 *
3 * Copyright (c) 2006,2007 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 * Vincent Sanders <vince@arm.linux.org.uk>
7 *
8 * S3C2410 CPU PLL tables
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23*/
24
25#include <linux/types.h>
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/sysdev.h>
29#include <linux/list.h>
30#include <linux/clk.h>
31#include <linux/err.h>
32
33#include <plat/cpu.h>
34#include <plat/cpu-freq-core.h>
35
36static struct cpufreq_frequency_table pll_vals_12MHz[] = {
37 { .frequency = 34000000, .index = PLLVAL(82, 2, 3), },
38 { .frequency = 45000000, .index = PLLVAL(82, 1, 3), },
39 { .frequency = 51000000, .index = PLLVAL(161, 3, 3), },
40 { .frequency = 48000000, .index = PLLVAL(120, 2, 3), },
41 { .frequency = 56000000, .index = PLLVAL(142, 2, 3), },
42 { .frequency = 68000000, .index = PLLVAL(82, 2, 2), },
43 { .frequency = 79000000, .index = PLLVAL(71, 1, 2), },
44 { .frequency = 85000000, .index = PLLVAL(105, 2, 2), },
45 { .frequency = 90000000, .index = PLLVAL(112, 2, 2), },
46 { .frequency = 101000000, .index = PLLVAL(127, 2, 2), },
47 { .frequency = 113000000, .index = PLLVAL(105, 1, 2), },
48 { .frequency = 118000000, .index = PLLVAL(150, 2, 2), },
49 { .frequency = 124000000, .index = PLLVAL(116, 1, 2), },
50 { .frequency = 135000000, .index = PLLVAL(82, 2, 1), },
51 { .frequency = 147000000, .index = PLLVAL(90, 2, 1), },
52 { .frequency = 152000000, .index = PLLVAL(68, 1, 1), },
53 { .frequency = 158000000, .index = PLLVAL(71, 1, 1), },
54 { .frequency = 170000000, .index = PLLVAL(77, 1, 1), },
55 { .frequency = 180000000, .index = PLLVAL(82, 1, 1), },
56 { .frequency = 186000000, .index = PLLVAL(85, 1, 1), },
57 { .frequency = 192000000, .index = PLLVAL(88, 1, 1), },
58 { .frequency = 203000000, .index = PLLVAL(161, 3, 1), },
59
60 /* 2410A extras */
61
62 { .frequency = 210000000, .index = PLLVAL(132, 2, 1), },
63 { .frequency = 226000000, .index = PLLVAL(105, 1, 1), },
64 { .frequency = 266000000, .index = PLLVAL(125, 1, 1), },
65 { .frequency = 268000000, .index = PLLVAL(126, 1, 1), },
66 { .frequency = 270000000, .index = PLLVAL(127, 1, 1), },
67};
68
69static int s3c2410_plls_add(struct sys_device *dev)
70{
71 return s3c_plltab_register(pll_vals_12MHz, ARRAY_SIZE(pll_vals_12MHz));
72}
73
74static struct sysdev_driver s3c2410_plls_drv = {
75 .add = s3c2410_plls_add,
76};
77
78static int __init s3c2410_pll_init(void)
79{
80 return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_plls_drv);
81
82}
83
84arch_initcall(s3c2410_pll_init);
85
86static struct sysdev_driver s3c2410a_plls_drv = {
87 .add = s3c2410_plls_add,
88};
89
90static int __init s3c2410a_pll_init(void)
91{
92 return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_plls_drv);
93}
94
95arch_initcall(s3c2410a_pll_init);
diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c
index 143e08a599d4..966119c8efee 100644
--- a/arch/arm/mach-s3c2410/pm.c
+++ b/arch/arm/mach-s3c2410/pm.c
@@ -119,6 +119,18 @@ static int __init s3c2410_pm_drvinit(void)
119} 119}
120 120
121arch_initcall(s3c2410_pm_drvinit); 121arch_initcall(s3c2410_pm_drvinit);
122
123static struct sysdev_driver s3c2410a_pm_driver = {
124 .add = s3c2410_pm_add,
125 .resume = s3c2410_pm_resume,
126};
127
128static int __init s3c2410a_pm_drvinit(void)
129{
130 return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_pm_driver);
131}
132
133arch_initcall(s3c2410a_pm_drvinit);
122#endif 134#endif
123 135
124#if defined(CONFIG_CPU_S3C2440) 136#if defined(CONFIG_CPU_S3C2440)
diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c
index feb141b1f915..91ba42f688ac 100644
--- a/arch/arm/mach-s3c2410/s3c2410.c
+++ b/arch/arm/mach-s3c2410/s3c2410.c
@@ -105,17 +105,33 @@ void __init_or_cpufreq s3c2410_setup_clocks(void)
105 s3c24xx_setup_clocks(fclk, hclk, pclk); 105 s3c24xx_setup_clocks(fclk, hclk, pclk);
106} 106}
107 107
108/* fake ARMCLK for use with cpufreq, etc. */
109
110static struct clk s3c2410_armclk = {
111 .name = "armclk",
112 .parent = &clk_f,
113 .id = -1,
114};
115
108void __init s3c2410_init_clocks(int xtal) 116void __init s3c2410_init_clocks(int xtal)
109{ 117{
110 s3c24xx_register_baseclocks(xtal); 118 s3c24xx_register_baseclocks(xtal);
111 s3c2410_setup_clocks(); 119 s3c2410_setup_clocks();
112 s3c2410_baseclk_add(); 120 s3c2410_baseclk_add();
121 s3c24xx_register_clock(&s3c2410_armclk);
113} 122}
114 123
115struct sysdev_class s3c2410_sysclass = { 124struct sysdev_class s3c2410_sysclass = {
116 .name = "s3c2410-core", 125 .name = "s3c2410-core",
117}; 126};
118 127
128/* Note, we would have liked to name this s3c2410-core, but we cannot
129 * register two sysdev_class with the same name.
130 */
131struct sysdev_class s3c2410a_sysclass = {
132 .name = "s3c2410a-core",
133};
134
119static struct sys_device s3c2410_sysdev = { 135static struct sys_device s3c2410_sysdev = {
120 .cls = &s3c2410_sysclass, 136 .cls = &s3c2410_sysclass,
121}; 137};
@@ -133,9 +149,22 @@ static int __init s3c2410_core_init(void)
133 149
134core_initcall(s3c2410_core_init); 150core_initcall(s3c2410_core_init);
135 151
152static int __init s3c2410a_core_init(void)
153{
154 return sysdev_class_register(&s3c2410a_sysclass);
155}
156
157core_initcall(s3c2410a_core_init);
158
136int __init s3c2410_init(void) 159int __init s3c2410_init(void)
137{ 160{
138 printk("S3C2410: Initialising architecture\n"); 161 printk("S3C2410: Initialising architecture\n");
139 162
140 return sysdev_register(&s3c2410_sysdev); 163 return sysdev_register(&s3c2410_sysdev);
141} 164}
165
166int __init s3c2410a_init(void)
167{
168 s3c2410_sysdev.cls = &s3c2410a_sysclass;
169 return s3c2410_init();
170}
diff --git a/arch/arm/mach-s3c2412/Kconfig b/arch/arm/mach-s3c2412/Kconfig
index 63586ffd0ae7..35c1bde89cf2 100644
--- a/arch/arm/mach-s3c2412/Kconfig
+++ b/arch/arm/mach-s3c2412/Kconfig
@@ -32,6 +32,15 @@ config S3C2412_PM
32 help 32 help
33 Internal config node to apply S3C2412 power management 33 Internal config node to apply S3C2412 power management
34 34
35# Note, the S3C2412 IOtiming support is in plat-s3c24xx
36
37config S3C2412_CPUFREQ
38 bool
39 depends on CPU_FREQ_S3C24XX && CPU_S3C2412
40 select S3C2412_IOTIMING
41 default y
42 help
43 CPU Frequency scaling support for S3C2412 and S3C2413 SoC CPUs.
35 44
36menu "S3C2412 Machines" 45menu "S3C2412 Machines"
37 46
diff --git a/arch/arm/mach-s3c2412/Makefile b/arch/arm/mach-s3c2412/Makefile
index 20918d5dc6a9..530ec46cbaea 100644
--- a/arch/arm/mach-s3c2412/Makefile
+++ b/arch/arm/mach-s3c2412/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_S3C2412) += clock.o
15obj-$(CONFIG_CPU_S3C2412) += gpio.o 15obj-$(CONFIG_CPU_S3C2412) += gpio.o
16obj-$(CONFIG_S3C2412_DMA) += dma.o 16obj-$(CONFIG_S3C2412_DMA) += dma.o
17obj-$(CONFIG_S3C2412_PM) += pm.o sleep.o 17obj-$(CONFIG_S3C2412_PM) += pm.o sleep.o
18obj-$(CONFIG_S3C2412_CPUFREQ) += cpu-freq.o
18 19
19# Machine support 20# Machine support
20 21
diff --git a/arch/arm/mach-s3c2412/cpu-freq.c b/arch/arm/mach-s3c2412/cpu-freq.c
new file mode 100644
index 000000000000..eb3ea1721335
--- /dev/null
+++ b/arch/arm/mach-s3c2412/cpu-freq.c
@@ -0,0 +1,257 @@
1/* linux/arch/arm/mach-s3c2412/cpu-freq.c
2 *
3 * Copyright 2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C2412 CPU Frequency scalling
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/ioport.h>
18#include <linux/cpufreq.h>
19#include <linux/sysdev.h>
20#include <linux/delay.h>
21#include <linux/clk.h>
22#include <linux/err.h>
23#include <linux/io.h>
24
25#include <asm/mach/arch.h>
26#include <asm/mach/map.h>
27
28#include <mach/regs-clock.h>
29#include <mach/regs-s3c2412-mem.h>
30
31#include <plat/cpu.h>
32#include <plat/clock.h>
33#include <plat/cpu-freq-core.h>
34
35/* our clock resources. */
36static struct clk *xtal;
37static struct clk *fclk;
38static struct clk *hclk;
39static struct clk *armclk;
40
41/* HDIV: 1, 2, 3, 4, 6, 8 */
42
43static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
44{
45 unsigned int hdiv, pdiv, armdiv, dvs;
46 unsigned long hclk, fclk, armclk, armdiv_clk;
47 unsigned long hclk_max;
48
49 fclk = cfg->freq.fclk;
50 armclk = cfg->freq.armclk;
51 hclk_max = cfg->max.hclk;
52
53 /* We can't run hclk above armclk as at the best we have to
54 * have armclk and hclk in dvs mode. */
55
56 if (hclk_max > armclk)
57 hclk_max = armclk;
58
59 s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n",
60 __func__, fclk, armclk, hclk_max);
61 s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n",
62 __func__, cfg->freq.fclk, cfg->freq.armclk,
63 cfg->freq.hclk, cfg->freq.pclk);
64
65 armdiv = fclk / armclk;
66
67 if (armdiv < 1)
68 armdiv = 1;
69 if (armdiv > 2)
70 armdiv = 2;
71
72 cfg->divs.arm_divisor = armdiv;
73 armdiv_clk = fclk / armdiv;
74
75 hdiv = armdiv_clk / hclk_max;
76 if (hdiv < 1)
77 hdiv = 1;
78
79 cfg->freq.hclk = hclk = armdiv_clk / hdiv;
80
81 /* set dvs depending on whether we reached armclk or not. */
82 cfg->divs.dvs = dvs = armclk < armdiv_clk;
83
84 /* update the actual armclk we achieved. */
85 cfg->freq.armclk = dvs ? hclk : armdiv_clk;
86
87 s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n",
88 __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs);
89
90 if (hdiv > 4)
91 goto invalid;
92
93 pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
94
95 if ((hclk / pdiv) > cfg->max.pclk)
96 pdiv++;
97
98 cfg->freq.pclk = hclk / pdiv;
99
100 s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
101
102 if (pdiv > 2)
103 goto invalid;
104
105 pdiv *= hdiv;
106
107 /* store the result, and then return */
108
109 cfg->divs.h_divisor = hdiv * armdiv;
110 cfg->divs.p_divisor = pdiv * armdiv;
111
112 return 0;
113
114 invalid:
115 return -EINVAL;
116}
117
118static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
119{
120 unsigned long clkdiv;
121 unsigned long olddiv;
122
123 olddiv = clkdiv = __raw_readl(S3C2410_CLKDIVN);
124
125 /* clear off current clock info */
126
127 clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN;
128 clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK;
129 clkdiv &= ~S3C2412_CLKDIVN_PDIVN;
130
131 if (cfg->divs.arm_divisor == 2)
132 clkdiv |= S3C2412_CLKDIVN_ARMDIVN;
133
134 clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1);
135
136 if (cfg->divs.p_divisor != cfg->divs.h_divisor)
137 clkdiv |= S3C2412_CLKDIVN_PDIVN;
138
139 s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv);
140 __raw_writel(clkdiv, S3C2410_CLKDIVN);
141
142 clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
143}
144
145static void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
146{
147 struct s3c_cpufreq_board *board = cfg->board;
148 unsigned long refresh;
149
150 s3c_freq_dbg("%s: refresh %u ns, hclk %lu\n", __func__,
151 board->refresh, cfg->freq.hclk);
152
153 /* Reduce both the refresh time (in ns) and the frequency (in MHz)
154 * by 10 each to ensure that we do not overflow 32 bit numbers. This
155 * should work for HCLK up to 133MHz and refresh period up to 30usec.
156 */
157
158 refresh = (board->refresh / 10);
159 refresh *= (cfg->freq.hclk / 100);
160 refresh /= (1 * 1000 * 1000); /* 10^6 */
161
162 s3c_freq_dbg("%s: setting refresh 0x%08lx\n", __func__, refresh);
163 __raw_writel(refresh, S3C2412_REFRESH);
164}
165
166/* set the default cpu frequency information, based on an 200MHz part
167 * as we have no other way of detecting the speed rating in software.
168 */
169
170static struct s3c_cpufreq_info s3c2412_cpufreq_info = {
171 .max = {
172 .fclk = 200000000,
173 .hclk = 100000000,
174 .pclk = 50000000,
175 },
176
177 .latency = 5000000, /* 5ms */
178
179 .locktime_m = 150,
180 .locktime_u = 150,
181 .locktime_bits = 16,
182
183 .name = "s3c2412",
184 .set_refresh = s3c2412_cpufreq_setrefresh,
185 .set_divs = s3c2412_cpufreq_setdivs,
186 .calc_divs = s3c2412_cpufreq_calcdivs,
187
188 .calc_iotiming = s3c2412_iotiming_calc,
189 .set_iotiming = s3c2412_iotiming_set,
190 .get_iotiming = s3c2412_iotiming_get,
191
192 .resume_clocks = s3c2412_setup_clocks,
193
194 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs),
195};
196
197static int s3c2412_cpufreq_add(struct sys_device *sysdev)
198{
199 unsigned long fclk_rate;
200
201 hclk = clk_get(NULL, "hclk");
202 if (IS_ERR(hclk)) {
203 printk(KERN_ERR "%s: cannot find hclk clock\n", __func__);
204 return -ENOENT;
205 }
206
207 fclk = clk_get(NULL, "fclk");
208 if (IS_ERR(fclk)) {
209 printk(KERN_ERR "%s: cannot find fclk clock\n", __func__);
210 goto err_fclk;
211 }
212
213 fclk_rate = clk_get_rate(fclk);
214 if (fclk_rate > 200000000) {
215 printk(KERN_INFO
216 "%s: fclk %ld MHz, assuming 266MHz capable part\n",
217 __func__, fclk_rate / 1000000);
218 s3c2412_cpufreq_info.max.fclk = 266000000;
219 s3c2412_cpufreq_info.max.hclk = 133000000;
220 s3c2412_cpufreq_info.max.pclk = 66000000;
221 }
222
223 armclk = clk_get(NULL, "armclk");
224 if (IS_ERR(armclk)) {
225 printk(KERN_ERR "%s: cannot find arm clock\n", __func__);
226 goto err_armclk;
227 }
228
229 xtal = clk_get(NULL, "xtal");
230 if (IS_ERR(xtal)) {
231 printk(KERN_ERR "%s: cannot find xtal clock\n", __func__);
232 goto err_xtal;
233 }
234
235 return s3c_cpufreq_register(&s3c2412_cpufreq_info);
236
237err_xtal:
238 clk_put(armclk);
239err_armclk:
240 clk_put(fclk);
241err_fclk:
242 clk_put(hclk);
243
244 return -ENOENT;
245}
246
247static struct sysdev_driver s3c2412_cpufreq_driver = {
248 .add = s3c2412_cpufreq_add,
249};
250
251static int s3c2412_cpufreq_init(void)
252{
253 return sysdev_driver_register(&s3c2412_sysclass,
254 &s3c2412_cpufreq_driver);
255}
256
257arch_initcall(s3c2412_cpufreq_init);
diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c
index 5b5aba69ec3f..bef39f77729d 100644
--- a/arch/arm/mach-s3c2412/s3c2412.c
+++ b/arch/arm/mach-s3c2412/s3c2412.c
@@ -69,6 +69,18 @@ static struct map_desc s3c2412_iodesc[] __initdata = {
69 IODESC_ENT(CLKPWR), 69 IODESC_ENT(CLKPWR),
70 IODESC_ENT(TIMER), 70 IODESC_ENT(TIMER),
71 IODESC_ENT(WATCHDOG), 71 IODESC_ENT(WATCHDOG),
72 {
73 .virtual = (unsigned long)S3C2412_VA_SSMC,
74 .pfn = __phys_to_pfn(S3C2412_PA_SSMC),
75 .length = SZ_1M,
76 .type = MT_DEVICE,
77 },
78 {
79 .virtual = (unsigned long)S3C2412_VA_EBI,
80 .pfn = __phys_to_pfn(S3C2412_PA_EBI),
81 .length = SZ_1M,
82 .type = MT_DEVICE,
83 },
72}; 84};
73 85
74/* uart registration process */ 86/* uart registration process */
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
index 8cfeaec37306..8ae1b288f7fa 100644
--- a/arch/arm/mach-s3c2440/Kconfig
+++ b/arch/arm/mach-s3c2440/Kconfig
@@ -33,6 +33,7 @@ config MACH_ANUBIS
33 select PM_SIMTEC if PM 33 select PM_SIMTEC if PM
34 select HAVE_PATA_PLATFORM 34 select HAVE_PATA_PLATFORM
35 select S3C24XX_GPIO_EXTRA64 35 select S3C24XX_GPIO_EXTRA64
36 select S3C2440_XTAL_12000000
36 select S3C_DEV_USB_HOST 37 select S3C_DEV_USB_HOST
37 help 38 help
38 Say Y here if you are using the Simtec Electronics ANUBIS 39 Say Y here if you are using the Simtec Electronics ANUBIS
@@ -44,6 +45,8 @@ config MACH_OSIRIS
44 select S3C24XX_DCLK 45 select S3C24XX_DCLK
45 select PM_SIMTEC if PM 46 select PM_SIMTEC if PM
46 select S3C24XX_GPIO_EXTRA128 47 select S3C24XX_GPIO_EXTRA128
48 select S3C2440_XTAL_12000000
49 select S3C2410_IOTIMING if S3C2440_CPUFREQ
47 select S3C_DEV_USB_HOST 50 select S3C_DEV_USB_HOST
48 help 51 help
49 Say Y here if you are using the Simtec IM2440D20 module, also 52 Say Y here if you are using the Simtec IM2440D20 module, also
@@ -52,6 +55,7 @@ config MACH_OSIRIS
52config MACH_RX3715 55config MACH_RX3715
53 bool "HP iPAQ rx3715" 56 bool "HP iPAQ rx3715"
54 select CPU_S3C2440 57 select CPU_S3C2440
58 select S3C2440_XTAL_16934400
55 select PM_H1940 if PM 59 select PM_H1940 if PM
56 help 60 help
57 Say Y here if you are using the HP iPAQ rx3715. 61 Say Y here if you are using the HP iPAQ rx3715.
@@ -59,6 +63,7 @@ config MACH_RX3715
59config ARCH_S3C2440 63config ARCH_S3C2440
60 bool "SMDK2440" 64 bool "SMDK2440"
61 select CPU_S3C2440 65 select CPU_S3C2440
66 select S3C2440_XTAL_16934400
62 select MACH_SMDK 67 select MACH_SMDK
63 select S3C_DEV_USB_HOST 68 select S3C_DEV_USB_HOST
64 help 69 help
@@ -67,6 +72,7 @@ config ARCH_S3C2440
67config MACH_NEXCODER_2440 72config MACH_NEXCODER_2440
68 bool "NexVision NEXCODER 2440 Light Board" 73 bool "NexVision NEXCODER 2440 Light Board"
69 select CPU_S3C2440 74 select CPU_S3C2440
75 select S3C2440_XTAL_12000000
70 select S3C_DEV_USB_HOST 76 select S3C_DEV_USB_HOST
71 help 77 help
72 Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board 78 Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board
@@ -75,6 +81,7 @@ config SMDK2440_CPU2440
75 bool "SMDK2440 with S3C2440 CPU module" 81 bool "SMDK2440 with S3C2440 CPU module"
76 depends on ARCH_S3C2440 82 depends on ARCH_S3C2440
77 default y if ARCH_S3C2440 83 default y if ARCH_S3C2440
84 select S3C2440_XTAL_16934400
78 select CPU_S3C2440 85 select CPU_S3C2440
79 86
80config MACH_AT2440EVB 87config MACH_AT2440EVB
diff --git a/arch/arm/mach-s3c2440/mach-osiris.c b/arch/arm/mach-s3c2440/mach-osiris.c
index cba064b49a64..2105a41281a4 100644
--- a/arch/arm/mach-s3c2440/mach-osiris.c
+++ b/arch/arm/mach-s3c2440/mach-osiris.c
@@ -34,6 +34,7 @@
34#include <asm/irq.h> 34#include <asm/irq.h>
35#include <asm/mach-types.h> 35#include <asm/mach-types.h>
36 36
37#include <plat/cpu-freq.h>
37#include <plat/regs-serial.h> 38#include <plat/regs-serial.h>
38#include <mach/regs-gpio.h> 39#include <mach/regs-gpio.h>
39#include <mach/regs-mem.h> 40#include <mach/regs-mem.h>
@@ -351,6 +352,12 @@ static struct clk *osiris_clocks[] __initdata = {
351 &s3c24xx_uclk, 352 &s3c24xx_uclk,
352}; 353};
353 354
355static struct s3c_cpufreq_board __initdata osiris_cpufreq = {
356 .refresh = 7800, /* refresh period is 7.8usec */
357 .auto_io = 1,
358 .need_io = 1,
359};
360
354static void __init osiris_map_io(void) 361static void __init osiris_map_io(void)
355{ 362{
356 unsigned long flags; 363 unsigned long flags;
@@ -402,6 +409,8 @@ static void __init osiris_init(void)
402 409
403 s3c_i2c0_set_platdata(NULL); 410 s3c_i2c0_set_platdata(NULL);
404 411
412 s3c_cpufreq_setboard(&osiris_cpufreq);
413
405 i2c_register_board_info(0, osiris_i2c_devs, 414 i2c_register_board_info(0, osiris_i2c_devs,
406 ARRAY_SIZE(osiris_i2c_devs)); 415 ARRAY_SIZE(osiris_i2c_devs));
407 416
diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h
index 5057d9948d35..e02e6c38a396 100644
--- a/arch/arm/mach-s3c6400/include/mach/map.h
+++ b/arch/arm/mach-s3c6400/include/mach/map.h
@@ -49,7 +49,7 @@
49#define S3C64XX_PA_IIC1 (0x7F00F000) 49#define S3C64XX_PA_IIC1 (0x7F00F000)
50 50
51#define S3C64XX_PA_GPIO (0x7F008000) 51#define S3C64XX_PA_GPIO (0x7F008000)
52#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000) 52#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000)
53#define S3C64XX_SZ_GPIO SZ_4K 53#define S3C64XX_SZ_GPIO SZ_4K
54 54
55#define S3C64XX_PA_SDRAM (0x50000000) 55#define S3C64XX_PA_SDRAM (0x50000000)
@@ -57,7 +57,7 @@
57#define S3C64XX_PA_VIC1 (0x71300000) 57#define S3C64XX_PA_VIC1 (0x71300000)
58 58
59#define S3C64XX_PA_MODEM (0x74108000) 59#define S3C64XX_PA_MODEM (0x74108000)
60#define S3C64XX_VA_MODEM S3C_ADDR(0x00600000) 60#define S3C64XX_VA_MODEM S3C_ADDR_CPU(0x00100000)
61 61
62#define S3C64XX_PA_USBHOST (0x74300000) 62#define S3C64XX_PA_USBHOST (0x74300000)
63 63
diff --git a/arch/arm/plat-s3c/include/plat/cpu-freq.h b/arch/arm/plat-s3c/include/plat/cpu-freq.h
index c86a13307e90..7b982b7f28cd 100644
--- a/arch/arm/plat-s3c/include/plat/cpu-freq.h
+++ b/arch/arm/plat-s3c/include/plat/cpu-freq.h
@@ -17,6 +17,21 @@ struct s3c_cpufreq_info;
17struct s3c_cpufreq_board; 17struct s3c_cpufreq_board;
18struct s3c_iotimings; 18struct s3c_iotimings;
19 19
20/**
21 * struct s3c_freq - frequency information (mainly for core drivers)
22 * @fclk: The FCLK frequency in Hz.
23 * @armclk: The ARMCLK frequency in Hz.
24 * @hclk_tns: HCLK cycle time in 10ths of nano-seconds.
25 * @hclk: The HCLK frequency in Hz.
26 * @pclk: The PCLK frequency in Hz.
27 *
28 * This contains the frequency information about the current configuration
29 * mainly for the core drivers to ensure we do not end up passing about
30 * a large number of parameters.
31 *
32 * The @hclk_tns field is a useful cache for the parts of the drivers that
33 * need to calculate IO timings and suchlike.
34 */
20struct s3c_freq { 35struct s3c_freq {
21 unsigned long fclk; 36 unsigned long fclk;
22 unsigned long armclk; 37 unsigned long armclk;
@@ -25,48 +40,84 @@ struct s3c_freq {
25 unsigned long pclk; 40 unsigned long pclk;
26}; 41};
27 42
28/* wrapper 'struct cpufreq_freqs' so that any drivers receiving the 43/**
44 * struct s3c_cpufreq_freqs - s3c cpufreq notification information.
45 * @freqs: The cpufreq setting information.
46 * @old: The old clock settings.
47 * @new: The new clock settings.
48 * @pll_changing: Set if the PLL is changing.
49 *
50 * Wrapper 'struct cpufreq_freqs' so that any drivers receiving the
29 * notification can use this information that is not provided by just 51 * notification can use this information that is not provided by just
30 * having the core frequency alone. 52 * having the core frequency alone.
53 *
54 * The pll_changing flag is used to indicate if the PLL itself is
55 * being set during this change. This is important as the clocks
56 * will temporarily be set to the XTAL clock during this time, so
57 * drivers may want to close down their output during this time.
58 *
59 * Note, this is not being used by any current drivers and therefore
60 * may be removed in the future.
31 */ 61 */
32
33struct s3c_cpufreq_freqs { 62struct s3c_cpufreq_freqs {
34 struct cpufreq_freqs freqs; 63 struct cpufreq_freqs freqs;
35 struct s3c_freq old; 64 struct s3c_freq old;
36 struct s3c_freq new; 65 struct s3c_freq new;
66
67 unsigned int pll_changing:1;
37}; 68};
38 69
39#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs) 70#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs)
40 71
72/**
73 * struct s3c_clkdivs - clock divisor information
74 * @p_divisor: Divisor from FCLK to PCLK.
75 * @h_divisor: Divisor from FCLK to HCLK.
76 * @arm_divisor: Divisor from FCLK to ARMCLK (not all CPUs).
77 * @dvs: Non-zero if using DVS mode for ARMCLK.
78 *
79 * Divisor settings for the core clocks.
80 */
41struct s3c_clkdivs { 81struct s3c_clkdivs {
42 int p_divisor; /* fclk / pclk */ 82 int p_divisor;
43 int h_divisor; /* fclk / hclk */ 83 int h_divisor;
44 int arm_divisor; /* not all cpus have this. */ 84 int arm_divisor;
45 unsigned char dvs; /* using dvs mode to arm. */ 85 unsigned char dvs;
46}; 86};
47 87
48#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s)) 88#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s))
49 89
90/**
91 * struct s3c_pllval - PLL value entry.
92 * @freq: The frequency for this entry in Hz.
93 * @pll_reg: The PLL register setting for this PLL value.
94 */
50struct s3c_pllval { 95struct s3c_pllval {
51 unsigned long freq; 96 unsigned long freq;
52 unsigned long pll_reg; 97 unsigned long pll_reg;
53}; 98};
54 99
55struct s3c_cpufreq_config { 100/**
56 struct s3c_freq freq; 101 * struct s3c_cpufreq_board - per-board cpu frequency informatin
57 struct s3c_pllval pll; 102 * @refresh: The SDRAM refresh period in nanoseconds.
58 struct s3c_clkdivs divs; 103 * @auto_io: Set if the IO timing settings should be generated from the
59 struct s3c_cpufreq_info *info; /* for core, not drivers */ 104 * initialisation time hardware registers.
60 struct s3c_cpufreq_board *board; 105 * @need_io: Set if the board has external IO on any of the chipselect
61}; 106 * lines that will require the hardware timing registers to be
62 107 * updated on a clock change.
63/* s3c_cpufreq_board 108 * @max: The maxium frequency limits for the system. Any field that
109 * is left at zero will use the CPU's settings.
110 *
111 * This contains the board specific settings that affect how the CPU
112 * drivers chose settings. These include the memory refresh and IO
113 * timing information.
64 * 114 *
65 * per-board configuraton information, such as memory refresh and 115 * Registration depends on the driver being used, the ARMCLK only
66 * how to initialise IO timings. 116 * implementation does not currently need this but the older style
117 * driver requires this to be available.
67 */ 118 */
68struct s3c_cpufreq_board { 119struct s3c_cpufreq_board {
69 unsigned int refresh; /* refresh period in ns */ 120 unsigned int refresh;
70 unsigned int auto_io:1; /* automatically init io timings. */ 121 unsigned int auto_io:1; /* automatically init io timings. */
71 unsigned int need_io:1; /* set if needs io timing support. */ 122 unsigned int need_io:1; /* set if needs io timing support. */
72 123
diff --git a/arch/arm/plat-s3c/include/plat/cpu.h b/arch/arm/plat-s3c/include/plat/cpu.h
index be541cbba070..fbc3d498e02e 100644
--- a/arch/arm/plat-s3c/include/plat/cpu.h
+++ b/arch/arm/plat-s3c/include/plat/cpu.h
@@ -65,6 +65,7 @@ extern struct sys_timer s3c24xx_timer;
65/* system device classes */ 65/* system device classes */
66 66
67extern struct sysdev_class s3c2410_sysclass; 67extern struct sysdev_class s3c2410_sysclass;
68extern struct sysdev_class s3c2410a_sysclass;
68extern struct sysdev_class s3c2412_sysclass; 69extern struct sysdev_class s3c2412_sysclass;
69extern struct sysdev_class s3c2440_sysclass; 70extern struct sysdev_class s3c2440_sysclass;
70extern struct sysdev_class s3c2442_sysclass; 71extern struct sysdev_class s3c2442_sysclass;
diff --git a/arch/arm/plat-s3c/include/plat/map-base.h b/arch/arm/plat-s3c/include/plat/map-base.h
index b84289d32a54..250be311c85b 100644
--- a/arch/arm/plat-s3c/include/plat/map-base.h
+++ b/arch/arm/plat-s3c/include/plat/map-base.h
@@ -32,9 +32,15 @@
32 32
33#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */ 33#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */
34#define S3C_VA_SYS S3C_ADDR(0x00100000) /* system control */ 34#define S3C_VA_SYS S3C_ADDR(0x00100000) /* system control */
35#define S3C_VA_MEM S3C_ADDR(0x00200000) /* system control */ 35#define S3C_VA_MEM S3C_ADDR(0x00200000) /* memory control */
36#define S3C_VA_TIMER S3C_ADDR(0x00300000) /* timer block */ 36#define S3C_VA_TIMER S3C_ADDR(0x00300000) /* timer block */
37#define S3C_VA_WATCHDOG S3C_ADDR(0x00400000) /* watchdog */ 37#define S3C_VA_WATCHDOG S3C_ADDR(0x00400000) /* watchdog */
38#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */ 38#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */
39 39
40/* This is used for the CPU specific mappings that may be needed, so that
41 * they do not need to directly used S3C_ADDR() and thus make it easier to
42 * modify the space for mapping.
43 */
44#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))
45
40#endif /* __ASM_PLAT_MAP_H */ 46#endif /* __ASM_PLAT_MAP_H */
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index aaae456018b5..2f9145683596 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -34,6 +34,40 @@ config CPU_S3C244X
34 help 34 help
35 Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. 35 Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems.
36 36
37config S3C2440_CPUFREQ
38 bool "S3C2440/S3C2442 CPU Frequency scaling support"
39 depends on CPU_FREQ_S3C24XX && (CPU_S3C2440 || CPU_S3C2442)
40 select S3C2410_CPUFREQ_UTILS
41 default y
42 help
43 CPU Frequency scaling support for S3C2440 and S3C2442 SoC CPUs.
44
45config S3C2440_XTAL_12000000
46 bool
47 help
48 Indicate that the build needs to support 12MHz system
49 crystal.
50
51config S3C2440_XTAL_16934400
52 bool
53 help
54 Indicate that the build needs to support 16.9344MHz system
55 crystal.
56
57config S3C2440_PLL_12000000
58 bool
59 depends on S3C2440_CPUFREQ && S3C2440_XTAL_12000000
60 default y if CPU_FREQ_S3C24XX_PLL
61 help
62 PLL tables for S3C2440 or S3C2442 CPUs with 12MHz crystals.
63
64config S3C2440_PLL_16934400
65 bool
66 depends on S3C2440_CPUFREQ && S3C2440_XTAL_16934400
67 default y if CPU_FREQ_S3C24XX_PLL
68 help
69 PLL tables for S3C2440 or S3C2442 CPUs with 16.934MHz crystals.
70
37config S3C24XX_PWM 71config S3C24XX_PWM
38 bool "PWM device support" 72 bool "PWM device support"
39 select HAVE_PWM 73 select HAVE_PWM
@@ -113,6 +147,31 @@ config S3C24XX_SPI_BUS1_GPD8_GPD9_GPD10
113 147
114# common code for s3c24xx based machines, such as the SMDKs. 148# common code for s3c24xx based machines, such as the SMDKs.
115 149
150# cpu frequency items common between s3c2410 and s3c2440/s3c2442
151
152config S3C2410_IOTIMING
153 bool
154 depends on CPU_FREQ_S3C24XX
155 help
156 Internal node to select io timing code that is common to the s3c2410
157 and s3c2440/s3c2442 cpu frequency support.
158
159config S3C2410_CPUFREQ_UTILS
160 bool
161 depends on CPU_FREQ_S3C24XX
162 help
163 Internal node to select timing code that is common to the s3c2410
164 and s3c2440/s3c244 cpu frequency support.
165
166# cpu frequency support common to s3c2412, s3c2413 and s3c2442
167
168config S3C2412_IOTIMING
169 bool
170 depends on CPU_FREQ_S3C24XX && (CPU_S3C2412 || CPU_S3C2443)
171 help
172 Intel node to select io timing code that is common to the s3c2412
173 and the s3c2443.
174
116config MACH_SMDK 175config MACH_SMDK
117 bool 176 bool
118 help 177 help
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index 59416796fb1d..651315c95ec4 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -20,11 +20,18 @@ obj-y += gpiolib.o
20obj-y += clock.o 20obj-y += clock.o
21obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o 21obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o
22 22
23obj-$(CONFIG_CPU_FREQ_S3C24XX) += cpu-freq.o
24obj-$(CONFIG_CPU_FREQ_S3C24XX_DEBUGFS) += cpu-freq-debugfs.o
25
23# Architecture dependant builds 26# Architecture dependant builds
24 27
25obj-$(CONFIG_CPU_S3C244X) += s3c244x.o 28obj-$(CONFIG_CPU_S3C244X) += s3c244x.o
26obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o 29obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o
27obj-$(CONFIG_CPU_S3C244X) += s3c244x-clock.o 30obj-$(CONFIG_CPU_S3C244X) += s3c244x-clock.o
31obj-$(CONFIG_S3C2440_CPUFREQ) += s3c2440-cpufreq.o
32obj-$(CONFIG_S3C2440_PLL_12000000) += s3c2440-pll-12000000.o
33obj-$(CONFIG_S3C2440_PLL_16934400) += s3c2440-pll-16934400.o
34
28obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o 35obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o
29obj-$(CONFIG_PM) += pm.o 36obj-$(CONFIG_PM) += pm.o
30obj-$(CONFIG_PM) += irq-pm.o 37obj-$(CONFIG_PM) += irq-pm.o
@@ -33,6 +40,9 @@ obj-$(CONFIG_S3C24XX_PWM) += pwm.o
33obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o 40obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o
34obj-$(CONFIG_S3C2410_DMA) += dma.o 41obj-$(CONFIG_S3C2410_DMA) += dma.o
35obj-$(CONFIG_S3C24XX_ADC) += adc.o 42obj-$(CONFIG_S3C24XX_ADC) += adc.o
43obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o
44obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o
45obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o
36 46
37# device specific setup and/or initialisation 47# device specific setup and/or initialisation
38obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o 48obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o
diff --git a/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c b/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
new file mode 100644
index 000000000000..a9276667c2fb
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
@@ -0,0 +1,199 @@
1/* linux/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
2 *
3 * Copyright (c) 2009 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C24XX CPU Frequency scaling - debugfs status support
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/ioport.h>
18#include <linux/cpufreq.h>
19#include <linux/debugfs.h>
20#include <linux/seq_file.h>
21#include <linux/err.h>
22
23#include <plat/cpu-freq-core.h>
24
25static struct dentry *dbgfs_root;
26static struct dentry *dbgfs_file_io;
27static struct dentry *dbgfs_file_info;
28static struct dentry *dbgfs_file_board;
29
30#define print_ns(x) ((x) / 10), ((x) % 10)
31
32static void show_max(struct seq_file *seq, struct s3c_freq *f)
33{
34 seq_printf(seq, "MAX: F=%lu, H=%lu, P=%lu, A=%lu\n",
35 f->fclk, f->hclk, f->pclk, f->armclk);
36}
37
38static int board_show(struct seq_file *seq, void *p)
39{
40 struct s3c_cpufreq_config *cfg;
41 struct s3c_cpufreq_board *brd;
42
43 cfg = s3c_cpufreq_getconfig();
44 if (!cfg) {
45 seq_printf(seq, "no configuration registered\n");
46 return 0;
47 }
48
49 brd = cfg->board;
50 if (!brd) {
51 seq_printf(seq, "no board definition set?\n");
52 return 0;
53 }
54
55 seq_printf(seq, "SDRAM refresh %u ns\n", brd->refresh);
56 seq_printf(seq, "auto_io=%u\n", brd->auto_io);
57 seq_printf(seq, "need_io=%u\n", brd->need_io);
58
59 show_max(seq, &brd->max);
60
61
62 return 0;
63}
64
65static int fops_board_open(struct inode *inode, struct file *file)
66{
67 return single_open(file, board_show, NULL);
68}
69
70static const struct file_operations fops_board = {
71 .open = fops_board_open,
72 .read = seq_read,
73 .llseek = seq_lseek,
74 .release = single_release,
75 .owner = THIS_MODULE,
76};
77
78static int info_show(struct seq_file *seq, void *p)
79{
80 struct s3c_cpufreq_config *cfg;
81
82 cfg = s3c_cpufreq_getconfig();
83 if (!cfg) {
84 seq_printf(seq, "no configuration registered\n");
85 return 0;
86 }
87
88 seq_printf(seq, " FCLK %ld Hz\n", cfg->freq.fclk);
89 seq_printf(seq, " HCLK %ld Hz (%lu.%lu ns)\n",
90 cfg->freq.hclk, print_ns(cfg->freq.hclk_tns));
91 seq_printf(seq, " PCLK %ld Hz\n", cfg->freq.hclk);
92 seq_printf(seq, "ARMCLK %ld Hz\n", cfg->freq.armclk);
93 seq_printf(seq, "\n");
94
95 show_max(seq, &cfg->max);
96
97 seq_printf(seq, "Divisors: P=%d, H=%d, A=%d, dvs=%s\n",
98 cfg->divs.h_divisor, cfg->divs.p_divisor,
99 cfg->divs.arm_divisor, cfg->divs.dvs ? "on" : "off");
100 seq_printf(seq, "\n");
101
102 seq_printf(seq, "lock_pll=%u\n", cfg->lock_pll);
103
104 return 0;
105}
106
107static int fops_info_open(struct inode *inode, struct file *file)
108{
109 return single_open(file, info_show, NULL);
110}
111
112static const struct file_operations fops_info = {
113 .open = fops_info_open,
114 .read = seq_read,
115 .llseek = seq_lseek,
116 .release = single_release,
117 .owner = THIS_MODULE,
118};
119
120static int io_show(struct seq_file *seq, void *p)
121{
122 void (*show_bank)(struct seq_file *, struct s3c_cpufreq_config *, union s3c_iobank *);
123 struct s3c_cpufreq_config *cfg;
124 struct s3c_iotimings *iot;
125 union s3c_iobank *iob;
126 int bank;
127
128 cfg = s3c_cpufreq_getconfig();
129 if (!cfg) {
130 seq_printf(seq, "no configuration registered\n");
131 return 0;
132 }
133
134 show_bank = cfg->info->debug_io_show;
135 if (!show_bank) {
136 seq_printf(seq, "no code to show bank timing\n");
137 return 0;
138 }
139
140 iot = s3c_cpufreq_getiotimings();
141 if (!iot) {
142 seq_printf(seq, "no io timings registered\n");
143 return 0;
144 }
145
146 seq_printf(seq, "hclk period is %lu.%lu ns\n", print_ns(cfg->freq.hclk_tns));
147
148 for (bank = 0; bank < MAX_BANKS; bank++) {
149 iob = &iot->bank[bank];
150
151 seq_printf(seq, "bank %d: ", bank);
152
153 if (!iob->io_2410) {
154 seq_printf(seq, "nothing set\n");
155 continue;
156 }
157
158 show_bank(seq, cfg, iob);
159 }
160
161 return 0;
162}
163
164static int fops_io_open(struct inode *inode, struct file *file)
165{
166 return single_open(file, io_show, NULL);
167}
168
169static const struct file_operations fops_io = {
170 .open = fops_io_open,
171 .read = seq_read,
172 .llseek = seq_lseek,
173 .release = single_release,
174 .owner = THIS_MODULE,
175};
176
177
178static int __init s3c_freq_debugfs_init(void)
179{
180 dbgfs_root = debugfs_create_dir("s3c-cpufreq", NULL);
181 if (IS_ERR(dbgfs_root)) {
182 printk(KERN_ERR "%s: error creating debugfs root\n", __func__);
183 return PTR_ERR(dbgfs_root);
184 }
185
186 dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root,
187 NULL, &fops_io);
188
189 dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root,
190 NULL, &fops_info);
191
192 dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root,
193 NULL, &fops_board);
194
195 return 0;
196}
197
198late_initcall(s3c_freq_debugfs_init);
199
diff --git a/arch/arm/plat-s3c24xx/cpu-freq.c b/arch/arm/plat-s3c24xx/cpu-freq.c
new file mode 100644
index 000000000000..4f1b789a1173
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/cpu-freq.c
@@ -0,0 +1,716 @@
1/* linux/arch/arm/plat-s3c24xx/cpu-freq.c
2 *
3 * Copyright (c) 2006,2007,2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C24XX CPU Frequency scaling
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/ioport.h>
18#include <linux/cpufreq.h>
19#include <linux/cpu.h>
20#include <linux/clk.h>
21#include <linux/err.h>
22#include <linux/io.h>
23#include <linux/sysdev.h>
24#include <linux/kobject.h>
25#include <linux/sysfs.h>
26
27#include <asm/mach/arch.h>
28#include <asm/mach/map.h>
29
30#include <plat/cpu.h>
31#include <plat/clock.h>
32#include <plat/cpu-freq-core.h>
33
34#include <mach/regs-clock.h>
35
36/* note, cpufreq support deals in kHz, no Hz */
37
38static struct cpufreq_driver s3c24xx_driver;
39static struct s3c_cpufreq_config cpu_cur;
40static struct s3c_iotimings s3c24xx_iotiming;
41static struct cpufreq_frequency_table *pll_reg;
42static unsigned int last_target = ~0;
43static unsigned int ftab_size;
44static struct cpufreq_frequency_table *ftab;
45
46static struct clk *_clk_mpll;
47static struct clk *_clk_xtal;
48static struct clk *clk_fclk;
49static struct clk *clk_hclk;
50static struct clk *clk_pclk;
51static struct clk *clk_arm;
52
53#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
54struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void)
55{
56 return &cpu_cur;
57}
58
59struct s3c_iotimings *s3c_cpufreq_getiotimings(void)
60{
61 return &s3c24xx_iotiming;
62}
63#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */
64
65static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg)
66{
67 unsigned long fclk, pclk, hclk, armclk;
68
69 cfg->freq.fclk = fclk = clk_get_rate(clk_fclk);
70 cfg->freq.hclk = hclk = clk_get_rate(clk_hclk);
71 cfg->freq.pclk = pclk = clk_get_rate(clk_pclk);
72 cfg->freq.armclk = armclk = clk_get_rate(clk_arm);
73
74 cfg->pll.index = __raw_readl(S3C2410_MPLLCON);
75 cfg->pll.frequency = fclk;
76
77 cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
78
79 cfg->divs.h_divisor = fclk / hclk;
80 cfg->divs.p_divisor = fclk / pclk;
81}
82
83static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg)
84{
85 unsigned long pll = cfg->pll.frequency;
86
87 cfg->freq.fclk = pll;
88 cfg->freq.hclk = pll / cfg->divs.h_divisor;
89 cfg->freq.pclk = pll / cfg->divs.p_divisor;
90
91 /* convert hclk into 10ths of nanoseconds for io calcs */
92 cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
93}
94
95static inline int closer(unsigned int target, unsigned int n, unsigned int c)
96{
97 int diff_cur = abs(target - c);
98 int diff_new = abs(target - n);
99
100 return (diff_new < diff_cur);
101}
102
103static void s3c_cpufreq_show(const char *pfx,
104 struct s3c_cpufreq_config *cfg)
105{
106 s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)\n",
107 pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk,
108 cfg->freq.hclk, cfg->divs.h_divisor,
109 cfg->freq.pclk, cfg->divs.p_divisor);
110}
111
112/* functions to wrapper the driver info calls to do the cpu specific work */
113
114static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg)
115{
116 if (cfg->info->set_iotiming)
117 (cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming);
118}
119
120static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg)
121{
122 if (cfg->info->calc_iotiming)
123 return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming);
124
125 return 0;
126}
127
128static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
129{
130 (cfg->info->set_refresh)(cfg);
131}
132
133static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
134{
135 (cfg->info->set_divs)(cfg);
136}
137
138static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
139{
140 return (cfg->info->calc_divs)(cfg);
141}
142
143static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg)
144{
145 (cfg->info->set_fvco)(cfg);
146}
147
148static inline void s3c_cpufreq_resume_clocks(void)
149{
150 cpu_cur.info->resume_clocks();
151}
152
153static inline void s3c_cpufreq_updateclk(struct clk *clk,
154 unsigned int freq)
155{
156 clk_set_rate(clk, freq);
157}
158
159static int s3c_cpufreq_settarget(struct cpufreq_policy *policy,
160 unsigned int target_freq,
161 struct cpufreq_frequency_table *pll)
162{
163 struct s3c_cpufreq_freqs freqs;
164 struct s3c_cpufreq_config cpu_new;
165 unsigned long flags;
166
167 cpu_new = cpu_cur; /* copy new from current */
168
169 s3c_cpufreq_show("cur", &cpu_cur);
170
171 /* TODO - check for DMA currently outstanding */
172
173 cpu_new.pll = pll ? *pll : cpu_cur.pll;
174
175 if (pll)
176 freqs.pll_changing = 1;
177
178 /* update our frequencies */
179
180 cpu_new.freq.armclk = target_freq;
181 cpu_new.freq.fclk = cpu_new.pll.frequency;
182
183 if (s3c_cpufreq_calcdivs(&cpu_new) < 0) {
184 printk(KERN_ERR "no divisors for %d\n", target_freq);
185 goto err_notpossible;
186 }
187
188 s3c_freq_dbg("%s: got divs\n", __func__);
189
190 s3c_cpufreq_calc(&cpu_new);
191
192 s3c_freq_dbg("%s: calculated frequencies for new\n", __func__);
193
194 if (cpu_new.freq.hclk != cpu_cur.freq.hclk) {
195 if (s3c_cpufreq_calcio(&cpu_new) < 0) {
196 printk(KERN_ERR "%s: no IO timings\n", __func__);
197 goto err_notpossible;
198 }
199 }
200
201 s3c_cpufreq_show("new", &cpu_new);
202
203 /* setup our cpufreq parameters */
204
205 freqs.old = cpu_cur.freq;
206 freqs.new = cpu_new.freq;
207
208 freqs.freqs.cpu = 0;
209 freqs.freqs.old = cpu_cur.freq.armclk / 1000;
210 freqs.freqs.new = cpu_new.freq.armclk / 1000;
211
212 /* update f/h/p clock settings before we issue the change
213 * notification, so that drivers do not need to do anything
214 * special if they want to recalculate on CPUFREQ_PRECHANGE. */
215
216 s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency);
217 s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk);
218 s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk);
219 s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk);
220
221 /* start the frequency change */
222
223 if (policy)
224 cpufreq_notify_transition(&freqs.freqs, CPUFREQ_PRECHANGE);
225
226 /* If hclk is staying the same, then we do not need to
227 * re-write the IO or the refresh timings whilst we are changing
228 * speed. */
229
230 local_irq_save(flags);
231
232 /* is our memory clock slowing down? */
233 if (cpu_new.freq.hclk < cpu_cur.freq.hclk) {
234 s3c_cpufreq_setrefresh(&cpu_new);
235 s3c_cpufreq_setio(&cpu_new);
236 }
237
238 if (cpu_new.freq.fclk == cpu_cur.freq.fclk) {
239 /* not changing PLL, just set the divisors */
240
241 s3c_cpufreq_setdivs(&cpu_new);
242 } else {
243 if (cpu_new.freq.fclk < cpu_cur.freq.fclk) {
244 /* slow the cpu down, then set divisors */
245
246 s3c_cpufreq_setfvco(&cpu_new);
247 s3c_cpufreq_setdivs(&cpu_new);
248 } else {
249 /* set the divisors, then speed up */
250
251 s3c_cpufreq_setdivs(&cpu_new);
252 s3c_cpufreq_setfvco(&cpu_new);
253 }
254 }
255
256 /* did our memory clock speed up */
257 if (cpu_new.freq.hclk > cpu_cur.freq.hclk) {
258 s3c_cpufreq_setrefresh(&cpu_new);
259 s3c_cpufreq_setio(&cpu_new);
260 }
261
262 /* update our current settings */
263 cpu_cur = cpu_new;
264
265 local_irq_restore(flags);
266
267 /* notify everyone we've done this */
268 if (policy)
269 cpufreq_notify_transition(&freqs.freqs, CPUFREQ_POSTCHANGE);
270
271 s3c_freq_dbg("%s: finished\n", __func__);
272 return 0;
273
274 err_notpossible:
275 printk(KERN_ERR "no compatible settings for %d\n", target_freq);
276 return -EINVAL;
277}
278
279/* s3c_cpufreq_target
280 *
281 * called by the cpufreq core to adjust the frequency that the CPU
282 * is currently running at.
283 */
284
285static int s3c_cpufreq_target(struct cpufreq_policy *policy,
286 unsigned int target_freq,
287 unsigned int relation)
288{
289 struct cpufreq_frequency_table *pll;
290 unsigned int index;
291
292 /* avoid repeated calls which cause a needless amout of duplicated
293 * logging output (and CPU time as the calculation process is
294 * done) */
295 if (target_freq == last_target)
296 return 0;
297
298 last_target = target_freq;
299
300 s3c_freq_dbg("%s: policy %p, target %u, relation %u\n",
301 __func__, policy, target_freq, relation);
302
303 if (ftab) {
304 if (cpufreq_frequency_table_target(policy, ftab,
305 target_freq, relation,
306 &index)) {
307 s3c_freq_dbg("%s: table failed\n", __func__);
308 return -EINVAL;
309 }
310
311 s3c_freq_dbg("%s: adjust %d to entry %d (%u)\n", __func__,
312 target_freq, index, ftab[index].frequency);
313 target_freq = ftab[index].frequency;
314 }
315
316 target_freq *= 1000; /* convert target to Hz */
317
318 /* find the settings for our new frequency */
319
320 if (!pll_reg || cpu_cur.lock_pll) {
321 /* either we've not got any PLL values, or we've locked
322 * to the current one. */
323 pll = NULL;
324 } else {
325 struct cpufreq_policy tmp_policy;
326 int ret;
327
328 /* we keep the cpu pll table in Hz, to ensure we get an
329 * accurate value for the PLL output. */
330
331 tmp_policy.min = policy->min * 1000;
332 tmp_policy.max = policy->max * 1000;
333 tmp_policy.cpu = policy->cpu;
334
335 /* cpufreq_frequency_table_target uses a pointer to 'index'
336 * which is the number of the table entry, not the value of
337 * the table entry's index field. */
338
339 ret = cpufreq_frequency_table_target(&tmp_policy, pll_reg,
340 target_freq, relation,
341 &index);
342
343 if (ret < 0) {
344 printk(KERN_ERR "%s: no PLL available\n", __func__);
345 goto err_notpossible;
346 }
347
348 pll = pll_reg + index;
349
350 s3c_freq_dbg("%s: target %u => %u\n",
351 __func__, target_freq, pll->frequency);
352
353 target_freq = pll->frequency;
354 }
355
356 return s3c_cpufreq_settarget(policy, target_freq, pll);
357
358 err_notpossible:
359 printk(KERN_ERR "no compatible settings for %d\n", target_freq);
360 return -EINVAL;
361}
362
363static unsigned int s3c_cpufreq_get(unsigned int cpu)
364{
365 return clk_get_rate(clk_arm) / 1000;
366}
367
368struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name)
369{
370 struct clk *clk;
371
372 clk = clk_get(dev, name);
373 if (IS_ERR(clk))
374 printk(KERN_ERR "cpufreq: failed to get clock '%s'\n", name);
375
376 return clk;
377}
378
379static int s3c_cpufreq_init(struct cpufreq_policy *policy)
380{
381 printk(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
382
383 if (policy->cpu != 0)
384 return -EINVAL;
385
386 policy->cur = s3c_cpufreq_get(0);
387 policy->min = policy->cpuinfo.min_freq = 0;
388 policy->max = policy->cpuinfo.max_freq = cpu_cur.info->max.fclk / 1000;
389 policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
390
391 /* feed the latency information from the cpu driver */
392 policy->cpuinfo.transition_latency = cpu_cur.info->latency;
393
394 if (ftab)
395 cpufreq_frequency_table_cpuinfo(policy, ftab);
396
397 return 0;
398}
399
400static __init int s3c_cpufreq_initclks(void)
401{
402 _clk_mpll = s3c_cpufreq_clk_get(NULL, "mpll");
403 _clk_xtal = s3c_cpufreq_clk_get(NULL, "xtal");
404 clk_fclk = s3c_cpufreq_clk_get(NULL, "fclk");
405 clk_hclk = s3c_cpufreq_clk_get(NULL, "hclk");
406 clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk");
407 clk_arm = s3c_cpufreq_clk_get(NULL, "armclk");
408
409 if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) ||
410 IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) {
411 printk(KERN_ERR "%s: could not get clock(s)\n", __func__);
412 return -ENOENT;
413 }
414
415 printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__,
416 clk_get_rate(clk_fclk) / 1000,
417 clk_get_rate(clk_hclk) / 1000,
418 clk_get_rate(clk_pclk) / 1000,
419 clk_get_rate(clk_arm) / 1000);
420
421 return 0;
422}
423
424static int s3c_cpufreq_verify(struct cpufreq_policy *policy)
425{
426 if (policy->cpu != 0)
427 return -EINVAL;
428
429 return 0;
430}
431
432#ifdef CONFIG_PM
433static struct cpufreq_frequency_table suspend_pll;
434static unsigned int suspend_freq;
435
436static int s3c_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg)
437{
438 suspend_pll.frequency = clk_get_rate(_clk_mpll);
439 suspend_pll.index = __raw_readl(S3C2410_MPLLCON);
440 suspend_freq = s3c_cpufreq_get(0) * 1000;
441
442 return 0;
443}
444
445static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
446{
447 int ret;
448
449 s3c_freq_dbg("%s: resuming with policy %p\n", __func__, policy);
450
451 last_target = ~0; /* invalidate last_target setting */
452
453 /* first, find out what speed we resumed at. */
454 s3c_cpufreq_resume_clocks();
455
456 /* whilst we will be called later on, we try and re-set the
457 * cpu frequencies as soon as possible so that we do not end
458 * up resuming devices and then immediatley having to re-set
459 * a number of settings once these devices have restarted.
460 *
461 * as a note, it is expected devices are not used until they
462 * have been un-suspended and at that time they should have
463 * used the updated clock settings.
464 */
465
466 ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll);
467 if (ret) {
468 printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__);
469 return ret;
470 }
471
472 return 0;
473}
474#else
475#define s3c_cpufreq_resume NULL
476#define s3c_cpufreq_suspend NULL
477#endif
478
479static struct cpufreq_driver s3c24xx_driver = {
480 .flags = CPUFREQ_STICKY,
481 .verify = s3c_cpufreq_verify,
482 .target = s3c_cpufreq_target,
483 .get = s3c_cpufreq_get,
484 .init = s3c_cpufreq_init,
485 .suspend = s3c_cpufreq_suspend,
486 .resume = s3c_cpufreq_resume,
487 .name = "s3c24xx",
488};
489
490
491int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info)
492{
493 if (!info || !info->name) {
494 printk(KERN_ERR "%s: failed to pass valid information\n",
495 __func__);
496 return -EINVAL;
497 }
498
499 printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n",
500 info->name);
501
502 /* check our driver info has valid data */
503
504 BUG_ON(info->set_refresh == NULL);
505 BUG_ON(info->set_divs == NULL);
506 BUG_ON(info->calc_divs == NULL);
507
508 /* info->set_fvco is optional, depending on whether there
509 * is a need to set the clock code. */
510
511 cpu_cur.info = info;
512
513 /* Note, driver registering should probably update locktime */
514
515 return 0;
516}
517
518int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
519{
520 struct s3c_cpufreq_board *ours;
521
522 if (!board) {
523 printk(KERN_INFO "%s: no board data\n", __func__);
524 return -EINVAL;
525 }
526
527 /* Copy the board information so that each board can make this
528 * initdata. */
529
530 ours = kzalloc(sizeof(struct s3c_cpufreq_board), GFP_KERNEL);
531 if (ours == NULL) {
532 printk(KERN_ERR "%s: no memory\n", __func__);
533 return -ENOMEM;
534 }
535
536 *ours = *board;
537 cpu_cur.board = ours;
538
539 return 0;
540}
541
542int __init s3c_cpufreq_auto_io(void)
543{
544 int ret;
545
546 if (!cpu_cur.info->get_iotiming) {
547 printk(KERN_ERR "%s: get_iotiming undefined\n", __func__);
548 return -ENOENT;
549 }
550
551 printk(KERN_INFO "%s: working out IO settings\n", __func__);
552
553 ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming);
554 if (ret)
555 printk(KERN_ERR "%s: failed to get timings\n", __func__);
556
557 return ret;
558}
559
560/* if one or is zero, then return the other, otherwise return the min */
561#define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
562
563/**
564 * s3c_cpufreq_freq_min - find the minimum settings for the given freq.
565 * @dst: The destination structure
566 * @a: One argument.
567 * @b: The other argument.
568 *
569 * Create a minimum of each frequency entry in the 'struct s3c_freq',
570 * unless the entry is zero when it is ignored and the non-zero argument
571 * used.
572 */
573static void s3c_cpufreq_freq_min(struct s3c_freq *dst,
574 struct s3c_freq *a, struct s3c_freq *b)
575{
576 dst->fclk = do_min(a->fclk, b->fclk);
577 dst->hclk = do_min(a->hclk, b->hclk);
578 dst->pclk = do_min(a->pclk, b->pclk);
579 dst->armclk = do_min(a->armclk, b->armclk);
580}
581
582static inline u32 calc_locktime(u32 freq, u32 time_us)
583{
584 u32 result;
585
586 result = freq * time_us;
587 result = DIV_ROUND_UP(result, 1000 * 1000);
588
589 return result;
590}
591
592static void s3c_cpufreq_update_loctkime(void)
593{
594 unsigned int bits = cpu_cur.info->locktime_bits;
595 u32 rate = (u32)clk_get_rate(_clk_xtal);
596 u32 val;
597
598 if (bits == 0) {
599 WARN_ON(1);
600 return;
601 }
602
603 val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits;
604 val |= calc_locktime(rate, cpu_cur.info->locktime_m);
605
606 printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val);
607 __raw_writel(val, S3C2410_LOCKTIME);
608}
609
610static int s3c_cpufreq_build_freq(void)
611{
612 int size, ret;
613
614 if (!cpu_cur.info->calc_freqtable)
615 return -EINVAL;
616
617 kfree(ftab);
618 ftab = NULL;
619
620 size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
621 size++;
622
623 ftab = kmalloc(sizeof(struct cpufreq_frequency_table) * size, GFP_KERNEL);
624 if (!ftab) {
625 printk(KERN_ERR "%s: no memory for tables\n", __func__);
626 return -ENOMEM;
627 }
628
629 ftab_size = size;
630
631 ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size);
632 s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END);
633
634 return 0;
635}
636
637static int __init s3c_cpufreq_initcall(void)
638{
639 int ret = 0;
640
641 if (cpu_cur.info && cpu_cur.board) {
642 ret = s3c_cpufreq_initclks();
643 if (ret)
644 goto out;
645
646 /* get current settings */
647 s3c_cpufreq_getcur(&cpu_cur);
648 s3c_cpufreq_show("cur", &cpu_cur);
649
650 if (cpu_cur.board->auto_io) {
651 ret = s3c_cpufreq_auto_io();
652 if (ret) {
653 printk(KERN_ERR "%s: failed to get io timing\n",
654 __func__);
655 goto out;
656 }
657 }
658
659 if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) {
660 printk(KERN_ERR "%s: no IO support registered\n",
661 __func__);
662 ret = -EINVAL;
663 goto out;
664 }
665
666 if (!cpu_cur.info->need_pll)
667 cpu_cur.lock_pll = 1;
668
669 s3c_cpufreq_update_loctkime();
670
671 s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max,
672 &cpu_cur.info->max);
673
674 if (cpu_cur.info->calc_freqtable)
675 s3c_cpufreq_build_freq();
676
677 ret = cpufreq_register_driver(&s3c24xx_driver);
678 }
679
680 out:
681 return ret;
682}
683
684late_initcall(s3c_cpufreq_initcall);
685
686/**
687 * s3c_plltab_register - register CPU PLL table.
688 * @plls: The list of PLL entries.
689 * @plls_no: The size of the PLL entries @plls.
690 *
691 * Register the given set of PLLs with the system.
692 */
693int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
694 unsigned int plls_no)
695{
696 struct cpufreq_frequency_table *vals;
697 unsigned int size;
698
699 size = sizeof(struct cpufreq_frequency_table) * (plls_no + 1);
700
701 vals = kmalloc(size, GFP_KERNEL);
702 if (vals) {
703 memcpy(vals, plls, size);
704 pll_reg = vals;
705
706 /* write a terminating entry, we don't store it in the
707 * table that is stored in the kernel */
708 vals += plls_no;
709 vals->frequency = CPUFREQ_TABLE_END;
710
711 printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no);
712 } else
713 printk(KERN_ERR "cpufreq: no memory for PLL tables\n");
714
715 return vals ? 0 : -ENOMEM;
716}
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c
index 1932b7e0da15..5447e60f3936 100644
--- a/arch/arm/plat-s3c24xx/cpu.c
+++ b/arch/arm/plat-s3c24xx/cpu.c
@@ -81,7 +81,7 @@ static struct cpu_table cpu_ids[] __initdata = {
81 .map_io = s3c2410_map_io, 81 .map_io = s3c2410_map_io,
82 .init_clocks = s3c2410_init_clocks, 82 .init_clocks = s3c2410_init_clocks,
83 .init_uarts = s3c2410_init_uarts, 83 .init_uarts = s3c2410_init_uarts,
84 .init = s3c2410_init, 84 .init = s3c2410a_init,
85 .name = name_s3c2410a 85 .name = name_s3c2410a
86 }, 86 },
87 { 87 {
diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
new file mode 100644
index 000000000000..efeb025affc7
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
@@ -0,0 +1,282 @@
1/* arch/arm/plat-s3c/include/plat/cpu-freq.h
2 *
3 * Copyright (c) 2006,2007,2009 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C CPU frequency scaling support - core support
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <plat/cpu-freq.h>
15
16struct seq_file;
17
18#define MAX_BANKS (8)
19#define S3C2412_MAX_IO (8)
20
21/**
22 * struct s3c2410_iobank_timing - IO bank timings for S3C2410 style timings
23 * @bankcon: The cached version of settings in this structure.
24 * @tacp:
25 * @tacs: Time from address valid to nCS asserted.
26 * @tcos: Time from nCS asserted to nOE or nWE asserted.
27 * @tacc: Time that nOE or nWE is asserted.
28 * @tcoh: Time nCS is held after nOE or nWE are released.
29 * @tcah: Time address is held for after
30 * @nwait_en: Whether nWAIT is enabled for this bank.
31 *
32 * This structure represents the IO timings for a S3C2410 style IO bank
33 * used by the CPU frequency support if it needs to change the settings
34 * of the IO.
35 */
36struct s3c2410_iobank_timing {
37 unsigned long bankcon;
38 unsigned int tacp;
39 unsigned int tacs;
40 unsigned int tcos;
41 unsigned int tacc;
42 unsigned int tcoh; /* nCS hold afrer nOE/nWE */
43 unsigned int tcah; /* Address hold after nCS */
44 unsigned char nwait_en; /* nWait enabled for bank. */
45};
46
47/**
48 * struct s3c2412_iobank_timing - io timings for PL092 (S3C2412) style IO
49 * @idcy: The idle cycle time between transactions.
50 * @wstrd: nCS release to end of read cycle.
51 * @wstwr: nCS release to end of write cycle.
52 * @wstoen: nCS assertion to nOE assertion time.
53 * @wstwen: nCS assertion to nWE assertion time.
54 * @wstbrd: Burst ready delay.
55 * @smbidcyr: Register cache for smbidcyr value.
56 * @smbwstrd: Register cache for smbwstrd value.
57 * @smbwstwr: Register cache for smbwstwr value.
58 * @smbwstoen: Register cache for smbwstoen value.
59 * @smbwstwen: Register cache for smbwstwen value.
60 * @smbwstbrd: Register cache for smbwstbrd value.
61 *
62 * Timing information for a IO bank on an S3C2412 or similar system which
63 * uses a PL093 block.
64 */
65struct s3c2412_iobank_timing {
66 unsigned int idcy;
67 unsigned int wstrd;
68 unsigned int wstwr;
69 unsigned int wstoen;
70 unsigned int wstwen;
71 unsigned int wstbrd;
72
73 /* register cache */
74 unsigned char smbidcyr;
75 unsigned char smbwstrd;
76 unsigned char smbwstwr;
77 unsigned char smbwstoen;
78 unsigned char smbwstwen;
79 unsigned char smbwstbrd;
80};
81
82union s3c_iobank {
83 struct s3c2410_iobank_timing *io_2410;
84 struct s3c2412_iobank_timing *io_2412;
85};
86
87/**
88 * struct s3c_iotimings - Chip IO timings holder
89 * @bank: The timings for each IO bank.
90 */
91struct s3c_iotimings {
92 union s3c_iobank bank[MAX_BANKS];
93};
94
95/**
96 * struct s3c_plltab - PLL table information.
97 * @vals: List of PLL values.
98 * @size: Size of the PLL table @vals.
99 */
100struct s3c_plltab {
101 struct s3c_pllval *vals;
102 int size;
103};
104
105/**
106 * struct s3c_cpufreq_config - current cpu frequency configuration
107 * @freq: The current settings for the core clocks.
108 * @max: Maxium settings, derived from core, board and user settings.
109 * @pll: The PLL table entry for the current PLL settings.
110 * @divs: The divisor settings for the core clocks.
111 * @info: The current core driver information.
112 * @board: The information for the board we are running on.
113 * @lock_pll: Set if the PLL settings cannot be changed.
114 *
115 * This is for the core drivers that need to know information about
116 * the current settings and values. It should not be needed by any
117 * device drivers.
118*/
119struct s3c_cpufreq_config {
120 struct s3c_freq freq;
121 struct s3c_freq max;
122 struct cpufreq_frequency_table pll;
123 struct s3c_clkdivs divs;
124 struct s3c_cpufreq_info *info; /* for core, not drivers */
125 struct s3c_cpufreq_board *board;
126
127 unsigned int lock_pll:1;
128};
129
130/**
131 * struct s3c_cpufreq_info - Information for the CPU frequency driver.
132 * @name: The name of this implementation.
133 * @max: The maximum frequencies for the system.
134 * @latency: Transition latency to give to cpufreq.
135 * @locktime_m: The lock-time in uS for the MPLL.
136 * @locktime_u: The lock-time in uS for the UPLL.
137 * @locttime_bits: The number of bits each LOCKTIME field.
138 * @need_pll: Set if this driver needs to change the PLL values to acheive
139 * any frequency changes. This is really only need by devices like the
140 * S3C2410 where there is no or limited divider between the PLL and the
141 * ARMCLK.
142 * @resume_clocks: Update the clocks on resume.
143 * @get_iotiming: Get the current IO timing data, mainly for use at start.
144 * @set_iotiming: Update the IO timings from the cached copies calculated
145 * from the @calc_iotiming entry when changing the frequency.
146 * @calc_iotiming: Calculate and update the cached copies of the IO timings
147 * from the newly calculated frequencies.
148 * @calc_freqtable: Calculate (fill in) the given frequency table from the
149 * current frequency configuration. If the table passed in is NULL,
150 * then the return is the number of elements to be filled for allocation
151 * of the table.
152 * @set_refresh: Set the memory refresh configuration.
153 * @set_fvco: Set the PLL frequencies.
154 * @set_divs: Update the clock divisors.
155 * @calc_divs: Calculate the clock divisors.
156 */
157struct s3c_cpufreq_info {
158 const char *name;
159 struct s3c_freq max;
160
161 unsigned int latency;
162
163 unsigned int locktime_m;
164 unsigned int locktime_u;
165 unsigned char locktime_bits;
166
167 unsigned int need_pll:1;
168
169 /* driver routines */
170
171 void (*resume_clocks)(void);
172
173 int (*get_iotiming)(struct s3c_cpufreq_config *cfg,
174 struct s3c_iotimings *timings);
175
176 void (*set_iotiming)(struct s3c_cpufreq_config *cfg,
177 struct s3c_iotimings *timings);
178
179 int (*calc_iotiming)(struct s3c_cpufreq_config *cfg,
180 struct s3c_iotimings *timings);
181
182 int (*calc_freqtable)(struct s3c_cpufreq_config *cfg,
183 struct cpufreq_frequency_table *t,
184 size_t table_size);
185
186 void (*debug_io_show)(struct seq_file *seq,
187 struct s3c_cpufreq_config *cfg,
188 union s3c_iobank *iob);
189
190 void (*set_refresh)(struct s3c_cpufreq_config *cfg);
191 void (*set_fvco)(struct s3c_cpufreq_config *cfg);
192 void (*set_divs)(struct s3c_cpufreq_config *cfg);
193 int (*calc_divs)(struct s3c_cpufreq_config *cfg);
194};
195
196extern int s3c_cpufreq_register(struct s3c_cpufreq_info *info);
197
198extern int s3c_plltab_register(struct cpufreq_frequency_table *plls, unsigned int plls_no);
199
200/* exports and utilities for debugfs */
201extern struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void);
202extern struct s3c_iotimings *s3c_cpufreq_getiotimings(void);
203
204extern void s3c2410_iotiming_debugfs(struct seq_file *seq,
205 struct s3c_cpufreq_config *cfg,
206 union s3c_iobank *iob);
207
208extern void s3c2412_iotiming_debugfs(struct seq_file *seq,
209 struct s3c_cpufreq_config *cfg,
210 union s3c_iobank *iob);
211
212#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
213#define s3c_cpufreq_debugfs_call(x) x
214#else
215#define s3c_cpufreq_debugfs_call(x) NULL
216#endif
217
218/* Useful utility functions. */
219
220extern struct clk *s3c_cpufreq_clk_get(struct device *, const char *);
221
222/* S3C2410 and compatible exported functions */
223
224extern void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg);
225
226extern int s3c2410_iotiming_calc(struct s3c_cpufreq_config *cfg,
227 struct s3c_iotimings *iot);
228
229extern int s3c2410_iotiming_get(struct s3c_cpufreq_config *cfg,
230 struct s3c_iotimings *timings);
231
232extern void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg,
233 struct s3c_iotimings *iot);
234
235extern void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg);
236
237/* S3C2412 compatible routines */
238
239extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
240 struct s3c_iotimings *timings);
241
242extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
243 struct s3c_iotimings *timings);
244
245extern int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg,
246 struct s3c_iotimings *iot);
247
248extern void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg,
249 struct s3c_iotimings *iot);
250
251#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG
252#define s3c_freq_dbg(x...) printk(KERN_INFO x)
253#else
254#define s3c_freq_dbg(x...) do { if (0) printk(x); } while (0)
255#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUG */
256
257#ifdef CONFIG_CPU_FREQ_S3C24XX_IODEBUG
258#define s3c_freq_iodbg(x...) printk(KERN_INFO x)
259#else
260#define s3c_freq_iodbg(x...) do { if (0) printk(x); } while (0)
261#endif /* CONFIG_CPU_FREQ_S3C24XX_IODEBUG */
262
263static inline int s3c_cpufreq_addfreq(struct cpufreq_frequency_table *table,
264 int index, size_t table_size,
265 unsigned int freq)
266{
267 if (index < 0)
268 return index;
269
270 if (table) {
271 if (index >= table_size)
272 return -ENOMEM;
273
274 s3c_freq_dbg("%s: { %d = %u kHz }\n",
275 __func__, index, freq);
276
277 table[index].index = index;
278 table[index].frequency = freq;
279 }
280
281 return index + 1;
282}
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
index a9ac9e29759e..b6deeef8f663 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
@@ -14,6 +14,7 @@
14#ifdef CONFIG_CPU_S3C2410 14#ifdef CONFIG_CPU_S3C2410
15 15
16extern int s3c2410_init(void); 16extern int s3c2410_init(void);
17extern int s3c2410a_init(void);
17 18
18extern void s3c2410_map_io(void); 19extern void s3c2410_map_io(void);
19 20
diff --git a/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c b/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
new file mode 100644
index 000000000000..43ea80190d87
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
@@ -0,0 +1,64 @@
1/* linux/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
2 *
3 * Copyright (c) 2009 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C24XX CPU Frequency scaling - utils for S3C2410/S3C2440/S3C2442
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/cpufreq.h>
17#include <linux/io.h>
18
19#include <mach/map.h>
20#include <mach/regs-mem.h>
21#include <mach/regs-clock.h>
22
23#include <plat/cpu-freq-core.h>
24
25/**
26 * s3c2410_cpufreq_setrefresh - set SDRAM refresh value
27 * @cfg: The frequency configuration
28 *
29 * Set the SDRAM refresh value appropriately for the configured
30 * frequency.
31 */
32void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
33{
34 struct s3c_cpufreq_board *board = cfg->board;
35 unsigned long refresh;
36 unsigned long refval;
37
38 /* Reduce both the refresh time (in ns) and the frequency (in MHz)
39 * down to ensure that we do not overflow 32 bit numbers.
40 *
41 * This should work for HCLK up to 133MHz and refresh period up
42 * to 30usec.
43 */
44
45 refresh = (cfg->freq.hclk / 100) * (board->refresh / 10);
46 refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale */
47 refresh = (1 << 11) + 1 - refresh;
48
49 s3c_freq_dbg("%s: refresh value %lu\n", __func__, refresh);
50
51 refval = __raw_readl(S3C2410_REFRESH);
52 refval &= ~((1 << 12) - 1);
53 refval |= refresh;
54 __raw_writel(refval, S3C2410_REFRESH);
55}
56
57/**
58 * s3c2410_set_fvco - set the PLL value
59 * @cfg: The frequency configuration
60 */
61void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg)
62{
63 __raw_writel(cfg->pll.index, S3C2410_MPLLCON);
64}
diff --git a/arch/arm/plat-s3c24xx/s3c2410-iotiming.c b/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
new file mode 100644
index 000000000000..d0a3a145cd4d
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
@@ -0,0 +1,477 @@
1/* linux/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
2 *
3 * Copyright (c) 2006,2008,2009 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C24XX CPU Frequency scaling - IO timing for S3C2410/S3C2440/S3C2442
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/cpufreq.h>
18#include <linux/seq_file.h>
19#include <linux/io.h>
20
21#include <mach/map.h>
22#include <mach/regs-mem.h>
23#include <mach/regs-clock.h>
24
25#include <plat/cpu-freq-core.h>
26
27#define print_ns(x) ((x) / 10), ((x) % 10)
28
29/**
30 * s3c2410_print_timing - print bank timing data for debug purposes
31 * @pfx: The prefix to put on the output
32 * @timings: The timing inforamtion to print.
33*/
34static void s3c2410_print_timing(const char *pfx,
35 struct s3c_iotimings *timings)
36{
37 struct s3c2410_iobank_timing *bt;
38 int bank;
39
40 for (bank = 0; bank < MAX_BANKS; bank++) {
41 bt = timings->bank[bank].io_2410;
42 if (!bt)
43 continue;
44
45 printk(KERN_DEBUG "%s %d: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, "
46 "Tcoh=%d.%d, Tcah=%d.%d\n", pfx, bank,
47 print_ns(bt->tacs),
48 print_ns(bt->tcos),
49 print_ns(bt->tacc),
50 print_ns(bt->tcoh),
51 print_ns(bt->tcah));
52 }
53}
54
55/**
56 * bank_reg - convert bank number to pointer to the control register.
57 * @bank: The IO bank number.
58 */
59static inline void __iomem *bank_reg(unsigned int bank)
60{
61 return S3C2410_BANKCON0 + (bank << 2);
62}
63
64/**
65 * bank_is_io - test whether bank is used for IO
66 * @bankcon: The bank control register.
67 *
68 * This is a simplistic test to see if any BANKCON[x] is not an IO
69 * bank. It currently does not take into account whether BWSCON has
70 * an illegal width-setting in it, or if the pin connected to nCS[x]
71 * is actually being handled as a chip-select.
72 */
73static inline int bank_is_io(unsigned long bankcon)
74{
75 return !(bankcon & S3C2410_BANKCON_SDRAM);
76}
77
78/**
79 * to_div - convert cycle time to divisor
80 * @cyc: The cycle time, in 10ths of nanoseconds.
81 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
82 *
83 * Convert the given cycle time into the divisor to use to obtain it from
84 * HCLK.
85*/
86static inline unsigned int to_div(unsigned int cyc, unsigned int hclk_tns)
87{
88 if (cyc == 0)
89 return 0;
90
91 return DIV_ROUND_UP(cyc, hclk_tns);
92}
93
94/**
95 * calc_0124 - calculate divisor control for divisors that do /0, /1. /2 and /4
96 * @cyc: The cycle time, in 10ths of nanoseconds.
97 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
98 * @v: Pointer to register to alter.
99 * @shift: The shift to get to the control bits.
100 *
101 * Calculate the divisor, and turn it into the correct control bits to
102 * set in the result, @v.
103 */
104static unsigned int calc_0124(unsigned int cyc, unsigned long hclk_tns,
105 unsigned long *v, int shift)
106{
107 unsigned int div = to_div(cyc, hclk_tns);
108 unsigned long val;
109
110 s3c_freq_iodbg("%s: cyc=%d, hclk=%lu, shift=%d => div %d\n",
111 __func__, cyc, hclk_tns, shift, div);
112
113 switch (div) {
114 case 0:
115 val = 0;
116 break;
117 case 1:
118 val = 1;
119 break;
120 case 2:
121 val = 2;
122 break;
123 case 3:
124 case 4:
125 val = 3;
126 break;
127 default:
128 return -1;
129 }
130
131 *v |= val << shift;
132 return 0;
133}
134
135int calc_tacp(unsigned int cyc, unsigned long hclk, unsigned long *v)
136{
137 /* Currently no support for Tacp calculations. */
138 return 0;
139}
140
141/**
142 * calc_tacc - calculate divisor control for tacc.
143 * @cyc: The cycle time, in 10ths of nanoseconds.
144 * @nwait_en: IS nWAIT enabled for this bank.
145 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
146 * @v: Pointer to register to alter.
147 *
148 * Calculate the divisor control for tACC, taking into account whether
149 * the bank has nWAIT enabled. The result is used to modify the value
150 * pointed to by @v.
151*/
152static int calc_tacc(unsigned int cyc, int nwait_en,
153 unsigned long hclk_tns, unsigned long *v)
154{
155 unsigned int div = to_div(cyc, hclk_tns);
156 unsigned long val;
157
158 s3c_freq_iodbg("%s: cyc=%u, nwait=%d, hclk=%lu => div=%u\n",
159 __func__, cyc, nwait_en, hclk_tns, div);
160
161 /* if nWait enabled on an bank, Tacc must be at-least 4 cycles. */
162 if (nwait_en && div < 4)
163 div = 4;
164
165 switch (div) {
166 case 0:
167 val = 0;
168 break;
169
170 case 1:
171 case 2:
172 case 3:
173 case 4:
174 val = div - 1;
175 break;
176
177 case 5:
178 case 6:
179 val = 4;
180 break;
181
182 case 7:
183 case 8:
184 val = 5;
185 break;
186
187 case 9:
188 case 10:
189 val = 6;
190 break;
191
192 case 11:
193 case 12:
194 case 13:
195 case 14:
196 val = 7;
197 break;
198
199 default:
200 return -1;
201 }
202
203 *v |= val << 8;
204 return 0;
205}
206
207/**
208 * s3c2410_calc_bank - calculate bank timing infromation
209 * @cfg: The configuration we need to calculate for.
210 * @bt: The bank timing information.
211 *
212 * Given the cycle timine for a bank @bt, calculate the new BANKCON
213 * setting for the @cfg timing. This updates the timing information
214 * ready for the cpu frequency change.
215 */
216static int s3c2410_calc_bank(struct s3c_cpufreq_config *cfg,
217 struct s3c2410_iobank_timing *bt)
218{
219 unsigned long hclk = cfg->freq.hclk_tns;
220 unsigned long res;
221 int ret;
222
223 res = bt->bankcon;
224 res &= (S3C2410_BANKCON_SDRAM | S3C2410_BANKCON_PMC16);
225
226 /* tacp: 2,3,4,5 */
227 /* tcah: 0,1,2,4 */
228 /* tcoh: 0,1,2,4 */
229 /* tacc: 1,2,3,4,6,7,10,14 (>4 for nwait) */
230 /* tcos: 0,1,2,4 */
231 /* tacs: 0,1,2,4 */
232
233 ret = calc_0124(bt->tacs, hclk, &res, S3C2410_BANKCON_Tacs_SHIFT);
234 ret |= calc_0124(bt->tcos, hclk, &res, S3C2410_BANKCON_Tcos_SHIFT);
235 ret |= calc_0124(bt->tcah, hclk, &res, S3C2410_BANKCON_Tcah_SHIFT);
236 ret |= calc_0124(bt->tcoh, hclk, &res, S3C2410_BANKCON_Tcoh_SHIFT);
237
238 if (ret)
239 return -EINVAL;
240
241 ret |= calc_tacp(bt->tacp, hclk, &res);
242 ret |= calc_tacc(bt->tacc, bt->nwait_en, hclk, &res);
243
244 if (ret)
245 return -EINVAL;
246
247 bt->bankcon = res;
248 return 0;
249}
250
251static unsigned int tacc_tab[] = {
252 [0] = 1,
253 [1] = 2,
254 [2] = 3,
255 [3] = 4,
256 [4] = 6,
257 [5] = 9,
258 [6] = 10,
259 [7] = 14,
260};
261
262/**
263 * get_tacc - turn tACC value into cycle time
264 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
265 * @val: The bank timing register value, shifed down.
266 */
267static unsigned int get_tacc(unsigned long hclk_tns,
268 unsigned long val)
269{
270 val &= 7;
271 return hclk_tns * tacc_tab[val];
272}
273
274/**
275 * get_0124 - turn 0/1/2/4 divider into cycle time
276 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
277 * @val: The bank timing register value, shifed down.
278 */
279static unsigned int get_0124(unsigned long hclk_tns,
280 unsigned long val)
281{
282 val &= 3;
283 return hclk_tns * ((val == 3) ? 4 : val);
284}
285
286/**
287 * s3c2410_iotiming_getbank - turn BANKCON into cycle time information
288 * @cfg: The frequency configuration
289 * @bt: The bank timing to fill in (uses cached BANKCON)
290 *
291 * Given the BANKCON setting in @bt and the current frequency settings
292 * in @cfg, update the cycle timing information.
293 */
294void s3c2410_iotiming_getbank(struct s3c_cpufreq_config *cfg,
295 struct s3c2410_iobank_timing *bt)
296{
297 unsigned long bankcon = bt->bankcon;
298 unsigned long hclk = cfg->freq.hclk_tns;
299
300 bt->tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
301 bt->tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
302 bt->tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
303 bt->tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
304 bt->tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
305}
306
307/**
308 * s3c2410_iotiming_debugfs - debugfs show io bank timing information
309 * @seq: The seq_file to write output to using seq_printf().
310 * @cfg: The current configuration.
311 * @iob: The IO bank information to decode.
312 */
313void s3c2410_iotiming_debugfs(struct seq_file *seq,
314 struct s3c_cpufreq_config *cfg,
315 union s3c_iobank *iob)
316{
317 struct s3c2410_iobank_timing *bt = iob->io_2410;
318 unsigned long bankcon = bt->bankcon;
319 unsigned long hclk = cfg->freq.hclk_tns;
320 unsigned int tacs;
321 unsigned int tcos;
322 unsigned int tacc;
323 unsigned int tcoh;
324 unsigned int tcah;
325
326 seq_printf(seq, "BANKCON=0x%08lx\n", bankcon);
327
328 tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
329 tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
330 tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
331 tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
332 tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
333
334 seq_printf(seq,
335 "\tRead: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
336 print_ns(bt->tacs),
337 print_ns(bt->tcos),
338 print_ns(bt->tacc),
339 print_ns(bt->tcoh),
340 print_ns(bt->tcah));
341
342 seq_printf(seq,
343 "\t Set: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
344 print_ns(tacs),
345 print_ns(tcos),
346 print_ns(tacc),
347 print_ns(tcoh),
348 print_ns(tcah));
349}
350
351/**
352 * s3c2410_iotiming_calc - Calculate bank timing for frequency change.
353 * @cfg: The frequency configuration
354 * @iot: The IO timing information to fill out.
355 *
356 * Calculate the new values for the banks in @iot based on the new
357 * frequency information in @cfg. This is then used by s3c2410_iotiming_set()
358 * to update the timing when necessary.
359 */
360int s3c2410_iotiming_calc(struct s3c_cpufreq_config *cfg,
361 struct s3c_iotimings *iot)
362{
363 struct s3c2410_iobank_timing *bt;
364 unsigned long bankcon;
365 int bank;
366 int ret;
367
368 for (bank = 0; bank < MAX_BANKS; bank++) {
369 bankcon = __raw_readl(bank_reg(bank));
370 bt = iot->bank[bank].io_2410;
371
372 if (!bt)
373 continue;
374
375 bt->bankcon = bankcon;
376
377 ret = s3c2410_calc_bank(cfg, bt);
378 if (ret) {
379 printk(KERN_ERR "%s: cannot calculate bank %d io\n",
380 __func__, bank);
381 goto err;
382 }
383
384 s3c_freq_iodbg("%s: bank %d: con=%08lx\n",
385 __func__, bank, bt->bankcon);
386 }
387
388 return 0;
389 err:
390 return ret;
391}
392
393/**
394 * s3c2410_iotiming_set - set the IO timings from the given setup.
395 * @cfg: The frequency configuration
396 * @iot: The IO timing information to use.
397 *
398 * Set all the currently used IO bank timing information generated
399 * by s3c2410_iotiming_calc() once the core has validated that all
400 * the new values are within permitted bounds.
401 */
402void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg,
403 struct s3c_iotimings *iot)
404{
405 struct s3c2410_iobank_timing *bt;
406 int bank;
407
408 /* set the io timings from the specifier */
409
410 for (bank = 0; bank < MAX_BANKS; bank++) {
411 bt = iot->bank[bank].io_2410;
412 if (!bt)
413 continue;
414
415 __raw_writel(bt->bankcon, bank_reg(bank));
416 }
417}
418
419/**
420 * s3c2410_iotiming_get - Get the timing information from current registers.
421 * @cfg: The frequency configuration
422 * @timings: The IO timing information to fill out.
423 *
424 * Calculate the @timings timing information from the current frequency
425 * information in @cfg, and the new frequency configur
426 * through all the IO banks, reading the state and then updating @iot
427 * as necessary.
428 *
429 * This is used at the moment on initialisation to get the current
430 * configuration so that boards do not have to carry their own setup
431 * if the timings are correct on initialisation.
432 */
433
434int s3c2410_iotiming_get(struct s3c_cpufreq_config *cfg,
435 struct s3c_iotimings *timings)
436{
437 struct s3c2410_iobank_timing *bt;
438 unsigned long bankcon;
439 unsigned long bwscon;
440 int bank;
441
442 bwscon = __raw_readl(S3C2410_BWSCON);
443
444 /* look through all banks to see what is currently set. */
445
446 for (bank = 0; bank < MAX_BANKS; bank++) {
447 bankcon = __raw_readl(bank_reg(bank));
448
449 if (!bank_is_io(bankcon))
450 continue;
451
452 s3c_freq_iodbg("%s: bank %d: con %08lx\n",
453 __func__, bank, bankcon);
454
455 bt = kzalloc(sizeof(struct s3c2410_iobank_timing), GFP_KERNEL);
456 if (!bt) {
457 printk(KERN_ERR "%s: no memory for bank\n", __func__);
458 return -ENOMEM;
459 }
460
461 /* find out in nWait is enabled for bank. */
462
463 if (bank != 0) {
464 unsigned long tmp = S3C2410_BWSCON_GET(bwscon, bank);
465 if (tmp & S3C2410_BWSCON_WS)
466 bt->nwait_en = 1;
467 }
468
469 timings->bank[bank].io_2410 = bt;
470 bt->bankcon = bankcon;
471
472 s3c2410_iotiming_getbank(cfg, bt);
473 }
474
475 s3c2410_print_timing("get", timings);
476 return 0;
477}
diff --git a/arch/arm/plat-s3c24xx/s3c2412-iotiming.c b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
new file mode 100644
index 000000000000..fd45e47facbc
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
@@ -0,0 +1,285 @@
1/* linux/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
2 *
3 * Copyright (c) 2006,2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * S3C2412/S3C2443 (PL093 based) IO timing support
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/ioport.h>
18#include <linux/cpufreq.h>
19#include <linux/seq_file.h>
20#include <linux/sysdev.h>
21#include <linux/delay.h>
22#include <linux/clk.h>
23#include <linux/err.h>
24
25#include <linux/amba/pl093.h>
26
27#include <asm/mach/arch.h>
28#include <asm/mach/map.h>
29
30#include <mach/regs-s3c2412-mem.h>
31
32#include <plat/cpu.h>
33#include <plat/cpu-freq-core.h>
34#include <plat/clock.h>
35
36#define print_ns(x) ((x) / 10), ((x) % 10)
37
38/**
39 * s3c2412_print_timing - print timing infromation via printk.
40 * @pfx: The prefix to print each line with.
41 * @iot: The IO timing information
42 */
43static void s3c2412_print_timing(const char *pfx, struct s3c_iotimings *iot)
44{
45 struct s3c2412_iobank_timing *bt;
46 unsigned int bank;
47
48 for (bank = 0; bank < MAX_BANKS; bank++) {
49 bt = iot->bank[bank].io_2412;
50 if (!bt)
51 continue;
52
53 printk(KERN_DEBUG "%s: %d: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
54 "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n", pfx, bank,
55 print_ns(bt->idcy),
56 print_ns(bt->wstrd),
57 print_ns(bt->wstwr),
58 print_ns(bt->wstoen),
59 print_ns(bt->wstwen),
60 print_ns(bt->wstbrd));
61 }
62}
63
64/**
65 * to_div - turn a cycle length into a divisor setting.
66 * @cyc_tns: The cycle time in 10ths of nanoseconds.
67 * @clk_tns: The clock period in 10ths of nanoseconds.
68 */
69static inline unsigned int to_div(unsigned int cyc_tns, unsigned int clk_tns)
70{
71 return cyc_tns ? DIV_ROUND_UP(cyc_tns, clk_tns) : 0;
72}
73
74/**
75 * calc_timing - calculate timing divisor value and check in range.
76 * @hwtm: The hardware timing in 10ths of nanoseconds.
77 * @clk_tns: The clock period in 10ths of nanoseconds.
78 * @err: Pointer to err variable to update in event of failure.
79 */
80static unsigned int calc_timing(unsigned int hwtm, unsigned int clk_tns,
81 unsigned int *err)
82{
83 unsigned int ret = to_div(hwtm, clk_tns);
84
85 if (ret > 0xf)
86 *err = -EINVAL;
87
88 return ret;
89}
90
91/**
92 * s3c2412_calc_bank - calculate the bank divisor settings.
93 * @cfg: The current frequency configuration.
94 * @bt: The bank timing.
95 */
96static int s3c2412_calc_bank(struct s3c_cpufreq_config *cfg,
97 struct s3c2412_iobank_timing *bt)
98{
99 unsigned int hclk = cfg->freq.hclk_tns;
100 int err = 0;
101
102 bt->smbidcyr = calc_timing(bt->idcy, hclk, &err);
103 bt->smbwstrd = calc_timing(bt->wstrd, hclk, &err);
104 bt->smbwstwr = calc_timing(bt->wstwr, hclk, &err);
105 bt->smbwstoen = calc_timing(bt->wstoen, hclk, &err);
106 bt->smbwstwen = calc_timing(bt->wstwen, hclk, &err);
107 bt->smbwstbrd = calc_timing(bt->wstbrd, hclk, &err);
108
109 return err;
110}
111
112/**
113 * s3c2412_iotiming_debugfs - debugfs show io bank timing information
114 * @seq: The seq_file to write output to using seq_printf().
115 * @cfg: The current configuration.
116 * @iob: The IO bank information to decode.
117*/
118void s3c2412_iotiming_debugfs(struct seq_file *seq,
119 struct s3c_cpufreq_config *cfg,
120 union s3c_iobank *iob)
121{
122 struct s3c2412_iobank_timing *bt = iob->io_2412;
123
124 seq_printf(seq,
125 "\tRead: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
126 "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n",
127 print_ns(bt->idcy),
128 print_ns(bt->wstrd),
129 print_ns(bt->wstwr),
130 print_ns(bt->wstoen),
131 print_ns(bt->wstwen),
132 print_ns(bt->wstbrd));
133}
134
135/**
136 * s3c2412_iotiming_calc - calculate all the bank divisor settings.
137 * @cfg: The current frequency configuration.
138 * @iot: The bank timing information.
139 *
140 * Calculate the timing information for all the banks that are
141 * configured as IO, using s3c2412_calc_bank().
142 */
143int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg,
144 struct s3c_iotimings *iot)
145{
146 struct s3c2412_iobank_timing *bt;
147 int bank;
148 int ret;
149
150 for (bank = 0; bank < MAX_BANKS; bank++) {
151 bt = iot->bank[bank].io_2412;
152 if (!bt)
153 continue;
154
155 ret = s3c2412_calc_bank(cfg, bt);
156 if (ret) {
157 printk(KERN_ERR "%s: cannot calculate bank %d io\n",
158 __func__, bank);
159 goto err;
160 }
161 }
162
163 return 0;
164 err:
165 return ret;
166}
167
168/**
169 * s3c2412_iotiming_set - set the timing information
170 * @cfg: The current frequency configuration.
171 * @iot: The bank timing information.
172 *
173 * Set the IO bank information from the details calculated earlier from
174 * calling s3c2412_iotiming_calc().
175 */
176void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg,
177 struct s3c_iotimings *iot)
178{
179 struct s3c2412_iobank_timing *bt;
180 void __iomem *regs;
181 int bank;
182
183 /* set the io timings from the specifier */
184
185 for (bank = 0; bank < MAX_BANKS; bank++) {
186 bt = iot->bank[bank].io_2412;
187 if (!bt)
188 continue;
189
190 regs = S3C2412_SSMC_BANK(bank);
191
192 __raw_writel(bt->smbidcyr, regs + SMBIDCYR);
193 __raw_writel(bt->smbwstrd, regs + SMBWSTRDR);
194 __raw_writel(bt->smbwstwr, regs + SMBWSTWRR);
195 __raw_writel(bt->smbwstoen, regs + SMBWSTOENR);
196 __raw_writel(bt->smbwstwen, regs + SMBWSTWENR);
197 __raw_writel(bt->smbwstbrd, regs + SMBWSTBRDR);
198 }
199}
200
201static inline unsigned int s3c2412_decode_timing(unsigned int clock, u32 reg)
202{
203 return (reg & 0xf) * clock;
204}
205
206static void s3c2412_iotiming_getbank(struct s3c_cpufreq_config *cfg,
207 struct s3c2412_iobank_timing *bt,
208 unsigned int bank)
209{
210 unsigned long clk = cfg->freq.hclk_tns; /* ssmc clock??? */
211 void __iomem *regs = S3C2412_SSMC_BANK(bank);
212
213 bt->idcy = s3c2412_decode_timing(clk, __raw_readl(regs + SMBIDCYR));
214 bt->wstrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTRDR));
215 bt->wstoen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTOENR));
216 bt->wstwen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTWENR));
217 bt->wstbrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTBRDR));
218}
219
220/**
221 * bank_is_io - return true if bank is (possibly) IO.
222 * @bank: The bank number.
223 * @bankcfg: The value of S3C2412_EBI_BANKCFG.
224 */
225static inline bool bank_is_io(unsigned int bank, u32 bankcfg)
226{
227 if (bank < 2)
228 return true;
229
230 return !(bankcfg & (1 << bank));
231}
232
233int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
234 struct s3c_iotimings *timings)
235{
236 struct s3c2412_iobank_timing *bt;
237 u32 bankcfg = __raw_readl(S3C2412_EBI_BANKCFG);
238 unsigned int bank;
239
240 /* look through all banks to see what is currently set. */
241
242 for (bank = 0; bank < MAX_BANKS; bank++) {
243 if (!bank_is_io(bank, bankcfg))
244 continue;
245
246 bt = kzalloc(sizeof(struct s3c2412_iobank_timing), GFP_KERNEL);
247 if (!bt) {
248 printk(KERN_ERR "%s: no memory for bank\n", __func__);
249 return -ENOMEM;
250 }
251
252 timings->bank[bank].io_2412 = bt;
253 s3c2412_iotiming_getbank(cfg, bt, bank);
254 }
255
256 s3c2412_print_timing("get", timings);
257 return 0;
258}
259
260/* this is in here as it is so small, it doesn't currently warrant a file
261 * to itself. We expect that any s3c24xx needing this is going to also
262 * need the iotiming support.
263 */
264void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
265{
266 struct s3c_cpufreq_board *board = cfg->board;
267 u32 refresh;
268
269 WARN_ON(board == NULL);
270
271 /* Reduce both the refresh time (in ns) and the frequency (in MHz)
272 * down to ensure that we do not overflow 32 bit numbers.
273 *
274 * This should work for HCLK up to 133MHz and refresh period up
275 * to 30usec.
276 */
277
278 refresh = (cfg->freq.hclk / 100) * (board->refresh / 10);
279 refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale */
280 refresh &= ((1 << 16) - 1);
281
282 s3c_freq_dbg("%s: refresh value %u\n", __func__, (unsigned int)refresh);
283
284 __raw_writel(refresh, S3C2412_REFRESH);
285}
diff --git a/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c b/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
new file mode 100644
index 000000000000..ae2e6c604f27
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
@@ -0,0 +1,311 @@
1/* linux/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
2 *
3 * Copyright (c) 2006,2008,2009 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 * Vincent Sanders <vince@simtec.co.uk>
7 *
8 * S3C2440/S3C2442 CPU Frequency scaling
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/interrupt.h>
18#include <linux/ioport.h>
19#include <linux/cpufreq.h>
20#include <linux/sysdev.h>
21#include <linux/delay.h>
22#include <linux/clk.h>
23#include <linux/err.h>
24#include <linux/io.h>
25
26#include <mach/hardware.h>
27
28#include <asm/mach/arch.h>
29#include <asm/mach/map.h>
30
31#include <mach/regs-clock.h>
32
33#include <plat/cpu.h>
34#include <plat/cpu-freq-core.h>
35#include <plat/clock.h>
36
37static struct clk *xtal;
38static struct clk *fclk;
39static struct clk *hclk;
40static struct clk *armclk;
41
42/* HDIV: 1, 2, 3, 4, 6, 8 */
43
44static inline int within_khz(unsigned long a, unsigned long b)
45{
46 long diff = a - b;
47
48 return (diff >= -1000 && diff <= 1000);
49}
50
51/**
52 * s3c2440_cpufreq_calcdivs - calculate divider settings
53 * @cfg: The cpu frequency settings.
54 *
55 * Calcualte the divider values for the given frequency settings
56 * specified in @cfg. The values are stored in @cfg for later use
57 * by the relevant set routine if the request settings can be reached.
58 */
59int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
60{
61 unsigned int hdiv, pdiv;
62 unsigned long hclk, fclk, armclk;
63 unsigned long hclk_max;
64
65 fclk = cfg->freq.fclk;
66 armclk = cfg->freq.armclk;
67 hclk_max = cfg->max.hclk;
68
69 s3c_freq_dbg("%s: fclk is %lu, armclk %lu, max hclk %lu\n",
70 __func__, fclk, armclk, hclk_max);
71
72 if (armclk > fclk) {
73 printk(KERN_WARNING "%s: armclk > fclk\n", __func__);
74 armclk = fclk;
75 }
76
77 /* if we are in DVS, we need HCLK to be <= ARMCLK */
78 if (armclk < fclk && armclk < hclk_max)
79 hclk_max = armclk;
80
81 for (hdiv = 1; hdiv < 9; hdiv++) {
82 if (hdiv == 5 || hdiv == 7)
83 hdiv++;
84
85 hclk = (fclk / hdiv);
86 if (hclk <= hclk_max || within_khz(hclk, hclk_max))
87 break;
88 }
89
90 s3c_freq_dbg("%s: hclk %lu, div %d\n", __func__, hclk, hdiv);
91
92 if (hdiv > 8)
93 goto invalid;
94
95 pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
96
97 if ((hclk / pdiv) > cfg->max.pclk)
98 pdiv++;
99
100 s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
101
102 if (pdiv > 2)
103 goto invalid;
104
105 pdiv *= hdiv;
106
107 /* calculate a valid armclk */
108
109 if (armclk < hclk)
110 armclk = hclk;
111
112 /* if we're running armclk lower than fclk, this really means
113 * that the system should go into dvs mode, which means that
114 * armclk is connected to hclk. */
115 if (armclk < fclk) {
116 cfg->divs.dvs = 1;
117 armclk = hclk;
118 } else
119 cfg->divs.dvs = 0;
120
121 cfg->freq.armclk = armclk;
122
123 /* store the result, and then return */
124
125 cfg->divs.h_divisor = hdiv;
126 cfg->divs.p_divisor = pdiv;
127
128 return 0;
129
130 invalid:
131 return -EINVAL;
132}
133
134#define CAMDIVN_HCLK_HALF (S3C2440_CAMDIVN_HCLK3_HALF | \
135 S3C2440_CAMDIVN_HCLK4_HALF)
136
137/**
138 * s3c2440_cpufreq_setdivs - set the cpu frequency divider settings
139 * @cfg: The cpu frequency settings.
140 *
141 * Set the divisors from the settings in @cfg, which where generated
142 * during the calculation phase by s3c2440_cpufreq_calcdivs().
143 */
144static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
145{
146 unsigned long clkdiv, camdiv;
147
148 s3c_freq_dbg("%s: divsiors: h=%d, p=%d\n", __func__,
149 cfg->divs.h_divisor, cfg->divs.p_divisor);
150
151 clkdiv = __raw_readl(S3C2410_CLKDIVN);
152 camdiv = __raw_readl(S3C2440_CAMDIVN);
153
154 clkdiv &= ~(S3C2440_CLKDIVN_HDIVN_MASK | S3C2440_CLKDIVN_PDIVN);
155 camdiv &= ~CAMDIVN_HCLK_HALF;
156
157 switch (cfg->divs.h_divisor) {
158 case 1:
159 clkdiv |= S3C2440_CLKDIVN_HDIVN_1;
160 break;
161
162 case 2:
163 clkdiv |= S3C2440_CLKDIVN_HDIVN_2;
164 break;
165
166 case 6:
167 camdiv |= S3C2440_CAMDIVN_HCLK3_HALF;
168 case 3:
169 clkdiv |= S3C2440_CLKDIVN_HDIVN_3_6;
170 break;
171
172 case 8:
173 camdiv |= S3C2440_CAMDIVN_HCLK4_HALF;
174 case 4:
175 clkdiv |= S3C2440_CLKDIVN_HDIVN_4_8;
176 break;
177
178 default:
179 BUG(); /* we don't expect to get here. */
180 }
181
182 if (cfg->divs.p_divisor != cfg->divs.h_divisor)
183 clkdiv |= S3C2440_CLKDIVN_PDIVN;
184
185 /* todo - set pclk. */
186
187 /* Write the divisors first with hclk intentionally halved so that
188 * when we write clkdiv we will under-frequency instead of over. We
189 * then make a short delay and remove the hclk halving if necessary.
190 */
191
192 __raw_writel(camdiv | CAMDIVN_HCLK_HALF, S3C2440_CAMDIVN);
193 __raw_writel(clkdiv, S3C2410_CLKDIVN);
194
195 ndelay(20);
196 __raw_writel(camdiv, S3C2440_CAMDIVN);
197
198 clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
199}
200
201static int run_freq_for(unsigned long max_hclk, unsigned long fclk,
202 int *divs,
203 struct cpufreq_frequency_table *table,
204 size_t table_size)
205{
206 unsigned long freq;
207 int index = 0;
208 int div;
209
210 for (div = *divs; div > 0; div = *divs++) {
211 freq = fclk / div;
212
213 if (freq > max_hclk && div != 1)
214 continue;
215
216 freq /= 1000; /* table is in kHz */
217 index = s3c_cpufreq_addfreq(table, index, table_size, freq);
218 if (index < 0)
219 break;
220 }
221
222 return index;
223}
224
225static int hclk_divs[] = { 1, 2, 3, 4, 6, 8, -1 };
226
227static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg,
228 struct cpufreq_frequency_table *table,
229 size_t table_size)
230{
231 int ret;
232
233 WARN_ON(cfg->info == NULL);
234 WARN_ON(cfg->board == NULL);
235
236 ret = run_freq_for(cfg->info->max.hclk,
237 cfg->info->max.fclk,
238 hclk_divs,
239 table, table_size);
240
241 s3c_freq_dbg("%s: returning %d\n", __func__, ret);
242
243 return ret;
244}
245
246struct s3c_cpufreq_info s3c2440_cpufreq_info = {
247 .max = {
248 .fclk = 400000000,
249 .hclk = 133333333,
250 .pclk = 66666666,
251 },
252
253 .locktime_m = 300,
254 .locktime_u = 300,
255 .locktime_bits = 16,
256
257 .name = "s3c244x",
258 .calc_iotiming = s3c2410_iotiming_calc,
259 .set_iotiming = s3c2410_iotiming_set,
260 .get_iotiming = s3c2410_iotiming_get,
261 .set_fvco = s3c2410_set_fvco,
262
263 .set_refresh = s3c2410_cpufreq_setrefresh,
264 .set_divs = s3c2440_cpufreq_setdivs,
265 .calc_divs = s3c2440_cpufreq_calcdivs,
266 .calc_freqtable = s3c2440_cpufreq_calctable,
267
268 .resume_clocks = s3c244x_setup_clocks,
269
270 .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
271};
272
273static int s3c2440_cpufreq_add(struct sys_device *sysdev)
274{
275 xtal = s3c_cpufreq_clk_get(NULL, "xtal");
276 hclk = s3c_cpufreq_clk_get(NULL, "hclk");
277 fclk = s3c_cpufreq_clk_get(NULL, "fclk");
278 armclk = s3c_cpufreq_clk_get(NULL, "armclk");
279
280 if (IS_ERR(xtal) || IS_ERR(hclk) || IS_ERR(fclk) || IS_ERR(armclk)) {
281 printk(KERN_ERR "%s: failed to get clocks\n", __func__);
282 return -ENOENT;
283 }
284
285 return s3c_cpufreq_register(&s3c2440_cpufreq_info);
286}
287
288static struct sysdev_driver s3c2440_cpufreq_driver = {
289 .add = s3c2440_cpufreq_add,
290};
291
292static int s3c2440_cpufreq_init(void)
293{
294 return sysdev_driver_register(&s3c2440_sysclass,
295 &s3c2440_cpufreq_driver);
296}
297
298/* arch_initcall adds the clocks we need, so use subsys_initcall. */
299subsys_initcall(s3c2440_cpufreq_init);
300
301static struct sysdev_driver s3c2442_cpufreq_driver = {
302 .add = s3c2440_cpufreq_add,
303};
304
305static int s3c2442_cpufreq_init(void)
306{
307 return sysdev_driver_register(&s3c2442_sysclass,
308 &s3c2442_cpufreq_driver);
309}
310
311subsys_initcall(s3c2442_cpufreq_init);
diff --git a/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c b/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
new file mode 100644
index 000000000000..ff9443b233aa
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
@@ -0,0 +1,97 @@
1/* arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
2 *
3 * Copyright (c) 2006,2007 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 * Vincent Sanders <vince@arm.linux.org.uk>
7 *
8 * S3C2440/S3C2442 CPU PLL tables (12MHz Crystal)
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/sysdev.h>
18#include <linux/clk.h>
19#include <linux/err.h>
20
21#include <plat/cpu.h>
22#include <plat/cpu-freq-core.h>
23
24static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = {
25 { .frequency = 75000000, .index = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */
26 { .frequency = 80000000, .index = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */
27 { .frequency = 90000000, .index = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */
28 { .frequency = 100000000, .index = PLLVAL(0x5c, 1, 3), }, /* FVco 800.000000 */
29 { .frequency = 110000000, .index = PLLVAL(0x66, 1, 3), }, /* FVco 880.000000 */
30 { .frequency = 120000000, .index = PLLVAL(0x70, 1, 3), }, /* FVco 960.000000 */
31 { .frequency = 150000000, .index = PLLVAL(0x75, 3, 2), }, /* FVco 600.000000 */
32 { .frequency = 160000000, .index = PLLVAL(0x98, 4, 2), }, /* FVco 640.000000 */
33 { .frequency = 170000000, .index = PLLVAL(0x4d, 1, 2), }, /* FVco 680.000000 */
34 { .frequency = 180000000, .index = PLLVAL(0x70, 2, 2), }, /* FVco 720.000000 */
35 { .frequency = 190000000, .index = PLLVAL(0x57, 1, 2), }, /* FVco 760.000000 */
36 { .frequency = 200000000, .index = PLLVAL(0x5c, 1, 2), }, /* FVco 800.000000 */
37 { .frequency = 210000000, .index = PLLVAL(0x84, 2, 2), }, /* FVco 840.000000 */
38 { .frequency = 220000000, .index = PLLVAL(0x66, 1, 2), }, /* FVco 880.000000 */
39 { .frequency = 230000000, .index = PLLVAL(0x6b, 1, 2), }, /* FVco 920.000000 */
40 { .frequency = 240000000, .index = PLLVAL(0x70, 1, 2), }, /* FVco 960.000000 */
41 { .frequency = 300000000, .index = PLLVAL(0x75, 3, 1), }, /* FVco 600.000000 */
42 { .frequency = 310000000, .index = PLLVAL(0x93, 4, 1), }, /* FVco 620.000000 */
43 { .frequency = 320000000, .index = PLLVAL(0x98, 4, 1), }, /* FVco 640.000000 */
44 { .frequency = 330000000, .index = PLLVAL(0x66, 2, 1), }, /* FVco 660.000000 */
45 { .frequency = 340000000, .index = PLLVAL(0x4d, 1, 1), }, /* FVco 680.000000 */
46 { .frequency = 350000000, .index = PLLVAL(0xa7, 4, 1), }, /* FVco 700.000000 */
47 { .frequency = 360000000, .index = PLLVAL(0x70, 2, 1), }, /* FVco 720.000000 */
48 { .frequency = 370000000, .index = PLLVAL(0xb1, 4, 1), }, /* FVco 740.000000 */
49 { .frequency = 380000000, .index = PLLVAL(0x57, 1, 1), }, /* FVco 760.000000 */
50 { .frequency = 390000000, .index = PLLVAL(0x7a, 2, 1), }, /* FVco 780.000000 */
51 { .frequency = 400000000, .index = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */
52};
53
54static int s3c2440_plls12_add(struct sys_device *dev)
55{
56 struct clk *xtal_clk;
57 unsigned long xtal;
58
59 xtal_clk = clk_get(NULL, "xtal");
60 if (IS_ERR(xtal_clk))
61 return PTR_ERR(xtal_clk);
62
63 xtal = clk_get_rate(xtal_clk);
64 clk_put(xtal_clk);
65
66 if (xtal == 12000000) {
67 printk(KERN_INFO "Using PLL table for 12MHz crystal\n");
68 return s3c_plltab_register(s3c2440_plls_12,
69 ARRAY_SIZE(s3c2440_plls_12));
70 }
71
72 return 0;
73}
74
75static struct sysdev_driver s3c2440_plls12_drv = {
76 .add = s3c2440_plls12_add,
77};
78
79static int __init s3c2440_pll_12mhz(void)
80{
81 return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_plls12_drv);
82
83}
84
85arch_initcall(s3c2440_pll_12mhz);
86
87static struct sysdev_driver s3c2442_plls12_drv = {
88 .add = s3c2440_plls12_add,
89};
90
91static int __init s3c2442_pll_12mhz(void)
92{
93 return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_plls12_drv);
94
95}
96
97arch_initcall(s3c2442_pll_12mhz);
diff --git a/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c b/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
new file mode 100644
index 000000000000..7679af13a94d
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
@@ -0,0 +1,127 @@
1/* arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
2 *
3 * Copyright (c) 2006-2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 * Vincent Sanders <vince@arm.linux.org.uk>
7 *
8 * S3C2440/S3C2442 CPU PLL tables (16.93444MHz Crystal)
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/sysdev.h>
18#include <linux/clk.h>
19#include <linux/err.h>
20
21#include <plat/cpu.h>
22#include <plat/cpu-freq-core.h>
23
24static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = {
25 { .frequency = 78019200, .index = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */
26 { .frequency = 84067200, .index = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */
27 { .frequency = 90115200, .index = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */
28 { .frequency = 96163200, .index = PLLVAL(151, 5, 3), }, /* FVco 769.305600 */
29 { .frequency = 102135600, .index = PLLVAL(185, 6, 3), }, /* FVco 817.084800 */
30 { .frequency = 108259200, .index = PLLVAL(171, 5, 3), }, /* FVco 866.073600 */
31 { .frequency = 114307200, .index = PLLVAL(127, 3, 3), }, /* FVco 914.457600 */
32 { .frequency = 120234240, .index = PLLVAL(134, 3, 3), }, /* FVco 961.873920 */
33 { .frequency = 126161280, .index = PLLVAL(141, 3, 3), }, /* FVco 1009.290240 */
34 { .frequency = 132088320, .index = PLLVAL(148, 3, 3), }, /* FVco 1056.706560 */
35 { .frequency = 138015360, .index = PLLVAL(155, 3, 3), }, /* FVco 1104.122880 */
36 { .frequency = 144789120, .index = PLLVAL(163, 3, 3), }, /* FVco 1158.312960 */
37 { .frequency = 150100363, .index = PLLVAL(187, 9, 2), }, /* FVco 600.401454 */
38 { .frequency = 156038400, .index = PLLVAL(121, 5, 2), }, /* FVco 624.153600 */
39 { .frequency = 162086400, .index = PLLVAL(126, 5, 2), }, /* FVco 648.345600 */
40 { .frequency = 168134400, .index = PLLVAL(131, 5, 2), }, /* FVco 672.537600 */
41 { .frequency = 174048000, .index = PLLVAL(177, 7, 2), }, /* FVco 696.192000 */
42 { .frequency = 180230400, .index = PLLVAL(141, 5, 2), }, /* FVco 720.921600 */
43 { .frequency = 186278400, .index = PLLVAL(124, 4, 2), }, /* FVco 745.113600 */
44 { .frequency = 192326400, .index = PLLVAL(151, 5, 2), }, /* FVco 769.305600 */
45 { .frequency = 198132480, .index = PLLVAL(109, 3, 2), }, /* FVco 792.529920 */
46 { .frequency = 204271200, .index = PLLVAL(185, 6, 2), }, /* FVco 817.084800 */
47 { .frequency = 210268800, .index = PLLVAL(141, 4, 2), }, /* FVco 841.075200 */
48 { .frequency = 216518400, .index = PLLVAL(171, 5, 2), }, /* FVco 866.073600 */
49 { .frequency = 222264000, .index = PLLVAL(97, 2, 2), }, /* FVco 889.056000 */
50 { .frequency = 228614400, .index = PLLVAL(127, 3, 2), }, /* FVco 914.457600 */
51 { .frequency = 234259200, .index = PLLVAL(158, 4, 2), }, /* FVco 937.036800 */
52 { .frequency = 240468480, .index = PLLVAL(134, 3, 2), }, /* FVco 961.873920 */
53 { .frequency = 246960000, .index = PLLVAL(167, 4, 2), }, /* FVco 987.840000 */
54 { .frequency = 252322560, .index = PLLVAL(141, 3, 2), }, /* FVco 1009.290240 */
55 { .frequency = 258249600, .index = PLLVAL(114, 2, 2), }, /* FVco 1032.998400 */
56 { .frequency = 264176640, .index = PLLVAL(148, 3, 2), }, /* FVco 1056.706560 */
57 { .frequency = 270950400, .index = PLLVAL(120, 2, 2), }, /* FVco 1083.801600 */
58 { .frequency = 276030720, .index = PLLVAL(155, 3, 2), }, /* FVco 1104.122880 */
59 { .frequency = 282240000, .index = PLLVAL(92, 1, 2), }, /* FVco 1128.960000 */
60 { .frequency = 289578240, .index = PLLVAL(163, 3, 2), }, /* FVco 1158.312960 */
61 { .frequency = 294235200, .index = PLLVAL(131, 2, 2), }, /* FVco 1176.940800 */
62 { .frequency = 300200727, .index = PLLVAL(187, 9, 1), }, /* FVco 600.401454 */
63 { .frequency = 306358690, .index = PLLVAL(191, 9, 1), }, /* FVco 612.717380 */
64 { .frequency = 312076800, .index = PLLVAL(121, 5, 1), }, /* FVco 624.153600 */
65 { .frequency = 318366720, .index = PLLVAL(86, 3, 1), }, /* FVco 636.733440 */
66 { .frequency = 324172800, .index = PLLVAL(126, 5, 1), }, /* FVco 648.345600 */
67 { .frequency = 330220800, .index = PLLVAL(109, 4, 1), }, /* FVco 660.441600 */
68 { .frequency = 336268800, .index = PLLVAL(131, 5, 1), }, /* FVco 672.537600 */
69 { .frequency = 342074880, .index = PLLVAL(93, 3, 1), }, /* FVco 684.149760 */
70 { .frequency = 348096000, .index = PLLVAL(177, 7, 1), }, /* FVco 696.192000 */
71 { .frequency = 355622400, .index = PLLVAL(118, 4, 1), }, /* FVco 711.244800 */
72 { .frequency = 360460800, .index = PLLVAL(141, 5, 1), }, /* FVco 720.921600 */
73 { .frequency = 366206400, .index = PLLVAL(165, 6, 1), }, /* FVco 732.412800 */
74 { .frequency = 372556800, .index = PLLVAL(124, 4, 1), }, /* FVco 745.113600 */
75 { .frequency = 378201600, .index = PLLVAL(126, 4, 1), }, /* FVco 756.403200 */
76 { .frequency = 384652800, .index = PLLVAL(151, 5, 1), }, /* FVco 769.305600 */
77 { .frequency = 391608000, .index = PLLVAL(177, 6, 1), }, /* FVco 783.216000 */
78 { .frequency = 396264960, .index = PLLVAL(109, 3, 1), }, /* FVco 792.529920 */
79 { .frequency = 402192000, .index = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */
80};
81
82static int s3c2440_plls169344_add(struct sys_device *dev)
83{
84 struct clk *xtal_clk;
85 unsigned long xtal;
86
87 xtal_clk = clk_get(NULL, "xtal");
88 if (IS_ERR(xtal_clk))
89 return PTR_ERR(xtal_clk);
90
91 xtal = clk_get_rate(xtal_clk);
92 clk_put(xtal_clk);
93
94 if (xtal == 169344000) {
95 printk(KERN_INFO "Using PLL table for 16.9344MHz crystal\n");
96 return s3c_plltab_register(s3c2440_plls_169344,
97 ARRAY_SIZE(s3c2440_plls_169344));
98 }
99
100 return 0;
101}
102
103static struct sysdev_driver s3c2440_plls169344_drv = {
104 .add = s3c2440_plls169344_add,
105};
106
107static int __init s3c2440_pll_16934400(void)
108{
109 return sysdev_driver_register(&s3c2440_sysclass,
110 &s3c2440_plls169344_drv);
111
112}
113
114arch_initcall(s3c2440_pll_16934400);
115
116static struct sysdev_driver s3c2442_plls169344_drv = {
117 .add = s3c2440_plls169344_add,
118};
119
120static int __init s3c2442_pll_16934400(void)
121{
122 return sysdev_driver_register(&s3c2442_sysclass,
123 &s3c2442_plls169344_drv);
124
125}
126
127arch_initcall(s3c2442_pll_16934400);
diff --git a/include/linux/amba/pl093.h b/include/linux/amba/pl093.h
new file mode 100644
index 000000000000..2983e3671adb
--- /dev/null
+++ b/include/linux/amba/pl093.h
@@ -0,0 +1,80 @@
1/* linux/amba/pl093.h
2 *
3 * Copyright (c) 2008 Simtec Electronics
4 * http://armlinux.simtec.co.uk/
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * AMBA PL093 SSMC (synchronous static memory controller)
8 * See DDI0236.pdf (r0p4) for more details
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#define SMB_BANK(x) ((x) * 0x20) /* each bank control set is 0x20 apart */
16
17/* Offsets for SMBxxxxRy registers */
18
19#define SMBIDCYR (0x00)
20#define SMBWSTRDR (0x04)
21#define SMBWSTWRR (0x08)
22#define SMBWSTOENR (0x0C)
23#define SMBWSTWENR (0x10)
24#define SMBCR (0x14)
25#define SMBSR (0x18)
26#define SMBWSTBRDR (0x1C)
27
28/* Masks for SMB registers */
29#define IDCY_MASK (0xf)
30#define WSTRD_MASK (0xf)
31#define WSTWR_MASK (0xf)
32#define WSTOEN_MASK (0xf)
33#define WSTWEN_MASK (0xf)
34
35/* Notes from datasheet:
36 * WSTOEN <= WSTRD
37 * WSTWEN <= WSTWR
38 *
39 * WSTOEN is not used with nWAIT
40 */
41
42/* SMBCR bit definitions */
43#define SMBCR_BIWRITEEN (1 << 21)
44#define SMBCR_ADDRVALIDWRITEEN (1 << 20)
45#define SMBCR_SYNCWRITE (1 << 17)
46#define SMBCR_BMWRITE (1 << 16)
47#define SMBCR_WRAPREAD (1 << 14)
48#define SMBCR_BIREADEN (1 << 13)
49#define SMBCR_ADDRVALIDREADEN (1 << 12)
50#define SMBCR_SYNCREAD (1 << 9)
51#define SMBCR_BMREAD (1 << 8)
52#define SMBCR_SMBLSPOL (1 << 6)
53#define SMBCR_WP (1 << 3)
54#define SMBCR_WAITEN (1 << 2)
55#define SMBCR_WAITPOL (1 << 1)
56#define SMBCR_RBLE (1 << 0)
57
58#define SMBCR_BURSTLENWRITE_MASK (3 << 18)
59#define SMBCR_BURSTLENWRITE_4 (0 << 18)
60#define SMBCR_BURSTLENWRITE_8 (1 << 18)
61#define SMBCR_BURSTLENWRITE_RESERVED (2 << 18)
62#define SMBCR_BURSTLENWRITE_CONTINUOUS (3 << 18)
63
64#define SMBCR_BURSTLENREAD_MASK (3 << 10)
65#define SMBCR_BURSTLENREAD_4 (0 << 10)
66#define SMBCR_BURSTLENREAD_8 (1 << 10)
67#define SMBCR_BURSTLENREAD_16 (2 << 10)
68#define SMBCR_BURSTLENREAD_CONTINUOUS (3 << 10)
69
70#define SMBCR_MW_MASK (3 << 4)
71#define SMBCR_MW_8BIT (0 << 4)
72#define SMBCR_MW_16BIT (1 << 4)
73#define SMBCR_MW_M32BIT (2 << 4)
74
75/* SSMC status registers */
76#define SSMCCSR (0x200)
77#define SSMCCR (0x204)
78#define SSMCITCR (0x208)
79#define SSMCITIP (0x20C)
80#define SSMCITIOP (0x210)