diff options
author | Kukjin Kim <kgene.kim@samsung.com> | 2013-01-31 16:54:38 -0800 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2013-02-03 16:17:10 -0800 |
commit | 09ec1d7ea67f6e23b6ef2178fa2ec48fd65477dc (patch) | |
tree | 38318b5c98f48754521a288f40e3c77a1fe8a623 /arch/arm/mach-s3c24xx | |
parent | f44ddba3635e35317057e976888d4a12dcb0f842 (diff) |
ARM: S3C24XX: Remove plat-s3c24xx directory in arch/arm/
This patch is for just moving plat-s3c24xx/*.c into mach-s3c24xx/, so
that we could remove plat-s3c24xx directory. But since the PLAT_S3C24XX
is used in drivers, the statement is not deleted and it will be sorted
out next time.
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/mach-s3c24xx')
-rw-r--r-- | arch/arm/mach-s3c24xx/Kconfig | 104 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/Makefile | 14 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/clock-dclk.c | 195 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/clock-s3c2410.c | 252 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/cpufreq-debugfs.c | 198 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/cpufreq-utils.c | 63 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/cpufreq.c | 715 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/dma.c | 1468 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/iotiming-s3c2410.c | 477 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/iotiming-s3c2412.c | 285 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/irq.c | 822 |
11 files changed, 4592 insertions, 1 deletions
diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig index 67df58bdc096..31eacad5b3e9 100644 --- a/arch/arm/mach-s3c24xx/Kconfig +++ b/arch/arm/mach-s3c24xx/Kconfig @@ -9,6 +9,15 @@ if ARCH_S3C24XX +config PLAT_S3C24XX + def_bool y + select ARCH_REQUIRE_GPIOLIB + select NO_IOPORT + select S3C_DEV_NAND + select IRQ_DOMAIN + help + Base platform code for any Samsung S3C24XX device + menu "SAMSUNG S3C24XX SoCs Support" comment "S3C24XX SoCs" @@ -83,6 +92,17 @@ config CPU_S3C2443 # common code +config S3C2410_CLOCK + bool + help + Clock code for the S3C2410, and similar processors which + is currently includes the S3C2410, S3C2440, S3C2442. + +config S3C24XX_DCLK + bool + help + Clock code for supporting DCLK/CLKOUT on S3C24XX architectures + config S3C24XX_SMDK bool help @@ -111,6 +131,22 @@ config S3C24XX_SETUP_TS help Compile in platform device definition for Samsung TouchScreen. +config S3C24XX_DMA + bool "S3C2410 DMA support" + depends on ARCH_S3C24XX + select S3C_DMA + help + S3C2410 DMA support. This is needed for drivers like sound which + use the S3C2410's DMA system to move data to and from the + peripheral blocks. + +config S3C2410_DMA_DEBUG + bool "S3C2410 DMA support debug" + depends on ARCH_S3C24XX && S3C2410_DMA + help + Enable debugging output for the DMA code. This option sends info + to the kernel log, at priority KERN_DEBUG. + config S3C2410_DMA bool depends on S3C24XX_DMA && (CPU_S3C2410 || CPU_S3C2442) @@ -123,6 +159,74 @@ config S3C2410_PM help Power Management code common to S3C2410 and better +# low-level serial option nodes + +config CPU_LLSERIAL_S3C2410_ONLY + bool + default y if CPU_LLSERIAL_S3C2410 && !CPU_LLSERIAL_S3C2440 + +config CPU_LLSERIAL_S3C2440_ONLY + bool + default y if CPU_LLSERIAL_S3C2440 && !CPU_LLSERIAL_S3C2410 + +config CPU_LLSERIAL_S3C2410 + bool + help + Selected if there is an S3C2410 (or register compatible) serial + low-level implementation needed + +config CPU_LLSERIAL_S3C2440 + bool + help + Selected if there is an S3C2440 (or register compatible) serial + low-level implementation needed + +# gpio configurations + +config S3C24XX_GPIO_EXTRA + int + default 128 if S3C24XX_GPIO_EXTRA128 + default 64 if S3C24XX_GPIO_EXTRA64 + default 16 if ARCH_H1940 + default 0 + +config S3C24XX_GPIO_EXTRA64 + bool + help + Add an extra 64 gpio numbers to the available GPIO pool. This is + available for boards that need extra gpios for external devices. + +config S3C24XX_GPIO_EXTRA128 + bool + help + Add an extra 128 gpio numbers to the available GPIO pool. This is + available for boards that need extra gpios for external devices. + +# 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. + # cpu-specific sections if CPU_S3C2410 diff --git a/arch/arm/mach-s3c24xx/Makefile b/arch/arm/mach-s3c24xx/Makefile index 1d67582da41a..af53d27d5c36 100644 --- a/arch/arm/mach-s3c24xx/Makefile +++ b/arch/arm/mach-s3c24xx/Makefile @@ -14,7 +14,7 @@ obj- := # core -obj-y += common.o +obj-y += common.o irq.o obj-$(CONFIG_CPU_S3C2410) += s3c2410.o obj-$(CONFIG_S3C2410_CPUFREQ) += cpufreq-s3c2410.o @@ -47,9 +47,21 @@ obj-$(CONFIG_PM) += pm.o irq-pm.o sleep.o # common code +obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o +obj-$(CONFIG_S3C24XX_DMA) += dma.o + +obj-$(CONFIG_S3C2410_CLOCK) += clock-s3c2410.o +obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += cpufreq-utils.o + +obj-$(CONFIG_S3C2410_IOTIMING) += iotiming-s3c2410.o +obj-$(CONFIG_S3C2412_IOTIMING) += iotiming-s3c2412.o + obj-$(CONFIG_S3C2443_COMMON) += common-s3c2443.o obj-$(CONFIG_S3C2443_DMA) += dma-s3c2443.o +obj-$(CONFIG_CPU_FREQ_S3C24XX) += cpufreq.o +obj-$(CONFIG_CPU_FREQ_S3C24XX_DEBUGFS) += cpufreq-debugfs.o + # # machine support # following is ordered alphabetically by option text. diff --git a/arch/arm/mach-s3c24xx/clock-dclk.c b/arch/arm/mach-s3c24xx/clock-dclk.c new file mode 100644 index 000000000000..1edd9b2369c5 --- /dev/null +++ b/arch/arm/mach-s3c24xx/clock-dclk.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2004-2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * 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. + * + * S3C24XX - definitions for DCLK and CLKOUT registers + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> + +#include <plat/clock.h> +#include <plat/cpu.h> + +/* clocks that could be registered by external code */ + +static int s3c24xx_dclk_enable(struct clk *clk, int enable) +{ + unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (enable) + dclkcon |= clk->ctrlbit; + else + dclkcon &= ~clk->ctrlbit; + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} + +static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long dclkcon; + unsigned int uclk; + + if (parent == &clk_upll) + uclk = 1; + else if (parent == &clk_p) + uclk = 0; + else + return -EINVAL; + + clk->parent = parent; + + dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; + } else { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; + } + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} +static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) +{ + unsigned long div; + + if ((rate == 0) || !clk->parent) + return 0; + + div = clk_get_rate(clk->parent) / rate; + if (div < 2) + div = 2; + else if (div > 16) + div = 16; + + return div; +} + +static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return 0; + + return clk_get_rate(clk->parent) / div; +} + +static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) +{ + unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return -EINVAL; + + if (clk == &s3c24xx_dclk0) { + mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | + S3C2410_DCLKCON_DCLK0_CMP_MASK; + data = S3C2410_DCLKCON_DCLK0_DIV(div) | + S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); + } else if (clk == &s3c24xx_dclk1) { + mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | + S3C2410_DCLKCON_DCLK1_CMP_MASK; + data = S3C2410_DCLKCON_DCLK1_DIV(div) | + S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); + } else + return -EINVAL; + + clk->rate = clk_get_rate(clk->parent) / div; + __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), + S3C24XX_DCLKCON); + return clk->rate; +} +static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long mask; + unsigned long source; + + /* calculate the MISCCR setting for the clock */ + + if (parent == &clk_mpll) + source = S3C2410_MISCCR_CLK0_MPLL; + else if (parent == &clk_upll) + source = S3C2410_MISCCR_CLK0_UPLL; + else if (parent == &clk_f) + source = S3C2410_MISCCR_CLK0_FCLK; + else if (parent == &clk_h) + source = S3C2410_MISCCR_CLK0_HCLK; + else if (parent == &clk_p) + source = S3C2410_MISCCR_CLK0_PCLK; + else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) + source = S3C2410_MISCCR_CLK0_DCLK0; + else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) + source = S3C2410_MISCCR_CLK0_DCLK0; + else + return -EINVAL; + + clk->parent = parent; + + if (clk == &s3c24xx_clkout0) + mask = S3C2410_MISCCR_CLK0_MASK; + else { + source <<= 4; + mask = S3C2410_MISCCR_CLK1_MASK; + } + + s3c2410_modify_misccr(mask, source); + return 0; +} + +/* external clock definitions */ + +static struct clk_ops dclk_ops = { + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .ops = &dclk_ops, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .ctrlbit = S3C2410_DCLKCON_DCLK1EN, + .enable = s3c24xx_dclk_enable, + .ops = &dclk_ops, +}; + +static struct clk_ops clkout_ops = { + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .ops = &clkout_ops, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .ops = &clkout_ops, +}; diff --git a/arch/arm/mach-s3c24xx/clock-s3c2410.c b/arch/arm/mach-s3c24xx/clock-s3c2410.c new file mode 100644 index 000000000000..641266f3d152 --- /dev/null +++ b/arch/arm/mach-s3c24xx/clock-s3c2410.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410,S3C2440,S3C2442 Clock control support + * + * 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/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/serial_core.h> +#include <linux/io.h> + +#include <asm/mach/map.h> + +#include <mach/hardware.h> + +#include <plat/regs-serial.h> +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> + +#include <plat/s3c2410.h> +#include <plat/clock.h> +#include <plat/cpu.h> + +int s3c2410_clkcon_enable(struct clk *clk, int enable) +{ + unsigned int clocks = clk->ctrlbit; + unsigned long clkcon; + + clkcon = __raw_readl(S3C2410_CLKCON); + + if (enable) + clkcon |= clocks; + else + clkcon &= ~clocks; + + /* ensure none of the special function bits set */ + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + + __raw_writel(clkcon, S3C2410_CLKCON); + + return 0; +} + +static int s3c2410_upll_enable(struct clk *clk, int enable) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long orig = clkslow; + + if (enable) + clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; + else + clkslow |= S3C2410_CLKSLOW_UCLK_OFF; + + __raw_writel(clkslow, S3C2410_CLKSLOW); + + /* if we started the UPLL, then allow to settle */ + + if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) + udelay(200); + + return 0; +} + +/* standard clock definitions */ + +static struct clk init_clocks_off[] = { + { + .name = "nand", + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_NAND, + }, { + .name = "sdi", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SDI, + }, { + .name = "adc", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_ADC, + }, { + .name = "i2c", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIC, + }, { + .name = "iis", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIS, + }, { + .name = "spi", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SPI, + } +}; + +static struct clk init_clocks[] = { + { + .name = "lcd", + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_LCDC, + }, { + .name = "gpio", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_GPIO, + }, { + .name = "usb-host", + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBH, + }, { + .name = "usb-device", + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBD, + }, { + .name = "timers", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_PWMT, + }, { + .name = "uart", + .devname = "s3c2410-uart.0", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART0, + }, { + .name = "uart", + .devname = "s3c2410-uart.1", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART1, + }, { + .name = "uart", + .devname = "s3c2410-uart.2", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART2, + }, { + .name = "rtc", + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_RTC, + }, { + .name = "watchdog", + .parent = &clk_p, + .ctrlbit = 0, + }, { + .name = "usb-bus-host", + .parent = &clk_usb_bus, + }, { + .name = "usb-bus-gadget", + .parent = &clk_usb_bus, + }, +}; + +/* s3c2410_baseclk_add() + * + * Add all the clocks used by the s3c2410 or compatible CPUs + * such as the S3C2440 and S3C2442. + * + * We cannot use a system device as we are needed before any + * of the init-calls that initialise the devices are actually + * done. +*/ + +int __init s3c2410_baseclk_add(void) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long clkcon = __raw_readl(S3C2410_CLKCON); + struct clk *clkp; + struct clk *xtal; + int ret; + int ptr; + + clk_upll.enable = s3c2410_upll_enable; + + if (s3c24xx_register_clock(&clk_usb_bus) < 0) + printk(KERN_ERR "failed to register usb bus clock\n"); + + /* register clocks from clock array */ + + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + /* ensure that we note the clock state */ + + clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + /* We must be careful disabling the clocks we are not intending to + * be using at boot time, as subsystems such as the LCD which do + * their own DMA requests to the bus can cause the system to lockup + * if they where in the middle of requesting bus access. + * + * Disabling the LCD clock if the LCD is active is very dangerous, + * and therefore the bootloader should be careful to not enable + * the LCD clock if it is not needed. + */ + + /* install (and disable) the clocks we do not need immediately */ + + s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + + /* show the clock-slow value */ + + xtal = clk_get(NULL, "xtal"); + + printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", + print_mhz(clk_get_rate(xtal) / + ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), + (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", + (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", + (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); + + s3c_pwmclk_init(); + return 0; +} diff --git a/arch/arm/mach-s3c24xx/cpufreq-debugfs.c b/arch/arm/mach-s3c24xx/cpufreq-debugfs.c new file mode 100644 index 000000000000..9b7b4289d66c --- /dev/null +++ b/arch/arm/mach-s3c24xx/cpufreq-debugfs.c @@ -0,0 +1,198 @@ +/* + * 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/export.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); + diff --git a/arch/arm/mach-s3c24xx/cpufreq-utils.c b/arch/arm/mach-s3c24xx/cpufreq-utils.c new file mode 100644 index 000000000000..89e4e2b7a82e --- /dev/null +++ b/arch/arm/mach-s3c24xx/cpufreq-utils.c @@ -0,0 +1,63 @@ +/* + * 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); +} diff --git a/arch/arm/mach-s3c24xx/cpufreq.c b/arch/arm/mach-s3c24xx/cpufreq.c new file mode 100644 index 000000000000..5f181e733eee --- /dev/null +++ b/arch/arm/mach-s3c24xx/cpufreq.c @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2006-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/device.h> +#include <linux/sysfs.h> +#include <linux/slab.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; + } + } |