diff options
author | Ben Dooks <ben-linux@fluff.org> | 2006-06-18 23:06:41 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-06-18 23:06:41 +0100 |
commit | 96ce2385dd2817da549910001a69ac0a2762a1b9 (patch) | |
tree | 18ec36e9e1e8a6b7c19aacb53c256fdb941c4ecd /arch | |
parent | 66a9b49a370baac75d90b7da9a2445997a8a9438 (diff) | |
download | lwn-96ce2385dd2817da549910001a69ac0a2762a1b9.tar.gz lwn-96ce2385dd2817da549910001a69ac0a2762a1b9.zip |
[ARM] 3559/1: S3C2442: core and serial port
Patch from Ben Dooks
Core support for the Samsung S3C2442, and the
serial port driver update to allow the serial
port blocks to be used.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-s3c2410/Kconfig | 26 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/Makefile | 10 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/clock.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/cpu.c | 24 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/cpu.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c2440-irq.c | 77 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c2440.c | 142 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c2442-clock.c | 171 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c2442.c | 52 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c2442.h | 17 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c244x-irq.c | 142 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c244x.c | 182 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/s3c244x.h | 25 |
13 files changed, 651 insertions, 220 deletions
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 970f98dadffc..0c334136db7c 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -70,6 +70,18 @@ config ARCH_S3C2440 help Say Y here if you are using the SMDK2440. +config SMDK2440_CPU2440 + bool "SMDK2440 with S3C2440 cpu module" + depends on ARCH_S3C2440 + default y if ARCH_S3C2440 + select CPU_S3C2440 + +config SMDK2440_CPU2442 + bool "SMDM2440 with S3C2442 cpu module" + depends on ARCH_S3C2440 + select CPU_S3C2442 + + config MACH_VR1000 bool "Thorcom VR1000" select CPU_S3C2410 @@ -109,12 +121,26 @@ config CPU_S3C2410 Support for S3C2410 and S3C2410A family from the S3C24XX line of Samsung Mobile CPUs. +config CPU_S3C244X + bool + depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) + help + Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. + config CPU_S3C2440 bool depends on ARCH_S3C2410 + select CPU_S3C244X help Support for S3C2440 Samsung Mobile CPU based systems. +config CPU_S3C2442 + bool + depends on ARCH_S3C2420 + select CPU_S3C244X + help + Support for S3C2442 Samsung Mobile CPU based systems. + comment "S3C2410 Boot" config S3C2410_BOOT_WATCHDOG diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 3e5712db6b52..5e09355cd4f4 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -24,6 +24,11 @@ obj-$(CONFIG_S3C2410_DMA) += dma.o obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o +# S3C244X support + +obj-$(CONFIG_CPU_S3C244X) += s3c244x.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o + # S3C2440 support obj-$(CONFIG_CPU_S3C2440) += s3c2440.o s3c2440-dsc.o @@ -31,6 +36,11 @@ obj-$(CONFIG_CPU_S3C2440) += s3c2440-irq.o obj-$(CONFIG_CPU_S3C2440) += s3c2440-clock.o obj-$(CONFIG_CPU_S3C2440) += s3c2410-gpio.o +# S3C2442 support + +obj-$(CONFIG_CPU_S3C2442) += s3c2442.o +obj-$(CONFIG_CPU_S3C2442) += s3c2442-clock.o + # bast extras obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index 6de713ad319a..99d174612b53 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -70,7 +70,7 @@ void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable) clkcon &= ~clocks; /* ensure none of the special function bits set */ - clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER | 3); __raw_writel(clkcon, S3C2410_CLKCON); } diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/mach-s3c2410/cpu.c index acc58adfbd21..52842e6e86e6 100644 --- a/arch/arm/mach-s3c2410/cpu.c +++ b/arch/arm/mach-s3c2410/cpu.c @@ -44,7 +44,9 @@ #include "clock.h" #include "s3c2400.h" #include "s3c2410.h" +#include "s3c244x.h" #include "s3c2440.h" +#include "s3c2442.h" struct cpu_table { unsigned long idcode; @@ -61,6 +63,7 @@ struct cpu_table { static const char name_s3c2400[] = "S3C2400"; static const char name_s3c2410[] = "S3C2410"; static const char name_s3c2440[] = "S3C2440"; +static const char name_s3c2442[] = "S3C2442"; static const char name_s3c2410a[] = "S3C2410A"; static const char name_s3c2440a[] = "S3C2440A"; @@ -86,22 +89,31 @@ static struct cpu_table cpu_ids[] __initdata = { { .idcode = 0x32440000, .idmask = 0xffffffff, - .map_io = s3c2440_map_io, - .init_clocks = s3c2440_init_clocks, - .init_uarts = s3c2440_init_uarts, + .map_io = s3c244x_map_io, + .init_clocks = s3c244x_init_clocks, + .init_uarts = s3c244x_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, + .map_io = s3c244x_map_io, + .init_clocks = s3c244x_init_clocks, + .init_uarts = s3c244x_init_uarts, .init = s3c2440_init, .name = name_s3c2440a }, { + .idcode = 0x32440aaa, + .idmask = 0xffffffff, + .map_io = s3c244x_map_io, + .init_clocks = s3c244x_init_clocks, + .init_uarts = s3c244x_init_uarts, + .init = s3c2442_init, + .name = name_s3c2442 + }, + { .idcode = 0x0, /* S3C2400 doesn't have an idcode */ .idmask = 0xffffffff, .map_io = s3c2400_map_io, diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h index 0c776acb063a..40862899b2f1 100644 --- a/arch/arm/mach-s3c2410/cpu.h +++ b/arch/arm/mach-s3c2410/cpu.h @@ -74,3 +74,4 @@ extern struct sys_timer s3c24xx_timer; /* system device classes */ extern struct sysdev_class s3c2440_sysclass; +extern struct sysdev_class s3c2442_sysclass; diff --git a/arch/arm/mach-s3c2410/s3c2440-irq.c b/arch/arm/mach-s3c2410/s3c2440-irq.c index 278d0044c85d..acfe3870727b 100644 --- a/arch/arm/mach-s3c2410/s3c2440-irq.c +++ b/arch/arm/mach-s3c2410/s3c2440-irq.c @@ -100,73 +100,12 @@ static struct irqchip s3c_irq_wdtac97 = { .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; - desc_handle_irq(IRQ_S3C2440_CAM_C, mydesc, regs); - } - if (subsrc & 2) { - mydesc = irq_desc + IRQ_S3C2440_CAM_P; - desc_handle_irq(IRQ_S3C2440_CAM_P, 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); @@ -179,18 +118,6 @@ static int s3c2440_irq_add(struct sys_device *sysdev) 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; } @@ -198,10 +125,10 @@ static struct sysdev_driver s3c2440_irq_driver = { .add = s3c2440_irq_add, }; -static int s3c24xx_irq_driver(void) +static int s3c2440_irq_init(void) { return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_irq_driver); } -arch_initcall(s3c24xx_irq_driver); +arch_initcall(s3c2440_irq_init); diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c index 54681740f9f1..0ab50f44f318 100644 --- a/arch/arm/mach-s3c2410/s3c2440.c +++ b/arch/arm/mach-s3c2410/s3c2440.c @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s3c2410/s3c2440.c * - * Copyright (c) 2004-2005 Simtec Electronics + * Copyright (c) 2004-2006 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * * Samsung S3C2440 Mobile CPU support @@ -8,16 +8,6 @@ * 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> @@ -50,144 +40,20 @@ #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), -}; - -/* uart initialisation */ - -void __init s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no) -{ - s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); -} - -#ifdef CONFIG_PM - -static 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) +int __init s3c2440_init(void) { - /* 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"; - s3c_device_nand.name = "s3c2440-nand"; + printk("S3C2440: Initialising architecture\n"); /* 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 = 2; - 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. -*/ - -static int __init s3c2440_core_init(void) -{ - return sysdev_class_register(&s3c2440_sysclass); -} - -core_initcall(s3c2440_core_init); - -int __init s3c2440_init(void) -{ - printk("S3C2440: Initialising architecture\n"); + /* register our system device for everything else */ return sysdev_register(&s3c2440_sysdev); } diff --git a/arch/arm/mach-s3c2410/s3c2442-clock.c b/arch/arm/mach-s3c2410/s3c2442-clock.c new file mode 100644 index 000000000000..5b7b301eb522 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2442-clock.c @@ -0,0 +1,171 @@ +/* linux/arch/arm/mach-s3c2410/s3c2442-clock.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2442 Clock support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/sysdev.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/mutex.h> +#include <linux/clk.h> + +#include <asm/hardware.h> +#include <asm/atomic.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/arch/regs-clock.h> + +#include "clock.h" +#include "cpu.h" + +/* S3C2442 extended clock support */ + +static unsigned long s3c2442_camif_upll_round(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + int div; + + if (rate > parent_rate) + return parent_rate; + + div = parent_rate / rate; + + if (div == 3) + return parent_rate / 3; + + /* note, we remove the +/- 1 calculations for the divisor */ + + div /= 2; + + if (div < 1) + div = 1; + else if (div > 16) + div = 16; + + return parent_rate / (div * 2); +} + +static int s3c2442_camif_upll_setrate(struct clk *clk, unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); + + rate = s3c2442_camif_upll_round(clk, rate); + + camdivn &= ~S3C2442_CAMDIVN_CAMCLK_DIV3; + + if (rate == parent_rate) { + camdivn &= ~S3C2440_CAMDIVN_CAMCLK_SEL; + } else if ((parent_rate / rate) == 3) { + camdivn |= S3C2440_CAMDIVN_CAMCLK_SEL; + camdivn |= S3C2442_CAMDIVN_CAMCLK_DIV3; + } else { + camdivn &= ~S3C2440_CAMDIVN_CAMCLK_MASK; + camdivn |= S3C2440_CAMDIVN_CAMCLK_SEL; + camdivn |= (((parent_rate / rate) / 2) - 1); + } + + __raw_writel(camdivn, S3C2440_CAMDIVN); + + return 0; +} + +/* Extra S3C2442 clocks */ + +static struct clk s3c2442_clk_cam = { + .name = "camif", + .id = -1, + .enable = s3c24xx_clkcon_enable, + .ctrlbit = S3C2440_CLKCON_CAMERA, +}; + +static struct clk s3c2442_clk_cam_upll = { + .name = "camif-upll", + .id = -1, + .set_rate = s3c2442_camif_upll_setrate, + .round_rate = s3c2442_camif_upll_round, +}; + +static int s3c2442_clk_add(struct sys_device *sysdev) +{ + unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); + unsigned long clkdivn; + struct clk *clk_h; + struct clk *clk_p; + struct clk *clk_upll; + + printk("S3C2442: Clock Support, DVS %s\n", + (camdivn & S3C2440_CAMDIVN_DVSEN) ? "on" : "off"); + + clk_p = clk_get(NULL, "pclk"); + clk_h = clk_get(NULL, "hclk"); + clk_upll = clk_get(NULL, "upll"); + + if (IS_ERR(clk_p) || IS_ERR(clk_h) || IS_ERR(clk_upll)) { + printk(KERN_ERR "S3C2442: Failed to get parent clocks\n"); + return -EINVAL; + } + + /* check rate of UPLL, and if it is near 96MHz, then change + * to using half the UPLL rate for the system */ + + if (clk_get_rate(clk_upll) > (94 * MHZ)) { + clk_usb_bus.rate = clk_get_rate(clk_upll) / 2; + + mutex_lock(&clocks_mutex); + + clkdivn = __raw_readl(S3C2410_CLKDIVN); + clkdivn |= S3C2440_CLKDIVN_UCLK; + __raw_writel(clkdivn, S3C2410_CLKDIVN); + + mutex_unlock(&clocks_mutex); + } + + s3c2442_clk_cam.parent = clk_h; + s3c2442_clk_cam_upll.parent = clk_upll; + + s3c24xx_register_clock(&s3c2442_clk_cam); + s3c24xx_register_clock(&s3c2442_clk_cam_upll); + + clk_disable(&s3c2442_clk_cam); + + return 0; +} + +static struct sysdev_driver s3c2442_clk_driver = { + .add = s3c2442_clk_add, +}; + +static __init int s3c2442_clk_init(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_clk_driver); +} + +arch_initcall(s3c2442_clk_init); diff --git a/arch/arm/mach-s3c2410/s3c2442.c b/arch/arm/mach-s3c2410/s3c2442.c new file mode 100644 index 000000000000..debae2430557 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2442.c @@ -0,0 +1,52 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2442 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. +*/ + +#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/platform_device.h> +#include <linux/sysdev.h> +#include <linux/clk.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 <asm/arch/regs-gpio.h> +#include <asm/arch/regs-gpioj.h> +#include <asm/arch/regs-dsc.h> + +#include "s3c2442.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "pm.h" + +static struct sys_device s3c2442_sysdev = { + .cls = &s3c2442_sysclass, +}; + +int __init s3c2442_init(void) +{ + printk("S3C2442: Initialising architecture\n"); + + return sysdev_register(&s3c2442_sysdev); +} diff --git a/arch/arm/mach-s3c2410/s3c2442.h b/arch/arm/mach-s3c2410/s3c2442.h new file mode 100644 index 000000000000..0ae37d24866c --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2442.h @@ -0,0 +1,17 @@ +/* arch/arm/mach-s3c2410/s3c2442.h + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2442 cpu support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifdef CONFIG_CPU_S3C2442 +extern int s3c2442_init(void); +#else +#define s3c2442_init NULL +#endif diff --git a/arch/arm/mach-s3c2410/s3c244x-irq.c b/arch/arm/mach-s3c2410/s3c244x-irq.c new file mode 100644 index 000000000000..2aadca1ce7eb --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c244x-irq.c @@ -0,0 +1,142 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440-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: + * 25-Jul-2005 BJD Split from irq.c + * +*/ + +#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" +#include "irq.h" + +/* 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; + desc_handle_irq(IRQ_S3C2440_CAM_C, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_S3C2440_CAM_P; + desc_handle_irq(IRQ_S3C2440_CAM_P, 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 s3c244x_irq_add(struct sys_device *sysdev) +{ + unsigned int irqno; + + 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 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 s3c244x_irq_driver = { + .add = s3c244x_irq_add, +}; + +static int s3c2440_irq_init(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c244x_irq_driver); +} + +arch_initcall(s3c2440_irq_init); + + +static int s3c2442_irq_init(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, &s3c244x_irq_driver); +} + +arch_initcall(s3c2442_irq_init); diff --git a/arch/arm/mach-s3c2410/s3c244x.c b/arch/arm/mach-s3c2410/s3c244x.c new file mode 100644 index 000000000000..96852a7000db --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c244x.c @@ -0,0 +1,182 @@ +/* linux/arch/arm/mach-s3c2410/s3c244x.c + * + * Copyright (c) 2004-2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2440 and S3C2442 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. +*/ + +#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/platform_device.h> +#include <linux/sysdev.h> +#include <linux/clk.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 <asm/arch/regs-gpio.h> +#include <asm/arch/regs-gpioj.h> +#include <asm/arch/regs-dsc.h> + +#include "s3c2440.h" +#include "s3c244x.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" +#include "pm.h" + +static struct map_desc s3c244x_iodesc[] __initdata = { + IODESC_ENT(CLKPWR), + IODESC_ENT(TIMER), + IODESC_ENT(WATCHDOG), + IODESC_ENT(LCD), + IODESC_ENT(ADC), + IODESC_ENT(USBHOST), +}; + +/* uart initialisation */ + +void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); +} + +void __init s3c244x_map_io(struct map_desc *mach_desc, int size) +{ + /* register our io-tables */ + + iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc)); + iotable_init(mach_desc, size); + + /* rename any peripherals used differing from the s3c2410 */ + + s3c_device_i2c.name = "s3c2440-i2c"; + s3c_device_nand.name = "s3c2440-nand"; +} + +void __init s3c244x_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 = 2; + 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("S3C244X: 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); +} + +#ifdef CONFIG_PM + +static struct sleep_save s3c244x_sleep[] = { + SAVE_ITEM(S3C2440_DSC0), + SAVE_ITEM(S3C2440_DSC1), + SAVE_ITEM(S3C2440_GPJDAT), + SAVE_ITEM(S3C2440_GPJCON), + SAVE_ITEM(S3C2440_GPJUP) +}; + +static int s3c244x_suspend(struct sys_device *dev, pm_message_t state) +{ + s3c2410_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); + return 0; +} + +static int s3c244x_resume(struct sys_device *dev) +{ + s3c2410_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); + return 0; +} + +#else +#define s3c244x_suspend NULL +#define s3c244x_resume NULL +#endif + +/* Since the S3C2442 and S3C2440 share items, put both sysclasses here */ + +struct sysdev_class s3c2440_sysclass = { + set_kset_name("s3c2440-core"), + .suspend = s3c244x_suspend, + .resume = s3c244x_resume +}; + +struct sysdev_class s3c2442_sysclass = { + set_kset_name("s3c2442-core"), + .suspend = s3c244x_suspend, + .resume = s3c244x_resume +}; + +/* 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. +*/ + +static int __init s3c2440_core_init(void) +{ + return sysdev_class_register(&s3c2440_sysclass); +} + +core_initcall(s3c2440_core_init); + +static int __init s3c2442_core_init(void) +{ + return sysdev_class_register(&s3c2442_sysclass); +} + +core_initcall(s3c2442_core_init); diff --git a/arch/arm/mach-s3c2410/s3c244x.h b/arch/arm/mach-s3c2410/s3c244x.h new file mode 100644 index 000000000000..3e7f5f75134d --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c244x.h @@ -0,0 +1,25 @@ +/* arch/arm/mach-s3c2410/s3c2440.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for S3C2440 and S3C2442 cpu support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442) + +extern void s3c244x_map_io(struct map_desc *mach_desc, int size); + +extern void s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c244x_init_clocks(int xtal); + +#else +#define s3c244x_init_clocks NULL +#define s3c244x_init_uarts NULL +#define s3c244x_map_io NULL +#endif |