Commit a21765a7 authored by Ben Dooks's avatar Ben Dooks Committed by Russell King

[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: default avatarBen Dooks <ben-linux@fluff.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent d19494b1
......@@ -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"
......
......@@ -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/
......
# arch/arm/mach-s3c2400/Kconfig
#
# Copyright 2007 Simtec Electronics
#
# Licensed under GPLv2
menu "S3C2400 Machines"
endmenu
# 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
/* 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>
*
......
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
# 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-y :=
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-$(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
......@@ -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>
......
/* linux/arch/arm/mach-s3c2410/bast.h
extern void bast_init_irq(void);
/* 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 */
static LIST_HEAD(clocks);
#include <asm/plat-s3c24xx/s3c2410.h>
#include <asm/plat-s3c24xx/clock.h>
#include <asm/plat-s3c24xx/cpu.h>
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;
}
unsigned int clocks = clk->ctrlbit;
unsigned long clkcon;
/* Clock API calls */
clkcon = __raw_readl(S3C2410_CLKCON);
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;
if (enable)
clkcon |= clocks;
else
idno = to_platform_device(dev)->id;
clkcon &= ~clocks;
mutex_lock(&clocks_mutex);
/* ensure none of the special function bits set */
clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
list_for_each_entry(p, &clocks, list) {
if (p->id == idno &&
strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;
break;
}
}
__raw_writel(clkcon, S3C2410_CLKCON);
/* 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)
static int s3c2410_upll_enable(struct clk *clk, int enable)
{
if (IS_ERR(clk) || clk == NULL)
return;
unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
unsigned long orig = clkslow;
mutex_lock(&clocks_mutex);
if (enable)
clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
else
clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
if ((--clk->usage) == 0)
(clk->enable)(clk, 0);
__raw_writel(clkslow, S3C2410_CLKSLOW);
mutex_unlock(&clocks_mutex);
clk_disable(clk->parent);
}
/* if we started the UPLL, then allow to settle */
if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
udelay(200);
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;
/* standard clock definitions */
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",
static struct clk init_clocks_disable[] = {
{
.name = "nand",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
};
struct clk clk_mpll = {
.name = "mpll",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_NAND,
}, {
.name = "sdi",
.id = -1,
};
struct clk clk_upll = {
.name = "upll",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SDI,
}, {
.name = "adc",
.id = -1,
.parent = NULL,
.ctrlbit = 0,
};
struct clk clk_f = {
.name = "fclk",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_ADC,
}, {
.name = "i2c",
.id = -1,
.rate = 0,
.parent = &clk_mpll,
.ctrlbit = 0,
};
struct clk clk_h = {
.name = "hclk",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIC,
}, {
.name = "iis",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
};
struct clk clk_p = {
.name = "pclk",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_IIS,
}, {
.name = "spi",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SPI,
}
};
struct clk clk_usb_bus = {
.name = "usb-bus",
static struct clk init_clocks[] = {
{
.name = "lcd",
.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",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_LCDC,
}, {
.name = "gpio",
.id = -1,
.ctrlbit = S3C2410_DCLKCON_DCLK0EN,
.enable = s3c24xx_dclk_enable,
.set_parent = s3c24xx_dclk_setparent,
};
struct clk s3c24xx_dclk1 = {
.name = "dclk1",
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_GPIO,
}, {
.name = "usb-host",
.id = -1,
.ctrlbit = S3C2410_DCLKCON_DCLK0EN,
.enable = s3c24xx_dclk_enable,
.set_parent = s3c24xx_dclk_setparent,
};
struct clk s3c24xx_clkout0 = {
.name = "clkout0",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBH,
}, {
.name = "usb-device",
.id = -1,
.set_parent = s3c24xx_clkout_setparent,
};
struct clk s3c24xx_clkout1 = {
.name = "clkout1",
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_USBD,
}, {
.name = "timers",
.id = -1,
.set_parent = s3c24xx_clkout_setparent,
};
struct clk s3c24xx_uclk = {
.name = "uclk",
.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,
},
};
/* initialise the clock system */
/* 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 s3c24xx_register_clock(struct clk *clk)
int __init s3c2410_baseclk_add(void)
{
clk->owner = THIS_MODULE;
unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
struct clk *clkp;
struct clk *xtal;
int ret;
int ptr;
if (clk->enable == NULL)
clk->enable = clk_null_enable;
clk_upll.enable = s3c2410_upll_enable;
/* add to the list of available clocks */
if (s3c24xx_register_clock(&clk_usb_bus) < 0)
printk(KERN_ERR "failed to register usb bus clock\n");
mutex_lock(&clocks_mutex);
list_add(&clk->list, &clocks);
mutex_unlock(&clocks_mutex);
/* register clocks from clock array */
return 0;
}
clkp = init_clocks;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
/* ensure that we note the clock state */
/* initalise all the clocks */
clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
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;
ret = s3c24xx_register_clock(clkp);
if (ret < 0) {
printk(KERN_ERR "Failed to register clock %s (%d)\n",
clkp->name, ret);
}
}
/* assume uart clocks are correctly setup */
/* 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.
*/
/* register our clocks */
/* install (and disable) the clocks we do not need immediately */
if (s3c24xx_register_clock(&clk_xtal) < 0)
printk(KERN_ERR "failed to register master xtal\n");
clkp = init_clocks_disable;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
if (s3c24xx_register_clock(&clk_mpll) < 0)
printk(KERN_ERR "failed to register mpll clock\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_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;
}
/* 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;
}
/* 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);
/* 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);
/***********************************************************************
/* 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
......
......@@ -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"
......
......@@ -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] = {
......
......@@ -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 */
......
......@@ -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 */
......
/***********************************************************************
/* 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 */
......
......@@ -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 */
......
/* 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),
#include <asm/mach-types.h>
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),
#include <asm/arch/regs-gpio.h>
#include <asm/arch/h1940.h>
SAVE_ITEM(S3C2410_GPHCON),
SAVE_ITEM(S3C2410_GPHDAT),
SAVE_ITEM(S3C2410_GPHUP),
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)
{
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)
static void s3c2410_pm_prepare(void)
{
if (crcs != NULL)
s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs);
}
/* ensure at least GSTATUS3 has the resume address */
/* in_region
*
* return TRUE if the area defined by ptr..ptr+size contatins the
* what..what+whatsz
*/
__raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
{
if ((what+whatsz) < ptr)
return 0;
DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4));
if (what > (ptr+size))
return 0;
if (machine_is_h1940()) {
void *base = phys_to_virt(H1940_SUSPEND_CHECK);
unsigned long ptr;
unsigned long calc = 0;
return 1;
}
/* generate check for the bootloader to check on resume */
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);
for (ptr = 0; ptr < 0x40000; ptr += 0x400)
calc += __raw_readl(base+ptr);
if (in_region(ptr, left, crcs, crc_size)) {
DBG("skipping %08lx, has crc block in\n", addr);
goto skip_check;
__raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM));
}
if (in_region(ptr, left, save_at, 32*4 )) {
DBG("skipping %08lx, has save block in\n", addr);
goto skip_check;
}
/* the RX3715 uses similar code and the same H1940 and the
* same offsets for resume and checksum pointers */
/* calculate and check the checksum */
if (machine_is_rx3715()) {
void *base = phys_to_virt(H1940_SUSPEND_CHECK);
unsigned long ptr;
unsigned long calc = 0;
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);
/* generate check for the bootloader to check on resume */
DBG("Restore CRC error at %08lx (%08x vs %08x)\n",
addr, calc, *val);
}
for (ptr = 0; ptr < 0x40000; ptr += 0x4)
calc += __raw_readl(base+ptr);
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));
tmp = __raw_readl(S3C2410_GSTATUS2);
tmp &= S3C2410_GSTATUS2_OFFRESET;
__raw_writel(tmp, S3C2410_GSTATUS2);
__raw_writel(ptr->val, ptr->reg);
}
}
if ( machine_is_aml_m5900() )
s3c2410_gpio_setpin(S3C2410_GPF2, 0);
/* 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);
}
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;
which &= ~mask;
pm_cpu_prep = s3c2410_pm_prepare;
pm_cpu_sleep = s3c2410_cpu_suspend;
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
/* 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;
}
/* 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
/* 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);
/* 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);
/* 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
/* 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
......@@ -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 */
......
/* 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
......@@ -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
......
# 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
# 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
/* 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
......
/* 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>
......
/* 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
......
/* 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 = {
};
......
/* 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 = {
......
/* 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)
{
......
/* 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;
......
# 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
# 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
/* 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 */
......
/* 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>
......
/* 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)
{
......
/* 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 */
......
/* 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"
......
/* 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 */
......
/* 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 */
......
/* 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 */
......
/* 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) */
......
/* 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,
......
# 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
# 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
/* 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 */
......
/* 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,
......
# 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
# 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
/* 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;
}
/* 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 */
......
/* 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;
......
/* 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 */
......
/* 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;
}
/* 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);
/* 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");
}
/* 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"
......
/* 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;
}
/* 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 */
......
/* 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),
......
/* 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>
......
/* 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
/* 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;
......
/*
/* linux/include/asm-arm/plat-s3c24xx/clock.h
* linux/arch/arm/mach-s3c2410/clock.h
*
* Copyright (c) 2004-2005 Simtec Electronics
......
/* linux/arch/arm/mach-s3c2410/common-smdk.h
/* linux/include/asm-arm/plat-s3c24xx/common-smdk.h
*
* Copyright (c) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/cpu.h
/* linux/include/asm-arm/plat-s3c24xx/cpu.h
*
* Copyright (c) 2004-2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/devs.h
/* linux/include/asm-arm/plat-s3c24xx/devs.h
*
* Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/dma.h
/* linux/include/asm-arm/plat-s3c24xx/dma.h
*
* Copyright (C) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/irq.h
/* linux/include/asm-arm/plat-s3c24xx/irq.h
*
* Copyright (c) 2004-2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* linux/arch/arm/mach-s3c2410/pm.h
/* linux/include/asm-arm/plat-s3c24xx/pm.h
*
* Copyright (c) 2004 Simtec Electronics
* Written by Ben Dooks, <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/s3c2400.h
/* linux/include/asm-arm/plat-s3c24xx/s3c2400.h
*
* Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/s3c2410.h
/* linux/include/asm-arm/plat-s3c24xx/s3c2410.h
*
* Copyright (c) 2004 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/s3c2412.h
/* linux/include/asm-arm/plat-s3c24xx/s3c2412.h
*
* Copyright (c) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/s3c2440.h
/* linux/include/asm-arm/plat-s3c24xx/s3c2440.h
*
* Copyright (c) 2004-2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
/* arch/arm/mach-s3c2410/s3c2442.h
/* linux/include/asm-arm/plat-s3c24xx/s3c2442.h
*
* Copyright (c) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment