Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 215ed323 authored by Ben Dooks's avatar Ben Dooks
Browse files

Merge branch 'next-s3c24xx-cpufreq' into next-s3c

parents 0fbdd270 e6d197a6
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
		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
+60 −1
Original line number Diff line number Diff line
@@ -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"
+18 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/* 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);
Loading