diff options
Diffstat (limited to 'arch/arm/mach-s3c2410')
34 files changed, 7722 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig new file mode 100644 index 000000000000..534df0c6c770 --- /dev/null +++ b/arch/arm/mach-s3c2410/Kconfig @@ -0,0 +1,169 @@ +if ARCH_S3C2410 + +menu "S3C24XX Implementations" + +config ARCH_BAST + bool "Simtec Electronics BAST (EB2410ITX)" + select CPU_S3C2410 + help + Say Y here if you are using the Simtec Electronics EB2410ITX + development board (also known as BAST) + + Product page: <http://www.simtec.co.uk/products/EB2410ITX/>. + +config ARCH_H1940 + bool "IPAQ H1940" + select CPU_S3C2410 + help + Say Y here if you are using the HP IPAQ H1940 + + <http://www.handhelds.org/projects/h1940.html>. + +config MACH_N30 + bool "Acer N30" + select CPU_S3C2410 + help + Say Y here if you are using the Acer N30 + + <http://zoo.weinigel.se/n30>. + +config ARCH_SMDK2410 + bool "SMDK2410/A9M2410" + select CPU_S3C2410 + help + Say Y here if you are using the SMDK2410 or the derived module A9M2410 + <http://www.fsforth.de> + +config ARCH_S3C2440 + bool "SMDK2440" + select CPU_S3C2440 + help + Say Y here if you are using the SMDK2440. + +config MACH_VR1000 + bool "Thorcom VR1000" + select CPU_S3C2410 + 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 + 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 + +endmenu + +config CPU_S3C2410 + bool + depends on ARCH_S3C2410 + help + Support for S3C2410 and S3C2410A family from the S3C24XX line + of Samsung Mobile CPUs. + +config CPU_S3C2440 + bool + depends on ARCH_S3C2410 + help + Support for S3C2440 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. + +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 `Documentation/arm/Samsing-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. + + Note, this does not affect the port used by the debug messages, + which is a separate configuration. + +endif diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile new file mode 100644 index 000000000000..7c379aad5d62 --- /dev/null +++ b/arch/arm/mach-s3c2410/Makefile @@ -0,0 +1,36 @@ + +# +# Makefile for the linux kernel. +# + +# Object file lists. + +obj-y := cpu.o irq.o time.o gpio.o clock.o devs.o +obj-m := +obj-n := +obj- := + +# S3C2410 support files + +obj-$(CONFIG_CPU_S3C2410) += s3c2410.o +obj-$(CONFIG_S3C2410_DMA) += dma.o + +# Power Management support + +obj-$(CONFIG_PM) += pm.o sleep.o + +# S3C2440 support + +obj-$(CONFIG_CPU_S3C2440) += s3c2440.o s3c2440-dsc.o + +# machine specific support + +obj-$(CONFIG_ARCH_BAST) += mach-bast.o usb-simtec.o +obj-$(CONFIG_ARCH_H1940) += mach-h1940.o +obj-$(CONFIG_MACH_N30) += mach-n30.o +obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.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_MACH_OTOM) += mach-otom.o +obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o diff --git a/arch/arm/mach-s3c2410/Makefile.boot b/arch/arm/mach-s3c2410/Makefile.boot new file mode 100644 index 000000000000..7dab2a0325b5 --- /dev/null +++ b/arch/arm/mach-s3c2410/Makefile.boot @@ -0,0 +1,3 @@ + zreladdr-y := 0x30008000 +params_phys-y := 0x30000100 + diff --git a/arch/arm/mach-s3c2410/bast-irq.c b/arch/arm/mach-s3c2410/bast-irq.c new file mode 100644 index 000000000000..5e5bbe893cbb --- /dev/null +++ b/arch/arm/mach-s3c2410/bast-irq.c @@ -0,0 +1,132 @@ +/* linux/arch/arm/mach-s3c2410/bast-irq.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * 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 + * + * Modifications: + * 08-Jan-2003 BJD Moved from central IRQ code + */ + + +#include <linux/init.h> +#include <linux/module.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/hardware/s3c2410/irq.h> + +#if 0 +#include <asm/debug-ll.h> +#endif + +#define irqdbf(x...) +#define irqdbf2(x...) + + +/* handle PC104 ISA interrupts from the system CPLD */ + +/* table of ISA irq nos to the relevant mask... zero means + * the irq is not implemented +*/ +static unsigned char bast_pc104_irqmasks[] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + 1, /* 3 */ + 0, /* 4 */ + 2, /* 5 */ + 0, /* 6 */ + 4, /* 7 */ + 0, /* 8 */ + 0, /* 9 */ + 8, /* 10 */ + 0, /* 11 */ + 0, /* 12 */ + 0, /* 13 */ + 0, /* 14 */ + 0, /* 15 */ +}; + +static unsigned char bast_pc104_irqs[] = { 3, 5, 7, 10 }; + +static void +bast_pc104_mask(unsigned int irqno) +{ + unsigned long temp; + + temp = __raw_readb(BAST_VA_PC104_IRQMASK); + temp &= ~bast_pc104_irqmasks[irqno]; + __raw_writeb(temp, BAST_VA_PC104_IRQMASK); + + if (temp == 0) + bast_extint_mask(IRQ_ISA); +} + +static void +bast_pc104_ack(unsigned int irqno) +{ + bast_extint_ack(IRQ_ISA); +} + +static void +bast_pc104_unmask(unsigned int irqno) +{ + unsigned long temp; + + temp = __raw_readb(BAST_VA_PC104_IRQMASK); + temp |= bast_pc104_irqmasks[irqno]; + __raw_writeb(temp, BAST_VA_PC104_IRQMASK); + + bast_extint_unmask(IRQ_ISA); +} + +static struct bast_pc104_chip = { + .mask = bast_pc104_mask, + .unmask = bast_pc104_unmask, + .ack = bast_pc104_ack +}; + +static void +bast_irq_pc104_demux(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int stat; + unsigned int irqno; + int i; + + stat = __raw_readb(BAST_VA_PC104_IRQREQ) & 0xf; + + for (i = 0; i < 4 && stat != 0; i++) { + if (stat & 1) { + irqno = bast_pc104_irqs[i]; + desc = irq_desc + irqno; + + desc->handle(irqno, desc, regs); + } + + stat >>= 1; + } +} diff --git a/arch/arm/mach-s3c2410/bast.h b/arch/arm/mach-s3c2410/bast.h new file mode 100644 index 000000000000..e5d03311752c --- /dev/null +++ b/arch/arm/mach-s3c2410/bast.h @@ -0,0 +1,2 @@ + +extern void bast_init_irq(void); diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c new file mode 100644 index 000000000000..e23f534d4e1d --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.c @@ -0,0 +1,507 @@ +/* linux/arch/arm/mach-s3c2410/clock.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 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/device.h> +#include <linux/sysdev.h> + +#include <linux/interrupt.h> +#include <linux/ioport.h> + +#include <asm/hardware.h> +#include <asm/atomic.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/hardware/clock.h> +#include <asm/arch/regs-clock.h> + +#include "clock.h" +#include "cpu.h" + +/* clock information */ + +static LIST_HEAD(clocks); +static DECLARE_MUTEX(clocks_sem); + +/* old functions */ + +void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable) +{ + unsigned long clkcon; + unsigned long flags; + + local_irq_save(flags); + + clkcon = __raw_readl(S3C2410_CLKCON); + clkcon &= ~clocks; + + if (enable) + clkcon |= clocks; + + /* ensure none of the special function bits set */ + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + + __raw_writel(clkcon, S3C2410_CLKCON); + + local_irq_restore(flags); +} + +/* enable and disable calls for use with the clk struct */ + +static int clk_null_enable(struct clk *clk, int enable) +{ + return 0; +} + +int s3c24xx_clkcon_enable(struct clk *clk, int enable) +{ + s3c24xx_clk_enable(clk->ctrlbit, 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; + + idno = (dev == NULL) ? -1 : to_platform_device(dev)->id; + + down(&clocks_sem); + + 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; + } + } + } + + up(&clocks_sem); + return clk; +} + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +int clk_enable(struct clk *clk) +{ + if (IS_ERR(clk)) + return -EINVAL; + + return (clk->enable)(clk, 1); +} + +void clk_disable(struct clk *clk) +{ + if (!IS_ERR(clk)) + (clk->enable)(clk, 0); +} + + +int clk_use(struct clk *clk) +{ + atomic_inc(&clk->used); + return 0; +} + + +void clk_unuse(struct clk *clk) +{ + atomic_dec(&clk->used); +} + +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + while (clk->parent != NULL && clk->rate == 0) + clk = clk->parent; + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return -EINVAL; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +EXPORT_SYMBOL(clk_get); +EXPORT_SYMBOL(clk_put); +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_use); +EXPORT_SYMBOL(clk_unuse); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); + +/* base clocks */ + +static struct clk clk_xtal = { + .name = "xtal", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +static struct clk clk_f = { + .name = "fclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +static struct clk clk_h = { + .name = "hclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +static struct clk clk_p = { + .name = "pclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +/* clocks that could be registered by external code */ + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .id = -1, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .id = -1, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .id = -1, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .id = -1, +}; + +struct clk s3c24xx_uclk = { + .name = "uclk", + .id = -1, +}; + + +/* clock definitions */ + +static struct clk init_clocks[] = { + { .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_NAND + }, + { .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_LCDC + }, + { .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBH + }, + { .name = "usb-device", + .id = -1, + .parent = &clk_h, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBD + }, + { .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_PWMT + }, + { .name = "sdi", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SDI + }, + { .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART0 + }, + { .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART1 + }, + { .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART2 + }, + { .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_GPIO + }, + { .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_RTC + }, + { .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_ADC + }, + { .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIC + }, + { .name = "iis", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIS + }, + { .name = "spi", + .id = -1, + .parent = &clk_p, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SPI + }, + { .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = 0 + } +}; + +/* initialise the clock system */ + +int s3c24xx_register_clock(struct clk *clk) +{ + clk->owner = THIS_MODULE; + atomic_set(&clk->used, 0); + + if (clk->enable == NULL) + clk->enable = clk_null_enable; + + /* add to the list of available clocks */ + + down(&clocks_sem); + list_add(&clk->list, &clocks); + up(&clocks_sem); + + return 0; +} + +/* initalise all the clocks */ + +int __init s3c24xx_setup_clocks(unsigned long xtal, + unsigned long fclk, + unsigned long hclk, + unsigned long pclk) +{ + struct clk *clkp = init_clocks; + int ptr; + int ret; + + printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n"); + + /* initialise the main system clocks */ + + clk_xtal.rate = xtal; + + clk_h.rate = hclk; + clk_p.rate = pclk; + clk_f.rate = fclk; + + /* it looks like just setting the register here is not good + * enough, and causes the odd hang at initial boot time, so + * do all of them indivdually. + * + * I think 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. + * + * and of course, this looks neater + */ + + s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0); + s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0); + s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0); + s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0); + s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0); + s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0); + + /* 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_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"); + + /* register clocks from clock array */ + + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + return 0; +} + +/* S3C2440 extended clock support */ + +#ifdef CONFIG_CPU_S3C2440 + +static struct clk s3c2440_clk_upll = { + .name = "upll", + .id = -1, +}; + +static struct clk s3c2440_clk_cam = { + .name = "camif", + .parent = &clk_h, + .id = -1, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2440_CLKCON_CAMERA, +}; + +static struct clk s3c2440_clk_ac97 = { + .name = "ac97", + .parent = &clk_p, + .id = -1, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2440_CLKCON_CAMERA, +}; + +static int s3c2440_clk_add(struct sys_device *sysdev) +{ + unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); + + s3c2440_clk_upll.rate = s3c2410_get_pll(upllcon, clk_xtal.rate) * 2; + + printk("S3C2440: Clock Support, UPLL %ld.%03ld MHz\n", + print_mhz(s3c2440_clk_upll.rate)); + + s3c24xx_register_clock(&s3c2440_clk_ac97); + s3c24xx_register_clock(&s3c2440_clk_cam); + s3c24xx_register_clock(&s3c2440_clk_upll); + + clk_disable(&s3c2440_clk_ac97); + clk_disable(&s3c2440_clk_cam); + + return 0; +} + +static struct sysdev_driver s3c2440_clk_driver = { + .add = s3c2440_clk_add, +}; + +static int s3c24xx_clk_driver(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_clk_driver); +} + +arch_initcall(s3c24xx_clk_driver); + +#endif /* CONFIG_CPU_S3C2440 */ diff --git a/arch/arm/mach-s3c2410/clock.h b/arch/arm/mach-s3c2410/clock.h new file mode 100644 index 000000000000..7953b6f397b9 --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.h @@ -0,0 +1,44 @@ +/* + * linux/arch/arm/mach-s3c2410/clock.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Written by Ben Dooks, <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +struct clk { + struct list_head list; + struct module *owner; + struct clk *parent; + const char *name; + int id; + atomic_t used; + unsigned long rate; + unsigned long ctrlbit; + int (*enable)(struct clk *, int enable); +}; + +/* other clocks which may be registered by board support */ + +extern struct clk s3c24xx_dclk0; +extern struct clk s3c24xx_dclk1; +extern struct clk s3c24xx_clkout0; +extern struct clk s3c24xx_clkout1; +extern struct clk s3c24xx_uclk; + +/* exports for arch/arm/mach-s3c2410 + * + * Please DO NOT use these outside of arch/arm/mach-s3c2410 +*/ + +extern int s3c24xx_clkcon_enable(struct clk *clk, int enable); +extern int s3c24xx_register_clock(struct clk *clk); + +extern int s3c24xx_setup_clocks(unsigned long xtal, + unsigned long fclk, + unsigned long hclk, + unsigned long pclk); diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/mach-s3c2410/cpu.c new file mode 100644 index 000000000000..ca366e9e264d --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.c @@ -0,0 +1,241 @@ +/* linux/arch/arm/mach-s3c2410/cpu.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C24XX CPU Support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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/device.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/delay.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <asm/arch/regs-gpio.h> + +#include "cpu.h" +#include "clock.h" +#include "s3c2410.h" +#include "s3c2440.h" + +struct cpu_table { + unsigned long idcode; + unsigned long idmask; + void (*map_io)(struct map_desc *mach_desc, int size); + void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); + void (*init_clocks)(int xtal); + int (*init)(void); + const char *name; +}; + +/* table of supported CPUs */ + +static const char name_s3c2410[] = "S3C2410"; +static const char name_s3c2440[] = "S3C2440"; +static const char name_s3c2410a[] = "S3C2410A"; +static const char name_s3c2440a[] = "S3C2440A"; + +static struct cpu_table cpu_ids[] __initdata = { + { + .idcode = 0x32410000, + .idmask = 0xffffffff, + .map_io = s3c2410_map_io, + .init_clocks = s3c2410_init_clocks, + .init_uarts = s3c2410_init_uarts, + .init = s3c2410_init, + .name = name_s3c2410 + }, + { + .idcode = 0x32410002, + .idmask = 0xffffffff, + .map_io = s3c2410_map_io, + .init_clocks = s3c2410_init_clocks, + .init_uarts = s3c2410_init_uarts, + .init = s3c2410_init, + .name = name_s3c2410a + }, + { + .idcode = 0x32440000, + .idmask = 0xffffffff, + .map_io = s3c2440_map_io, + .init_clocks = s3c2440_init_clocks, + .init_uarts = s3c2440_init_uarts, + .init = s3c2440_init, + .name = name_s3c2440 + }, + { + .idcode = 0x32440001, + .idmask = 0xffffffff, + .map_io = s3c2440_map_io, + .init_clocks = s3c2440_init_clocks, + .init_uarts = s3c2440_init_uarts, + .init = s3c2440_init, + .name = name_s3c2440a + } +}; + +/* minimal IO mapping */ + +static struct map_desc s3c_iodesc[] __initdata = { + IODESC_ENT(GPIO), + IODESC_ENT(IRQ), + IODESC_ENT(MEMCTRL), + IODESC_ENT(UART) +}; + + +static struct cpu_table * +s3c_lookup_cpu(unsigned long idcode) +{ + struct cpu_table *tab; + int count; + + tab = cpu_ids; + for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { + if ((idcode & tab->idmask) == tab->idcode) + return tab; + } + + return NULL; +} + +/* board information */ + +static struct s3c24xx_board *board; + +void s3c24xx_set_board(struct s3c24xx_board *b) +{ + int i; + + board = b; + + if (b->clocks_count != 0) { + struct clk **ptr = b->clocks;; + + for (i = b->clocks_count; i > 0; i--, ptr++) + s3c24xx_register_clock(*ptr); + } +} + +/* cpu information */ + +static struct cpu_table *cpu; + +void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) +{ + unsigned long idcode; + + /* initialise the io descriptors we need for initialisation */ + iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); + + idcode = __raw_readl(S3C2410_GSTATUS1); + cpu = s3c_lookup_cpu(idcode); + + if (cpu == NULL) { + printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); + panic("Unknown S3C24XX CPU"); + } + + if (cpu->map_io == NULL || cpu->init == NULL) { + printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); + panic("Unsupported S3C24XX CPU"); + } + + printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); + + (cpu->map_io)(mach_desc, size); +} + +/* s3c24xx_init_clocks + * + * Initialise the clock subsystem and associated information from the + * given master crystal value. + * + * xtal = 0 -> use default PLL crystal value (normally 12MHz) + * != 0 -> PLL crystal value in Hz +*/ + +void __init s3c24xx_init_clocks(int xtal) +{ + if (xtal == 0) + xtal = 12*1000*1000; + + if (cpu == NULL) + panic("s3c24xx_init_clocks: no cpu setup?\n"); + + if (cpu->init_clocks == NULL) + panic("s3c24xx_init_clocks: cpu has no clock init\n"); + else + (cpu->init_clocks)(xtal); +} + +void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + if (cpu == NULL) + return; + + if (cpu->init_uarts == NULL) { + printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); + } else + (cpu->init_uarts)(cfg, no); +} + +static int __init s3c_arch_init(void) +{ + int ret; + + // do the correct init for cpu + + if (cpu == NULL) + panic("s3c_arch_init: NULL cpu\n"); + + ret = (cpu->init)(); + if (ret != 0) + return ret; + + if (board != NULL) { + struct platform_device **ptr = board->devices; + int i; + + for (i = 0; i < board->devices_count; i++, ptr++) { + ret = platform_device_register(*ptr); + + if (ret) { + printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)->name, ret, *ptr); + } + } + + /* mask any error, we may not need all these board + * devices */ + ret = 0; + } + + return ret; +} + +arch_initcall(s3c_arch_init); diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h new file mode 100644 index 000000000000..478c15c0e36a --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.h @@ -0,0 +1,69 @@ +/* arch/arm/mach-s3c2410/cpu.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for S3C24XX CPU support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 24-Aug-2004 BJD Start of generic S3C24XX support + * 18-Oct-2004 BJD Moved board struct into this file + * 04-Jan-2005 BJD New uart initialisation + * 10-Jan-2005 BJD Moved generic init here, specific to cpu headers + * 14-Jan-2005 BJD Added s3c24xx_init_clocks() call + * 10-Mar-2005 LCVR Changed S3C2410_{VA,SZ} to S3C24XX_{VA,SZ} & IODESC_ENT + * 14-Mar-2005 BJD Updated for __iomem +*/ + +/* todo - fix when rmk changes iodescs to use `void __iomem *` */ + +#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE } + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) + +/* forward declaration */ +struct s3c2410_uartcfg; +struct map_desc; + +/* core initialisation functions */ + +extern void s3c24xx_init_irq(void); + +extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); + +extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c24xx_init_clocks(int xtal); + +/* the board structure is used at first initialsation time + * to get info such as the devices to register for this + * board. This is done because platfrom_add_devices() cannot + * be called from the map_io entry. +*/ + +struct s3c24xx_board { + struct platform_device **devices; + unsigned int devices_count; + + struct clk **clocks; + unsigned int clocks_count; +}; + +extern void s3c24xx_set_board(struct s3c24xx_board *board); + +/* timer for 2410/2440 */ + +struct sys_timer; +extern struct sys_timer s3c24xx_timer; + +/* system device classes */ + +extern struct sysdev_class s3c2440_sysclass; diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/mach-s3c2410/devs.c new file mode 100644 index 000000000000..64792f678668 --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.c @@ -0,0 +1,485 @@ +/* linux/arch/arm/mach-s3c2410/devs.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Base S3C2410 platform device definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 10-Mar-2005 LCVR Changed S3C2410_{VA,SZ} to S3C24XX_{VA,SZ} + * 10-Feb-2005 BJD Added camera from guillaume.gourat@nexvision.tv + * 29-Aug-2004 BJD Added timers 0 through 3 + * 29-Aug-2004 BJD Changed index of devices we only have one of to -1 + * 21-Aug-2004 BJD Added IRQ_TICK to RTC resources + * 18-Aug-2004 BJD Created initial version +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <asm/arch/regs-serial.h> + +#include "devs.h" + +/* Serial port registrations */ + +struct platform_device *s3c24xx_uart_devs[3]; + +/* USB Host Controller */ + +static struct resource s3c_usb_resource[] = { + [0] = { + .start = S3C2410_PA_USBHOST, + .end = S3C2410_PA_USBHOST + S3C24XX_SZ_USBHOST, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBH, + .end = IRQ_USBH, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_usb_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_usb = { + .name = "s3c2410-ohci", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_resource), + .resource = s3c_usb_resource, + .dev = { + .dma_mask = &s3c_device_usb_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_usb); + +/* LCD Controller */ + +static struct resource s3c_lcd_resource[] = { + [0] = { + .start = S3C2410_PA_LCD, + .end = S3C2410_PA_LCD + S3C24XX_SZ_LCD, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD, + .end = IRQ_LCD, + .flags = IORESOURCE_IRQ, + } + +}; + +static u64 s3c_device_lcd_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_lcd = { + .name = "s3c2410-lcd", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_lcd_resource), + .resource = s3c_lcd_resource, + .dev = { + .dma_mask = &s3c_device_lcd_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_lcd); + +/* NAND Controller */ + +static struct resource s3c_nand_resource[] = { + [0] = { + .start = S3C2410_PA_NAND, + .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND, + .flags = IORESOURCE_MEM, + } +}; + +struct platform_device s3c_device_nand = { + .name = "s3c2410-nand", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_nand_resource), + .resource = s3c_nand_resource, +}; + +EXPORT_SYMBOL(s3c_device_nand); + +/* USB Device (Gadget)*/ + +static struct resource s3c_usbgadget_resource[] = { + [0] = { + .start = S3C2410_PA_USBDEV, + .end = S3C2410_PA_USBDEV + S3C24XX_SZ_USBDEV, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBD, + .end = IRQ_USBD, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_usbgadget = { + .name = "s3c2410-usbgadget", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usbgadget_resource), + .resource = s3c_usbgadget_resource, +}; + +EXPORT_SYMBOL(s3c_device_usbgadget); + +/* Watchdog */ + +static struct resource s3c_wdt_resource[] = { + [0] = { + .start = S3C2410_PA_WATCHDOG, + .end = S3C2410_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_WDT, + .end = IRQ_WDT, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_wdt = { + .name = "s3c2410-wdt", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_wdt_resource), + .resource = s3c_wdt_resource, +}; + +EXPORT_SYMBOL(s3c_device_wdt); + +/* I2C */ + +static struct resource s3c_i2c_resource[] = { + [0] = { + .start = S3C2410_PA_IIC, + .end = S3C2410_PA_IIC + S3C24XX_SZ_IIC, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IIC, + .end = IRQ_IIC, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_i2c = { + .name = "s3c2410-i2c", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_i2c_resource), + .resource = s3c_i2c_resource, +}; + +EXPORT_SYMBOL(s3c_device_i2c); + +/* IIS */ + +static struct resource s3c_iis_resource[] = { + [0] = { + .start = S3C2410_PA_IIS, + .end = S3C2410_PA_IIS + S3C24XX_SZ_IIS, + .flags = IORESOURCE_MEM, + } +}; + +static u64 s3c_device_iis_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_iis = { + .name = "s3c2410-iis", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_iis_resource), + .resource = s3c_iis_resource, + .dev = { + .dma_mask = &s3c_device_iis_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_iis); + +/* RTC */ + +static struct resource s3c_rtc_resource[] = { + [0] = { + .start = S3C2410_PA_RTC, + .end = S3C2410_PA_RTC + 0xff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC, + .end = IRQ_RTC, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_TICK, + .end = IRQ_TICK, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device s3c_device_rtc = { + .name = "s3c2410-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_rtc_resource), + .resource = s3c_rtc_resource, +}; + +EXPORT_SYMBOL(s3c_device_rtc); + +/* ADC */ + +static struct resource s3c_adc_resource[] = { + [0] = { + .start = S3C2410_PA_ADC, + .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TC, + .end = IRQ_ADC, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_adc = { + .name = "s3c2410-adc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_adc_resource), + .resource = s3c_adc_resource, +}; + +/* SDI */ + +static struct resource s3c_sdi_resource[] = { + [0] = { + .start = S3C2410_PA_SDI, + .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SDI, + .end = IRQ_SDI, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_sdi = { + .name = "s3c2410-sdi", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_sdi_resource), + .resource = s3c_sdi_resource, +}; + +EXPORT_SYMBOL(s3c_device_sdi); + +/* SPI (0) */ + +static struct resource s3c_spi0_resource[] = { + [0] = { + .start = S3C2410_PA_SPI, + .end = S3C2410_PA_SPI + 0x1f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SPI0, + .end = IRQ_SPI0, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_spi0 = { + .name = "s3c2410-spi", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_spi0_resource), + .resource = s3c_spi0_resource, +}; + +EXPORT_SYMBOL(s3c_device_spi0); + +/* SPI (1) */ + +static struct resource s3c_spi1_resource[] = { + [0] = { + .start = S3C2410_PA_SPI + 0x20, + .end = S3C2410_PA_SPI + 0x20 + 0x1f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SPI1, + .end = IRQ_SPI1, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_spi1 = { + .name = "s3c2410-spi", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_spi1_resource), + .resource = s3c_spi1_resource, +}; + +EXPORT_SYMBOL(s3c_device_spi1); + +/* pwm timer blocks */ + +static struct resource s3c_timer0_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x0C, + .end = S3C2410_PA_TIMER + 0x0C + 0xB, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER0, + .end = IRQ_TIMER0, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer0 = { + .name = "s3c2410-timer", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_timer0_resource), + .resource = s3c_timer0_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer0); + +/* timer 1 */ + +static struct resource s3c_timer1_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x18, + .end = S3C2410_PA_TIMER + 0x23, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER1, + .end = IRQ_TIMER1, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer1 = { + .name = "s3c2410-timer", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_timer1_resource), + .resource = s3c_timer1_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer1); + +/* timer 2 */ + +static struct resource s3c_timer2_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x24, + .end = S3C2410_PA_TIMER + 0x2F, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER2, + .end = IRQ_TIMER2, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer2 = { + .name = "s3c2410-timer", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_timer2_resource), + .resource = s3c_timer2_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer2); + +/* timer 3 */ + +static struct resource s3c_timer3_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x30, + .end = S3C2410_PA_TIMER + 0x3B, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER3, + .end = IRQ_TIMER3, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer3 = { + .name = "s3c2410-timer", + .id = 3, + .num_resources = ARRAY_SIZE(s3c_timer3_resource), + .resource = s3c_timer3_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer3); + +#ifdef CONFIG_CPU_S3C2440 + +/* Camif Controller */ + +static struct resource s3c_camif_resource[] = { + [0] = { + .start = S3C2440_PA_CAMIF, + .end = S3C2440_PA_CAMIF + S3C2440_SZ_CAMIF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_CAM, + .end = IRQ_CAM, + .flags = IORESOURCE_IRQ, + } + +}; + +static u64 s3c_device_camif_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_camif = { + .name = "s3c2440-camif", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_camif_resource), + .resource = s3c_camif_resource, + .dev = { + .dma_mask = &s3c_device_camif_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_camif); + +#endif // CONFIG_CPU_S32440 diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h new file mode 100644 index 000000000000..d6328f96728b --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.h @@ -0,0 +1,48 @@ +/* arch/arm/mach-s3c2410/devs.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2410 standard platform devices + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 18-Aug-2004 BJD Created initial version + * 27-Aug-2004 BJD Added timers 0 through 3 + * 10-Feb-2005 BJD Added camera from guillaume.gourat@nexvision.tv +*/ +#include <linux/config.h> + +extern struct platform_device *s3c24xx_uart_devs[]; + +extern struct platform_device s3c_device_usb; +extern struct platform_device s3c_device_lcd; +extern struct platform_device s3c_device_wdt; +extern struct platform_device s3c_device_i2c; +extern struct platform_device s3c_device_iis; +extern struct platform_device s3c_device_rtc; +extern struct platform_device s3c_device_adc; +extern struct platform_device s3c_device_sdi; + +extern struct platform_device s3c_device_spi0; +extern struct platform_device s3c_device_spi1; + +extern struct platform_device s3c_device_nand; + +extern struct platform_device s3c_device_timer0; +extern struct platform_device s3c_device_timer1; +extern struct platform_device s3c_device_timer2; +extern struct platform_device s3c_device_timer3; + +extern struct platform_device s3c_device_usbgadget; + +/* s3c2440 specific devices */ + +#ifdef CONFIG_CPU_S3C2440 + +extern struct platform_device s3c_device_camif; + +#endif diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c new file mode 100644 index 000000000000..bc229fab86d4 --- /dev/null +++ b/arch/arm/mach-s3c2410/dma.c @@ -0,0 +1,1210 @@ +/* linux/arch/arm/mach-bast/dma.c + * + * (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 DMA core + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * 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. + * + * Changelog: + * 27-Feb-2005 BJD Added kmem cache for dma descriptors + * 18-Nov-2004 BJD Removed error for loading onto stopped channel + * 10-Nov-2004 BJD Ensure all external symbols exported for modules + * 10-Nov-2004 BJD Use sys_device and sysdev_class for power management + * 08-Aug-2004 BJD Apply rmk's suggestions + * 21-Jul-2004 BJD Ported to linux 2.6 + * 12-Jul-2004 BJD Finished re-write and change of API + * 06-Jul-2004 BJD Rewrote dma code to try and cope with various problems + * 23-May-2003 BJD Created file + * 19-Aug-2003 BJD Cleanup, header fix, added URL + * + * This file is based on the Sangwook Lee/Samsung patches, re-written due + * to various ommisions from the code (such as flexible dma configuration) + * for use with the BAST system board. + * + * The re-write is pretty much complete, and should be good enough for any + * possible DMA function + */ + +#include <linux/config.h> + +#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> + +/* io map for dma */ +static void __iomem *dma_base; +static kmem_cache_t *dma_kmem; + +/* dma channel state information */ +s3c2410_dma_chan_t 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(s3c2410_dma_chan_t *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(s3c2410_dma_chan_t *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_showregs(const char *fname, int line, s3c2410_dma_chan_t *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, s3c2410_dma_chan_t *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_showregs(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 */ + +#define check_channel(chan) \ + do { if ((chan) >= S3C2410_DMA_CHANNELS) { \ + printk(KERN_ERR "%s: invalid channel %d\n", __FUNCTION__, (chan)); \ + return -EINVAL; \ + } } while(0) + + +/* s3c2410_dma_stats_timeout + * + * Update DMA stats from timeout info +*/ + +static void +s3c2410_dma_stats_timeout(s3c2410_dma_stats_t *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(s3c2410_dma_chan_t *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(s3c2410_dma_chan_t *chan, + s3c2410_dma_buf_t *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; + } + + 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(s3c2410_dma_chan_t *chan, s3c2410_chan_op_t 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(s3c2410_dma_chan_t *chan, s3c2410_dma_buf_t *buf, + s3c2410_dma_buffresult_t result) +{ + pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", + chan->callback_fn, buf, buf->id, buf->size, result); + + 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(s3c2410_dma_chan_t *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("wrote %08lx to DMASKTRIG\n", 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); + + 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(s3c2410_dma_chan_t *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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + s3c2410_dma_buf_t *buf; + unsigned long flags; + + check_channel(channel); + + 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 (%d alloc)\n", + __FUNCTION__, 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(s3c2410_dma_buf_t *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(s3c2410_dma_chan_t *chan) +{ + pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", + chan->number, chan->load_state); + + 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\n", + chan->number); + return; + } + break; + + default: + pr_debug("dma%d: lastxfer: unhandled load_state %d with no next", + 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 pt_regs *regs) +{ + s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw; + s3c2410_dma_buf_t *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 { + } + + if (chan->next != NULL) { + 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\n", + chan->number); + 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, S3C2410_DMAOP_STOP); + } + } + + no_load: + return IRQ_HANDLED; +} + + + +/* s3c2410_request_dma + * + * get control of an dma channel +*/ + +int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, + void *dev) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + unsigned long flags; + int err; + + pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", + channel, client->name, dev); + + check_channel(channel); + + local_irq_save(flags); + + dbg_showchan(chan); + + if (chan->in_use) { + if (client != chan->client) { + printk(KERN_ERR "dma%d: already in use\n", channel); + local_irq_restore(flags); + return -EBUSY; + } else { + printk(KERN_ERR "dma%d: client already has channel\n", channel); + } + } + + chan->client = client; + chan->in_use = 1; + + if (!chan->irq_claimed) { + pr_debug("dma%d: %s : requesting irq %d\n", + channel, __FUNCTION__, chan->irq); + + err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT, + client->name, (void *)chan); + + if (err) { + chan->in_use = 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_claimed = 1; + 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, s3c2410_dma_client_t *client) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + unsigned long flags; + + check_channel(channel); + + 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; + + local_irq_restore(flags); + + return 0; +} + +EXPORT_SYMBOL(s3c2410_dma_free); + +static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) +{ + unsigned long tmp; + unsigned long flags; + + 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; + 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 + + chan->state = S3C2410_DMA_IDLE; + chan->load_state = S3C2410_DMALOAD_NONE; + + local_irq_restore(flags); + + return 0; +} + +/* s3c2410_dma_flush + * + * stop the channel, and remove all current and pending transfers +*/ + +static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) +{ + s3c2410_dma_buf_t *buf, *next; + unsigned long flags; + + pr_debug("%s:\n", __FUNCTION__); + + 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); + } + } + + local_irq_restore(flags); + + return 0; +} + + +int +s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + switch (op) { + case S3C2410_DMAOP_START: + return s3c2410_dma_start(chan); + + case S3C2410_DMAOP_STOP: + return s3c2410_dma_dostop(chan); + + case S3C2410_DMAOP_PAUSE: + return -ENOENT; + + case S3C2410_DMAOP_RESUME: + return -ENOENT; + + case S3C2410_DMAOP_FLUSH: + return s3c2410_dma_flush(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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", + __FUNCTION__, channel, xferunit, dcon); + + check_channel(channel); + + 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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + 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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + 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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + 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, + s3c2410_dmasrc_t source, + int hwcfg, + unsigned long devaddr) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + 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) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + 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) +{ + s3c2410_dma_chan_t *cp = container_of(dev, s3c2410_dma_chan_t, 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 */ + +static 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, kmem_cache_t *c, unsigned long f) +{ + memset(p, 0, sizeof(s3c2410_dma_buf_t)); +} + + +/* initialisation code */ + +static int __init s3c2410_init_dma(void) +{ + s3c2410_dma_chan_t *cp; + int channel; + int ret; + + printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n"); + + dma_base = ioremap(S3C2410_PA_DMA, 0x200); + if (dma_base == NULL) { + printk(KERN_ERR "dma failed to remap register block\n"); + return -ENOMEM; + } + + 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(s3c2410_dma_buf_t), 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(s3c2410_dma_chan_t)); + + /* 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; +} + +__initcall(s3c2410_init_dma); diff --git a/arch/arm/mach-s3c2410/gpio.c b/arch/arm/mach-s3c2410/gpio.c new file mode 100644 index 000000000000..94f1776cf312 --- /dev/null +++ b/arch/arm/mach-s3c2410/gpio.c @@ -0,0 +1,213 @@ +/* linux/arch/arm/mach-s3c2410/gpio.c + * + * Copyright (c) 2004-2005 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 + * + * Changelog + * 13-Sep-2004 BJD Implemented change of MISCCR + * 14-Sep-2004 BJD Added getpin call + * 14-Sep-2004 BJD Fixed bug in setpin() call + * 30-Sep-2004 BJD Fixed cfgpin() mask bug + * 01-Oct-2004 BJD Added getcfg() to get pin configuration + * 01-Oct-2004 BJD Fixed mask bug in pullup() call + * 01-Oct-2004 BJD Added getirq() to turn pin into irqno + * 04-Oct-2004 BJD Added irq filter controls for GPIO + * 05-Nov-2004 BJD EXPORT_SYMBOL() added for all code + * 13-Mar-2005 BJD Updates for __iomem + */ + + +#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 = S3C2410_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; + } + + 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 = S3C2410_GPIO_BASE(pin); + unsigned long mask; + + if (pin < S3C2410_GPIO_BANKB) { + mask = 1 << S3C2410_GPIO_OFFSET(pin); + } else { + mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; + } + + return __raw_readl(base) & mask; +} + +EXPORT_SYMBOL(s3c2410_gpio_getcfg); + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + void __iomem *base = S3C2410_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 = S3C2410_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 = S3C2410_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(S3C2410_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C2410_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_EINT23) + 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); + +int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, + unsigned int config) +{ + void __iomem *reg = S3C2410_EINFLT0; + unsigned long flags; + unsigned long val; + + if (pin < S3C2410_GPG8 || pin > S3C2410_GPG15) + return -1; + + config &= 0xff; + + pin -= S3C2410_GPG8_EINT16; + 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(S3C2410_EXTINT2); + val &= ~(1 << ((pin * 4) + 3)); + val |= on << ((pin * 4) + 3); + __raw_writel(val, S3C2410_EXTINT2); + + local_irq_restore(flags); + + return 0; +} + +EXPORT_SYMBOL(s3c2410_gpio_irqfilter); diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c new file mode 100644 index 000000000000..b668c48f4399 --- /dev/null +++ b/arch/arm/mach-s3c2410/irq.c @@ -0,0 +1,966 @@ +/* linux/arch/arm/mach-s3c2410/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-2004 Ben Dooks + * Fixed edge-triggering on ADC IRQ +*/ + +#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 "cpu.h" +#include "pm.h" + +#define irqdbf(x...) +#define irqdbf2(x...) + +#define EXTINT_OFF (IRQ_EINT4 - 4) + +/* 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; + +static 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); +} + +static struct irqchip s3c_irq_level_chip = { + .ack = s3c_irq_maskack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .wake = s3c_irq_wake +}; + +static struct irqchip s3c_irq_chip = { + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .wake = s3c_irq_wake +}; + +/* S3C2410_EINTMASK + * S3C2410_EINTPEND + */ + +static void +s3c_irqext_mask(unsigned int irqno) +{ + unsigned long mask; + + irqno -= EXTINT_OFF; + + mask = __raw_readl(S3C2410_EINTMASK); + mask |= ( 1UL << irqno); + __raw_writel(mask, S3C2410_EINTMASK); + + if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) { + /* check to see if all need masking */ + + if ((mask & (0xf << 4)) == (0xf << 4)) { + /* all masked, mask the parent */ + s3c_irq_mask(IRQ_EINT4t7); + } + } else { + /* todo: the same check as above for the rest of the irq regs...*/ + + } +} + +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(S3C2410_EINTMASK); + + __raw_writel(bit, S3C2410_EINTPEND); + + req = __raw_readl(S3C2410_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(S3C2410_EINTMASK); + mask &= ~( 1UL << irqno); + __raw_writel(mask, S3C2410_EINTMASK); + + s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23); +} + +static 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 = S3C2410_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 = S3C2410_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 = S3C2410_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 = S3C2410_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 irqchip s3c_irqext_chip = { + .mask = s3c_irqext_mask, + .unmask = s3c_irqext_unmask, + .ack = s3c_irqext_ack, + .type = s3c_irqext_type, + .wake = s3c_irqext_wake +}; + +static struct irqchip s3c_irq_eint0t4 = { + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .wake = s3c_irq_wake, + .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)) +#define INTMSK_LCD (1UL << (IRQ_LCD - IRQ_EINT0)) + +static inline void +s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, + int subcheck) +{ + unsigned long mask; + unsigned long submask; + + submask = __raw_readl(S3C2410_INTSUBMSK); + mask = __raw_readl(S3C2410_INTMSK); + + submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); + + /* check to see if we need to mask the parent IRQ */ + + if ((submask & subcheck) == subcheck) { + __raw_writel(mask | parentbit, S3C2410_INTMSK); + } + + /* write back masks */ + __raw_writel(submask, S3C2410_INTSUBMSK); + +} + +static inline void +s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) +{ + unsigned long mask; + unsigned long submask; + + submask = __raw_readl(S3C2410_INTSUBMSK); + mask = __raw_readl(S3C2410_INTMSK); + + submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0)); + mask &= ~parentbit; + + /* write back masks */ + __raw_writel(submask, S3C2410_INTSUBMSK); + __raw_writel(mask, S3C2410_INTMSK); +} + + +static inline void +s3c_irqsub_maskack(unsigned int irqno, unsigned int parentmask, unsigned int group) +{ + unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); + + s3c_irqsub_mask(irqno, parentmask, group); + + __raw_writel(bit, S3C2410_SUBSRCPND); + + /* only ack parent if we've got all the irqs (seems we must + * ack, all and hope that the irq system retriggers ok when + * the interrupt goes off again) + */ + + if (1) { + __raw_writel(parentmask, S3C2410_SRCPND); + __raw_writel(parentmask, S3C2410_INTPND); + } +} + +static inline void +s3c_irqsub_ack(unsigned int irqno, unsigned int parentmask, unsigned int group) +{ + unsigned int bit = 1UL << (irqno - IRQ_S3CUART_RX0); + + __raw_writel(bit, S3C2410_SUBSRCPND); + + /* only ack parent if we've got all the irqs (seems we must + * ack, all and hope that the irq system retriggers ok when + * the interrupt goes off again) + */ + + if (1) { + __raw_writel(parentmask, S3C2410_SRCPND); + __raw_writel(parentmask, S3C2410_INTPND); + } +} + +/* 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 irqchip s3c_irq_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 irqchip s3c_irq_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 irqchip s3c_irq_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 irqchip s3c_irq_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 irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int subsrc, submsk; + unsigned int offset = 9; + struct irqdesc *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; + mydesc->handle( IRQ_TC, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_ADC; + mydesc->handle(IRQ_ADC, mydesc, regs); + } + } +} + +static void s3c_irq_demux_uart(unsigned int start, + struct pt_regs *regs) +{ + unsigned int subsrc, submsk; + unsigned int offset = start - IRQ_S3CUART_RX0; + struct irqdesc *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(start, desc, regs); + + desc++; + + if (subsrc & 2) + desc->handle(start+1, desc, regs); + + desc++; + + if (subsrc & 4) + desc->handle(start+2, desc, regs); + } +} + +/* uart demux entry points */ + +static void +s3c_irq_demux_uart0(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX0, regs); +} + +static void +s3c_irq_demux_uart1(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX1, regs); +} + +static void +s3c_irq_demux_uart2(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + irq = irq; + s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs); +} + + +/* 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(S3C2410_EINTPEND); + + if (pend == 0 || pend == last) + break; + + __raw_writel(pend, S3C2410_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_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) { + /* set all the s3c2410 internal irqs */ + + switch (irqno) { + /* deal with the special IRQs (cascaded) */ + + case IRQ_UART0: + case IRQ_UART1: + case IRQ_UART2: + case IRQ_LCD: + case IRQ_ADCPARENT: + set_irq_chip(irqno, &s3c_irq_level_chip); + set_irq_handler(irqno, do_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, do_edge_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + } + + /* setup the cascade irq handlers */ + + 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, do_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, do_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, do_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, do_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, do_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, do_edge_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + + irqdbf("s3c2410: registered interrupt handlers\n"); +} + +/* s3c2440 irq code +*/ + +#ifdef CONFIG_CPU_S3C2440 + +/* WDT/AC97 */ + +static void s3c_irq_demux_wdtac97(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int subsrc, submsk; + struct irqdesc *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 >>= 13; + subsrc &= 3; + + if (subsrc != 0) { + if (subsrc & 1) { + mydesc = irq_desc + IRQ_S3C2440_WDT; + mydesc->handle( IRQ_S3C2440_WDT, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_S3C2440_AC97; + mydesc->handle(IRQ_S3C2440_AC97, mydesc, regs); + } + } +} + + +#define INTMSK_WDT (1UL << (IRQ_WDT - IRQ_EINT0)) + +static void +s3c_irq_wdtac97_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_WDT, 3<<13); +} + +static void +s3c_irq_wdtac97_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_WDT); +} + +static void +s3c_irq_wdtac97_ack(unsigned int irqno) +{ + s3c_irqsub_maskack(irqno, INTMSK_WDT, 3<<13); +} + +static struct irqchip s3c_irq_wdtac97 = { + .mask = s3c_irq_wdtac97_mask, + .unmask = s3c_irq_wdtac97_unmask, + .ack = s3c_irq_wdtac97_ack, +}; + +/* camera irq */ + +static void s3c_irq_demux_cam(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int subsrc, submsk; + struct irqdesc *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 >>= 11; + subsrc &= 3; + + if (subsrc != 0) { + if (subsrc & 1) { + mydesc = irq_desc + IRQ_S3C2440_CAM_C; + mydesc->handle( IRQ_S3C2440_WDT, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_S3C2440_CAM_P; + mydesc->handle(IRQ_S3C2440_AC97, mydesc, regs); + } + } +} + +#define INTMSK_CAM (1UL << (IRQ_CAM - IRQ_EINT0)) + +static void +s3c_irq_cam_mask(unsigned int irqno) +{ + s3c_irqsub_mask(irqno, INTMSK_CAM, 3<<11); +} + +static void +s3c_irq_cam_unmask(unsigned int irqno) +{ + s3c_irqsub_unmask(irqno, INTMSK_CAM); +} + +static void +s3c_irq_cam_ack(unsigned int irqno) +{ + s3c_irqsub_maskack(irqno, INTMSK_CAM, 3<<11); +} + +static struct irqchip s3c_irq_cam = { + .mask = s3c_irq_cam_mask, + .unmask = s3c_irq_cam_unmask, + .ack = s3c_irq_cam_ack, +}; + +static int s3c2440_irq_add(struct sys_device *sysdev) +{ + unsigned int irqno; + + printk("S3C2440: IRQ Support\n"); + + set_irq_chip(IRQ_NFCON, &s3c_irq_level_chip); + set_irq_handler(IRQ_NFCON, do_level_IRQ); + set_irq_flags(IRQ_NFCON, IRQF_VALID); + + /* add new chained handler for wdt, ac7 */ + + set_irq_chip(IRQ_WDT, &s3c_irq_level_chip); + set_irq_handler(IRQ_WDT, do_level_IRQ); + set_irq_chained_handler(IRQ_WDT, s3c_irq_demux_wdtac97); + + for (irqno = IRQ_S3C2440_WDT; irqno <= IRQ_S3C2440_AC97; irqno++) { + set_irq_chip(irqno, &s3c_irq_wdtac97); + set_irq_handler(irqno, do_level_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + + /* add chained handler for camera */ + + set_irq_chip(IRQ_CAM, &s3c_irq_level_chip); + set_irq_handler(IRQ_CAM, do_level_IRQ); + set_irq_chained_handler(IRQ_CAM, s3c_irq_demux_cam); + + for (irqno = IRQ_S3C2440_CAM_C; irqno <= IRQ_S3C2440_CAM_P; irqno++) { + set_irq_chip(irqno, &s3c_irq_cam); + set_irq_handler(irqno, do_level_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + + return 0; +} + +static struct sysdev_driver s3c2440_irq_driver = { + .add = s3c2440_irq_add, +}; + +static int s3c24xx_irq_driver(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_irq_driver); +} + +arch_initcall(s3c24xx_irq_driver); + +#endif /* CONFIG_CPU_S3C2440 */ + diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c new file mode 100644 index 000000000000..3bb97eb6e693 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -0,0 +1,409 @@ +/* linux/arch/arm/mach-s3c2410/mach-bast.c + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 14-Sep-2004 BJD USB power control + * 20-Aug-2004 BJD Added s3c2410_board struct + * 18-Aug-2004 BJD Added platform devices from default set + * 16-May-2003 BJD Created initial version + * 16-Aug-2003 BJD Fixed header files and copyright, added URL + * 05-Sep-2003 BJD Moved to v2.6 kernel + * 06-Jan-2003 BJD Updates for <arch/map.h> + * 18-Jan-2003 BJD Added serial port configuration + * 05-Oct-2004 BJD Power management code + * 04-Nov-2004 BJD Updated serial port clocks + * 04-Jan-2005 BJD New uart init call + * 10-Jan-2005 BJD Removed include of s3c2410.h + * 14-Jan-2005 BJD Add support for muitlple NAND devices + * 03-Mar-2005 BJD Ensured that bast-cpld.h is included + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * 14-Mar-2006 BJD Updated for __iomem changes +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/bast-map.h> +#include <asm/arch/bast-irq.h> +#include <asm/arch/bast-cpld.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +//#include <asm/debug-ll.h> +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-mem.h> +#include <asm/arch/nand.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> + +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "usb-simtec.h" +#include "pm.h" + +#define COPYRIGHT ", (c) 2004-2005 Simtec Electronics" + +/* macros for virtual address mods for the io space entries */ +#define VA_C5(item) ((unsigned long)(item) + BAST_VAM_CS5) +#define VA_C4(item) ((unsigned long)(item) + BAST_VAM_CS4) +#define VA_C3(item) ((unsigned long)(item) + BAST_VAM_CS3) +#define VA_C2(item) ((unsigned long)(item) + BAST_VAM_CS2) + +/* macros to modify the physical addresses for io space */ + +#define PA_CS2(item) ((item) + S3C2410_CS2) +#define PA_CS3(item) ((item) + S3C2410_CS3) +#define PA_CS4(item) ((item) + S3C2410_CS4) +#define PA_CS5(item) ((item) + S3C2410_CS5) + +static struct map_desc bast_iodesc[] __initdata = { + /* ISA IO areas */ + + { (u32)S3C24XX_VA_ISA_BYTE, PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { (u32)S3C24XX_VA_ISA_WORD, PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + + /* we could possibly compress the next set down into a set of smaller tables + * pagetables, but that would mean using an L2 section, and it still means + * we cannot actually feed the same register to an LDR due to 16K spacing + */ + + /* bast CPLD control registers, and external interrupt controls */ + { (u32)BAST_VA_CTRL1, BAST_PA_CTRL1, SZ_1M, MT_DEVICE }, + { (u32)BAST_VA_CTRL2, BAST_PA_CTRL2, SZ_1M, MT_DEVICE }, + { (u32)BAST_VA_CTRL3, BAST_PA_CTRL3, SZ_1M, MT_DEVICE }, + { (u32)BAST_VA_CTRL4, BAST_PA_CTRL4, SZ_1M, MT_DEVICE }, + + /* PC104 IRQ mux */ + { (u32)BAST_VA_PC104_IRQREQ, BAST_PA_PC104_IRQREQ, SZ_1M, MT_DEVICE }, + { (u32)BAST_VA_PC104_IRQRAW, BAST_PA_PC104_IRQRAW, SZ_1M, MT_DEVICE }, + { (u32)BAST_VA_PC104_IRQMASK, BAST_PA_PC104_IRQMASK, SZ_1M, MT_DEVICE }, + + /* peripheral space... one for each of fast/slow/byte/16bit */ + /* note, ide is only decoded in word space, even though some registers + * are only 8bit */ + + /* slow, byte */ + { VA_C2(BAST_VA_ISAIO), PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { VA_C2(BAST_VA_ISAMEM), PA_CS2(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, + { VA_C2(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_SUPERIO), PA_CS2(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_DM9000), PA_CS2(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C2(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* slow, word */ + { VA_C3(BAST_VA_ISAIO), PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { VA_C3(BAST_VA_ISAMEM), PA_CS3(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, + { VA_C3(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_SUPERIO), PA_CS3(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_DM9000), PA_CS3(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C3(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* fast, byte */ + { VA_C4(BAST_VA_ISAIO), PA_CS4(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { VA_C4(BAST_VA_ISAMEM), PA_CS4(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, + { VA_C4(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_SUPERIO), PA_CS4(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_DM9000), PA_CS4(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C4(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* fast, word */ + { VA_C5(BAST_VA_ISAIO), PA_CS5(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { VA_C5(BAST_VA_ISAMEM), PA_CS5(BAST_PA_ISAMEM), SZ_16M, MT_DEVICE }, + { VA_C5(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_SUPERIO), PA_CS5(BAST_PA_SUPERIO), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_DM9000), PA_CS5(BAST_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C5(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, MT_DEVICE }, +}; + +#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +static struct s3c24xx_uart_clksrc bast_serial_clocks[] = { + [0] = { + .name = "uclk", + .divisor = 1, + .min_baud = 0, + .max_baud = 0, + }, + [1] = { + .name = "pclk", + .divisor = 1, + .min_baud = 0, + .max_baud = 0. + } +}; + + +static struct s3c2410_uartcfg bast_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = bast_serial_clocks, + .clocks_size = ARRAY_SIZE(bast_serial_clocks) + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = bast_serial_clocks, + .clocks_size = ARRAY_SIZE(bast_serial_clocks) + }, + /* port 2 is not actually used */ + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = bast_serial_clocks, + .clocks_size = ARRAY_SIZE(bast_serial_clocks) + } +}; + +/* NOR Flash on BAST board */ + +static struct resource bast_nor_resource[] = { + [0] = { + .start = S3C2410_CS1 + 0x4000000, + .end = S3C2410_CS1 + 0x4000000 + (32*1024*1024) - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device bast_device_nor = { + .name = "bast-nor", + .id = -1, + .num_resources = ARRAY_SIZE(bast_nor_resource), + .resource = bast_nor_resource, +}; + +/* NAND Flash on BAST board */ + + +static int smartmedia_map[] = { 0 }; +static int chip0_map[] = { 1 }; +static int chip1_map[] = { 2 }; +static int chip2_map[] = { 3 }; + +struct mtd_partition bast_default_nand_part[] = { + [0] = { + .name = "Boot Agent", + .size = SZ_16K, + .offset = 0 + }, + [1] = { + .name = "/boot", + .size = SZ_4M - SZ_16K, + .offset = SZ_16K, + }, + [2] = { + .name = "user", + .offset = SZ_4M, + .size = MTDPART_SIZ_FULL, + } +}; + +/* the bast has 4 selectable slots for nand-flash, the three + * on-board chip areas, as well as the external SmartMedia + * slot. + * + * Note, there is no current hot-plug support for the SmartMedia + * socket. +*/ + +static struct s3c2410_nand_set bast_nand_sets[] = { + [0] = { + .name = "SmartMedia", + .nr_chips = 1, + .nr_map = smartmedia_map, + .nr_partitions = ARRAY_SIZE(bast_default_nand_part), + .partitions = bast_default_nand_part + }, + [1] = { + .name = "chip0", + .nr_chips = 1, + .nr_map = chip0_map, + .nr_partitions = ARRAY_SIZE(bast_default_nand_part), + .partitions = bast_default_nand_part + }, + [2] = { + .name = "chip1", + .nr_chips = 1, + .nr_map = chip1_map, + .nr_partitions = ARRAY_SIZE(bast_default_nand_part), + .partitions = bast_default_nand_part + }, + [3] = { + .name = "chip2", + .nr_chips = 1, + .nr_map = chip2_map, + .nr_partitions = ARRAY_SIZE(bast_default_nand_part), + .partitions = bast_default_nand_part + } +}; + +static void bast_nand_select(struct s3c2410_nand_set *set, int slot) +{ + unsigned int tmp; + + slot = set->nr_map[slot] & 3; + + pr_debug("bast_nand: selecting slot %d (set %p,%p)\n", + slot, set, set->nr_map); + + tmp = __raw_readb(BAST_VA_CTRL2); + tmp &= BAST_CPLD_CTLR2_IDERST; + tmp |= slot; + tmp |= BAST_CPLD_CTRL2_WNAND; + + pr_debug("bast_nand: ctrl2 now %02x\n", tmp); + + __raw_writeb(tmp, BAST_VA_CTRL2); +} + +static struct s3c2410_platform_nand bast_nand_info = { + .tacls = 80, + .twrph0 = 80, + .twrph1 = 80, + .nr_sets = ARRAY_SIZE(bast_nand_sets), + .sets = bast_nand_sets, + .select_chip = bast_nand_select, +}; + + +/* Standard BAST devices */ + +static struct platform_device *bast_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, + &s3c_device_rtc, + &s3c_device_nand, + &bast_device_nor +}; + +static struct clk *bast_clocks[] = { + &s3c24xx_dclk0, + &s3c24xx_dclk1, + &s3c24xx_clkout0, + &s3c24xx_clkout1, + &s3c24xx_uclk, +}; + +static struct s3c24xx_board bast_board __initdata = { + .devices = bast_devices, + .devices_count = ARRAY_SIZE(bast_devices), + .clocks = bast_clocks, + .clocks_count = ARRAY_SIZE(bast_clocks) +}; + +void __init bast_map_io(void) +{ + /* initialise the clocks */ + + s3c24xx_dclk0.parent = NULL; + s3c24xx_dclk0.rate = 12*1000*1000; + + s3c24xx_dclk1.parent = NULL; + s3c24xx_dclk1.rate = 24*1000*1000; + + s3c24xx_clkout0.parent = &s3c24xx_dclk0; + s3c24xx_clkout1.parent = &s3c24xx_dclk1; + + s3c24xx_uclk.parent = &s3c24xx_clkout1; + + s3c_device_nand.dev.platform_data = &bast_nand_info; + + s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(bast_uartcfgs, ARRAY_SIZE(bast_uartcfgs)); + s3c24xx_set_board(&bast_board); + usb_simtec_init(); +} + +void __init bast_init_irq(void) +{ + s3c24xx_init_irq(); +} + +#ifdef CONFIG_PM + +/* bast_init_machine + * + * enable the power management functions for the EB2410ITX +*/ + +static __init void bast_init_machine(void) +{ + unsigned long gstatus4; + + printk(KERN_INFO "BAST Power Manangement" COPYRIGHT "\n"); + + gstatus4 = (__raw_readl(S3C2410_BANKCON7) & 0x3) << 30; + gstatus4 |= (__raw_readl(S3C2410_BANKCON6) & 0x3) << 28; + gstatus4 |= (__raw_readl(S3C2410_BANKSIZE) & S3C2410_BANKSIZE_MASK); + + __raw_writel(gstatus4, S3C2410_GSTATUS4); + + s3c2410_pm_init(); +} + +#else +#define bast_init_machine NULL +#endif + + +MACHINE_START(BAST, "Simtec-BAST") + MAINTAINER("Ben Dooks <ben@simtec.co.uk>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(bast_map_io) + INITIRQ(bast_init_irq) + .init_machine = bast_init_machine, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c new file mode 100644 index 000000000000..2924afc068a4 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -0,0 +1,126 @@ +/* linux/arch/arm/mach-s3c2410/mach-h1940.c + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.handhelds.org/projects/h1940.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 16-May-2003 BJD Created initial version + * 16-Aug-2003 BJD Fixed header files and copyright, added URL + * 05-Sep-2003 BJD Moved to v2.6 kernel + * 06-Jan-2003 BJD Updates for <arch/map.h> + * 18-Jan-2003 BJD Added serial port configuration + * 17-Feb-2003 BJD Copied to mach-ipaq.c + * 21-Aug-2004 BJD Added struct s3c2410_board + * 04-Sep-2004 BJD Changed uart init, renamed ipaq_ -> h1940_ + * 18-Oct-2004 BJD Updated new board structure name + * 04-Nov-2004 BJD Change for new serial clock + * 04-Jan-2005 BJD Updated uart init call + * 10-Jan-2005 BJD Removed include of s3c2410.h + * 14-Jan-2005 BJD Added clock init + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/hardware/iomd.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +//#include <asm/debug-ll.h> +#include <asm/arch/regs-serial.h> + +#include <linux/serial_core.h> + +#include "clock.h" +#include "devs.h" +#include "cpu.h" + +static struct map_desc h1940_iodesc[] __initdata = { + /* nothing here yet */ +}; + +#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg h1940_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x03, + .ufcon = 0x51, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = 0x245, + .ulcon = 0x03, + .ufcon = 0x00, + }, + /* IR port */ + [2] = { + .hwport = 2, + .flags = 0, + .uart_flags = UPF_CONS_FLOW, + .ucon = 0x3c5, + .ulcon = 0x43, + .ufcon = 0x51, + } +}; + + + + +static struct platform_device *h1940_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c24xx_board h1940_board __initdata = { + .devices = h1940_devices, + .devices_count = ARRAY_SIZE(h1940_devices) +}; + +void __init h1940_map_io(void) +{ + s3c24xx_init_io(h1940_iodesc, ARRAY_SIZE(h1940_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(h1940_uartcfgs, ARRAY_SIZE(h1940_uartcfgs)); + s3c24xx_set_board(&h1940_board); +} + +void __init h1940_init_irq(void) +{ + s3c24xx_init_irq(); + +} + +MACHINE_START(H1940, "IPAQ-H1940") + MAINTAINER("Ben Dooks <ben@fluff.org>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(h1940_map_io) + INITIRQ(h1940_init_irq) + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c new file mode 100644 index 000000000000..bd15998c129b --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-n30.c @@ -0,0 +1,155 @@ +/* linux/arch/arm/mach-s3c2410/mach-n30.c + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Copyright (c) 2005 Christer Weinigel <christer@weinigel.se> + * + * There is a wiki with more information about the n30 port at + * http://handhelds.org/moin/moin.cgi/AcerN30Documentation . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kthread.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/hardware/iomd.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/iic.h> + +#include <linux/serial_core.h> + +#include "s3c2410.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" + +static struct map_desc n30_iodesc[] __initdata = { + /* nothing here yet */ +}; + +static struct s3c2410_uartcfg n30_uartcfgs[] = { + /* Normal serial port */ + [0] = { + .hwport = 0, + .flags = 0, + .ucon = 0x2c5, + .ulcon = 0x03, + .ufcon = 0x51, + }, + /* IR port */ + [1] = { + .hwport = 1, + .flags = 0, + .uart_flags = UPF_CONS_FLOW, + .ucon = 0x2c5, + .ulcon = 0x43, + .ufcon = 0x51, + }, + /* The BlueTooth controller is connected to port 2 */ + [2] = { + .hwport = 2, + .flags = 0, + .ucon = 0x2c5, + .ulcon = 0x03, + .ufcon = 0x51, + }, +}; + +static struct platform_device *n30_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, + &s3c_device_usbgadget, +}; + +static struct s3c2410_platform_i2c n30_i2ccfg = { + .flags = 0, + .slave_addr = 0x10, + .bus_freq = 10*1000, + .max_freq = 10*1000, +}; + +static struct s3c24xx_board n30_board __initdata = { + .devices = n30_devices, + .devices_count = ARRAY_SIZE(n30_devices) +}; + +void __init n30_map_io(void) +{ + s3c24xx_init_io(n30_iodesc, ARRAY_SIZE(n30_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(n30_uartcfgs, ARRAY_SIZE(n30_uartcfgs)); + s3c24xx_set_board(&n30_board); +} + +void __init n30_init_irq(void) +{ + s3c24xx_init_irq(); +} + + +static int n30_usbstart_thread(void *unused) +{ + /* Turn off suspend on both USB ports, and switch the + * selectable USB port to USB device mode. */ + writel(readl(S3C2410_MISCCR) & ~0x00003008, S3C2410_MISCCR); + + /* Turn off the D+ pull up for 3 seconds so that the USB host + * at the other end will do a rescan of the USB bus. */ + s3c2410_gpio_setpin(S3C2410_GPB3, 0); + + msleep_interruptible(3*HZ); + + s3c2410_gpio_setpin(S3C2410_GPB3, 1); + + return 0; +} + + +void __init n30_init(void) +{ + s3c_device_i2c.dev.platform_data = &n30_i2ccfg; + + kthread_run(n30_usbstart_thread, NULL, "n30_usbstart"); +} + +MACHINE_START(N30, "Acer-N30") + MAINTAINER("Christer Weinigel <christer@weinigel.se>, Ben Dooks <ben-linux@fluff.org>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + + .timer = &s3c24xx_timer, + .init_machine = n30_init, + .init_irq = n30_init_irq, + .map_io = n30_map_io, +MACHINE_END + +/* + Local variables: + compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.." + c-basic-offset: 8 + End: +*/ diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2410/mach-nexcoder.c new file mode 100644 index 000000000000..70487bf4b71e --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-nexcoder.c @@ -0,0 +1,156 @@ +/* linux/arch/arm/mach-s3c2410/mach-nexcoder.c + * + * Copyright (c) 2004 Nex Vision + * Guillaume GOURAT <guillaume.gourat@nexvision.tv> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 15-10-2004 GG Created initial version + * 12-03-2005 BJD Updated for release + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/device.h> + +#include <linux/mtd/map.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/setup.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +//#include <asm/debug-ll.h> +#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" + +static struct map_desc nexcoder_iodesc[] __initdata = { + /* nothing here yet */ +}; + +#define UCON S3C2410_UCON_DEFAULT +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg nexcoder_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + } +}; + +/* NOR Flash on NexVision NexCoder 2440 board */ + +static struct resource nexcoder_nor_resource[] = { + [0] = { + .start = S3C2410_CS0, + .end = S3C2410_CS0 + (8*1024*1024) - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct map_info nexcoder_nor_map = { + .bankwidth = 2, +}; + +static struct platform_device nexcoder_device_nor = { + .name = "mtd-flash", + .id = -1, + .num_resources = ARRAY_SIZE(nexcoder_nor_resource), + .resource = nexcoder_nor_resource, + .dev = + { + .platform_data = &nexcoder_nor_map, + } +}; + +/* Standard Nexcoder devices */ + +static struct platform_device *nexcoder_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, + &s3c_device_rtc, + &s3c_device_camif, + &s3c_device_spi0, + &s3c_device_spi1, + &nexcoder_device_nor, +}; + +static struct s3c24xx_board nexcoder_board __initdata = { + .devices = nexcoder_devices, + .devices_count = ARRAY_SIZE(nexcoder_devices), +}; + + +static void __init nexcoder_sensorboard_init(void) +{ + // Initialize SCCB bus + s3c2410_gpio_setpin(S3C2410_GPE14, 1); // IICSCL + s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_OUTP); + s3c2410_gpio_setpin(S3C2410_GPE15, 1); // IICSDA + s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_OUTP); + + // Power up the sensor board + s3c2410_gpio_setpin(S3C2410_GPF1, 1); + s3c2410_gpio_cfgpin(S3C2410_GPF1, S3C2410_GPF1_OUTP); // CAM_GPIO7 => nLDO_PWRDN + s3c2410_gpio_setpin(S3C2410_GPF2, 0); + s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_OUTP); // CAM_GPIO6 => CAM_PWRDN +} + +void __init nexcoder_map_io(void) +{ + s3c24xx_init_io(nexcoder_iodesc, ARRAY_SIZE(nexcoder_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(nexcoder_uartcfgs, ARRAY_SIZE(nexcoder_uartcfgs)); + s3c24xx_set_board(&nexcoder_board); + nexcoder_sensorboard_init(); +} + + +MACHINE_START(NEXCODER_2440, "NexVision - Nexcoder 2440") + MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + .map_io = nexcoder_map_io, + .init_irq = s3c24xx_init_irq, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c new file mode 100644 index 000000000000..67d8ce8fb00f --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-otom.c @@ -0,0 +1,124 @@ +/* linux/arch/arm/mach-s3c2410/mach-otom.c + * + * Copyright (c) 2004 Nex Vision + * Guillaume GOURAT <guillaume.gourat@nexvision.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/otom-map.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> + +#include "s3c2410.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" + +static struct map_desc otom11_iodesc[] __initdata = { + /* Device area */ + { (u32)OTOM_VA_CS8900A_BASE, OTOM_PA_CS8900A_BASE, SZ_16M, MT_DEVICE }, +}; + +#define UCON S3C2410_UCON_DEFAULT +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg otom11_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + /* port 2 is not actually used */ + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + } +}; + +/* NOR Flash on NexVision OTOM board */ + +static struct resource otom_nor_resource[] = { + [0] = { + .start = S3C2410_CS0, + .end = S3C2410_CS0 + (4*1024*1024) - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device otom_device_nor = { + .name = "mtd-flash", + .id = -1, + .num_resources = ARRAY_SIZE(otom_nor_resource), + .resource = otom_nor_resource, +}; + +/* Standard OTOM devices */ + +static struct platform_device *otom11_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, + &s3c_device_rtc, + &otom_device_nor, +}; + +static struct s3c24xx_board otom11_board __initdata = { + .devices = otom11_devices, + .devices_count = ARRAY_SIZE(otom11_devices) +}; + + +void __init otom11_map_io(void) +{ + s3c24xx_init_io(otom11_iodesc, ARRAY_SIZE(otom11_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(otom11_uartcfgs, ARRAY_SIZE(otom11_uartcfgs)); + s3c24xx_set_board(&otom11_board); +} + + +MACHINE_START(OTOM, "Nex Vision - Otom 1.1") + MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + .map_io = otom11_map_io, + .init_irq = s3c24xx_init_irq, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2410/mach-rx3715.c new file mode 100644 index 000000000000..f8d3a9784e71 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-rx3715.c @@ -0,0 +1,141 @@ +/* linux/arch/arm/mach-s3c2410/mach-rx3715.c + * + * Copyright (c) 2003,2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.handhelds.org/projects/rx3715.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 16-Sep-2004 BJD Copied from mach-h1940.c + * 25-Oct-2004 BJD Updates for 2.6.10-rc1 + * 10-Jan-2005 BJD Removed include of s3c2410.h s3c2440.h + * 14-Jan-2005 BJD Added new clock init + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * 14-Mar-2005 BJD Fixed __iomem warnings +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/serial.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/hardware/iomd.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> + +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "pm.h" + +static struct map_desc rx3715_iodesc[] __initdata = { + /* dump ISA space somewhere unused */ + + { (u32)S3C24XX_VA_ISA_WORD, S3C2410_CS3, SZ_16M, MT_DEVICE }, + { (u32)S3C24XX_VA_ISA_BYTE, S3C2410_CS3, SZ_16M, MT_DEVICE }, +}; + + +static struct s3c24xx_uart_clksrc rx3715_serial_clocks[] = { + [0] = { + .name = "fclk", + .divisor = 0, + .min_baud = 0, + .max_baud = 0, + } +}; + +static struct s3c2410_uartcfg rx3715_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x03, + .ufcon = 0x51, + .clocks = rx3715_serial_clocks, + .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x03, + .ufcon = 0x00, + .clocks = rx3715_serial_clocks, + .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), + }, + /* IR port */ + [2] = { + .hwport = 2, + .uart_flags = UPF_CONS_FLOW, + .ucon = 0x3c5, + .ulcon = 0x43, + .ufcon = 0x51, + .clocks = rx3715_serial_clocks, + .clocks_size = ARRAY_SIZE(rx3715_serial_clocks), + } +}; + +static struct platform_device *rx3715_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c24xx_board rx3715_board __initdata = { + .devices = rx3715_devices, + .devices_count = ARRAY_SIZE(rx3715_devices) +}; + +void __init rx3715_map_io(void) +{ + s3c24xx_init_io(rx3715_iodesc, ARRAY_SIZE(rx3715_iodesc)); + s3c24xx_init_clocks(16934000); + s3c24xx_init_uarts(rx3715_uartcfgs, ARRAY_SIZE(rx3715_uartcfgs)); + s3c24xx_set_board(&rx3715_board); +} + +void __init rx3715_init_irq(void) +{ + s3c24xx_init_irq(); +} + +#ifdef CONFIG_PM +static void __init rx3715_init_machine(void) +{ + s3c2410_pm_init(); +} +#else +#define rx3715_init_machine NULL +#endif + +MACHINE_START(RX3715, "IPAQ-RX3715") + MAINTAINER("Ben Dooks <ben@fluff.org>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(rx3715_map_io) + INITIRQ(rx3715_init_irq) + INIT_MACHINE(rx3715_init_machine) + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c new file mode 100644 index 000000000000..c1a4a1420ea0 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -0,0 +1,123 @@ +/*********************************************************************** + * + * linux/arch/arm/mach-s3c2410/mach-smdk2410.c + * + * Copyright (C) 2004 by FS Forth-Systeme GmbH + * All rights reserved. + * + * $Id: mach-smdk2410.c,v 1.1 2004/05/11 14:15:38 mpietrek Exp $ + * @Author: Jonas Dietsche + * + * 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 + * + * @History: + * derived from linux/arch/arm/mach-s3c2410/mach-bast.c, written by + * Ben Dooks <ben@simtec.co.uk> + * + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * + ***********************************************************************/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <asm/arch/regs-serial.h> + +#include "devs.h" +#include "cpu.h" + +static struct map_desc smdk2410_iodesc[] __initdata = { + /* nothing here yet */ +}; + +#define UCON S3C2410_UCON_DEFAULT +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg smdk2410_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + } +}; + +static struct platform_device *smdk2410_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c24xx_board smdk2410_board __initdata = { + .devices = smdk2410_devices, + .devices_count = ARRAY_SIZE(smdk2410_devices) +}; + +void __init smdk2410_map_io(void) +{ + s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs)); + s3c24xx_set_board(&smdk2410_board); +} + +void __init smdk2410_init_irq(void) +{ + s3c24xx_init_irq(); +} + +MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch + * to SMDK2410 */ + MAINTAINER("Jonas Dietsche") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(smdk2410_map_io) + INITIRQ(smdk2410_init_irq) + .timer = &s3c24xx_timer, +MACHINE_END + + diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c new file mode 100644 index 000000000000..7857176d9bcb --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2440.c @@ -0,0 +1,135 @@ +/* linux/arch/arm/mach-s3c2410/mach-smdk2440.c + * + * Copyright (c) 2004,2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.fluff.org/ben/smdk2440/ + * + * Thanks to Dimity Andric and TomTom for the loan of an SMDK2440. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 01-Nov-2004 BJD Initial version + * 12-Nov-2004 BJD Updated for release + * 04-Jan-2005 BJD Fixes for pre-release + * 22-Feb-2005 BJD Updated for 2.6.11-rc5 relesa + * 10-Mar-2005 LCVR Replaced S3C2410_VA by S3C24XX_VA + * 14-Mar-2005 BJD void __iomem fixes +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/hardware/iomd.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +//#include <asm/debug-ll.h> +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/idle.h> + +#include "s3c2410.h" +#include "s3c2440.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "pm.h" + +static struct map_desc smdk2440_iodesc[] __initdata = { + /* ISA IO Space map (memory space selected by A24) */ + + { (u32)S3C24XX_VA_ISA_WORD, S3C2410_CS2, SZ_16M, MT_DEVICE }, + { (u32)S3C24XX_VA_ISA_BYTE, S3C2410_CS2, SZ_16M, MT_DEVICE }, +}; + +#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +static struct s3c2410_uartcfg smdk2440_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x03, + .ufcon = 0x51, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x03, + .ufcon = 0x51, + }, + /* IR port */ + [2] = { + .hwport = 2, + .flags = 0, + .ucon = 0x3c5, + .ulcon = 0x43, + .ufcon = 0x51, + } +}; + +static struct platform_device *smdk2440_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c24xx_board smdk2440_board __initdata = { + .devices = smdk2440_devices, + .devices_count = ARRAY_SIZE(smdk2440_devices) +}; + +void __init smdk2440_map_io(void) +{ + s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); + s3c24xx_init_clocks(16934400); + s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); + s3c24xx_set_board(&smdk2440_board); +} + +void __init smdk2440_machine_init(void) +{ + /* Configure the LEDs (even if we have no LED support)*/ + + s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP); + + s3c2410_gpio_setpin(S3C2410_GPF4, 0); + s3c2410_gpio_setpin(S3C2410_GPF5, 0); + s3c2410_gpio_setpin(S3C2410_GPF6, 0); + s3c2410_gpio_setpin(S3C2410_GPF7, 0); + + s3c2410_pm_init(); +} + +MACHINE_START(S3C2440, "SMDK2440") + MAINTAINER("Ben Dooks <ben@fluff.org>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + + .init_irq = s3c24xx_init_irq, + .map_io = smdk2440_map_io, + .init_machine = smdk2440_machine_init, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c new file mode 100644 index 000000000000..5512146b1ce4 --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -0,0 +1,317 @@ +/* linux/arch/arm/mach-s3c2410/mach-vr1000.c + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Machine support for Thorcom VR1000 board. Designed for Thorcom by + * Simtec Electronics, http://www.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. + * + * Modifications: + * 14-Sep-2004 BJD USB Power control + * 04-Sep-2004 BJD Added new uart init, and io init + * 21-Aug-2004 BJD Added struct s3c2410_board + * 06-Aug-2004 BJD Fixed call to time initialisation + * 05-Apr-2004 BJD Copied to make mach-vr1000.c + * 18-Oct-2004 BJD Updated board struct + * 04-Nov-2004 BJD Clock and serial configuration update + * + * 04-Jan-2005 BJD Updated uart init call + * 10-Jan-2005 BJD Removed include of s3c2410.h + * 14-Jan-2005 BJD Added clock init + * 15-Jan-2005 BJD Add serial port device definition + * 20-Jan-2005 BJD Use UPF_IOREMAP for ports + * 10-Feb-2005 BJD Added power-off capability + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * 14-Mar-2006 BJD void __iomem fixes +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> + +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/bast-map.h> +#include <asm/arch/vr1000-map.h> +#include <asm/arch/vr1000-irq.h> +#include <asm/arch/vr1000-cpld.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> + +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "usb-simtec.h" + +/* macros for virtual address mods for the io space entries */ +#define VA_C5(item) ((unsigned long)(item) + BAST_VAM_CS5) +#define VA_C4(item) ((unsigned long)(item) + BAST_VAM_CS4) +#define VA_C3(item) ((unsigned long)(item) + BAST_VAM_CS3) +#define VA_C2(item) ((unsigned long)(item) + BAST_VAM_CS2) + +/* macros to modify the physical addresses for io space */ + +#define PA_CS2(item) ((item) + S3C2410_CS2) +#define PA_CS3(item) ((item) + S3C2410_CS3) +#define PA_CS4(item) ((item) + S3C2410_CS4) +#define PA_CS5(item) ((item) + S3C2410_CS5) + +static struct map_desc vr1000_iodesc[] __initdata = { + /* ISA IO areas */ + + { (u32)S3C24XX_VA_ISA_BYTE, PA_CS2(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + { (u32)S3C24XX_VA_ISA_WORD, PA_CS3(BAST_PA_ISAIO), SZ_16M, MT_DEVICE }, + + /* we could possibly compress the next set down into a set of smaller tables + * pagetables, but that would mean using an L2 section, and it still means + * we cannot actually feed the same register to an LDR due to 16K spacing + */ + + /* bast CPLD control registers, and external interrupt controls */ + { (u32)VR1000_VA_CTRL1, VR1000_PA_CTRL1, SZ_1M, MT_DEVICE }, + { (u32)VR1000_VA_CTRL2, VR1000_PA_CTRL2, SZ_1M, MT_DEVICE }, + { (u32)VR1000_VA_CTRL3, VR1000_PA_CTRL3, SZ_1M, MT_DEVICE }, + { (u32)VR1000_VA_CTRL4, VR1000_PA_CTRL4, SZ_1M, MT_DEVICE }, + + /* peripheral space... one for each of fast/slow/byte/16bit */ + /* note, ide is only decoded in word space, even though some registers + * are only 8bit */ + + /* slow, byte */ + { VA_C2(VR1000_VA_DM9000), PA_CS2(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C2(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C2(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C2(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C2(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* slow, word */ + { VA_C3(VR1000_VA_DM9000), PA_CS3(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C3(VR1000_VA_IDEPRI), PA_CS3(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C3(VR1000_VA_IDESEC), PA_CS3(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C3(VR1000_VA_IDEPRIAUX), PA_CS3(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C3(VR1000_VA_IDESECAUX), PA_CS3(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* fast, byte */ + { VA_C4(VR1000_VA_DM9000), PA_CS4(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C4(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C4(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C4(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C4(VR1000_VA_IDESECAUX), PA_CS5(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, + + /* fast, word */ + { VA_C5(VR1000_VA_DM9000), PA_CS5(VR1000_PA_DM9000), SZ_1M, MT_DEVICE }, + { VA_C5(VR1000_VA_IDEPRI), PA_CS5(VR1000_PA_IDEPRI), SZ_1M, MT_DEVICE }, + { VA_C5(VR1000_VA_IDESEC), PA_CS5(VR1000_PA_IDESEC), SZ_1M, MT_DEVICE }, + { VA_C5(VR1000_VA_IDEPRIAUX), PA_CS5(VR1000_PA_IDEPRIAUX), SZ_1M, MT_DEVICE }, + { VA_C5(VR1000_VA_IDESECAUX), PA_CS5(VR1000_PA_IDESECAUX), SZ_1M, MT_DEVICE }, +}; + +#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK +#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB +#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE + +/* uart clock source(s) */ + +static struct s3c24xx_uart_clksrc vr1000_serial_clocks[] = { + [0] = { + .name = "uclk", + .divisor = 1, + .min_baud = 0, + .max_baud = 0, + }, + [1] = { + .name = "pclk", + .divisor = 1, + .min_baud = 0, + .max_baud = 0. + } +}; + +static struct s3c2410_uartcfg vr1000_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = vr1000_serial_clocks, + .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = vr1000_serial_clocks, + .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), + }, + /* port 2 is not actually used */ + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + .clocks = vr1000_serial_clocks, + .clocks_size = ARRAY_SIZE(vr1000_serial_clocks), + + } +}; + +/* definitions for the vr1000 extra 16550 serial ports */ + +#define VR1000_BAUDBASE (3692307) + +#define VR1000_SERIAL_MAPBASE(x) (VR1000_PA_SERIAL + 0x80 + ((x) << 5)) + +static struct plat_serial8250_port serial_platform_data[] = { + [0] = { + .mapbase = VR1000_SERIAL_MAPBASE(0), + .irq = IRQ_VR1000_SERIAL + 0, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 0, + .uartclk = VR1000_BAUDBASE, + }, + [1] = { + .mapbase = VR1000_SERIAL_MAPBASE(1), + .irq = IRQ_VR1000_SERIAL + 1, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 0, + .uartclk = VR1000_BAUDBASE, + }, + [2] = { + .mapbase = VR1000_SERIAL_MAPBASE(2), + .irq = IRQ_VR1000_SERIAL + 2, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 0, + .uartclk = VR1000_BAUDBASE, + }, + [3] = { + .mapbase = VR1000_SERIAL_MAPBASE(3), + .irq = IRQ_VR1000_SERIAL + 3, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 0, + .uartclk = VR1000_BAUDBASE, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +/* MTD NOR Flash */ + +static struct resource vr1000_nor_resource[] = { + [0] = { + .start = S3C2410_CS1 + 0x4000000, + .end = S3C2410_CS1 + 0x4000000 + SZ_16M - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device vr1000_nor = { + .name = "bast-nor", + .id = -1, + .num_resources = ARRAY_SIZE(vr1000_nor_resource), + .resource = vr1000_nor_resource, +}; + + +static struct platform_device *vr1000_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, + &serial_device, + &vr1000_nor, +}; + +static struct clk *vr1000_clocks[] = { + &s3c24xx_dclk0, + &s3c24xx_dclk1, + &s3c24xx_clkout0, + &s3c24xx_clkout1, + &s3c24xx_uclk, +}; + +static struct s3c24xx_board vr1000_board __initdata = { + .devices = vr1000_devices, + .devices_count = ARRAY_SIZE(vr1000_devices), + .clocks = vr1000_clocks, + .clocks_count = ARRAY_SIZE(vr1000_clocks), +}; + +static void vr1000_power_off(void) +{ + s3c2410_gpio_cfgpin(S3C2410_GPB9, S3C2410_GPB9_OUTP); + s3c2410_gpio_setpin(S3C2410_GPB9, 1); +} + +void __init vr1000_map_io(void) +{ + /* initialise clock sources */ + + s3c24xx_dclk0.parent = NULL; + s3c24xx_dclk0.rate = 12*1000*1000; + + s3c24xx_dclk1.parent = NULL; + s3c24xx_dclk1.rate = 3692307; + + s3c24xx_clkout0.parent = &s3c24xx_dclk0; + s3c24xx_clkout1.parent = &s3c24xx_dclk1; + + s3c24xx_uclk.parent = &s3c24xx_clkout1; + + pm_power_off = vr1000_power_off; + + s3c24xx_init_io(vr1000_iodesc, ARRAY_SIZE(vr1000_iodesc)); + s3c24xx_init_clocks(0); + s3c24xx_init_uarts(vr1000_uartcfgs, ARRAY_SIZE(vr1000_uartcfgs)); + s3c24xx_set_board(&vr1000_board); + usb_simtec_init(); +} + +void __init vr1000_init_irq(void) +{ + s3c24xx_init_irq(); +} + +MACHINE_START(VR1000, "Thorcom-VR1000") + MAINTAINER("Ben Dooks <ben@simtec.co.uk>") + BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART) + BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100) + MAPIO(vr1000_map_io) + INITIRQ(vr1000_init_irq) + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c new file mode 100644 index 000000000000..13a48ee77484 --- /dev/null +++ b/arch/arm/mach-s3c2410/pm.c @@ -0,0 +1,672 @@ +/* linux/arch/arm/mach-s3c2410/pm.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 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 + * + * Modifications: + * 10-Mar-2005 LCVR Changed S3C2410_VA_UART to S3C24XX_VA_UART +*/ + +#include <linux/config.h> +#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 <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; + +/* cache functions from arch/arm/mm/proc-arm920.S */ + +extern void arm920_flush_kern_cache_all(void); + +#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), +}; + +/* this lot should be really saved by the IRQ code */ +static struct sleep_save irq_save[] = { + SAVE_ITEM(S3C2410_EXTINT0), + SAVE_ITEM(S3C2410_EXTINT1), + SAVE_ITEM(S3C2410_EXTINT2), + SAVE_ITEM(S3C2410_EINFLT0), + SAVE_ITEM(S3C2410_EINFLT1), + SAVE_ITEM(S3C2410_EINFLT2), + SAVE_ITEM(S3C2410_EINFLT3), + SAVE_ITEM(S3C2410_EINTMASK), + SAVE_ITEM(S3C2410_INTMSK) +}; + +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 *); + +static 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); + pinstate >>= S3C2410_GPIO_OFFSET(pin)*2; + + if (!irqstate) { + if (pinstate == 0x02) + DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin); + } else { + if (pinstate == 0x02) { + DBG("Disabling IRQ %d (pin %d)\n", irq, pin); + s3c2410_gpio_cfgpin(pin, 0x00); + } + } +} + +/* 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); + } +} + +#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]; + unsigned long tmp; + + /* ensure the debug is initialised (if enabled) */ + + s3c2410_pm_debug_init(); + + DBG("s3c2410_pm_enter(%d)\n", state); + + 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); + + /* ensure at least GESTATUS3 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)); + + /* save all necessary core registers not covered by the drivers */ + + s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save)); + s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_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); + + /* flush cache back to ram */ + + arm920_flush_kern_cache_all(); + + s3c2410_pm_check_store(); + + // need to make some form of time-delta + + /* send the cpu to sleep... */ + + __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ + + s3c2410_cpu_suspend(regs_save); + + /* unset the return-from-sleep flag, to ensure reset */ + + tmp = __raw_readl(S3C2410_GSTATUS2); + tmp &= S3C2410_GSTATUS2_OFFRESET; + __raw_writel(tmp, S3C2410_GSTATUS2); + + /* 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(irq_save, ARRAY_SIZE(irq_save)); + s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); + + s3c2410_pm_debug_init(); + + /* check what irq (if any) restored the system */ + + DBG("post sleep: IRQs 0x%08x, 0x%08x\n", + __raw_readl(S3C2410_SRCPND), + __raw_readl(S3C2410_EINTPEND)); + + s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), + s3c_irqwake_intmask); + + s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), + s3c_irqwake_eintmask); + + DBG("post sleep, preparing to return\n"); + + s3c2410_pm_check_restore(); + + /* ok, let's return from sleep */ + + DBG("S3C2410 PM Resume (post-restore)\n"); + return 0; +} + +/* + * Called after processes are frozen, but before we shut down devices. + */ +static int s3c2410_pm_prepare(suspend_state_t state) +{ + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static int s3c2410_pm_finish(suspend_state_t state) +{ + return 0; +} + +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct pm_ops s3c2410_pm_ops = { + .pm_disk_mode = PM_DISK_FIRMWARE, + .prepare = s3c2410_pm_prepare, + .enter = s3c2410_pm_enter, + .finish = s3c2410_pm_finish, +}; + +/* s3c2410_pm_init + * + * Attach the power management functions. This should be called + * from the board specific initialisation if the board supports + * it. +*/ + +int __init s3c2410_pm_init(void) +{ + printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); + + pm_set_ops(&s3c2410_pm_ops); + return 0; +} diff --git a/arch/arm/mach-s3c2410/pm.h b/arch/arm/mach-s3c2410/pm.h new file mode 100644 index 000000000000..7a5e714c7386 --- /dev/null +++ b/arch/arm/mach-s3c2410/pm.h @@ -0,0 +1,59 @@ +/* linux/arch/arm/mach-s3c2410/pm.h + * + * Copyright (c) 2004 Simtec Electronics + * Written by Ben Dooks, <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +/* s3c2410_pm_init + * + * called from board at initialisation time to setup the power + * management +*/ + +#ifdef CONFIG_PM + +extern __init int s3c2410_pm_init(void); + +#else + +static inline int s3c2410_pm_init(void) +{ + return 0; +} +#endif + +/* configuration for the IRQ mask over sleep */ +extern unsigned long s3c_irqwake_intmask; +extern unsigned long s3c_irqwake_eintmask; + +/* IRQ masks for IRQs allowed to go to sleep (see irq.c) */ +extern unsigned long s3c_irqwake_intallow; +extern unsigned long s3c_irqwake_eintallow; + +/* Flags for PM Control */ + +extern unsigned long s3c_pm_flags; + +/* from sleep.S */ + +extern void s3c2410_cpu_suspend(unsigned long *saveblk); +extern void s3c2410_cpu_resume(void); + +extern unsigned long s3c2410_sleep_save_phys; + +/* sleep save info */ + +struct sleep_save { + void __iomem *reg; + unsigned long val; +}; + +#define SAVE_ITEM(x) \ + { .reg = (x) } + +extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count); +extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count); diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c new file mode 100644 index 000000000000..ff2f25409e44 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -0,0 +1,200 @@ +/* linux/arch/arm/mach-s3c2410/s3c2410.c + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 16-May-2003 BJD Created initial version + * 16-Aug-2003 BJD Fixed header files and copyright, added URL + * 05-Sep-2003 BJD Moved to kernel v2.6 + * 18-Jan-2004 BJD Added serial port configuration + * 21-Aug-2004 BJD Added new struct s3c2410_board handler + * 28-Sep-2004 BJD Updates for new serial port bits + * 04-Nov-2004 BJD Updated UART configuration process + * 10-Jan-2005 BJD Removed s3c2410_clock_tick_rate +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-serial.h> + +#include "s3c2410.h" +#include "cpu.h" +#include "clock.h" + +/* Initial IO mappings */ + +static struct map_desc s3c2410_iodesc[] __initdata = { + IODESC_ENT(USBHOST), + IODESC_ENT(CLKPWR), + IODESC_ENT(LCD), + IODESC_ENT(UART), + IODESC_ENT(TIMER), + IODESC_ENT(ADC), + IODESC_ENT(WATCHDOG) +}; + +static struct resource s3c_uart0_resource[] = { + [0] = { + .start = S3C2410_PA_UART0, + .end = S3C2410_PA_UART0 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX0, + .end = IRQ_S3CUART_ERR0, + .flags = IORESOURCE_IRQ, + } + +}; + +static struct resource s3c_uart1_resource[] = { + [0] = { + .start = S3C2410_PA_UART1, + .end = S3C2410_PA_UART1 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX1, + .end = IRQ_S3CUART_ERR1, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource s3c_uart2_resource[] = { + [0] = { + .start = S3C2410_PA_UART2, + .end = S3C2410_PA_UART2 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX2, + .end = IRQ_S3CUART_ERR2, + .flags = IORESOURCE_IRQ, + } +}; + +/* our uart devices */ + +static struct platform_device s3c_uart0 = { + .name = "s3c2410-uart", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_uart0_resource), + .resource = s3c_uart0_resource, +}; + + +static struct platform_device s3c_uart1 = { + .name = "s3c2410-uart", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_uart1_resource), + .resource = s3c_uart1_resource, +}; + +static struct platform_device s3c_uart2 = { + .name = "s3c2410-uart", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_uart2_resource), + .resource = s3c_uart2_resource, +}; + +static struct platform_device *uart_devices[] __initdata = { + &s3c_uart0, + &s3c_uart1, + &s3c_uart2 +}; + +/* store our uart devices for the serial driver console */ +struct platform_device *s3c2410_uart_devices[3]; + +static int s3c2410_uart_count = 0; + +/* uart registration process */ + +void __init s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + struct platform_device *platdev; + int uart; + + for (uart = 0; uart < no; uart++, cfg++) { + platdev = uart_devices[cfg->hwport]; + + s3c24xx_uart_devs[uart] = platdev; + platdev->dev.platform_data = cfg; + } + + s3c2410_uart_count = uart; +} + +/* s3c2410_map_io + * + * register the standard cpu IO areas, and any passed in from the + * machine specific initialisation. +*/ + +void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size) +{ + /* register our io-tables */ + + iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc)); + iotable_init(mach_desc, mach_size); +} + +void __init s3c2410_init_clocks(int xtal) +{ + unsigned long tmp; + unsigned long fclk; + unsigned long hclk; + unsigned long pclk; + + /* now we've got our machine bits initialised, work out what + * clocks we've got */ + + fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal); + + tmp = __raw_readl(S3C2410_CLKDIVN); + + /* work out clock scalings */ + + hclk = fclk / ((tmp & S3C2410_CLKDIVN_HDIVN) ? 2 : 1); + pclk = hclk / ((tmp & S3C2410_CLKDIVN_PDIVN) ? 2 : 1); + + /* print brieft summary of clocks, etc */ + + printk("S3C2410: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", + print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); + + /* initialise the clocks here, to allow other things like the + * console to use them + */ + + s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); +} + +int __init s3c2410_init(void) +{ + printk("S3C2410: Initialising architecture\n"); + + return platform_add_devices(s3c24xx_uart_devs, s3c2410_uart_count); +} diff --git a/arch/arm/mach-s3c2410/s3c2410.h b/arch/arm/mach-s3c2410/s3c2410.h new file mode 100644 index 000000000000..4d5312a48209 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2410.h @@ -0,0 +1,37 @@ +/* arch/arm/mach-s3c2410/s3c2410.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2410 machine directory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 18-Aug-2004 BJD Created initial version + * 20-Aug-2004 BJD Added s3c2410_board struct + * 04-Sep-2004 BJD Added s3c2410_init_uarts() call + * 17-Oct-2004 BJD Moved board out to cpu + * 04-Jan-2005 BJD Changed uart init + * 10-Jan-2005 BJD Removed timer to cpu.h, moved 2410 specific bits here + * 14-Jan-2005 BJD Added s3c2410_init_clocks call +*/ + +#ifdef CONFIG_CPU_S3C2410 + +extern int s3c2410_init(void); + +extern void s3c2410_map_io(struct map_desc *mach_desc, int size); + +extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c2410_init_clocks(int xtal); + +#else +#define s3c2410_init_clocks NULL +#define s3c2410_init_uarts NULL +#define s3c2410_map_io NULL +#define s3c2410_init NULL +#endif diff --git a/arch/arm/mach-s3c2410/s3c2440-dsc.c b/arch/arm/mach-s3c2410/s3c2440-dsc.c new file mode 100644 index 000000000000..16fa2a3b38fa --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440-dsc.c @@ -0,0 +1,59 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440-dsc.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2440 Drive Strength Control support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 29-Aug-2004 BJD Start of drive-strength control + * 09-Nov-2004 BJD Added symbol export + * 11-Jan-2005 BJD Include fix +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-dsc.h> + +#include "cpu.h" +#include "s3c2440.h" + +int s3c2440_set_dsc(unsigned int pin, unsigned int value) +{ + void __iomem *base; + unsigned long val; + unsigned long flags; + unsigned long mask; + + base = (pin & S3C2440_SELECT_DSC1) ? S3C2440_DSC1 : S3C2440_DSC0; + mask = 3 << S3C2440_DSC_GETSHIFT(pin); + + local_irq_save(flags); + + val = __raw_readl(base); + val &= ~mask; + val |= value & mask; + __raw_writel(val, base); + + local_irq_restore(flags); + return 0; +} + +EXPORT_SYMBOL(s3c2440_set_dsc); diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c new file mode 100644 index 000000000000..9a8cc5ae2255 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.c @@ -0,0 +1,281 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2440 Mobile CPU support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 24-Aug-2004 BJD Start of s3c2440 support + * 12-Oct-2004 BJD Moved clock info out to clock.c + * 01-Nov-2004 BJD Fixed clock build code + * 09-Nov-2004 BJD Added sysdev for power management + * 04-Nov-2004 BJD New serial registration + * 15-Nov-2004 BJD Rename the i2c device for the s3c2440 + * 14-Jan-2005 BJD Moved clock init code into seperate function + * 14-Jan-2005 BJD Removed un-used clock bits +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/sysdev.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hardware/clock.h> + +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-serial.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-gpioj.h> +#include <asm/arch/regs-dsc.h> + +#include "s3c2440.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "pm.h" + + +static struct map_desc s3c2440_iodesc[] __initdata = { + IODESC_ENT(USBHOST), + IODESC_ENT(CLKPWR), + IODESC_ENT(LCD), + IODESC_ENT(TIMER), + IODESC_ENT(ADC), + IODESC_ENT(WATCHDOG), +}; + +static struct resource s3c_uart0_resource[] = { + [0] = { + .start = S3C2410_PA_UART0, + .end = S3C2410_PA_UART0 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX0, + .end = IRQ_S3CUART_ERR0, + .flags = IORESOURCE_IRQ, + } + +}; + +static struct resource s3c_uart1_resource[] = { + [0] = { + .start = S3C2410_PA_UART1, + .end = S3C2410_PA_UART1 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX1, + .end = IRQ_S3CUART_ERR1, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource s3c_uart2_resource[] = { + [0] = { + .start = S3C2410_PA_UART2, + .end = S3C2410_PA_UART2 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX2, + .end = IRQ_S3CUART_ERR2, + .flags = IORESOURCE_IRQ, + } +}; + +/* our uart devices */ + +static struct platform_device s3c_uart0 = { + .name = "s3c2440-uart", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_uart0_resource), + .resource = s3c_uart0_resource, +}; + +static struct platform_device s3c_uart1 = { + .name = "s3c2440-uart", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_uart1_resource), + .resource = s3c_uart1_resource, +}; + +static struct platform_device s3c_uart2 = { + .name = "s3c2440-uart", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_uart2_resource), + .resource = s3c_uart2_resource, +}; + +static struct platform_device *uart_devices[] __initdata = { + &s3c_uart0, + &s3c_uart1, + &s3c_uart2 +}; + +/* uart initialisation */ + +static int __initdata s3c2440_uart_count; + +void __init s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + struct platform_device *platdev; + int uart; + + for (uart = 0; uart < no; uart++, cfg++) { + platdev = uart_devices[cfg->hwport]; + + s3c24xx_uart_devs[uart] = platdev; + platdev->dev.platform_data = cfg; + } + + s3c2440_uart_count = uart; +} + + +#ifdef CONFIG_PM + +struct sleep_save s3c2440_sleep[] = { + SAVE_ITEM(S3C2440_DSC0), + SAVE_ITEM(S3C2440_DSC1), + SAVE_ITEM(S3C2440_GPJDAT), + SAVE_ITEM(S3C2440_GPJCON), + SAVE_ITEM(S3C2440_GPJUP) +}; + +static int s3c2440_suspend(struct sys_device *dev, pm_message_t state) +{ + s3c2410_pm_do_save(s3c2440_sleep, ARRAY_SIZE(s3c2440_sleep)); + return 0; +} + +static int s3c2440_resume(struct sys_device *dev) +{ + s3c2410_pm_do_restore(s3c2440_sleep, ARRAY_SIZE(s3c2440_sleep)); + return 0; +} + +#else +#define s3c2440_suspend NULL +#define s3c2440_resume NULL +#endif + +struct sysdev_class s3c2440_sysclass = { + set_kset_name("s3c2440-core"), + .suspend = s3c2440_suspend, + .resume = s3c2440_resume +}; + +static struct sys_device s3c2440_sysdev = { + .cls = &s3c2440_sysclass, +}; + +void __init s3c2440_map_io(struct map_desc *mach_desc, int size) +{ + /* register our io-tables */ + + iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc)); + iotable_init(mach_desc, size); + /* rename any peripherals used differing from the s3c2410 */ + + s3c_device_i2c.name = "s3c2440-i2c"; + + /* change irq for watchdog */ + + s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT; + s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT; +} + +void __init s3c2440_init_clocks(int xtal) +{ + unsigned long clkdiv; + unsigned long camdiv; + unsigned long hclk, fclk, pclk; + int hdiv = 1; + + /* now we've got our machine bits initialised, work out what + * clocks we've got */ + + fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; + + clkdiv = __raw_readl(S3C2410_CLKDIVN); + camdiv = __raw_readl(S3C2440_CAMDIVN); + + /* work out clock scalings */ + + switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { + case S3C2440_CLKDIVN_HDIVN_1: + hdiv = 1; + break; + + case S3C2440_CLKDIVN_HDIVN_2: + hdiv = 1; + break; + + case S3C2440_CLKDIVN_HDIVN_4_8: + hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; + break; + + case S3C2440_CLKDIVN_HDIVN_3_6: + hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3; + break; + } + + hclk = fclk / hdiv; + pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1); + + /* print brief summary of clocks, etc */ + + printk("S3C2440: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", + print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); + + /* initialise the clocks here, to allow other things like the + * console to use them, and to add new ones after the initialisation + */ + + s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); +} + +/* need to register class before we actually register the device, and + * we also need to ensure that it has been initialised before any of the + * drivers even try to use it (even if not on an s3c2440 based system) + * as a driver which may support both 2410 and 2440 may try and use it. +*/ + +int __init s3c2440_core_init(void) +{ + return sysdev_class_register(&s3c2440_sysclass); +} + +core_initcall(s3c2440_core_init); + +int __init s3c2440_init(void) +{ + int ret; + + printk("S3C2440: Initialising architecture\n"); + + ret = sysdev_register(&s3c2440_sysdev); + if (ret != 0) + printk(KERN_ERR "failed to register sysdev for s3c2440\n"); + else + ret = platform_add_devices(s3c24xx_uart_devs, s3c2440_uart_count); + + return ret; +} diff --git a/arch/arm/mach-s3c2410/s3c2440.h b/arch/arm/mach-s3c2410/s3c2440.h new file mode 100644 index 000000000000..29cb6df65a48 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.h @@ -0,0 +1,35 @@ +/* arch/arm/mach-s3c2410/s3c2440.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2440 cpu support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 24-Aug-2004 BJD Start of S3C2440 CPU support + * 04-Nov-2004 BJD Added s3c2440_init_uarts() + * 04-Jan-2005 BJD Moved uart init to cpu code + * 10-Jan-2005 BJD Moved 2440 specific init here + * 14-Jan-2005 BJD Split the clock initialisation code +*/ + +#ifdef CONFIG_CPU_S3C2440 + +extern int s3c2440_init(void); + +extern void s3c2440_map_io(struct map_desc *mach_desc, int size); + +extern void s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c2440_init_clocks(int xtal); + +#else +#define s3c2440_init_clocks NULL +#define s3c2440_init_uarts NULL +#define s3c2440_map_io NULL +#define s3c2440_init NULL +#endif diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S new file mode 100644 index 000000000000..61768dac7fee --- /dev/null +++ b/arch/arm/mach-s3c2410/sleep.S @@ -0,0 +1,180 @@ +/* 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/config.h> +#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_suspend + * + * put the cpu into sleep mode + * + * entry: + * r0 = sleep save block + */ + +ENTRY(s3c2410_cpu_suspend) + 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, c2, c0, 0 @ auxiliary control register + mrc p15, 0, r9, c1, c0, 0 @ control register + + stmia r0, { r4 - r13 } + + @@ flush the caches to ensure everything is back out to + @@ SDRAM before the core powers down + + bl arm920_flush_kern_cache_all + + @@ prepare cpu to sleep + + ldr r4, =S3C2410_REFRESH + ldr r5, =S3C2410_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 + + @@ return to the caller, after having the MMU + @@ turned on, this restores the last bits from the + @@ stack +resume_with_mmu: + 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 | MODE_SVC + msr cpsr_c, r0 + + @@ load UART to allow us to print the two characters for + @@ resume debug + + mov r2, #S3C2410_PA_UART & 0xff000000 + orr r2, r2, #S3C2410_PA_UART & 0xff000 + +#if 0 + /* SMDK2440 LED set */ + mov r14, #S3C2410_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 + mcr p15, 0, r8, c1, c1, 0 @ auxilliary control + +#ifdef CONFIG_DEBUG_RESUME + mov r3, #'R' + strb r3, [ r2, #S3C2410_UTXH ] +#endif + + ldr r2, =resume_with_mmu + mcr p15, 0, r9, c1, c0, 0 @ turn on MMU, etc + nop @ second-to-last before mmu + mov pc, r2 @ go back to virtual address + + .ltorg diff --git a/arch/arm/mach-s3c2410/time.c b/arch/arm/mach-s3c2410/time.c new file mode 100644 index 000000000000..179f0e031af4 --- /dev/null +++ b/arch/arm/mach-s3c2410/time.c @@ -0,0 +1,256 @@ +/* linux/arch/arm/mach-s3c2410/time.c + * + * Copyright (C) 2003-2005 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/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/err.h> + +#include <asm/system.h> +#include <asm/leds.h> +#include <asm/mach-types.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/arch/map.h> +#include <asm/arch/regs-timer.h> +#include <asm/arch/regs-irq.h> +#include <asm/mach/time.h> +#include <asm/hardware/clock.h> + +#include "clock.h" + +static unsigned long timer_startval; +static unsigned long timer_usec_ticks; + +#define TIMER_USEC_SHIFT 16 + +/* we use the shifted arithmetic to work out the ratio of timer ticks + * to usecs, as often the peripheral clock is not a nice even multiple + * of 1MHz. + * + * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok + * for the current HZ value of 200 without producing overflows. + * + * Original patch by Dimitry Andric, updated by Ben Dooks +*/ + + +/* timer_mask_usec_ticks + * + * given a clock and divisor, make the value to pass into timer_ticks_to_usec + * to scale the ticks into usecs +*/ + +static inline unsigned long +timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) +{ + unsigned long den = pclk / 1000; + + return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; +} + +/* timer_ticks_to_usec + * + * convert timer ticks to usec. +*/ + +static inline unsigned long timer_ticks_to_usec(unsigned long ticks) +{ + unsigned long res; + + res = ticks * timer_usec_ticks; + res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ + + return res >> TIMER_USEC_SHIFT; +} + +/*** + * Returns microsecond since last clock interrupt. Note that interrupts + * will have been disabled by do_gettimeoffset() + * IRQs are disabled before entering here from do_gettimeofday() + */ + +#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) + +static unsigned long s3c2410_gettimeoffset (void) +{ + unsigned long tdone; + unsigned long irqpend; + unsigned long tval; + + /* work out how many ticks have gone since last timer interrupt */ + + tval = __raw_readl(S3C2410_TCNTO(4)); + tdone = timer_startval - tval; + + /* check to see if there is an interrupt pending */ + + irqpend = __raw_readl(S3C2410_SRCPND); + if (irqpend & SRCPND_TIMER4) { + /* re-read the timer, and try and fix up for the missed + * interrupt. Note, the interrupt may go off before the + * timer has re-loaded from wrapping. + */ + + tval = __raw_readl(S3C2410_TCNTO(4)); + tdone = timer_startval - tval; + + if (tval != 0) + tdone += timer_startval; + } + + return timer_ticks_to_usec(tdone); +} + + +/* + * IRQ handler for the timer + */ +static irqreturn_t +s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + write_seqlock(&xtime_lock); + timer_tick(regs); + write_sequnlock(&xtime_lock); + return IRQ_HANDLED; +} + +static struct irqaction s3c2410_timer_irq = { + .name = "S3C2410 Timer Tick", + .flags = SA_INTERRUPT, + .handler = s3c2410_timer_interrupt +}; + +/* + * Set up timer interrupt, and return the current time in seconds. + * + * Currently we only use timer4, as it is the only timer which has no + * other function that can be exploited externally + */ +static void s3c2410_timer_setup (void) +{ + unsigned long tcon; + unsigned long tcnt; + unsigned long tcfg1; + unsigned long tcfg0; + + tcnt = 0xffff; /* default value for tcnt */ + + /* read the current timer configuration bits */ + + tcon = __raw_readl(S3C2410_TCON); + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + /* configure the system for whichever machine is in use */ + + if (machine_is_bast() || machine_is_vr1000()) { + /* timer is at 12MHz, scaler is 1 */ + timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); + tcnt = 12000000 / HZ; + + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; + } else { + unsigned long pclk; + struct clk *clk; + + /* for the h1940 (and others), we use the pclk from the core + * to generate the timer values. since values around 50 to + * 70MHz are not values we can directly generate the timer + * value from, we need to pre-scale and divide before using it. + * + * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz + * (8.45 ticks per usec) + */ + + /* this is used as default if no other timer can be found */ + + clk = clk_get(NULL, "timers"); + if (IS_ERR(clk)) + panic("failed to get clock for system timer"); + + clk_use(clk); + clk_enable(clk); + + pclk = clk_get_rate(clk); + + /* configure clock tick */ + + timer_usec_ticks = timer_mask_usec_ticks(6, pclk); + + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; + + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; + + tcnt = (pclk / 6) / HZ; + } + + /* timers reload after counting zero, so reduce the count by 1 */ + + tcnt--; + + printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", + tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); + + /* check to see if timer is within 16bit range... */ + if (tcnt > 0xffff) { + panic("setup_timer: HZ is too small, cannot configure timer!"); + return; + } + + __raw_writel(tcfg1, S3C2410_TCFG1); + __raw_writel(tcfg0, S3C2410_TCFG0); + + timer_startval = tcnt; + __raw_writel(tcnt, S3C2410_TCNTB(4)); + + /* ensure timer is stopped... */ + + tcon &= ~(7<<20); + tcon |= S3C2410_TCON_T4RELOAD; + tcon |= S3C2410_TCON_T4MANUALUPD; + + __raw_writel(tcon, S3C2410_TCON); + __raw_writel(tcnt, S3C2410_TCNTB(4)); + __raw_writel(tcnt, S3C2410_TCMPB(4)); + + /* start the timer running */ + tcon |= S3C2410_TCON_T4START; + tcon &= ~S3C2410_TCON_T4MANUALUPD; + __raw_writel(tcon, S3C2410_TCON); +} + +static void __init s3c2410_timer_init (void) +{ + s3c2410_timer_setup(); + setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); +} + +struct sys_timer s3c24xx_timer = { + .init = s3c2410_timer_init, + .offset = s3c2410_gettimeoffset, + .resume = s3c2410_timer_setup +}; diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c new file mode 100644 index 000000000000..7f2b61362976 --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.c @@ -0,0 +1,113 @@ +/* linux/arch/arm/mach-s3c2410/usb-simtec.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * Simtec BAST and Thorcom VR1000 USB port support functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 14-Sep-2004 BJD Created + * 18-Oct-2004 BJD Cleanups, and added code to report OC cleared +*/ + +#define DEBUG + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/bast-map.h> +#include <asm/arch/bast-irq.h> +#include <asm/arch/usb-control.h> +#include <asm/arch/regs-gpio.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include "devs.h" +#include "usb-simtec.h" + +/* control power and monitor over-current events on various Simtec + * designed boards. +*/ + +static void +usb_simtec_powercontrol(int port, int to) +{ + pr_debug("usb_simtec_powercontrol(%d,%d)\n", port, to); + + if (port == 1) + s3c2410_gpio_setpin(S3C2410_GPB4, to ? 0:1); +} + +static irqreturn_t +usb_simtec_ocirq(int irq, void *pw, struct pt_regs *regs) +{ + struct s3c2410_hcd_info *info = (struct s3c2410_hcd_info *)pw; + + if (s3c2410_gpio_getpin(S3C2410_GPG10) == 0) { + pr_debug("usb_simtec: over-current irq (oc detected)\n"); + s3c2410_report_oc(info, 3); + } else { + pr_debug("usb_simtec: over-current irq (oc cleared)\n"); + s3c2410_report_oc(info, 0); + } + + return IRQ_HANDLED; +} + +static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on) +{ + int ret; + + if (on) { + ret = request_irq(IRQ_USBOC, usb_simtec_ocirq, SA_INTERRUPT, + "USB Over-current", info); + if (ret != 0) { + printk(KERN_ERR "failed to request usb oc irq\n"); + } + + set_irq_type(IRQ_USBOC, IRQT_BOTHEDGE); + } else { + free_irq(IRQ_USBOC, info); + } +} + +static struct s3c2410_hcd_info usb_simtec_info = { + .port[0] = { + .flags = S3C_HCDFLG_USED + }, + .port[1] = { + .flags = S3C_HCDFLG_USED + }, + + .power_control = usb_simtec_powercontrol, + .enable_oc = usb_simtec_enableoc, +}; + + +int usb_simtec_init(void) +{ + printk("USB Power Control, (c) 2004 Simtec Electronics\n"); + s3c_device_usb.dev.platform_data = &usb_simtec_info; + + s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP); + s3c2410_gpio_setpin(S3C2410_GPB4, 1); + return 0; +} diff --git a/arch/arm/mach-s3c2410/usb-simtec.h b/arch/arm/mach-s3c2410/usb-simtec.h new file mode 100644 index 000000000000..92c0cc83aeec --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.h @@ -0,0 +1,19 @@ +/* linux/arch/arm/mach-s3c2410/usb-simtec.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * Simtec BAST and Thorcom VR1000 USB port support functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 20-Aug-2004 BJD Created +*/ + +extern int usb_simtec_init(void); + |