aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-s3c64xx
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2010-01-26 13:41:30 +0900
committerBen Dooks <ben-linux@fluff.org>2010-02-20 22:33:54 +0000
commitf7be9abaa5f4a64fdcca6808bb7eacb3547e574e (patch)
tree0c14f12dcf4828bcdb8096faf0a577b3cc0c942e /arch/arm/mach-s3c64xx
parent88fc68a280709f3fb9488986ab39eac330d17b6d (diff)
ARM: S3C64XX: Move core support to mach-s3c64xx
Move the core S3C64XX support to mach-s3c64xx as it is unlikely to be used outside of this directory. Also move the SoC header files in with it. This includes the clock, cpu, cpufreq, dma, gpiolib and pll support. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r--arch/arm/mach-s3c64xx/Kconfig4
-rw-r--r--arch/arm/mach-s3c64xx/Makefile15
-rw-r--r--arch/arm/mach-s3c64xx/clock.c306
-rw-r--r--arch/arm/mach-s3c64xx/cpu.c161
-rw-r--r--arch/arm/mach-s3c64xx/cpufreq.c270
-rw-r--r--arch/arm/mach-s3c64xx/dma.c750
-rw-r--r--arch/arm/mach-s3c64xx/gpiolib.c288
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/pll.h74
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/s3c6400.h35
-rw-r--r--arch/arm/mach-s3c64xx/include/mach/s3c6410.h29
-rw-r--r--arch/arm/mach-s3c64xx/mach-anw6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-hmt.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-ncp.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6400.c2
-rw-r--r--arch/arm/mach-s3c64xx/mach-smdk6410.c2
-rw-r--r--arch/arm/mach-s3c64xx/pm.c173
-rw-r--r--arch/arm/mach-s3c64xx/s3c6400.c2
-rw-r--r--arch/arm/mach-s3c64xx/s3c6410.c4
-rw-r--r--arch/arm/mach-s3c64xx/sleep.S144
19 files changed, 2257 insertions, 8 deletions
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index 15e065ef19a..7c9cd9a9901 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -19,6 +19,10 @@ config CPU_S3C6410
help
Enable S3C6410 CPU support
+config S3C64XX_DMA
+ bool "S3C64XX DMA"
+ select S3C_DMA
+
config S3C64XX_SETUP_SDHCI
select S3C64XX_SETUP_SDHCI_GPIO
bool
diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile
index 49b71d5f2e5..4417f1ad99b 100644
--- a/arch/arm/mach-s3c64xx/Makefile
+++ b/arch/arm/mach-s3c64xx/Makefile
@@ -10,6 +10,11 @@ obj-m :=
obj-n :=
obj- :=
+# Core files
+obj-y += cpu.o
+obj-y += clock.o
+obj-y += gpiolib.o
+
# Core support for S3C6400 system
obj-$(CONFIG_CPU_S3C6400) += s3c6400.o
@@ -18,6 +23,14 @@ obj-$(CONFIG_CPU_S3C6410) += s3c6410.o
obj-y += irq.o
obj-y += irq-eint.o
+# CPU frequency scaling
+
+obj-$(CONFIG_CPU_FREQ_S3C64XX) += cpufreq.o
+
+# DMA support
+
+obj-$(CONFIG_S3C64XX_DMA) += dma.o
+
# Device setup
obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
@@ -28,6 +41,8 @@ obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
# PM
+obj-$(CONFIG_PM) += pm.o
+obj-$(CONFIG_PM) += sleep.o
obj-$(CONFIG_PM) += irq-pm.o
# Machine support
diff --git a/arch/arm/mach-s3c64xx/clock.c b/arch/arm/mach-s3c64xx/clock.c
new file mode 100644
index 00000000000..229bb3bcc54
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/clock.c
@@ -0,0 +1,306 @@
+/* linux/arch/arm/plat-s3c64xx/clock.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX Base clock 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/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <mach/regs-sys.h>
+#include <mach/regs-clock.h>
+#include <mach/pll.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+struct clk clk_h2 = {
+ .name = "hclk2",
+ .id = -1,
+ .rate = 0,
+};
+
+struct clk clk_27m = {
+ .name = "clk_27m",
+ .id = -1,
+ .rate = 27000000,
+};
+
+static int clk_48m_ctrl(struct clk *clk, int enable)
+{
+ unsigned long flags;
+ u32 val;
+
+ /* can't rely on clock lock, this register has other usages */
+ local_irq_save(flags);
+
+ val = __raw_readl(S3C64XX_OTHERS);
+ if (enable)
+ val |= S3C64XX_OTHERS_USBMASK;
+ else
+ val &= ~S3C64XX_OTHERS_USBMASK;
+
+ __raw_writel(val, S3C64XX_OTHERS);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+struct clk clk_48m = {
+ .name = "clk_48m",
+ .id = -1,
+ .rate = 48000000,
+ .enable = clk_48m_ctrl,
+};
+
+static int inline s3c64xx_gate(void __iomem *reg,
+ struct clk *clk,
+ int enable)
+{
+ unsigned int ctrlbit = clk->ctrlbit;
+ u32 con;
+
+ con = __raw_readl(reg);
+
+ if (enable)
+ con |= ctrlbit;
+ else
+ con &= ~ctrlbit;
+
+ __raw_writel(con, reg);
+ return 0;
+}
+
+static int s3c64xx_pclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_PCLK_GATE, clk, enable);
+}
+
+static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
+}
+
+int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
+}
+
+static struct clk init_clocks_disable[] = {
+ {
+ .name = "nand",
+ .id = -1,
+ .parent = &clk_h,
+ }, {
+ .name = "adc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_TSADC,
+ }, {
+ .name = "i2c",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIC,
+ }, {
+ .name = "iis",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIS0,
+ }, {
+ .name = "iis",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIS1,
+ }, {
+ .name = "spi",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_SPI0,
+ }, {
+ .name = "spi",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_SPI1,
+ }, {
+ .name = "spi_48m",
+ .id = 0,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_SPI0_48,
+ }, {
+ .name = "spi_48m",
+ .id = 1,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_SPI1_48,
+ }, {
+ .name = "48m",
+ .id = 0,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC0_48,
+ }, {
+ .name = "48m",
+ .id = 1,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC1_48,
+ }, {
+ .name = "48m",
+ .id = 2,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC2_48,
+ }, {
+ .name = "dma0",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_DMA0,
+ }, {
+ .name = "dma1",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_DMA1,
+ },
+};
+
+static struct clk init_clocks[] = {
+ {
+ .name = "lcd",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_LCD,
+ }, {
+ .name = "gpio",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_GPIO,
+ }, {
+ .name = "usb-host",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_UHOST,
+ }, {
+ .name = "hsmmc",
+ .id = 0,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC0,
+ }, {
+ .name = "hsmmc",
+ .id = 1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC1,
+ }, {
+ .name = "hsmmc",
+ .id = 2,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC2,
+ }, {
+ .name = "timers",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_PWM,
+ }, {
+ .name = "uart",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART0,
+ }, {
+ .name = "uart",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART1,
+ }, {
+ .name = "uart",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART2,
+ }, {
+ .name = "uart",
+ .id = 3,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART3,
+ }, {
+ .name = "rtc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_RTC,
+ }, {
+ .name = "watchdog",
+ .id = -1,
+ .parent = &clk_p,
+ .ctrlbit = S3C_CLKCON_PCLK_WDT,
+ }, {
+ .name = "ac97",
+ .id = -1,
+ .parent = &clk_p,
+ .ctrlbit = S3C_CLKCON_PCLK_AC97,
+ }
+};
+
+static struct clk *clks[] __initdata = {
+ &clk_ext,
+ &clk_epll,
+ &clk_27m,
+ &clk_48m,
+ &clk_h2,
+};
+
+void __init s3c64xx_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
+ s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
+
+ clkp = init_clocks_disable;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
+
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+
+ (clkp->enable)(clkp, 0);
+ }
+
+ s3c_pwmclk_init();
+}
diff --git a/arch/arm/mach-s3c64xx/cpu.c b/arch/arm/mach-s3c64xx/cpu.c
new file mode 100644
index 00000000000..410d688a691
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/cpu.c
@@ -0,0 +1,161 @@
+/* linux/arch/arm/plat-s3c64xx/cpu.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX CPU 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/sysdev.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/regs-serial.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+#include <mach/s3c6400.h>
+#include <mach/s3c6410.h>
+
+/* table of supported CPUs */
+
+static const char name_s3c6400[] = "S3C6400";
+static const char name_s3c6410[] = "S3C6410";
+
+static struct cpu_table cpu_ids[] __initdata = {
+ {
+ .idcode = 0x36400000,
+ .idmask = 0xfffff000,
+ .map_io = s3c6400_map_io,
+ .init_clocks = s3c6400_init_clocks,
+ .init_uarts = s3c6400_init_uarts,
+ .init = s3c6400_init,
+ .name = name_s3c6400,
+ }, {
+ .idcode = 0x36410100,
+ .idmask = 0xffffff00,
+ .map_io = s3c6410_map_io,
+ .init_clocks = s3c6410_init_clocks,
+ .init_uarts = s3c6410_init_uarts,
+ .init = s3c6410_init,
+ .name = name_s3c6410,
+ },
+};
+
+/* minimal IO mapping */
+
+/* see notes on uart map in arch/arm/mach-s3c6400/include/mach/debug-macro.S */
+#define UART_OFFS (S3C_PA_UART & 0xfffff)
+
+static struct map_desc s3c_iodesc[] __initdata = {
+ {
+ .virtual = (unsigned long)S3C_VA_SYS,
+ .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_MEM,
+ .pfn = __phys_to_pfn(S3C64XX_PA_SROM),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS),
+ .pfn = __phys_to_pfn(S3C_PA_UART),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)VA_VIC0,
+ .pfn = __phys_to_pfn(S3C64XX_PA_VIC0),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)VA_VIC1,
+ .pfn = __phys_to_pfn(S3C64XX_PA_VIC1),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_TIMER,
+ .pfn = __phys_to_pfn(S3C_PA_TIMER),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C64XX_VA_GPIO,
+ .pfn = __phys_to_pfn(S3C64XX_PA_GPIO),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C64XX_VA_MODEM,
+ .pfn = __phys_to_pfn(S3C64XX_PA_MODEM),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_WATCHDOG,
+ .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_USB_HSPHY,
+ .pfn = __phys_to_pfn(S3C64XX_PA_USB_HSPHY),
+ .length = SZ_1K,
+ .type = MT_DEVICE,
+ },
+};
+
+
+struct sysdev_class s3c64xx_sysclass = {
+ .name = "s3c64xx-core",
+};
+
+static struct sys_device s3c64xx_sysdev = {
+ .cls = &s3c64xx_sysclass,
+};
+
+
+/* read cpu identification code */
+
+void __init s3c64xx_init_io(struct map_desc *mach_desc, int size)
+{
+ unsigned long idcode;
+
+ /* initialise the io descriptors we need for initialisation */
+ iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
+ iotable_init(mach_desc, size);
+
+ idcode = __raw_readl(S3C_VA_SYS + 0x118);
+ if (!idcode) {
+ /* S3C6400 has the ID register in a different place,
+ * and needs a write before it can be read. */
+
+ __raw_writel(0x0, S3C_VA_SYS + 0xA1C);
+ idcode = __raw_readl(S3C_VA_SYS + 0xA1C);
+ }
+
+ s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
+}
+
+static __init int s3c64xx_sysdev_init(void)
+{
+ sysdev_class_register(&s3c64xx_sysclass);
+ return sysdev_register(&s3c64xx_sysdev);
+}
+
+core_initcall(s3c64xx_sysdev_init);
diff --git a/arch/arm/mach-s3c64xx/cpufreq.c b/arch/arm/mach-s3c64xx/cpufreq.c
new file mode 100644
index 00000000000..74c0e8347de
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/cpufreq.c
@@ -0,0 +1,270 @@
+/* linux/arch/arm/plat-s3c64xx/cpufreq.c
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * S3C64xx CPUfreq 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/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+
+static struct clk *armclk;
+static struct regulator *vddarm;
+static unsigned long regulator_latency;
+
+#ifdef CONFIG_CPU_S3C6410
+struct s3c64xx_dvfs {
+ unsigned int vddarm_min;
+ unsigned int vddarm_max;
+};
+
+static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
+ [0] = { 1000000, 1150000 },
+ [1] = { 1050000, 1150000 },
+ [2] = { 1100000, 1150000 },
+ [3] = { 1200000, 1350000 },
+};
+
+static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
+ { 0, 66000 },
+ { 0, 133000 },
+ { 1, 222000 },
+ { 1, 266000 },
+ { 2, 333000 },
+ { 2, 400000 },
+ { 2, 532000 },
+ { 2, 533000 },
+ { 3, 667000 },
+ { 0, CPUFREQ_TABLE_END },
+};
+#endif
+
+static int s3c64xx_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, s3c64xx_freq_table);
+}
+
+static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu)
+{
+ if (cpu != 0)
+ return 0;
+
+ return clk_get_rate(armclk) / 1000;
+}
+
+static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ int ret;
+ unsigned int i;
+ struct cpufreq_freqs freqs;
+ struct s3c64xx_dvfs *dvfs;
+
+ ret = cpufreq_frequency_table_target(policy, s3c64xx_freq_table,
+ target_freq, relation, &i);
+ if (ret != 0)
+ return ret;
+
+ freqs.cpu = 0;
+ freqs.old = clk_get_rate(armclk) / 1000;
+ freqs.new = s3c64xx_freq_table[i].frequency;
+ freqs.flags = 0;
+ dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index];
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+#ifdef CONFIG_REGULATOR
+ if (vddarm && freqs.new > freqs.old) {
+ ret = regulator_set_voltage(vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
+ freqs.new, ret);
+ goto err;
+ }
+ }
+#endif
+
+ ret = clk_set_rate(armclk, freqs.new * 1000);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to set rate %dkHz: %d\n",
+ freqs.new, ret);
+ goto err;
+ }
+
+#ifdef CONFIG_REGULATOR
+ if (vddarm && freqs.new < freqs.old) {
+ ret = regulator_set_voltage(vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n",
+ freqs.new, ret);
+ goto err_clk;
+ }
+ }
+#endif
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ pr_debug("cpufreq: Set actual frequency %lukHz\n",
+ clk_get_rate(armclk) / 1000);
+
+ return 0;
+
+err_clk:
+ if (clk_set_rate(armclk, freqs.old * 1000) < 0)
+ pr_err("Failed to restore original clock rate\n");
+err:
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return ret;
+}
+
+#ifdef CONFIG_REGULATOR
+static void __init s3c64xx_cpufreq_config_regulator(void)
+{
+ int count, v, i, found;
+ struct cpufreq_frequency_table *freq;
+ struct s3c64xx_dvfs *dvfs;
+
+ count = regulator_count_voltages(vddarm);
+ if (count < 0) {
+ pr_err("cpufreq: Unable to check supported voltages\n");
+ }
+
+ freq = s3c64xx_freq_table;
+ while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
+ if (freq->frequency == CPUFREQ_ENTRY_INVALID)
+ continue;
+
+ dvfs = &s3c64xx_dvfs_table[freq->index];
+ found = 0;
+
+ for (i = 0; i < count; i++) {
+ v = regulator_list_voltage(vddarm, i);
+ if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
+ found = 1;
+ }
+
+ if (!found) {
+ pr_debug("cpufreq: %dkHz unsupported by regulator\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ freq++;
+ }
+
+ /* Guess based on having to do an I2C/SPI write; in future we
+ * will be able to query the regulator performance here. */
+ regulator_latency = 1 * 1000 * 1000;
+}
+#endif
+
+static int __init s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+ int ret;
+ struct cpufreq_frequency_table *freq;
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ if (s3c64xx_freq_table == NULL) {
+ pr_err("cpufreq: No frequency information for this CPU\n");
+ return -ENODEV;
+ }
+
+ armclk = clk_get(NULL, "armclk");
+ if (IS_ERR(armclk)) {
+ pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n",
+ PTR_ERR(armclk));
+ return PTR_ERR(armclk);
+ }
+
+#ifdef CONFIG_REGULATOR
+ vddarm = regulator_get(NULL, "vddarm");
+ if (IS_ERR(vddarm)) {
+ ret = PTR_ERR(vddarm);
+ pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
+ pr_err("cpufreq: Only frequency scaling available\n");
+ vddarm = NULL;
+ } else {
+ s3c64xx_cpufreq_config_regulator();
+ }
+#endif
+
+ freq = s3c64xx_freq_table;
+ while (freq->frequency != CPUFREQ_TABLE_END) {
+ unsigned long r;
+
+ /* Check for frequencies we can generate */
+ r = clk_round_rate(armclk, freq->frequency * 1000);
+ r /= 1000;
+ if (r != freq->frequency) {
+ pr_debug("cpufreq: %dkHz unsupported by clock\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ /* If we have no regulator then assume startup
+ * frequency is the maximum we can support. */
+ if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0))
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+
+ freq++;
+ }
+
+ policy->cur = clk_get_rate(armclk) / 1000;
+
+ /* Datasheet says PLL stabalisation time (if we were to use
+ * the PLLs, which we don't currently) is ~300us worst case,
+ * but add some fudge.
+ */
+ policy->cpuinfo.transition_latency = (500 * 1000) + regulator_latency;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to configure frequency table: %d\n",
+ ret);
+ regulator_put(vddarm);
+ clk_put(armclk);
+ }
+
+ return ret;
+}
+
+static struct cpufreq_driver s3c64xx_cpufreq_driver = {
+ .owner = THIS_MODULE,
+ .flags = 0,
+ .verify = s3c64xx_cpufreq_verify_speed,
+ .target = s3c64xx_cpufreq_set_target,
+ .get = s3c64xx_cpufreq_get_speed,
+ .init = s3c64xx_cpufreq_driver_init,
+ .name = "s3c",
+};
+
+static int __init s3c64xx_cpufreq_init(void)
+{
+ return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
+}
+module_init(s3c64xx_cpufreq_init);
diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c
new file mode 100644
index 00000000000..0e0edf75e8e
--- /dev/null
+++ b/arch/arm/mach-s3c64xx/dma.c
@@ -0,0 +1,750 @@
+/* linux/arch/arm/plat-s3c64xx/dma.c
+ *
+ * Copyright 2009 Openmoko, Inc.
+ * Copyright 2009 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX DMA core
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/dmapool.h>
+#include <linux/sysdev.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/dma.h>
+#include <mach/map.h>
+#include <mach/irqs.h>
+
+#include <plat/dma-plat.h>
+#include <mach/regs-sys.h>
+
+#include <asm/hardware/pl080.h>
+
+/* dma channel state information */
+
+struct s3c64xx_dmac {
+ struct sys_device sysdev;
+ struct clk *clk;
+ void __iomem *regs;
+ struct s3c2410_dma_chan *channels;
+ enum dma_ch chanbase;
+};
+
+/* pool to provide LLI buffers */
+static struct dma_pool *dma_pool;
+
+/* Debug configuration and code */
+
+static unsigned char debug_show_buffs = 0;
+
+static void dbg_showchan(struct s3c2410_dma_chan *chan)
+{
+ pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n",
+ chan->number,
+ readl(chan->regs + PL080_CH_SRC_ADDR),
+ readl(chan->regs + PL080_CH_DST_ADDR),
+ readl(chan->regs + PL080_CH_LLI),
+ readl(chan->regs + PL080_CH_CONTROL),
+ readl(chan->regs + PL080S_CH_CONTROL2),
+ readl(chan->regs + PL080S_CH_CONFIG));
+}
+
+static void show_lli(struct pl080s_lli *lli)
+{
+ pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n",
+ lli, lli->src_addr, lli->dst_addr, lli->next_lli,
+ lli->control0, lli->control1);
+}
+
+static void dbg_showbuffs(struct s3c2410_dma_chan *chan)
+{
+ struct s3c64xx_dma_buff *ptr;
+ struct s3c64xx_dma_buff *end;
+
+ pr_debug("DMA%d: buffs next %p, curr %p, end %p\n",
+ chan->number, chan->next, chan->curr, chan->end);
+
+ ptr = chan->next;
+ end = chan->end;
+
+ if (debug_show_buffs) {
+ for (; ptr != NULL; ptr = ptr->next) {
+ pr_debug("DMA%d: %08x ",
+ chan->number, ptr->lli_dma);
+ show_lli(ptr->lli);
+ }
+ }
+}
+
+/* End of Debug */
+
+static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel)
+{
+ struct s3c2410_dma_chan *chan;
+ unsigned int start, offs;
+
+ start = 0;
+
+ if (channel >= DMACH_PCM1_TX)
+ start = 8;
+
+ for (offs = 0; offs < 8; offs++) {
+ chan = &s3c2410_chans[start + offs];
+ if (!chan->in_use)
+ goto found;
+ }
+
+ return NULL;
+
+found:
+ s3c_dma_chan_map[channel] = chan;
+ return chan;
+}
+
+int s3c2410_dma_config(unsigned int channel, int xferunit)
+{
+ struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
+
+ if (chan == NULL)
+ return -EINVAL;
+
+ switch (xferunit) {
+ case 1:
+ chan->hw_width = 0;
+ break;
+ case 2:
+ chan->hw_width = 1;
+ break;
+ case 4:
+ chan->hw_width = 2;
+ break;
+ default:
+ printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(s3c2410_dma_config);
+
+static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
+ struct pl080s_lli *lli,
+ dma_addr_t data, int size)
+{
+ dma_addr_t src, dst;
+ u32 control0, control1;
+
+ switch (chan->source) {
+ case S3C2410_DMASRC_HW:
+ src = chan->dev_addr;
+ dst = data;
+ control0 = PL080_CONTROL_SRC_AHB2;
+ control0 |= PL080_CONTROL_DST_INCR;
+ break;
+
+ case S3C2410_DMASRC_MEM:
+ src = data;
+ dst = chan->dev_addr;
+ control0 = PL080_CONTROL_DST_AHB2;
+ control0 |= PL080_CONTROL_SRC_INCR;
+ break;
+ default:
+ BUG();
+ }
+
+ /* note, we do not currently setup any of the burst controls */
+
+ control1 = size >> chan->hw_width; /* size in no of xfers */
+ control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */
+ control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */
+ control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT;
+ control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT;
+
+ lli->src_addr = src;
+ lli->dst_addr = dst;
+ lli->next_lli = 0;
+ lli->control0 = control0;
+ lli->control1 = control1;
+}
+
+static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan,
+ struct pl080s_lli *lli)
+{
+ void __iomem *regs = chan->regs;
+
+ pr_debug("%s: LLI %p => regs\n", __func__, lli);
+ show_lli(lli);
+
+ writel(lli->src_addr, regs + PL080_CH_SRC_ADDR);
+ writel(lli->dst_addr, regs + PL080_CH_DST_ADDR);
+ writel(lli->next_lli, regs + PL080_CH_LLI);
+ writel(lli->control0, regs + PL080_CH_CONTROL);
+ writel(lli->control1, regs + PL080S_CH_CONTROL2);
+}
+
+static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan)
+{
+ struct s3c64xx_dmac *dmac = chan->dmac;
+ u32 config;
+ u32 bit = chan->bit;
+
+ dbg_showchan(chan);
+
+ pr_debug("%s: clearing interrupts\n", __func__);
+
+ /* clear interrupts */
+ writel(bit, dmac->regs + PL080_TC_CLEAR);
+ writel(bit, dmac->regs + PL080_ERR_CLEAR);
+
+ pr_debug("%s: starting channel\n", __func__);
+
+ config = readl(chan->regs + PL080S_CH_CONFIG);
+ config |= PL080_CONFIG_ENABLE;
+
+ pr_debug("%s: writing config %08x\n", __func__, config);
+ writel(config, chan->regs + PL080S_CH_CONFIG);
+
+ return 0;
+}
+
+static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan)
+{
+ u32 config;
+ int timeout;
+
+ pr_debug("%s: stopping channel\n", __func__);
+
+ dbg_showchan(chan);
+
+ config = readl(chan->regs + PL080S_CH_CONFIG);
+ config |= PL080_CONFIG_HALT;
+ writel(config, chan->regs + PL080S_CH_CONFIG);
+
+ timeout = 1000;
+ do {
+ config = readl(chan->regs + PL080S_CH_CONFIG);
+ pr_debug("%s: %d - config %08x\n", __func__, timeout, config);
+ if (config & PL080_CONFIG_ACTIVE)
+ udelay(10);
+ else
+ break;
+ } while (--timeout > 0);
+
+ if (config & PL080_CONFIG_ACTIVE) {
+ printk(KERN_ERR "%s: channel still active\n", __func__);
+ return -EFAULT;
+ }
+
+ config = readl(chan->regs + PL080S_CH_CONFIG);
+ config &= ~PL080_CONFIG_ENABLE;
+ writel(config, chan->regs + PL080S_CH_CONFIG);
+
+ return 0;
+}
+
+static inline void s3c64xx_dma_bufffdone(struct s3c2410_dma_chan *chan,
+ struct s3c64xx_dma_buff *buf,
+ enum s3c2410_dma_buffresult result)
+{
+ if (chan->callback_fn != NULL)
+ (chan->callback_fn)(chan, buf->pw, 0, result);
+}
+
+static void s3c64xx_dma_freebuf