Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
215ed323
Commit
215ed323
authored
Aug 14, 2009
by
Ben Dooks
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next-s3c24xx-cpufreq' into next-s3c
parents
0fbdd270
e6d197a6
Changes
38
Show whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
3605 additions
and
26 deletions
+3605
-26
Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
+75
-0
arch/arm/Kconfig
arch/arm/Kconfig
+60
-1
arch/arm/mach-s3c2410/Kconfig
arch/arm/mach-s3c2410/Kconfig
+18
-0
arch/arm/mach-s3c2410/Makefile
arch/arm/mach-s3c2410/Makefile
+2
-0
arch/arm/mach-s3c2410/cpu-freq.c
arch/arm/mach-s3c2410/cpu-freq.c
+159
-0
arch/arm/mach-s3c2410/dma.c
arch/arm/mach-s3c2410/dma.c
+11
-0
arch/arm/mach-s3c2410/include/mach/map.h
arch/arm/mach-s3c2410/include/mach/map.h
+7
-0
arch/arm/mach-s3c2410/include/mach/regs-mem.h
arch/arm/mach-s3c2410/include/mach/regs-mem.h
+10
-0
arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
+21
-2
arch/arm/mach-s3c2410/irq.c
arch/arm/mach-s3c2410/irq.c
+14
-1
arch/arm/mach-s3c2410/mach-bast.c
arch/arm/mach-s3c2410/mach-bast.c
+9
-0
arch/arm/mach-s3c2410/pll.c
arch/arm/mach-s3c2410/pll.c
+95
-0
arch/arm/mach-s3c2410/pm.c
arch/arm/mach-s3c2410/pm.c
+12
-0
arch/arm/mach-s3c2410/s3c2410.c
arch/arm/mach-s3c2410/s3c2410.c
+29
-0
arch/arm/mach-s3c2412/Kconfig
arch/arm/mach-s3c2412/Kconfig
+9
-0
arch/arm/mach-s3c2412/Makefile
arch/arm/mach-s3c2412/Makefile
+1
-0
arch/arm/mach-s3c2412/cpu-freq.c
arch/arm/mach-s3c2412/cpu-freq.c
+257
-0
arch/arm/mach-s3c2412/s3c2412.c
arch/arm/mach-s3c2412/s3c2412.c
+12
-0
arch/arm/mach-s3c2440/Kconfig
arch/arm/mach-s3c2440/Kconfig
+7
-0
arch/arm/mach-s3c2440/mach-osiris.c
arch/arm/mach-s3c2440/mach-osiris.c
+9
-0
arch/arm/mach-s3c6400/include/mach/map.h
arch/arm/mach-s3c6400/include/mach/map.h
+2
-2
arch/arm/plat-s3c/include/plat/cpu-freq.h
arch/arm/plat-s3c/include/plat/cpu-freq.h
+69
-18
arch/arm/plat-s3c/include/plat/cpu.h
arch/arm/plat-s3c/include/plat/cpu.h
+1
-0
arch/arm/plat-s3c/include/plat/map-base.h
arch/arm/plat-s3c/include/plat/map-base.h
+7
-1
arch/arm/plat-s3c24xx/Kconfig
arch/arm/plat-s3c24xx/Kconfig
+59
-0
arch/arm/plat-s3c24xx/Makefile
arch/arm/plat-s3c24xx/Makefile
+10
-0
arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
+199
-0
arch/arm/plat-s3c24xx/cpu-freq.c
arch/arm/plat-s3c24xx/cpu-freq.c
+716
-0
arch/arm/plat-s3c24xx/cpu.c
arch/arm/plat-s3c24xx/cpu.c
+1
-1
arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
+282
-0
arch/arm/plat-s3c24xx/include/plat/s3c2410.h
arch/arm/plat-s3c24xx/include/plat/s3c2410.h
+1
-0
arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
+64
-0
arch/arm/plat-s3c24xx/s3c2410-iotiming.c
arch/arm/plat-s3c24xx/s3c2410-iotiming.c
+477
-0
arch/arm/plat-s3c24xx/s3c2412-iotiming.c
arch/arm/plat-s3c24xx/s3c2412-iotiming.c
+285
-0
arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
+311
-0
arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
+97
-0
arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
+127
-0
include/linux/amba/pl093.h
include/linux/amba/pl093.h
+80
-0
No files found.
Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
0 → 100644
View file @
215ed323
S3C24XX CPUfreq support
=======================
Introduction
------------
The S3C24XX series support a number of power saving systems, such as
the ability to change the core, memory and peripheral operating
frequencies. The core control is exported via the CPUFreq driver
which has a number of different manual or automatic controls over the
rate the core is running at.
There are two forms of the driver depending on the specific CPU and
how the clocks are arranged. The first implementation used as single
PLL to feed the ARM, memory and peripherals via a series of dividers
and muxes and this is the implementation that is documented here. A
newer version where there is a seperate PLL and clock divider for the
ARM core is available as a seperate driver.
Layout
------
The code core manages the CPU specific drivers, any data that they
need to register and the interface to the generic drivers/cpufreq
system. Each CPU registers a driver to control the PLL, clock dividers
and anything else associated with it. Any board that wants to use this
framework needs to supply at least basic details of what is required.
The core registers with drivers/cpufreq at init time if all the data
necessary has been supplied.
CPU support
-----------
The support for each CPU depends on the facilities provided by the
SoC and the driver as each device has different PLL and clock chains
associated with it.
Slow Mode
---------
The SLOW mode where the PLL is turned off altogether and the
system is fed by the external crystal input is currently not
supported.
sysfs
-----
The core code exports extra information via sysfs in the directory
devices/system/cpu/cpu0/arch-freq.
Board Support
-------------
Each board that wants to use the cpufreq code must register some basic
information with the core driver to provide information about what the
board requires and any restrictions being placed on it.
The board needs to supply information about whether it needs the IO bank
timings changing, any maximum frequency limits and information about the
SDRAM refresh rate.
Document Author
---------------
Ben Dooks, Copyright 2009 Simtec Electronics
Licensed under GPLv2
arch/arm/Kconfig
View file @
215ed323
...
...
@@ -126,6 +126,13 @@ config ARCH_HAS_ILOG2_U32
config ARCH_HAS_ILOG2_U64
bool
config ARCH_HAS_CPUFREQ
bool
help
Internal node to signify that the ARCH has CPUFREQ support
and that the relevant menu configurations are displayed for
it.
config GENERIC_HWEIGHT
bool
default y
...
...
@@ -203,6 +210,7 @@ config ARCH_AAEC2000
config ARCH_INTEGRATOR
bool "ARM Ltd. Integrator family"
select ARM_AMBA
select ARCH_HAS_CPUFREQ
select HAVE_CLK
select COMMON_CLKDEV
select ICST525
...
...
@@ -509,6 +517,7 @@ config ARCH_PXA
bool "PXA2xx/PXA3xx-based"
depends on MMU
select ARCH_MTD_XIP
select ARCH_HAS_CPUFREQ
select GENERIC_GPIO
select HAVE_CLK
select COMMON_CLKDEV
...
...
@@ -551,6 +560,7 @@ config ARCH_SA1100
select ISA
select ARCH_SPARSEMEM_ENABLE
select ARCH_MTD_XIP
select ARCH_HAS_CPUFREQ
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
...
...
@@ -563,6 +573,7 @@ config ARCH_SA1100
config ARCH_S3C2410
bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443"
select GENERIC_GPIO
select ARCH_HAS_CPUFREQ
select HAVE_CLK
help
Samsung S3C2410X CPU based systems, such as the Simtec Electronics
...
...
@@ -573,6 +584,7 @@ config ARCH_S3C64XX
bool "Samsung S3C64XX"
select GENERIC_GPIO
select HAVE_CLK
select ARCH_HAS_CPUFREQ
help
Samsung S3C64XX series based systems
...
...
@@ -632,6 +644,7 @@ config ARCH_OMAP
select GENERIC_GPIO
select HAVE_CLK
select ARCH_REQUIRE_GPIOLIB
select ARCH_HAS_CPUFREQ
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
help
...
...
@@ -1241,7 +1254,7 @@ endmenu
menu "CPU Power Management"
if
(ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX)
if
ARCH_HAS_CPUFREQ
source "drivers/cpufreq/Kconfig"
...
...
@@ -1276,6 +1289,52 @@ config CPU_FREQ_S3C64XX
bool "CPUfreq support for Samsung S3C64XX CPUs"
depends on CPU_FREQ && CPU_S3C6410
config CPU_FREQ_S3C
bool
help
Internal configuration node for common cpufreq on Samsung SoC
config CPU_FREQ_S3C24XX
bool "CPUfreq driver for Samsung S3C24XX series CPUs"
depends on ARCH_S3C2410 && CPU_FREQ && EXPERIMENTAL
select CPU_FREQ_S3C
help
This enables the CPUfreq driver for the Samsung S3C24XX family
of CPUs.
For details, take a look at <file:Documentation/cpu-freq>.
If in doubt, say N.
config CPU_FREQ_S3C24XX_PLL
bool "Support CPUfreq changing of PLL frequency"
depends on CPU_FREQ_S3C24XX && EXPERIMENTAL
help
Compile in support for changing the PLL frequency from the
S3C24XX series CPUfreq driver. The PLL takes time to settle
after a frequency change, so by default it is not enabled.
This also means that the PLL tables for the selected CPU(s) will
be built which may increase the size of the kernel image.
config CPU_FREQ_S3C24XX_DEBUG
bool "Debug CPUfreq Samsung driver core"
depends on CPU_FREQ_S3C24XX
help
Enable s3c_freq_dbg for the Samsung S3C CPUfreq core
config CPU_FREQ_S3C24XX_IODEBUG
bool "Debug CPUfreq Samsung driver IO timing"
depends on CPU_FREQ_S3C24XX
help
Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core
config CPU_FREQ_S3C24XX_DEBUGFS
bool "Export debugfs for CPUFreq"
depends on CPU_FREQ_S3C24XX && DEBUG_FS
help
Export status information via debugfs.
endif
source "drivers/cpuidle/Kconfig"
...
...
arch/arm/mach-s3c2410/Kconfig
View file @
215ed323
...
...
@@ -12,6 +12,7 @@ config CPU_S3C2410
select S3C2410_GPIO
select CPU_LLSERIAL_S3C2410
select S3C2410_PM if PM
select S3C2410_CPUFREQ if CPU_FREQ_S3C24XX
help
Support for S3C2410 and S3C2410A family from the S3C24XX line
of Samsung Mobile CPUs.
...
...
@@ -45,6 +46,22 @@ config MACH_BAST_IDE
Internal node for machines with an BAST style IDE
interface
# cpu frequency scaling support
config S3C2410_CPUFREQ
bool
depends on CPU_FREQ_S3C24XX && CPU_S3C2410
select S3C2410_CPUFREQ_UTILS
help
CPU Frequency scaling support for S3C2410
config S3C2410_PLLTABLE
bool
depends on S3C2410_CPUFREQ && CPU_FREQ_S3C24XX_PLL
default y
help
Select the PLL table for the S3C2410
menu "S3C2410 Machines"
config ARCH_SMDK2410
...
...
@@ -79,6 +96,7 @@ config MACH_N30
config ARCH_BAST
bool "Simtec Electronics BAST (EB2410ITX)"
select CPU_S3C2410
select S3C2410_IOTIMING if S3C2410_CPUFREQ
select PM_SIMTEC if PM
select SIMTEC_NOR
select MACH_BAST_IDE
...
...
arch/arm/mach-s3c2410/Makefile
View file @
215ed323
...
...
@@ -15,6 +15,8 @@ obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
obj-$(CONFIG_CPU_S3C2410_DMA)
+=
dma.o
obj-$(CONFIG_S3C2410_PM)
+=
pm.o sleep.o
obj-$(CONFIG_S3C2410_GPIO)
+=
gpio.o
obj-$(CONFIG_S3C2410_CPUFREQ)
+=
cpu-freq.o
obj-$(CONFIG_S3C2410_PLLTABLE)
+=
pll.o
# Machine support
...
...
arch/arm/mach-s3c2410/cpu-freq.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/mach-s3c2410/cpu-freq.c
*
* Copyright (c) 2006,2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2410 CPU Frequency scaling
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/regs-clock.h>
#include <plat/cpu.h>
#include <plat/clock.h>
#include <plat/cpu-freq-core.h>
/* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */
static
void
s3c2410_cpufreq_setdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
u32
clkdiv
=
0
;
if
(
cfg
->
divs
.
h_divisor
==
2
)
clkdiv
|=
S3C2410_CLKDIVN_HDIVN
;
if
(
cfg
->
divs
.
p_divisor
!=
cfg
->
divs
.
h_divisor
)
clkdiv
|=
S3C2410_CLKDIVN_PDIVN
;
__raw_writel
(
clkdiv
,
S3C2410_CLKDIVN
);
}
static
int
s3c2410_cpufreq_calcdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
long
hclk
,
fclk
,
pclk
;
unsigned
int
hdiv
,
pdiv
;
unsigned
long
hclk_max
;
fclk
=
cfg
->
freq
.
fclk
;
hclk_max
=
cfg
->
max
.
hclk
;
cfg
->
freq
.
armclk
=
fclk
;
s3c_freq_dbg
(
"%s: fclk is %lu, max hclk %lu
\n
"
,
__func__
,
fclk
,
hclk_max
);
hdiv
=
(
fclk
>
cfg
->
max
.
hclk
)
?
2
:
1
;
hclk
=
fclk
/
hdiv
;
if
(
hclk
>
cfg
->
max
.
hclk
)
{
s3c_freq_dbg
(
"%s: hclk too big
\n
"
,
__func__
);
return
-
EINVAL
;
}
pdiv
=
(
hclk
>
cfg
->
max
.
pclk
)
?
2
:
1
;
pclk
=
hclk
/
pdiv
;
if
(
pclk
>
cfg
->
max
.
pclk
)
{
s3c_freq_dbg
(
"%s: pclk too big
\n
"
,
__func__
);
return
-
EINVAL
;
}
pdiv
*=
hdiv
;
/* record the result */
cfg
->
divs
.
p_divisor
=
pdiv
;
cfg
->
divs
.
h_divisor
=
hdiv
;
return
0
;
}
static
struct
s3c_cpufreq_info
s3c2410_cpufreq_info
=
{
.
max
=
{
.
fclk
=
200000000
,
.
hclk
=
100000000
,
.
pclk
=
50000000
,
},
/* transition latency is about 5ms worst-case, so
* set 10ms to be sure */
.
latency
=
10000000
,
.
locktime_m
=
150
,
.
locktime_u
=
150
,
.
locktime_bits
=
12
,
.
need_pll
=
1
,
.
name
=
"s3c2410"
,
.
calc_iotiming
=
s3c2410_iotiming_calc
,
.
set_iotiming
=
s3c2410_iotiming_set
,
.
get_iotiming
=
s3c2410_iotiming_get
,
.
resume_clocks
=
s3c2410_setup_clocks
,
.
set_fvco
=
s3c2410_set_fvco
,
.
set_refresh
=
s3c2410_cpufreq_setrefresh
,
.
set_divs
=
s3c2410_cpufreq_setdivs
,
.
calc_divs
=
s3c2410_cpufreq_calcdivs
,
.
debug_io_show
=
s3c_cpufreq_debugfs_call
(
s3c2410_iotiming_debugfs
),
};
static
int
s3c2410_cpufreq_add
(
struct
sys_device
*
sysdev
)
{
return
s3c_cpufreq_register
(
&
s3c2410_cpufreq_info
);
}
static
struct
sysdev_driver
s3c2410_cpufreq_driver
=
{
.
add
=
s3c2410_cpufreq_add
,
};
static
int
__init
s3c2410_cpufreq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410_sysclass
,
&
s3c2410_cpufreq_driver
);
}
arch_initcall
(
s3c2410_cpufreq_init
);
static
int
s3c2410a_cpufreq_add
(
struct
sys_device
*
sysdev
)
{
/* alter the maximum freq settings for S3C2410A. If a board knows
* it only has a maximum of 200, then it should register its own
* limits. */
s3c2410_cpufreq_info
.
max
.
fclk
=
266000000
;
s3c2410_cpufreq_info
.
max
.
hclk
=
133000000
;
s3c2410_cpufreq_info
.
max
.
pclk
=
66500000
;
s3c2410_cpufreq_info
.
name
=
"s3c2410a"
;
return
s3c2410_cpufreq_add
(
sysdev
);
}
static
struct
sysdev_driver
s3c2410a_cpufreq_driver
=
{
.
add
=
s3c2410a_cpufreq_add
,
};
static
int
__init
s3c2410a_cpufreq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410a_sysclass
,
&
s3c2410a_cpufreq_driver
);
}
arch_initcall
(
s3c2410a_cpufreq_init
);
arch/arm/mach-s3c2410/dma.c
View file @
215ed323
...
...
@@ -164,6 +164,17 @@ static int __init s3c2410_dma_drvinit(void)
}
arch_initcall
(
s3c2410_dma_drvinit
);
static
struct
sysdev_driver
s3c2410a_dma_driver
=
{
.
add
=
s3c2410_dma_add
,
};
static
int
__init
s3c2410a_dma_drvinit
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410a_sysclass
,
&
s3c2410a_dma_driver
);
}
arch_initcall
(
s3c2410a_dma_drvinit
);
#endif
#if defined(CONFIG_CPU_S3C2442)
...
...
arch/arm/mach-s3c2410/include/mach/map.h
View file @
215ed323
...
...
@@ -67,6 +67,13 @@
#define S3C2443_PA_HSMMC (0x4A800000)
#define S3C2443_SZ_HSMMC (256)
/* S3C2412 memory and IO controls */
#define S3C2412_PA_SSMC (0x4F000000)
#define S3C2412_VA_SSMC S3C_ADDR_CPU(0x00000000)
#define S3C2412_PA_EBI (0x48800000)
#define S3C2412_VA_EBI S3C_ADDR_CPU(0x00010000)
/* physical addresses of all the chip-select areas */
#define S3C2410_CS0 (0x00000000)
...
...
arch/arm/mach-s3c2410/include/mach/regs-mem.h
View file @
215ed323
...
...
@@ -73,6 +73,16 @@
#define S3C2410_BWSCON_WS7 (1<<30)
#define S3C2410_BWSCON_ST7 (1<<31)
/* accesor functions for getting BANK(n) configuration. (n != 0) */
#define S3C2410_BWSCON_GET(_bwscon, _bank) (((_bwscon) >> ((_bank) * 4)) & 0xf)
#define S3C2410_BWSCON_DW8 (0)
#define S3C2410_BWSCON_DW16 (1)
#define S3C2410_BWSCON_DW32 (2)
#define S3C2410_BWSCON_WS (1 << 2)
#define S3C2410_BWSCON_ST (1 << 3)
/* memory set (rom, ram) */
#define S3C2410_BANKCON0 S3C2410_MEMREG(0x0004)
#define S3C2410_BANKCON1 S3C2410_MEMREG(0x0008)
...
...
arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
View file @
215ed323
...
...
@@ -14,9 +14,11 @@
#ifndef __ASM_ARM_REGS_S3C2412_MEM
#define __ASM_ARM_REGS_S3C2412_MEM
#ifndef S3C2412_MEMREG
#define S3C2412_MEMREG(x) (S3C24XX_VA_MEMCTRL + (x))
#endif
#define S3C2412_EBIREG(x) (S3C2412_VA_EBI + (x))
#define S3C2412_SSMCREG(x) (S3C2412_VA_SSMC + (x))
#define S3C2412_SSMC(x, o) (S3C2412_SSMCREG((x * 0x20) + (o)))
#define S3C2412_BANKCFG S3C2412_MEMREG(0x00)
#define S3C2412_BANKCON1 S3C2412_MEMREG(0x04)
...
...
@@ -26,4 +28,21 @@
#define S3C2412_REFRESH S3C2412_MEMREG(0x10)
#define S3C2412_TIMEOUT S3C2412_MEMREG(0x14)
/* EBI control registers */
#define S3C2412_EBI_PR S3C2412_EBIREG(0x00)
#define S3C2412_EBI_BANKCFG S3C2412_EBIREG(0x04)
/* SSMC control registers */
#define S3C2412_SSMC_BANK(x) S3C2412_SSMC(x, 0x00)
#define S3C2412_SMIDCYR(x) S3C2412_SSMC(x, 0x00)
#define S3C2412_SMBWSTRD(x) S3C2412_SSMC(x, 0x04)
#define S3C2412_SMBWSTWRR(x) S3C2412_SSMC(x, 0x08)
#define S3C2412_SMBWSTOENR(x) S3C2412_SSMC(x, 0x0C)
#define S3C2412_SMBWSTWENR(x) S3C2412_SSMC(x, 0x10)
#define S3C2412_SMBCR(x) S3C2412_SSMC(x, 0x14)
#define S3C2412_SMBSR(x) S3C2412_SSMC(x, 0x18)
#define S3C2412_SMBWSTBRDR(x) S3C2412_SSMC(x, 0x1C)
#endif
/* __ASM_ARM_REGS_S3C2412_MEM */
arch/arm/mach-s3c2410/irq.c
View file @
215ed323
...
...
@@ -39,9 +39,22 @@ static struct sysdev_driver s3c2410_irq_driver = {
.
resume
=
s3c24xx_irq_resume
,
};
static
int
s3c2410_irq_init
(
void
)
static
int
__init
s3c2410_irq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410_sysclass
,
&
s3c2410_irq_driver
);
}
arch_initcall
(
s3c2410_irq_init
);
static
struct
sysdev_driver
s3c2410a_irq_driver
=
{
.
add
=
s3c2410_irq_add
,
.
suspend
=
s3c24xx_irq_suspend
,
.
resume
=
s3c24xx_irq_resume
,
};
static
int
__init
s3c2410a_irq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410a_sysclass
,
&
s3c2410a_irq_driver
);
}
arch_initcall
(
s3c2410a_irq_init
);
arch/arm/mach-s3c2410/mach-bast.c
View file @
215ed323
...
...
@@ -60,6 +60,7 @@
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/cpu-freq.h>
#include "usb-simtec.h"
#include "nor-simtec.h"
...
...
@@ -601,6 +602,12 @@ static struct clk *bast_clocks[] __initdata = {
&
s3c24xx_uclk
,
};
static
struct
s3c_cpufreq_board
__initdata
bast_cpufreq
=
{
.
refresh
=
7800
,
/* 7.8usec */
.
auto_io
=
1
,
.
need_io
=
1
,
};
static
void
__init
bast_map_io
(
void
)
{
/* initialise the clocks */
...
...
@@ -640,6 +647,8 @@ static void __init bast_init(void)
usb_simtec_init
();
nor_simtec_init
();
s3c_cpufreq_setboard
(
&
bast_cpufreq
);
}
MACHINE_START
(
BAST
,
"Simtec-BAST"
)
...
...
arch/arm/mach-s3c2410/pll.c
0 → 100644
View file @
215ed323
/* arch/arm/mach-s3c2410/pll.c
*
* Copyright (c) 2006,2007 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
* Vincent Sanders <vince@arm.linux.org.uk>
*
* S3C2410 CPU PLL tables
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
static
struct
cpufreq_frequency_table
pll_vals_12MHz
[]
=
{
{
.
frequency
=
34000000
,
.
index
=
PLLVAL
(
82
,
2
,
3
),
},
{
.
frequency
=
45000000
,
.
index
=
PLLVAL
(
82
,
1
,
3
),
},
{
.
frequency
=
51000000
,
.
index
=
PLLVAL
(
161
,
3
,
3
),
},
{
.
frequency
=
48000000
,
.
index
=
PLLVAL
(
120
,
2
,
3
),
},
{
.
frequency
=
56000000
,
.
index
=
PLLVAL
(
142
,
2
,
3
),
},
{
.
frequency
=
68000000
,
.
index
=
PLLVAL
(
82
,
2
,
2
),
},
{
.
frequency
=
79000000
,
.
index
=
PLLVAL
(
71
,
1
,
2
),
},
{
.
frequency
=
85000000
,
.
index
=
PLLVAL
(
105
,
2
,
2
),
},
{
.
frequency
=
90000000
,
.
index
=
PLLVAL
(
112
,
2
,
2
),
},
{
.
frequency
=
101000000
,
.
index
=
PLLVAL
(
127
,
2
,
2
),
},
{
.
frequency
=
113000000
,
.
index
=
PLLVAL
(
105
,
1
,
2
),
},
{
.
frequency
=
118000000
,
.
index
=
PLLVAL
(
150
,
2
,
2
),
},
{
.
frequency
=
124000000
,
.
index
=
PLLVAL
(
116
,
1
,
2
),
},
{
.
frequency
=
135000000
,
.
index
=
PLLVAL
(
82
,
2
,
1
),
},
{
.
frequency
=
147000000
,
.
index
=
PLLVAL
(
90
,
2
,
1
),
},
{
.
frequency
=
152000000
,
.
index
=
PLLVAL
(
68
,
1
,
1
),
},
{
.
frequency
=
158000000
,
.
index
=
PLLVAL
(
71
,
1
,
1
),
},
{
.
frequency
=
170000000
,
.
index
=
PLLVAL
(
77
,
1
,
1
),
},
{
.
frequency
=
180000000
,
.
index
=
PLLVAL
(
82
,
1
,
1
),
},
{
.
frequency
=
186000000
,
.
index
=
PLLVAL
(
85
,
1
,
1
),
},
{
.
frequency
=
192000000
,
.
index
=
PLLVAL
(
88
,
1
,
1
),
},
{
.
frequency
=
203000000
,
.
index
=
PLLVAL
(
161
,
3
,
1
),
},
/* 2410A extras */
{
.
frequency
=
210000000
,
.
index
=
PLLVAL
(
132
,
2
,
1
),
},
{
.
frequency
=
226000000
,
.
index
=
PLLVAL
(
105
,
1
,
1
),
},
{
.
frequency
=
266000000
,
.
index
=
PLLVAL
(
125
,
1
,
1
),
},
{
.
frequency
=
268000000
,
.
index
=
PLLVAL
(
126
,
1
,
1
),
},
{
.
frequency
=
270000000
,
.
index
=
PLLVAL
(
127
,
1
,
1
),
},
};
static
int
s3c2410_plls_add
(
struct
sys_device
*
dev
)
{
return
s3c_plltab_register
(
pll_vals_12MHz
,
ARRAY_SIZE
(
pll_vals_12MHz
));
}
static
struct
sysdev_driver
s3c2410_plls_drv
=
{
.
add
=
s3c2410_plls_add
,
};
static
int
__init
s3c2410_pll_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410_sysclass
,
&
s3c2410_plls_drv
);
}
arch_initcall
(
s3c2410_pll_init
);
static
struct
sysdev_driver
s3c2410a_plls_drv
=
{
.
add
=
s3c2410_plls_add
,
};
static
int
__init
s3c2410a_pll_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410a_sysclass
,
&
s3c2410a_plls_drv
);
}
arch_initcall
(
s3c2410a_pll_init
);
arch/arm/mach-s3c2410/pm.c
View file @
215ed323
...
...
@@ -119,6 +119,18 @@ static int __init s3c2410_pm_drvinit(void)
}
arch_initcall
(
s3c2410_pm_drvinit
);
static
struct
sysdev_driver
s3c2410a_pm_driver
=
{
.
add
=
s3c2410_pm_add
,
.
resume
=
s3c2410_pm_resume
,
};
static
int
__init
s3c2410a_pm_drvinit
(
void
)
{
return
sysdev_driver_register
(
&
s3c2410a_sysclass
,
&
s3c2410a_pm_driver
);
}
arch_initcall
(
s3c2410a_pm_drvinit
);
#endif
#if defined(CONFIG_CPU_S3C2440)
...
...
arch/arm/mach-s3c2410/s3c2410.c
View file @
215ed323
...
...
@@ -105,17 +105,33 @@ void __init_or_cpufreq s3c2410_setup_clocks(void)
s3c24xx_setup_clocks
(
fclk
,
hclk
,
pclk
);
}
/* fake ARMCLK for use with cpufreq, etc. */
static
struct
clk
s3c2410_armclk
=
{
.
name
=
"armclk"
,
.
parent
=
&
clk_f
,
.
id
=
-
1
,
};
void
__init
s3c2410_init_clocks
(
int
xtal
)
{
s3c24xx_register_baseclocks
(
xtal
);
s3c2410_setup_clocks
();
s3c2410_baseclk_add
();
s3c24xx_register_clock
(
&
s3c2410_armclk
);
}
struct
sysdev_class
s3c2410_sysclass
=
{
.
name
=
"s3c2410-core"
,
};
/* Note, we would have liked to name this s3c2410-core, but we cannot
* register two sysdev_class with the same name.
*/
struct
sysdev_class
s3c2410a_sysclass
=
{
.
name
=
"s3c2410a-core"
,
};
static
struct
sys_device
s3c2410_sysdev
=
{
.
cls
=
&
s3c2410_sysclass
,
};
...
...
@@ -133,9 +149,22 @@ static int __init s3c2410_core_init(void)
core_initcall
(
s3c2410_core_init
);
static
int
__init
s3c2410a_core_init
(
void
)
{
return
sysdev_class_register
(
&
s3c2410a_sysclass
);
}
core_initcall
(
s3c2410a_core_init
);
int
__init
s3c2410_init
(
void
)
{
printk
(
"S3C2410: Initialising architecture
\n
"
);
return
sysdev_register
(
&
s3c2410_sysdev
);
}
int
__init
s3c2410a_init
(
void
)
{
s3c2410_sysdev
.
cls
=
&
s3c2410a_sysclass
;
return
s3c2410_init
();
}
arch/arm/mach-s3c2412/Kconfig
View file @
215ed323
...
...
@@ -32,6 +32,15 @@ config S3C2412_PM
help
Internal config node to apply S3C2412 power management
# Note, the S3C2412 IOtiming support is in plat-s3c24xx
config S3C2412_CPUFREQ
bool
depends on CPU_FREQ_S3C24XX && CPU_S3C2412
select S3C2412_IOTIMING
default y
help
CPU Frequency scaling support for S3C2412 and S3C2413 SoC CPUs.
menu "S3C2412 Machines"
...
...
arch/arm/mach-s3c2412/Makefile
View file @
215ed323
...
...
@@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_S3C2412) += clock.o
obj-$(CONFIG_CPU_S3C2412)
+=
gpio.o
obj-$(CONFIG_S3C2412_DMA)
+=
dma.o
obj-$(CONFIG_S3C2412_PM)
+=
pm.o sleep.o
obj-$(CONFIG_S3C2412_CPUFREQ)
+=
cpu-freq.o
# Machine support
...
...
arch/arm/mach-s3c2412/cpu-freq.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/mach-s3c2412/cpu-freq.c
*
* Copyright 2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2412 CPU Frequency scalling
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/sysdev.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-s3c2412-mem.h>
#include <plat/cpu.h>
#include <plat/clock.h>
#include <plat/cpu-freq-core.h>
/* our clock resources. */
static
struct
clk
*
xtal
;
static
struct
clk
*
fclk
;
static
struct
clk
*
hclk
;
static
struct
clk
*
armclk
;
/* HDIV: 1, 2, 3, 4, 6, 8 */
static
int
s3c2412_cpufreq_calcdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
int
hdiv
,
pdiv
,
armdiv
,
dvs
;
unsigned
long
hclk
,
fclk
,
armclk
,
armdiv_clk
;
unsigned
long
hclk_max
;
fclk
=
cfg
->
freq
.
fclk
;
armclk
=
cfg
->
freq
.
armclk
;
hclk_max
=
cfg
->
max
.
hclk
;
/* We can't run hclk above armclk as at the best we have to
* have armclk and hclk in dvs mode. */
if
(
hclk_max
>
armclk
)
hclk_max
=
armclk
;
s3c_freq_dbg
(
"%s: fclk=%lu, armclk=%lu, hclk_max=%lu
\n
"
,
__func__
,
fclk
,
armclk
,
hclk_max
);
s3c_freq_dbg
(
"%s: want f=%lu, arm=%lu, h=%lu, p=%lu
\n
"
,
__func__
,
cfg
->
freq
.
fclk
,
cfg
->
freq
.
armclk
,
cfg
->
freq
.
hclk
,
cfg
->
freq
.
pclk
);
armdiv
=
fclk
/
armclk
;
if
(
armdiv
<
1
)
armdiv
=
1
;
if
(
armdiv
>
2
)
armdiv
=
2
;
cfg
->
divs
.
arm_divisor
=
armdiv
;
armdiv_clk
=
fclk
/
armdiv
;
hdiv
=
armdiv_clk
/
hclk_max
;
if
(
hdiv
<
1
)
hdiv
=
1
;
cfg
->
freq
.
hclk
=
hclk
=
armdiv_clk
/
hdiv
;
/* set dvs depending on whether we reached armclk or not. */
cfg
->
divs
.
dvs
=
dvs
=
armclk
<
armdiv_clk
;
/* update the actual armclk we achieved. */
cfg
->
freq
.
armclk
=
dvs
?
hclk
:
armdiv_clk
;
s3c_freq_dbg
(
"%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d
\n
"
,
__func__
,
armclk
,
hclk
,
armdiv
,
hdiv
,
cfg
->
divs
.
dvs
);
if
(
hdiv
>
4
)
goto
invalid
;
pdiv
=
(
hclk
>
cfg
->
max
.
pclk
)
?
2
:
1
;
if
((
hclk
/
pdiv
)
>
cfg
->
max
.
pclk
)
pdiv
++
;
cfg
->
freq
.
pclk
=
hclk
/
pdiv
;
s3c_freq_dbg
(
"%s: pdiv %d
\n
"
,
__func__
,
pdiv
);
if
(
pdiv
>
2
)
goto
invalid
;
pdiv
*=
hdiv
;
/* store the result, and then return */
cfg
->
divs
.
h_divisor
=
hdiv
*
armdiv
;
cfg
->
divs
.
p_divisor
=
pdiv
*
armdiv
;
return
0
;
invalid:
return
-
EINVAL
;
}
static
void
s3c2412_cpufreq_setdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
long
clkdiv
;
unsigned
long
olddiv
;
olddiv
=
clkdiv
=
__raw_readl
(
S3C2410_CLKDIVN
);
/* clear off current clock info */
clkdiv
&=
~
S3C2412_CLKDIVN_ARMDIVN
;
clkdiv
&=
~
S3C2412_CLKDIVN_HDIVN_MASK
;
clkdiv
&=
~
S3C2412_CLKDIVN_PDIVN
;
if
(
cfg
->
divs
.
arm_divisor
==
2
)
clkdiv
|=
S3C2412_CLKDIVN_ARMDIVN
;
clkdiv
|=
((
cfg
->
divs
.
h_divisor
/
cfg
->
divs
.
arm_divisor
)
-
1
);
if
(
cfg
->
divs
.
p_divisor
!=
cfg
->
divs
.
h_divisor
)
clkdiv
|=
S3C2412_CLKDIVN_PDIVN
;
s3c_freq_dbg
(
"%s: div %08lx => %08lx
\n
"
,
__func__
,
olddiv
,
clkdiv
);
__raw_writel
(
clkdiv
,
S3C2410_CLKDIVN
);
clk_set_parent
(
armclk
,
cfg
->
divs
.
dvs
?
hclk
:
fclk
);
}
static
void
s3c2412_cpufreq_setrefresh
(
struct
s3c_cpufreq_config
*
cfg
)
{
struct
s3c_cpufreq_board
*
board
=
cfg
->
board
;
unsigned
long
refresh
;
s3c_freq_dbg
(
"%s: refresh %u ns, hclk %lu
\n
"
,
__func__
,
board
->
refresh
,
cfg
->
freq
.
hclk
);
/* Reduce both the refresh time (in ns) and the frequency (in MHz)
* by 10 each to ensure that we do not overflow 32 bit numbers. This
* should work for HCLK up to 133MHz and refresh period up to 30usec.
*/
refresh
=
(
board
->
refresh
/
10
);
refresh
*=
(
cfg
->
freq
.
hclk
/
100
);
refresh
/=
(
1
*
1000
*
1000
);
/* 10^6 */
s3c_freq_dbg
(
"%s: setting refresh 0x%08lx
\n
"
,
__func__
,
refresh
);
__raw_writel
(
refresh
,
S3C2412_REFRESH
);
}
/* set the default cpu frequency information, based on an 200MHz part
* as we have no other way of detecting the speed rating in software.
*/
static
struct
s3c_cpufreq_info
s3c2412_cpufreq_info
=
{
.
max
=
{
.
fclk
=
200000000
,
.
hclk
=
100000000
,
.
pclk
=
50000000
,
},
.
latency
=
5000000
,
/* 5ms */
.
locktime_m
=
150
,
.
locktime_u
=
150
,
.
locktime_bits
=
16
,
.
name
=
"s3c2412"
,
.
set_refresh
=
s3c2412_cpufreq_setrefresh
,
.
set_divs
=
s3c2412_cpufreq_setdivs
,
.
calc_divs
=
s3c2412_cpufreq_calcdivs
,
.
calc_iotiming
=
s3c2412_iotiming_calc
,
.
set_iotiming
=
s3c2412_iotiming_set
,
.
get_iotiming
=
s3c2412_iotiming_get
,
.
resume_clocks
=
s3c2412_setup_clocks
,
.
debug_io_show
=
s3c_cpufreq_debugfs_call
(
s3c2412_iotiming_debugfs
),
};
static
int
s3c2412_cpufreq_add
(
struct
sys_device
*
sysdev
)
{
unsigned
long
fclk_rate
;
hclk
=
clk_get
(
NULL
,
"hclk"
);
if
(
IS_ERR
(
hclk
))
{
printk
(
KERN_ERR
"%s: cannot find hclk clock
\n
"
,
__func__
);
return
-
ENOENT
;
}
fclk
=
clk_get
(
NULL
,
"fclk"
);
if
(
IS_ERR
(
fclk
))
{
printk
(
KERN_ERR
"%s: cannot find fclk clock
\n
"
,
__func__
);
goto
err_fclk
;
}
fclk_rate
=
clk_get_rate
(
fclk
);
if
(
fclk_rate
>
200000000
)
{
printk
(
KERN_INFO
"%s: fclk %ld MHz, assuming 266MHz capable part
\n
"
,
__func__
,
fclk_rate
/
1000000
);
s3c2412_cpufreq_info
.
max
.
fclk
=
266000000
;
s3c2412_cpufreq_info
.
max
.
hclk
=
133000000
;
s3c2412_cpufreq_info
.
max
.
pclk
=
66000000
;
}
armclk
=
clk_get
(
NULL
,
"armclk"
);
if
(
IS_ERR
(
armclk
))
{
printk
(
KERN_ERR
"%s: cannot find arm clock
\n
"
,
__func__
);
goto
err_armclk
;
}
xtal
=
clk_get
(
NULL
,
"xtal"
);
if
(
IS_ERR
(
xtal
))
{
printk
(
KERN_ERR
"%s: cannot find xtal clock
\n
"
,
__func__
);
goto
err_xtal
;
}
return
s3c_cpufreq_register
(
&
s3c2412_cpufreq_info
);
err_xtal:
clk_put
(
armclk
);
err_armclk:
clk_put
(
fclk
);
err_fclk:
clk_put
(
hclk
);
return
-
ENOENT
;
}
static
struct
sysdev_driver
s3c2412_cpufreq_driver
=
{
.
add
=
s3c2412_cpufreq_add
,
};
static
int
s3c2412_cpufreq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2412_sysclass
,
&
s3c2412_cpufreq_driver
);
}
arch_initcall
(
s3c2412_cpufreq_init
);
arch/arm/mach-s3c2412/s3c2412.c
View file @
215ed323
...
...
@@ -69,6 +69,18 @@ static struct map_desc s3c2412_iodesc[] __initdata = {
IODESC_ENT
(
CLKPWR
),
IODESC_ENT
(
TIMER
),
IODESC_ENT
(
WATCHDOG
),
{
.
virtual
=
(
unsigned
long
)
S3C2412_VA_SSMC
,
.
pfn
=
__phys_to_pfn
(
S3C2412_PA_SSMC
),
.
length
=
SZ_1M
,
.
type
=
MT_DEVICE
,
},
{
.
virtual
=
(
unsigned
long
)
S3C2412_VA_EBI
,
.
pfn
=
__phys_to_pfn
(
S3C2412_PA_EBI
),
.
length
=
SZ_1M
,
.
type
=
MT_DEVICE
,
},
};
/* uart registration process */
...
...
arch/arm/mach-s3c2440/Kconfig
View file @
215ed323
...
...
@@ -33,6 +33,7 @@ config MACH_ANUBIS
select PM_SIMTEC if PM
select HAVE_PATA_PLATFORM
select S3C24XX_GPIO_EXTRA64
select S3C2440_XTAL_12000000
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Simtec Electronics ANUBIS
...
...
@@ -44,6 +45,8 @@ config MACH_OSIRIS
select S3C24XX_DCLK
select PM_SIMTEC if PM
select S3C24XX_GPIO_EXTRA128
select S3C2440_XTAL_12000000
select S3C2410_IOTIMING if S3C2440_CPUFREQ
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Simtec IM2440D20 module, also
...
...
@@ -52,6 +55,7 @@ config MACH_OSIRIS
config MACH_RX3715
bool "HP iPAQ rx3715"
select CPU_S3C2440
select S3C2440_XTAL_16934400
select PM_H1940 if PM
help
Say Y here if you are using the HP iPAQ rx3715.
...
...
@@ -59,6 +63,7 @@ config MACH_RX3715
config ARCH_S3C2440
bool "SMDK2440"
select CPU_S3C2440
select S3C2440_XTAL_16934400
select MACH_SMDK
select S3C_DEV_USB_HOST
help
...
...
@@ -67,6 +72,7 @@ config ARCH_S3C2440
config MACH_NEXCODER_2440
bool "NexVision NEXCODER 2440 Light Board"
select CPU_S3C2440
select S3C2440_XTAL_12000000
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board
...
...
@@ -75,6 +81,7 @@ config SMDK2440_CPU2440
bool "SMDK2440 with S3C2440 CPU module"
depends on ARCH_S3C2440
default y if ARCH_S3C2440
select S3C2440_XTAL_16934400
select CPU_S3C2440
config MACH_AT2440EVB
...
...
arch/arm/mach-s3c2440/mach-osiris.c
View file @
215ed323
...
...
@@ -34,6 +34,7 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <plat/cpu-freq.h>
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-mem.h>
...
...
@@ -351,6 +352,12 @@ static struct clk *osiris_clocks[] __initdata = {
&
s3c24xx_uclk
,
};
static
struct
s3c_cpufreq_board
__initdata
osiris_cpufreq
=
{
.
refresh
=
7800
,
/* refresh period is 7.8usec */
.
auto_io
=
1
,
.
need_io
=
1
,
};
static
void
__init
osiris_map_io
(
void
)
{
unsigned
long
flags
;
...
...
@@ -402,6 +409,8 @@ static void __init osiris_init(void)
s3c_i2c0_set_platdata
(
NULL
);
s3c_cpufreq_setboard
(
&
osiris_cpufreq
);
i2c_register_board_info
(
0
,
osiris_i2c_devs
,
ARRAY_SIZE
(
osiris_i2c_devs
));
...
...
arch/arm/mach-s3c6400/include/mach/map.h
View file @
215ed323
...
...
@@ -49,7 +49,7 @@
#define S3C64XX_PA_IIC1 (0x7F00F000)
#define S3C64XX_PA_GPIO (0x7F008000)
#define S3C64XX_VA_GPIO S3C_ADDR
(0x005
00000)
#define S3C64XX_VA_GPIO S3C_ADDR
_CPU(0x000
00000)
#define S3C64XX_SZ_GPIO SZ_4K
#define S3C64XX_PA_SDRAM (0x50000000)
...
...
@@ -57,7 +57,7 @@
#define S3C64XX_PA_VIC1 (0x71300000)
#define S3C64XX_PA_MODEM (0x74108000)
#define S3C64XX_VA_MODEM S3C_ADDR
(0x006
00000)
#define S3C64XX_VA_MODEM S3C_ADDR
_CPU(0x001
00000)
#define S3C64XX_PA_USBHOST (0x74300000)
...
...
arch/arm/plat-s3c/include/plat/cpu-freq.h
View file @
215ed323
...
...
@@ -17,6 +17,21 @@ struct s3c_cpufreq_info;
struct
s3c_cpufreq_board
;
struct
s3c_iotimings
;
/**
* struct s3c_freq - frequency information (mainly for core drivers)
* @fclk: The FCLK frequency in Hz.
* @armclk: The ARMCLK frequency in Hz.
* @hclk_tns: HCLK cycle time in 10ths of nano-seconds.
* @hclk: The HCLK frequency in Hz.
* @pclk: The PCLK frequency in Hz.
*
* This contains the frequency information about the current configuration
* mainly for the core drivers to ensure we do not end up passing about
* a large number of parameters.
*
* The @hclk_tns field is a useful cache for the parts of the drivers that
* need to calculate IO timings and suchlike.
*/
struct
s3c_freq
{
unsigned
long
fclk
;
unsigned
long
armclk
;
...
...
@@ -25,48 +40,84 @@ struct s3c_freq {
unsigned
long
pclk
;
};
/* wrapper 'struct cpufreq_freqs' so that any drivers receiving the
/**
* struct s3c_cpufreq_freqs - s3c cpufreq notification information.
* @freqs: The cpufreq setting information.
* @old: The old clock settings.
* @new: The new clock settings.
* @pll_changing: Set if the PLL is changing.
*
* Wrapper 'struct cpufreq_freqs' so that any drivers receiving the
* notification can use this information that is not provided by just
* having the core frequency alone.
*
* The pll_changing flag is used to indicate if the PLL itself is
* being set during this change. This is important as the clocks
* will temporarily be set to the XTAL clock during this time, so
* drivers may want to close down their output during this time.
*
* Note, this is not being used by any current drivers and therefore
* may be removed in the future.
*/
struct
s3c_cpufreq_freqs
{
struct
cpufreq_freqs
freqs
;
struct
s3c_freq
old
;
struct
s3c_freq
new
;
unsigned
int
pll_changing
:
1
;
};
#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs)
/**
* struct s3c_clkdivs - clock divisor information
* @p_divisor: Divisor from FCLK to PCLK.
* @h_divisor: Divisor from FCLK to HCLK.
* @arm_divisor: Divisor from FCLK to ARMCLK (not all CPUs).
* @dvs: Non-zero if using DVS mode for ARMCLK.
*
* Divisor settings for the core clocks.
*/
struct
s3c_clkdivs
{
int
p_divisor
;
/* fclk / pclk */
int
h_divisor
;
/* fclk / hclk */
int
arm_divisor
;
/* not all cpus have this. */
unsigned
char
dvs
;
/* using dvs mode to arm. */
int
p_divisor
;
int
h_divisor
;
int
arm_divisor
;
unsigned
char
dvs
;
};
#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s))
/**
* struct s3c_pllval - PLL value entry.
* @freq: The frequency for this entry in Hz.
* @pll_reg: The PLL register setting for this PLL value.
*/
struct
s3c_pllval
{
unsigned
long
freq
;
unsigned
long
pll_reg
;
};
struct
s3c_cpufreq_config
{
struct
s3c_freq
freq
;
struct
s3c_pllval
pll
;
struct
s3c_clkdivs
divs
;
struct
s3c_cpufreq_info
*
info
;
/* for core, not drivers */
struct
s3c_cpufreq_board
*
board
;
};
/* s3c_cpufreq_board
/**
* struct s3c_cpufreq_board - per-board cpu frequency informatin
* @refresh: The SDRAM refresh period in nanoseconds.
* @auto_io: Set if the IO timing settings should be generated from the
* initialisation time hardware registers.
* @need_io: Set if the board has external IO on any of the chipselect
* lines that will require the hardware timing registers to be
* updated on a clock change.
* @max: The maxium frequency limits for the system. Any field that
* is left at zero will use the CPU's settings.
*
* This contains the board specific settings that affect how the CPU
* drivers chose settings. These include the memory refresh and IO
* timing information.
*
* per-board configuraton information, such as memory refresh and
* how to initialise IO timings.
* Registration depends on the driver being used, the ARMCLK only
* implementation does not currently need this but the older style
* driver requires this to be available.
*/
struct
s3c_cpufreq_board
{
unsigned
int
refresh
;
/* refresh period in ns */
unsigned
int
refresh
;
unsigned
int
auto_io
:
1
;
/* automatically init io timings. */
unsigned
int
need_io
:
1
;
/* set if needs io timing support. */
...
...
arch/arm/plat-s3c/include/plat/cpu.h
View file @
215ed323
...
...
@@ -65,6 +65,7 @@ extern struct sys_timer s3c24xx_timer;
/* system device classes */
extern
struct
sysdev_class
s3c2410_sysclass
;
extern
struct
sysdev_class
s3c2410a_sysclass
;
extern
struct
sysdev_class
s3c2412_sysclass
;
extern
struct
sysdev_class
s3c2440_sysclass
;
extern
struct
sysdev_class
s3c2442_sysclass
;
...
...
arch/arm/plat-s3c/include/plat/map-base.h
View file @
215ed323
...
...
@@ -32,9 +32,15 @@
#define S3C_VA_IRQ S3C_ADDR(0x00000000)
/* irq controller(s) */
#define S3C_VA_SYS S3C_ADDR(0x00100000)
/* system control */
#define S3C_VA_MEM S3C_ADDR(0x00200000)
/*
system
control */
#define S3C_VA_MEM S3C_ADDR(0x00200000)
/*
memory
control */
#define S3C_VA_TIMER S3C_ADDR(0x00300000)
/* timer block */
#define S3C_VA_WATCHDOG S3C_ADDR(0x00400000)
/* watchdog */
#define S3C_VA_UART S3C_ADDR(0x01000000)
/* UART */
/* This is used for the CPU specific mappings that may be needed, so that
* they do not need to directly used S3C_ADDR() and thus make it easier to
* modify the space for mapping.
*/
#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))
#endif
/* __ASM_PLAT_MAP_H */
arch/arm/plat-s3c24xx/Kconfig
View file @
215ed323
...
...
@@ -34,6 +34,40 @@ config CPU_S3C244X
help
Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems.
config S3C2440_CPUFREQ
bool "S3C2440/S3C2442 CPU Frequency scaling support"
depends on CPU_FREQ_S3C24XX && (CPU_S3C2440 || CPU_S3C2442)
select S3C2410_CPUFREQ_UTILS
default y
help
CPU Frequency scaling support for S3C2440 and S3C2442 SoC CPUs.
config S3C2440_XTAL_12000000
bool
help
Indicate that the build needs to support 12MHz system
crystal.
config S3C2440_XTAL_16934400
bool
help
Indicate that the build needs to support 16.9344MHz system
crystal.
config S3C2440_PLL_12000000
bool
depends on S3C2440_CPUFREQ && S3C2440_XTAL_12000000
default y if CPU_FREQ_S3C24XX_PLL
help
PLL tables for S3C2440 or S3C2442 CPUs with 12MHz crystals.
config S3C2440_PLL_16934400
bool
depends on S3C2440_CPUFREQ && S3C2440_XTAL_16934400
default y if CPU_FREQ_S3C24XX_PLL
help
PLL tables for S3C2440 or S3C2442 CPUs with 16.934MHz crystals.
config S3C24XX_PWM
bool "PWM device support"
select HAVE_PWM
...
...
@@ -113,6 +147,31 @@ config S3C24XX_SPI_BUS1_GPD8_GPD9_GPD10
# common code for s3c24xx based machines, such as the SMDKs.
# cpu frequency items common between s3c2410 and s3c2440/s3c2442
config S3C2410_IOTIMING
bool
depends on CPU_FREQ_S3C24XX
help
Internal node to select io timing code that is common to the s3c2410
and s3c2440/s3c2442 cpu frequency support.
config S3C2410_CPUFREQ_UTILS
bool
depends on CPU_FREQ_S3C24XX
help
Internal node to select timing code that is common to the s3c2410
and s3c2440/s3c244 cpu frequency support.
# cpu frequency support common to s3c2412, s3c2413 and s3c2442
config S3C2412_IOTIMING
bool
depends on CPU_FREQ_S3C24XX && (CPU_S3C2412 || CPU_S3C2443)
help
Intel node to select io timing code that is common to the s3c2412
and the s3c2443.
config MACH_SMDK
bool
help
...
...
arch/arm/plat-s3c24xx/Makefile
View file @
215ed323
...
...
@@ -20,11 +20,18 @@ obj-y += gpiolib.o
obj-y
+=
clock.o
obj-$(CONFIG_S3C24XX_DCLK)
+=
clock-dclk.o
obj-$(CONFIG_CPU_FREQ_S3C24XX)
+=
cpu-freq.o
obj-$(CONFIG_CPU_FREQ_S3C24XX_DEBUGFS)
+=
cpu-freq-debugfs.o
# Architecture dependant builds
obj-$(CONFIG_CPU_S3C244X)
+=
s3c244x.o
obj-$(CONFIG_CPU_S3C244X)
+=
s3c244x-irq.o
obj-$(CONFIG_CPU_S3C244X)
+=
s3c244x-clock.o
obj-$(CONFIG_S3C2440_CPUFREQ)
+=
s3c2440-cpufreq.o
obj-$(CONFIG_S3C2440_PLL_12000000)
+=
s3c2440-pll-12000000.o
obj-$(CONFIG_S3C2440_PLL_16934400)
+=
s3c2440-pll-16934400.o
obj-$(CONFIG_PM_SIMTEC)
+=
pm-simtec.o
obj-$(CONFIG_PM)
+=
pm.o
obj-$(CONFIG_PM)
+=
irq-pm.o
...
...
@@ -33,6 +40,9 @@ obj-$(CONFIG_S3C24XX_PWM) += pwm.o
obj-$(CONFIG_S3C2410_CLOCK)
+=
s3c2410-clock.o
obj-$(CONFIG_S3C2410_DMA)
+=
dma.o
obj-$(CONFIG_S3C24XX_ADC)
+=
adc.o
obj-$(CONFIG_S3C2410_IOTIMING)
+=
s3c2410-iotiming.o
obj-$(CONFIG_S3C2412_IOTIMING)
+=
s3c2412-iotiming.o
obj-$(CONFIG_S3C2410_CPUFREQ_UTILS)
+=
s3c2410-cpufreq-utils.o
# device specific setup and/or initialisation
obj-$(CONFIG_ARCH_S3C2410)
+=
setup-i2c.o
...
...
arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
*
* Copyright (c) 2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX CPU Frequency scaling - debugfs status support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <plat/cpu-freq-core.h>
static
struct
dentry
*
dbgfs_root
;
static
struct
dentry
*
dbgfs_file_io
;
static
struct
dentry
*
dbgfs_file_info
;
static
struct
dentry
*
dbgfs_file_board
;
#define print_ns(x) ((x) / 10), ((x) % 10)
static
void
show_max
(
struct
seq_file
*
seq
,
struct
s3c_freq
*
f
)
{
seq_printf
(
seq
,
"MAX: F=%lu, H=%lu, P=%lu, A=%lu
\n
"
,
f
->
fclk
,
f
->
hclk
,
f
->
pclk
,
f
->
armclk
);
}
static
int
board_show
(
struct
seq_file
*
seq
,
void
*
p
)
{
struct
s3c_cpufreq_config
*
cfg
;
struct
s3c_cpufreq_board
*
brd
;
cfg
=
s3c_cpufreq_getconfig
();
if
(
!
cfg
)
{
seq_printf
(
seq
,
"no configuration registered
\n
"
);
return
0
;
}
brd
=
cfg
->
board
;
if
(
!
brd
)
{
seq_printf
(
seq
,
"no board definition set?
\n
"
);
return
0
;
}
seq_printf
(
seq
,
"SDRAM refresh %u ns
\n
"
,
brd
->
refresh
);
seq_printf
(
seq
,
"auto_io=%u
\n
"
,
brd
->
auto_io
);
seq_printf
(
seq
,
"need_io=%u
\n
"
,
brd
->
need_io
);
show_max
(
seq
,
&
brd
->
max
);
return
0
;
}
static
int
fops_board_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
single_open
(
file
,
board_show
,
NULL
);
}
static
const
struct
file_operations
fops_board
=
{
.
open
=
fops_board_open
,
.
read
=
seq_read
,
.
llseek
=
seq_lseek
,
.
release
=
single_release
,
.
owner
=
THIS_MODULE
,
};
static
int
info_show
(
struct
seq_file
*
seq
,
void
*
p
)
{
struct
s3c_cpufreq_config
*
cfg
;
cfg
=
s3c_cpufreq_getconfig
();
if
(
!
cfg
)
{
seq_printf
(
seq
,
"no configuration registered
\n
"
);
return
0
;
}
seq_printf
(
seq
,
" FCLK %ld Hz
\n
"
,
cfg
->
freq
.
fclk
);
seq_printf
(
seq
,
" HCLK %ld Hz (%lu.%lu ns)
\n
"
,
cfg
->
freq
.
hclk
,
print_ns
(
cfg
->
freq
.
hclk_tns
));
seq_printf
(
seq
,
" PCLK %ld Hz
\n
"
,
cfg
->
freq
.
hclk
);
seq_printf
(
seq
,
"ARMCLK %ld Hz
\n
"
,
cfg
->
freq
.
armclk
);
seq_printf
(
seq
,
"
\n
"
);
show_max
(
seq
,
&
cfg
->
max
);
seq_printf
(
seq
,
"Divisors: P=%d, H=%d, A=%d, dvs=%s
\n
"
,
cfg
->
divs
.
h_divisor
,
cfg
->
divs
.
p_divisor
,
cfg
->
divs
.
arm_divisor
,
cfg
->
divs
.
dvs
?
"on"
:
"off"
);
seq_printf
(
seq
,
"
\n
"
);
seq_printf
(
seq
,
"lock_pll=%u
\n
"
,
cfg
->
lock_pll
);
return
0
;
}
static
int
fops_info_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
single_open
(
file
,
info_show
,
NULL
);
}
static
const
struct
file_operations
fops_info
=
{
.
open
=
fops_info_open
,
.
read
=
seq_read
,
.
llseek
=
seq_lseek
,
.
release
=
single_release
,
.
owner
=
THIS_MODULE
,
};
static
int
io_show
(
struct
seq_file
*
seq
,
void
*
p
)
{
void
(
*
show_bank
)(
struct
seq_file
*
,
struct
s3c_cpufreq_config
*
,
union
s3c_iobank
*
);
struct
s3c_cpufreq_config
*
cfg
;
struct
s3c_iotimings
*
iot
;
union
s3c_iobank
*
iob
;
int
bank
;
cfg
=
s3c_cpufreq_getconfig
();
if
(
!
cfg
)
{
seq_printf
(
seq
,
"no configuration registered
\n
"
);
return
0
;
}
show_bank
=
cfg
->
info
->
debug_io_show
;
if
(
!
show_bank
)
{
seq_printf
(
seq
,
"no code to show bank timing
\n
"
);
return
0
;
}
iot
=
s3c_cpufreq_getiotimings
();
if
(
!
iot
)
{
seq_printf
(
seq
,
"no io timings registered
\n
"
);
return
0
;
}
seq_printf
(
seq
,
"hclk period is %lu.%lu ns
\n
"
,
print_ns
(
cfg
->
freq
.
hclk_tns
));
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
iob
=
&
iot
->
bank
[
bank
];
seq_printf
(
seq
,
"bank %d: "
,
bank
);
if
(
!
iob
->
io_2410
)
{
seq_printf
(
seq
,
"nothing set
\n
"
);
continue
;
}
show_bank
(
seq
,
cfg
,
iob
);
}
return
0
;
}
static
int
fops_io_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
single_open
(
file
,
io_show
,
NULL
);
}
static
const
struct
file_operations
fops_io
=
{
.
open
=
fops_io_open
,
.
read
=
seq_read
,
.
llseek
=
seq_lseek
,
.
release
=
single_release
,
.
owner
=
THIS_MODULE
,
};
static
int
__init
s3c_freq_debugfs_init
(
void
)
{
dbgfs_root
=
debugfs_create_dir
(
"s3c-cpufreq"
,
NULL
);
if
(
IS_ERR
(
dbgfs_root
))
{
printk
(
KERN_ERR
"%s: error creating debugfs root
\n
"
,
__func__
);
return
PTR_ERR
(
dbgfs_root
);
}
dbgfs_file_io
=
debugfs_create_file
(
"io-timing"
,
S_IRUGO
,
dbgfs_root
,
NULL
,
&
fops_io
);
dbgfs_file_info
=
debugfs_create_file
(
"info"
,
S_IRUGO
,
dbgfs_root
,
NULL
,
&
fops_info
);
dbgfs_file_board
=
debugfs_create_file
(
"board"
,
S_IRUGO
,
dbgfs_root
,
NULL
,
&
fops_board
);
return
0
;
}
late_initcall
(
s3c_freq_debugfs_init
);
arch/arm/plat-s3c24xx/cpu-freq.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/cpu-freq.c
*
* Copyright (c) 2006,2007,2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX CPU Frequency scaling
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/sysdev.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <plat/cpu.h>
#include <plat/clock.h>
#include <plat/cpu-freq-core.h>
#include <mach/regs-clock.h>
/* note, cpufreq support deals in kHz, no Hz */
static
struct
cpufreq_driver
s3c24xx_driver
;
static
struct
s3c_cpufreq_config
cpu_cur
;
static
struct
s3c_iotimings
s3c24xx_iotiming
;
static
struct
cpufreq_frequency_table
*
pll_reg
;
static
unsigned
int
last_target
=
~
0
;
static
unsigned
int
ftab_size
;
static
struct
cpufreq_frequency_table
*
ftab
;
static
struct
clk
*
_clk_mpll
;
static
struct
clk
*
_clk_xtal
;
static
struct
clk
*
clk_fclk
;
static
struct
clk
*
clk_hclk
;
static
struct
clk
*
clk_pclk
;
static
struct
clk
*
clk_arm
;
#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
struct
s3c_cpufreq_config
*
s3c_cpufreq_getconfig
(
void
)
{
return
&
cpu_cur
;
}
struct
s3c_iotimings
*
s3c_cpufreq_getiotimings
(
void
)
{
return
&
s3c24xx_iotiming
;
}
#endif
/* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */
static
void
s3c_cpufreq_getcur
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
long
fclk
,
pclk
,
hclk
,
armclk
;
cfg
->
freq
.
fclk
=
fclk
=
clk_get_rate
(
clk_fclk
);
cfg
->
freq
.
hclk
=
hclk
=
clk_get_rate
(
clk_hclk
);
cfg
->
freq
.
pclk
=
pclk
=
clk_get_rate
(
clk_pclk
);
cfg
->
freq
.
armclk
=
armclk
=
clk_get_rate
(
clk_arm
);
cfg
->
pll
.
index
=
__raw_readl
(
S3C2410_MPLLCON
);
cfg
->
pll
.
frequency
=
fclk
;
cfg
->
freq
.
hclk_tns
=
1000000000
/
(
cfg
->
freq
.
hclk
/
10
);
cfg
->
divs
.
h_divisor
=
fclk
/
hclk
;
cfg
->
divs
.
p_divisor
=
fclk
/
pclk
;
}
static
inline
void
s3c_cpufreq_calc
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
long
pll
=
cfg
->
pll
.
frequency
;
cfg
->
freq
.
fclk
=
pll
;
cfg
->
freq
.
hclk
=
pll
/
cfg
->
divs
.
h_divisor
;
cfg
->
freq
.
pclk
=
pll
/
cfg
->
divs
.
p_divisor
;
/* convert hclk into 10ths of nanoseconds for io calcs */
cfg
->
freq
.
hclk_tns
=
1000000000
/
(
cfg
->
freq
.
hclk
/
10
);
}
static
inline
int
closer
(
unsigned
int
target
,
unsigned
int
n
,
unsigned
int
c
)
{
int
diff_cur
=
abs
(
target
-
c
);
int
diff_new
=
abs
(
target
-
n
);
return
(
diff_new
<
diff_cur
);
}
static
void
s3c_cpufreq_show
(
const
char
*
pfx
,
struct
s3c_cpufreq_config
*
cfg
)
{
s3c_freq_dbg
(
"%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)
\n
"
,
pfx
,
cfg
->
pll
.
frequency
,
cfg
->
freq
.
fclk
,
cfg
->
freq
.
armclk
,
cfg
->
freq
.
hclk
,
cfg
->
divs
.
h_divisor
,
cfg
->
freq
.
pclk
,
cfg
->
divs
.
p_divisor
);
}
/* functions to wrapper the driver info calls to do the cpu specific work */
static
void
s3c_cpufreq_setio
(
struct
s3c_cpufreq_config
*
cfg
)
{
if
(
cfg
->
info
->
set_iotiming
)
(
cfg
->
info
->
set_iotiming
)(
cfg
,
&
s3c24xx_iotiming
);
}
static
int
s3c_cpufreq_calcio
(
struct
s3c_cpufreq_config
*
cfg
)
{
if
(
cfg
->
info
->
calc_iotiming
)
return
(
cfg
->
info
->
calc_iotiming
)(
cfg
,
&
s3c24xx_iotiming
);
return
0
;
}
static
void
s3c_cpufreq_setrefresh
(
struct
s3c_cpufreq_config
*
cfg
)
{
(
cfg
->
info
->
set_refresh
)(
cfg
);
}
static
void
s3c_cpufreq_setdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
(
cfg
->
info
->
set_divs
)(
cfg
);
}
static
int
s3c_cpufreq_calcdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
return
(
cfg
->
info
->
calc_divs
)(
cfg
);
}
static
void
s3c_cpufreq_setfvco
(
struct
s3c_cpufreq_config
*
cfg
)
{
(
cfg
->
info
->
set_fvco
)(
cfg
);
}
static
inline
void
s3c_cpufreq_resume_clocks
(
void
)
{
cpu_cur
.
info
->
resume_clocks
();
}
static
inline
void
s3c_cpufreq_updateclk
(
struct
clk
*
clk
,
unsigned
int
freq
)
{
clk_set_rate
(
clk
,
freq
);
}
static
int
s3c_cpufreq_settarget
(
struct
cpufreq_policy
*
policy
,
unsigned
int
target_freq
,
struct
cpufreq_frequency_table
*
pll
)
{
struct
s3c_cpufreq_freqs
freqs
;
struct
s3c_cpufreq_config
cpu_new
;
unsigned
long
flags
;
cpu_new
=
cpu_cur
;
/* copy new from current */
s3c_cpufreq_show
(
"cur"
,
&
cpu_cur
);
/* TODO - check for DMA currently outstanding */
cpu_new
.
pll
=
pll
?
*
pll
:
cpu_cur
.
pll
;
if
(
pll
)
freqs
.
pll_changing
=
1
;
/* update our frequencies */
cpu_new
.
freq
.
armclk
=
target_freq
;
cpu_new
.
freq
.
fclk
=
cpu_new
.
pll
.
frequency
;
if
(
s3c_cpufreq_calcdivs
(
&
cpu_new
)
<
0
)
{
printk
(
KERN_ERR
"no divisors for %d
\n
"
,
target_freq
);
goto
err_notpossible
;
}
s3c_freq_dbg
(
"%s: got divs
\n
"
,
__func__
);
s3c_cpufreq_calc
(
&
cpu_new
);
s3c_freq_dbg
(
"%s: calculated frequencies for new
\n
"
,
__func__
);
if
(
cpu_new
.
freq
.
hclk
!=
cpu_cur
.
freq
.
hclk
)
{
if
(
s3c_cpufreq_calcio
(
&
cpu_new
)
<
0
)
{
printk
(
KERN_ERR
"%s: no IO timings
\n
"
,
__func__
);
goto
err_notpossible
;
}
}
s3c_cpufreq_show
(
"new"
,
&
cpu_new
);
/* setup our cpufreq parameters */
freqs
.
old
=
cpu_cur
.
freq
;
freqs
.
new
=
cpu_new
.
freq
;
freqs
.
freqs
.
cpu
=
0
;
freqs
.
freqs
.
old
=
cpu_cur
.
freq
.
armclk
/
1000
;
freqs
.
freqs
.
new
=
cpu_new
.
freq
.
armclk
/
1000
;
/* update f/h/p clock settings before we issue the change
* notification, so that drivers do not need to do anything
* special if they want to recalculate on CPUFREQ_PRECHANGE. */
s3c_cpufreq_updateclk
(
_clk_mpll
,
cpu_new
.
pll
.
frequency
);
s3c_cpufreq_updateclk
(
clk_fclk
,
cpu_new
.
freq
.
fclk
);
s3c_cpufreq_updateclk
(
clk_hclk
,
cpu_new
.
freq
.
hclk
);
s3c_cpufreq_updateclk
(
clk_pclk
,
cpu_new
.
freq
.
pclk
);
/* start the frequency change */
if
(
policy
)
cpufreq_notify_transition
(
&
freqs
.
freqs
,
CPUFREQ_PRECHANGE
);
/* If hclk is staying the same, then we do not need to
* re-write the IO or the refresh timings whilst we are changing
* speed. */
local_irq_save
(
flags
);
/* is our memory clock slowing down? */
if
(
cpu_new
.
freq
.
hclk
<
cpu_cur
.
freq
.
hclk
)
{
s3c_cpufreq_setrefresh
(
&
cpu_new
);
s3c_cpufreq_setio
(
&
cpu_new
);
}
if
(
cpu_new
.
freq
.
fclk
==
cpu_cur
.
freq
.
fclk
)
{
/* not changing PLL, just set the divisors */
s3c_cpufreq_setdivs
(
&
cpu_new
);
}
else
{
if
(
cpu_new
.
freq
.
fclk
<
cpu_cur
.
freq
.
fclk
)
{
/* slow the cpu down, then set divisors */
s3c_cpufreq_setfvco
(
&
cpu_new
);
s3c_cpufreq_setdivs
(
&
cpu_new
);
}
else
{
/* set the divisors, then speed up */
s3c_cpufreq_setdivs
(
&
cpu_new
);
s3c_cpufreq_setfvco
(
&
cpu_new
);
}
}
/* did our memory clock speed up */
if
(
cpu_new
.
freq
.
hclk
>
cpu_cur
.
freq
.
hclk
)
{
s3c_cpufreq_setrefresh
(
&
cpu_new
);
s3c_cpufreq_setio
(
&
cpu_new
);
}
/* update our current settings */
cpu_cur
=
cpu_new
;
local_irq_restore
(
flags
);
/* notify everyone we've done this */
if
(
policy
)
cpufreq_notify_transition
(
&
freqs
.
freqs
,
CPUFREQ_POSTCHANGE
);
s3c_freq_dbg
(
"%s: finished
\n
"
,
__func__
);
return
0
;
err_notpossible:
printk
(
KERN_ERR
"no compatible settings for %d
\n
"
,
target_freq
);
return
-
EINVAL
;
}
/* s3c_cpufreq_target
*
* called by the cpufreq core to adjust the frequency that the CPU
* is currently running at.
*/
static
int
s3c_cpufreq_target
(
struct
cpufreq_policy
*
policy
,
unsigned
int
target_freq
,
unsigned
int
relation
)
{
struct
cpufreq_frequency_table
*
pll
;
unsigned
int
index
;
/* avoid repeated calls which cause a needless amout of duplicated
* logging output (and CPU time as the calculation process is
* done) */
if
(
target_freq
==
last_target
)
return
0
;
last_target
=
target_freq
;
s3c_freq_dbg
(
"%s: policy %p, target %u, relation %u
\n
"
,
__func__
,
policy
,
target_freq
,
relation
);
if
(
ftab
)
{
if
(
cpufreq_frequency_table_target
(
policy
,
ftab
,
target_freq
,
relation
,
&
index
))
{
s3c_freq_dbg
(
"%s: table failed
\n
"
,
__func__
);
return
-
EINVAL
;
}
s3c_freq_dbg
(
"%s: adjust %d to entry %d (%u)
\n
"
,
__func__
,
target_freq
,
index
,
ftab
[
index
].
frequency
);
target_freq
=
ftab
[
index
].
frequency
;
}
target_freq
*=
1000
;
/* convert target to Hz */
/* find the settings for our new frequency */
if
(
!
pll_reg
||
cpu_cur
.
lock_pll
)
{
/* either we've not got any PLL values, or we've locked
* to the current one. */
pll
=
NULL
;
}
else
{
struct
cpufreq_policy
tmp_policy
;
int
ret
;
/* we keep the cpu pll table in Hz, to ensure we get an
* accurate value for the PLL output. */
tmp_policy
.
min
=
policy
->
min
*
1000
;
tmp_policy
.
max
=
policy
->
max
*
1000
;
tmp_policy
.
cpu
=
policy
->
cpu
;
/* cpufreq_frequency_table_target uses a pointer to 'index'
* which is the number of the table entry, not the value of
* the table entry's index field. */
ret
=
cpufreq_frequency_table_target
(
&
tmp_policy
,
pll_reg
,
target_freq
,
relation
,
&
index
);
if
(
ret
<
0
)
{
printk
(
KERN_ERR
"%s: no PLL available
\n
"
,
__func__
);
goto
err_notpossible
;
}
pll
=
pll_reg
+
index
;
s3c_freq_dbg
(
"%s: target %u => %u
\n
"
,
__func__
,
target_freq
,
pll
->
frequency
);
target_freq
=
pll
->
frequency
;
}
return
s3c_cpufreq_settarget
(
policy
,
target_freq
,
pll
);
err_notpossible:
printk
(
KERN_ERR
"no compatible settings for %d
\n
"
,
target_freq
);
return
-
EINVAL
;
}
static
unsigned
int
s3c_cpufreq_get
(
unsigned
int
cpu
)
{
return
clk_get_rate
(
clk_arm
)
/
1000
;
}
struct
clk
*
s3c_cpufreq_clk_get
(
struct
device
*
dev
,
const
char
*
name
)
{
struct
clk
*
clk
;
clk
=
clk_get
(
dev
,
name
);
if
(
IS_ERR
(
clk
))
printk
(
KERN_ERR
"cpufreq: failed to get clock '%s'
\n
"
,
name
);
return
clk
;
}
static
int
s3c_cpufreq_init
(
struct
cpufreq_policy
*
policy
)
{
printk
(
KERN_INFO
"%s: initialising policy %p
\n
"
,
__func__
,
policy
);
if
(
policy
->
cpu
!=
0
)
return
-
EINVAL
;
policy
->
cur
=
s3c_cpufreq_get
(
0
);
policy
->
min
=
policy
->
cpuinfo
.
min_freq
=
0
;
policy
->
max
=
policy
->
cpuinfo
.
max_freq
=
cpu_cur
.
info
->
max
.
fclk
/
1000
;
policy
->
governor
=
CPUFREQ_DEFAULT_GOVERNOR
;
/* feed the latency information from the cpu driver */
policy
->
cpuinfo
.
transition_latency
=
cpu_cur
.
info
->
latency
;
if
(
ftab
)
cpufreq_frequency_table_cpuinfo
(
policy
,
ftab
);
return
0
;
}
static
__init
int
s3c_cpufreq_initclks
(
void
)
{
_clk_mpll
=
s3c_cpufreq_clk_get
(
NULL
,
"mpll"
);
_clk_xtal
=
s3c_cpufreq_clk_get
(
NULL
,
"xtal"
);
clk_fclk
=
s3c_cpufreq_clk_get
(
NULL
,
"fclk"
);
clk_hclk
=
s3c_cpufreq_clk_get
(
NULL
,
"hclk"
);
clk_pclk
=
s3c_cpufreq_clk_get
(
NULL
,
"pclk"
);
clk_arm
=
s3c_cpufreq_clk_get
(
NULL
,
"armclk"
);
if
(
IS_ERR
(
clk_fclk
)
||
IS_ERR
(
clk_hclk
)
||
IS_ERR
(
clk_pclk
)
||
IS_ERR
(
_clk_mpll
)
||
IS_ERR
(
clk_arm
)
||
IS_ERR
(
_clk_xtal
))
{
printk
(
KERN_ERR
"%s: could not get clock(s)
\n
"
,
__func__
);
return
-
ENOENT
;
}
printk
(
KERN_INFO
"%s: clocks f=%lu,h=%lu,p=%lu,a=%lu
\n
"
,
__func__
,
clk_get_rate
(
clk_fclk
)
/
1000
,
clk_get_rate
(
clk_hclk
)
/
1000
,
clk_get_rate
(
clk_pclk
)
/
1000
,
clk_get_rate
(
clk_arm
)
/
1000
);
return
0
;
}
static
int
s3c_cpufreq_verify
(
struct
cpufreq_policy
*
policy
)
{
if
(
policy
->
cpu
!=
0
)
return
-
EINVAL
;
return
0
;
}
#ifdef CONFIG_PM
static
struct
cpufreq_frequency_table
suspend_pll
;
static
unsigned
int
suspend_freq
;
static
int
s3c_cpufreq_suspend
(
struct
cpufreq_policy
*
policy
,
pm_message_t
pmsg
)
{
suspend_pll
.
frequency
=
clk_get_rate
(
_clk_mpll
);
suspend_pll
.
index
=
__raw_readl
(
S3C2410_MPLLCON
);
suspend_freq
=
s3c_cpufreq_get
(
0
)
*
1000
;
return
0
;
}
static
int
s3c_cpufreq_resume
(
struct
cpufreq_policy
*
policy
)
{
int
ret
;
s3c_freq_dbg
(
"%s: resuming with policy %p
\n
"
,
__func__
,
policy
);
last_target
=
~
0
;
/* invalidate last_target setting */
/* first, find out what speed we resumed at. */
s3c_cpufreq_resume_clocks
();
/* whilst we will be called later on, we try and re-set the
* cpu frequencies as soon as possible so that we do not end
* up resuming devices and then immediatley having to re-set
* a number of settings once these devices have restarted.
*
* as a note, it is expected devices are not used until they
* have been un-suspended and at that time they should have
* used the updated clock settings.
*/
ret
=
s3c_cpufreq_settarget
(
NULL
,
suspend_freq
,
&
suspend_pll
);
if
(
ret
)
{
printk
(
KERN_ERR
"%s: failed to reset pll/freq
\n
"
,
__func__
);
return
ret
;
}
return
0
;
}
#else
#define s3c_cpufreq_resume NULL
#define s3c_cpufreq_suspend NULL
#endif
static
struct
cpufreq_driver
s3c24xx_driver
=
{
.
flags
=
CPUFREQ_STICKY
,
.
verify
=
s3c_cpufreq_verify
,
.
target
=
s3c_cpufreq_target
,
.
get
=
s3c_cpufreq_get
,
.
init
=
s3c_cpufreq_init
,
.
suspend
=
s3c_cpufreq_suspend
,
.
resume
=
s3c_cpufreq_resume
,
.
name
=
"s3c24xx"
,
};
int
__init
s3c_cpufreq_register
(
struct
s3c_cpufreq_info
*
info
)
{
if
(
!
info
||
!
info
->
name
)
{
printk
(
KERN_ERR
"%s: failed to pass valid information
\n
"
,
__func__
);
return
-
EINVAL
;
}
printk
(
KERN_INFO
"S3C24XX CPU Frequency driver, %s cpu support
\n
"
,
info
->
name
);
/* check our driver info has valid data */
BUG_ON
(
info
->
set_refresh
==
NULL
);
BUG_ON
(
info
->
set_divs
==
NULL
);
BUG_ON
(
info
->
calc_divs
==
NULL
);
/* info->set_fvco is optional, depending on whether there
* is a need to set the clock code. */
cpu_cur
.
info
=
info
;
/* Note, driver registering should probably update locktime */
return
0
;
}
int
__init
s3c_cpufreq_setboard
(
struct
s3c_cpufreq_board
*
board
)
{
struct
s3c_cpufreq_board
*
ours
;
if
(
!
board
)
{
printk
(
KERN_INFO
"%s: no board data
\n
"
,
__func__
);
return
-
EINVAL
;
}
/* Copy the board information so that each board can make this
* initdata. */
ours
=
kzalloc
(
sizeof
(
struct
s3c_cpufreq_board
),
GFP_KERNEL
);
if
(
ours
==
NULL
)
{
printk
(
KERN_ERR
"%s: no memory
\n
"
,
__func__
);
return
-
ENOMEM
;
}
*
ours
=
*
board
;
cpu_cur
.
board
=
ours
;
return
0
;
}
int
__init
s3c_cpufreq_auto_io
(
void
)
{
int
ret
;
if
(
!
cpu_cur
.
info
->
get_iotiming
)
{
printk
(
KERN_ERR
"%s: get_iotiming undefined
\n
"
,
__func__
);
return
-
ENOENT
;
}
printk
(
KERN_INFO
"%s: working out IO settings
\n
"
,
__func__
);
ret
=
(
cpu_cur
.
info
->
get_iotiming
)(
&
cpu_cur
,
&
s3c24xx_iotiming
);
if
(
ret
)
printk
(
KERN_ERR
"%s: failed to get timings
\n
"
,
__func__
);
return
ret
;
}
/* if one or is zero, then return the other, otherwise return the min */
#define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
/**
* s3c_cpufreq_freq_min - find the minimum settings for the given freq.
* @dst: The destination structure
* @a: One argument.
* @b: The other argument.
*
* Create a minimum of each frequency entry in the 'struct s3c_freq',
* unless the entry is zero when it is ignored and the non-zero argument
* used.
*/
static
void
s3c_cpufreq_freq_min
(
struct
s3c_freq
*
dst
,
struct
s3c_freq
*
a
,
struct
s3c_freq
*
b
)
{
dst
->
fclk
=
do_min
(
a
->
fclk
,
b
->
fclk
);
dst
->
hclk
=
do_min
(
a
->
hclk
,
b
->
hclk
);
dst
->
pclk
=
do_min
(
a
->
pclk
,
b
->
pclk
);
dst
->
armclk
=
do_min
(
a
->
armclk
,
b
->
armclk
);
}
static
inline
u32
calc_locktime
(
u32
freq
,
u32
time_us
)
{
u32
result
;
result
=
freq
*
time_us
;
result
=
DIV_ROUND_UP
(
result
,
1000
*
1000
);
return
result
;
}
static
void
s3c_cpufreq_update_loctkime
(
void
)
{
unsigned
int
bits
=
cpu_cur
.
info
->
locktime_bits
;
u32
rate
=
(
u32
)
clk_get_rate
(
_clk_xtal
);
u32
val
;
if
(
bits
==
0
)
{
WARN_ON
(
1
);
return
;
}
val
=
calc_locktime
(
rate
,
cpu_cur
.
info
->
locktime_u
)
<<
bits
;
val
|=
calc_locktime
(
rate
,
cpu_cur
.
info
->
locktime_m
);
printk
(
KERN_INFO
"%s: new locktime is 0x%08x
\n
"
,
__func__
,
val
);
__raw_writel
(
val
,
S3C2410_LOCKTIME
);
}
static
int
s3c_cpufreq_build_freq
(
void
)
{
int
size
,
ret
;
if
(
!
cpu_cur
.
info
->
calc_freqtable
)
return
-
EINVAL
;
kfree
(
ftab
);
ftab
=
NULL
;
size
=
cpu_cur
.
info
->
calc_freqtable
(
&
cpu_cur
,
NULL
,
0
);
size
++
;
ftab
=
kmalloc
(
sizeof
(
struct
cpufreq_frequency_table
)
*
size
,
GFP_KERNEL
);
if
(
!
ftab
)
{
printk
(
KERN_ERR
"%s: no memory for tables
\n
"
,
__func__
);
return
-
ENOMEM
;
}
ftab_size
=
size
;
ret
=
cpu_cur
.
info
->
calc_freqtable
(
&
cpu_cur
,
ftab
,
size
);
s3c_cpufreq_addfreq
(
ftab
,
ret
,
size
,
CPUFREQ_TABLE_END
);
return
0
;
}
static
int
__init
s3c_cpufreq_initcall
(
void
)
{
int
ret
=
0
;
if
(
cpu_cur
.
info
&&
cpu_cur
.
board
)
{
ret
=
s3c_cpufreq_initclks
();
if
(
ret
)
goto
out
;
/* get current settings */
s3c_cpufreq_getcur
(
&
cpu_cur
);
s3c_cpufreq_show
(
"cur"
,
&
cpu_cur
);
if
(
cpu_cur
.
board
->
auto_io
)
{
ret
=
s3c_cpufreq_auto_io
();
if
(
ret
)
{
printk
(
KERN_ERR
"%s: failed to get io timing
\n
"
,
__func__
);
goto
out
;
}
}
if
(
cpu_cur
.
board
->
need_io
&&
!
cpu_cur
.
info
->
set_iotiming
)
{
printk
(
KERN_ERR
"%s: no IO support registered
\n
"
,
__func__
);
ret
=
-
EINVAL
;
goto
out
;
}
if
(
!
cpu_cur
.
info
->
need_pll
)
cpu_cur
.
lock_pll
=
1
;
s3c_cpufreq_update_loctkime
();
s3c_cpufreq_freq_min
(
&
cpu_cur
.
max
,
&
cpu_cur
.
board
->
max
,
&
cpu_cur
.
info
->
max
);
if
(
cpu_cur
.
info
->
calc_freqtable
)
s3c_cpufreq_build_freq
();
ret
=
cpufreq_register_driver
(
&
s3c24xx_driver
);
}
out:
return
ret
;
}
late_initcall
(
s3c_cpufreq_initcall
);
/**
* s3c_plltab_register - register CPU PLL table.
* @plls: The list of PLL entries.
* @plls_no: The size of the PLL entries @plls.
*
* Register the given set of PLLs with the system.
*/
int
__init
s3c_plltab_register
(
struct
cpufreq_frequency_table
*
plls
,
unsigned
int
plls_no
)
{
struct
cpufreq_frequency_table
*
vals
;
unsigned
int
size
;
size
=
sizeof
(
struct
cpufreq_frequency_table
)
*
(
plls_no
+
1
);
vals
=
kmalloc
(
size
,
GFP_KERNEL
);
if
(
vals
)
{
memcpy
(
vals
,
plls
,
size
);
pll_reg
=
vals
;
/* write a terminating entry, we don't store it in the
* table that is stored in the kernel */
vals
+=
plls_no
;
vals
->
frequency
=
CPUFREQ_TABLE_END
;
printk
(
KERN_INFO
"cpufreq: %d PLL entries
\n
"
,
plls_no
);
}
else
printk
(
KERN_ERR
"cpufreq: no memory for PLL tables
\n
"
);
return
vals
?
0
:
-
ENOMEM
;
}
arch/arm/plat-s3c24xx/cpu.c
View file @
215ed323
...
...
@@ -81,7 +81,7 @@ static struct cpu_table cpu_ids[] __initdata = {
.
map_io
=
s3c2410_map_io
,
.
init_clocks
=
s3c2410_init_clocks
,
.
init_uarts
=
s3c2410_init_uarts
,
.
init
=
s3c2410_init
,
.
init
=
s3c2410
a
_init
,
.
name
=
name_s3c2410a
},
{
...
...
arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
0 → 100644
View file @
215ed323
/* arch/arm/plat-s3c/include/plat/cpu-freq.h
*
* Copyright (c) 2006,2007,2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C CPU frequency scaling support - core support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <plat/cpu-freq.h>
struct
seq_file
;
#define MAX_BANKS (8)
#define S3C2412_MAX_IO (8)
/**
* struct s3c2410_iobank_timing - IO bank timings for S3C2410 style timings
* @bankcon: The cached version of settings in this structure.
* @tacp:
* @tacs: Time from address valid to nCS asserted.
* @tcos: Time from nCS asserted to nOE or nWE asserted.
* @tacc: Time that nOE or nWE is asserted.
* @tcoh: Time nCS is held after nOE or nWE are released.
* @tcah: Time address is held for after
* @nwait_en: Whether nWAIT is enabled for this bank.
*
* This structure represents the IO timings for a S3C2410 style IO bank
* used by the CPU frequency support if it needs to change the settings
* of the IO.
*/
struct
s3c2410_iobank_timing
{
unsigned
long
bankcon
;
unsigned
int
tacp
;
unsigned
int
tacs
;
unsigned
int
tcos
;
unsigned
int
tacc
;
unsigned
int
tcoh
;
/* nCS hold afrer nOE/nWE */
unsigned
int
tcah
;
/* Address hold after nCS */
unsigned
char
nwait_en
;
/* nWait enabled for bank. */
};
/**
* struct s3c2412_iobank_timing - io timings for PL092 (S3C2412) style IO
* @idcy: The idle cycle time between transactions.
* @wstrd: nCS release to end of read cycle.
* @wstwr: nCS release to end of write cycle.
* @wstoen: nCS assertion to nOE assertion time.
* @wstwen: nCS assertion to nWE assertion time.
* @wstbrd: Burst ready delay.
* @smbidcyr: Register cache for smbidcyr value.
* @smbwstrd: Register cache for smbwstrd value.
* @smbwstwr: Register cache for smbwstwr value.
* @smbwstoen: Register cache for smbwstoen value.
* @smbwstwen: Register cache for smbwstwen value.
* @smbwstbrd: Register cache for smbwstbrd value.
*
* Timing information for a IO bank on an S3C2412 or similar system which
* uses a PL093 block.
*/
struct
s3c2412_iobank_timing
{
unsigned
int
idcy
;
unsigned
int
wstrd
;
unsigned
int
wstwr
;
unsigned
int
wstoen
;
unsigned
int
wstwen
;
unsigned
int
wstbrd
;
/* register cache */
unsigned
char
smbidcyr
;
unsigned
char
smbwstrd
;
unsigned
char
smbwstwr
;
unsigned
char
smbwstoen
;
unsigned
char
smbwstwen
;
unsigned
char
smbwstbrd
;
};
union
s3c_iobank
{
struct
s3c2410_iobank_timing
*
io_2410
;
struct
s3c2412_iobank_timing
*
io_2412
;
};
/**
* struct s3c_iotimings - Chip IO timings holder
* @bank: The timings for each IO bank.
*/
struct
s3c_iotimings
{
union
s3c_iobank
bank
[
MAX_BANKS
];
};
/**
* struct s3c_plltab - PLL table information.
* @vals: List of PLL values.
* @size: Size of the PLL table @vals.
*/
struct
s3c_plltab
{
struct
s3c_pllval
*
vals
;
int
size
;
};
/**
* struct s3c_cpufreq_config - current cpu frequency configuration
* @freq: The current settings for the core clocks.
* @max: Maxium settings, derived from core, board and user settings.
* @pll: The PLL table entry for the current PLL settings.
* @divs: The divisor settings for the core clocks.
* @info: The current core driver information.
* @board: The information for the board we are running on.
* @lock_pll: Set if the PLL settings cannot be changed.
*
* This is for the core drivers that need to know information about
* the current settings and values. It should not be needed by any
* device drivers.
*/
struct
s3c_cpufreq_config
{
struct
s3c_freq
freq
;
struct
s3c_freq
max
;
struct
cpufreq_frequency_table
pll
;
struct
s3c_clkdivs
divs
;
struct
s3c_cpufreq_info
*
info
;
/* for core, not drivers */
struct
s3c_cpufreq_board
*
board
;
unsigned
int
lock_pll
:
1
;
};
/**
* struct s3c_cpufreq_info - Information for the CPU frequency driver.
* @name: The name of this implementation.
* @max: The maximum frequencies for the system.
* @latency: Transition latency to give to cpufreq.
* @locktime_m: The lock-time in uS for the MPLL.
* @locktime_u: The lock-time in uS for the UPLL.
* @locttime_bits: The number of bits each LOCKTIME field.
* @need_pll: Set if this driver needs to change the PLL values to acheive
* any frequency changes. This is really only need by devices like the
* S3C2410 where there is no or limited divider between the PLL and the
* ARMCLK.
* @resume_clocks: Update the clocks on resume.
* @get_iotiming: Get the current IO timing data, mainly for use at start.
* @set_iotiming: Update the IO timings from the cached copies calculated
* from the @calc_iotiming entry when changing the frequency.
* @calc_iotiming: Calculate and update the cached copies of the IO timings
* from the newly calculated frequencies.
* @calc_freqtable: Calculate (fill in) the given frequency table from the
* current frequency configuration. If the table passed in is NULL,
* then the return is the number of elements to be filled for allocation
* of the table.
* @set_refresh: Set the memory refresh configuration.
* @set_fvco: Set the PLL frequencies.
* @set_divs: Update the clock divisors.
* @calc_divs: Calculate the clock divisors.
*/
struct
s3c_cpufreq_info
{
const
char
*
name
;
struct
s3c_freq
max
;
unsigned
int
latency
;
unsigned
int
locktime_m
;
unsigned
int
locktime_u
;
unsigned
char
locktime_bits
;
unsigned
int
need_pll
:
1
;
/* driver routines */
void
(
*
resume_clocks
)(
void
);
int
(
*
get_iotiming
)(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
void
(
*
set_iotiming
)(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
int
(
*
calc_iotiming
)(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
int
(
*
calc_freqtable
)(
struct
s3c_cpufreq_config
*
cfg
,
struct
cpufreq_frequency_table
*
t
,
size_t
table_size
);
void
(
*
debug_io_show
)(
struct
seq_file
*
seq
,
struct
s3c_cpufreq_config
*
cfg
,
union
s3c_iobank
*
iob
);
void
(
*
set_refresh
)(
struct
s3c_cpufreq_config
*
cfg
);
void
(
*
set_fvco
)(
struct
s3c_cpufreq_config
*
cfg
);
void
(
*
set_divs
)(
struct
s3c_cpufreq_config
*
cfg
);
int
(
*
calc_divs
)(
struct
s3c_cpufreq_config
*
cfg
);
};
extern
int
s3c_cpufreq_register
(
struct
s3c_cpufreq_info
*
info
);
extern
int
s3c_plltab_register
(
struct
cpufreq_frequency_table
*
plls
,
unsigned
int
plls_no
);
/* exports and utilities for debugfs */
extern
struct
s3c_cpufreq_config
*
s3c_cpufreq_getconfig
(
void
);
extern
struct
s3c_iotimings
*
s3c_cpufreq_getiotimings
(
void
);
extern
void
s3c2410_iotiming_debugfs
(
struct
seq_file
*
seq
,
struct
s3c_cpufreq_config
*
cfg
,
union
s3c_iobank
*
iob
);
extern
void
s3c2412_iotiming_debugfs
(
struct
seq_file
*
seq
,
struct
s3c_cpufreq_config
*
cfg
,
union
s3c_iobank
*
iob
);
#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
#define s3c_cpufreq_debugfs_call(x) x
#else
#define s3c_cpufreq_debugfs_call(x) NULL
#endif
/* Useful utility functions. */
extern
struct
clk
*
s3c_cpufreq_clk_get
(
struct
device
*
,
const
char
*
);
/* S3C2410 and compatible exported functions */
extern
void
s3c2410_cpufreq_setrefresh
(
struct
s3c_cpufreq_config
*
cfg
);
extern
int
s3c2410_iotiming_calc
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
);
extern
int
s3c2410_iotiming_get
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
extern
void
s3c2410_iotiming_set
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
);
extern
void
s3c2410_set_fvco
(
struct
s3c_cpufreq_config
*
cfg
);
/* S3C2412 compatible routines */
extern
int
s3c2412_iotiming_get
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
extern
int
s3c2412_iotiming_get
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
);
extern
int
s3c2412_iotiming_calc
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
);
extern
void
s3c2412_iotiming_set
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
);
#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG
#define s3c_freq_dbg(x...) printk(KERN_INFO x)
#else
#define s3c_freq_dbg(x...) do { if (0) printk(x); } while (0)
#endif
/* CONFIG_CPU_FREQ_S3C24XX_DEBUG */
#ifdef CONFIG_CPU_FREQ_S3C24XX_IODEBUG
#define s3c_freq_iodbg(x...) printk(KERN_INFO x)
#else
#define s3c_freq_iodbg(x...) do { if (0) printk(x); } while (0)
#endif
/* CONFIG_CPU_FREQ_S3C24XX_IODEBUG */
static
inline
int
s3c_cpufreq_addfreq
(
struct
cpufreq_frequency_table
*
table
,
int
index
,
size_t
table_size
,
unsigned
int
freq
)
{
if
(
index
<
0
)
return
index
;
if
(
table
)
{
if
(
index
>=
table_size
)
return
-
ENOMEM
;
s3c_freq_dbg
(
"%s: { %d = %u kHz }
\n
"
,
__func__
,
index
,
freq
);
table
[
index
].
index
=
index
;
table
[
index
].
frequency
=
freq
;
}
return
index
+
1
;
}
arch/arm/plat-s3c24xx/include/plat/s3c2410.h
View file @
215ed323
...
...
@@ -14,6 +14,7 @@
#ifdef CONFIG_CPU_S3C2410
extern
int
s3c2410_init
(
void
);
extern
int
s3c2410a_init
(
void
);
extern
void
s3c2410_map_io
(
void
);
...
...
arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
*
* Copyright (c) 2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX CPU Frequency scaling - utils for S3C2410/S3C2440/S3C2442
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-mem.h>
#include <mach/regs-clock.h>
#include <plat/cpu-freq-core.h>
/**
* s3c2410_cpufreq_setrefresh - set SDRAM refresh value
* @cfg: The frequency configuration
*
* Set the SDRAM refresh value appropriately for the configured
* frequency.
*/
void
s3c2410_cpufreq_setrefresh
(
struct
s3c_cpufreq_config
*
cfg
)
{
struct
s3c_cpufreq_board
*
board
=
cfg
->
board
;
unsigned
long
refresh
;
unsigned
long
refval
;
/* Reduce both the refresh time (in ns) and the frequency (in MHz)
* down to ensure that we do not overflow 32 bit numbers.
*
* This should work for HCLK up to 133MHz and refresh period up
* to 30usec.
*/
refresh
=
(
cfg
->
freq
.
hclk
/
100
)
*
(
board
->
refresh
/
10
);
refresh
=
DIV_ROUND_UP
(
refresh
,
(
1000
*
1000
));
/* apply scale */
refresh
=
(
1
<<
11
)
+
1
-
refresh
;
s3c_freq_dbg
(
"%s: refresh value %lu
\n
"
,
__func__
,
refresh
);
refval
=
__raw_readl
(
S3C2410_REFRESH
);
refval
&=
~
((
1
<<
12
)
-
1
);
refval
|=
refresh
;
__raw_writel
(
refval
,
S3C2410_REFRESH
);
}
/**
* s3c2410_set_fvco - set the PLL value
* @cfg: The frequency configuration
*/
void
s3c2410_set_fvco
(
struct
s3c_cpufreq_config
*
cfg
)
{
__raw_writel
(
cfg
->
pll
.
index
,
S3C2410_MPLLCON
);
}
arch/arm/plat-s3c24xx/s3c2410-iotiming.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
*
* Copyright (c) 2006,2008,2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX CPU Frequency scaling - IO timing for S3C2410/S3C2440/S3C2442
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-mem.h>
#include <mach/regs-clock.h>
#include <plat/cpu-freq-core.h>
#define print_ns(x) ((x) / 10), ((x) % 10)
/**
* s3c2410_print_timing - print bank timing data for debug purposes
* @pfx: The prefix to put on the output
* @timings: The timing inforamtion to print.
*/
static
void
s3c2410_print_timing
(
const
char
*
pfx
,
struct
s3c_iotimings
*
timings
)
{
struct
s3c2410_iobank_timing
*
bt
;
int
bank
;
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bt
=
timings
->
bank
[
bank
].
io_2410
;
if
(
!
bt
)
continue
;
printk
(
KERN_DEBUG
"%s %d: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, "
"Tcoh=%d.%d, Tcah=%d.%d
\n
"
,
pfx
,
bank
,
print_ns
(
bt
->
tacs
),
print_ns
(
bt
->
tcos
),
print_ns
(
bt
->
tacc
),
print_ns
(
bt
->
tcoh
),
print_ns
(
bt
->
tcah
));
}
}
/**
* bank_reg - convert bank number to pointer to the control register.
* @bank: The IO bank number.
*/
static
inline
void
__iomem
*
bank_reg
(
unsigned
int
bank
)
{
return
S3C2410_BANKCON0
+
(
bank
<<
2
);
}
/**
* bank_is_io - test whether bank is used for IO
* @bankcon: The bank control register.
*
* This is a simplistic test to see if any BANKCON[x] is not an IO
* bank. It currently does not take into account whether BWSCON has
* an illegal width-setting in it, or if the pin connected to nCS[x]
* is actually being handled as a chip-select.
*/
static
inline
int
bank_is_io
(
unsigned
long
bankcon
)
{
return
!
(
bankcon
&
S3C2410_BANKCON_SDRAM
);
}
/**
* to_div - convert cycle time to divisor
* @cyc: The cycle time, in 10ths of nanoseconds.
* @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
*
* Convert the given cycle time into the divisor to use to obtain it from
* HCLK.
*/
static
inline
unsigned
int
to_div
(
unsigned
int
cyc
,
unsigned
int
hclk_tns
)
{
if
(
cyc
==
0
)
return
0
;
return
DIV_ROUND_UP
(
cyc
,
hclk_tns
);
}
/**
* calc_0124 - calculate divisor control for divisors that do /0, /1. /2 and /4
* @cyc: The cycle time, in 10ths of nanoseconds.
* @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
* @v: Pointer to register to alter.
* @shift: The shift to get to the control bits.
*
* Calculate the divisor, and turn it into the correct control bits to
* set in the result, @v.
*/
static
unsigned
int
calc_0124
(
unsigned
int
cyc
,
unsigned
long
hclk_tns
,
unsigned
long
*
v
,
int
shift
)
{
unsigned
int
div
=
to_div
(
cyc
,
hclk_tns
);
unsigned
long
val
;
s3c_freq_iodbg
(
"%s: cyc=%d, hclk=%lu, shift=%d => div %d
\n
"
,
__func__
,
cyc
,
hclk_tns
,
shift
,
div
);
switch
(
div
)
{
case
0
:
val
=
0
;
break
;
case
1
:
val
=
1
;
break
;
case
2
:
val
=
2
;
break
;
case
3
:
case
4
:
val
=
3
;
break
;
default:
return
-
1
;
}
*
v
|=
val
<<
shift
;
return
0
;
}
int
calc_tacp
(
unsigned
int
cyc
,
unsigned
long
hclk
,
unsigned
long
*
v
)
{
/* Currently no support for Tacp calculations. */
return
0
;
}
/**
* calc_tacc - calculate divisor control for tacc.
* @cyc: The cycle time, in 10ths of nanoseconds.
* @nwait_en: IS nWAIT enabled for this bank.
* @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
* @v: Pointer to register to alter.
*
* Calculate the divisor control for tACC, taking into account whether
* the bank has nWAIT enabled. The result is used to modify the value
* pointed to by @v.
*/
static
int
calc_tacc
(
unsigned
int
cyc
,
int
nwait_en
,
unsigned
long
hclk_tns
,
unsigned
long
*
v
)
{
unsigned
int
div
=
to_div
(
cyc
,
hclk_tns
);
unsigned
long
val
;
s3c_freq_iodbg
(
"%s: cyc=%u, nwait=%d, hclk=%lu => div=%u
\n
"
,
__func__
,
cyc
,
nwait_en
,
hclk_tns
,
div
);
/* if nWait enabled on an bank, Tacc must be at-least 4 cycles. */
if
(
nwait_en
&&
div
<
4
)
div
=
4
;
switch
(
div
)
{
case
0
:
val
=
0
;
break
;
case
1
:
case
2
:
case
3
:
case
4
:
val
=
div
-
1
;
break
;
case
5
:
case
6
:
val
=
4
;
break
;
case
7
:
case
8
:
val
=
5
;
break
;
case
9
:
case
10
:
val
=
6
;
break
;
case
11
:
case
12
:
case
13
:
case
14
:
val
=
7
;
break
;
default:
return
-
1
;
}
*
v
|=
val
<<
8
;
return
0
;
}
/**
* s3c2410_calc_bank - calculate bank timing infromation
* @cfg: The configuration we need to calculate for.
* @bt: The bank timing information.
*
* Given the cycle timine for a bank @bt, calculate the new BANKCON
* setting for the @cfg timing. This updates the timing information
* ready for the cpu frequency change.
*/
static
int
s3c2410_calc_bank
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c2410_iobank_timing
*
bt
)
{
unsigned
long
hclk
=
cfg
->
freq
.
hclk_tns
;
unsigned
long
res
;
int
ret
;
res
=
bt
->
bankcon
;
res
&=
(
S3C2410_BANKCON_SDRAM
|
S3C2410_BANKCON_PMC16
);
/* tacp: 2,3,4,5 */
/* tcah: 0,1,2,4 */
/* tcoh: 0,1,2,4 */
/* tacc: 1,2,3,4,6,7,10,14 (>4 for nwait) */
/* tcos: 0,1,2,4 */
/* tacs: 0,1,2,4 */
ret
=
calc_0124
(
bt
->
tacs
,
hclk
,
&
res
,
S3C2410_BANKCON_Tacs_SHIFT
);
ret
|=
calc_0124
(
bt
->
tcos
,
hclk
,
&
res
,
S3C2410_BANKCON_Tcos_SHIFT
);
ret
|=
calc_0124
(
bt
->
tcah
,
hclk
,
&
res
,
S3C2410_BANKCON_Tcah_SHIFT
);
ret
|=
calc_0124
(
bt
->
tcoh
,
hclk
,
&
res
,
S3C2410_BANKCON_Tcoh_SHIFT
);
if
(
ret
)
return
-
EINVAL
;
ret
|=
calc_tacp
(
bt
->
tacp
,
hclk
,
&
res
);
ret
|=
calc_tacc
(
bt
->
tacc
,
bt
->
nwait_en
,
hclk
,
&
res
);
if
(
ret
)
return
-
EINVAL
;
bt
->
bankcon
=
res
;
return
0
;
}
static
unsigned
int
tacc_tab
[]
=
{
[
0
]
=
1
,
[
1
]
=
2
,
[
2
]
=
3
,
[
3
]
=
4
,
[
4
]
=
6
,
[
5
]
=
9
,
[
6
]
=
10
,
[
7
]
=
14
,
};
/**
* get_tacc - turn tACC value into cycle time
* @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
* @val: The bank timing register value, shifed down.
*/
static
unsigned
int
get_tacc
(
unsigned
long
hclk_tns
,
unsigned
long
val
)
{
val
&=
7
;
return
hclk_tns
*
tacc_tab
[
val
];
}
/**
* get_0124 - turn 0/1/2/4 divider into cycle time
* @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
* @val: The bank timing register value, shifed down.
*/
static
unsigned
int
get_0124
(
unsigned
long
hclk_tns
,
unsigned
long
val
)
{
val
&=
3
;
return
hclk_tns
*
((
val
==
3
)
?
4
:
val
);
}
/**
* s3c2410_iotiming_getbank - turn BANKCON into cycle time information
* @cfg: The frequency configuration
* @bt: The bank timing to fill in (uses cached BANKCON)
*
* Given the BANKCON setting in @bt and the current frequency settings
* in @cfg, update the cycle timing information.
*/
void
s3c2410_iotiming_getbank
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c2410_iobank_timing
*
bt
)
{
unsigned
long
bankcon
=
bt
->
bankcon
;
unsigned
long
hclk
=
cfg
->
freq
.
hclk_tns
;
bt
->
tcah
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcah_SHIFT
);
bt
->
tcoh
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcoh_SHIFT
);
bt
->
tcos
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcos_SHIFT
);
bt
->
tacs
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tacs_SHIFT
);
bt
->
tacc
=
get_tacc
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tacc_SHIFT
);
}
/**
* s3c2410_iotiming_debugfs - debugfs show io bank timing information
* @seq: The seq_file to write output to using seq_printf().
* @cfg: The current configuration.
* @iob: The IO bank information to decode.
*/
void
s3c2410_iotiming_debugfs
(
struct
seq_file
*
seq
,
struct
s3c_cpufreq_config
*
cfg
,
union
s3c_iobank
*
iob
)
{
struct
s3c2410_iobank_timing
*
bt
=
iob
->
io_2410
;
unsigned
long
bankcon
=
bt
->
bankcon
;
unsigned
long
hclk
=
cfg
->
freq
.
hclk_tns
;
unsigned
int
tacs
;
unsigned
int
tcos
;
unsigned
int
tacc
;
unsigned
int
tcoh
;
unsigned
int
tcah
;
seq_printf
(
seq
,
"BANKCON=0x%08lx
\n
"
,
bankcon
);
tcah
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcah_SHIFT
);
tcoh
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcoh_SHIFT
);
tcos
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tcos_SHIFT
);
tacs
=
get_0124
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tacs_SHIFT
);
tacc
=
get_tacc
(
hclk
,
bankcon
>>
S3C2410_BANKCON_Tacc_SHIFT
);
seq_printf
(
seq
,
"
\t
Read: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d
\n
"
,
print_ns
(
bt
->
tacs
),
print_ns
(
bt
->
tcos
),
print_ns
(
bt
->
tacc
),
print_ns
(
bt
->
tcoh
),
print_ns
(
bt
->
tcah
));
seq_printf
(
seq
,
"
\t
Set: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d
\n
"
,
print_ns
(
tacs
),
print_ns
(
tcos
),
print_ns
(
tacc
),
print_ns
(
tcoh
),
print_ns
(
tcah
));
}
/**
* s3c2410_iotiming_calc - Calculate bank timing for frequency change.
* @cfg: The frequency configuration
* @iot: The IO timing information to fill out.
*
* Calculate the new values for the banks in @iot based on the new
* frequency information in @cfg. This is then used by s3c2410_iotiming_set()
* to update the timing when necessary.
*/
int
s3c2410_iotiming_calc
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
)
{
struct
s3c2410_iobank_timing
*
bt
;
unsigned
long
bankcon
;
int
bank
;
int
ret
;
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bankcon
=
__raw_readl
(
bank_reg
(
bank
));
bt
=
iot
->
bank
[
bank
].
io_2410
;
if
(
!
bt
)
continue
;
bt
->
bankcon
=
bankcon
;
ret
=
s3c2410_calc_bank
(
cfg
,
bt
);
if
(
ret
)
{
printk
(
KERN_ERR
"%s: cannot calculate bank %d io
\n
"
,
__func__
,
bank
);
goto
err
;
}
s3c_freq_iodbg
(
"%s: bank %d: con=%08lx
\n
"
,
__func__
,
bank
,
bt
->
bankcon
);
}
return
0
;
err:
return
ret
;
}
/**
* s3c2410_iotiming_set - set the IO timings from the given setup.
* @cfg: The frequency configuration
* @iot: The IO timing information to use.
*
* Set all the currently used IO bank timing information generated
* by s3c2410_iotiming_calc() once the core has validated that all
* the new values are within permitted bounds.
*/
void
s3c2410_iotiming_set
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
)
{
struct
s3c2410_iobank_timing
*
bt
;
int
bank
;
/* set the io timings from the specifier */
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bt
=
iot
->
bank
[
bank
].
io_2410
;
if
(
!
bt
)
continue
;
__raw_writel
(
bt
->
bankcon
,
bank_reg
(
bank
));
}
}
/**
* s3c2410_iotiming_get - Get the timing information from current registers.
* @cfg: The frequency configuration
* @timings: The IO timing information to fill out.
*
* Calculate the @timings timing information from the current frequency
* information in @cfg, and the new frequency configur
* through all the IO banks, reading the state and then updating @iot
* as necessary.
*
* This is used at the moment on initialisation to get the current
* configuration so that boards do not have to carry their own setup
* if the timings are correct on initialisation.
*/
int
s3c2410_iotiming_get
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
)
{
struct
s3c2410_iobank_timing
*
bt
;
unsigned
long
bankcon
;
unsigned
long
bwscon
;
int
bank
;
bwscon
=
__raw_readl
(
S3C2410_BWSCON
);
/* look through all banks to see what is currently set. */
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bankcon
=
__raw_readl
(
bank_reg
(
bank
));
if
(
!
bank_is_io
(
bankcon
))
continue
;
s3c_freq_iodbg
(
"%s: bank %d: con %08lx
\n
"
,
__func__
,
bank
,
bankcon
);
bt
=
kzalloc
(
sizeof
(
struct
s3c2410_iobank_timing
),
GFP_KERNEL
);
if
(
!
bt
)
{
printk
(
KERN_ERR
"%s: no memory for bank
\n
"
,
__func__
);
return
-
ENOMEM
;
}
/* find out in nWait is enabled for bank. */
if
(
bank
!=
0
)
{
unsigned
long
tmp
=
S3C2410_BWSCON_GET
(
bwscon
,
bank
);
if
(
tmp
&
S3C2410_BWSCON_WS
)
bt
->
nwait_en
=
1
;
}
timings
->
bank
[
bank
].
io_2410
=
bt
;
bt
->
bankcon
=
bankcon
;
s3c2410_iotiming_getbank
(
cfg
,
bt
);
}
s3c2410_print_timing
(
"get"
,
timings
);
return
0
;
}
arch/arm/plat-s3c24xx/s3c2412-iotiming.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
*
* Copyright (c) 2006,2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2412/S3C2443 (PL093 based) IO timing support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/sysdev.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/amba/pl093.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/regs-s3c2412-mem.h>
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
#include <plat/clock.h>
#define print_ns(x) ((x) / 10), ((x) % 10)
/**
* s3c2412_print_timing - print timing infromation via printk.
* @pfx: The prefix to print each line with.
* @iot: The IO timing information
*/
static
void
s3c2412_print_timing
(
const
char
*
pfx
,
struct
s3c_iotimings
*
iot
)
{
struct
s3c2412_iobank_timing
*
bt
;
unsigned
int
bank
;
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bt
=
iot
->
bank
[
bank
].
io_2412
;
if
(
!
bt
)
continue
;
printk
(
KERN_DEBUG
"%s: %d: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
"wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d
\n
"
,
pfx
,
bank
,
print_ns
(
bt
->
idcy
),
print_ns
(
bt
->
wstrd
),
print_ns
(
bt
->
wstwr
),
print_ns
(
bt
->
wstoen
),
print_ns
(
bt
->
wstwen
),
print_ns
(
bt
->
wstbrd
));
}
}
/**
* to_div - turn a cycle length into a divisor setting.
* @cyc_tns: The cycle time in 10ths of nanoseconds.
* @clk_tns: The clock period in 10ths of nanoseconds.
*/
static
inline
unsigned
int
to_div
(
unsigned
int
cyc_tns
,
unsigned
int
clk_tns
)
{
return
cyc_tns
?
DIV_ROUND_UP
(
cyc_tns
,
clk_tns
)
:
0
;
}
/**
* calc_timing - calculate timing divisor value and check in range.
* @hwtm: The hardware timing in 10ths of nanoseconds.
* @clk_tns: The clock period in 10ths of nanoseconds.
* @err: Pointer to err variable to update in event of failure.
*/
static
unsigned
int
calc_timing
(
unsigned
int
hwtm
,
unsigned
int
clk_tns
,
unsigned
int
*
err
)
{
unsigned
int
ret
=
to_div
(
hwtm
,
clk_tns
);
if
(
ret
>
0xf
)
*
err
=
-
EINVAL
;
return
ret
;
}
/**
* s3c2412_calc_bank - calculate the bank divisor settings.
* @cfg: The current frequency configuration.
* @bt: The bank timing.
*/
static
int
s3c2412_calc_bank
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c2412_iobank_timing
*
bt
)
{
unsigned
int
hclk
=
cfg
->
freq
.
hclk_tns
;
int
err
=
0
;
bt
->
smbidcyr
=
calc_timing
(
bt
->
idcy
,
hclk
,
&
err
);
bt
->
smbwstrd
=
calc_timing
(
bt
->
wstrd
,
hclk
,
&
err
);
bt
->
smbwstwr
=
calc_timing
(
bt
->
wstwr
,
hclk
,
&
err
);
bt
->
smbwstoen
=
calc_timing
(
bt
->
wstoen
,
hclk
,
&
err
);
bt
->
smbwstwen
=
calc_timing
(
bt
->
wstwen
,
hclk
,
&
err
);
bt
->
smbwstbrd
=
calc_timing
(
bt
->
wstbrd
,
hclk
,
&
err
);
return
err
;
}
/**
* s3c2412_iotiming_debugfs - debugfs show io bank timing information
* @seq: The seq_file to write output to using seq_printf().
* @cfg: The current configuration.
* @iob: The IO bank information to decode.
*/
void
s3c2412_iotiming_debugfs
(
struct
seq_file
*
seq
,
struct
s3c_cpufreq_config
*
cfg
,
union
s3c_iobank
*
iob
)
{
struct
s3c2412_iobank_timing
*
bt
=
iob
->
io_2412
;
seq_printf
(
seq
,
"
\t
Read: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
"wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d
\n
"
,
print_ns
(
bt
->
idcy
),
print_ns
(
bt
->
wstrd
),
print_ns
(
bt
->
wstwr
),
print_ns
(
bt
->
wstoen
),
print_ns
(
bt
->
wstwen
),
print_ns
(
bt
->
wstbrd
));
}
/**
* s3c2412_iotiming_calc - calculate all the bank divisor settings.
* @cfg: The current frequency configuration.
* @iot: The bank timing information.
*
* Calculate the timing information for all the banks that are
* configured as IO, using s3c2412_calc_bank().
*/
int
s3c2412_iotiming_calc
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
)
{
struct
s3c2412_iobank_timing
*
bt
;
int
bank
;
int
ret
;
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bt
=
iot
->
bank
[
bank
].
io_2412
;
if
(
!
bt
)
continue
;
ret
=
s3c2412_calc_bank
(
cfg
,
bt
);
if
(
ret
)
{
printk
(
KERN_ERR
"%s: cannot calculate bank %d io
\n
"
,
__func__
,
bank
);
goto
err
;
}
}
return
0
;
err:
return
ret
;
}
/**
* s3c2412_iotiming_set - set the timing information
* @cfg: The current frequency configuration.
* @iot: The bank timing information.
*
* Set the IO bank information from the details calculated earlier from
* calling s3c2412_iotiming_calc().
*/
void
s3c2412_iotiming_set
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
iot
)
{
struct
s3c2412_iobank_timing
*
bt
;
void
__iomem
*
regs
;
int
bank
;
/* set the io timings from the specifier */
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
bt
=
iot
->
bank
[
bank
].
io_2412
;
if
(
!
bt
)
continue
;
regs
=
S3C2412_SSMC_BANK
(
bank
);
__raw_writel
(
bt
->
smbidcyr
,
regs
+
SMBIDCYR
);
__raw_writel
(
bt
->
smbwstrd
,
regs
+
SMBWSTRDR
);
__raw_writel
(
bt
->
smbwstwr
,
regs
+
SMBWSTWRR
);
__raw_writel
(
bt
->
smbwstoen
,
regs
+
SMBWSTOENR
);
__raw_writel
(
bt
->
smbwstwen
,
regs
+
SMBWSTWENR
);
__raw_writel
(
bt
->
smbwstbrd
,
regs
+
SMBWSTBRDR
);
}
}
static
inline
unsigned
int
s3c2412_decode_timing
(
unsigned
int
clock
,
u32
reg
)
{
return
(
reg
&
0xf
)
*
clock
;
}
static
void
s3c2412_iotiming_getbank
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c2412_iobank_timing
*
bt
,
unsigned
int
bank
)
{
unsigned
long
clk
=
cfg
->
freq
.
hclk_tns
;
/* ssmc clock??? */
void
__iomem
*
regs
=
S3C2412_SSMC_BANK
(
bank
);
bt
->
idcy
=
s3c2412_decode_timing
(
clk
,
__raw_readl
(
regs
+
SMBIDCYR
));
bt
->
wstrd
=
s3c2412_decode_timing
(
clk
,
__raw_readl
(
regs
+
SMBWSTRDR
));
bt
->
wstoen
=
s3c2412_decode_timing
(
clk
,
__raw_readl
(
regs
+
SMBWSTOENR
));
bt
->
wstwen
=
s3c2412_decode_timing
(
clk
,
__raw_readl
(
regs
+
SMBWSTWENR
));
bt
->
wstbrd
=
s3c2412_decode_timing
(
clk
,
__raw_readl
(
regs
+
SMBWSTBRDR
));
}
/**
* bank_is_io - return true if bank is (possibly) IO.
* @bank: The bank number.
* @bankcfg: The value of S3C2412_EBI_BANKCFG.
*/
static
inline
bool
bank_is_io
(
unsigned
int
bank
,
u32
bankcfg
)
{
if
(
bank
<
2
)
return
true
;
return
!
(
bankcfg
&
(
1
<<
bank
));
}
int
s3c2412_iotiming_get
(
struct
s3c_cpufreq_config
*
cfg
,
struct
s3c_iotimings
*
timings
)
{
struct
s3c2412_iobank_timing
*
bt
;
u32
bankcfg
=
__raw_readl
(
S3C2412_EBI_BANKCFG
);
unsigned
int
bank
;
/* look through all banks to see what is currently set. */
for
(
bank
=
0
;
bank
<
MAX_BANKS
;
bank
++
)
{
if
(
!
bank_is_io
(
bank
,
bankcfg
))
continue
;
bt
=
kzalloc
(
sizeof
(
struct
s3c2412_iobank_timing
),
GFP_KERNEL
);
if
(
!
bt
)
{
printk
(
KERN_ERR
"%s: no memory for bank
\n
"
,
__func__
);
return
-
ENOMEM
;
}
timings
->
bank
[
bank
].
io_2412
=
bt
;
s3c2412_iotiming_getbank
(
cfg
,
bt
,
bank
);
}
s3c2412_print_timing
(
"get"
,
timings
);
return
0
;
}
/* this is in here as it is so small, it doesn't currently warrant a file
* to itself. We expect that any s3c24xx needing this is going to also
* need the iotiming support.
*/
void
s3c2412_cpufreq_setrefresh
(
struct
s3c_cpufreq_config
*
cfg
)
{
struct
s3c_cpufreq_board
*
board
=
cfg
->
board
;
u32
refresh
;
WARN_ON
(
board
==
NULL
);
/* Reduce both the refresh time (in ns) and the frequency (in MHz)
* down to ensure that we do not overflow 32 bit numbers.
*
* This should work for HCLK up to 133MHz and refresh period up
* to 30usec.
*/
refresh
=
(
cfg
->
freq
.
hclk
/
100
)
*
(
board
->
refresh
/
10
);
refresh
=
DIV_ROUND_UP
(
refresh
,
(
1000
*
1000
));
/* apply scale */
refresh
&=
((
1
<<
16
)
-
1
);
s3c_freq_dbg
(
"%s: refresh value %u
\n
"
,
__func__
,
(
unsigned
int
)
refresh
);
__raw_writel
(
refresh
,
S3C2412_REFRESH
);
}
arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
0 → 100644
View file @
215ed323
/* linux/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
*
* Copyright (c) 2006,2008,2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
* Vincent Sanders <vince@simtec.co.uk>
*
* S3C2440/S3C2442 CPU Frequency scaling
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/sysdev.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/regs-clock.h>
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
#include <plat/clock.h>
static
struct
clk
*
xtal
;
static
struct
clk
*
fclk
;
static
struct
clk
*
hclk
;
static
struct
clk
*
armclk
;
/* HDIV: 1, 2, 3, 4, 6, 8 */
static
inline
int
within_khz
(
unsigned
long
a
,
unsigned
long
b
)
{
long
diff
=
a
-
b
;
return
(
diff
>=
-
1000
&&
diff
<=
1000
);
}
/**
* s3c2440_cpufreq_calcdivs - calculate divider settings
* @cfg: The cpu frequency settings.
*
* Calcualte the divider values for the given frequency settings
* specified in @cfg. The values are stored in @cfg for later use
* by the relevant set routine if the request settings can be reached.
*/
int
s3c2440_cpufreq_calcdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
int
hdiv
,
pdiv
;
unsigned
long
hclk
,
fclk
,
armclk
;
unsigned
long
hclk_max
;
fclk
=
cfg
->
freq
.
fclk
;
armclk
=
cfg
->
freq
.
armclk
;
hclk_max
=
cfg
->
max
.
hclk
;
s3c_freq_dbg
(
"%s: fclk is %lu, armclk %lu, max hclk %lu
\n
"
,
__func__
,
fclk
,
armclk
,
hclk_max
);
if
(
armclk
>
fclk
)
{
printk
(
KERN_WARNING
"%s: armclk > fclk
\n
"
,
__func__
);
armclk
=
fclk
;
}
/* if we are in DVS, we need HCLK to be <= ARMCLK */
if
(
armclk
<
fclk
&&
armclk
<
hclk_max
)
hclk_max
=
armclk
;
for
(
hdiv
=
1
;
hdiv
<
9
;
hdiv
++
)
{
if
(
hdiv
==
5
||
hdiv
==
7
)
hdiv
++
;
hclk
=
(
fclk
/
hdiv
);
if
(
hclk
<=
hclk_max
||
within_khz
(
hclk
,
hclk_max
))
break
;
}
s3c_freq_dbg
(
"%s: hclk %lu, div %d
\n
"
,
__func__
,
hclk
,
hdiv
);
if
(
hdiv
>
8
)
goto
invalid
;
pdiv
=
(
hclk
>
cfg
->
max
.
pclk
)
?
2
:
1
;
if
((
hclk
/
pdiv
)
>
cfg
->
max
.
pclk
)
pdiv
++
;
s3c_freq_dbg
(
"%s: pdiv %d
\n
"
,
__func__
,
pdiv
);
if
(
pdiv
>
2
)
goto
invalid
;
pdiv
*=
hdiv
;
/* calculate a valid armclk */
if
(
armclk
<
hclk
)
armclk
=
hclk
;
/* if we're running armclk lower than fclk, this really means
* that the system should go into dvs mode, which means that
* armclk is connected to hclk. */
if
(
armclk
<
fclk
)
{
cfg
->
divs
.
dvs
=
1
;
armclk
=
hclk
;
}
else
cfg
->
divs
.
dvs
=
0
;
cfg
->
freq
.
armclk
=
armclk
;
/* store the result, and then return */
cfg
->
divs
.
h_divisor
=
hdiv
;
cfg
->
divs
.
p_divisor
=
pdiv
;
return
0
;
invalid:
return
-
EINVAL
;
}
#define CAMDIVN_HCLK_HALF (S3C2440_CAMDIVN_HCLK3_HALF | \
S3C2440_CAMDIVN_HCLK4_HALF)
/**
* s3c2440_cpufreq_setdivs - set the cpu frequency divider settings
* @cfg: The cpu frequency settings.
*
* Set the divisors from the settings in @cfg, which where generated
* during the calculation phase by s3c2440_cpufreq_calcdivs().
*/
static
void
s3c2440_cpufreq_setdivs
(
struct
s3c_cpufreq_config
*
cfg
)
{
unsigned
long
clkdiv
,
camdiv
;
s3c_freq_dbg
(
"%s: divsiors: h=%d, p=%d
\n
"
,
__func__
,
cfg
->
divs
.
h_divisor
,
cfg
->
divs
.
p_divisor
);
clkdiv
=
__raw_readl
(
S3C2410_CLKDIVN
);
camdiv
=
__raw_readl
(
S3C2440_CAMDIVN
);
clkdiv
&=
~
(
S3C2440_CLKDIVN_HDIVN_MASK
|
S3C2440_CLKDIVN_PDIVN
);
camdiv
&=
~
CAMDIVN_HCLK_HALF
;
switch
(
cfg
->
divs
.
h_divisor
)
{
case
1
:
clkdiv
|=
S3C2440_CLKDIVN_HDIVN_1
;
break
;
case
2
:
clkdiv
|=
S3C2440_CLKDIVN_HDIVN_2
;
break
;
case
6
:
camdiv
|=
S3C2440_CAMDIVN_HCLK3_HALF
;
case
3
:
clkdiv
|=
S3C2440_CLKDIVN_HDIVN_3_6
;
break
;
case
8
:
camdiv
|=
S3C2440_CAMDIVN_HCLK4_HALF
;
case
4
:
clkdiv
|=
S3C2440_CLKDIVN_HDIVN_4_8
;
break
;
default:
BUG
();
/* we don't expect to get here. */
}
if
(
cfg
->
divs
.
p_divisor
!=
cfg
->
divs
.
h_divisor
)
clkdiv
|=
S3C2440_CLKDIVN_PDIVN
;
/* todo - set pclk. */
/* Write the divisors first with hclk intentionally halved so that
* when we write clkdiv we will under-frequency instead of over. We
* then make a short delay and remove the hclk halving if necessary.
*/
__raw_writel
(
camdiv
|
CAMDIVN_HCLK_HALF
,
S3C2440_CAMDIVN
);
__raw_writel
(
clkdiv
,
S3C2410_CLKDIVN
);
ndelay
(
20
);
__raw_writel
(
camdiv
,
S3C2440_CAMDIVN
);
clk_set_parent
(
armclk
,
cfg
->
divs
.
dvs
?
hclk
:
fclk
);
}
static
int
run_freq_for
(
unsigned
long
max_hclk
,
unsigned
long
fclk
,
int
*
divs
,
struct
cpufreq_frequency_table
*
table
,
size_t
table_size
)
{
unsigned
long
freq
;
int
index
=
0
;
int
div
;
for
(
div
=
*
divs
;
div
>
0
;
div
=
*
divs
++
)
{
freq
=
fclk
/
div
;
if
(
freq
>
max_hclk
&&
div
!=
1
)
continue
;
freq
/=
1000
;
/* table is in kHz */
index
=
s3c_cpufreq_addfreq
(
table
,
index
,
table_size
,
freq
);
if
(
index
<
0
)
break
;
}
return
index
;
}
static
int
hclk_divs
[]
=
{
1
,
2
,
3
,
4
,
6
,
8
,
-
1
};
static
int
s3c2440_cpufreq_calctable
(
struct
s3c_cpufreq_config
*
cfg
,
struct
cpufreq_frequency_table
*
table
,
size_t
table_size
)
{
int
ret
;
WARN_ON
(
cfg
->
info
==
NULL
);
WARN_ON
(
cfg
->
board
==
NULL
);
ret
=
run_freq_for
(
cfg
->
info
->
max
.
hclk
,
cfg
->
info
->
max
.
fclk
,
hclk_divs
,
table
,
table_size
);
s3c_freq_dbg
(
"%s: returning %d
\n
"
,
__func__
,
ret
);
return
ret
;
}
struct
s3c_cpufreq_info
s3c2440_cpufreq_info
=
{
.
max
=
{
.
fclk
=
400000000
,
.
hclk
=
133333333
,
.
pclk
=
66666666
,
},
.
locktime_m
=
300
,
.
locktime_u
=
300
,
.
locktime_bits
=
16
,
.
name
=
"s3c244x"
,
.
calc_iotiming
=
s3c2410_iotiming_calc
,
.
set_iotiming
=
s3c2410_iotiming_set
,
.
get_iotiming
=
s3c2410_iotiming_get
,
.
set_fvco
=
s3c2410_set_fvco
,
.
set_refresh
=
s3c2410_cpufreq_setrefresh
,
.
set_divs
=
s3c2440_cpufreq_setdivs
,
.
calc_divs
=
s3c2440_cpufreq_calcdivs
,
.
calc_freqtable
=
s3c2440_cpufreq_calctable
,
.
resume_clocks
=
s3c244x_setup_clocks
,
.
debug_io_show
=
s3c_cpufreq_debugfs_call
(
s3c2410_iotiming_debugfs
),
};
static
int
s3c2440_cpufreq_add
(
struct
sys_device
*
sysdev
)
{
xtal
=
s3c_cpufreq_clk_get
(
NULL
,
"xtal"
);
hclk
=
s3c_cpufreq_clk_get
(
NULL
,
"hclk"
);
fclk
=
s3c_cpufreq_clk_get
(
NULL
,
"fclk"
);
armclk
=
s3c_cpufreq_clk_get
(
NULL
,
"armclk"
);
if
(
IS_ERR
(
xtal
)
||
IS_ERR
(
hclk
)
||
IS_ERR
(
fclk
)
||
IS_ERR
(
armclk
))
{
printk
(
KERN_ERR
"%s: failed to get clocks
\n
"
,
__func__
);
return
-
ENOENT
;
}
return
s3c_cpufreq_register
(
&
s3c2440_cpufreq_info
);
}
static
struct
sysdev_driver
s3c2440_cpufreq_driver
=
{
.
add
=
s3c2440_cpufreq_add
,
};
static
int
s3c2440_cpufreq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2440_sysclass
,
&
s3c2440_cpufreq_driver
);
}
/* arch_initcall adds the clocks we need, so use subsys_initcall. */
subsys_initcall
(
s3c2440_cpufreq_init
);
static
struct
sysdev_driver
s3c2442_cpufreq_driver
=
{
.
add
=
s3c2440_cpufreq_add
,
};
static
int
s3c2442_cpufreq_init
(
void
)
{
return
sysdev_driver_register
(
&
s3c2442_sysclass
,
&
s3c2442_cpufreq_driver
);
}
subsys_initcall
(
s3c2442_cpufreq_init
);
arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
0 → 100644
View file @
215ed323
/* arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
*
* Copyright (c) 2006,2007 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
* Vincent Sanders <vince@arm.linux.org.uk>
*
* S3C2440/S3C2442 CPU PLL tables (12MHz Crystal)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
static
struct
cpufreq_frequency_table
s3c2440_plls_12
[]
__initdata
=
{
{
.
frequency
=
75000000
,
.
index
=
PLLVAL
(
0x75
,
3
,
3
),
},
/* FVco 600.000000 */
{
.
frequency
=
80000000
,
.
index
=
PLLVAL
(
0x98
,
4
,
3
),
},
/* FVco 640.000000 */
{
.
frequency
=
90000000
,
.
index
=
PLLVAL
(
0x70
,
2
,
3
),
},
/* FVco 720.000000 */
{
.
frequency
=
100000000
,
.
index
=
PLLVAL
(
0x5c
,
1
,
3
),
},
/* FVco 800.000000 */
{
.
frequency
=
110000000
,
.
index
=
PLLVAL
(
0x66
,
1
,
3
),
},
/* FVco 880.000000 */
{
.
frequency
=
120000000
,
.
index
=
PLLVAL
(
0x70
,
1
,
3
),
},
/* FVco 960.000000 */
{
.
frequency
=
150000000
,
.
index
=
PLLVAL
(
0x75
,
3
,
2
),
},
/* FVco 600.000000 */
{
.
frequency
=
160000000
,
.
index
=
PLLVAL
(
0x98
,
4
,
2
),
},
/* FVco 640.000000 */
{
.
frequency
=
170000000
,
.
index
=
PLLVAL
(
0x4d
,
1
,
2
),
},
/* FVco 680.000000 */
{
.
frequency
=
180000000
,
.
index
=
PLLVAL
(
0x70
,
2
,
2
),
},
/* FVco 720.000000 */
{
.
frequency
=
190000000
,
.
index
=
PLLVAL
(
0x57
,
1
,
2
),
},
/* FVco 760.000000 */
{
.
frequency
=
200000000
,
.
index
=
PLLVAL
(
0x5c
,
1
,
2
),
},
/* FVco 800.000000 */
{
.
frequency
=
210000000
,
.
index
=
PLLVAL
(
0x84
,
2
,
2
),
},
/* FVco 840.000000 */
{
.
frequency
=
220000000
,
.
index
=
PLLVAL
(
0x66
,
1
,
2
),
},
/* FVco 880.000000 */
{
.
frequency
=
230000000
,
.
index
=
PLLVAL
(
0x6b
,
1
,
2
),
},
/* FVco 920.000000 */
{
.
frequency
=
240000000
,
.
index
=
PLLVAL
(
0x70
,
1
,
2
),
},
/* FVco 960.000000 */
{
.
frequency
=
300000000
,
.
index
=
PLLVAL
(
0x75
,
3
,
1
),
},
/* FVco 600.000000 */
{
.
frequency
=
310000000
,
.
index
=
PLLVAL
(
0x93
,
4
,
1
),
},
/* FVco 620.000000 */
{
.
frequency
=
320000000
,
.
index
=
PLLVAL
(
0x98
,
4
,
1
),
},
/* FVco 640.000000 */
{
.
frequency
=
330000000
,
.
index
=
PLLVAL
(
0x66
,
2
,
1
),
},
/* FVco 660.000000 */
{
.
frequency
=
340000000
,
.
index
=
PLLVAL
(
0x4d
,
1
,
1
),
},
/* FVco 680.000000 */
{
.
frequency
=
350000000
,
.
index
=
PLLVAL
(
0xa7
,
4
,
1
),
},
/* FVco 700.000000 */
{
.
frequency
=
360000000
,
.
index
=
PLLVAL
(
0x70
,
2
,
1
),
},
/* FVco 720.000000 */
{
.
frequency
=
370000000
,
.
index
=
PLLVAL
(
0xb1
,
4
,
1
),
},
/* FVco 740.000000 */
{
.
frequency
=
380000000
,
.
index
=
PLLVAL
(
0x57
,
1
,
1
),
},
/* FVco 760.000000 */
{
.
frequency
=
390000000
,
.
index
=
PLLVAL
(
0x7a
,
2
,
1
),
},
/* FVco 780.000000 */
{
.
frequency
=
400000000
,
.
index
=
PLLVAL
(
0x5c
,
1
,
1
),
},
/* FVco 800.000000 */
};
static
int
s3c2440_plls12_add
(
struct
sys_device
*
dev
)
{
struct
clk
*
xtal_clk
;
unsigned
long
xtal
;
xtal_clk
=
clk_get
(
NULL
,
"xtal"
);
if
(
IS_ERR
(
xtal_clk
))
return
PTR_ERR
(
xtal_clk
);
xtal
=
clk_get_rate
(
xtal_clk
);
clk_put
(
xtal_clk
);
if
(
xtal
==
12000000
)
{
printk
(
KERN_INFO
"Using PLL table for 12MHz crystal
\n
"
);
return
s3c_plltab_register
(
s3c2440_plls_12
,
ARRAY_SIZE
(
s3c2440_plls_12
));
}
return
0
;
}
static
struct
sysdev_driver
s3c2440_plls12_drv
=
{
.
add
=
s3c2440_plls12_add
,
};
static
int
__init
s3c2440_pll_12mhz
(
void
)
{
return
sysdev_driver_register
(
&
s3c2440_sysclass
,
&
s3c2440_plls12_drv
);
}
arch_initcall
(
s3c2440_pll_12mhz
);
static
struct
sysdev_driver
s3c2442_plls12_drv
=
{
.
add
=
s3c2440_plls12_add
,
};
static
int
__init
s3c2442_pll_12mhz
(
void
)
{
return
sysdev_driver_register
(
&
s3c2442_sysclass
,
&
s3c2442_plls12_drv
);
}
arch_initcall
(
s3c2442_pll_12mhz
);
arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
0 → 100644
View file @
215ed323
/* arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
*
* Copyright (c) 2006-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
* Vincent Sanders <vince@arm.linux.org.uk>
*
* S3C2440/S3C2442 CPU PLL tables (16.93444MHz Crystal)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
static
struct
cpufreq_frequency_table
s3c2440_plls_169344
[]
__initdata
=
{
{
.
frequency
=
78019200
,
.
index
=
PLLVAL
(
121
,
5
,
3
),
},
/* FVco 624.153600 */
{
.
frequency
=
84067200
,
.
index
=
PLLVAL
(
131
,
5
,
3
),
},
/* FVco 672.537600 */
{
.
frequency
=
90115200
,
.
index
=
PLLVAL
(
141
,
5
,
3
),
},
/* FVco 720.921600 */
{
.
frequency
=
96163200
,
.
index
=
PLLVAL
(
151
,
5
,
3
),
},
/* FVco 769.305600 */
{
.
frequency
=
102135600
,
.
index
=
PLLVAL
(
185
,
6
,
3
),
},
/* FVco 817.084800 */
{
.
frequency
=
108259200
,
.
index
=
PLLVAL
(
171
,
5
,
3
),
},
/* FVco 866.073600 */
{
.
frequency
=
114307200
,
.
index
=
PLLVAL
(
127
,
3
,
3
),
},
/* FVco 914.457600 */
{
.
frequency
=
120234240
,
.
index
=
PLLVAL
(
134
,
3
,
3
),
},
/* FVco 961.873920 */
{
.
frequency
=
126161280
,
.
index
=
PLLVAL
(
141
,
3
,
3
),
},
/* FVco 1009.290240 */
{
.
frequency
=
132088320
,
.
index
=
PLLVAL
(
148
,
3
,
3
),
},
/* FVco 1056.706560 */
{
.
frequency
=
138015360
,
.
index
=
PLLVAL
(
155
,
3
,
3
),
},
/* FVco 1104.122880 */
{
.
frequency
=
144789120
,
.
index
=
PLLVAL
(
163
,
3
,
3
),
},
/* FVco 1158.312960 */
{
.
frequency
=
150100363
,
.
index
=
PLLVAL
(
187
,
9
,
2
),
},
/* FVco 600.401454 */
{
.
frequency
=
156038400
,
.
index
=
PLLVAL
(
121
,
5
,
2
),
},
/* FVco 624.153600 */
{
.
frequency
=
162086400
,
.
index
=
PLLVAL
(
126
,
5
,
2
),
},
/* FVco 648.345600 */
{
.
frequency
=
168134400
,
.
index
=
PLLVAL
(
131
,
5
,
2
),
},
/* FVco 672.537600 */
{
.
frequency
=
174048000
,
.
index
=
PLLVAL
(
177
,
7
,
2
),
},
/* FVco 696.192000 */
{
.
frequency
=
180230400
,
.
index
=
PLLVAL
(
141
,
5
,
2
),
},
/* FVco 720.921600 */
{
.
frequency
=
186278400
,
.
index
=
PLLVAL
(
124
,
4
,
2
),
},
/* FVco 745.113600 */
{
.
frequency
=
192326400
,
.
index
=
PLLVAL
(
151
,
5
,
2
),
},
/* FVco 769.305600 */
{
.
frequency
=
198132480
,
.
index
=
PLLVAL
(
109
,
3
,
2
),
},
/* FVco 792.529920 */
{
.
frequency
=
204271200
,
.
index
=
PLLVAL
(
185
,
6
,
2
),
},
/* FVco 817.084800 */
{
.
frequency
=
210268800
,
.
index
=
PLLVAL
(
141
,
4
,
2
),
},
/* FVco 841.075200 */
{
.
frequency
=
216518400
,
.
index
=
PLLVAL
(
171
,
5
,
2
),
},
/* FVco 866.073600 */
{
.
frequency
=
222264000
,
.
index
=
PLLVAL
(
97
,
2
,
2
),
},
/* FVco 889.056000 */
{
.
frequency
=
228614400
,
.
index
=
PLLVAL
(
127
,
3
,
2
),
},
/* FVco 914.457600 */
{
.
frequency
=
234259200
,
.
index
=
PLLVAL
(
158
,
4
,
2
),
},
/* FVco 937.036800 */
{
.
frequency
=
240468480
,
.
index
=
PLLVAL
(
134
,
3
,
2
),
},
/* FVco 961.873920 */
{
.
frequency
=
246960000
,
.
index
=
PLLVAL
(
167
,
4
,
2
),
},
/* FVco 987.840000 */
{
.
frequency
=
252322560
,
.
index
=
PLLVAL
(
141
,
3
,
2
),
},
/* FVco 1009.290240 */
{
.
frequency
=
258249600
,
.
index
=
PLLVAL
(
114
,
2
,
2
),
},
/* FVco 1032.998400 */
{
.
frequency
=
264176640
,
.
index
=
PLLVAL
(
148
,
3
,
2
),
},
/* FVco 1056.706560 */
{
.
frequency
=
270950400
,
.
index
=
PLLVAL
(
120
,
2
,
2
),
},
/* FVco 1083.801600 */
{
.
frequency
=
276030720
,
.
index
=
PLLVAL
(
155
,
3
,
2
),
},
/* FVco 1104.122880 */
{
.
frequency
=
282240000
,
.
index
=
PLLVAL
(
92
,
1
,
2
),
},
/* FVco 1128.960000 */
{
.
frequency
=
289578240
,
.
index
=
PLLVAL
(
163
,
3
,
2
),
},
/* FVco 1158.312960 */
{
.
frequency
=
294235200
,
.
index
=
PLLVAL
(
131
,
2
,
2
),
},
/* FVco 1176.940800 */
{
.
frequency
=
300200727
,
.
index
=
PLLVAL
(
187
,
9
,
1
),
},
/* FVco 600.401454 */
{
.
frequency
=
306358690
,
.
index
=
PLLVAL
(
191
,
9
,
1
),
},
/* FVco 612.717380 */
{
.
frequency
=
312076800
,
.
index
=
PLLVAL
(
121
,
5
,
1
),
},
/* FVco 624.153600 */
{
.
frequency
=
318366720
,
.
index
=
PLLVAL
(
86
,
3
,
1
),
},
/* FVco 636.733440 */
{
.
frequency
=
324172800
,
.
index
=
PLLVAL
(
126
,
5
,
1
),
},
/* FVco 648.345600 */
{
.
frequency
=
330220800
,
.
index
=
PLLVAL
(
109
,
4
,
1
),
},
/* FVco 660.441600 */
{
.
frequency
=
336268800
,
.
index
=
PLLVAL
(
131
,
5
,
1
),
},
/* FVco 672.537600 */
{
.
frequency
=
342074880
,
.
index
=
PLLVAL
(
93
,
3
,
1
),
},
/* FVco 684.149760 */
{
.
frequency
=
348096000
,
.
index
=
PLLVAL
(
177
,
7
,
1
),
},
/* FVco 696.192000 */
{
.
frequency
=
355622400
,
.
index
=
PLLVAL
(
118
,
4
,
1
),
},
/* FVco 711.244800 */
{
.
frequency
=
360460800
,
.
index
=
PLLVAL
(
141
,
5
,
1
),
},
/* FVco 720.921600 */
{
.
frequency
=
366206400
,
.
index
=
PLLVAL
(
165
,
6
,
1
),
},
/* FVco 732.412800 */
{
.
frequency
=
372556800
,
.
index
=
PLLVAL
(
124
,
4
,
1
),
},
/* FVco 745.113600 */
{
.
frequency
=
378201600
,
.
index
=
PLLVAL
(
126
,
4
,
1
),
},
/* FVco 756.403200 */
{
.
frequency
=
384652800
,
.
index
=
PLLVAL
(
151
,
5
,
1
),
},
/* FVco 769.305600 */
{
.
frequency
=
391608000
,
.
index
=
PLLVAL
(
177
,
6
,
1
),
},
/* FVco 783.216000 */
{
.
frequency
=
396264960
,
.
index
=
PLLVAL
(
109
,
3
,
1
),
},
/* FVco 792.529920 */
{
.
frequency
=
402192000
,
.
index
=
PLLVAL
(
87
,
2
,
1
),
},
/* FVco 804.384000 */
};
static
int
s3c2440_plls169344_add
(
struct
sys_device
*
dev
)
{
struct
clk
*
xtal_clk
;
unsigned
long
xtal
;
xtal_clk
=
clk_get
(
NULL
,
"xtal"
);
if
(
IS_ERR
(
xtal_clk
))
return
PTR_ERR
(
xtal_clk
);
xtal
=
clk_get_rate
(
xtal_clk
);
clk_put
(
xtal_clk
);
if
(
xtal
==
169344000
)
{
printk
(
KERN_INFO
"Using PLL table for 16.9344MHz crystal
\n
"
);
return
s3c_plltab_register
(
s3c2440_plls_169344
,
ARRAY_SIZE
(
s3c2440_plls_169344
));
}
return
0
;
}
static
struct
sysdev_driver
s3c2440_plls169344_drv
=
{
.
add
=
s3c2440_plls169344_add
,
};
static
int
__init
s3c2440_pll_16934400
(
void
)
{
return
sysdev_driver_register
(
&
s3c2440_sysclass
,
&
s3c2440_plls169344_drv
);
}
arch_initcall
(
s3c2440_pll_16934400
);
static
struct
sysdev_driver
s3c2442_plls169344_drv
=
{
.
add
=
s3c2440_plls169344_add
,
};
static
int
__init
s3c2442_pll_16934400
(
void
)
{
return
sysdev_driver_register
(
&
s3c2442_sysclass
,
&
s3c2442_plls169344_drv
);
}
arch_initcall
(
s3c2442_pll_16934400
);
include/linux/amba/pl093.h
0 → 100644
View file @
215ed323
/* linux/amba/pl093.h
*
* Copyright (c) 2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* AMBA PL093 SSMC (synchronous static memory controller)
* See DDI0236.pdf (r0p4) for more details
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define SMB_BANK(x) ((x) * 0x20)
/* each bank control set is 0x20 apart */
/* Offsets for SMBxxxxRy registers */
#define SMBIDCYR (0x00)
#define SMBWSTRDR (0x04)
#define SMBWSTWRR (0x08)
#define SMBWSTOENR (0x0C)
#define SMBWSTWENR (0x10)
#define SMBCR (0x14)
#define SMBSR (0x18)
#define SMBWSTBRDR (0x1C)
/* Masks for SMB registers */
#define IDCY_MASK (0xf)
#define WSTRD_MASK (0xf)
#define WSTWR_MASK (0xf)
#define WSTOEN_MASK (0xf)
#define WSTWEN_MASK (0xf)
/* Notes from datasheet:
* WSTOEN <= WSTRD
* WSTWEN <= WSTWR
*
* WSTOEN is not used with nWAIT
*/
/* SMBCR bit definitions */
#define SMBCR_BIWRITEEN (1 << 21)
#define SMBCR_ADDRVALIDWRITEEN (1 << 20)
#define SMBCR_SYNCWRITE (1 << 17)
#define SMBCR_BMWRITE (1 << 16)
#define SMBCR_WRAPREAD (1 << 14)
#define SMBCR_BIREADEN (1 << 13)
#define SMBCR_ADDRVALIDREADEN (1 << 12)
#define SMBCR_SYNCREAD (1 << 9)
#define SMBCR_BMREAD (1 << 8)
#define SMBCR_SMBLSPOL (1 << 6)
#define SMBCR_WP (1 << 3)
#define SMBCR_WAITEN (1 << 2)
#define SMBCR_WAITPOL (1 << 1)
#define SMBCR_RBLE (1 << 0)
#define SMBCR_BURSTLENWRITE_MASK (3 << 18)
#define SMBCR_BURSTLENWRITE_4 (0 << 18)
#define SMBCR_BURSTLENWRITE_8 (1 << 18)
#define SMBCR_BURSTLENWRITE_RESERVED (2 << 18)
#define SMBCR_BURSTLENWRITE_CONTINUOUS (3 << 18)
#define SMBCR_BURSTLENREAD_MASK (3 << 10)
#define SMBCR_BURSTLENREAD_4 (0 << 10)
#define SMBCR_BURSTLENREAD_8 (1 << 10)
#define SMBCR_BURSTLENREAD_16 (2 << 10)
#define SMBCR_BURSTLENREAD_CONTINUOUS (3 << 10)
#define SMBCR_MW_MASK (3 << 4)
#define SMBCR_MW_8BIT (0 << 4)
#define SMBCR_MW_16BIT (1 << 4)
#define SMBCR_MW_M32BIT (2 << 4)
/* SSMC status registers */
#define SSMCCSR (0x200)
#define SSMCCR (0x204)
#define SSMCITCR (0x208)
#define SSMCITIP (0x20C)
#define SSMCITIOP (0x210)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment