Merge branch 'next-s3c24xx-cpufreq' into next-s3c
This commit is contained in:
commit
215ed3236a
38 changed files with 3605 additions and 26 deletions
75
Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
Normal file
75
Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
Normal file
|
@ -0,0 +1,75 @@
|
|||
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
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
159
arch/arm/mach-s3c2410/cpu-freq.c
Normal file
159
arch/arm/mach-s3c2410/cpu-freq.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
/* 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);
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
95
arch/arm/mach-s3c2410/pll.c
Normal file
95
arch/arm/mach-s3c2410/pll.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* 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);
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
257
arch/arm/mach-s3c2412/cpu-freq.c
Normal file
257
arch/arm/mach-s3c2412/cpu-freq.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/* 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);
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#define S3C64XX_PA_IIC1 (0x7F00F000)
|
||||
|
||||
#define S3C64XX_PA_GPIO (0x7F008000)
|
||||
#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)
|
||||
#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000)
|
||||
#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(0x00600000)
|
||||
#define S3C64XX_VA_MODEM S3C_ADDR_CPU(0x00100000)
|
||||
|
||||
#define S3C64XX_PA_USBHOST (0x74300000)
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* per-board configuraton information, such as memory refresh and
|
||||
* how to initialise IO timings.
|
||||
* This contains the board specific settings that affect how the CPU
|
||||
* drivers chose settings. These include the memory refresh and IO
|
||||
* timing information.
|
||||
*
|
||||
* 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. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
199
arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
Normal file
199
arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/* 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);
|
||||
|
716
arch/arm/plat-s3c24xx/cpu-freq.c
Normal file
716
arch/arm/plat-s3c24xx/cpu-freq.c
Normal file
|
@ -0,0 +1,716 @@
|
|||
/* 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;
|
||||
}
|
|
@ -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 = s3c2410a_init,
|
||||
.name = name_s3c2410a
|
||||
},
|
||||
{
|
||||
|
|
282
arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
Normal file
282
arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
Normal file
|
@ -0,0 +1,282 @@
|
|||
/* 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;
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
#ifdef CONFIG_CPU_S3C2410
|
||||
|
||||
extern int s3c2410_init(void);
|
||||
extern int s3c2410a_init(void);
|
||||
|
||||
extern void s3c2410_map_io(void);
|
||||
|
||||
|
|
64
arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
Normal file
64
arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* 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);
|
||||
}
|
477
arch/arm/plat-s3c24xx/s3c2410-iotiming.c
Normal file
477
arch/arm/plat-s3c24xx/s3c2410-iotiming.c
Normal file
|
@ -0,0 +1,477 @@
|
|||
/* 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,
|
||||
"\tRead: 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;
|
||||
}
|
285
arch/arm/plat-s3c24xx/s3c2412-iotiming.c
Normal file
285
arch/arm/plat-s3c24xx/s3c2412-iotiming.c
Normal file
|
@ -0,0 +1,285 @@
|
|||
/* 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,
|
||||
"\tRead: 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);
|
||||
}
|
311
arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
Normal file
311
arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/* 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);
|
97
arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
Normal file
97
arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* 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);
|
127
arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
Normal file
127
arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/* 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);
|
80
include/linux/amba/pl093.h
Normal file
80
include/linux/amba/pl093.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* 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)
|
Loading…
Reference in a new issue