diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-02-11 18:31:01 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-11 17:36:09 +0000 |
commit | a21765a70ec06be175d3997320a83fa66fcc8955 (patch) | |
tree | 24bdbf437a9bd5b7c1af05898f5aa25dccf67fe9 /arch/arm | |
parent | d19494b187b20e363f9b434b9ceab4159ac88324 (diff) | |
download | linux-a21765a70ec06be175d3997320a83fa66fcc8955.tar.gz linux-a21765a70ec06be175d3997320a83fa66fcc8955.tar.bz2 linux-a21765a70ec06be175d3997320a83fa66fcc8955.zip |
[ARM] 4157/2: S3C24XX: move arch/arch/mach-s3c2410 into cpu components
The following patch and script moves the arch/arm/mach-s3c2410
directory into arch/arm/plat-s3c24xx for the generic core code
and inti arch/arm/mach-s3c{cpu} for the cpu/machine support files
Include directory include/asm-arm/plat-s3c24xx is added for the
core include files.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
83 files changed, 4771 insertions, 5225 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6783c2e5512d..51ee13e7c74c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -363,7 +363,15 @@ source "arch/arm/mach-omap1/Kconfig" source "arch/arm/mach-omap2/Kconfig" +source "arch/arm/plat-s3c24xx/Kconfig" + +if ARCH_S3C2410 +source "arch/arm/mach-s3c2400/Kconfig" source "arch/arm/mach-s3c2410/Kconfig" +source "arch/arm/mach-s3c2412/Kconfig" +source "arch/arm/mach-s3c2440/Kconfig" +source "arch/arm/mach-s3c2442/Kconfig" +endif source "arch/arm/mach-lh7a40x/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 000f1100b553..2df1ea0a0c2d 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -149,7 +149,7 @@ MACHINE := arch/arm/mach-$(machine-y)/ else MACHINE := endif - + export TEXT_OFFSET GZFLAGS MMUEXT # Do we have FASTFPE? @@ -161,6 +161,10 @@ endif # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ core-y += $(MACHINE) +core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2400/ +core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2412/ +core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2440/ +core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2442/ core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/ core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) core-$(CONFIG_VFP) += arch/arm/vfp/ @@ -168,6 +172,7 @@ core-$(CONFIG_VFP) += arch/arm/vfp/ # If we have a common platform directory, then include it in the build. core-$(CONFIG_PLAT_IOP) += arch/arm/plat-iop/ core-$(CONFIG_ARCH_OMAP) += arch/arm/plat-omap/ +core-$(CONFIG_PLAT_S3C24XX) += arch/arm/plat-s3c24xx/ drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ drivers-$(CONFIG_ARCH_CLPS7500) += drivers/acorn/char/ diff --git a/arch/arm/mach-s3c2400/Kconfig b/arch/arm/mach-s3c2400/Kconfig new file mode 100644 index 000000000000..deab0722836e --- /dev/null +++ b/arch/arm/mach-s3c2400/Kconfig @@ -0,0 +1,13 @@ +# arch/arm/mach-s3c2400/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + + + +menu "S3C2400 Machines" + + +endmenu + diff --git a/arch/arm/mach-s3c2400/Makefile b/arch/arm/mach-s3c2400/Makefile new file mode 100644 index 000000000000..7e23f4e13766 --- /dev/null +++ b/arch/arm/mach-s3c2400/Makefile @@ -0,0 +1,15 @@ +# arch/arm/mach-s3c2400/Makefile +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_CPU_S3C2400) += gpio.o + +# Machine support + diff --git a/arch/arm/mach-s3c2410/s3c2400-gpio.c b/arch/arm/mach-s3c2400/gpio.c index 1576d01d5f82..758e160410e9 100644 --- a/arch/arm/mach-s3c2410/s3c2400-gpio.c +++ b/arch/arm/mach-s3c2400/gpio.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2400-gpio.c +/* linux/arch/arm/mach-s3c2400/gpio.c * * Copyright (c) 2006 Lucas Correia Villa Real <lucasvr@gobolinux.org> * diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 0ab590ec4582..016b53f5d6fc 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -1,54 +1,51 @@ -if ARCH_S3C2410 +# arch/arm/mach-s3c2410/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 -menu "S3C24XX Implementations" +config CPU_S3C2410 + bool + depends on ARCH_S3C2410 + select S3C2410_CLOCK + select S3C2410_GPIO + select S3C2410_PM if PM + help + Support for S3C2410 and S3C2410A family from the S3C24XX line + of Samsung Mobile CPUs. -config MACH_AML_M5900 - bool "AML M5900 Series" - select CPU_S3C2410 - select PM_SIMTEC if PM +config CPU_S3C2410_DMA + bool + depends on S3C2410_DMA && (CPU_S3C2410 || CPU_S3C2442) + default y if CPU_S3C2410 || CPU_S3C2442 help - Say Y here if you are using the American Microsystems M5900 Series - <http://www.amltd.com> + DMA device selection for S3C2410 and compatible CPUs -config MACH_ANUBIS - bool "Simtec Electronics ANUBIS" - select CPU_S3C2440 - select PM_SIMTEC if PM +config S3C2410_PM + bool help - Say Y here if you are using the Simtec Electronics ANUBIS - development system + Power Management code common to S3C2410 and better -config MACH_OSIRIS - bool "Simtec IM2440D20 (OSIRIS) module" - select CPU_S3C2440 - select PM_SIMTEC if PM +config S3C2410_GPIO + bool help - Say Y here if you are using the Simtec IM2440D20 module, also - known as the Osiris. + GPIO code for S3C2410 and similar processors -config ARCH_BAST - bool "Simtec Electronics BAST (EB2410ITX)" - select CPU_S3C2410 - select PM_SIMTEC if PM - select ISA +config S3C2410_CLOCK + bool help - Say Y here if you are using the Simtec Electronics EB2410ITX - development board (also known as BAST) + Clock code for the S3C2410, and similar processors - Product page: <http://www.simtec.co.uk/products/EB2410ITX/>. -config BAST_PC104_IRQ - bool "BAST PC104 IRQ support" - depends on ARCH_BAST - default y - help - Say Y here to enable the PC104 IRQ routing on the - Simtec BAST (EB2410ITX) +menu "S3C2410 Machines" -config PM_H1940 - bool +config ARCH_SMDK2410 + bool "SMDK2410/A9M2410" + select CPU_S3C2410 + select MACH_SMDK help - Internal node for H1940 and related PM + Say Y here if you are using the SMDK2410 or the derived module A9M2410 + <http://www.fsforth.de> config ARCH_H1940 bool "IPAQ H1940" @@ -57,7 +54,10 @@ config ARCH_H1940 help Say Y here if you are using the HP IPAQ H1940 - <http://www.handhelds.org/projects/h1940.html>. +config PM_H1940 + bool + help + Internal node for H1940 and related PM config MACH_N30 bool "Acer N30" @@ -65,53 +65,36 @@ config MACH_N30 help Say Y here if you are using the Acer N30 - <http://zoo.weinigel.se/n30>. - -config MACH_SMDK - bool - help - Common machine code for SMDK2410 and SMDK2440 - -config ARCH_SMDK2410 - bool "SMDK2410/A9M2410" +config ARCH_BAST + bool "Simtec Electronics BAST (EB2410ITX)" select CPU_S3C2410 - select MACH_SMDK + select PM_SIMTEC if PM + select ISA help - Say Y here if you are using the SMDK2410 or the derived module A9M2410 - <http://www.fsforth.de> + Say Y here if you are using the Simtec Electronics EB2410ITX + development board (also known as BAST) -config ARCH_S3C2440 - bool "SMDK2440" - select CPU_S3C2440 - select MACH_SMDK +config MACH_OTOM + bool "NexVision OTOM Board" + select CPU_S3C2410 help - Say Y here if you are using the SMDK2440. - -config SMDK2440_CPU2440 - bool "SMDK2440 with S3C2440 CPU module" - depends on ARCH_S3C2440 - default y if ARCH_S3C2440 - select CPU_S3C2440 - -config SMDK2440_CPU2442 - bool "SMDM2440 with S3C2442 CPU module" - depends on ARCH_S3C2440 - select CPU_S3C2442 + Say Y here if you are using the Nex Vision OTOM board -config MACH_S3C2413 - bool +config MACH_AML_M5900 + bool "AML M5900 Series" + select CPU_S3C2410 + select PM_SIMTEC if PM help - Internal node for S3C2413 version of SMDK2413, so that - machine_is_s3c2413() will work when MACH_SMDK2413 is - selected + Say Y here if you are using the American Microsystems M5900 Series + <http://www.amltd.com> -config MACH_SMDK2413 - bool "SMDK2413" - select CPU_S3C2412 - select MACH_S3C2413 - select MACH_SMDK +config BAST_PC104_IRQ + bool "BAST PC104 IRQ support" + depends on ARCH_BAST + default y help - Say Y here if you are using an SMDK2413 + Say Y here to enable the PC104 IRQ routing on the + Simtec BAST (EB2410ITX) config MACH_VR1000 bool "Thorcom VR1000" @@ -120,223 +103,6 @@ config MACH_VR1000 help Say Y here if you are using the Thorcom VR1000 board. - This linux port is currently being maintained by Simtec, on behalf - of Thorcom. Any queries, please contact Thorcom first. - -config MACH_RX3715 - bool "HP iPAQ rx3715" - select CPU_S3C2440 - select PM_H1940 if PM - help - Say Y here if you are using the HP iPAQ rx3715. - - See <http://www.handhelds.org/projects/rx3715.html> for more - information on this project - -config MACH_OTOM - bool "NexVision OTOM Board" - select CPU_S3C2410 - help - Say Y here if you are using the Nex Vision OTOM board - -config MACH_NEXCODER_2440 - bool "NexVision NEXCODER 2440 Light Board" - select CPU_S3C2440 - help - Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board - -config MACH_VSTMS - bool "VMSTMS" - select CPU_S3C2412 - help - Say Y here if you are using an VSTMS board endmenu -config S3C2410_CLOCK - bool - help - Clock code for the S3C2410, and similar processors - -config S3C2410_GPIO - bool - help - GPIO code for S3C2410 and similar processors - -config S3C2410_PM - bool - help - Power Management code common to S3C2410 and better - -config CPU_S3C2410_DMA - bool - depends on S3C2410_DMA && (CPU_S3C2410 || CPU_S3C2442) - default y if CPU_S3C2410 || CPU_S3C2442 - help - DMA device selection for S3C2410 and compatible CPUs - -config CPU_S3C2410 - bool - depends on ARCH_S3C2410 - select S3C2410_CLOCK - select S3C2410_GPIO - select S3C2410_PM if PM - help - Support for S3C2410 and S3C2410A family from the S3C24XX line - of Samsung Mobile CPUs. - -# internal node to signify if we are only dealing with an S3C2412 - -config CPU_S3C2412_ONLY - bool - depends on ARCH_S3C2410 && !CPU_S3C2400 && !CPU_S3C2410 && \ - !CPU_S3C2440 && !CPU_S3C2442 && CPU_S3C2412 - default y if CPU_S3C2412 - -config S3C2412_PM - bool - help - Internal config node to apply S3C2412 power management - -config S3C2412_DMA - bool - depends on CPU_S3C2412 - help - Internal config node for S3C2412 DMA support - -config CPU_S3C2412 - bool - depends on ARCH_S3C2410 - select S3C2412_PM if PM - select S3C2412_DMA if S3C2410_DMA - help - Support for the S3C2412 and S3C2413 SoCs from the S3C24XX line - -config CPU_S3C244X - bool - depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) - help - Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. - -config S3C2440_DMA - bool - depends on ARCH_S3C2410 && CPU_S3C24405B - help - Support for S3C2440 specific DMA code5A - -config CPU_S3C2440 - bool - depends on ARCH_S3C2410 - select S3C2410_CLOCK - select S3C2410_PM if PM - select S3C2410_GPIO - select S3C2440_DMA if S3C2410_DMA - select CPU_S3C244X - help - Support for S3C2440 Samsung Mobile CPU based systems. - -config CPU_S3C2442 - bool - depends on ARCH_S3C2420 - select S3C2410_CLOCK - select S3C2410_GPIO - select S3C2410_PM if PM - select CPU_S3C244X - help - Support for S3C2442 Samsung Mobile CPU based systems. - -comment "S3C2410 Boot" - -config S3C2410_BOOT_WATCHDOG - bool "S3C2410 Initialisation watchdog" - depends on ARCH_S3C2410 && S3C2410_WATCHDOG - help - Say y to enable the watchdog during the kernel decompression - stage. If the kernel fails to uncompress, then the watchdog - will trigger a reset and the system should restart. - - Although this uses the same hardware unit as the kernel watchdog - driver, it is not a replacement for it. If you use this option, - you will have to use the watchdg driver to either stop the timeout - or restart it. If you do not, then your kernel will reboot after - startup. - - The driver uses a fixed timeout value, so the exact time till the - system resets depends on the value of PCLK. The timeout on an - 200MHz s3c2410 should be about 30 seconds. - -config S3C2410_BOOT_ERROR_RESET - bool "S3C2410 Reboot on decompression error" - depends on ARCH_S3C2410 - help - Say y here to use the watchdog to reset the system if the - kernel decompressor detects an error during decompression. - - -comment "S3C2410 Setup" - -config S3C2410_DMA - bool "S3C2410 DMA support" - depends on ARCH_S3C2410 - 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_S3C2410 && S3C2410_DMA - help - Enable debugging output for the DMA code. This option sends info - to the kernel log, at priority KERN_DEBUG. - - Note, it is easy to create and fill the log buffer in a small - amount of time, as well as using an significant percentage of - the CPU time doing so. - -config S3C2410_PM_DEBUG - bool "S3C2410 PM Suspend debug" - depends on ARCH_S3C2410 && PM - help - Say Y here if you want verbose debugging from the PM Suspend and - Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> - for more information. - -config S3C2410_PM_CHECK - bool "S3C2410 PM Suspend Memory CRC" - depends on ARCH_S3C2410 && PM && CRC32 - help - Enable the PM code's memory area checksum over sleep. This option - will generate CRCs of all blocks of memory, and store them before - going to sleep. The blocks are then checked on resume for any - errors. - -config S3C2410_PM_CHECK_CHUNKSIZE - int "S3C2410 PM Suspend CRC Chunksize (KiB)" - depends on ARCH_S3C2410 && PM && S3C2410_PM_CHECK - default 64 - help - Set the chunksize in Kilobytes of the CRC for checking memory - corruption over suspend and resume. A smaller value will mean that - the CRC data block will take more memory, but wil identify any - faults with better precision. - -config PM_SIMTEC - bool - help - Common power management code for systems that are - compatible with the Simtec style of power management - -config S3C2410_LOWLEVEL_UART_PORT - int "S3C2410 UART to use for low-level messages" - default 0 - help - Choice of which UART port to use for the low-level messages, - such as the `Uncompressing...` at start time. The value of - this configuration should be between zero and two. The port - must have been initialised by the boot-loader before use. - - Note, this does not affect the port used by the debug messages, - which is a separate configuration. - -endif diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 1cc5febdaabd..b33ac5b84773 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -1,85 +1,30 @@ - +# arch/arm/mach-s3c2410/Makefile # -# Makefile for the linux kernel. +# Copyright 2007 Simtec Electronics # +# Licensed under GPLv2 -# Object file lists. - -obj-y := cpu.o irq.o time.o gpio.o clock.o devs.o -obj-m := -obj-n := -obj- := - -# DMA -obj-$(CONFIG_S3C2410_DMA) += dma.o - -# S3C2400 support files -obj-$(CONFIG_CPU_S3C2400) += s3c2400-gpio.o - -# S3C2410 support files +obj-y := +obj-m := +obj-n := +obj- := obj-$(CONFIG_CPU_S3C2410) += s3c2410.o -obj-$(CONFIG_CPU_S3C2410) += s3c2410-irq.o - -obj-$(CONFIG_S3C2410_PM) += s3c2410-pm.o s3c2410-sleep.o -obj-$(CONFIG_S3C2410_GPIO) += s3c2410-gpio.o -obj-$(CONFIG_CPU_S3C2410_DMA) += s3c2410-dma.o - -# Power Management support - -obj-$(CONFIG_PM) += pm.o sleep.o -obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o -obj-$(CONFIG_PM_H1940) += pm-h1940.o - -# S3C2412 support -obj-$(CONFIG_CPU_S3C2412) += s3c2412.o -obj-$(CONFIG_CPU_S3C2412) += s3c2412-irq.o -obj-$(CONFIG_CPU_S3C2412) += s3c2412-clock.o - -obj-$(CONFIG_S3C2412_PM) += s3c2412-pm.o -obj-$(CONFIG_S3C2412_DMA) += s3c2412-dma.o - -# -# S3C244X support - -obj-$(CONFIG_CPU_S3C244X) += s3c244x.o -obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o - -# Clock control - -obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o - -# S3C2440 support +obj-$(CONFIG_CPU_S3C2410) += irq.o +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_CLOCK) += clock.o -obj-$(CONFIG_CPU_S3C2440) += s3c2440.o s3c2440-dsc.o -obj-$(CONFIG_CPU_S3C2440) += s3c2440-irq.o -obj-$(CONFIG_CPU_S3C2440) += s3c2440-clock.o -obj-$(CONFIG_S3C2440_DMA) += s3c2440-dma.o +# Machine support -# S3C2442 support - -obj-$(CONFIG_CPU_S3C2442) += s3c2442.o -obj-$(CONFIG_CPU_S3C2442) += s3c2442-clock.o - -# bast extras - -obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o - -# machine specific support - -obj-$(CONFIG_MACH_AML_M5900) += mach-amlm5900.o -obj-$(CONFIG_MACH_ANUBIS) += mach-anubis.o -obj-$(CONFIG_MACH_OSIRIS) += mach-osiris.o -obj-$(CONFIG_ARCH_BAST) += mach-bast.o usb-simtec.o +obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.o obj-$(CONFIG_ARCH_H1940) += mach-h1940.o +obj-$(CONFIG_PM_H1940) += pm-h1940.o obj-$(CONFIG_MACH_N30) += mach-n30.o -obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.o -obj-$(CONFIG_MACH_SMDK2413) += mach-smdk2413.o -obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o -obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o -obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o +obj-$(CONFIG_ARCH_BAST) += mach-bast.o usb-simtec.o obj-$(CONFIG_MACH_OTOM) += mach-otom.o -obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o -obj-$(CONFIG_MACH_VSTMS) += mach-vstms.o - -obj-$(CONFIG_MACH_SMDK) += common-smdk.o
\ No newline at end of file +obj-$(CONFIG_MACH_AML_M5900) += mach-amlm5900.o +obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o +obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o diff --git a/arch/arm/mach-s3c2410/bast-irq.c b/arch/arm/mach-s3c2410/bast-irq.c index 379efe70778c..daeba427d781 100644 --- a/arch/arm/mach-s3c2410/bast-irq.c +++ b/arch/arm/mach-s3c2410/bast-irq.c @@ -39,7 +39,7 @@ #include <asm/arch/bast-map.h> #include <asm/arch/bast-irq.h> -#include "irq.h" +#include <asm/plat-s3c24xx/irq.h> #if 0 #include <asm/debug-ll.h> diff --git a/arch/arm/mach-s3c2410/bast.h b/arch/arm/mach-s3c2410/bast.h index e5d03311752c..e98543742eb9 100644 --- a/arch/arm/mach-s3c2410/bast.h +++ b/arch/arm/mach-s3c2410/bast.h @@ -1,2 +1,2 @@ - +/* linux/arch/arm/mach-s3c2410/bast.h extern void bast_init_irq(void); diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index e13fb6778890..5b4831c4c1d8 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -1,15 +1,9 @@ /* linux/arch/arm/mach-s3c2410/clock.c * - * Copyright (c) 2004-2005 Simtec Electronics + * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C24XX Core clock control support - * - * Based on, and code from linux/arch/arm/mach-versatile/clock.c - ** - ** Copyright (C) 2004 ARM Limited. - ** Written by Deep Blue Solutions Limited. - * + * 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 @@ -32,418 +26,251 @@ #include <linux/list.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/platform_device.h> #include <linux/sysdev.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> #include <linux/clk.h> #include <linux/mutex.h> #include <linux/delay.h> +#include <linux/serial_core.h> + +#include <asm/mach/map.h> #include <asm/hardware.h> -#include <asm/irq.h> #include <asm/io.h> +#include <asm/arch/regs-serial.h> #include <asm/arch/regs-clock.h> #include <asm/arch/regs-gpio.h> -#include "clock.h" -#include "cpu.h" - -/* clock information */ +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> -static LIST_HEAD(clocks); - -DEFINE_MUTEX(clocks_mutex); - -/* enable and disable calls for use with the clk struct */ - -static int clk_null_enable(struct clk *clk, int enable) +int s3c2410_clkcon_enable(struct clk *clk, int enable) { - return 0; -} - -/* Clock API calls */ + unsigned int clocks = clk->ctrlbit; + unsigned long clkcon; -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *p; - struct clk *clk = ERR_PTR(-ENOENT); - int idno; + clkcon = __raw_readl(S3C2410_CLKCON); - if (dev == NULL || dev->bus != &platform_bus_type) - idno = -1; + if (enable) + clkcon |= clocks; else - idno = to_platform_device(dev)->id; - - mutex_lock(&clocks_mutex); - - list_for_each_entry(p, &clocks, list) { - if (p->id == idno && - strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - - /* check for the case where a device was supplied, but the - * clock that was being searched for is not device specific */ - - if (IS_ERR(clk)) { - list_for_each_entry(p, &clocks, list) { - if (p->id == -1 && strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - } + clkcon &= ~clocks; - mutex_unlock(&clocks_mutex); - return clk; -} + /* ensure none of the special function bits set */ + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); -void clk_put(struct clk *clk) -{ - module_put(clk->owner); -} + __raw_writel(clkcon, S3C2410_CLKCON); -int clk_enable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return -EINVAL; - - clk_enable(clk->parent); - - mutex_lock(&clocks_mutex); - - if ((clk->usage++) == 0) - (clk->enable)(clk, 1); - - mutex_unlock(&clocks_mutex); return 0; } -void clk_disable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return; - - mutex_lock(&clocks_mutex); - - if ((--clk->usage) == 0) - (clk->enable)(clk, 0); - - mutex_unlock(&clocks_mutex); - clk_disable(clk->parent); -} - - -unsigned long clk_get_rate(struct clk *clk) -{ - if (IS_ERR(clk)) - return 0; - - if (clk->rate != 0) - return clk->rate; - - if (clk->get_rate != NULL) - return (clk->get_rate)(clk); - - if (clk->parent != NULL) - return clk_get_rate(clk->parent); - - return clk->rate; -} - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (!IS_ERR(clk) && clk->round_rate) - return (clk->round_rate)(clk, rate); - - return rate; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - int ret; - - if (IS_ERR(clk)) - return -EINVAL; - - mutex_lock(&clocks_mutex); - ret = (clk->set_rate)(clk, rate); - mutex_unlock(&clocks_mutex); - - return ret; -} - -struct clk *clk_get_parent(struct clk *clk) +static int s3c2410_upll_enable(struct clk *clk, int enable) { - return clk->parent; -} - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - int ret = 0; - - if (IS_ERR(clk)) - return -EINVAL; - - mutex_lock(&clocks_mutex); - - if (clk->set_parent) - ret = (clk->set_parent)(clk, parent); - - mutex_unlock(&clocks_mutex); - - return ret; -} - -EXPORT_SYMBOL(clk_get); -EXPORT_SYMBOL(clk_put); -EXPORT_SYMBOL(clk_enable); -EXPORT_SYMBOL(clk_disable); -EXPORT_SYMBOL(clk_get_rate); -EXPORT_SYMBOL(clk_round_rate); -EXPORT_SYMBOL(clk_set_rate); -EXPORT_SYMBOL(clk_get_parent); -EXPORT_SYMBOL(clk_set_parent); - -/* base clocks */ - -struct clk clk_xtal = { - .name = "xtal", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_mpll = { - .name = "mpll", - .id = -1, -}; - -struct clk clk_upll = { - .name = "upll", - .id = -1, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_f = { - .name = "fclk", - .id = -1, - .rate = 0, - .parent = &clk_mpll, - .ctrlbit = 0, -}; - -struct clk clk_h = { - .name = "hclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_p = { - .name = "pclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_usb_bus = { - .name = "usb-bus", - .id = -1, - .rate = 0, - .parent = &clk_upll, -}; - -/* 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); + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long orig = clkslow; if (enable) - dclkcon |= clk->ctrlbit; + clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; else - dclkcon &= ~clk->ctrlbit; + clkslow |= S3C2410_CLKSLOW_UCLK_OFF; - __raw_writel(dclkcon, S3C24XX_DCLKCON); + __raw_writel(clkslow, S3C2410_CLKSLOW); - return 0; -} + /* if we started the UPLL, then allow to settle */ -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); + if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) + udelay(200); return 0; } - -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_xtal) - 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_dclk0) - mask = S3C2410_MISCCR_CLK0_MASK; - else { - source <<= 4; - mask = S3C2410_MISCCR_CLK1_MASK; +/* standard clock definitions */ + +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_NAND, + }, { + .name = "sdi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SDI, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_ADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIC, + }, { + .name = "iis", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIS, + }, { + .name = "spi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SPI, } - - s3c2410_modify_misccr(mask, source); - return 0; -} - -/* external clock definitions */ - -struct clk s3c24xx_dclk0 = { - .name = "dclk0", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK0EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, -}; - -struct clk s3c24xx_dclk1 = { - .name = "dclk1", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK0EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, }; -struct clk s3c24xx_clkout0 = { - .name = "clkout0", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, +static struct clk init_clocks[] = { + { + .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_LCDC, + }, { + .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_GPIO, + }, { + .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBH, + }, { + .name = "usb-device", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBD, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_PWMT, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART2, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = 0, + }, { + .name = "usb-bus-host", + .id = -1, + .parent = &clk_usb_bus, + }, { + .name = "usb-bus-gadget", + .id = -1, + .parent = &clk_usb_bus, + }, }; -struct clk s3c24xx_clkout1 = { - .name = "clkout1", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, -}; - -struct clk s3c24xx_uclk = { - .name = "uclk", - .id = -1, -}; - -/* initialise the clock system */ - -int s3c24xx_register_clock(struct clk *clk) -{ - clk->owner = THIS_MODULE; - - if (clk->enable == NULL) - clk->enable = clk_null_enable; - - /* add to the list of available clocks */ - - mutex_lock(&clocks_mutex); - list_add(&clk->list, &clocks); - mutex_unlock(&clocks_mutex); - - return 0; -} - -/* initalise all the clocks */ +/* 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 s3c24xx_setup_clocks(unsigned long xtal, - unsigned long fclk, - unsigned long hclk, - unsigned long pclk) +int __init s3c2410_baseclk_add(void) { - printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long clkcon = __raw_readl(S3C2410_CLKCON); + struct clk *clkp; + struct clk *xtal; + int ret; + int ptr; - /* initialise the main system clocks */ + clk_upll.enable = s3c2410_upll_enable; - clk_xtal.rate = xtal; - clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + if (s3c24xx_register_clock(&clk_usb_bus) < 0) + printk(KERN_ERR "failed to register usb bus clock\n"); - clk_mpll.rate = fclk; - clk_h.rate = hclk; - clk_p.rate = pclk; - clk_f.rate = fclk; + /* register clocks from clock array */ - /* assume uart clocks are correctly setup */ + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + /* ensure that we note the clock state */ - /* register our clocks */ + clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; - if (s3c24xx_register_clock(&clk_xtal) < 0) - printk(KERN_ERR "failed to register master xtal\n"); + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } - if (s3c24xx_register_clock(&clk_mpll) < 0) - printk(KERN_ERR "failed to register mpll clock\n"); + /* We must be careful disabling the clocks we are not intending to + * be using at boot time, as subsytems 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 */ + + 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); + } - if (s3c24xx_register_clock(&clk_upll) < 0) - printk(KERN_ERR "failed to register upll clock\n"); + s3c2410_clkcon_enable(clkp, 0); + } - if (s3c24xx_register_clock(&clk_f) < 0) - printk(KERN_ERR "failed to register cpu fclk\n"); + /* show the clock-slow value */ - if (s3c24xx_register_clock(&clk_h) < 0) - printk(KERN_ERR "failed to register cpu hclk\n"); + xtal = clk_get(NULL, "xtal"); - if (s3c24xx_register_clock(&clk_p) < 0) - printk(KERN_ERR "failed to register cpu pclk\n"); + 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"); return 0; } diff --git a/arch/arm/mach-s3c2410/clock.h b/arch/arm/mach-s3c2410/clock.h deleted file mode 100644 index 7f0ea03e1d49..000000000000 --- a/arch/arm/mach-s3c2410/clock.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * linux/arch/arm/mach-s3c2410/clock.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * Written by Ben Dooks, <ben@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. -*/ - -struct clk { - struct list_head list; - struct module *owner; - struct clk *parent; - const char *name; - int id; - int usage; - unsigned long rate; - unsigned long ctrlbit; - - int (*enable)(struct clk *, int enable); - int (*set_rate)(struct clk *c, unsigned long rate); - unsigned long (*get_rate)(struct clk *c); - unsigned long (*round_rate)(struct clk *c, unsigned long rate); - int (*set_parent)(struct clk *c, struct clk *parent); -}; - -/* other clocks which may be registered by board support */ - -extern struct clk s3c24xx_dclk0; -extern struct clk s3c24xx_dclk1; -extern struct clk s3c24xx_clkout0; -extern struct clk s3c24xx_clkout1; -extern struct clk s3c24xx_uclk; - -extern struct clk clk_usb_bus; - -/* core clock support */ - -extern struct clk clk_f; -extern struct clk clk_h; -extern struct clk clk_p; -extern struct clk clk_mpll; -extern struct clk clk_upll; -extern struct clk clk_xtal; - -/* exports for arch/arm/mach-s3c2410 - * - * Please DO NOT use these outside of arch/arm/mach-s3c2410 -*/ - -extern struct mutex clocks_mutex; - -extern int s3c2410_clkcon_enable(struct clk *clk, int enable); - -extern int s3c24xx_register_clock(struct clk *clk); - -extern int s3c24xx_setup_clocks(unsigned long xtal, - unsigned long fclk, - unsigned long hclk, - unsigned long pclk); diff --git a/arch/arm/mach-s3c2410/common-smdk.h b/arch/arm/mach-s3c2410/common-smdk.h deleted file mode 100644 index 0e3a3be330a3..000000000000 --- a/arch/arm/mach-s3c2410/common-smdk.h +++ /dev/null @@ -1,15 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/common-smdk.h - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Common code for SMDK2410 and SMDK2440 boards - * - * http://www.fluff.org/ben/smdk2440/ - * - * 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. -*/ - -extern void smdk_machine_init(void); diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h deleted file mode 100644 index be42e4032a6d..000000000000 --- a/arch/arm/mach-s3c2410/cpu.h +++ /dev/null @@ -1,69 +0,0 @@ -/* arch/arm/mach-s3c2410/cpu.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for S3C24XX 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. -*/ - -/* todo - fix when rmk changes iodescs to use `void __iomem *` */ - -#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE } - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) - -/* forward declaration */ -struct s3c24xx_uart_resources; -struct platform_device; -struct s3c2410_uartcfg; -struct map_desc; - -/* core initialisation functions */ - -extern void s3c24xx_init_irq(void); - -extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); - -extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); - -extern void s3c24xx_init_clocks(int xtal); - -extern void s3c24xx_init_uartdevs(char *name, - struct s3c24xx_uart_resources *res, - struct s3c2410_uartcfg *cfg, int no); - -/* the board structure is used at first initialsation time - * to get info such as the devices to register for this - * board. This is done because platfrom_add_devices() cannot - * be called from the map_io entry. -*/ - -struct s3c24xx_board { - struct platform_device **devices; - unsigned int devices_count; - - struct clk **clocks; - unsigned int clocks_count; -}; - -extern void s3c24xx_set_board(struct s3c24xx_board *board); - -/* timer for 2410/2440 */ - -struct sys_timer; -extern struct sys_timer s3c24xx_timer; - -/* system device classes */ - -extern struct sysdev_class s3c2410_sysclass; -extern struct sysdev_class s3c2412_sysclass; -extern struct sysdev_class s3c2440_sysclass; -extern struct sysdev_class s3c2442_sysclass; diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h deleted file mode 100644 index 14fb0bade716..000000000000 --- a/arch/arm/mach-s3c2410/devs.h +++ /dev/null @@ -1,51 +0,0 @@ -/* arch/arm/mach-s3c2410/devs.h - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for s3c2410 standard platform devices - * - * 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/platform_device.h> - -struct s3c24xx_uart_resources { - struct resource *resources; - unsigned long nr_resources; -}; - -extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; - -extern struct platform_device *s3c24xx_uart_devs[]; -extern struct platform_device *s3c24xx_uart_src[]; - -extern struct platform_device s3c_device_usb; -extern struct platform_device s3c_device_lcd; -extern struct platform_device s3c_device_wdt; -extern struct platform_device s3c_device_i2c; -extern struct platform_device s3c_device_iis; -extern struct platform_device s3c_device_rtc; -extern struct platform_device s3c_device_adc; -extern struct platform_device s3c_device_sdi; - -extern struct platform_device s3c_device_spi0; -extern struct platform_device s3c_device_spi1; - -extern struct platform_device s3c_device_nand; - -extern struct platform_device s3c_device_timer0; -extern struct platform_device s3c_device_timer1; -extern struct platform_device s3c_device_timer2; -extern struct platform_device s3c_device_timer3; - -extern struct platform_device s3c_device_usbgadget; - -/* s3c2440 specific devices */ - -#ifdef CONFIG_CPU_S3C2440 - -extern struct platform_device s3c_device_camif; - -#endif diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c index fa860e716b4f..22c613600caa 100644 --- a/arch/arm/mach-s3c2410/dma.c +++ b/arch/arm/mach-s3c2410/dma.c @@ -1,9 +1,9 @@ /* linux/arch/arm/mach-s3c2410/dma.c * - * Copyright (c) 2003-2005,2006 Simtec Electronics + * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C2410 DMA core + * S3C2410 DMA selection * * http://armlinux.simtec.co.uk/ * @@ -12,1430 +12,150 @@ * published by the Free Software Foundation. */ - -#ifdef CONFIG_S3C2410_DMA_DEBUG -#define DEBUG -#endif - -#include <linux/module.h> +#include <linux/kernel.h> #include <linux/init.h> -#include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> #include <linux/sysdev.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/delay.h> +#include <linux/serial_core.h> -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/hardware.h> -#include <asm/io.h> #include <asm/dma.h> - -#include <asm/mach/dma.h> -#include <asm/arch/map.h> - -#include "dma.h" - -/* io map for dma */ -static void __iomem *dma_base; -static struct kmem_cache *dma_kmem; - -struct s3c24xx_dma_selection dma_sel; - -/* dma channel state information */ -struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; - -/* debugging functions */ - -#define BUF_MAGIC (0xcafebabe) - -#define dmawarn(fmt...) printk(KERN_DEBUG fmt) - -#define dma_regaddr(chan, reg) ((chan)->regs + (reg)) - -#if 1 -#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg)) -#else -static inline void -dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val) -{ - pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); - writel(val, dma_regaddr(chan, reg)); -} -#endif - -#define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) - -/* captured register state for debug */ - -struct s3c2410_dma_regstate { - unsigned long dcsrc; - unsigned long disrc; - unsigned long dstat; - unsigned long dcon; - unsigned long dmsktrig; +#include <asm/arch/dma.h> +#include <asm/plat-s3c24xx/dma.h> + +#include <asm/plat-s3c24xx/cpu.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-ac97.h> +#include <asm/arch/regs-mem.h> +#include <asm/arch/regs-lcd.h> +#include <asm/arch/regs-sdi.h> +#include <asm/arch/regs-iis.h> +#include <asm/arch/regs-spi.h> + +static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = { + [DMACH_XD0] = { + .name = "xdreq0", + .channels[0] = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID, + }, + [DMACH_XD1] = { + .name = "xdreq1", + .channels[1] = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID, + }, + [DMACH_SDI] = { + .name = "sdi", + .channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID, + .channels[2] = S3C2410_DCON_CH2_SDI | DMA_CH_VALID, + .channels[3] = S3C2410_DCON_CH3_SDI | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO, + .hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO, + }, + [DMACH_SPI0] = { + .name = "spi0", + .channels[1] = S3C2410_DCON_CH1_SPI | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT, + .hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT, + }, + [DMACH_SPI1] = { + .name = "spi1", + .channels[3] = S3C2410_DCON_CH3_SPI | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT, + .hw_addr.from = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT, + }, + [DMACH_UART0] = { + .name = "uart0", + .channels[0] = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH, + .hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH, + }, + [DMACH_UART1] = { + .name = "uart1", + .channels[1] = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH, + .hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH, + }, + [DMACH_UART2] = { + .name = "uart2", + .channels[3] = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH, + .hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH, + }, + [DMACH_TIMER] = { + .name = "timer", + .channels[0] = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID, + .channels[2] = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID, + .channels[3] = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID, + }, + [DMACH_I2S_IN] = { + .name = "i2s-sdi", + .channels[1] = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID, + .channels[2] = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID, + .hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO, + }, + [DMACH_I2S_OUT] = { + .name = "i2s-sdo", + .channels[2] = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID, + .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO, + }, + [DMACH_USB_EP1] = { + .name = "usb-ep1", + .channels[0] = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID, + }, + [DMACH_USB_EP2] = { + .name = "usb-ep2", + .channels[1] = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID, + }, + [DMACH_USB_EP3] = { + .name = "usb-ep3", + .channels[2] = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID, + }, + [DMACH_USB_EP4] = { + .name = "usb-ep4", + .channels[3] =S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID, + }, }; -#ifdef CONFIG_S3C2410_DMA_DEBUG - -/* dmadbg_showregs - * - * simple debug routine to print the current state of the dma registers -*/ - -static void -dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs) -{ - regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC); - regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC); - regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT); - regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON); - regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); -} - -static void -dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan, - struct s3c2410_dma_regstate *regs) -{ - printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", - chan->number, fname, line, - regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig, - regs->dcon); -} - -static void -dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan) -{ - struct s3c2410_dma_regstate state; - - dmadbg_capture(chan, &state); - - printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n", - chan->number, fname, line, chan->load_state, - chan->curr, chan->next, chan->end); - - dmadbg_dumpregs(fname, line, chan, &state); -} - -static void -dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) -{ - struct s3c2410_dma_regstate state; - - dmadbg_capture(chan, &state); - dmadbg_dumpregs(fname, line, chan, &state); -} - -#define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) -#define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan)) -#else -#define dbg_showregs(chan) do { } while(0) -#define dbg_showchan(chan) do { } while(0) -#endif /* CONFIG_S3C2410_DMA_DEBUG */ - -static struct s3c2410_dma_chan *dma_chan_map[DMACH_MAX]; - -/* lookup_dma_channel - * - * change the dma channel number given into a real dma channel id -*/ - -static struct s3c2410_dma_chan *lookup_dma_channel(unsigned int channel) -{ - if (channel & DMACH_LOW_LEVEL) - return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL]; - else - return dma_chan_map[channel]; -} - -/* s3c2410_dma_stats_timeout - * - * Update DMA stats from timeout info -*/ - -static void -s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val) +static void s3c2410_dma_select(struct s3c2410_dma_chan *chan, + struct s3c24xx_dma_map *map) { - if (stats == NULL) - return; - - if (val > stats->timeout_longest) - stats->timeout_longest = val; - if (val < stats->timeout_shortest) - stats->timeout_shortest = val; - - stats->timeout_avg += val; + chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID; } -/* s3c2410_dma_waitforload - * - * wait for the DMA engine to load a buffer, and update the state accordingly -*/ - -static int -s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line) -{ - int timeout = chan->load_timeout; - int took; - - if (chan->load_state != S3C2410_DMALOAD_1LOADED) { - printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); - return 0; - } - - if (chan->stats != NULL) - chan->stats->loads++; - - while (--timeout > 0) { - if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { - took = chan->load_timeout - timeout; - - s3c2410_dma_stats_timeout(chan->stats, took); - - switch (chan->load_state) { - case S3C2410_DMALOAD_1LOADED: - chan->load_state = S3C2410_DMALOAD_1RUNNING; - break; - - default: - printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); - } - - return 1; - } - } - - if (chan->stats != NULL) { - chan->stats->timeout_failed++; - } - - return 0; -} - - - -/* s3c2410_dma_loadbuffer - * - * load a buffer, and update the channel state -*/ - -static inline int -s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, - struct s3c2410_dma_buf *buf) -{ - unsigned long reload; - - pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n", - buf, (unsigned long)buf->data, buf->size); - - if (buf == NULL) { - dmawarn("buffer is NULL\n"); - return -EINVAL; - } - - /* check the state of the channel before we do anything */ - - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n"); - } - - if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) { - dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n"); - } - - /* it would seem sensible if we are the last buffer to not bother - * with the auto-reload bit, so that the DMA engine will not try - * and load another transfer after this one has finished... - */ - if (chan->load_state == S3C2410_DMALOAD_NONE) { - pr_debug("load_state is none, checking for noreload (next=%p)\n", - buf->next); - reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; - } else { - //pr_debug("load_state is %d => autoreload\n", chan->load_state); - reload = S3C2410_DCON_AUTORELOAD; - } - - if ((buf->data & 0xf0000000) != 0x30000000) { - dmawarn("dmaload: buffer is %p\n", (void *)buf->data); - } - - writel(buf->data, chan->addr_reg); - - dma_wrreg(chan, S3C2410_DMA_DCON, - chan->dcon | reload | (buf->size/chan->xfer_unit)); - - chan->next = buf->next; - - /* update the state of the channel */ - - switch (chan->load_state) { - case S3C2410_DMALOAD_NONE: - chan->load_state = S3C2410_DMALOAD_1LOADED; - break; - - case S3C2410_DMALOAD_1RUNNING: - chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING; - break; - - default: - dmawarn("dmaload: unknown state %d in loadbuffer\n", - chan->load_state); - break; - } - - return 0; -} - -/* s3c2410_dma_call_op - * - * small routine to call the op routine with the given op if it has been - * registered -*/ - -static void -s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op) -{ - if (chan->op_fn != NULL) { - (chan->op_fn)(chan, op); - } -} - -/* s3c2410_dma_buffdone - * - * small wrapper to check if callback routine needs to be called, and - * if so, call it -*/ - -static inline void -s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, - enum s3c2410_dma_buffresult result) -{ -#if 0 - pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", - chan->callback_fn, buf, buf->id, buf->size, result); -#endif - - if (chan->callback_fn != NULL) { - (chan->callback_fn)(chan, buf->id, buf->size, result); - } -} - -/* s3c2410_dma_start - * - * start a dma channel going -*/ - -static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) -{ - unsigned long tmp; - unsigned long flags; - - pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); - - local_irq_save(flags); - - if (chan->state == S3C2410_DMA_RUNNING) { - pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); - local_irq_restore(flags); - return 0; - } - - chan->state = S3C2410_DMA_RUNNING; - - /* check wether there is anything to load, and if not, see - * if we can find anything to load - */ - - if (chan->load_state == S3C2410_DMALOAD_NONE) { - if (chan->next == NULL) { - printk(KERN_ERR "dma%d: channel has nothing loaded\n", - chan->number); - chan->state = S3C2410_DMA_IDLE; - local_irq_restore(flags); - return -EINVAL; - } - - s3c2410_dma_loadbuffer(chan, chan->next); - } - - dbg_showchan(chan); - - /* enable the channel */ - - if (!chan->irq_enabled) { - enable_irq(chan->irq); - chan->irq_enabled = 1; - } - - /* start the channel going */ - - tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - tmp &= ~S3C2410_DMASKTRIG_STOP; - tmp |= S3C2410_DMASKTRIG_ON; - dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); - - pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); - -#if 0 - /* the dma buffer loads should take care of clearing the AUTO - * reloading feature */ - tmp = dma_rdreg(chan, S3C2410_DMA_DCON); - tmp &= ~S3C2410_DCON_NORELOAD; - dma_wrreg(chan, S3C2410_DMA_DCON, tmp); -#endif - - s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); - - dbg_showchan(chan); - - /* if we've only loaded one buffer onto the channel, then chec - * to see if we have another, and if so, try and load it so when - * the first buffer is finished, the new one will be loaded onto - * the channel */ - - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __FUNCTION__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - - local_irq_restore(flags); - - return 0; -} - -/* s3c2410_dma_canload - * - * work out if we can queue another buffer into the DMA engine -*/ - -static int -s3c2410_dma_canload(struct s3c2410_dma_chan *chan) -{ - if (chan->load_state == S3C2410_DMALOAD_NONE || - chan->load_state == S3C2410_DMALOAD_1RUNNING) - return 1; - - return 0; -} - -/* s3c2410_dma_enqueue - * - * queue an given buffer for dma transfer. - * - * id the device driver's id information for this buffer - * data the physical address of the buffer data - * size the size of the buffer in bytes - * - * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART - * is checked, and if set, the channel is started. If this flag isn't set, - * then an error will be returned. - * - * It is possible to queue more than one DMA buffer onto a channel at - * once, and the code will deal with the re-loading of the next buffer - * when necessary. -*/ - -int s3c2410_dma_enqueue(unsigned int channel, void *id, - dma_addr_t data, int size) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - struct s3c2410_dma_buf *buf; - unsigned long flags; - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: id=%p, data=%08x, size=%d\n", - __FUNCTION__, id, (unsigned int)data, size); - - buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); - if (buf == NULL) { - pr_debug("%s: out of memory (%ld alloc)\n", - __FUNCTION__, (long)sizeof(*buf)); - return -ENOMEM; - } - - //pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); - //dbg_showchan(chan); - - buf->next = NULL; - buf->data = buf->ptr = data; - buf->size = size; - buf->id = id; - buf->magic = BUF_MAGIC; - - local_irq_save(flags); - - if (chan->curr == NULL) { - /* we've got nothing loaded... */ - pr_debug("%s: buffer %p queued onto empty channel\n", - __FUNCTION__, buf); - - chan->curr = buf; - chan->end = buf; - chan->next = NULL; - } else { - pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", - chan->number, __FUNCTION__, buf); - - if (chan->end == NULL) - pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", - chan->number, __FUNCTION__, chan); - - chan->end->next = buf; - chan->end = buf; - } - - /* if necessary, update the next buffer field */ - if (chan->next == NULL) - chan->next = buf; - - /* check to see if we can load a buffer */ - if (chan->state == S3C2410_DMA_RUNNING) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - printk(KERN_ERR "dma%d: loadbuffer:" - "timeout loading buffer\n", - chan->number); - dbg_showchan(chan); - local_irq_restore(flags); - return -EINVAL; - } - } - - while (s3c2410_dma_canload(chan) && chan->next != NULL) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } else if (chan->state == S3C2410_DMA_IDLE) { - if (chan->flags & S3C2410_DMAF_AUTOSTART) { - s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); - } - } - - local_irq_restore(flags); - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_enqueue); - -static inline void -s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) -{ - int magicok = (buf->magic == BUF_MAGIC); - - buf->magic = -1; - - if (magicok) { - kmem_cache_free(dma_kmem, buf); - } else { - printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf); - } -} - -/* s3c2410_dma_lastxfer - * - * called when the system is out of buffers, to ensure that the channel - * is prepared for shutdown. -*/ - -static inline void -s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) -{ -#if 0 - pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", - chan->number, chan->load_state); -#endif - - switch (chan->load_state) { - case S3C2410_DMALOAD_NONE: - break; - - case S3C2410_DMALOAD_1LOADED: - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - /* flag error? */ - printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", - chan->number, __FUNCTION__); - return; - } - break; - - case S3C2410_DMALOAD_1LOADED_1RUNNING: - /* I belive in this case we do not have anything to do - * until the next buffer comes along, and we turn off the - * reload */ - return; - - default: - pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", - chan->number, chan->load_state); - return; - - } - - /* hopefully this'll shut the damned thing up after the transfer... */ - dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); -} - - -#define dmadbg2(x...) - -static irqreturn_t -s3c2410_dma_irq(int irq, void *devpw) -{ - struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; - struct s3c2410_dma_buf *buf; - - buf = chan->curr; - - dbg_showchan(chan); - - /* modify the channel state */ - - switch (chan->load_state) { - case S3C2410_DMALOAD_1RUNNING: - /* TODO - if we are running only one buffer, we probably - * want to reload here, and then worry about the buffer - * callback */ - - chan->load_state = S3C2410_DMALOAD_NONE; - break; - - case S3C2410_DMALOAD_1LOADED: - /* iirc, we should go back to NONE loaded here, we - * had a buffer, and it was never verified as being - * loaded. - */ - - chan->load_state = S3C2410_DMALOAD_NONE; - break; - - case S3C2410_DMALOAD_1LOADED_1RUNNING: - /* we'll worry about checking to see if another buffer is - * ready after we've called back the owner. This should - * ensure we do not wait around too long for the DMA - * engine to start the next transfer - */ - - chan->load_state = S3C2410_DMALOAD_1LOADED; - break; - - case S3C2410_DMALOAD_NONE: - printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", - chan->number); - break; - - default: - printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", - chan->number, chan->load_state); - break; - } - - if (buf != NULL) { - /* update the chain to make sure that if we load any more - * buffers when we call the callback function, things should - * work properly */ - - chan->curr = buf->next; - buf->next = NULL; - - if (buf->magic != BUF_MAGIC) { - printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", - chan->number, __FUNCTION__, buf); - return IRQ_HANDLED; - } - - s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); - - /* free resouces */ - s3c2410_dma_freebuf(buf); - } else { - } - - /* only reload if the channel is still running... our buffer done - * routine may have altered the state by requesting the dma channel - * to stop or shutdown... */ - - /* todo: check that when the channel is shut-down from inside this - * function, we cope with unsetting reload, etc */ - - if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { - unsigned long flags; - - switch (chan->load_state) { - case S3C2410_DMALOAD_1RUNNING: - /* don't need to do anything for this state */ - break; - - case S3C2410_DMALOAD_NONE: - /* can load buffer immediately */ - break; - - case S3C2410_DMALOAD_1LOADED: - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - /* flag error? */ - printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", - chan->number, __FUNCTION__); - return IRQ_HANDLED; - } - - break; - - case S3C2410_DMALOAD_1LOADED_1RUNNING: - goto no_load; - - default: - printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", - chan->number, chan->load_state); - return IRQ_HANDLED; - } - - local_irq_save(flags); - s3c2410_dma_loadbuffer(chan, chan->next); - local_irq_restore(flags); - } else { - s3c2410_dma_lastxfer(chan); - - /* see if we can stop this channel.. */ - if (chan->load_state == S3C2410_DMALOAD_NONE) { - pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", - chan->number, jiffies); - s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, - S3C2410_DMAOP_STOP); - } - } - - no_load: - return IRQ_HANDLED; -} - -static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel); - -/* s3c2410_request_dma - * - * get control of an dma channel -*/ - -int s3c2410_dma_request(unsigned int channel, - struct s3c2410_dma_client *client, - void *dev) -{ - struct s3c2410_dma_chan *chan; - unsigned long flags; - int err; - - pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", - channel, client->name, dev); - - local_irq_save(flags); - - chan = s3c2410_dma_map_channel(channel); - if (chan == NULL) { - local_irq_restore(flags); - return -EBUSY; - } - - dbg_showchan(chan); - - chan->client = client; - chan->in_use = 1; - - if (!chan->irq_claimed) { - pr_debug("dma%d: %s : requesting irq %d\n", - channel, __FUNCTION__, chan->irq); - - chan->irq_claimed = 1; - local_irq_restore(flags); - - err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, - client->name, (void *)chan); - - local_irq_save(flags); - - if (err) { - chan->in_use = 0; - chan->irq_claimed = 0; - local_irq_restore(flags); - - printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", - client->name, chan->irq, chan->number); - return err; - } - - chan->irq_enabled = 1; - } - - local_irq_restore(flags); - - /* need to setup */ - - pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_request); - -/* s3c2410_dma_free - * - * release the given channel back to the system, will stop and flush - * any outstanding transfers, and ensure the channel is ready for the - * next claimant. - * - * Note, although a warning is currently printed if the freeing client - * info is not the same as the registrant's client info, the free is still - * allowed to go through. -*/ +static struct s3c24xx_dma_selection __initdata s3c2410_dma_sel = { + .select = s3c2410_dma_select, + .dcon_mask = 7 << 24, + .map = s3c2410_dma_mappings, + .map_size = ARRAY_SIZE(s3c2410_dma_mappings), +}; -int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) +static int s3c2410_dma_add(struct sys_device *sysdev) { - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - unsigned long flags; - - if (chan == NULL) - return -EINVAL; - - local_irq_save(flags); - - if (chan->client != client) { - printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", - channel, chan->client, client); - } - - /* sort out stopping and freeing the channel */ - - if (chan->state != S3C2410_DMA_IDLE) { - pr_debug("%s: need to stop dma channel %p\n", - __FUNCTION__, chan); - - /* possibly flush the channel */ - s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP); - } - - chan->client = NULL; - chan->in_use = 0; - - if (chan->irq_claimed) - free_irq(chan->irq, (void *)chan); - - chan->irq_claimed = 0; - - if (!(channel & DMACH_LOW_LEVEL)) - dma_chan_map[channel] = NULL; - - local_irq_restore(flags); - - return 0; + return s3c24xx_dma_init_map(&s3c2410_dma_sel); } -EXPORT_SYMBOL(s3c2410_dma_free); - -static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) -{ - unsigned long flags; - unsigned long tmp; - - pr_debug("%s:\n", __FUNCTION__); - - dbg_showchan(chan); - - local_irq_save(flags); - - s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); - - tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - tmp |= S3C2410_DMASKTRIG_STOP; - //tmp &= ~S3C2410_DMASKTRIG_ON; - dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); - -#if 0 - /* should also clear interrupts, according to WinCE BSP */ - tmp = dma_rdreg(chan, S3C2410_DMA_DCON); - tmp |= S3C2410_DCON_NORELOAD; - dma_wrreg(chan, S3C2410_DMA_DCON, tmp); -#endif - - /* should stop do this, or should we wait for flush? */ - chan->state = S3C2410_DMA_IDLE; - chan->load_state = S3C2410_DMALOAD_NONE; - - local_irq_restore(flags); - - return 0; -} +#if defined(CONFIG_CPU_S3C2410) +static struct sysdev_driver s3c2410_dma_driver = { + .add = s3c2410_dma_add, +}; -void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan) +static int __init s3c2410_dma_init(void) { - unsigned long tmp; - unsigned int timeout = 0x10000; - - while (timeout-- > 0) { - tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - - if (!(tmp & S3C2410_DMASKTRIG_ON)) - return; - } - - pr_debug("dma%d: failed to stop?\n", chan->number); + return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver); } - -/* s3c2410_dma_flush - * - * stop the channel, and remove all current and pending transfers -*/ - -static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) -{ - struct s3c2410_dma_buf *buf, *next; - unsigned long flags; - - pr_debug("%s: chan %p (%d)\n", __FUNCTION__, chan, chan->number); - - dbg_showchan(chan); - - local_irq_save(flags); - - if (chan->state != S3C2410_DMA_IDLE) { - pr_debug("%s: stopping channel...\n", __FUNCTION__ ); - s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); - } - - buf = chan->curr; - if (buf == NULL) - buf = chan->next; - - chan->curr = chan->next = chan->end = NULL; - - if (buf != NULL) { - for ( ; buf != NULL; buf = next) { - next = buf->next; - - pr_debug("%s: free buffer %p, next %p\n", - __FUNCTION__, buf, buf->next); - - s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); - s3c2410_dma_freebuf(buf); - } - } - - dbg_showregs(chan); - - s3c2410_dma_waitforstop(chan); - -#if 0 - /* should also clear interrupts, according to WinCE BSP */ - { - unsigned long tmp; - - tmp = dma_rdreg(chan, S3C2410_DMA_DCON); - tmp |= S3C2410_DCON_NORELOAD; - dma_wrreg(chan, S3C2410_DMA_DCON, tmp); - } +arch_initcall(s3c2410_dma_init); #endif - dbg_showregs(chan); - - local_irq_restore(flags); - - return 0; -} - -int -s3c2410_dma_started(struct s3c2410_dma_chan *chan) -{ - unsigned long flags; - - local_irq_save(flags); - - dbg_showchan(chan); - - /* if we've only loaded one buffer onto the channel, then chec - * to see if we have another, and if so, try and load it so when - * the first buffer is finished, the new one will be loaded onto - * the channel */ - - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __FUNCTION__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - - local_irq_restore(flags); - - return 0; - -} - -int -s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - switch (op) { - case S3C2410_DMAOP_START: - return s3c2410_dma_start(chan); - - case S3C2410_DMAOP_STOP: - return s3c2410_dma_dostop(chan); - - case S3C2410_DMAOP_PAUSE: - case S3C2410_DMAOP_RESUME: - return -ENOENT; - - case S3C2410_DMAOP_FLUSH: - return s3c2410_dma_flush(chan); - - case S3C2410_DMAOP_STARTED: - return s3c2410_dma_started(chan); - - case S3C2410_DMAOP_TIMEOUT: - return 0; - - } - - return -ENOENT; /* unknown, don't bother */ -} - -EXPORT_SYMBOL(s3c2410_dma_ctrl); - -/* DMA configuration for each channel - * - * DISRCC -> source of the DMA (AHB,APB) - * DISRC -> source address of the DMA - * DIDSTC -> destination of the DMA (AHB,APD) - * DIDST -> destination address of the DMA -*/ - -/* s3c2410_dma_config - * - * xfersize: size of unit in bytes (1,2,4) - * dcon: base value of the DCONx register -*/ - -int s3c2410_dma_config(dmach_t channel, - int xferunit, - int dcon) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", - __FUNCTION__, channel, xferunit, dcon); - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: Initial dcon is %08x\n", __FUNCTION__, dcon); - - dcon |= chan->dcon & dma_sel.dcon_mask; - - pr_debug("%s: New dcon is %08x\n", __FUNCTION__, dcon); - - switch (xferunit) { - case 1: - dcon |= S3C2410_DCON_BYTE; - break; - - case 2: - dcon |= S3C2410_DCON_HALFWORD; - break; - - case 4: - dcon |= S3C2410_DCON_WORD; - break; - - default: - pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit); - return -EINVAL; - } - - dcon |= S3C2410_DCON_HWTRIG; - dcon |= S3C2410_DCON_INTREQ; - - pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon); - - chan->dcon = dcon; - chan->xfer_unit = xferunit; - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_config); - -int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); - - chan->flags = flags; - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_setflags); - - -/* do we need to protect the settings of the fields from - * irq? -*/ - -int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); - - chan->op_fn = rtn; - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_set_opfn); - -int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); - - chan->callback_fn = rtn; - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn); - -/* s3c2410_dma_devconfig - * - * configure the dma source/destination hardware type and address - * - * source: S3C2410_DMASRC_HW: source is hardware - * S3C2410_DMASRC_MEM: source is memory - * - * hwcfg: the value for xxxSTCn register, - * bit 0: 0=increment pointer, 1=leave pointer - * bit 1: 0=soucre is AHB, 1=soucre is APB - * - * devaddr: physical address of the source -*/ - -int s3c2410_dma_devconfig(int channel, - enum s3c2410_dmasrc source, - int hwcfg, - unsigned long devaddr) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", - __FUNCTION__, (int)source, hwcfg, devaddr); - - chan->source = source; - chan->dev_addr = devaddr; - - switch (source) { - case S3C2410_DMASRC_HW: - /* source is hardware */ - pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", - __FUNCTION__, devaddr, hwcfg); - dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); - dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); - dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); - - chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); - return 0; - - case S3C2410_DMASRC_MEM: - /* source is memory */ - pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n", - __FUNCTION__, devaddr, hwcfg); - dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); - dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); - dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); - - chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); - return 0; - } - - printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source); - return -EINVAL; -} - -EXPORT_SYMBOL(s3c2410_dma_devconfig); - -/* s3c2410_dma_getposition - * - * returns the current transfer points for the dma source and destination -*/ - -int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) -{ - struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); - - if (chan == NULL) - return -EINVAL; - - if (src != NULL) - *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); - - if (dst != NULL) - *dst = dma_rdreg(chan, S3C2410_DMA_DCDST); - - return 0; -} - -EXPORT_SYMBOL(s3c2410_dma_getposition); - - -/* system device class */ - -#ifdef CONFIG_PM - -static int s3c2410_dma_suspend(struct sys_device *dev, pm_message_t state) -{ - struct s3c2410_dma_chan *cp = container_of(dev, struct s3c2410_dma_chan, dev); - - printk(KERN_DEBUG "suspending dma channel %d\n", cp->number); - - if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) { - /* the dma channel is still working, which is probably - * a bad thing to do over suspend/resume. We stop the - * channel and assume that the client is either going to - * retry after resume, or that it is broken. - */ - - printk(KERN_INFO "dma: stopping channel %d due to suspend\n", - cp->number); - - s3c2410_dma_dostop(cp); - } - - return 0; -} - -static int s3c2410_dma_resume(struct sys_device *dev) -{ - return 0; -} - -#else -#define s3c2410_dma_suspend NULL -#define s3c2410_dma_resume NULL -#endif /* CONFIG_PM */ - -struct sysdev_class dma_sysclass = { - set_kset_name("s3c24xx-dma"), - .suspend = s3c2410_dma_suspend, - .resume = s3c2410_dma_resume, +#if defined(CONFIG_CPU_S3C2442) +/* S3C2442 DMA contains the same selection table as the S3C2410 */ +static struct sysdev_driver s3c2442_dma_driver = { + .add = s3c2410_dma_add, }; -/* kmem cache implementation */ - -static void s3c2410_dma_cache_ctor(void *p, struct kmem_cache *c, unsigned long f) -{ - memset(p, 0, sizeof(struct s3c2410_dma_buf)); -} - -/* initialisation code */ - -static int __init s3c2410_init_dma(void) -{ - struct s3c2410_dma_chan *cp; - int channel; - int ret; - - printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n"); - - dma_base = ioremap(S3C24XX_PA_DMA, 0x200); - if (dma_base == NULL) { - printk(KERN_ERR "dma failed to remap register block\n"); - return -ENOMEM; - } - - printk("Registering sysclass\n"); - - ret = sysdev_class_register(&dma_sysclass); - if (ret != 0) { - printk(KERN_ERR "dma sysclass registration failed\n"); - goto err; - } - - dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c2410_dma_buf), 0, - SLAB_HWCACHE_ALIGN, - s3c2410_dma_cache_ctor, NULL); - - if (dma_kmem == NULL) { - printk(KERN_ERR "dma failed to make kmem cache\n"); - ret = -ENOMEM; - goto err; - } - - for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) { - cp = &s3c2410_chans[channel]; - - memset(cp, 0, sizeof(struct s3c2410_dma_chan)); - - /* dma channel irqs are in order.. */ - cp->number = channel; - cp->irq = channel + IRQ_DMA0; - cp->regs = dma_base + (channel*0x40); - - /* point current stats somewhere */ - cp->stats = &cp->stats_store; - cp->stats_store.timeout_shortest = LONG_MAX; - - /* basic channel configuration */ - - cp->load_timeout = 1<<18; - - /* register system device */ - - cp->dev.cls = &dma_sysclass; - cp->dev.id = channel; - ret = sysdev_register(&cp->dev); - - printk("DMA channel %d at %p, irq %d\n", - cp->number, cp->regs, cp->irq); - } - - return 0; - - err: - kmem_cache_destroy(dma_kmem); - iounmap(dma_base); - dma_base = NULL; - return ret; -} - -core_initcall(s3c2410_init_dma); - -static inline int is_channel_valid(unsigned int channel) +static int __init s3c2442_dma_init(void) { - return (channel & DMA_CH_VALID); + return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_dma_driver); } -/* s3c2410_dma_map_channel() - * - * turn the virtual channel number into a real, and un-used hardware - * channel. - * - * currently this code uses first-free channel from the specified harware - * map, not taking into account anything that the board setup code may - * have to say about the likely peripheral set to be in use. -*/ - -struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) -{ - struct s3c24xx_dma_map *ch_map; - struct s3c2410_dma_chan *dmach; - int ch; - - if (dma_sel.map == NULL || channel > dma_sel.map_size) - return NULL; - - ch_map = dma_sel.map + channel; - - for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { - if (!is_channel_valid(ch_map->channels[ch])) - continue; - - if (s3c2410_chans[ch].in_use == 0) { - printk("mapped channel %d to %d\n", channel, ch); - break; - } - } - - if (ch >= S3C2410_DMA_CHANNELS) - return NULL; - - /* update our channel mapping */ - - dmach = &s3c2410_chans[ch]; - dma_chan_map[channel] = dmach; - - /* select the channel */ - - (dma_sel.select)(dmach, ch_map); - - return dmach; -} - -static void s3c24xx_dma_show_ch(struct s3c24xx_dma_map *map, int ch) -{ - /* show the channel configuration */ - - printk("%2d: %20s, channels %c%c%c%c\n", ch, map->name, - (is_channel_valid(map->channels[0]) ? '0' : '-'), - (is_channel_valid(map->channels[1]) ? '1' : '-'), - (is_channel_valid(map->channels[2]) ? '2' : '-'), - (is_channel_valid(map->channels[3]) ? '3' : '-')); -} - -static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch) -{ - if (1) - s3c24xx_dma_show_ch(map, ch); - - return 0; -} - -int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) -{ - struct s3c24xx_dma_map *nmap; - size_t map_sz = sizeof(*nmap) * sel->map_size; - int ptr; - - nmap = kmalloc(map_sz, GFP_KERNEL); - if (nmap == NULL) - return -ENOMEM; - - memcpy(nmap, sel->map, map_sz); - memcpy(&dma_sel, sel, sizeof(*sel)); - - dma_sel.map = nmap; - - for (ptr = 0; ptr < sel->map_size; ptr++) - s3c24xx_dma_check_entry(nmap+ptr, ptr); +arch_initcall(s3c2442_dma_init); +#endif - return 0; -} diff --git a/arch/arm/mach-s3c2410/dma.h b/arch/arm/mach-s3c2410/dma.h deleted file mode 100644 index 0ebfe0aab80b..000000000000 --- a/arch/arm/mach-s3c2410/dma.h +++ /dev/null @@ -1,45 +0,0 @@ -/* arch/arm/mach-s3c2410/dma.h - * - * Copyright (C) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Samsung S3C24XX DMA 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. -*/ - -extern struct sysdev_class dma_sysclass; -extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; - -#define DMA_CH_VALID (1<<31) - -struct s3c24xx_dma_addr { - unsigned long from; - unsigned long to; -}; - -/* struct s3c24xx_dma_map - * - * this holds the mapping information for the channel selected - * to be connected to the specified device -*/ - -struct s3c24xx_dma_map { - const char *name; - struct s3c24xx_dma_addr hw_addr; - - unsigned long channels[S3C2410_DMA_CHANNELS]; -}; - -struct s3c24xx_dma_selection { - struct s3c24xx_dma_map *map; - unsigned long map_size; - unsigned long dcon_mask; - - void (*select)(struct s3c2410_dma_chan *chan, - struct s3c24xx_dma_map *map); -}; - -extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); diff --git a/arch/arm/mach-s3c2410/gpio.c b/arch/arm/mach-s3c2410/gpio.c index f6fb215bb48c..01e795d1146e 100644 --- a/arch/arm/mach-s3c2410/gpio.c +++ b/arch/arm/mach-s3c2410/gpio.c @@ -1,9 +1,9 @@ /* linux/arch/arm/mach-s3c2410/gpio.c * - * Copyright (c) 2004-2005 Simtec Electronics + * Copyright (c) 2004-2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C24XX GPIO support + * S3C2410 GPIO 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 @@ -18,8 +18,7 @@ * 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/kernel.h> #include <linux/init.h> @@ -33,156 +32,40 @@ #include <asm/arch/regs-gpio.h> -void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) -{ - void __iomem *base = S3C24XX_GPIO_BASE(pin); - unsigned long mask; - unsigned long con; - unsigned long flags; - - if (pin < S3C2410_GPIO_BANKB) { - mask = 1 << S3C2410_GPIO_OFFSET(pin); - } else { - mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; - } - - switch (function) { - case S3C2410_GPIO_LEAVE: - mask = 0; - function = 0; - break; - - case S3C2410_GPIO_INPUT: - case S3C2410_GPIO_OUTPUT: - case S3C2410_GPIO_SFN2: - case S3C2410_GPIO_SFN3: - if (pin < S3C2410_GPIO_BANKB) { - function -= 1; - function &= 1; - function <<= S3C2410_GPIO_OFFSET(pin); - } else { - function &= 3; - function <<= S3C2410_GPIO_OFFSET(pin)*2; - } - } - - /* modify the specified register wwith IRQs off */ - - local_irq_save(flags); - - con = __raw_readl(base + 0x00); - con &= ~mask; - con |= function; - - __raw_writel(con, base + 0x00); - - local_irq_restore(flags); -} - -EXPORT_SYMBOL(s3c2410_gpio_cfgpin); - -unsigned int s3c2410_gpio_getcfg(unsigned int pin) -{ - void __iomem *base = S3C24XX_GPIO_BASE(pin); - unsigned long val = __raw_readl(base); - - if (pin < S3C2410_GPIO_BANKB) { - val >>= S3C2410_GPIO_OFFSET(pin); - val &= 1; - val += 1; - } else { - val >>= S3C2410_GPIO_OFFSET(pin)*2; - val &= 3; - } - - return val | S3C2410_GPIO_INPUT; -} - -EXPORT_SYMBOL(s3c2410_gpio_getcfg); - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, + unsigned int config) { - void __iomem *base = S3C24XX_GPIO_BASE(pin); - unsigned long offs = S3C2410_GPIO_OFFSET(pin); + void __iomem *reg = S3C24XX_EINFLT0; unsigned long flags; - unsigned long up; - - if (pin < S3C2410_GPIO_BANKB) - return; - - local_irq_save(flags); - - up = __raw_readl(base + 0x08); - up &= ~(1L << offs); - up |= to << offs; - __raw_writel(up, base + 0x08); + unsigned long val; - local_irq_restore(flags); -} + if (pin < S3C2410_GPG8 || pin > S3C2410_GPG15) + return -1; -EXPORT_SYMBOL(s3c2410_gpio_pullup); + config &= 0xff; -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - void __iomem *base = S3C24XX_GPIO_BASE(pin); - unsigned long offs = S3C2410_GPIO_OFFSET(pin); - unsigned long flags; - unsigned long dat; + pin -= S3C2410_GPG8; + reg += pin & ~3; local_irq_save(flags); - dat = __raw_readl(base + 0x04); - dat &= ~(1 << offs); - dat |= to << offs; - __raw_writel(dat, base + 0x04); - - local_irq_restore(flags); -} - -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - void __iomem *base = S3C24XX_GPIO_BASE(pin); - unsigned long offs = S3C2410_GPIO_OFFSET(pin); + /* update filter width and clock source */ - return __raw_readl(base + 0x04) & (1<< offs); -} + val = __raw_readl(reg); + val &= ~(0xff << ((pin & 3) * 8)); + val |= config << ((pin & 3) * 8); + __raw_writel(val, reg); -EXPORT_SYMBOL(s3c2410_gpio_getpin); + /* update filter enable */ -unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) -{ - unsigned long flags; - unsigned long misccr; + val = __raw_readl(S3C24XX_EXTINT2); + val &= ~(1 << ((pin * 4) + 3)); + val |= on << ((pin * 4) + 3); + __raw_writel(val, S3C24XX_EXTINT2); - local_irq_save(flags); - misccr = __raw_readl(S3C24XX_MISCCR); - misccr &= ~clear; - misccr ^= change; - __raw_writel(misccr, S3C24XX_MISCCR); local_irq_restore(flags); - return misccr; -} - -EXPORT_SYMBOL(s3c2410_modify_misccr); - -int s3c2410_gpio_getirq(unsigned int pin) -{ - if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15) - return -1; /* not valid interrupts */ - - if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) - return -1; /* not valid pin */ - - if (pin < S3C2410_GPF4) - return (pin - S3C2410_GPF0) + IRQ_EINT0; - - if (pin < S3C2410_GPG0) - return (pin - S3C2410_GPF4) + IRQ_EINT4; - - return (pin - S3C2410_GPG0) + IRQ_EINT8; + return 0; } -EXPORT_SYMBOL(s3c2410_gpio_getirq); +EXPORT_SYMBOL(s3c2410_gpio_irqfilter); diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c index 3c0ed7871c55..53cbdaa43ac6 100644 --- a/arch/arm/mach-s3c2410/irq.c +++ b/arch/arm/mach-s3c2410/irq.c @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s3c2410/irq.c * - * Copyright (c) 2003,2004 Simtec Electronics + * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * * This program is free software; you can redistribute it and/or modify @@ -17,37 +17,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Changelog: - * - * 22-Jul-2004 Ben Dooks <ben@simtec.co.uk> - * Fixed compile warnings - * - * 22-Jul-2004 Roc Wu <cooloney@yahoo.com.cn> - * Fixed s3c_extirq_type - * - * 21-Jul-2004 Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org> - * Addition of ADC/TC demux - * - * 04-Oct-2004 Klaus Fetscher <k.fetscher@fetron.de> - * Fix for set_irq_type() on low EINT numbers - * - * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> - * Tidy up KF's patch and sort out new release - * - * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> - * Add support for power management controls - * - * 04-Nov-2004 Ben Dooks - * Fix standard IRQ wake for EINT0..4 and RTC - * - * 22-Feb-2005 Ben Dooks - * Fixed edge-triggering on ADC IRQ - * - * 28-Jun-2005 Ben Dooks - * Mark IRQ_LCD valid - * - * 25-Jul-2005 Ben Dooks - * Split the S3C2440 IRQ code to seperate file */ #include <linux/init.h> @@ -57,745 +26,23 @@ #include <linux/ptrace.h> #include <linux/sysdev.h> -#include <asm/hardware.h> -#include <asm/irq.h> -#include <asm/io.h> - -#include <asm/mach/irq.h> - -#include <asm/arch/regs-irq.h> -#include <asm/arch/regs-gpio.h> - -#include "cpu.h" -#include "pm.h" -#include "irq.h" - -/* wakeup irq control */ - -#ifdef CONFIG_PM - -/* state for IRQs over sleep */ - -/* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources - * - * set bit to 1 in allow bitfield to enable the wakeup settings on it -*/ - -unsigned long s3c_irqwake_intallow = 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL; -unsigned long s3c_irqwake_intmask = 0xffffffffL; -unsigned long s3c_irqwake_eintallow = 0x0000fff0L; -unsigned long s3c_irqwake_eintmask = 0xffffffffL; - -int -s3c_irq_wake(unsigned int irqno, unsigned int state) -{ - unsigned long irqbit = 1 << (irqno - IRQ_EINT0); - - if (!(s3c_irqwake_intallow & irqbit)) - return -ENOENT; - - printk(KERN_INFO "wake %s for irq %d\n", - state ? "enabled" : "disabled", irqno); - - if (!state) - s3c_irqwake_intmask |= irqbit; - else - s3c_irqwake_intmask &= ~irqbit; - - return 0; -} - -static int -s3c_irqext_wake(unsigned int irqno, unsigned int state) -{ - unsigned long bit = 1L << (irqno - EXTINT_OFF); - - if (!(s3c_irqwake_eintallow & bit)) - return -ENOENT; - - printk(KERN_INFO "wake %s for irq %d\n", - state ? "enabled" : "disabled", irqno); - - if (!state) - s3c_irqwake_eintmask |= bit; - else - s3c_irqwake_eintmask &= ~bit; - - return 0; -} - -#else -#define s3c_irqext_wake NULL -#define s3c_irq_wake NULL -#endif - - -static void -s3c_irq_mask(unsigned int irqno) -{ - unsigned long mask; - - irqno -= IRQ_EINT0; - - mask = __raw_readl(S3C2410_INTMSK); - mask |= 1UL << irqno; - __raw_writel(mask, S3C2410_INTMSK); -} - -static inline void -s3c_irq_ack(unsigned int irqno) -{ - unsigned long bitval = 1UL << (irqno - IRQ_EINT0); - - __raw_writel(bitval, S3C2410_SRCPND); - __raw_writel(bitval, S3C2410_INTPND); -} - -static inline void -s3c_irq_maskack(unsigned int irqno) -{ - unsigned long bitval = 1UL << (irqno - IRQ_EINT0); - unsigned long mask; - - mask = __raw_readl(S3C2410_INTMSK); - __raw_writel(mask|bitval, S3C2410_INTMSK); - - __raw_writel(bitval, S3C2410_SRCPND); - __raw_writel(bitval, S3C2410_INTPND); -} - - -static void -s3c_irq_unmask(unsigned int irqno) -{ - unsigned long mask; - - if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) - irqdbf2("s3c_irq_unmask %d\n", irqno); - - irqno -= IRQ_EINT0; - - mask = __raw_readl(S3C2410_INTMSK); - mask &= ~(1UL << irqno); - __raw_writel(mask, S3C2410_INTMSK); -} - -struct irq_chip s3c_irq_level_chip = { - .name = "s3c-level", - .ack = s3c_irq_maskack, - .mask = s3c_irq_mask, - .unmask = s3c_irq_unmask, - .set_wake = s3c_irq_wake -}; - -static struct irq_chip s3c_irq_chip = { - .name = "s3c", - .ack = s3c_irq_ack, - .mask = s3c_irq_mask, - .unmask = s3c_irq_unmask, - .set_wake = s3c_irq_wake -}; - -static void -s3c_irqext_mask(unsigned int irqno) -{ - unsigned long mask; - - irqno -= EXTINT_OFF; - - mask = __raw_readl(S3C24XX_EINTMASK); - mask |= ( 1UL << irqno); - __raw_writel(mask, S3C24XX_EINTMASK); -} - -static void -s3c_irqext_ack(unsigned int irqno) -{ - unsigned long req; - unsigned long bit; - unsigned long mask; +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> - bit = 1UL << (irqno - EXTINT_OFF); - - mask = __raw_readl(S3C24XX_EINTMASK); - - __raw_writel(bit, S3C24XX_EINTPEND); - - req = __raw_readl(S3C24XX_EINTPEND); - req &= ~mask; - - /* not sure if we should be acking the parent irq... */ - - if (irqno <= IRQ_EINT7 ) { - if ((req & 0xf0) == 0) - s3c_irq_ack(IRQ_EINT4t7); - } else { - if ((req >> 8) == 0) - s3c_irq_ack(IRQ_EINT8t23); - } -} - -static void -s3c_irqext_unmask(unsigned int irqno) +static int s3c2410_irq_add(struct sys_device *sysdev) { - unsigned long mask; - - irqno -= EXTINT_OFF; - - mask = __raw_readl(S3C24XX_EINTMASK); - mask &= ~( 1UL << irqno); - __raw_writel(mask, S3C24XX_EINTMASK); -} - -int -s3c_irqext_type(unsigned int irq, unsigned int type) -{ - void __iomem *extint_reg; - void __iomem *gpcon_reg; - unsigned long gpcon_offset, extint_offset; - unsigned long newvalue = 0, value; - - if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) - { - gpcon_reg = S3C2410_GPFCON; - extint_reg = S3C24XX_EXTINT0; - gpcon_offset = (irq - IRQ_EINT0) * 2; - extint_offset = (irq - IRQ_EINT0) * 4; - } - else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) - { - gpcon_reg = S3C2410_GPFCON; - extint_reg = S3C24XX_EXTINT0; - gpcon_offset = (irq - (EXTINT_OFF)) * 2; - extint_offset = (irq - (EXTINT_OFF)) * 4; - } - else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) - { - gpcon_reg = S3C2410_GPGCON; - extint_reg = S3C24XX_EXTINT1; - gpcon_offset = (irq - IRQ_EINT8) * 2; - extint_offset = (irq - IRQ_EINT8) * 4; - } - else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) - { - gpcon_reg = S3C2410_GPGCON; - extint_reg = S3C24XX_EXTINT2; - gpcon_offset = (irq - IRQ_EINT8) * 2; - extint_offset = (irq - IRQ_EINT16) * 4; - } else - return -1; - - /* Set the GPIO to external interrupt mode */ - value = __raw_readl(gpcon_reg); - value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); - __raw_writel(value, gpcon_reg); - - /* Set the external interrupt to pointed trigger type */ - switch (type) - { - case IRQT_NOEDGE: - printk(KERN_WARNING "No edge setting!\n"); - break; - - case IRQT_RISING: - newvalue = S3C2410_EXTINT_RISEEDGE; - break; - - case IRQT_FALLING: - newvalue = S3C2410_EXTINT_FALLEDGE; - break; - - case IRQT_BOTHEDGE: - newvalue = S3C2410_EXTINT_BOTHEDGE; - break; - - case IRQT_LOW: - newvalue = S3C2410_EXTINT_LOWLEV; - break; - - case IRQT_HIGH: - newvalue = S3C2410_EXTINT_HILEV; - break; - - default: - printk(KERN_ERR "No such irq type %d", type); - return -1; - } - - value = __raw_readl(extint_reg); - value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); - __raw_writel(value, extint_reg); - return 0; } -static struct irq_chip s3c_irqext_chip = { - .name = "s3c-ext", - .mask = s3c_irqext_mask, - .unmask = s3c_irqext_unmask, - .ack = s3c_irqext_ack, - .set_type = s3c_irqext_type, - .set_wake = s3c_irqext_wake -}; - -static struct irq_chip s3c_irq_eint0t4 = { - .name = "s3c-ext0", - .ack = s3c_irq_ack, - .mask = s3c_irq_mask, - .unmask = s3c_irq_unmask, - .set_wake = s3c_irq_wake, - .set_type = s3c_irqext_type, -}; - -/* mask values for the parent registers for each of the interrupt types */ - -#define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) -#define INTMSK_UART1 (1UL << (IRQ_UART1 - IRQ_EINT0)) -#define INTMSK_UART2 (1UL << (IRQ_UART2 - IRQ_EINT0)) -#define INTMSK_ADCPARENT (1UL << (IRQ_ADCPARENT - IRQ_EINT0)) - - -/* UART0 */ - -static void -s3c_irq_uart0_mask(unsigned int irqno) -{ - s3c_irqsub_mask(irqno, INTMSK_UART0, 7); -} - -static void -s3c_irq_uart0_unmask(unsigned int irqno) -{ - s3c_irqsub_unmask(irqno, INTMSK_UART0); -} - -static void -s3c_irq_uart0_ack(unsigned int irqno) -{ - s3c_irqsub_maskack(irqno, INTMSK_UART0, 7); -} - -static struct irq_chip s3c_irq_uart0 = { - .name = "s3c-uart0", - .mask = s3c_irq_uart0_mask, - .unmask = s3c_irq_uart0_unmask, - .ack = s3c_irq_uart0_ack, -}; - -/* UART1 */ - -static void -s3c_irq_uart1_mask(unsigned int irqno) -{ - s3c_irqsub_mask(irqno, INTMSK_UART1, 7 << 3); -} - -static void -s3c_irq_uart1_unmask(unsigned int irqno) -{ - s3c_irqsub_unmask(irqno, INTMSK_UART1); -} - -static void -s3c_irq_uart1_ack(unsigned int irqno) -{ - s3c_irqsub_maskack(irqno, INTMSK_UART1, 7 << 3); -} - -static struct irq_chip s3c_irq_uart1 = { - .name = "s3c-uart1", - .mask = s3c_irq_uart1_mask, - .unmask = s3c_irq_uart1_unmask, - .ack = s3c_irq_uart1_ack, -}; - -/* UART2 */ - -static void -s3c_irq_uart2_mask(unsigned int irqno) -{ - s3c_irqsub_mask(irqno, INTMSK_UART2, 7 << 6); -} - -static void -s3c_irq_uart2_unmask(unsigned int irqno) -{ - s3c_irqsub_unmask(irqno, INTMSK_UART2); -} - -static void -s3c_irq_uart2_ack(unsigned int irqno) -{ - s3c_irqsub_maskack(irqno, INTMSK_UART2, 7 << 6); -} - -static struct irq_chip s3c_irq_uart2 = { - .name = "s3c-uart2", - .mask = s3c_irq_uart2_mask, - .unmask = s3c_irq_uart2_unmask, - .ack = s3c_irq_uart2_ack, -}; - -/* ADC and Touchscreen */ - -static void -s3c_irq_adc_mask(unsigned int irqno) -{ - s3c_irqsub_mask(irqno, INTMSK_ADCPARENT, 3 << 9); -} - -static void -s3c_irq_adc_unmask(unsigned int irqno) -{ - s3c_irqsub_unmask(irqno, INTMSK_ADCPARENT); -} - -static void -s3c_irq_adc_ack(unsigned int irqno) -{ - s3c_irqsub_ack(irqno, INTMSK_ADCPARENT, 3 << 9); -} - -static struct irq_chip s3c_irq_adc = { - .name = "s3c-adc", - .mask = s3c_irq_adc_mask, - .unmask = s3c_irq_adc_unmask, - .ack = s3c_irq_adc_ack, -}; - -/* irq demux for adc */ -static void s3c_irq_demux_adc(unsigned int irq, - struct irq_desc *desc) -{ - unsigned int subsrc, submsk; - unsigned int offset = 9; - struct irq_desc *mydesc; - - /* read the current pending interrupts, and the mask - * for what it is available */ - - subsrc = __raw_readl(S3C2410_SUBSRCPND); - submsk = __raw_readl(S3C2410_INTSUBMSK); - - subsrc &= ~submsk; - subsrc >>= offset; - subsrc &= 3; - - if (subsrc != 0) { - if (subsrc & 1) { - mydesc = irq_desc + IRQ_TC; - desc_handle_irq(IRQ_TC, mydesc); - } - if (subsrc & 2) { - mydesc = irq_desc + IRQ_ADC; - desc_handle_irq(IRQ_ADC, mydesc); - } - } -} - -static void s3c_irq_demux_uart(unsigned int start) -{ - unsigned int subsrc, submsk; - unsigned int offset = start - IRQ_S3CUART_RX0; - struct irq_desc *desc; - - /* read the current pending interrupts, and the mask - * for what it is available */ - - subsrc = __raw_readl(S3C2410_SUBSRCPND); - submsk = __raw_readl(S3C2410_INTSUBMSK); - - irqdbf2("s3c_irq_demux_uart: start=%d (%d), subsrc=0x%08x,0x%08x\n", - start, offset, subsrc, submsk); - - subsrc &= ~submsk; - subsrc >>= offset; - subsrc &= 7; - - if (subsrc != 0) { - desc = irq_desc + start; - - if (subsrc & 1) - desc_handle_irq(start, desc); - - desc++; - - if (subsrc & 2) - desc_handle_irq(start+1, desc); - - desc++; - - if (subsrc & 4) - desc_handle_irq(start+2, desc); - } -} - -/* uart demux entry points */ - -static void -s3c_irq_demux_uart0(unsigned int irq, - struct irq_desc *desc) -{ - irq = irq; - s3c_irq_demux_uart(IRQ_S3CUART_RX0); -} - -static void -s3c_irq_demux_uart1(unsigned int irq, - struct irq_desc *desc) -{ - irq = irq; - s3c_irq_demux_uart(IRQ_S3CUART_RX1); -} - -static void -s3c_irq_demux_uart2(unsigned int irq, - struct irq_desc *desc) -{ - irq = irq; - s3c_irq_demux_uart(IRQ_S3CUART_RX2); -} - -static void -s3c_irq_demux_extint8(unsigned int irq, - struct irq_desc *desc) -{ - unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); - unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); - - eintpnd &= ~eintmsk; - eintpnd &= ~0xff; /* ignore lower irqs */ - - /* we may as well handle all the pending IRQs here */ - - while (eintpnd) { - irq = __ffs(eintpnd); - eintpnd &= ~(1<<irq); - - irq += (IRQ_EINT4 - 4); - desc_handle_irq(irq, irq_desc + irq); - } - -} - -static void -s3c_irq_demux_extint4t7(unsigned int irq, - struct irq_desc *desc) -{ - unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); - unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); - - eintpnd &= ~eintmsk; - eintpnd &= 0xff; /* only lower irqs */ - - /* we may as well handle all the pending IRQs here */ - - while (eintpnd) { - irq = __ffs(eintpnd); - eintpnd &= ~(1<<irq); - - irq += (IRQ_EINT4 - 4); - - desc_handle_irq(irq, irq_desc + irq); - } -} - -#ifdef CONFIG_PM - -static struct sleep_save irq_save[] = { - SAVE_ITEM(S3C2410_INTMSK), - SAVE_ITEM(S3C2410_INTSUBMSK), +static struct sysdev_driver s3c2410_irq_driver = { + .add = s3c2410_irq_add, + .suspend = s3c24xx_irq_suspend, + .resume = s3c24xx_irq_resume, }; -/* the extint values move between the s3c2410/s3c2440 and the s3c2412 - * so we use an array to hold them, and to calculate the address of - * the register at run-time -*/ - -static unsigned long save_extint[3]; -static unsigned long save_eintflt[4]; -static unsigned long save_eintmask; - -int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state) +static int s3c2410_irq_init(void) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(save_extint); i++) - save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4)); - - for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) - save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4)); - - s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); - save_eintmask = __raw_readl(S3C24XX_EINTMASK); - - return 0; + return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_irq_driver); } -int s3c24xx_irq_resume(struct sys_device *dev) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(save_extint); i++) - __raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4)); - - for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) - __raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4)); - - s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); - __raw_writel(save_eintmask, S3C24XX_EINTMASK); - - return 0; -} - -#else -#define s3c24xx_irq_suspend NULL -#define s3c24xx_irq_resume NULL -#endif - -/* s3c24xx_init_irq - * - * Initialise S3C2410 IRQ system -*/ - -void __init s3c24xx_init_irq(void) -{ - unsigned long pend; - unsigned long last; - int irqno; - int i; - - irqdbf("s3c2410_init_irq: clearing interrupt status flags\n"); - - /* first, clear all interrupts pending... */ - - last = 0; - for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C24XX_EINTPEND); - - if (pend == 0 || pend == last) - break; - - __raw_writel(pend, S3C24XX_EINTPEND); - printk("irq: clearing pending ext status %08x\n", (int)pend); - last = pend; - } - - last = 0; - for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_INTPND); - - if (pend == 0 || pend == last) - break; - - __raw_writel(pend, S3C2410_SRCPND); - __raw_writel(pend, S3C2410_INTPND); - printk("irq: clearing pending status %08x\n", (int)pend); - last = pend; - } - - last = 0; - for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_SUBSRCPND); - - if (pend == 0 || pend == last) - break; - - printk("irq: clearing subpending status %08x\n", (int)pend); - __raw_writel(pend, S3C2410_SUBSRCPND); - last = pend; - } - - /* register the main interrupts */ - - irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); - - for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { - /* set all the s3c2410 internal irqs */ - - switch (irqno) { - /* deal with the special IRQs (cascaded) */ - - case IRQ_EINT4t7: - case IRQ_EINT8t23: - case IRQ_UART0: - case IRQ_UART1: - case IRQ_UART2: - case IRQ_ADCPARENT: - set_irq_chip(irqno, &s3c_irq_level_chip); - set_irq_handler(irqno, handle_level_irq); - break; - - case IRQ_RESERVED6: - case IRQ_RESERVED24: - /* no IRQ here */ - break; - - default: - //irqdbf("registering irq %d (s3c irq)\n", irqno); - set_irq_chip(irqno, &s3c_irq_chip); - set_irq_handler(irqno, handle_edge_irq); - set_irq_flags(irqno, IRQF_VALID); - } - } - - /* setup the cascade irq handlers */ - - set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7); - set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); - - set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); - set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); - set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); - set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); - - /* external interrupts */ - - for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { - irqdbf("registering irq %d (ext int)\n", irqno); - set_irq_chip(irqno, &s3c_irq_eint0t4); - set_irq_handler(irqno, handle_edge_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { - irqdbf("registering irq %d (extended s3c irq)\n", irqno); - set_irq_chip(irqno, &s3c_irqext_chip); - set_irq_handler(irqno, handle_edge_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - /* register the uart interrupts */ - - irqdbf("s3c2410: registering external interrupts\n"); - - for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { - irqdbf("registering irq %d (s3c uart0 irq)\n", irqno); - set_irq_chip(irqno, &s3c_irq_uart0); - set_irq_handler(irqno, handle_level_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { - irqdbf("registering irq %d (s3c uart1 irq)\n", irqno); - set_irq_chip(irqno, &s3c_irq_uart1); - set_irq_handler(irqno, handle_level_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { - irqdbf("registering irq %d (s3c uart2 irq)\n", irqno); - set_irq_chip(irqno, &s3c_irq_uart2); - set_irq_handler(irqno, handle_level_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { - irqdbf("registering irq %d (s3c adc irq)\n", irqno); - set_irq_chip(irqno, &s3c_irq_adc); - set_irq_handler(irqno, handle_edge_irq); - set_irq_flags(irqno, IRQF_VALID); - } - - irqdbf("s3c2410: registered interrupt handlers\n"); -} +arch_initcall(s3c2410_irq_init); diff --git a/arch/arm/mach-s3c2410/irq.h b/arch/arm/mach-s3c2410/irq.h deleted file mode 100644 index e5913da3b919..000000000000 --- a/arch/arm/mach-s3c2410/irq.h +++ /dev/null @@ -1,107 +0,0 @@ -/* arch/arm/mach-s3c2410/irq.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for S3C24XX CPU IRQ 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. -*/ - -#define irqdbf(x...) -#define irqdbf2(x...) - -#define EXTINT_OFF (IRQ_EINT4 - 4) - -extern struct irq_chip s3c_irq_level_chip; - -static inline void -s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, - int subcheck) -{ - unsigned long mask; - unsigned long submask; - - submask = __raw_readl(S3C2410_INTSUBMSK); - mask = __raw_readl(S3C2410_INTMSK); - - submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); - - /* check to see if we need to mask the parent IRQ */ - - if ((submask & subcheck) == subcheck) { - __raw_writel(mask | parentbit, S3C2410_INTMSK); - } - - /* write back masks */ - __raw_writel(submask, S3C2410_INTSUBMSK); - -} - -static inline void -s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) -{ - unsigned long mask; - unsigned long submask; - - submask = __raw_readl(S3C2410_INTSUBMSK); - mask = __raw_readl(S3C2410_INTMSK); - - submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0)); - mask &= ~parentbit; - - /* write back masks */ - __raw_writel(submask, S3C2410_INTSUBMSK); - __raw_writel(mask, S3C2410_INTMSK); -} - - -static inline void -s3c_irqsub_maskack(unsigned int irqno, unsigned int parentmask, unsigned int group) -{ - unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); - - s3c_irqsub_mask(irqno, parentmask, group); - - __raw_writel(bit, S3C2410_SUBSRCPND); - - /* only ack parent if we've got all the irqs (seems we must - * ack, all and hope that the irq system retriggers ok when - * the interrupt goes off again) - */ - - if (1) { - __raw_writel(parentmask, S3C2410_SRCPND); - __raw_writel(parentmask, S3C2410_INTPND); - } -} - -static inline void -s3c_irqsub_ack(unsigned int irqno, unsigned int parentmask, unsigned int group) -{ - unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); - - __raw_writel(bit, S3C2410_SUBSRCPND); - - /* only ack parent if we've got all the irqs (seems we must - * ack, all and hope that the irq system retriggers ok when - * the interrupt goes off again) - */ - - if (1) { - __raw_writel(parentmask, S3C2410_SRCPND); - __raw_writel(parentmask, S3C2410_INTPND); - } -} - -/* exported for use in arch/arm/mach-s3c2410 */ - -#ifdef CONFIG_PM -extern int s3c_irq_wake(unsigned int irqno, unsigned int state); -#else -#define s3c_irq_wake NULL -#endif - -extern int s3c_irqext_type(unsigned int irq, unsigned int type); diff --git a/arch/arm/mach-s3c2410/mach-amlm5900.c b/arch/arm/mach-s3c2410/mach-amlm5900.c index 817e2c684410..d54cda1b3006 100644 --- a/arch/arm/mach-s3c2410/mach-amlm5900.c +++ b/arch/arm/mach-s3c2410/mach-amlm5900.c @@ -1,4 +1,4 @@ -/*********************************************************************** +/* linux/arch/arm/mach-s3c2410/mach-amlm5900.c * * linux/arch/arm/mach-s3c2410/mach-amlm5900.c * @@ -52,8 +52,8 @@ #include <asm/arch/regs-lcd.h> #include <asm/arch/regs-gpio.h> -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> #ifdef CONFIG_MTD_PARTITIONS diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c index b8b76757ec54..7b81296427eb 100644 --- a/arch/arm/mach-s3c2410/mach-bast.c +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -50,9 +50,9 @@ #include <linux/serial_8250.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> #include "usb-simtec.h" #define COPYRIGHT ", (c) 2004-2005 Simtec Electronics" diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 15b625eae499..5af26e177966 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -38,10 +38,10 @@ #include <asm/arch/h1940-latch.h> #include <asm/arch/fb.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" -#include "pm.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> static struct map_desc h1940_iodesc[] __initdata = { [0] = { diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c index 0411e9adb54d..dbac7311f98f 100644 --- a/arch/arm/mach-s3c2410/mach-n30.c +++ b/arch/arm/mach-s3c2410/mach-n30.c @@ -38,10 +38,10 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/iic.h> -#include "s3c2410.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> static struct map_desc n30_iodesc[] __initdata = { /* nothing here yet */ diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c index 2c738b375e4d..c78ab75b44f3 100644 --- a/arch/arm/mach-s3c2410/mach-otom.c +++ b/arch/arm/mach-s3c2410/mach-otom.c @@ -32,10 +32,10 @@ #include <asm/arch/regs-serial.h> #include <asm/arch/regs-gpio.h> -#include "s3c2410.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> static struct map_desc otom11_iodesc[] __initdata = { /* Device area */ diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index 01c0c986d827..57b8a80f33d0 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -1,4 +1,4 @@ -/*********************************************************************** +/* linux/arch/arm/mach-s3c2410/mach-smdk2410.c * * linux/arch/arm/mach-s3c2410/mach-smdk2410.c * @@ -49,10 +49,10 @@ #include <asm/arch/regs-serial.h> -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> -#include "common-smdk.h" +#include <asm/plat-s3c24xx/common-smdk.h> static struct map_desc smdk2410_iodesc[] __initdata = { /* nothing here yet */ diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c index a382fc095110..c947c75bcbf0 100644 --- a/arch/arm/mach-s3c2410/mach-vr1000.c +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -43,9 +43,9 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/leds-gpio.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> #include "usb-simtec.h" /* macros for virtual address mods for the io space entries */ diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c index ebf294dd31da..3b3a7db4e0dd 100644 --- a/arch/arm/mach-s3c2410/pm.c +++ b/arch/arm/mach-s3c2410/pm.c @@ -1,11 +1,9 @@ /* linux/arch/arm/mach-s3c2410/pm.c * - * Copyright (c) 2004,2006 Simtec Electronics + * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C24XX Power Manager (Suspend-To-RAM) support - * - * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information + * S3C2410 (and compatible) Power Manager (Suspend-To-RAM) 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 @@ -20,640 +18,139 @@ * 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 - * - * Parts based on arch/arm/mach-pxa/pm.c - * - * Thanks to Dimitry Andric for debugging */ #include <linux/init.h> #include <linux/suspend.h> #include <linux/errno.h> #include <linux/time.h> -#include <linux/interrupt.h> -#include <linux/crc32.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/serial_core.h> +#include <linux/sysdev.h> -#include <asm/cacheflush.h> #include <asm/hardware.h> #include <asm/io.h> -#include <asm/arch/regs-serial.h> -#include <asm/arch/regs-clock.h> -#include <asm/arch/regs-gpio.h> -#include <asm/arch/regs-mem.h> -#include <asm/arch/regs-irq.h> - -#include <asm/mach/time.h> - -#include "pm.h" - -/* for external use */ - -unsigned long s3c_pm_flags; - -#define PFX "s3c24xx-pm: " - -static struct sleep_save core_save[] = { - SAVE_ITEM(S3C2410_LOCKTIME), - SAVE_ITEM(S3C2410_CLKCON), - - /* we restore the timings here, with the proviso that the board - * brings the system up in an slower, or equal frequency setting - * to the original system. - * - * if we cannot guarantee this, then things are going to go very - * wrong here, as we modify the refresh and both pll settings. - */ - - SAVE_ITEM(S3C2410_BWSCON), - SAVE_ITEM(S3C2410_BANKCON0), - SAVE_ITEM(S3C2410_BANKCON1), - SAVE_ITEM(S3C2410_BANKCON2), - SAVE_ITEM(S3C2410_BANKCON3), - SAVE_ITEM(S3C2410_BANKCON4), - SAVE_ITEM(S3C2410_BANKCON5), - - SAVE_ITEM(S3C2410_CLKDIVN), - SAVE_ITEM(S3C2410_MPLLCON), - SAVE_ITEM(S3C2410_UPLLCON), - SAVE_ITEM(S3C2410_CLKSLOW), - SAVE_ITEM(S3C2410_REFRESH), -}; - -static struct sleep_save gpio_save[] = { - SAVE_ITEM(S3C2410_GPACON), - SAVE_ITEM(S3C2410_GPADAT), - - SAVE_ITEM(S3C2410_GPBCON), - SAVE_ITEM(S3C2410_GPBDAT), - SAVE_ITEM(S3C2410_GPBUP), - - SAVE_ITEM(S3C2410_GPCCON), - SAVE_ITEM(S3C2410_GPCDAT), - SAVE_ITEM(S3C2410_GPCUP), - - SAVE_ITEM(S3C2410_GPDCON), - SAVE_ITEM(S3C2410_GPDDAT), - SAVE_ITEM(S3C2410_GPDUP), - - SAVE_ITEM(S3C2410_GPECON), - SAVE_ITEM(S3C2410_GPEDAT), - SAVE_ITEM(S3C2410_GPEUP), - - SAVE_ITEM(S3C2410_GPFCON), - SAVE_ITEM(S3C2410_GPFDAT), - SAVE_ITEM(S3C2410_GPFUP), +#include <asm/mach-types.h> - SAVE_ITEM(S3C2410_GPGCON), - SAVE_ITEM(S3C2410_GPGDAT), - SAVE_ITEM(S3C2410_GPGUP), - - SAVE_ITEM(S3C2410_GPHCON), - SAVE_ITEM(S3C2410_GPHDAT), - SAVE_ITEM(S3C2410_GPHUP), +#include <asm/arch/regs-gpio.h> +#include <asm/arch/h1940.h> - SAVE_ITEM(S3C2410_DCLKCON), -}; +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> #ifdef CONFIG_S3C2410_PM_DEBUG - -#define SAVE_UART(va) \ - SAVE_ITEM((va) + S3C2410_ULCON), \ - SAVE_ITEM((va) + S3C2410_UCON), \ - SAVE_ITEM((va) + S3C2410_UFCON), \ - SAVE_ITEM((va) + S3C2410_UMCON), \ - SAVE_ITEM((va) + S3C2410_UBRDIV) - -static struct sleep_save uart_save[] = { - SAVE_UART(S3C24XX_VA_UART0), - SAVE_UART(S3C24XX_VA_UART1), -#ifndef CONFIG_CPU_S3C2400 - SAVE_UART(S3C24XX_VA_UART2), -#endif -}; - -/* debug - * - * we send the debug to printascii() to allow it to be seen if the - * system never wakes up from the sleep -*/ - -extern void printascii(const char *); - -void pm_dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsprintf(buff, fmt, va); - va_end(va); - - printascii(buff); -} - -static void s3c2410_pm_debug_init(void) -{ - unsigned long tmp = __raw_readl(S3C2410_CLKCON); - - /* re-start uart clocks */ - tmp |= S3C2410_CLKCON_UART0; - tmp |= S3C2410_CLKCON_UART1; - tmp |= S3C2410_CLKCON_UART2; - - __raw_writel(tmp, S3C2410_CLKCON); - udelay(10); -} - +extern void pm_dbg(const char *fmt, ...); #define DBG(fmt...) pm_dbg(fmt) #else #define DBG(fmt...) printk(KERN_DEBUG fmt) - -#define s3c2410_pm_debug_init() do { } while(0) - -static struct sleep_save uart_save[] = {}; #endif -#if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0 - -/* suspend checking code... - * - * this next area does a set of crc checks over all the installed - * memory, so the system can verify if the resume was ok. - * - * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, - * increasing it will mean that the area corrupted will be less easy to spot, - * and reducing the size will cause the CRC save area to grow -*/ - -#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) - -static u32 crc_size; /* size needed for the crc block */ -static u32 *crcs; /* allocated over suspend/resume */ - -typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); - -/* s3c2410_pm_run_res - * - * go thorugh the given resource list, and look for system ram -*/ - -static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) -{ - while (ptr != NULL) { - if (ptr->child != NULL) - s3c2410_pm_run_res(ptr->child, fn, arg); - - if ((ptr->flags & IORESOURCE_MEM) && - strcmp(ptr->name, "System RAM") == 0) { - DBG("Found system RAM at %08lx..%08lx\n", - ptr->start, ptr->end); - arg = (fn)(ptr, arg); - } - - ptr = ptr->sibling; - } -} - -static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg) -{ - s3c2410_pm_run_res(&iomem_resource, fn, arg); -} - -static u32 *s3c2410_pm_countram(struct resource *res, u32 *val) -{ - u32 size = (u32)(res->end - res->start)+1; - - size += CHECK_CHUNKSIZE-1; - size /= CHECK_CHUNKSIZE; - - DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); - - *val += size * sizeof(u32); - return val; -} - -/* s3c2410_pm_prepare_check - * - * prepare the necessary information for creating the CRCs. This - * must be done before the final save, as it will require memory - * allocating, and thus touching bits of the kernel we do not - * know about. -*/ - -static void s3c2410_pm_check_prepare(void) +static void s3c2410_pm_prepare(void) { - crc_size = 0; + /* ensure at least GSTATUS3 has the resume address */ - s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size); + __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3); - DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); + DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); + DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); - crcs = kmalloc(crc_size+4, GFP_KERNEL); - if (crcs == NULL) - printk(KERN_ERR "Cannot allocated CRC save area\n"); -} + if (machine_is_h1940()) { + void *base = phys_to_virt(H1940_SUSPEND_CHECK); + unsigned long ptr; + unsigned long calc = 0; -static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val) -{ - unsigned long addr, left; + /* generate check for the bootloader to check on resume */ - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; + for (ptr = 0; ptr < 0x40000; ptr += 0x400) + calc += __raw_readl(base+ptr); - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; - - *val = crc32_le(~0, phys_to_virt(addr), left); - val++; + __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); } - return val; -} - -/* s3c2410_pm_check_store - * - * compute the CRC values for the memory blocks before the final - * sleep. -*/ - -static void s3c2410_pm_check_store(void) -{ - if (crcs != NULL) - s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs); -} - -/* in_region - * - * return TRUE if the area defined by ptr..ptr+size contatins the - * what..what+whatsz -*/ - -static inline int in_region(void *ptr, int size, void *what, size_t whatsz) -{ - if ((what+whatsz) < ptr) - return 0; - - if (what > (ptr+size)) - return 0; - - return 1; -} - -static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) -{ - void *save_at = phys_to_virt(s3c2410_sleep_save_phys); - unsigned long addr; - unsigned long left; - void *ptr; - u32 calc; - - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; + /* the RX3715 uses similar code and the same H1940 and the + * same offsets for resume and checksum pointers */ - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; + if (machine_is_rx3715()) { + void *base = phys_to_virt(H1940_SUSPEND_CHECK); + unsigned long ptr; + unsigned long calc = 0; - ptr = phys_to_virt(addr); + /* generate check for the bootloader to check on resume */ - if (in_region(ptr, left, crcs, crc_size)) { - DBG("skipping %08lx, has crc block in\n", addr); - goto skip_check; - } + for (ptr = 0; ptr < 0x40000; ptr += 0x4) + calc += __raw_readl(base+ptr); - if (in_region(ptr, left, save_at, 32*4 )) { - DBG("skipping %08lx, has save block in\n", addr); - goto skip_check; - } - - /* calculate and check the checksum */ - - calc = crc32_le(~0, ptr, left); - if (calc != *val) { - printk(KERN_ERR PFX "Restore CRC error at " - "%08lx (%08x vs %08x)\n", addr, calc, *val); - - DBG("Restore CRC error at %08lx (%08x vs %08x)\n", - addr, calc, *val); - } - - skip_check: - val++; + __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); } - return val; -} + if ( machine_is_aml_m5900() ) + s3c2410_gpio_setpin(S3C2410_GPF2, 1); -/* s3c2410_pm_check_restore - * - * check the CRCs after the restore event and free the memory used - * to hold them -*/ - -static void s3c2410_pm_check_restore(void) -{ - if (crcs != NULL) { - s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs); - kfree(crcs); - crcs = NULL; - } } -#else - -#define s3c2410_pm_check_prepare() do { } while(0) -#define s3c2410_pm_check_restore() do { } while(0) -#define s3c2410_pm_check_store() do { } while(0) -#endif - -/* helper functions to save and restore register state */ - -void s3c2410_pm_do_save(struct sleep_save *ptr, int count) +static int s3c2410_pm_resume(struct sys_device *dev) { - for (; count > 0; count--, ptr++) { - ptr->val = __raw_readl(ptr->reg); - DBG("saved %p value %08lx\n", ptr->reg, ptr->val); - } -} + unsigned long tmp; -/* s3c2410_pm_do_restore - * - * restore the system from the given list of saved registers - * - * Note, we do not use DBG() in here, as the system may not have - * restore the UARTs state yet -*/ + /* unset the return-from-sleep flag, to ensure reset */ -void s3c2410_pm_do_restore(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", - ptr->reg, ptr->val, __raw_readl(ptr->reg)); - - __raw_writel(ptr->val, ptr->reg); - } -} + tmp = __raw_readl(S3C2410_GSTATUS2); + tmp &= S3C2410_GSTATUS2_OFFRESET; + __raw_writel(tmp, S3C2410_GSTATUS2); -/* s3c2410_pm_do_restore_core - * - * similar to s3c2410_pm_do_restore_core - * - * WARNING: Do not put any debug in here that may effect memory or use - * peripherals, as things may be changing! -*/ + if ( machine_is_aml_m5900() ) + s3c2410_gpio_setpin(S3C2410_GPF2, 0); -static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - __raw_writel(ptr->val, ptr->reg); - } + return 0; } -/* s3c2410_pm_show_resume_irqs - * - * print any IRQs asserted at resume time (ie, we woke from) -*/ - -static void s3c2410_pm_show_resume_irqs(int start, unsigned long which, - unsigned long mask) +static int s3c2410_pm_add(struct sys_device *dev) { - int i; + pm_cpu_prep = s3c2410_pm_prepare; + pm_cpu_sleep = s3c2410_cpu_suspend; - which &= ~mask; - - for (i = 0; i <= 31; i++) { - if ((which) & (1L<<i)) { - DBG("IRQ %d asserted at resume\n", start+i); - } - } + return 0; } -/* s3c2410_pm_check_resume_pin - * - * check to see if the pin is configured correctly for sleep mode, and - * make any necessary adjustments if it is not -*/ - -static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) -{ - unsigned long irqstate; - unsigned long pinstate; - int irq = s3c2410_gpio_getirq(pin); - - if (irqoffs < 4) - irqstate = s3c_irqwake_intmask & (1L<<irqoffs); - else - irqstate = s3c_irqwake_eintmask & (1L<<irqoffs); - - pinstate = s3c2410_gpio_getcfg(pin); - - if (!irqstate) { - if (pinstate == S3C2410_GPIO_IRQ) - DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); - } else { - if (pinstate == S3C2410_GPIO_IRQ) { - DBG("Disabling IRQ %d (pin %d)\n", irq, pin); - s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT); - } - } -} +#if defined(CONFIG_CPU_S3C2410) +static struct sysdev_driver s3c2410_pm_driver = { + .add = s3c2410_pm_add, + .resume = s3c2410_pm_resume, +}; -/* s3c2410_pm_configure_extint - * - * configure all external interrupt pins -*/ +/* register ourselves */ -static void s3c2410_pm_configure_extint(void) +static int __init s3c2410_pm_drvinit(void) { - int pin; - - /* for each of the external interrupts (EINT0..EINT15) we - * need to check wether it is an external interrupt source, - * and then configure it as an input if it is not - */ - - for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) { - s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0); - } - - for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) { - s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8); - } + return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_pm_driver); } -void (*pm_cpu_prep)(void); -void (*pm_cpu_sleep)(void); - -#define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) - -/* s3c2410_pm_enter - * - * central control for sleep/resume process -*/ - -static int s3c2410_pm_enter(suspend_state_t state) -{ - unsigned long regs_save[16]; - - /* ensure the debug is initialised (if enabled) */ - - s3c2410_pm_debug_init(); - - DBG("s3c2410_pm_enter(%d)\n", state); - - if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { - printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); - return -EINVAL; - } - - if (state != PM_SUSPEND_MEM) { - printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); - return -EINVAL; - } - - /* check if we have anything to wake-up with... bad things seem - * to happen if you suspend with no wakeup (system will often - * require a full power-cycle) - */ - - if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && - !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { - printk(KERN_ERR PFX "No sources enabled for wake-up!\n"); - printk(KERN_ERR PFX "Aborting sleep\n"); - return -EINVAL; - } - - /* prepare check area if configured */ - - s3c2410_pm_check_prepare(); - - /* store the physical address of the register recovery block */ - - s3c2410_sleep_save_phys = virt_to_phys(regs_save); - - DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys); - - /* save all necessary core registers not covered by the drivers */ - - s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); - s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); - s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); - - /* set the irq configuration for wake */ - - s3c2410_pm_configure_extint(); - - DBG("sleep: irq wakeup masks: %08lx,%08lx\n", - s3c_irqwake_intmask, s3c_irqwake_eintmask); - - __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); - __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); - - /* ack any outstanding external interrupts before we go to sleep */ - - __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); - __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); - __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); - - /* call cpu specific preperation */ - - pm_cpu_prep(); - - /* flush cache back to ram */ - - flush_cache_all(); - - s3c2410_pm_check_store(); - - /* send the cpu to sleep... */ - - __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ - - /* s3c2410_cpu_save will also act as our return point from when - * we resume as it saves its own register state, so use the return - * code to differentiate return from save and return from sleep */ - - if (s3c2410_cpu_save(regs_save) == 0) { - flush_cache_all(); - pm_cpu_sleep(); - } - - /* restore the cpu state */ - - cpu_init(); - - /* restore the system state */ - - s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); - s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save)); - s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); - - s3c2410_pm_debug_init(); - - /* check what irq (if any) restored the system */ - - DBG("post sleep: IRQs 0x%08x, 0x%08x\n", - __raw_readl(S3C2410_SRCPND), - __raw_readl(S3C2410_EINTPEND)); - - s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), - s3c_irqwake_intmask); - - s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), - s3c_irqwake_eintmask); - - DBG("post sleep, preparing to return\n"); - - s3c2410_pm_check_restore(); - - /* ok, let's return from sleep */ +arch_initcall(s3c2410_pm_drvinit); +#endif - DBG("S3C2410 PM Resume (post-restore)\n"); - return 0; -} +#if defined(CONFIG_CPU_S3C2440) +static struct sysdev_driver s3c2440_pm_driver = { + .add = s3c2410_pm_add, + .resume = s3c2410_pm_resume, +}; -/* - * Called after processes are frozen, but before we shut down devices. - */ -static int s3c2410_pm_prepare(suspend_state_t state) +static int __init s3c2440_pm_drvinit(void) { - return 0; + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_pm_driver); } -/* - * Called after devices are re-setup, but before processes are thawed. - */ -static int s3c2410_pm_finish(suspend_state_t state) -{ - return 0; -} +arch_initcall(s3c2440_pm_drvinit); +#endif -/* - * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. - */ -static struct pm_ops s3c2410_pm_ops = { - .pm_disk_mode = PM_DISK_FIRMWARE, - .prepare = s3c2410_pm_prepare, - .enter = s3c2410_pm_enter, - .finish = s3c2410_pm_finish, +#if defined(CONFIG_CPU_S3C2442) +static struct sysdev_driver s3c2442_pm_driver = { + .add = s3c2410_pm_add, + .resume = s3c2410_pm_resume, }; -/* s3c2410_pm_init - * - * Attach the power management functions. This should be called - * from the board specific initialisation if the board supports - * it. -*/ - -int __init s3c2410_pm_init(void) +static int __init s3c2442_pm_drvinit(void) { - printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); - - pm_set_ops(&s3c2410_pm_ops); - return 0; + return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_pm_driver); } + +arch_initcall(s3c2442_pm_drvinit); +#endif diff --git a/arch/arm/mach-s3c2410/pm.h b/arch/arm/mach-s3c2410/pm.h deleted file mode 100644 index ffe197a119fb..000000000000 --- a/arch/arm/mach-s3c2410/pm.h +++ /dev/null @@ -1,73 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/pm.h - * - * Copyright (c) 2004 Simtec Electronics - * Written by Ben Dooks, <ben@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. -*/ - -/* s3c2410_pm_init - * - * called from board at initialisation time to setup the power - * management -*/ - -#ifdef CONFIG_PM - -extern __init int s3c2410_pm_init(void); - -#else - -static inline int s3c2410_pm_init(void) -{ - return 0; -} -#endif - -/* configuration for the IRQ mask over sleep */ -extern unsigned long s3c_irqwake_intmask; -extern unsigned long s3c_irqwake_eintmask; - -/* IRQ masks for IRQs allowed to go to sleep (see irq.c) */ -extern unsigned long s3c_irqwake_intallow; -extern unsigned long s3c_irqwake_eintallow; - -/* per-cpu sleep functions */ - -extern void (*pm_cpu_prep)(void); -extern void (*pm_cpu_sleep)(void); - -/* Flags for PM Control */ - -extern unsigned long s3c_pm_flags; - -/* from sleep.S */ - -extern int s3c2410_cpu_save(unsigned long *saveblk); -extern void s3c2410_cpu_suspend(void); -extern void s3c2410_cpu_resume(void); - -extern unsigned long s3c2410_sleep_save_phys; - -/* sleep save info */ - -struct sleep_save { - void __iomem *reg; - unsigned long val; -}; - -#define SAVE_ITEM(x) \ - { .reg = (x) } - -extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count); -extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count); - -#ifdef CONFIG_PM -extern int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state); -extern int s3c24xx_irq_resume(struct sys_device *dev); -#else -#define s3c24xx_irq_suspend NULL -#define s3c24xx_irq_resume NULL -#endif diff --git a/arch/arm/mach-s3c2410/s3c2400.h b/arch/arm/mach-s3c2410/s3c2400.h deleted file mode 100644 index 8b2394e1ed40..000000000000 --- a/arch/arm/mach-s3c2410/s3c2400.h +++ /dev/null @@ -1,31 +0,0 @@ -/* arch/arm/mach-s3c2410/s3c2400.h - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for S3C2400 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. - * - * Modifications: - * 09-Fev-2006 LCVR First version, based on s3c2410.h -*/ - -#ifdef CONFIG_CPU_S3C2400 - -extern int s3c2400_init(void); - -extern void s3c2400_map_io(struct map_desc *mach_desc, int size); - -extern void s3c2400_init_uarts(struct s3c2410_uartcfg *cfg, int no); - -extern void s3c2400_init_clocks(int xtal); - -#else -#define s3c2400_init_clocks NULL -#define s3c2400_init_uarts NULL -#define s3c2400_map_io NULL -#define s3c2400_init NULL -#endif diff --git a/arch/arm/mach-s3c2410/s3c2410-clock.c b/arch/arm/mach-s3c2410/s3c2410-clock.c deleted file mode 100644 index 992cc6af230e..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-clock.c +++ /dev/null @@ -1,276 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-clock.c - * - * 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/sysdev.h> -#include <linux/clk.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/serial_core.h> - -#include <asm/mach/map.h> - -#include <asm/hardware.h> -#include <asm/io.h> - -#include <asm/arch/regs-serial.h> -#include <asm/arch/regs-clock.h> -#include <asm/arch/regs-gpio.h> - -#include "s3c2410.h" -#include "clock.h" -#include "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_disable[] = { - { - .name = "nand", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_NAND, - }, { - .name = "sdi", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_SDI, - }, { - .name = "adc", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_ADC, - }, { - .name = "i2c", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_IIC, - }, { - .name = "iis", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_IIS, - }, { - .name = "spi", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_SPI, - } -}; - -static struct clk init_clocks[] = { - { - .name = "lcd", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_LCDC, - }, { - .name = "gpio", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_GPIO, - }, { - .name = "usb-host", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_USBH, - }, { - .name = "usb-device", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_USBD, - }, { - .name = "timers", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_PWMT, - }, { - .name = "uart", - .id = 0, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART0, - }, { - .name = "uart", - .id = 1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART1, - }, { - .name = "uart", - .id = 2, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART2, - }, { - .name = "rtc", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_RTC, - }, { - .name = "watchdog", - .id = -1, - .parent = &clk_p, - .ctrlbit = 0, - }, { - .name = "usb-bus-host", - .id = -1, - .parent = &clk_usb_bus, - }, { - .name = "usb-bus-gadget", - .id = -1, - .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 subsytems 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 */ - - 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); - } - - s3c2410_clkcon_enable(clkp, 0); - } - - /* 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"); - - return 0; -} diff --git a/arch/arm/mach-s3c2410/s3c2410-dma.c b/arch/arm/mach-s3c2410/s3c2410-dma.c deleted file mode 100644 index e67ba3911f11..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-dma.c +++ /dev/null @@ -1,161 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-dma.c - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * S3C2410 DMA selection - * - * 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. -*/ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sysdev.h> -#include <linux/serial_core.h> - -#include <asm/dma.h> -#include <asm/arch/dma.h> -#include "dma.h" - -#include "cpu.h" - -#include <asm/arch/regs-serial.h> -#include <asm/arch/regs-gpio.h> -#include <asm/arch/regs-ac97.h> -#include <asm/arch/regs-mem.h> -#include <asm/arch/regs-lcd.h> -#include <asm/arch/regs-sdi.h> -#include <asm/arch/regs-iis.h> -#include <asm/arch/regs-spi.h> - -static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = { - [DMACH_XD0] = { - .name = "xdreq0", - .channels[0] = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID, - }, - [DMACH_XD1] = { - .name = "xdreq1", - .channels[1] = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID, - }, - [DMACH_SDI] = { - .name = "sdi", - .channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID, - .channels[2] = S3C2410_DCON_CH2_SDI | DMA_CH_VALID, - .channels[3] = S3C2410_DCON_CH3_SDI | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO, - .hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO, - }, - [DMACH_SPI0] = { - .name = "spi0", - .channels[1] = S3C2410_DCON_CH1_SPI | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT, - .hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT, - }, - [DMACH_SPI1] = { - .name = "spi1", - .channels[3] = S3C2410_DCON_CH3_SPI | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT, - .hw_addr.from = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT, - }, - [DMACH_UART0] = { - .name = "uart0", - .channels[0] = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH, - .hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH, - }, - [DMACH_UART1] = { - .name = "uart1", - .channels[1] = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH, - .hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH, - }, - [DMACH_UART2] = { - .name = "uart2", - .channels[3] = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH, - .hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH, - }, - [DMACH_TIMER] = { - .name = "timer", - .channels[0] = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID, - .channels[2] = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID, - .channels[3] = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID, - }, - [DMACH_I2S_IN] = { - .name = "i2s-sdi", - .channels[1] = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID, - .channels[2] = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID, - .hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO, - }, - [DMACH_I2S_OUT] = { - .name = "i2s-sdo", - .channels[2] = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID, - .hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO, - }, - [DMACH_USB_EP1] = { - .name = "usb-ep1", - .channels[0] = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID, - }, - [DMACH_USB_EP2] = { - .name = "usb-ep2", - .channels[1] = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID, - }, - [DMACH_USB_EP3] = { - .name = "usb-ep3", - .channels[2] = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID, - }, - [DMACH_USB_EP4] = { - .name = "usb-ep4", - .channels[3] =S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID, - }, -}; - -static void s3c2410_dma_select(struct s3c2410_dma_chan *chan, - struct s3c24xx_dma_map *map) -{ - chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID; -} - -static struct s3c24xx_dma_selection __initdata s3c2410_dma_sel = { - .select = s3c2410_dma_select, - .dcon_mask = 7 << 24, - .map = s3c2410_dma_mappings, - .map_size = ARRAY_SIZE(s3c2410_dma_mappings), -}; - -static int s3c2410_dma_add(struct sys_device *sysdev) -{ - return s3c24xx_dma_init_map(&s3c2410_dma_sel); -} - -#if defined(CONFIG_CPU_S3C2410) -static struct sysdev_driver s3c2410_dma_driver = { - .add = s3c2410_dma_add, -}; - -static int __init s3c2410_dma_init(void) -{ - return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver); -} - -arch_initcall(s3c2410_dma_init); -#endif - -#if defined(CONFIG_CPU_S3C2442) -/* S3C2442 DMA contains the same selection table as the S3C2410 */ -static struct sysdev_driver s3c2442_dma_driver = { - .add = s3c2410_dma_add, -}; - -static int __init s3c2442_dma_init(void) -{ - return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_dma_driver); -} - -arch_initcall(s3c2442_dma_init); -#endif - diff --git a/arch/arm/mach-s3c2410/s3c2410-gpio.c b/arch/arm/mach-s3c2410/s3c2410-gpio.c deleted file mode 100644 index ec3a276cc3cf..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-gpio.c +++ /dev/null @@ -1,71 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-gpio.c - * - * Copyright (c) 2004-2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * S3C2410 GPIO 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/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> - -#include <asm/hardware.h> -#include <asm/irq.h> -#include <asm/io.h> - -#include <asm/arch/regs-gpio.h> - -int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, - unsigned int config) -{ - void __iomem *reg = S3C24XX_EINFLT0; - unsigned long flags; - unsigned long val; - - if (pin < S3C2410_GPG8 || pin > S3C2410_GPG15) - return -1; - - config &= 0xff; - - pin -= S3C2410_GPG8; - reg += pin & ~3; - - local_irq_save(flags); - - /* update filter width and clock source */ - - val = __raw_readl(reg); - val &= ~(0xff << ((pin & 3) * 8)); - val |= config << ((pin & 3) * 8); - __raw_writel(val, reg); - - /* update filter enable */ - - val = __raw_readl(S3C24XX_EXTINT2); - val &= ~(1 << ((pin * 4) + 3)); - val |= on << ((pin * 4) + 3); - __raw_writel(val, S3C24XX_EXTINT2); - - local_irq_restore(flags); - - return 0; -} - -EXPORT_SYMBOL(s3c2410_gpio_irqfilter); diff --git a/arch/arm/mach-s3c2410/s3c2410-irq.c b/arch/arm/mach-s3c2410/s3c2410-irq.c deleted file mode 100644 index c796c9c76e78..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-irq.c +++ /dev/null @@ -1,48 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-irq.c - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@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 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/interrupt.h> -#include <linux/ioport.h> -#include <linux/ptrace.h> -#include <linux/sysdev.h> - -#include "cpu.h" -#include "pm.h" - -static int s3c2410_irq_add(struct sys_device *sysdev) -{ - return 0; -} - -static struct sysdev_driver s3c2410_irq_driver = { - .add = s3c2410_irq_add, - .suspend = s3c24xx_irq_suspend, - .resume = s3c24xx_irq_resume, -}; - -static int s3c2410_irq_init(void) -{ - return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_irq_driver); -} - -arch_initcall(s3c2410_irq_init); diff --git a/arch/arm/mach-s3c2410/s3c2410-pm.c b/arch/arm/mach-s3c2410/s3c2410-pm.c deleted file mode 100644 index 8bb6e5e21f59..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-pm.c +++ /dev/null @@ -1,156 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-pm.c - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * S3C2410 (and compatible) Power Manager (Suspend-To-RAM) 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/suspend.h> -#include <linux/errno.h> -#include <linux/time.h> -#include <linux/sysdev.h> - -#include <asm/hardware.h> -#include <asm/io.h> - -#include <asm/mach-types.h> - -#include <asm/arch/regs-gpio.h> -#include <asm/arch/h1940.h> - -#include "cpu.h" -#include "pm.h" - -#ifdef CONFIG_S3C2410_PM_DEBUG -extern void pm_dbg(const char *fmt, ...); -#define DBG(fmt...) pm_dbg(fmt) -#else -#define DBG(fmt...) printk(KERN_DEBUG fmt) -#endif - -static void s3c2410_pm_prepare(void) -{ - /* ensure at least GSTATUS3 has the resume address */ - - __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3); - - DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); - DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); - - if (machine_is_h1940()) { - void *base = phys_to_virt(H1940_SUSPEND_CHECK); - unsigned long ptr; - unsigned long calc = 0; - - /* generate check for the bootloader to check on resume */ - - for (ptr = 0; ptr < 0x40000; ptr += 0x400) - calc += __raw_readl(base+ptr); - - __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); - } - - /* the RX3715 uses similar code and the same H1940 and the - * same offsets for resume and checksum pointers */ - - if (machine_is_rx3715()) { - void *base = phys_to_virt(H1940_SUSPEND_CHECK); - unsigned long ptr; - unsigned long calc = 0; - - /* generate check for the bootloader to check on resume */ - - for (ptr = 0; ptr < 0x40000; ptr += 0x4) - calc += __raw_readl(base+ptr); - - __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); - } - - if ( machine_is_aml_m5900() ) - s3c2410_gpio_setpin(S3C2410_GPF2, 1); - -} - -static int s3c2410_pm_resume(struct sys_device *dev) -{ - unsigned long tmp; - - /* unset the return-from-sleep flag, to ensure reset */ - - tmp = __raw_readl(S3C2410_GSTATUS2); - tmp &= S3C2410_GSTATUS2_OFFRESET; - __raw_writel(tmp, S3C2410_GSTATUS2); - - if ( machine_is_aml_m5900() ) - s3c2410_gpio_setpin(S3C2410_GPF2, 0); - - return 0; -} - -static int s3c2410_pm_add(struct sys_device *dev) -{ - pm_cpu_prep = s3c2410_pm_prepare; - pm_cpu_sleep = s3c2410_cpu_suspend; - - return 0; -} - -#if defined(CONFIG_CPU_S3C2410) -static struct sysdev_driver s3c2410_pm_driver = { - .add = s3c2410_pm_add, - .resume = s3c2410_pm_resume, -}; - -/* register ourselves */ - -static int __init s3c2410_pm_drvinit(void) -{ - return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_pm_driver); -} - -arch_initcall(s3c2410_pm_drvinit); -#endif - -#if defined(CONFIG_CPU_S3C2440) -static struct sysdev_driver s3c2440_pm_driver = { - .add = s3c2410_pm_add, - .resume = s3c2410_pm_resume, -}; - -static int __init s3c2440_pm_drvinit(void) -{ - return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_pm_driver); -} - -arch_initcall(s3c2440_pm_drvinit); -#endif - -#if defined(CONFIG_CPU_S3C2442) -static struct sysdev_driver s3c2442_pm_driver = { - .add = s3c2410_pm_add, - .resume = s3c2410_pm_resume, -}; - -static int __init s3c2442_pm_drvinit(void) -{ - return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_pm_driver); -} - -arch_initcall(s3c2442_pm_drvinit); -#endif diff --git a/arch/arm/mach-s3c2410/s3c2410-sleep.S b/arch/arm/mach-s3c2410/s3c2410-sleep.S deleted file mode 100644 index 9179a1024588..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410-sleep.S +++ /dev/null @@ -1,68 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/s3c2410-sleep.S - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * S3C2410 Power Manager (Suspend-To-RAM) support - * - * Based on PXA/SA1100 sleep code by: - * Nicolas Pitre, (c) 2002 Monta Vista Software Inc - * Cliff Brake, (c) 2001 - * - * 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/linkage.h> -#include <asm/assembler.h> -#include <asm/hardware.h> -#include <asm/arch/map.h> - -#include <asm/arch/regs-gpio.h> -#include <asm/arch/regs-clock.h> -#include <asm/arch/regs-mem.h> -#include <asm/arch/regs-serial.h> - - /* s3c2410_cpu_suspend - * - * put the cpu into sleep mode - */ - -ENTRY(s3c2410_cpu_suspend) - @@ prepare cpu to sleep - - ldr r4, =S3C2410_REFRESH - ldr r5, =S3C24XX_MISCCR - ldr r6, =S3C2410_CLKCON - ldr r7, [ r4 ] @ get REFRESH (and ensure in TLB) - ldr r8, [ r5 ] @ get MISCCR (and ensure in TLB) - ldr r9, [ r6 ] @ get CLKCON (and ensure in TLB) - - orr r7, r7, #S3C2410_REFRESH_SELF @ SDRAM sleep command - orr r8, r8, #S3C2410_MISCCR_SDSLEEP @ SDRAM power-down signals - orr r9, r9, #S3C2410_CLKCON_POWER @ power down command - - teq pc, #0 @ first as a trial-run to load cache - bl s3c2410_do_sleep - teq r0, r0 @ now do it for real - b s3c2410_do_sleep @ - - @@ align next bit of code to cache line - .align 8 -s3c2410_do_sleep: - streq r7, [ r4 ] @ SDRAM sleep command - streq r8, [ r5 ] @ SDRAM power-down config - streq r9, [ r6 ] @ CPU sleep -1: beq 1b - mov pc, r14 diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index 4cdc0d70c19f..d96fdcf2d2e3 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -31,10 +31,10 @@ #include <asm/arch/regs-clock.h> #include <asm/arch/regs-serial.h> -#include "s3c2410.h" -#include "cpu.h" -#include "devs.h" -#include "clock.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/clock.h> /* Initial IO mappings */ diff --git a/arch/arm/mach-s3c2410/s3c2410.h b/arch/arm/mach-s3c2410/s3c2410.h deleted file mode 100644 index fbed084f26d0..000000000000 --- a/arch/arm/mach-s3c2410/s3c2410.h +++ /dev/null @@ -1,31 +0,0 @@ -/* arch/arm/mach-s3c2410/s3c2410.h - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for s3c2410 machine directory - * - * 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. - * -*/ - -#ifdef CONFIG_CPU_S3C2410 - -extern int s3c2410_init(void); - -extern void s3c2410_map_io(struct map_desc *mach_desc, int size); - -extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); - -extern void s3c2410_init_clocks(int xtal); - -extern int s3c2410_baseclk_add(void); - -#else -#define s3c2410_init_clocks NULL -#define s3c2410_init_uarts NULL -#define s3c2410_map_io NULL -#define s3c2410_init NULL -#endif diff --git a/arch/arm/mach-s3c2410/s3c2412.h b/arch/arm/mach-s3c2410/s3c2412.h deleted file mode 100644 index c6e56032a6e7..000000000000 --- a/arch/arm/mach-s3c2410/s3c2412.h +++ /dev/null @@ -1,29 +0,0 @@ -/* arch/arm/mach-s3c2410/s3c2412.h - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for s3c2412 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. -*/ - -#ifdef CONFIG_CPU_S3C2412 - -extern int s3c2412_init(void); - -extern void s3c2412_map_io(struct map_desc *mach_desc, int size); - -extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no); - -extern void s3c2412_init_clocks(int xtal); - -extern int s3c2412_baseclk_add(void); -#else -#define s3c2412_init_clocks NULL -#define s3c2412_init_uarts NULL -#define s3c2412_map_io NULL -#define s3c2412_init NULL -#endif diff --git a/arch/arm/mach-s3c2410/s3c2440.h b/arch/arm/mach-s3c2410/s3c2440.h deleted file mode 100644 index dcd316076c59..000000000000 --- a/arch/arm/mach-s3c2410/s3c2440.h +++ /dev/null @@ -1,17 +0,0 @@ -/* arch/arm/mach-s3c2410/s3c2440.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for s3c2440 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. -*/ - -#ifdef CONFIG_CPU_S3C2440 -extern int s3c2440_init(void); -#else -#define s3c2440_init NULL -#endif diff --git a/arch/arm/mach-s3c2410/s3c2442.h b/arch/arm/mach-s3c2410/s3c2442.h deleted file mode 100644 index 0ae37d24866c..000000000000 --- a/arch/arm/mach-s3c2410/s3c2442.h +++ /dev/null @@ -1,17 +0,0 @@ -/* arch/arm/mach-s3c2410/s3c2442.h - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Header file for s3c2442 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. -*/ - -#ifdef CONFIG_CPU_S3C2442 -extern int s3c2442_init(void); -#else -#define s3c2442_init NULL -#endif diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S index 2018c2e1dcc5..9179a1024588 100644 --- a/arch/arm/mach-s3c2410/sleep.S +++ b/arch/arm/mach-s3c2410/sleep.S @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/sleep.S +/* linux/arch/arm/mach-s3c2410/s3c2410-sleep.S * * Copyright (c) 2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -34,126 +34,35 @@ #include <asm/arch/regs-mem.h> #include <asm/arch/regs-serial.h> -/* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not - * reset the UART configuration, only enable if you really need this! -*/ -//#define CONFIG_DEBUG_RESUME - - .text - - /* s3c2410_cpu_save - * - * save enough of the CPU state to allow us to re-start - * pm.c code. as we store items like the sp/lr, we will - * end up returning from this function when the cpu resumes - * so the return value is set to mark this. - * - * This arangement means we avoid having to flush the cache - * from this code. - * - * entry: - * r0 = pointer to save block - * - * exit: - * r0 = 0 => we stored everything - * 1 => resumed from sleep - */ - -ENTRY(s3c2410_cpu_save) - stmfd sp!, { r4 - r12, lr } - - @@ store co-processor registers - - mrc p15, 0, r4, c15, c1, 0 @ CP access register - mrc p15, 0, r5, c13, c0, 0 @ PID - mrc p15, 0, r6, c3, c0, 0 @ Domain ID - mrc p15, 0, r7, c2, c0, 0 @ translation table base address - mrc p15, 0, r8, c1, c0, 0 @ control register - - stmia r0, { r4 - r13 } - - mov r0, #0 - ldmfd sp, { r4 - r12, pc } - - @@ return to the caller, after having the MMU - @@ turned on, this restores the last bits from the - @@ stack -resume_with_mmu: - mov r0, #1 - ldmfd sp!, { r4 - r12, pc } - - .ltorg - - @@ the next bits sit in the .data segment, even though they - @@ happen to be code... the s3c2410_sleep_save_phys needs to be - @@ accessed by the resume code before it can restore the MMU. - @@ This means that the variable has to be close enough for the - @@ code to read it... since the .text segment needs to be RO, - @@ the data segment can be the only place to put this code. - - .data - - .global s3c2410_sleep_save_phys -s3c2410_sleep_save_phys: - .word 0 - - /* s3c2410_cpu_resume + /* s3c2410_cpu_suspend * - * resume code entry for bootloader to call - * - * we must put this code here in the data segment as we have no - * other way of restoring the stack pointer after sleep, and we - * must not write to the code segment (code is read-only) + * put the cpu into sleep mode */ -ENTRY(s3c2410_cpu_resume) - mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE - msr cpsr_c, r0 - - @@ load UART to allow us to print the two characters for - @@ resume debug - - mov r2, #S3C24XX_PA_UART & 0xff000000 - orr r2, r2, #S3C24XX_PA_UART & 0xff000 - -#if 0 - /* SMDK2440 LED set */ - mov r14, #S3C24XX_PA_GPIO - ldr r12, [ r14, #0x54 ] - bic r12, r12, #3<<4 - orr r12, r12, #1<<7 - str r12, [ r14, #0x54 ] -#endif - -#ifdef CONFIG_DEBUG_RESUME - mov r3, #'L' - strb r3, [ r2, #S3C2410_UTXH ] -1001: - ldrb r14, [ r3, #S3C2410_UTRSTAT ] - tst r14, #S3C2410_UTRSTAT_TXE - beq 1001b -#endif /* CONFIG_DEBUG_RESUME */ - - mov r1, #0 - mcr p15, 0, r1, c8, c7, 0 @@ invalidate I & D TLBs - mcr p15, 0, r1, c7, c7, 0 @@ invalidate I & D caches - - ldr r0, s3c2410_sleep_save_phys @ address of restore block - ldmia r0, { r4 - r13 } - - mcr p15, 0, r4, c15, c1, 0 @ CP access register - mcr p15, 0, r5, c13, c0, 0 @ PID - mcr p15, 0, r6, c3, c0, 0 @ Domain ID - mcr p15, 0, r7, c2, c0, 0 @ translation table base - -#ifdef CONFIG_DEBUG_RESUME - mov r3, #'R' - strb r3, [ r2, #S3C2410_UTXH ] -#endif - - ldr r2, =resume_with_mmu - mcr p15, 0, r8, c1, c0, 0 @ turn on MMU, etc - nop @ second-to-last before mmu - mov pc, r2 @ go back to virtual address - - .ltorg +ENTRY(s3c2410_cpu_suspend) + @@ prepare cpu to sleep + + ldr r4, =S3C2410_REFRESH + ldr r5, =S3C24XX_MISCCR + ldr r6, =S3C2410_CLKCON + ldr r7, [ r4 ] @ get REFRESH (and ensure in TLB) + ldr r8, [ r5 ] @ get MISCCR (and ensure in TLB) + ldr r9, [ r6 ] @ get CLKCON (and ensure in TLB) + + orr r7, r7, #S3C2410_REFRESH_SELF @ SDRAM sleep command + orr r8, r8, #S3C2410_MISCCR_SDSLEEP @ SDRAM power-down signals + orr r9, r9, #S3C2410_CLKCON_POWER @ power down command + + teq pc, #0 @ first as a trial-run to load cache + bl s3c2410_do_sleep + teq r0, r0 @ now do it for real + b s3c2410_do_sleep @ + + @@ align next bit of code to cache line + .align 8 +s3c2410_do_sleep: + streq r7, [ r4 ] @ SDRAM sleep command + streq r8, [ r5 ] @ SDRAM power-down config + streq r9, [ r6 ] @ CPU sleep +1: beq 1b + mov pc, r14 diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c index 22b0e1cdd4bf..bcd562ac1d3d 100644 --- a/arch/arm/mach-s3c2410/usb-simtec.c +++ b/arch/arm/mach-s3c2410/usb-simtec.c @@ -35,7 +35,7 @@ #include <asm/io.h> #include <asm/irq.h> -#include "devs.h" +#include <asm/plat-s3c24xx/devs.h> #include "usb-simtec.h" /* control power and monitor over-current events on various Simtec diff --git a/arch/arm/mach-s3c2412/Kconfig b/arch/arm/mach-s3c2412/Kconfig new file mode 100644 index 000000000000..6d629de84cdb --- /dev/null +++ b/arch/arm/mach-s3c2412/Kconfig @@ -0,0 +1,58 @@ +# arch/arm/mach-s3c2412/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +config CPU_S3C2412 + bool + depends on ARCH_S3C2410 + select S3C2412_PM if PM + select S3C2412_DMA if S3C2410_DMA + help + Support for the S3C2412 and S3C2413 SoCs from the S3C24XX line + +config CPU_S3C2412_ONLY + bool + depends on ARCH_S3C2410 && !CPU_S3C2400 && !CPU_S3C2410 && \ + !CPU_S3C2440 && !CPU_S3C2442 && CPU_S3C2412 + default y if CPU_S3C2412 + +config S3C2412_DMA + bool + depends on CPU_S3C2412 + help + Internal config node for S3C2412 DMA support + +config S3C2412_PM + bool + help + Internal config node to apply S3C2412 power management + + +menu "S3C2412 Machines" + +config MACH_SMDK2413 + bool "SMDK2413" + select CPU_S3C2412 + select MACH_S3C2413 + select MACH_SMDK + help + Say Y here if you are using an SMDK2413 + +config MACH_S3C2413 + bool + help + Internal node for S3C2413 version of SMDK2413, so that + machine_is_s3c2413() will work when MACH_SMDK2413 is + selected + +config MACH_VSTMS + bool "VMSTMS" + select CPU_S3C2412 + help + Say Y here if you are using an VSTMS board + + +endmenu + diff --git a/arch/arm/mach-s3c2412/Makefile b/arch/arm/mach-s3c2412/Makefile new file mode 100644 index 000000000000..f8e011691b31 --- /dev/null +++ b/arch/arm/mach-s3c2412/Makefile @@ -0,0 +1,21 @@ +# arch/arm/mach-s3c2412/Makefile +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_CPU_S3C2412) += s3c2412.o +obj-$(CONFIG_CPU_S3C2412) += irq.o +obj-$(CONFIG_CPU_S3C2412) += clock.o +obj-$(CONFIG_S3C2412_DMA) += dma.o +obj-$(CONFIG_S3C2412_PM) += pm.o + +# Machine support + +obj-$(CONFIG_MACH_SMDK2413) += mach-smdk2413.o +obj-$(CONFIG_MACH_VSTMS) += mach-vstms.o diff --git a/arch/arm/mach-s3c2410/s3c2412-clock.c b/arch/arm/mach-s3c2412/clock.c index 8f94ad83901d..6a8e4448770b 100644 --- a/arch/arm/mach-s3c2410/s3c2412-clock.c +++ b/arch/arm/mach-s3c2412/clock.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2412-clock.c +/* linux/arch/arm/mach-s3c2412/clock.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -41,9 +41,9 @@ #include <asm/arch/regs-clock.h> #include <asm/arch/regs-gpio.h> -#include "s3c2412.h" -#include "clock.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2412.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> /* We currently have to assume that the system is running * from the XTPll input, and that all ***REFCLKs are being diff --git a/arch/arm/mach-s3c2410/s3c2412-dma.c b/arch/arm/mach-s3c2412/dma.c index 138f726ac6bf..28b598287fae 100644 --- a/arch/arm/mach-s3c2410/s3c2412-dma.c +++ b/arch/arm/mach-s3c2412/dma.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2412-dma.c +/* linux/arch/arm/mach-s3c2412/dma.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -21,8 +21,8 @@ #include <asm/arch/dma.h> #include <asm/io.h> -#include "dma.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/dma.h> +#include <asm/plat-s3c24xx/cpu.h> #include <asm/arch/regs-serial.h> #include <asm/arch/regs-gpio.h> diff --git a/arch/arm/mach-s3c2410/s3c2412-irq.c b/arch/arm/mach-s3c2412/irq.c index ffcc30b23a80..e89dbdcb1b7b 100644 --- a/arch/arm/mach-s3c2410/s3c2412-irq.c +++ b/arch/arm/mach-s3c2412/irq.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2412/s3c2412-irq.c +/* linux/arch/arm/mach-s3c2412/irq.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -35,9 +35,9 @@ #include <asm/arch/regs-irq.h> #include <asm/arch/regs-gpio.h> -#include "cpu.h" -#include "irq.h" -#include "pm.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/irq.h> +#include <asm/plat-s3c24xx/pm.h> /* the s3c2412 changes the behaviour of IRQ_EINT0 through IRQ_EINT3 by * having them turn up in both the INT* and the EINT* registers. Whilst diff --git a/arch/arm/mach-s3c2410/mach-smdk2413.c b/arch/arm/mach-s3c2412/mach-smdk2413.c index 4f89abd7a6df..32b15617ef2a 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2413.c +++ b/arch/arm/mach-s3c2412/mach-smdk2413.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-smdk2413.c +/* linux/arch/arm/mach-s3c2412/mach-smdk2413.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -39,13 +39,13 @@ #include <asm/arch/idle.h> #include <asm/arch/fb.h> -#include "s3c2410.h" -#include "s3c2412.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2412.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> -#include "common-smdk.h" +#include <asm/plat-s3c24xx/common-smdk.h> static struct map_desc smdk2413_iodesc[] __initdata = { }; diff --git a/arch/arm/mach-s3c2410/mach-vstms.c b/arch/arm/mach-s3c2412/mach-vstms.c index 0360e1055bcd..f1afb707ff16 100644 --- a/arch/arm/mach-s3c2410/mach-vstms.c +++ b/arch/arm/mach-s3c2412/mach-vstms.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-vstms.c +/* linux/arch/arm/mach-s3c2412/mach-vstms.c * * (C) 2006 Thomas Gleixner <tglx@linutronix.de> * @@ -43,11 +43,11 @@ #include <asm/arch/nand.h> -#include "s3c2410.h" -#include "s3c2412.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2412.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> static struct map_desc vstms_iodesc[] __initdata = { diff --git a/arch/arm/mach-s3c2410/s3c2412-pm.c b/arch/arm/mach-s3c2412/pm.c index 19b63322d259..8988dac388a9 100644 --- a/arch/arm/mach-s3c2410/s3c2412-pm.c +++ b/arch/arm/mach-s3c2412/pm.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2412-pm.c +/* linux/arch/arm/mach-s3c2412/pm.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -28,10 +28,10 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-dsc.h> -#include "cpu.h" -#include "pm.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> -#include "s3c2412.h" +#include <asm/plat-s3c24xx/s3c2412.h> static void s3c2412_cpu_suspend(void) { diff --git a/arch/arm/mach-s3c2410/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c index 2f651a811ecd..aafe0bc593f1 100644 --- a/arch/arm/mach-s3c2410/s3c2412.c +++ b/arch/arm/mach-s3c2412/s3c2412.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2412.c +/* linux/arch/arm/mach-s3c2412/s3c2412.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -38,11 +38,11 @@ #include <asm/arch/regs-gpioj.h> #include <asm/arch/regs-dsc.h> -#include "s3c2412.h" -#include "cpu.h" -#include "devs.h" -#include "clock.h" -#include "pm.h" +#include <asm/plat-s3c24xx/s3c2412.h> +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/pm.h> #ifndef CONFIG_CPU_S3C2412_ONLY void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO; diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig new file mode 100644 index 000000000000..e3bfda098c0f --- /dev/null +++ b/arch/arm/mach-s3c2440/Kconfig @@ -0,0 +1,71 @@ +# arch/arm/mach-s3c2440/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +config CPU_S3C2440 + bool + depends on ARCH_S3C2410 + select S3C2410_CLOCK + select S3C2410_PM if PM + select S3C2410_GPIO + select S3C2440_DMA if S3C2410_DMA + select CPU_S3C244X + help + Support for S3C2440 Samsung Mobile CPU based systems. + +config S3C2440_DMA + bool + depends on ARCH_S3C2410 && CPU_S3C24405B + help + Support for S3C2440 specific DMA code5A + + +menu "S3C2440 Machines" + +config MACH_ANUBIS + bool "Simtec Electronics ANUBIS" + select CPU_S3C2440 + select PM_SIMTEC if PM + help + Say Y here if you are using the Simtec Electronics ANUBIS + development system + +config MACH_OSIRIS + bool "Simtec IM2440D20 (OSIRIS) module" + select CPU_S3C2440 + select PM_SIMTEC if PM + help + Say Y here if you are using the Simtec IM2440D20 module, also + known as the Osiris. + +config MACH_RX3715 + bool "HP iPAQ rx3715" + select CPU_S3C2440 + select PM_H1940 if PM + help + Say Y here if you are using the HP iPAQ rx3715. + +config ARCH_S3C2440 + bool "SMDK2440" + select CPU_S3C2440 + select MACH_SMDK + help + Say Y here if you are using the SMDK2440. + +config MACH_NEXCODER_2440 + bool "NexVision NEXCODER 2440 Light Board" + select CPU_S3C2440 + help + Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board + +config SMDK2440_CPU2440 + bool "SMDK2440 with S3C2440 CPU module" + depends on ARCH_S3C2440 + default y if ARCH_S3C2440 + select CPU_S3C2440 + + +endmenu + diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile new file mode 100644 index 000000000000..c81ed6248dcb --- /dev/null +++ b/arch/arm/mach-s3c2440/Makefile @@ -0,0 +1,23 @@ +# arch/arm/mach-s3c2440/Makefile +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o +obj-$(CONFIG_CPU_S3C2440) += irq.o +obj-$(CONFIG_CPU_S3C2440) += clock.o +obj-$(CONFIG_S3C2440_DMA) += dma.o + +# Machine support + +obj-$(CONFIG_MACH_ANUBIS) += mach-anubis.o +obj-$(CONFIG_MACH_OSIRIS) += mach-osiris.o +obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o +obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o +obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o diff --git a/arch/arm/mach-s3c2410/s3c2440-clock.c b/arch/arm/mach-s3c2440/clock.c index ba13c1d079d1..79e2ea4adaf3 100644 --- a/arch/arm/mach-s3c2410/s3c2440-clock.c +++ b/arch/arm/mach-s3c2440/clock.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2440-clock.c +/* linux/arch/arm/mach-s3c2440/clock.c * * Copyright (c) 2004-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ @@ -41,8 +41,8 @@ #include <asm/arch/regs-clock.h> -#include "clock.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> /* S3C2440 extended clock support */ diff --git a/arch/arm/mach-s3c2410/s3c2440-dma.c b/arch/arm/mach-s3c2440/dma.c index 47b861b9443d..8e51137620f2 100644 --- a/arch/arm/mach-s3c2410/s3c2440-dma.c +++ b/arch/arm/mach-s3c2440/dma.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2440-dma.c +/* linux/arch/arm/mach-s3c2440/dma.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -19,9 +19,9 @@ #include <asm/dma.h> #include <asm/arch/dma.h> -#include "dma.h" +#include <asm/plat-s3c24xx/dma.h> -#include "cpu.h" +#include <asm/plat-s3c24xx/cpu.h> #include <asm/arch/regs-serial.h> #include <asm/arch/regs-gpio.h> diff --git a/arch/arm/mach-s3c2410/s3c2440-dsc.c b/arch/arm/mach-s3c2440/dsc.c index c92ea66ba45e..2995ff5681bb 100644 --- a/arch/arm/mach-s3c2410/s3c2440-dsc.c +++ b/arch/arm/mach-s3c2440/dsc.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2440-dsc.c +/* linux/arch/arm/mach-s3c2440/dsc.c * * Copyright (c) 2004-2005 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -27,8 +27,8 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-dsc.h> -#include "cpu.h" -#include "s3c2440.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/s3c2440.h> int s3c2440_set_dsc(unsigned int pin, unsigned int value) { diff --git a/arch/arm/mach-s3c2410/s3c2440-irq.c b/arch/arm/mach-s3c2440/irq.c index 1ba19b27ab05..1069d13d8c57 100644 --- a/arch/arm/mach-s3c2410/s3c2440-irq.c +++ b/arch/arm/mach-s3c2440/irq.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2440-irq.c +/* linux/arch/arm/mach-s3c2440/irq.c * * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -35,9 +35,9 @@ #include <asm/arch/regs-irq.h> #include <asm/arch/regs-gpio.h> -#include "cpu.h" -#include "pm.h" -#include "irq.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> +#include <asm/plat-s3c24xx/irq.h> /* WDT/AC97 */ diff --git a/arch/arm/mach-s3c2410/mach-anubis.c b/arch/arm/mach-s3c2440/mach-anubis.c index 0fad0c2fe07b..3f0288eb1ed5 100644 --- a/arch/arm/mach-s3c2410/mach-anubis.c +++ b/arch/arm/mach-s3c2440/mach-anubis.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-anubis.c +/* linux/arch/arm/mach-s3c2440/mach-anubis.c * * Copyright (c) 2003-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ @@ -42,9 +42,9 @@ #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> #define COPYRIGHT ", (c) 2005 Simtec Electronics" diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2440/mach-nexcoder.c index d6dfdad8c90b..6d551d88330b 100644 --- a/arch/arm/mach-s3c2410/mach-nexcoder.c +++ b/arch/arm/mach-s3c2440/mach-nexcoder.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-nexcoder.c +/* linux/arch/arm/mach-s3c2440/mach-nexcoder.c * * Copyright (c) 2004 Nex Vision * Guillaume GOURAT <guillaume.gourat@nexvision.tv> @@ -38,11 +38,11 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-serial.h> -#include "s3c2410.h" -#include "s3c2440.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2440.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> static struct map_desc nexcoder_iodesc[] __initdata = { /* nothing here yet */ diff --git a/arch/arm/mach-s3c2410/mach-osiris.c b/arch/arm/mach-s3c2440/mach-osiris.c index 37b40850c9b9..2ed8e51f20c8 100644 --- a/arch/arm/mach-s3c2410/mach-osiris.c +++ b/arch/arm/mach-s3c2440/mach-osiris.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-osiris.c +/* linux/arch/arm/mach-s3c2440/mach-osiris.c * * Copyright (c) 2005 Simtec Electronics * http://armlinux.simtec.co.uk/ @@ -41,9 +41,9 @@ #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> /* onboard perihpheral map */ diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c index ecbcdf79d739..1d4e19bd9206 100644 --- a/arch/arm/mach-s3c2410/mach-rx3715.c +++ b/arch/arm/mach-s3c2440/mach-rx3715.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-rx3715.c +/* linux/arch/arm/mach-s3c2440/mach-rx3715.c * * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -46,10 +46,10 @@ #include <asm/arch/nand.h> #include <asm/arch/fb.h> -#include "clock.h" -#include "devs.h" -#include "cpu.h" -#include "pm.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> static struct map_desc rx3715_iodesc[] __initdata = { /* dump ISA space somewhere unused */ diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2440/mach-smdk2440.c index 2b61f4ed1da4..270e42b9b5c1 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2440.c +++ b/arch/arm/mach-s3c2440/mach-smdk2440.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/mach-smdk2440.c +/* linux/arch/arm/mach-s3c2440/mach-smdk2440.c * * Copyright (c) 2004,2005 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -40,13 +40,13 @@ #include <asm/arch/idle.h> #include <asm/arch/fb.h> -#include "s3c2410.h" -#include "s3c2440.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2440.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> -#include "common-smdk.h" +#include <asm/plat-s3c24xx/common-smdk.h> static struct map_desc smdk2440_iodesc[] __initdata = { /* ISA IO Space map (memory space selected by A24) */ diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c index 344eb27cca48..90e1da61fbc3 100644 --- a/arch/arm/mach-s3c2410/s3c2440.c +++ b/arch/arm/mach-s3c2440/s3c2440.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2440.c +/* linux/arch/arm/mach-s3c2440/s3c2440.c * * Copyright (c) 2004-2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -29,9 +29,9 @@ #include <asm/io.h> #include <asm/irq.h> -#include "s3c2440.h" -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2440.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> static struct sys_device s3c2440_sysdev = { .cls = &s3c2440_sysclass, diff --git a/arch/arm/mach-s3c2442/Kconfig b/arch/arm/mach-s3c2442/Kconfig new file mode 100644 index 000000000000..bf8d87abfab3 --- /dev/null +++ b/arch/arm/mach-s3c2442/Kconfig @@ -0,0 +1,27 @@ +# arch/arm/mach-s3c2442/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +config CPU_S3C2442 + bool + depends on ARCH_S3C2420 + select S3C2410_CLOCK + select S3C2410_GPIO + select S3C2410_PM if PM + select CPU_S3C244X + help + Support for S3C2442 Samsung Mobile CPU based systems. + + +menu "S3C2442 Machines" + +config SMDK2440_CPU2442 + bool "SMDM2440 with S3C2442 CPU module" + depends on ARCH_S3C2440 + select CPU_S3C2442 + + +endmenu + diff --git a/arch/arm/mach-s3c2442/Makefile b/arch/arm/mach-s3c2442/Makefile new file mode 100644 index 000000000000..2a909c6c5798 --- /dev/null +++ b/arch/arm/mach-s3c2442/Makefile @@ -0,0 +1,16 @@ +# arch/arm/mach-s3c2442/Makefile +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_CPU_S3C2442) += s3c2442.o +obj-$(CONFIG_CPU_S3C2442) += clock.o + +# Machine support + diff --git a/arch/arm/mach-s3c2410/s3c2442-clock.c b/arch/arm/mach-s3c2442/clock.c index 4e292ca7c9be..5b9e830ac4d3 100644 --- a/arch/arm/mach-s3c2410/s3c2442-clock.c +++ b/arch/arm/mach-s3c2442/clock.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2442-clock.c +/* linux/arch/arm/mach-s3c2442/clock.c * * Copyright (c) 2004-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ @@ -41,8 +41,8 @@ #include <asm/arch/regs-clock.h> -#include "clock.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> /* S3C2442 extended clock support */ diff --git a/arch/arm/mach-s3c2410/s3c2442.c b/arch/arm/mach-s3c2442/s3c2442.c index 428732ee68c4..fbf8264249da 100644 --- a/arch/arm/mach-s3c2410/s3c2442.c +++ b/arch/arm/mach-s3c2442/s3c2442.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c2442.c +/* linux/arch/arm/mach-s3c2442/s3c2442.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -19,8 +19,8 @@ #include <linux/serial_core.h> #include <linux/sysdev.h> -#include "s3c2442.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/s3c2442.h> +#include <asm/plat-s3c24xx/cpu.h> static struct sys_device s3c2442_sysdev = { .cls = &s3c2442_sysclass, diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig new file mode 100644 index 000000000000..97813645b5ee --- /dev/null +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -0,0 +1,96 @@ +# arch/arm/plat-s3c24xx/Kconfig +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +config PLAT_S3C24XX + bool + depends on ARCH_S3C2410 + default y + help + Base platform code for any Samsung S3C device + +config CPU_S3C244X + bool + depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) + help + Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. + +config PM_SIMTEC + bool + help + Common power management code for systems that are + compatible with the Simtec style of power management + +config S3C2410_BOOT_WATCHDOG + bool "S3C2410 Initialisation watchdog" + depends on ARCH_S3C2410 && S3C2410_WATCHDOG + help + Say y to enable the watchdog during the kernel decompression + stage. If the kernel fails to uncompress, then the watchdog + will trigger a reset and the system should restart. + +config S3C2410_BOOT_ERROR_RESET + bool "S3C2410 Reboot on decompression error" + depends on ARCH_S3C2410 + help + Say y here to use the watchdog to reset the system if the + kernel decompressor detects an error during decompression. + +config S3C2410_PM_DEBUG + bool "S3C2410 PM Suspend debug" + depends on ARCH_S3C2410 && PM + help + Say Y here if you want verbose debugging from the PM Suspend and + Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> + for more information. + +config S3C2410_PM_CHECK + bool "S3C2410 PM Suspend Memory CRC" + depends on ARCH_S3C2410 && PM && CRC32 + help + Enable the PM code's memory area checksum over sleep. This option + will generate CRCs of all blocks of memory, and store them before + going to sleep. The blocks are then checked on resume for any + errors. + +config S3C2410_PM_CHECK_CHUNKSIZE + int "S3C2410 PM Suspend CRC Chunksize (KiB)" + depends on ARCH_S3C2410 && PM && S3C2410_PM_CHECK + default 64 + help + Set the chunksize in Kilobytes of the CRC for checking memory + corruption over suspend and resume. A smaller value will mean that + the CRC data block will take more memory, but wil identify any + faults with better precision. + +config S3C2410_LOWLEVEL_UART_PORT + int "S3C2410 UART to use for low-level messages" + default 0 + help + Choice of which UART port to use for the low-level messages, + such as the `Uncompressing...` at start time. The value of + this configuration should be between zero and two. The port + must have been initialised by the boot-loader before use. + +config S3C2410_DMA + bool "S3C2410 DMA support" + depends on ARCH_S3C2410 + 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_S3C2410 && S3C2410_DMA + help + Enable debugging output for the DMA code. This option sends info + to the kernel log, at priority KERN_DEBUG. + +config MACH_SMDK + bool + help + Common machine code for SMDK2410 and SMDK2440 + diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile new file mode 100644 index 000000000000..8e5ccaa1f03c --- /dev/null +++ b/arch/arm/plat-s3c24xx/Makefile @@ -0,0 +1,30 @@ +# arch/arm/plat-s3c24xx/Makefile +# +# Copyright 2007 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + + +# Core files + +obj-y += cpu.o +obj-y += irq.o +obj-y += devs.o +obj-y += gpio.o +obj-y += time.o +obj-y += clock.o + +# Architecture dependant builds + +obj-$(CONFIG_CPU_S3C244X) += s3c244x.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o +obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o +obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_PM) += sleep.o +obj-$(CONFIG_S3C2410_DMA) += dma.o +obj-$(CONFIG_MACH_SMDK) += common-smdk.o diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c new file mode 100644 index 000000000000..d3dc03a7383a --- /dev/null +++ b/arch/arm/plat-s3c24xx/clock.c @@ -0,0 +1,449 @@ +/* linux/arch/arm/plat-s3c24xx/clock.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C24XX Core clock control support + * + * Based on, and code from linux/arch/arm/mach-versatile/clock.c + ** + ** Copyright (C) 2004 ARM Limited. + ** Written by Deep Blue Solutions Limited. + * + * + * 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/platform_device.h> +#include <linux/sysdev.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> + +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> + +/* clock information */ + +static LIST_HEAD(clocks); + +DEFINE_MUTEX(clocks_mutex); + +/* enable and disable calls for use with the clk struct */ + +static int clk_null_enable(struct clk *clk, int enable) +{ + return 0; +} + +/* Clock API calls */ + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p; + struct clk *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; + + mutex_lock(&clocks_mutex); + + list_for_each_entry(p, &clocks, list) { + if (p->id == idno && + strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + /* check for the case where a device was supplied, but the + * clock that was being searched for is not device specific */ + + if (IS_ERR(clk)) { + list_for_each_entry(p, &clocks, list) { + if (p->id == -1 && strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + } + + mutex_unlock(&clocks_mutex); + return clk; +} + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +int clk_enable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return -EINVAL; + + clk_enable(clk->parent); + + mutex_lock(&clocks_mutex); + + if ((clk->usage++) == 0) + (clk->enable)(clk, 1); + + mutex_unlock(&clocks_mutex); + return 0; +} + +void clk_disable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return; + + mutex_lock(&clocks_mutex); + + if ((--clk->usage) == 0) + (clk->enable)(clk, 0); + + mutex_unlock(&clocks_mutex); + clk_disable(clk->parent); +} + + +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + if (clk->get_rate != NULL) + return (clk->get_rate)(clk); + + if (clk->parent != NULL) + return clk_get_rate(clk->parent); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (!IS_ERR(clk) && clk->round_rate) + return (clk->round_rate)(clk, rate); + + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clocks_mutex); + ret = (clk->set_rate)(clk, rate); + mutex_unlock(&clocks_mutex); + + return ret; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clocks_mutex); + + if (clk->set_parent) + ret = (clk->set_parent)(clk, parent); + + mutex_unlock(&clocks_mutex); + + return ret; +} + +EXPORT_SYMBOL(clk_get); +EXPORT_SYMBOL(clk_put); +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); +EXPORT_SYMBOL(clk_set_parent); + +/* base clocks */ + +struct clk clk_xtal = { + .name = "xtal", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_mpll = { + .name = "mpll", + .id = -1, +}; + +struct clk clk_upll = { + .name = "upll", + .id = -1, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_f = { + .name = "fclk", + .id = -1, + .rate = 0, + .parent = &clk_mpll, + .ctrlbit = 0, +}; + +struct clk clk_h = { + .name = "hclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_p = { + .name = "pclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_usb_bus = { + .name = "usb-bus", + .id = -1, + .rate = 0, + .parent = &clk_upll, +}; + +/* 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 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_xtal) + 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_dclk0) + mask = S3C2410_MISCCR_CLK0_MASK; + else { + source <<= 4; + mask = S3C2410_MISCCR_CLK1_MASK; + } + + s3c2410_modify_misccr(mask, source); + return 0; +} + +/* external clock definitions */ + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_uclk = { + .name = "uclk", + .id = -1, +}; + +/* initialise the clock system */ + +int s3c24xx_register_clock(struct clk *clk) +{ + clk->owner = THIS_MODULE; + + if (clk->enable == NULL) + clk->enable = clk_null_enable; + + /* add to the list of available clocks */ + + mutex_lock(&clocks_mutex); + list_add(&clk->list, &clocks); + mutex_unlock(&clocks_mutex); + + return 0; +} + +/* initalise all the clocks */ + +int __init s3c24xx_setup_clocks(unsigned long xtal, + unsigned long fclk, + unsigned long hclk, + unsigned long pclk) +{ + printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); + + /* initialise the main system clocks */ + + clk_xtal.rate = xtal; + clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + + clk_mpll.rate = fclk; + clk_h.rate = hclk; + clk_p.rate = pclk; + clk_f.rate = fclk; + + /* assume uart clocks are correctly setup */ + + /* register our clocks */ + + if (s3c24xx_register_clock(&clk_xtal) < 0) + printk(KERN_ERR "failed to register master xtal\n"); + + if (s3c24xx_register_clock(&clk_mpll) < 0) + printk(KERN_ERR "failed to register mpll clock\n"); + + if (s3c24xx_register_clock(&clk_upll) < 0) + printk(KERN_ERR "failed to register upll clock\n"); + + if (s3c24xx_register_clock(&clk_f) < 0) + printk(KERN_ERR "failed to register cpu fclk\n"); + + if (s3c24xx_register_clock(&clk_h) < 0) + printk(KERN_ERR "failed to register cpu hclk\n"); + + if (s3c24xx_register_clock(&clk_p) < 0) + printk(KERN_ERR "failed to register cpu pclk\n"); + + return 0; +} diff --git a/arch/arm/mach-s3c2410/common-smdk.c b/arch/arm/plat-s3c24xx/common-smdk.c index a40eaa656177..908efa7d745f 100644 --- a/arch/arm/mach-s3c2410/common-smdk.c +++ b/arch/arm/plat-s3c24xx/common-smdk.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/common-smdk.c +/* linux/arch/arm/plat-s3c24xx/common-smdk.c * * Copyright (c) 2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -38,9 +38,9 @@ #include <asm/arch/nand.h> -#include "common-smdk.h" -#include "devs.h" -#include "pm.h" +#include <asm/plat-s3c24xx/common-smdk.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/pm.h> /* LED devices */ diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/plat-s3c24xx/cpu.c index ae1f5bb63f7a..2fbb74969379 100644 --- a/arch/arm/mach-s3c2410/cpu.c +++ b/arch/arm/plat-s3c24xx/cpu.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/cpu.c +/* linux/arch/arm/plat-s3c24xx/cpu.c * * Copyright (c) 2004-2005 Simtec Electronics * http://www.simtec.co.uk/products/SWLINUX/ @@ -40,15 +40,15 @@ #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-serial.h> -#include "cpu.h" -#include "devs.h" -#include "clock.h" -#include "s3c2400.h" -#include "s3c2410.h" -#include "s3c2412.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/s3c2400.h> +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2412.h> #include "s3c244x.h" -#include "s3c2440.h" -#include "s3c2442.h" +#include <asm/plat-s3c24xx/s3c2440.h> +#include <asm/plat-s3c24xx/s3c2442.h> struct cpu_table { unsigned long idcode; diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/plat-s3c24xx/devs.c index faccde2092d2..6d46c4e2a4f7 100644 --- a/arch/arm/mach-s3c2410/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/devs.c +/* linux/arch/arm/plat-s3c24xx/devs.c * * Copyright (c) 2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -30,8 +30,8 @@ #include <asm/arch/regs-serial.h> -#include "devs.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> /* Serial port registrations */ diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c new file mode 100644 index 000000000000..c784e1f816bb --- /dev/null +++ b/arch/arm/plat-s3c24xx/dma.c @@ -0,0 +1,1441 @@ +/* linux/arch/arm/plat-s3c24xx/dma.c + * + * Copyright (c) 2003-2005,2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 DMA core + * + * 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. +*/ + + +#ifdef CONFIG_S3C2410_DMA_DEBUG +#define DEBUG +#endif + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/sysdev.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <asm/mach/dma.h> +#include <asm/arch/map.h> + +#include <asm/plat-s3c24xx/dma.h> + +/* io map for dma */ +static void __iomem *dma_base; +static struct kmem_cache *dma_kmem; + +struct s3c24xx_dma_selection dma_sel; + +/* dma channel state information */ +struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; + +/* debugging functions */ + +#define BUF_MAGIC (0xcafebabe) + +#define dmawarn(fmt...) printk(KERN_DEBUG fmt) + +#define dma_regaddr(chan, reg) ((chan)->regs + (reg)) + +#if 1 +#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg)) +#else +static inline void +dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val) +{ + pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); + writel(val, dma_regaddr(chan, reg)); +} +#endif + +#define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) + +/* captured register state for debug */ + +struct s3c2410_dma_regstate { + unsigned long dcsrc; + unsigned long disrc; + unsigned long dstat; + unsigned long dcon; + unsigned long dmsktrig; +}; + +#ifdef CONFIG_S3C2410_DMA_DEBUG + +/* dmadbg_showregs + * + * simple debug routine to print the current state of the dma registers +*/ + +static void +dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs) +{ + regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC); + regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC); + regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT); + regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON); + regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); +} + +static void +dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan, + struct s3c2410_dma_regstate *regs) +{ + printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", + chan->number, fname, line, + regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig, + regs->dcon); +} + +static void +dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan) +{ + struct s3c2410_dma_regstate state; + + dmadbg_capture(chan, &state); + + printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n", + chan->number, fname, line, chan->load_state, + chan->curr, chan->next, chan->end); + + dmadbg_dumpregs(fname, line, chan, &state); +} + +static void +dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) +{ + struct s3c2410_dma_regstate state; + + dmadbg_capture(chan, &state); + dmadbg_dumpregs(fname, line, chan, &state); +} + +#define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) +#define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan)) +#else +#define dbg_showregs(chan) do { } while(0) +#define dbg_showchan(chan) do { } while(0) +#endif /* CONFIG_S3C2410_DMA_DEBUG */ + +static struct s3c2410_dma_chan *dma_chan_map[DMACH_MAX]; + +/* lookup_dma_channel + * + * change the dma channel number given into a real dma channel id +*/ + +static struct s3c2410_dma_chan *lookup_dma_channel(unsigned int channel) +{ + if (channel & DMACH_LOW_LEVEL) + return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL]; + else + return dma_chan_map[channel]; +} + +/* s3c2410_dma_stats_timeout + * + * Update DMA stats from timeout info +*/ + +static void +s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val) +{ + if (stats == NULL) + return; + + if (val > stats->timeout_longest) + stats->timeout_longest = val; + if (val < stats->timeout_shortest) + stats->timeout_shortest = val; + + stats->timeout_avg += val; +} + +/* s3c2410_dma_waitforload + * + * wait for the DMA engine to load a buffer, and update the state accordingly +*/ + +static int +s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line) +{ + int timeout = chan->load_timeout; + int took; + + if (chan->load_state != S3C2410_DMALOAD_1LOADED) { + printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); + return 0; + } + + if (chan->stats != NULL) + chan->stats->loads++; + + while (--timeout > 0) { + if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { + took = chan->load_timeout - timeout; + + s3c2410_dma_stats_timeout(chan->stats, took); + + switch (chan->load_state) { + case S3C2410_DMALOAD_1LOADED: + chan->load_state = S3C2410_DMALOAD_1RUNNING; + break; + + default: + printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); + } + + return 1; + } + } + + if (chan->stats != NULL) { + chan->stats->timeout_failed++; + } + + return 0; +} + + + +/* s3c2410_dma_loadbuffer + * + * load a buffer, and update the channel state +*/ + +static inline int +s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, + struct s3c2410_dma_buf *buf) +{ + unsigned long reload; + + pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n", + buf, (unsigned long)buf->data, buf->size); + + if (buf == NULL) { + dmawarn("buffer is NULL\n"); + return -EINVAL; + } + + /* check the state of the channel before we do anything */ + + if (chan->load_state == S3C2410_DMALOAD_1LOADED) { + dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n"); + } + + if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) { + dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n"); + } + + /* it would seem sensible if we are the last buffer to not bother + * with the auto-reload bit, so that the DMA engine will not try + * and load another transfer after this one has finished... + */ + if (chan->load_state == S3C2410_DMALOAD_NONE) { + pr_debug("load_state is none, checking for noreload (next=%p)\n", + buf->next); + reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; + } else { + //pr_debug("load_state is %d => autoreload\n", chan->load_state); + reload = S3C2410_DCON_AUTORELOAD; + } + + if ((buf->data & 0xf0000000) != 0x30000000) { + dmawarn("dmaload: buffer is %p\n", (void *)buf->data); + } + + writel(buf->data, chan->addr_reg); + + dma_wrreg(chan, S3C2410_DMA_DCON, + chan->dcon | reload | (buf->size/chan->xfer_unit)); + + chan->next = buf->next; + + /* update the state of the channel */ + + switch (chan->load_state) { + case S3C2410_DMALOAD_NONE: + chan->load_state = S3C2410_DMALOAD_1LOADED; + break; + + case S3C2410_DMALOAD_1RUNNING: + chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING; + break; + + default: + dmawarn("dmaload: unknown state %d in loadbuffer\n", + chan->load_state); + break; + } + + return 0; +} + +/* s3c2410_dma_call_op + * + * small routine to call the op routine with the given op if it has been + * registered +*/ + +static void +s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op) +{ + if (chan->op_fn != NULL) { + (chan->op_fn)(chan, op); + } +} + +/* s3c2410_dma_buffdone + * + * small wrapper to check if callback routine needs to be called, and + * if so, call it +*/ + +static inline void +s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, + enum s3c2410_dma_buffresult result) +{ +#if 0 + pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", + chan->callback_fn, buf, buf->id, buf->size, result); +#endif + + if (chan->callback_fn != NULL) { + (chan->callback_fn)(chan, buf->id, buf->size, result); + } +} + +/* s3c2410_dma_start + * + * start a dma channel going +*/ + +static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) +{ + unsigned long tmp; + unsigned long flags; + + pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); + + local_irq_save(flags); + + if (chan->state == S3C2410_DMA_RUNNING) { + pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); + local_irq_restore(flags); + return 0; + } + + chan->state = S3C2410_DMA_RUNNING; + + /* check wether there is anything to load, and if not, see + * if we can find anything to load + */ + + if (chan->load_state == S3C2410_DMALOAD_NONE) { + if (chan->next == NULL) { + printk(KERN_ERR "dma%d: channel has nothing loaded\n", + chan->number); + chan->state = S3C2410_DMA_IDLE; + local_irq_restore(flags); + return -EINVAL; + } + + s3c2410_dma_loadbuffer(chan, chan->next); + } + + dbg_showchan(chan); + + /* enable the channel */ + + if (!chan->irq_enabled) { + enable_irq(chan->irq); + chan->irq_enabled = 1; + } + + /* start the channel going */ + + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp &= ~S3C2410_DMASKTRIG_STOP; + tmp |= S3C2410_DMASKTRIG_ON; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + + pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); + +#if 0 + /* the dma buffer loads should take care of clearing the AUTO + * reloading feature */ + tmp = dma_rdreg(chan, S3C2410_DMA_DCON); + tmp &= ~S3C2410_DCON_NORELOAD; + dma_wrreg(chan, S3C2410_DMA_DCON, tmp); +#endif + + s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); + + dbg_showchan(chan); + + /* if we've only loaded one buffer onto the channel, then chec + * to see if we have another, and if so, try and load it so when + * the first buffer is finished, the new one will be loaded onto + * the channel */ + + if (chan->next != NULL) { + if (chan->load_state == S3C2410_DMALOAD_1LOADED) { + + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + pr_debug("%s: buff not yet loaded, no more todo\n", + __FUNCTION__); + } else { + chan->load_state = S3C2410_DMALOAD_1RUNNING; + s3c2410_dma_loadbuffer(chan, chan->next); + } + + } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { + s3c2410_dma_loadbuffer(chan, chan->next); + } + } + + + local_irq_restore(flags); + + return 0; +} + +/* s3c2410_dma_canload + * + * work out if we can queue another buffer into the DMA engine +*/ + +static int +s3c2410_dma_canload(struct s3c2410_dma_chan *chan) +{ + if (chan->load_state == S3C2410_DMALOAD_NONE || + chan->load_state == S3C2410_DMALOAD_1RUNNING) + return 1; + + return 0; +} + +/* s3c2410_dma_enqueue + * + * queue an given buffer for dma transfer. + * + * id the device driver's id information for this buffer + * data the physical address of the buffer data + * size the size of the buffer in bytes + * + * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART + * is checked, and if set, the channel is started. If this flag isn't set, + * then an error will be returned. + * + * It is possible to queue more than one DMA buffer onto a channel at + * once, and the code will deal with the re-loading of the next buffer + * when necessary. +*/ + +int s3c2410_dma_enqueue(unsigned int channel, void *id, + dma_addr_t data, int size) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + struct s3c2410_dma_buf *buf; + unsigned long flags; + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: id=%p, data=%08x, size=%d\n", + __FUNCTION__, id, (unsigned int)data, size); + + buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); + if (buf == NULL) { + pr_debug("%s: out of memory (%ld alloc)\n", + __FUNCTION__, (long)sizeof(*buf)); + return -ENOMEM; + } + + //pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); + //dbg_showchan(chan); + + buf->next = NULL; + buf->data = buf->ptr = data; + buf->size = size; + buf->id = id; + buf->magic = BUF_MAGIC; + + local_irq_save(flags); + + if (chan->curr == NULL) { + /* we've got nothing loaded... */ + pr_debug("%s: buffer %p queued onto empty channel\n", + __FUNCTION__, buf); + + chan->curr = buf; + chan->end = buf; + chan->next = NULL; + } else { + pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", + chan->number, __FUNCTION__, buf); + + if (chan->end == NULL) + pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", + chan->number, __FUNCTION__, chan); + + chan->end->next = buf; + chan->end = buf; + } + + /* if necessary, update the next buffer field */ + if (chan->next == NULL) + chan->next = buf; + + /* check to see if we can load a buffer */ + if (chan->state == S3C2410_DMA_RUNNING) { + if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + printk(KERN_ERR "dma%d: loadbuffer:" + "timeout loading buffer\n", + chan->number); + dbg_showchan(chan); + local_irq_restore(flags); + return -EINVAL; + } + } + + while (s3c2410_dma_canload(chan) && chan->next != NULL) { + s3c2410_dma_loadbuffer(chan, chan->next); + } + } else if (chan->state == S3C2410_DMA_IDLE) { + if (chan->flags & S3C2410_DMAF_AUTOSTART) { + s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); + } + } + + local_irq_restore(flags); + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_enqueue); + +static inline void +s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) +{ + int magicok = (buf->magic == BUF_MAGIC); + + buf->magic = -1; + + if (magicok) { + kmem_cache_free(dma_kmem, buf); + } else { + printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf); + } +} + +/* s3c2410_dma_lastxfer + * + * called when the system is out of buffers, to ensure that the channel + * is prepared for shutdown. +*/ + +static inline void +s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) +{ +#if 0 + pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", + chan->number, chan->load_state); +#endif + + switch (chan->load_state) { + case S3C2410_DMALOAD_NONE: + break; + + case S3C2410_DMALOAD_1LOADED: + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + /* flag error? */ + printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", + chan->number, __FUNCTION__); + return; + } + break; + + case S3C2410_DMALOAD_1LOADED_1RUNNING: + /* I belive in this case we do not have anything to do + * until the next buffer comes along, and we turn off the + * reload */ + return; + + default: + pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", + chan->number, chan->load_state); + return; + + } + + /* hopefully this'll shut the damned thing up after the transfer... */ + dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); +} + + +#define dmadbg2(x...) + +static irqreturn_t +s3c2410_dma_irq(int irq, void *devpw) +{ + struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; + struct s3c2410_dma_buf *buf; + + buf = chan->curr; + + dbg_showchan(chan); + + /* modify the channel state */ + + switch (chan->load_state) { + case S3C2410_DMALOAD_1RUNNING: + /* TODO - if we are running only one buffer, we probably + * want to reload here, and then worry about the buffer + * callback */ + + chan->load_state = S3C2410_DMALOAD_NONE; + break; + + case S3C2410_DMALOAD_1LOADED: + /* iirc, we should go back to NONE loaded here, we + * had a buffer, and it was never verified as being + * loaded. + */ + + chan->load_state = S3C2410_DMALOAD_NONE; + break; + + case S3C2410_DMALOAD_1LOADED_1RUNNING: + /* we'll worry about checking to see if another buffer is + * ready after we've called back the owner. This should + * ensure we do not wait around too long for the DMA + * engine to start the next transfer + */ + + chan->load_state = S3C2410_DMALOAD_1LOADED; + break; + + case S3C2410_DMALOAD_NONE: + printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", + chan->number); + break; + + default: + printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", + chan->number, chan->load_state); + break; + } + + if (buf != NULL) { + /* update the chain to make sure that if we load any more + * buffers when we call the callback function, things should + * work properly */ + + chan->curr = buf->next; + buf->next = NULL; + + if (buf->magic != BUF_MAGIC) { + printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", + chan->number, __FUNCTION__, buf); + return IRQ_HANDLED; + } + + s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); + + /* free resouces */ + s3c2410_dma_freebuf(buf); + } else { + } + + /* only reload if the channel is still running... our buffer done + * routine may have altered the state by requesting the dma channel + * to stop or shutdown... */ + + /* todo: check that when the channel is shut-down from inside this + * function, we cope with unsetting reload, etc */ + + if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { + unsigned long flags; + + switch (chan->load_state) { + case S3C2410_DMALOAD_1RUNNING: + /* don't need to do anything for this state */ + break; + + case S3C2410_DMALOAD_NONE: + /* can load buffer immediately */ + break; + + case S3C2410_DMALOAD_1LOADED: + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + /* flag error? */ + printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", + chan->number, __FUNCTION__); + return IRQ_HANDLED; + } + + break; + + case S3C2410_DMALOAD_1LOADED_1RUNNING: + goto no_load; + + default: + printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", + chan->number, chan->load_state); + return IRQ_HANDLED; + } + + local_irq_save(flags); + s3c2410_dma_loadbuffer(chan, chan->next); + local_irq_restore(flags); + } else { + s3c2410_dma_lastxfer(chan); + + /* see if we can stop this channel.. */ + if (chan->load_state == S3C2410_DMALOAD_NONE) { + pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", + chan->number, jiffies); + s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, + S3C2410_DMAOP_STOP); + } + } + + no_load: + return IRQ_HANDLED; +} + +static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel); + +/* s3c2410_request_dma + * + * get control of an dma channel +*/ + +int s3c2410_dma_request(unsigned int channel, + struct s3c2410_dma_client *client, + void *dev) +{ + struct s3c2410_dma_chan *chan; + unsigned long flags; + int err; + + pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", + channel, client->name, dev); + + local_irq_save(flags); + + chan = s3c2410_dma_map_channel(channel); + if (chan == NULL) { + local_irq_restore(flags); + return -EBUSY; + } + + dbg_showchan(chan); + + chan->client = client; + chan->in_use = 1; + + if (!chan->irq_claimed) { + pr_debug("dma%d: %s : requesting irq %d\n", + channel, __FUNCTION__, chan->irq); + + chan->irq_claimed = 1; + local_irq_restore(flags); + + err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, + client->name, (void *)chan); + + local_irq_save(flags); + + if (err) { + chan->in_use = 0; + chan->irq_claimed = 0; + local_irq_restore(flags); + + printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", + client->name, chan->irq, chan->number); + return err; + } + + chan->irq_enabled = 1; + } + + local_irq_restore(flags); + + /* need to setup */ + + pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_request); + +/* s3c2410_dma_free + * + * release the given channel back to the system, will stop and flush + * any outstanding transfers, and ensure the channel is ready for the + * next claimant. + * + * Note, although a warning is currently printed if the freeing client + * info is not the same as the registrant's client info, the free is still + * allowed to go through. +*/ + +int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + unsigned long flags; + + if (chan == NULL) + return -EINVAL; + + local_irq_save(flags); + + if (chan->client != client) { + printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", + channel, chan->client, client); + } + + /* sort out stopping and freeing the channel */ + + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("%s: need to stop dma channel %p\n", + __FUNCTION__, chan); + + /* possibly flush the channel */ + s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP); + } + + chan->client = NULL; + chan->in_use = 0; + + if (chan->irq_claimed) + free_irq(chan->irq, (void *)chan); + + chan->irq_claimed = 0; + + if (!(channel & DMACH_LOW_LEVEL)) + dma_chan_map[channel] = NULL; + + local_irq_restore(flags); + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_free); + +static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) +{ + unsigned long flags; + unsigned long tmp; + + pr_debug("%s:\n", __FUNCTION__); + + dbg_showchan(chan); + + local_irq_save(flags); + + s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); + + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp |= S3C2410_DMASKTRIG_STOP; + //tmp &= ~S3C2410_DMASKTRIG_ON; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + +#if 0 + /* should also clear interrupts, according to WinCE BSP */ + tmp = dma_rdreg(chan, S3C2410_DMA_DCON); + tmp |= S3C2410_DCON_NORELOAD; + dma_wrreg(chan, S3C2410_DMA_DCON, tmp); +#endif + + /* should stop do this, or should we wait for flush? */ + chan->state = S3C2410_DMA_IDLE; + chan->load_state = S3C2410_DMALOAD_NONE; + + local_irq_restore(flags); + + return 0; +} + +void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan) +{ + unsigned long tmp; + unsigned int timeout = 0x10000; + + while (timeout-- > 0) { + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + + if (!(tmp & S3C2410_DMASKTRIG_ON)) + return; + } + + pr_debug("dma%d: failed to stop?\n", chan->number); +} + + +/* s3c2410_dma_flush + * + * stop the channel, and remove all current and pending transfers +*/ + +static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) +{ + struct s3c2410_dma_buf *buf, *next; + unsigned long flags; + + pr_debug("%s: chan %p (%d)\n", __FUNCTION__, chan, chan->number); + + dbg_showchan(chan); + + local_irq_save(flags); + + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("%s: stopping channel...\n", __FUNCTION__ ); + s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); + } + + buf = chan->curr; + if (buf == NULL) + buf = chan->next; + + chan->curr = chan->next = chan->end = NULL; + + if (buf != NULL) { + for ( ; buf != NULL; buf = next) { + next = buf->next; + + pr_debug("%s: free buffer %p, next %p\n", + __FUNCTION__, buf, buf->next); + + s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); + s3c2410_dma_freebuf(buf); + } + } + + dbg_showregs(chan); + + s3c2410_dma_waitforstop(chan); + +#if 0 + /* should also clear interrupts, according to WinCE BSP */ + { + unsigned long tmp; + + tmp = dma_rdreg(chan, S3C2410_DMA_DCON); + tmp |= S3C2410_DCON_NORELOAD; + dma_wrreg(chan, S3C2410_DMA_DCON, tmp); + } +#endif + + dbg_showregs(chan); + + local_irq_restore(flags); + + return 0; +} + +int +s3c2410_dma_started(struct s3c2410_dma_chan *chan) +{ + unsigned long flags; + + local_irq_save(flags); + + dbg_showchan(chan); + + /* if we've only loaded one buffer onto the channel, then chec + * to see if we have another, and if so, try and load it so when + * the first buffer is finished, the new one will be loaded onto + * the channel */ + + if (chan->next != NULL) { + if (chan->load_state == S3C2410_DMALOAD_1LOADED) { + + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + pr_debug("%s: buff not yet loaded, no more todo\n", + __FUNCTION__); + } else { + chan->load_state = S3C2410_DMALOAD_1RUNNING; + s3c2410_dma_loadbuffer(chan, chan->next); + } + + } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { + s3c2410_dma_loadbuffer(chan, chan->next); + } + } + + + local_irq_restore(flags); + + return 0; + +} + +int +s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + switch (op) { + case S3C2410_DMAOP_START: + return s3c2410_dma_start(chan); + + case S3C2410_DMAOP_STOP: + return s3c2410_dma_dostop(chan); + + case S3C2410_DMAOP_PAUSE: + case S3C2410_DMAOP_RESUME: + return -ENOENT; + + case S3C2410_DMAOP_FLUSH: + return s3c2410_dma_flush(chan); + + case S3C2410_DMAOP_STARTED: + return s3c2410_dma_started(chan); + + case S3C2410_DMAOP_TIMEOUT: + return 0; + + } + + return -ENOENT; /* unknown, don't bother */ +} + +EXPORT_SYMBOL(s3c2410_dma_ctrl); + +/* DMA configuration for each channel + * + * DISRCC -> source of the DMA (AHB,APB) + * DISRC -> source address of the DMA + * DIDSTC -> destination of the DMA (AHB,APD) + * DIDST -> destination address of the DMA +*/ + +/* s3c2410_dma_config + * + * xfersize: size of unit in bytes (1,2,4) + * dcon: base value of the DCONx register +*/ + +int s3c2410_dma_config(dmach_t channel, + int xferunit, + int dcon) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", + __FUNCTION__, channel, xferunit, dcon); + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: Initial dcon is %08x\n", __FUNCTION__, dcon); + + dcon |= chan->dcon & dma_sel.dcon_mask; + + pr_debug("%s: New dcon is %08x\n", __FUNCTION__, dcon); + + switch (xferunit) { + case 1: + dcon |= S3C2410_DCON_BYTE; + break; + + case 2: + dcon |= S3C2410_DCON_HALFWORD; + break; + + case 4: + dcon |= S3C2410_DCON_WORD; + break; + + default: + pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit); + return -EINVAL; + } + + dcon |= S3C2410_DCON_HWTRIG; + dcon |= S3C2410_DCON_INTREQ; + + pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon); + + chan->dcon = dcon; + chan->xfer_unit = xferunit; + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_config); + +int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); + + chan->flags = flags; + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_setflags); + + +/* do we need to protect the settings of the fields from + * irq? +*/ + +int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); + + chan->op_fn = rtn; + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_set_opfn); + +int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); + + chan->callback_fn = rtn; + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn); + +/* s3c2410_dma_devconfig + * + * configure the dma source/destination hardware type and address + * + * source: S3C2410_DMASRC_HW: source is hardware + * S3C2410_DMASRC_MEM: source is memory + * + * hwcfg: the value for xxxSTCn register, + * bit 0: 0=increment pointer, 1=leave pointer + * bit 1: 0=soucre is AHB, 1=soucre is APB + * + * devaddr: physical address of the source +*/ + +int s3c2410_dma_devconfig(int channel, + enum s3c2410_dmasrc source, + int hwcfg, + unsigned long devaddr) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", + __FUNCTION__, (int)source, hwcfg, devaddr); + + chan->source = source; + chan->dev_addr = devaddr; + + switch (source) { + case S3C2410_DMASRC_HW: + /* source is hardware */ + pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", + __FUNCTION__, devaddr, hwcfg); + dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); + dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); + dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); + + chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); + return 0; + + case S3C2410_DMASRC_MEM: + /* source is memory */ + pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n", + __FUNCTION__, devaddr, hwcfg); + dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); + dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); + dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); + + chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); + return 0; + } + + printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source); + return -EINVAL; +} + +EXPORT_SYMBOL(s3c2410_dma_devconfig); + +/* s3c2410_dma_getposition + * + * returns the current transfer points for the dma source and destination +*/ + +int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) +{ + struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); + + if (chan == NULL) + return -EINVAL; + + if (src != NULL) + *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); + + if (dst != NULL) + *dst = dma_rdreg(chan, S3C2410_DMA_DCDST); + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_getposition); + + +/* system device class */ + +#ifdef CONFIG_PM + +static int s3c2410_dma_suspend(struct sys_device *dev, pm_message_t state) +{ + struct s3c2410_dma_chan *cp = container_of(dev, struct s3c2410_dma_chan, dev); + + printk(KERN_DEBUG "suspending dma channel %d\n", cp->number); + + if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) { + /* the dma channel is still working, which is probably + * a bad thing to do over suspend/resume. We stop the + * channel and assume that the client is either going to + * retry after resume, or that it is broken. + */ + + printk(KERN_INFO "dma: stopping channel %d due to suspend\n", + cp->number); + + s3c2410_dma_dostop(cp); + } + + return 0; +} + +static int s3c2410_dma_resume(struct sys_device *dev) +{ + return 0; +} + +#else +#define s3c2410_dma_suspend NULL +#define s3c2410_dma_resume NULL +#endif /* CONFIG_PM */ + +struct sysdev_class dma_sysclass = { + set_kset_name("s3c24xx-dma"), + .suspend = s3c2410_dma_suspend, + .resume = s3c2410_dma_resume, +}; + +/* kmem cache implementation */ + +static void s3c2410_dma_cache_ctor(void *p, struct kmem_cache *c, unsigned long f) +{ + memset(p, 0, sizeof(struct s3c2410_dma_buf)); +} + +/* initialisation code */ + +static int __init s3c2410_init_dma(void) +{ + struct s3c2410_dma_chan *cp; + int channel; + int ret; + + printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n"); + + dma_base = ioremap(S3C24XX_PA_DMA, 0x200); + if (dma_base == NULL) { + printk(KERN_ERR "dma failed to remap register block\n"); + return -ENOMEM; + } + + printk("Registering sysclass\n"); + + ret = sysdev_class_register(&dma_sysclass); + if (ret != 0) { + printk(KERN_ERR "dma sysclass registration failed\n"); + goto err; + } + + dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c2410_dma_buf), 0, + SLAB_HWCACHE_ALIGN, + s3c2410_dma_cache_ctor, NULL); + + if (dma_kmem == NULL) { + printk(KERN_ERR "dma failed to make kmem cache\n"); + ret = -ENOMEM; + goto err; + } + + for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) { + cp = &s3c2410_chans[channel]; + + memset(cp, 0, sizeof(struct s3c2410_dma_chan)); + + /* dma channel irqs are in order.. */ + cp->number = channel; + cp->irq = channel + IRQ_DMA0; + cp->regs = dma_base + (channel*0x40); + + /* point current stats somewhere */ + cp->stats = &cp->stats_store; + cp->stats_store.timeout_shortest = LONG_MAX; + + /* basic channel configuration */ + + cp->load_timeout = 1<<18; + + /* register system device */ + + cp->dev.cls = &dma_sysclass; + cp->dev.id = channel; + ret = sysdev_register(&cp->dev); + + printk("DMA channel %d at %p, irq %d\n", + cp->number, cp->regs, cp->irq); + } + + return 0; + + err: + kmem_cache_destroy(dma_kmem); + iounmap(dma_base); + dma_base = NULL; + return ret; +} + +core_initcall(s3c2410_init_dma); + +static inline int is_channel_valid(unsigned int channel) +{ + return (channel & DMA_CH_VALID); +} + +/* s3c2410_dma_map_channel() + * + * turn the virtual channel number into a real, and un-used hardware + * channel. + * + * currently this code uses first-free channel from the specified harware + * map, not taking into account anything that the board setup code may + * have to say about the likely peripheral set to be in use. +*/ + +struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) +{ + struct s3c24xx_dma_map *ch_map; + struct s3c2410_dma_chan *dmach; + int ch; + + if (dma_sel.map == NULL || channel > dma_sel.map_size) + return NULL; + + ch_map = dma_sel.map + channel; + + for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { + if (!is_channel_valid(ch_map->channels[ch])) + continue; + + if (s3c2410_chans[ch].in_use == 0) { + printk("mapped channel %d to %d\n", channel, ch); + break; + } + } + + if (ch >= S3C2410_DMA_CHANNELS) + return NULL; + + /* update our channel mapping */ + + dmach = &s3c2410_chans[ch]; + dma_chan_map[channel] = dmach; + + /* select the channel */ + + (dma_sel.select)(dmach, ch_map); + + return dmach; +} + +static void s3c24xx_dma_show_ch(struct s3c24xx_dma_map *map, int ch) +{ + /* show the channel configuration */ + + printk("%2d: %20s, channels %c%c%c%c\n", ch, map->name, + (is_channel_valid(map->channels[0]) ? '0' : '-'), + (is_channel_valid(map->channels[1]) ? '1' : '-'), + (is_channel_valid(map->channels[2]) ? '2' : '-'), + (is_channel_valid(map->channels[3]) ? '3' : '-')); +} + +static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch) +{ + if (1) + s3c24xx_dma_show_ch(map, ch); + + return 0; +} + +int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) +{ + struct s3c24xx_dma_map *nmap; + size_t map_sz = sizeof(*nmap) * sel->map_size; + int ptr; + + nmap = kmalloc(map_sz, GFP_KERNEL); + if (nmap == NULL) + return -ENOMEM; + + memcpy(nmap, sel->map, map_sz); + memcpy(&dma_sel, sel, sizeof(*sel)); + + dma_sel.map = nmap; + + for (ptr = 0; ptr < sel->map_size; ptr++) + s3c24xx_dma_check_entry(nmap+ptr, ptr); + + return 0; +} diff --git a/arch/arm/plat-s3c24xx/gpio.c b/arch/arm/plat-s3c24xx/gpio.c new file mode 100644 index 000000000000..ec3a09c4d181 --- /dev/null +++ b/arch/arm/plat-s3c24xx/gpio.c @@ -0,0 +1,188 @@ +/* linux/arch/arm/plat-s3c24xx/gpio.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C24XX GPIO 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/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/arch/regs-gpio.h> + +void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) +{ + void __iomem *base = S3C24XX_GPIO_BASE(pin); + unsigned long mask; + unsigned long con; + unsigned long flags; + + if (pin < S3C2410_GPIO_BANKB) { + mask = 1 << S3C2410_GPIO_OFFSET(pin); + } else { + mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; + } + + switch (function) { + case S3C2410_GPIO_LEAVE: + mask = 0; + function = 0; + break; + + case S3C2410_GPIO_INPUT: + case S3C2410_GPIO_OUTPUT: + case S3C2410_GPIO_SFN2: + case S3C2410_GPIO_SFN3: + if (pin < S3C2410_GPIO_BANKB) { + function -= 1; + function &= 1; + function <<= S3C2410_GPIO_OFFSET(pin); + } else { + function &= 3; + function <<= S3C2410_GPIO_OFFSET(pin)*2; + } + } + + /* modify the specified register wwith IRQs off */ + + local_irq_save(flags); + + con = __raw_readl(base + 0x00); + con &= ~mask; + con |= function; + + __raw_writel(con, base + 0x00); + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(s3c2410_gpio_cfgpin); + +unsigned int s3c2410_gpio_getcfg(unsigned int pin) +{ + void __iomem *base = S3C24XX_GPIO_BASE(pin); + unsigned long val = __raw_readl(base); + + if (pin < S3C2410_GPIO_BANKB) { + val >>= S3C2410_GPIO_OFFSET(pin); + val &= 1; + val += 1; + } else { + val >>= S3C2410_GPIO_OFFSET(pin)*2; + val &= 3; + } + + return val | S3C2410_GPIO_INPUT; +} + +EXPORT_SYMBOL(s3c2410_gpio_getcfg); + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + void __iomem *base = S3C24XX_GPIO_BASE(pin); + unsigned long offs = S3C2410_GPIO_OFFSET(pin); + unsigned long flags; + unsigned long up; + + if (pin < S3C2410_GPIO_BANKB) + return; + + local_irq_save(flags); + + up = __raw_readl(base + 0x08); + up &= ~(1L << offs); + up |= to << offs; + __raw_writel(up, base + 0x08); + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(s3c2410_gpio_pullup); + +void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) +{ + void __iomem *base = S3C24XX_GPIO_BASE(pin); + unsigned long offs = S3C2410_GPIO_OFFSET(pin); + unsigned long flags; + unsigned long dat; + + local_irq_save(flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offs); + dat |= to << offs; + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(s3c2410_gpio_setpin); + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + void __iomem *base = S3C24XX_GPIO_BASE(pin); + unsigned long offs = S3C2410_GPIO_OFFSET(pin); + + return __raw_readl(base + 0x04) & (1<< offs); +} + +EXPORT_SYMBOL(s3c2410_gpio_getpin); + +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C24XX_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C24XX_MISCCR); + local_irq_restore(flags); + + return misccr; +} + +EXPORT_SYMBOL(s3c2410_modify_misccr); + +int s3c2410_gpio_getirq(unsigned int pin) +{ + if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15) + return -1; /* not valid interrupts */ + + if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) + return -1; /* not valid pin */ + + if (pin < S3C2410_GPF4) + return (pin - S3C2410_GPF0) + IRQ_EINT0; + + if (pin < S3C2410_GPG0) + return (pin - S3C2410_GPF4) + IRQ_EINT4; + + return (pin - S3C2410_GPG0) + IRQ_EINT8; +} + +EXPORT_SYMBOL(s3c2410_gpio_getirq); diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c new file mode 100644 index 000000000000..ce186398e3fd --- /dev/null +++ b/arch/arm/plat-s3c24xx/irq.c @@ -0,0 +1,801 @@ +/* linux/arch/arm/plat-s3c24xx/irq.c + * + * Copyright (c) 2003,2004 Simtec Electronics + * Ben Dooks <ben@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 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 + * + * Changelog: + * + * 22-Jul-2004 Ben Dooks <ben@simtec.co.uk> + * Fixed compile warnings + * + * 22-Jul-2004 Roc Wu <cooloney@yahoo.com.cn> + * Fixed s3c_extirq_type + * + * 21-Jul-2004 Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org> + * Addition of ADC/TC demux + * + * 04-Oct-2004 Klaus Fetscher <k.fetscher@fetron.de> + * Fix for set_irq_type() on low EINT numbers + * + * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> + * Tidy up KF's patch and sort out new release + * + * 05-Oct-2004 Ben Dooks <ben@simtec.co.uk> + * Add support for power management controls + * + * 04-Nov-2004 Ben Dooks + * Fix standard IRQ wake for EINT0..4 and RTC + * + * 22-Feb-2005 Ben Dooks + * Fixed edge-triggering on ADC IRQ + * + * 28-Jun-2005 Ben Dooks + * Mark IRQ_LCD valid + * + * 25-Jul-2005 Ben Dooks + * Split the S3C2440 IRQ code to seperate file +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/ptrace.h> +#include <linux/sysdev.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/mach/irq.h> + +#include <asm/arch/regs-irq.h> +#include <asm/arch/regs-gpio.h> + +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> +#include <asm/plat-s3c24xx/irq.h> + +/* wakeup irq control */ + +#ifdef CONFIG_PM + +/* state for IRQs over sleep */ + +/* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources + * + * set bit to 1 in allow bitfield to enable the wakeup settings on it +*/ + +unsigned long s3c_irqwake_intallow = 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL; +unsigned long s3c_irqwake_intmask = 0xffffffffL; +unsigned long s3c_irqwake_eintallow = 0x0000fff0L; +unsigned long s3c_irqwake_eintmask = 0xffffffffL; + +int +s3c_irq_wake(unsigned int irqno, unsigned int state) +{ + unsigned long irqbit = 1 << (irqno - IRQ_EINT0); + + if (!(s3c_irqwake_intallow & irqbit)) + return -ENOENT; + + printk(KERN_INFO "wake %s for irq %d\n", + state ? "enabled" : "disabled", irqno); + + if (!state) + s3c_irqwake_intmask |= irqbit; + else + s3c_irqwake_intmask &= ~irqbit; + + return 0; +} + +static int +s3c_irqext_wake(unsigned int irqno, unsigned int state) +{ + unsigned long bit = 1L << (irqno - EXTINT_OFF); + + if (!(s3c_irqwake_eintallow & bit)) + return -ENOENT; + + printk(KERN_INFO "wake %s for irq %d\n", + state ? "enabled" : "disabled", irqno); + + if (!state) + s3c_irqwake_eintmask |= bit; + else + s3c_irqwake_eintmask &= ~bit; + + return 0; +} + +#else +#define s3c_irqext_wake NULL +#define s3c_irq_wake NULL +#endif + + +static void +s3c_irq_mask(unsigned int irqno) +{ + unsigned long mask; + + irqno -= IRQ_EINT0; + + mask = __raw_readl(S3C2410_INTMSK); + mask |= 1UL << irqno; + __raw_writel(mask, S3C2410_INTMSK); +} + +static inline void +s3c_irq_ack(unsigned int irqno) +{ + unsigned long bitval = 1UL << (irqno - IRQ_EINT0); + + __raw_writel(bitval, S3C2410_SRCPND); + __raw_writel(bitval, S3C2410_INTPND); +} + +static inline void +s3c_irq_maskack(unsigned int irqno) +{ + unsigned long bitval = 1UL << (irqno - IRQ_EINT0); + unsigned long mask; + + mask = __raw_readl(S3C2410_INTMSK); + __raw_writel(mask|bitval, S3C2410_INTMSK); + + __raw_writel(bitval, S3C2410_SRCPND); + __raw_writel(bitval, S3C2410_INTPND); +} + + +static void +s3c_irq_unmask(unsigned int irqno) +{ + unsigned long mask; + + if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) + irqdbf2("s3c_irq_unmask %d\n", irqno); + + irqno -= IRQ_EINT0; + + mask = __raw_readl(S3C2410_INTMSK); + mask &= ~(1UL << irqno); + __raw_writel(mask, S3C2410_INTMSK); +} + +struct irq_chip s3c_irq_level_chip = { + .name = "s3c-level", + .ack = s3c_irq_maskack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .set_wake = s3c_irq_wake +}; + +static struct irq_chip s3c_irq_chip = { + .name = "s3c", + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .set_wake = s3c_irq_wake +}; + +static void +s3c_irqext_mask(unsigned int irqno) +{ + unsigned long mask; + + irqno -= EXTINT_OFF; + + mask = __raw_readl(S3C24XX_EINTMASK); + mask |= ( 1UL << irqno); + __raw_writel(mask, S3C24XX_EINTMASK); +} + +static void +s3c_irqext_ack(unsigned int irqno) +{ + unsigned long req; + unsigned long bit; + unsigned long mask; + + bit = 1UL << (irqno - EXTINT_OFF); + + mask = __raw_readl(S3C24XX_EINTMASK); + + __raw_writel(bit, S3C24XX_EINTPEND); + + req = __raw_readl(S3C24XX_EINTPEND); + req &= ~mask; + + /* not sure if we should be acking the parent irq... */ + + if (irqno <= IRQ_EINT7 ) { + if ((req & 0xf0) == 0) + s3c_irq_ack(IRQ_EINT4t7); + } else { + if ((req >> 8) == 0) + s3c_irq_ack(IRQ_EINT8t23); + } +} + +static void +s3c_irqext_unmask(unsigned int irqno) +{ + unsigned long mask; + + irqno -= EXTINT_OFF; + + mask = __raw_readl(S3C24XX_EINTMASK); + mask &= ~( 1UL << irqno); + __raw_writel(mask, S3C24XX_EINTMASK); +} + +int +s3c_irqext_type(unsigned int irq, unsigned int type) +{ + void __iomem *extint_reg; + void __iomem *gpcon_reg; + unsigned long gpcon_offset, extint_offset; + unsigned long newvalue = 0, value; + + if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C24XX_EXTINT0; + gpcon_offset = (irq - IRQ_EINT0) * 2; + extint_offset = (irq - IRQ_EINT0) * 4; + } + else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C24XX_EXTINT0; + gpcon_offset = (irq - (EXTINT_OFF)) * 2; + extint_offset = (irq - (EXTINT_OFF)) * 4; + } + else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C24XX_EXTINT1; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT8) * 4; + } + else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C24XX_EXTINT2; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT16) * 4; + } else + return -1; + + /* Set the GPIO to external interrupt mode */ + value = __raw_readl(gpcon_reg); + value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); + __raw_writel(value, gpcon_reg); + + /* Set the external interrupt to pointed trigger type */ + switch (type) + { + case IRQT_NOEDGE: + printk(KERN_WARNING "No edge setting!\n"); + break; + + case IRQT_RISING: + newvalue = S3C2410_EXTINT_RISEEDGE; + break; + + case IRQT_FALLING: + newvalue = S3C2410_EXTINT_FALLEDGE; + break; + + case IRQT_BOTHEDGE: + newvalue = S3C2410_EXTINT_BOTHEDGE; + break; + + case IRQT_LOW: + newvalue = S3C2410_EXTINT_LOWLEV; + break; + + case IRQT_HIGH: + newvalue = S3C2410_EXTINT_HILEV; + break; + + default: + printk(KERN_ERR "No such irq type %d", type); + return -1; + } + + value = __raw_readl(extint_reg); + value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); + __raw_writel(value, extint_reg); + + return 0; +} + +static struct irq_chip s3c_irqext_chip = { + .name = "s3c-ext", + .mask = s3c_irqext_mask, + .unmask = s3c_irqext_unmask, + .ack = s3c_irqext_ack, + .set_type = s3c_irqext_type, + .set_wake = s3c_irqext_wake +}; + +static struct irq_chip s3c_irq_eint0t4 = { + .name = "s3c-ext0", + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .set_wake = s3c_irq_wake, + .set_type = s3c_irqext_type, +}; + +/* mask values for the parent registers for each of the interrupt types */ + +#define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) +#define INTMSK_UART1 (1UL << (IRQ_UART1 - IRQ_EINT0)) +#define INTMSK_UART2 (1UL << (IRQ_UART2 - IRQ_EINT0)) +#define INTMSK_ADCPARENT (1UL << (IRQ_ADCPARENT - IRQ_EINT0)) + + +/* UART0 */ + +static void +s3c_irq_uart0_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_UART0, 7); +} + +static void +s3c_irq_uart0_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_UART0); +} + +static void +s3c_irq_uart0_ack(unsigned int irqno) +{ + s3c_irqsub_maskack(irqno, INTMSK_UART0, 7); +} + +static struct irq_chip s3c_irq_uart0 = { + .name = "s3c-uart0", + .mask = s3c_irq_uart0_mask, + .unmask = s3c_irq_uart0_unmask, + .ack = s3c_irq_uart0_ack, +}; + +/* UART1 */ + +static void +s3c_irq_uart1_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_UART1, 7 << 3); +} + +static void +s3c_irq_uart1_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_UART1); +} + +static void +s3c_irq_uart1_ack(unsigned int irqno) +{ + s3c_irqsub_maskack(irqno, INTMSK_UART1, 7 << 3); +} + +static struct irq_chip s3c_irq_uart1 = { + .name = "s3c-uart1", + .mask = s3c_irq_uart1_mask, + .unmask = s3c_irq_uart1_unmask, + .ack = s3c_irq_uart1_ack, +}; + +/* UART2 */ + +static void +s3c_irq_uart2_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_UART2, 7 << 6); +} + +static void +s3c_irq_uart2_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_UART2); +} + +static void +s3c_irq_uart2_ack(unsigned int irqno) +{ + s3c_irqsub_maskack(irqno, INTMSK_UART2, 7 << 6); +} + +static struct irq_chip s3c_irq_uart2 = { + .name = "s3c-uart2", + .mask = s3c_irq_uart2_mask, + .unmask = s3c_irq_uart2_unmask, + .ack = s3c_irq_uart2_ack, +}; + +/* ADC and Touchscreen */ + +static void +s3c_irq_adc_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_ADCPARENT, 3 << 9); +} + +static void +s3c_irq_adc_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_ADCPARENT); +} + +static void +s3c_irq_adc_ack(unsigned int irqno) +{ + s3c_irqsub_ack(irqno, INTMSK_ADCPARENT, 3 << 9); +} + +static struct irq_chip s3c_irq_adc = { + .name = "s3c-adc", + .mask = s3c_irq_adc_mask, + .unmask = s3c_irq_adc_unmask, + .ack = s3c_irq_adc_ack, +}; + +/* irq demux for adc */ +static void s3c_irq_demux_adc(unsigned int irq, + struct irq_desc *desc) +{ + unsigned int subsrc, submsk; + unsigned int offset = 9; + struct irq_desc *mydesc; + + /* read the current pending interrupts, and the mask + * for what it is available */ + + subsrc = __raw_readl(S3C2410_SUBSRCPND); + submsk = __raw_readl(S3C2410_INTSUBMSK); + + subsrc &= ~submsk; + subsrc >>= offset; + subsrc &= 3; + + if (subsrc != 0) { + if (subsrc & 1) { + mydesc = irq_desc + IRQ_TC; + desc_handle_irq(IRQ_TC, mydesc); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_ADC; + desc_handle_irq(IRQ_ADC, mydesc); + } + } +} + +static void s3c_irq_demux_uart(unsigned int start) +{ + unsigned int subsrc, submsk; + unsigned int offset = start - IRQ_S3CUART_RX0; + struct irq_desc *desc; + + /* read the current pending interrupts, and the mask + * for what it is available */ + + subsrc = __raw_readl(S3C2410_SUBSRCPND); + submsk = __raw_readl(S3C2410_INTSUBMSK); + + irqdbf2("s3c_irq_demux_uart: start=%d (%d), subsrc=0x%08x,0x%08x\n", + start, offset, subsrc, submsk); + + subsrc &= ~submsk; + subsrc >>= offset; + subsrc &= 7; + + if (subsrc != 0) { + desc = irq_desc + start; + + if (subsrc & 1) + desc_handle_irq(start, desc); + + desc++; + + if (subsrc & 2) + desc_handle_irq(start+1, desc); + + desc++; + + if (subsrc & 4) + desc_handle_irq(start+2, desc); + } +} + +/* uart demux entry points */ + +static void +s3c_irq_demux_uart0(unsigned int irq, + struct irq_desc *desc) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX0); +} + +static void +s3c_irq_demux_uart1(unsigned int irq, + struct irq_desc *desc) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX1); +} + +static void +s3c_irq_demux_uart2(unsigned int irq, + struct irq_desc *desc) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX2); +} + +static void +s3c_irq_demux_extint8(unsigned int irq, + struct irq_desc *desc) +{ + unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); + unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); + + eintpnd &= ~eintmsk; + eintpnd &= ~0xff; /* ignore lower irqs */ + + /* we may as well handle all the pending IRQs here */ + + while (eintpnd) { + irq = __ffs(eintpnd); + eintpnd &= ~(1<<irq); + + irq += (IRQ_EINT4 - 4); + desc_handle_irq(irq, irq_desc + irq); + } + +} + +static void +s3c_irq_demux_extint4t7(unsigned int irq, + struct irq_desc *desc) +{ + unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); + unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); + + eintpnd &= ~eintmsk; + eintpnd &= 0xff; /* only lower irqs */ + + /* we may as well handle all the pending IRQs here */ + + while (eintpnd) { + irq = __ffs(eintpnd); + eintpnd &= ~(1<<irq); + + irq += (IRQ_EINT4 - 4); + + desc_handle_irq(irq, irq_desc + irq); + } +} + +#ifdef CONFIG_PM + +static struct sleep_save irq_save[] = { + SAVE_ITEM(S3C2410_INTMSK), + SAVE_ITEM(S3C2410_INTSUBMSK), +}; + +/* the extint values move between the s3c2410/s3c2440 and the s3c2412 + * so we use an array to hold them, and to calculate the address of + * the register at run-time +*/ + +static unsigned long save_extint[3]; +static unsigned long save_eintflt[4]; +static unsigned long save_eintmask; + +int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(save_extint); i++) + save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4)); + + for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) + save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4)); + + s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); + save_eintmask = __raw_readl(S3C24XX_EINTMASK); + + return 0; +} + +int s3c24xx_irq_resume(struct sys_device *dev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(save_extint); i++) + __raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4)); + + for (i = 0; i < ARRAY_SIZE(save_eintflt); i++) + __raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4)); + + s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); + __raw_writel(save_eintmask, S3C24XX_EINTMASK); + + return 0; +} + +#else +#define s3c24xx_irq_suspend NULL +#define s3c24xx_irq_resume NULL +#endif + +/* s3c24xx_init_irq + * + * Initialise S3C2410 IRQ system +*/ + +void __init s3c24xx_init_irq(void) +{ + unsigned long pend; + unsigned long last; + int irqno; + int i; + + irqdbf("s3c2410_init_irq: clearing interrupt status flags\n"); + + /* first, clear all interrupts pending... */ + + last = 0; + for (i = 0; i < 4; i++) { + pend = __raw_readl(S3C24XX_EINTPEND); + + if (pend == 0 || pend == last) + break; + + __raw_writel(pend, S3C24XX_EINTPEND); + printk("irq: clearing pending ext status %08x\n", (int)pend); + last = pend; + } + + last = 0; + for (i = 0; i < 4; i++) { + pend = __raw_readl(S3C2410_INTPND); + + if (pend == 0 || pend == last) + break; + + __raw_writel(pend, S3C2410_SRCPND); + __raw_writel(pend, S3C2410_INTPND); + printk("irq: clearing pending status %08x\n", (int)pend); + last = pend; + } + + last = 0; + for (i = 0; i < 4; i++) { + pend = __raw_readl(S3C2410_SUBSRCPND); + + if (pend == 0 || pend == last) + break; + + printk("irq: clearing subpending status %08x\n", (int)pend); + __raw_writel(pend, S3C2410_SUBSRCPND); + last = pend; + } + + /* register the main interrupts */ + + irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); + + for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { + /* set all the s3c2410 internal irqs */ + + switch (irqno) { + /* deal with the special IRQs (cascaded) */ + + case IRQ_EINT4t7: + case IRQ_EINT8t23: + case IRQ_UART0: + case IRQ_UART1: + case IRQ_UART2: + case IRQ_ADCPARENT: + set_irq_chip(irqno, &s3c_irq_level_chip); + set_irq_handler(irqno, handle_level_irq); + break; + + case IRQ_RESERVED6: + case IRQ_RESERVED24: + /* no IRQ here */ + break; + + default: + //irqdbf("registering irq %d (s3c irq)\n", irqno); + set_irq_chip(irqno, &s3c_irq_chip); + set_irq_handler(irqno, handle_edge_irq); + set_irq_flags(irqno, IRQF_VALID); + } + } + + /* setup the cascade irq handlers */ + + set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7); + set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); + + set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); + set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); + set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); + set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); + + /* external interrupts */ + + for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { + irqdbf("registering irq %d (ext int)\n", irqno); + set_irq_chip(irqno, &s3c_irq_eint0t4); + set_irq_handler(irqno, handle_edge_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { + irqdbf("registering irq %d (extended s3c irq)\n", irqno); + set_irq_chip(irqno, &s3c_irqext_chip); + set_irq_handler(irqno, handle_edge_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + /* register the uart interrupts */ + + irqdbf("s3c2410: registering external interrupts\n"); + + for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { + irqdbf("registering irq %d (s3c uart0 irq)\n", irqno); + set_irq_chip(irqno, &s3c_irq_uart0); + set_irq_handler(irqno, handle_level_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { + irqdbf("registering irq %d (s3c uart1 irq)\n", irqno); + set_irq_chip(irqno, &s3c_irq_uart1); + set_irq_handler(irqno, handle_level_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { + irqdbf("registering irq %d (s3c uart2 irq)\n", irqno); + set_irq_chip(irqno, &s3c_irq_uart2); + set_irq_handler(irqno, handle_level_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { + irqdbf("registering irq %d (s3c adc irq)\n", irqno); + set_irq_chip(irqno, &s3c_irq_adc); + set_irq_handler(irqno, handle_edge_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + irqdbf("s3c2410: registered interrupt handlers\n"); +} diff --git a/arch/arm/mach-s3c2410/pm-simtec.c b/arch/arm/plat-s3c24xx/pm-simtec.c index 619133eb7168..bd965f2feeca 100644 --- a/arch/arm/mach-s3c2410/pm-simtec.c +++ b/arch/arm/plat-s3c24xx/pm-simtec.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/pm-simtec.c +/* linux/arch/arm/plat-s3c24xx/pm-simtec.c * * Copyright (c) 2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -32,7 +32,7 @@ #include <asm/mach-types.h> -#include "pm.h" +#include <asm/plat-s3c24xx/pm.h> #define COPYRIGHT ", (c) 2005 Simtec Electronics" diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c new file mode 100644 index 000000000000..ecf68d611904 --- /dev/null +++ b/arch/arm/plat-s3c24xx/pm.c @@ -0,0 +1,659 @@ +/* linux/arch/arm/plat-s3c24xx/pm.c + * + * Copyright (c) 2004,2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C24XX Power Manager (Suspend-To-RAM) support + * + * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information + * + * 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 + * + * Parts based on arch/arm/mach-pxa/pm.c + * + * Thanks to Dimitry Andric for debugging +*/ + +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/crc32.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/serial_core.h> + +#include <asm/cacheflush.h> +#include <asm/hardware.h> +#include <asm/io.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-mem.h> +#include <asm/arch/regs-irq.h> + +#include <asm/mach/time.h> + +#include <asm/plat-s3c24xx/pm.h> + +/* for external use */ + +unsigned long s3c_pm_flags; + +#define PFX "s3c24xx-pm: " + +static struct sleep_save core_save[] = { + SAVE_ITEM(S3C2410_LOCKTIME), + SAVE_ITEM(S3C2410_CLKCON), + + /* we restore the timings here, with the proviso that the board + * brings the system up in an slower, or equal frequency setting + * to the original system. + * + * if we cannot guarantee this, then things are going to go very + * wrong here, as we modify the refresh and both pll settings. + */ + + SAVE_ITEM(S3C2410_BWSCON), + SAVE_ITEM(S3C2410_BANKCON0), + SAVE_ITEM(S3C2410_BANKCON1), + SAVE_ITEM(S3C2410_BANKCON2), + SAVE_ITEM(S3C2410_BANKCON3), + SAVE_ITEM(S3C2410_BANKCON4), + SAVE_ITEM(S3C2410_BANKCON5), + + SAVE_ITEM(S3C2410_CLKDIVN), + SAVE_ITEM(S3C2410_MPLLCON), + SAVE_ITEM(S3C2410_UPLLCON), + SAVE_ITEM(S3C2410_CLKSLOW), + SAVE_ITEM(S3C2410_REFRESH), +}; + +static struct sleep_save gpio_save[] = { + SAVE_ITEM(S3C2410_GPACON), + SAVE_ITEM(S3C2410_GPADAT), + + SAVE_ITEM(S3C2410_GPBCON), + SAVE_ITEM(S3C2410_GPBDAT), + SAVE_ITEM(S3C2410_GPBUP), + + SAVE_ITEM(S3C2410_GPCCON), + SAVE_ITEM(S3C2410_GPCDAT), + SAVE_ITEM(S3C2410_GPCUP), + + SAVE_ITEM(S3C2410_GPDCON), + SAVE_ITEM(S3C2410_GPDDAT), + SAVE_ITEM(S3C2410_GPDUP), + + SAVE_ITEM(S3C2410_GPECON), + SAVE_ITEM(S3C2410_GPEDAT), + SAVE_ITEM(S3C2410_GPEUP), + + SAVE_ITEM(S3C2410_GPFCON), + SAVE_ITEM(S3C2410_GPFDAT), + SAVE_ITEM(S3C2410_GPFUP), + + SAVE_ITEM(S3C2410_GPGCON), + SAVE_ITEM(S3C2410_GPGDAT), + SAVE_ITEM(S3C2410_GPGUP), + + SAVE_ITEM(S3C2410_GPHCON), + SAVE_ITEM(S3C2410_GPHDAT), + SAVE_ITEM(S3C2410_GPHUP), + + SAVE_ITEM(S3C2410_DCLKCON), +}; + +#ifdef CONFIG_S3C2410_PM_DEBUG + +#define SAVE_UART(va) \ + SAVE_ITEM((va) + S3C2410_ULCON), \ + SAVE_ITEM((va) + S3C2410_UCON), \ + SAVE_ITEM((va) + S3C2410_UFCON), \ + SAVE_ITEM((va) + S3C2410_UMCON), \ + SAVE_ITEM((va) + S3C2410_UBRDIV) + +static struct sleep_save uart_save[] = { + SAVE_UART(S3C24XX_VA_UART0), + SAVE_UART(S3C24XX_VA_UART1), +#ifndef CONFIG_CPU_S3C2400 + SAVE_UART(S3C24XX_VA_UART2), +#endif +}; + +/* debug + * + * we send the debug to printascii() to allow it to be seen if the + * system never wakes up from the sleep +*/ + +extern void printascii(const char *); + +void pm_dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + printascii(buff); +} + +static void s3c2410_pm_debug_init(void) +{ + unsigned long tmp = __raw_readl(S3C2410_CLKCON); + + /* re-start uart clocks */ + tmp |= S3C2410_CLKCON_UART0; + tmp |= S3C2410_CLKCON_UART1; + tmp |= S3C2410_CLKCON_UART2; + + __raw_writel(tmp, S3C2410_CLKCON); + udelay(10); +} + +#define DBG(fmt...) pm_dbg(fmt) +#else +#define DBG(fmt...) printk(KERN_DEBUG fmt) + +#define s3c2410_pm_debug_init() do { } while(0) + +static struct sleep_save uart_save[] = {}; +#endif + +#if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0 + +/* suspend checking code... + * + * this next area does a set of crc checks over all the installed + * memory, so the system can verify if the resume was ok. + * + * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, + * increasing it will mean that the area corrupted will be less easy to spot, + * and reducing the size will cause the CRC save area to grow +*/ + +#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) + +static u32 crc_size; /* size needed for the crc block */ +static u32 *crcs; /* allocated over suspend/resume */ + +typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); + +/* s3c2410_pm_run_res + * + * go thorugh the given resource list, and look for system ram +*/ + +static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) +{ + while (ptr != NULL) { + if (ptr->child != NULL) + s3c2410_pm_run_res(ptr->child, fn, arg); + + if ((ptr->flags & IORESOURCE_MEM) && + strcmp(ptr->name, "System RAM") == 0) { + DBG("Found system RAM at %08lx..%08lx\n", + ptr->start, ptr->end); + arg = (fn)(ptr, arg); + } + + ptr = ptr->sibling; + } +} + +static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg) +{ + s3c2410_pm_run_res(&iomem_resource, fn, arg); +} + +static u32 *s3c2410_pm_countram(struct resource *res, u32 *val) +{ + u32 size = (u32)(res->end - res->start)+1; + + size += CHECK_CHUNKSIZE-1; + size /= CHECK_CHUNKSIZE; + + DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size); + + *val += size * sizeof(u32); + return val; +} + +/* s3c2410_pm_prepare_check + * + * prepare the necessary information for creating the CRCs. This + * must be done before the final save, as it will require memory + * allocating, and thus touching bits of the kernel we do not + * know about. +*/ + +static void s3c2410_pm_check_prepare(void) +{ + crc_size = 0; + + s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size); + + DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size); + + crcs = kmalloc(crc_size+4, GFP_KERNEL); + if (crcs == NULL) + printk(KERN_ERR "Cannot allocated CRC save area\n"); +} + +static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val) +{ + unsigned long addr, left; + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + *val = crc32_le(~0, phys_to_virt(addr), left); + val++; + } + + return val; +} + +/* s3c2410_pm_check_store + * + * compute the CRC values for the memory blocks before the final + * sleep. +*/ + +static void s3c2410_pm_check_store(void) +{ + if (crcs != NULL) + s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs); +} + +/* in_region + * + * return TRUE if the area defined by ptr..ptr+size contatins the + * what..what+whatsz +*/ + +static inline int in_region(void *ptr, int size, void *what, size_t whatsz) +{ + if ((what+whatsz) < ptr) + return 0; + + if (what > (ptr+size)) + return 0; + + return 1; +} + +static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val) +{ + void *save_at = phys_to_virt(s3c2410_sleep_save_phys); + unsigned long addr; + unsigned long left; + void *ptr; + u32 calc; + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + ptr = phys_to_virt(addr); + + if (in_region(ptr, left, crcs, crc_size)) { + DBG("skipping %08lx, has crc block in\n", addr); + goto skip_check; + } + + if (in_region(ptr, left, save_at, 32*4 )) { + DBG("skipping %08lx, has save block in\n", addr); + goto skip_check; + } + + /* calculate and check the checksum */ + + calc = crc32_le(~0, ptr, left); + if (calc != *val) { + printk(KERN_ERR PFX "Restore CRC error at " + "%08lx (%08x vs %08x)\n", addr, calc, *val); + + DBG("Restore CRC error at %08lx (%08x vs %08x)\n", + addr, calc, *val); + } + + skip_check: + val++; + } + + return val; +} + +/* s3c2410_pm_check_restore + * + * check the CRCs after the restore event and free the memory used + * to hold them +*/ + +static void s3c2410_pm_check_restore(void) +{ + if (crcs != NULL) { + s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs); + kfree(crcs); + crcs = NULL; + } +} + +#else + +#define s3c2410_pm_check_prepare() do { } while(0) +#define s3c2410_pm_check_restore() do { } while(0) +#define s3c2410_pm_check_store() do { } while(0) +#endif + +/* helper functions to save and restore register state */ + +void s3c2410_pm_do_save(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + ptr->val = __raw_readl(ptr->reg); + DBG("saved %p value %08lx\n", ptr->reg, ptr->val); + } +} + +/* s3c2410_pm_do_restore + * + * restore the system from the given list of saved registers + * + * Note, we do not use DBG() in here, as the system may not have + * restore the UARTs state yet +*/ + +void s3c2410_pm_do_restore(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", + ptr->reg, ptr->val, __raw_readl(ptr->reg)); + + __raw_writel(ptr->val, ptr->reg); + } +} + +/* s3c2410_pm_do_restore_core + * + * similar to s3c2410_pm_do_restore_core + * + * WARNING: Do not put any debug in here that may effect memory or use + * peripherals, as things may be changing! +*/ + +static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + __raw_writel(ptr->val, ptr->reg); + } +} + +/* s3c2410_pm_show_resume_irqs + * + * print any IRQs asserted at resume time (ie, we woke from) +*/ + +static void s3c2410_pm_show_resume_irqs(int start, unsigned long which, + unsigned long mask) +{ + int i; + + which &= ~mask; + + for (i = 0; i <= 31; i++) { + if ((which) & (1L<<i)) { + DBG("IRQ %d asserted at resume\n", start+i); + } + } +} + +/* s3c2410_pm_check_resume_pin + * + * check to see if the pin is configured correctly for sleep mode, and + * make any necessary adjustments if it is not +*/ + +static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) +{ + unsigned long irqstate; + unsigned long pinstate; + int irq = s3c2410_gpio_getirq(pin); + + if (irqoffs < 4) + irqstate = s3c_irqwake_intmask & (1L<<irqoffs); + else + irqstate = s3c_irqwake_eintmask & (1L<<irqoffs); + + pinstate = s3c2410_gpio_getcfg(pin); + + if (!irqstate) { + if (pinstate == S3C2410_GPIO_IRQ) + DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); + } else { + if (pinstate == S3C2410_GPIO_IRQ) { + DBG("Disabling IRQ %d (pin %d)\n", irq, pin); + s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT); + } + } +} + +/* s3c2410_pm_configure_extint + * + * configure all external interrupt pins +*/ + +static void s3c2410_pm_configure_extint(void) +{ + int pin; + + /* for each of the external interrupts (EINT0..EINT15) we + * need to check wether it is an external interrupt source, + * and then configure it as an input if it is not + */ + + for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) { + s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0); + } + + for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) { + s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8); + } +} + +void (*pm_cpu_prep)(void); +void (*pm_cpu_sleep)(void); + +#define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) + +/* s3c2410_pm_enter + * + * central control for sleep/resume process +*/ + +static int s3c2410_pm_enter(suspend_state_t state) +{ + unsigned long regs_save[16]; + + /* ensure the debug is initialised (if enabled) */ + + s3c2410_pm_debug_init(); + + DBG("s3c2410_pm_enter(%d)\n", state); + + if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { + printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); + return -EINVAL; + } + + if (state != PM_SUSPEND_MEM) { + printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n"); + return -EINVAL; + } + + /* check if we have anything to wake-up with... bad things seem + * to happen if you suspend with no wakeup (system will often + * require a full power-cycle) + */ + + if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && + !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { + printk(KERN_ERR PFX "No sources enabled for wake-up!\n"); + printk(KERN_ERR PFX "Aborting sleep\n"); + return -EINVAL; + } + + /* prepare check area if configured */ + + s3c2410_pm_check_prepare(); + + /* store the physical address of the register recovery block */ + + s3c2410_sleep_save_phys = virt_to_phys(regs_save); + + DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys); + + /* save all necessary core registers not covered by the drivers */ + + s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); + s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save)); + s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); + + /* set the irq configuration for wake */ + + s3c2410_pm_configure_extint(); + + DBG("sleep: irq wakeup masks: %08lx,%08lx\n", + s3c_irqwake_intmask, s3c_irqwake_eintmask); + + __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); + __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); + + /* ack any outstanding external interrupts before we go to sleep */ + + __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); + __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); + __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); + + /* call cpu specific preperation */ + + pm_cpu_prep(); + + /* flush cache back to ram */ + + flush_cache_all(); + + s3c2410_pm_check_store(); + + /* send the cpu to sleep... */ + + __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ + + /* s3c2410_cpu_save will also act as our return point from when + * we resume as it saves its own register state, so use the return + * code to differentiate return from save and return from sleep */ + + if (s3c2410_cpu_save(regs_save) == 0) { + flush_cache_all(); + pm_cpu_sleep(); + } + + /* restore the cpu state */ + + cpu_init(); + + /* restore the system state */ + + s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); + s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save)); + s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); + + s3c2410_pm_debug_init(); + + /* check what irq (if any) restored the system */ + + DBG("post sleep: IRQs 0x%08x, 0x%08x\n", + __raw_readl(S3C2410_SRCPND), + __raw_readl(S3C2410_EINTPEND)); + + s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), + s3c_irqwake_intmask); + + s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), + s3c_irqwake_eintmask); + + DBG("post sleep, preparing to return\n"); + + s3c2410_pm_check_restore(); + + /* ok, let's return from sleep */ + + DBG("S3C2410 PM Resume (post-restore)\n"); + return 0; +} + +/* + * Called after processes are frozen, but before we shut down devices. + */ +static int s3c2410_pm_prepare(suspend_state_t state) +{ + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static int s3c2410_pm_finish(suspend_state_t state) +{ + return 0; +} + +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct pm_ops s3c2410_pm_ops = { + .pm_disk_mode = PM_DISK_FIRMWARE, + .prepare = s3c2410_pm_prepare, + .enter = s3c2410_pm_enter, + .finish = s3c2410_pm_finish, +}; + +/* s3c2410_pm_init + * + * Attach the power management functions. This should be called + * from the board specific initialisation if the board supports + * it. +*/ + +int __init s3c2410_pm_init(void) +{ + printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); + + pm_set_ops(&s3c2410_pm_ops); + return 0; +} diff --git a/arch/arm/mach-s3c2410/s3c244x-irq.c b/arch/arm/plat-s3c24xx/s3c244x-irq.c index ede94636a72a..a0e39d894014 100644 --- a/arch/arm/mach-s3c2410/s3c244x-irq.c +++ b/arch/arm/plat-s3c24xx/s3c244x-irq.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c244x-irq.c +/* linux/arch/arm/plat-s3c24xx/s3c244x-irq.c * * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -35,9 +35,9 @@ #include <asm/arch/regs-irq.h> #include <asm/arch/regs-gpio.h> -#include "cpu.h" -#include "pm.h" -#include "irq.h" +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> +#include <asm/plat-s3c24xx/irq.h> /* camera irq */ diff --git a/arch/arm/mach-s3c2410/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index 23c7494ad10d..87aace4c8f8c 100644 --- a/arch/arm/mach-s3c2410/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/s3c244x.c +/* linux/arch/arm/plat-s3c24xx/s3c244x.c * * Copyright (c) 2004-2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -35,13 +35,13 @@ #include <asm/arch/regs-gpioj.h> #include <asm/arch/regs-dsc.h> -#include "s3c2410.h" -#include "s3c2440.h" +#include <asm/plat-s3c24xx/s3c2410.h> +#include <asm/plat-s3c24xx/s3c2440.h> #include "s3c244x.h" -#include "clock.h" -#include "devs.h" -#include "cpu.h" -#include "pm.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/devs.h> +#include <asm/plat-s3c24xx/cpu.h> +#include <asm/plat-s3c24xx/pm.h> static struct map_desc s3c244x_iodesc[] __initdata = { IODESC_ENT(CLKPWR), diff --git a/arch/arm/mach-s3c2410/s3c244x.h b/arch/arm/plat-s3c24xx/s3c244x.h index 1488c1eb37e6..f8ed17676a35 100644 --- a/arch/arm/mach-s3c2410/s3c244x.h +++ b/arch/arm/plat-s3c24xx/s3c244x.h @@ -1,4 +1,4 @@ -/* arch/arm/mach-s3c2410/s3c244x.h +/* linux/arch/arm/plat-s3c24xx/s3c244x.h * * Copyright (c) 2004-2005 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> diff --git a/arch/arm/plat-s3c24xx/sleep.S b/arch/arm/plat-s3c24xx/sleep.S new file mode 100644 index 000000000000..2018c2e1dcc5 --- /dev/null +++ b/arch/arm/plat-s3c24xx/sleep.S @@ -0,0 +1,159 @@ +/* linux/arch/arm/mach-s3c2410/sleep.S + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 Power Manager (Suspend-To-RAM) support + * + * Based on PXA/SA1100 sleep code by: + * Nicolas Pitre, (c) 2002 Monta Vista Software Inc + * Cliff Brake, (c) 2001 + * + * 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/linkage.h> +#include <asm/assembler.h> +#include <asm/hardware.h> +#include <asm/arch/map.h> + +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-mem.h> +#include <asm/arch/regs-serial.h> + +/* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not + * reset the UART configuration, only enable if you really need this! +*/ +//#define CONFIG_DEBUG_RESUME + + .text + + /* s3c2410_cpu_save + * + * save enough of the CPU state to allow us to re-start + * pm.c code. as we store items like the sp/lr, we will + * end up returning from this function when the cpu resumes + * so the return value is set to mark this. + * + * This arangement means we avoid having to flush the cache + * from this code. + * + * entry: + * r0 = pointer to save block + * + * exit: + * r0 = 0 => we stored everything + * 1 => resumed from sleep + */ + +ENTRY(s3c2410_cpu_save) + stmfd sp!, { r4 - r12, lr } + + @@ store co-processor registers + + mrc p15, 0, r4, c15, c1, 0 @ CP access register + mrc p15, 0, r5, c13, c0, 0 @ PID + mrc p15, 0, r6, c3, c0, 0 @ Domain ID + mrc p15, 0, r7, c2, c0, 0 @ translation table base address + mrc p15, 0, r8, c1, c0, 0 @ control register + + stmia r0, { r4 - r13 } + + mov r0, #0 + ldmfd sp, { r4 - r12, pc } + + @@ return to the caller, after having the MMU + @@ turned on, this restores the last bits from the + @@ stack +resume_with_mmu: + mov r0, #1 + ldmfd sp!, { r4 - r12, pc } + + .ltorg + + @@ the next bits sit in the .data segment, even though they + @@ happen to be code... the s3c2410_sleep_save_phys needs to be + @@ accessed by the resume code before it can restore the MMU. + @@ This means that the variable has to be close enough for the + @@ code to read it... since the .text segment needs to be RO, + @@ the data segment can be the only place to put this code. + + .data + + .global s3c2410_sleep_save_phys +s3c2410_sleep_save_phys: + .word 0 + + /* s3c2410_cpu_resume + * + * resume code entry for bootloader to call + * + * we must put this code here in the data segment as we have no + * other way of restoring the stack pointer after sleep, and we + * must not write to the code segment (code is read-only) + */ + +ENTRY(s3c2410_cpu_resume) + mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE + msr cpsr_c, r0 + + @@ load UART to allow us to print the two characters for + @@ resume debug + + mov r2, #S3C24XX_PA_UART & 0xff000000 + orr r2, r2, #S3C24XX_PA_UART & 0xff000 + +#if 0 + /* SMDK2440 LED set */ + mov r14, #S3C24XX_PA_GPIO + ldr r12, [ r14, #0x54 ] + bic r12, r12, #3<<4 + orr r12, r12, #1<<7 + str r12, [ r14, #0x54 ] +#endif + +#ifdef CONFIG_DEBUG_RESUME + mov r3, #'L' + strb r3, [ r2, #S3C2410_UTXH ] +1001: + ldrb r14, [ r3, #S3C2410_UTRSTAT ] + tst r14, #S3C2410_UTRSTAT_TXE + beq 1001b +#endif /* CONFIG_DEBUG_RESUME */ + + mov r1, #0 + mcr p15, 0, r1, c8, c7, 0 @@ invalidate I & D TLBs + mcr p15, 0, r1, c7, c7, 0 @@ invalidate I & D caches + + ldr r0, s3c2410_sleep_save_phys @ address of restore block + ldmia r0, { r4 - r13 } + + mcr p15, 0, r4, c15, c1, 0 @ CP access register + mcr p15, 0, r5, c13, c0, 0 @ PID + mcr p15, 0, r6, c3, c0, 0 @ Domain ID + mcr p15, 0, r7, c2, c0, 0 @ translation table base + +#ifdef CONFIG_DEBUG_RESUME + mov r3, #'R' + strb r3, [ r2, #S3C2410_UTXH ] +#endif + + ldr r2, =resume_with_mmu + mcr p15, 0, r8, c1, c0, 0 @ turn on MMU, etc + nop @ second-to-last before mmu + mov pc, r2 @ go back to virtual address + + .ltorg diff --git a/arch/arm/mach-s3c2410/time.c b/arch/arm/plat-s3c24xx/time.c index 9910bf0f2cea..c523d1c9cce5 100644 --- a/arch/arm/mach-s3c2410/time.c +++ b/arch/arm/plat-s3c24xx/time.c @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/time.c +/* linux/arch/arm/plat-s3c24xx/time.c * * Copyright (C) 2003-2005 Simtec Electronics * Ben Dooks, <ben@simtec.co.uk> @@ -37,8 +37,8 @@ #include <asm/arch/regs-irq.h> #include <asm/mach/time.h> -#include "clock.h" -#include "cpu.h" +#include <asm/plat-s3c24xx/clock.h> +#include <asm/plat-s3c24xx/cpu.h> static unsigned long timer_startval; static unsigned long timer_usec_ticks; |