diff options
Diffstat (limited to 'arch/arm/mach-s3c2410')
39 files changed, 2445 insertions, 663 deletions
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 970f98dadff..f5d9cd498a5 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -70,6 +70,24 @@ 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_SMDK2413 + bool "SMDK2413" + select CPU_S3C2412 + select MACH_SMDK + help + Say Y here if you are using an SMDK2413 + config MACH_VR1000 bool "Thorcom VR1000" select CPU_S3C2410 @@ -102,19 +120,55 @@ config MACH_NEXCODER_2440 endmenu +config S3C2410_CLOCK + bool + help + Clock code for the S3C2410, and similar processors + config CPU_S3C2410 bool depends on ARCH_S3C2410 + select S3C2410_CLOCK help Support for S3C2410 and S3C2410A family from the S3C24XX line of Samsung Mobile CPUs. +# internal node to signify if we are only dealing with an S3C2412 + +config CPU_S3C2412_ONLY + bool + depends on ARCH_S3C2410 && !CPU_S3C2400 && !CPU_S3C2410 && \ + !CPU_S3C2440 && !CPU_S3C2442 && CPU_S3C2412 + default y if CPU_S3C2412 + +config CPU_S3C2412 + bool + depends on ARCH_S3C2410 + help + Support for the S3C2412 and S3C2413 SoCs from the S3C24XX line + +config CPU_S3C244X + bool + depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) + help + Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. + config CPU_S3C2440 bool depends on ARCH_S3C2410 + select S3C2410_CLOCK + select CPU_S3C244X help Support for S3C2440 Samsung Mobile CPU based systems. +config CPU_S3C2442 + bool + depends on ARCH_S3C2420 + select S3C2410_CLOCK + 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 3e5712db6b5..0c7938645df 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -24,6 +24,20 @@ obj-$(CONFIG_S3C2410_DMA) += dma.o obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o +# S3C2412 support +obj-$(CONFIG_CPU_S3C2412) += s3c2412.o +obj-$(CONFIG_CPU_S3C2412) += s3c2412-clock.o + +# +# S3C244X support + +obj-$(CONFIG_CPU_S3C244X) += s3c244x.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o + +# Clock control + +obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o + # S3C2440 support obj-$(CONFIG_CPU_S3C2440) += s3c2440.o s3c2440-dsc.o @@ -31,6 +45,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 @@ -43,6 +62,7 @@ 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_MACH_SMDK2413) += mach-smdk2413.o obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index 6de713ad319..e13fb677889 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -3,7 +3,7 @@ * Copyright (c) 2004-2005 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * - * S3C2410 Clock control support + * S3C24XX Core clock control support * * Based on, and code from linux/arch/arm/mach-versatile/clock.c ** @@ -56,25 +56,6 @@ static LIST_HEAD(clocks); DEFINE_MUTEX(clocks_mutex); -/* old functions */ - -void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable) -{ - unsigned long clkcon; - - clkcon = __raw_readl(S3C2410_CLKCON); - - if (enable) - clkcon |= clocks; - else - clkcon &= ~clocks; - - /* ensure none of the special function bits set */ - clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); - - __raw_writel(clkcon, S3C2410_CLKCON); -} - /* enable and disable calls for use with the clk struct */ static int clk_null_enable(struct clk *clk, int enable) @@ -82,12 +63,6 @@ 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) @@ -173,8 +148,11 @@ unsigned long clk_get_rate(struct clk *clk) if (clk->rate != 0) return clk->rate; - while (clk->parent != NULL && clk->rate == 0) - clk = clk->parent; + if (clk->get_rate != NULL) + return (clk->get_rate)(clk); + + if (clk->parent != NULL) + return clk_get_rate(clk->parent); return clk->rate; } @@ -233,31 +211,9 @@ EXPORT_SYMBOL(clk_set_rate); EXPORT_SYMBOL(clk_get_parent); EXPORT_SYMBOL(clk_set_parent); -/* base clock enable */ - -static int s3c24xx_upll_enable(struct clk *clk, int enable) -{ - unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); - unsigned long orig = clkslow; - - if (enable) - clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; - else - clkslow |= S3C2410_CLKSLOW_UCLK_OFF; - - __raw_writel(clkslow, S3C2410_CLKSLOW); - - /* if we started the UPLL, then allow to settle */ - - if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) - udelay(200); - - return 0; -} - /* base clocks */ -static struct clk clk_xtal = { +struct clk clk_xtal = { .name = "xtal", .id = -1, .rate = 0, @@ -265,23 +221,27 @@ static struct clk clk_xtal = { .ctrlbit = 0, }; -static struct clk clk_upll = { +struct clk clk_mpll = { + .name = "mpll", + .id = -1, +}; + +struct clk clk_upll = { .name = "upll", .id = -1, .parent = NULL, - .enable = s3c24xx_upll_enable, .ctrlbit = 0, }; -static struct clk clk_f = { +struct clk clk_f = { .name = "fclk", .id = -1, .rate = 0, - .parent = NULL, + .parent = &clk_mpll, .ctrlbit = 0, }; -static struct clk clk_h = { +struct clk clk_h = { .name = "hclk", .id = -1, .rate = 0, @@ -289,7 +249,7 @@ static struct clk clk_h = { .ctrlbit = 0, }; -static struct clk clk_p = { +struct clk clk_p = { .name = "pclk", .id = -1, .rate = 0, @@ -308,14 +268,14 @@ struct clk clk_usb_bus = { static int s3c24xx_dclk_enable(struct clk *clk, int enable) { - unsigned long dclkcon = __raw_readl(S3C2410_DCLKCON); + unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); if (enable) dclkcon |= clk->ctrlbit; else dclkcon &= ~clk->ctrlbit; - __raw_writel(dclkcon, S3C2410_DCLKCON); + __raw_writel(dclkcon, S3C24XX_DCLKCON); return 0; } @@ -334,7 +294,7 @@ static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) clk->parent = parent; - dclkcon = __raw_readl(S3C2410_DCLKCON); + dclkcon = __raw_readl(S3C24XX_DCLKCON); if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { if (uclk) @@ -348,7 +308,7 @@ static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; } - __raw_writel(dclkcon, S3C2410_DCLKCON); + __raw_writel(dclkcon, S3C24XX_DCLKCON); return 0; } @@ -426,108 +386,6 @@ struct clk s3c24xx_uclk = { .id = -1, }; - -/* standard 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) @@ -537,14 +395,6 @@ int s3c24xx_register_clock(struct clk *clk) if (clk->enable == NULL) clk->enable = clk_null_enable; - /* if this is a standard clock, set the usage state */ - - if (clk->ctrlbit && clk->enable == s3c24xx_clkcon_enable) { - unsigned long clkcon = __raw_readl(S3C2410_CLKCON); - - clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0; - } - /* add to the list of available clocks */ mutex_lock(&clocks_mutex); @@ -561,44 +411,18 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, unsigned long hclk, unsigned long pclk) { - unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); - unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); - struct clk *clkp = init_clocks; - int ptr; - int ret; - - printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n"); + printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); /* initialise the main system clocks */ clk_xtal.rate = xtal; - clk_upll.rate = s3c2410_get_pll(upllcon, xtal); + clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + clk_mpll.rate = fclk; clk_h.rate = hclk; clk_p.rate = pclk; clk_f.rate = fclk; - /* We must be careful disabling the clocks we are not intending to - * be using at boot time, as subsytems such as the LCD which do - * their own DMA requests to the bus can cause the system to lockup - * if they where in the middle of requesting bus access. - * - * Disabling the LCD clock if the LCD is active is very dangerous, - * and therefore the bootloader should be careful to not enable - * the LCD clock if it is not needed. - */ - - mutex_lock(&clocks_mutex); - - 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); - - mutex_unlock(&clocks_mutex); - /* assume uart clocks are correctly setup */ /* register our clocks */ @@ -606,6 +430,9 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, if (s3c24xx_register_clock(&clk_xtal) < 0) printk(KERN_ERR "failed to register master xtal\n"); + if (s3c24xx_register_clock(&clk_mpll) < 0) + printk(KERN_ERR "failed to register mpll clock\n"); + if (s3c24xx_register_clock(&clk_upll) < 0) printk(KERN_ERR "failed to register upll clock\n"); @@ -618,27 +445,5 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, if (s3c24xx_register_clock(&clk_p) < 0) printk(KERN_ERR "failed to register cpu pclk\n"); - - if (s3c24xx_register_clock(&clk_usb_bus) < 0) - printk(KERN_ERR "failed to register usb bus clock\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); - } - } - - /* show the clock-slow value */ - - printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", - print_mhz(xtal / ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), - (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", - (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", - (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); - return 0; } diff --git a/arch/arm/mach-s3c2410/clock.h b/arch/arm/mach-s3c2410/clock.h index 01bb458bf8e..7f0ea03e1d4 100644 --- a/arch/arm/mach-s3c2410/clock.h +++ b/arch/arm/mach-s3c2410/clock.h @@ -22,6 +22,7 @@ struct clk { int (*enable)(struct clk *, int enable); int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); unsigned long (*round_rate)(struct clk *c, unsigned long rate); int (*set_parent)(struct clk *c, struct clk *parent); }; @@ -36,6 +37,15 @@ extern struct clk s3c24xx_uclk; extern struct clk clk_usb_bus; +/* core clock support */ + +extern struct clk clk_f; +extern struct clk clk_h; +extern struct clk clk_p; +extern struct clk clk_mpll; +extern struct clk clk_upll; +extern struct clk clk_xtal; + /* exports for arch/arm/mach-s3c2410 * * Please DO NOT use these outside of arch/arm/mach-s3c2410 @@ -43,7 +53,8 @@ extern struct clk clk_usb_bus; extern struct mutex clocks_mutex; -extern int s3c24xx_clkcon_enable(struct clk *clk, int enable); +extern int s3c2410_clkcon_enable(struct clk *clk, int enable); + extern int s3c24xx_register_clock(struct clk *clk); extern int s3c24xx_setup_clocks(unsigned long xtal, diff --git a/arch/arm/mach-s3c2410/common-smdk.c b/arch/arm/mach-s3c2410/common-smdk.c index c940890f621..a40eaa65617 100644 --- a/arch/arm/mach-s3c2410/common-smdk.c +++ b/arch/arm/mach-s3c2410/common-smdk.c @@ -34,6 +34,7 @@ #include <asm/irq.h> #include <asm/arch/regs-gpio.h> +#include <asm/arch/leds-gpio.h> #include <asm/arch/nand.h> @@ -41,6 +42,66 @@ #include "devs.h" #include "pm.h" +/* LED devices */ + +static struct s3c24xx_led_platdata smdk_pdata_led4 = { + .gpio = S3C2410_GPF4, + .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, + .name = "led4", + .def_trigger = "timer", +}; + +static struct s3c24xx_led_platdata smdk_pdata_led5 = { + .gpio = S3C2410_GPF5, + .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, + .name = "led5", + .def_trigger = "nand-disk", +}; + +static struct s3c24xx_led_platdata smdk_pdata_led6 = { + .gpio = S3C2410_GPF6, + .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, + .name = "led6", +}; + +static struct s3c24xx_led_platdata smdk_pdata_led7 = { + .gpio = S3C2410_GPF7, + .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, + .name = "led7", +}; + +static struct platform_device smdk_led4 = { + .name = "s3c24xx_led", + .id = 0, + .dev = { + .platform_data = &smdk_pdata_led4, + }, +}; + +static struct platform_device smdk_led5 = { + .name = "s3c24xx_led", + .id = 1, + .dev = { + .platform_data = &smdk_pdata_led5, + }, +}; + +static struct platform_device smdk_led6 = { + .name = "s3c24xx_led", + .id = 2, + .dev = { + .platform_data = &smdk_pdata_led6, + }, +}; + +static struct platform_device smdk_led7 = { + .name = "s3c24xx_led", + .id = 3, + .dev = { + .platform_data = &smdk_pdata_led7, + }, +}; + /* NAND parititon from 2.4.18-swl5 */ static struct mtd_partition smdk_default_nand_part[] = { @@ -111,6 +172,10 @@ static struct s3c2410_platform_nand smdk_nand_info = { static struct platform_device __initdata *smdk_devs[] = { &s3c_device_nand, + &smdk_led4, + &smdk_led5, + &smdk_led6, + &smdk_led7, }; void __init smdk_machine_init(void) diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/mach-s3c2410/cpu.c index 70c34fcf785..1c3c6adae6c 100644 --- a/arch/arm/mach-s3c2410/cpu.c +++ b/arch/arm/mach-s3c2410/cpu.c @@ -37,12 +37,17 @@ #include <asm/mach/map.h> #include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-serial.h> #include "cpu.h" +#include "devs.h" #include "clock.h" #include "s3c2400.h" #include "s3c2410.h" +#include "s3c2412.h" +#include "s3c244x.h" #include "s3c2440.h" +#include "s3c2442.h" struct cpu_table { unsigned long idcode; @@ -58,7 +63,9 @@ struct cpu_table { static const char name_s3c2400[] = "S3C2400"; static const char name_s3c2410[] = "S3C2410"; +static const char name_s3c2412[] = "S3C2412"; static const char name_s3c2440[] = "S3C2440"; +static const char name_s3c2442[] = "S3C2442"; static const char name_s3c2410a[] = "S3C2410A"; static const char name_s3c2440a[] = "S3C2440A"; @@ -84,22 +91,40 @@ 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 = 0x32412001, + .idmask = 0xffffffff, + .map_io = s3c2412_map_io, + .init_clocks = s3c2412_init_clocks, + .init_uarts = s3c2412_init_uarts, + .init = s3c2412_init, + .name = name_s3c2412, + }, + { .idcode = 0x0, /* S3C2400 doesn't have an idcode */ .idmask = 0xffffffff, .map_io = s3c2400_map_io, @@ -157,6 +182,24 @@ void s3c24xx_set_board(struct s3c24xx_board *b) static struct cpu_table *cpu; +static unsigned long s3c24xx_read_idcode_v5(void) +{ +#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) + return __raw_readl(S3C2412_GSTATUS1); +#else + return 1UL; /* don't look like an 2400 */ +#endif +} + +static unsigned long s3c24xx_read_idcode_v4(void) +{ +#ifndef CONFIG_CPU_S3C2400 + return __raw_readl(S3C2410_GSTATUS1); +#else + return 0UL; +#endif +} + void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) { unsigned long idcode = 0x0; @@ -164,9 +207,11 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) /* initialise the io descriptors we need for initialisation */ iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); -#ifndef CONFIG_CPU_S3C2400 - idcode = __raw_readl(S3C2410_GSTATUS1); -#endif + if (cpu_architecture() >= CPU_ARCH_ARMv5) { + idcode = s3c24xx_read_idcode_v5(); + } else { + idcode = s3c24xx_read_idcode_v4(); + } cpu = s3c_lookup_cpu(idcode); @@ -175,13 +220,13 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) panic("Unknown S3C24XX CPU"); } + printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); + 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); } @@ -208,6 +253,49 @@ void __init s3c24xx_init_clocks(int xtal) (cpu->init_clocks)(xtal); } +/* uart management */ + +static int nr_uarts __initdata = 0; + +static struct s3c2410_uartcfg uart_cfgs[3]; + +/* s3c24xx_init_uartdevs + * + * copy the specified platform data and configuration into our central + * set of devices, before the data is thrown away after the init process. + * + * This also fills in the array passed to the serial driver for the + * early initialisation of the console. +*/ + +void __init s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no) +{ + struct platform_device *platdev; + struct s3c2410_uartcfg *cfgptr = uart_cfgs; + struct s3c24xx_uart_resources *resp; + int uart; + + memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); + + for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { + platdev = s3c24xx_uart_src[cfgptr->hwport]; + + resp = res + cfgptr->hwport; + + s3c24xx_uart_devs[uart] = platdev; + + platdev->name = name; + platdev->resource = resp->resources; + platdev->num_resources = resp->nr_resources; + + platdev->dev.platform_data = cfgptr; + } + + nr_uarts = no; +} + void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) { if (cpu == NULL) @@ -232,6 +320,10 @@ static int __init s3c_arch_init(void) if (ret != 0) return ret; + ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); + if (ret != 0) + return ret; + if (board != NULL) { struct platform_device **ptr = board->devices; int i; diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h index fc1067783f6..b0ed9d2d141 100644 --- a/arch/arm/mach-s3c2410/cpu.h +++ b/arch/arm/mach-s3c2410/cpu.h @@ -31,6 +31,8 @@ #define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) /* forward declaration */ +struct s3c24xx_uart_resources; +struct platform_device; struct s3c2410_uartcfg; struct map_desc; @@ -44,6 +46,10 @@ extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void s3c24xx_init_clocks(int xtal); +extern void s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no); + /* the board structure is used at first initialsation time * to get info such as the devices to register for this * board. This is done because platfrom_add_devices() cannot @@ -67,4 +73,7 @@ extern struct sys_timer s3c24xx_timer; /* system device classes */ +extern struct sysdev_class s3c2410_sysclass; +extern struct sysdev_class s3c2412_sysclass; extern struct sysdev_class s3c2440_sysclass; +extern struct sysdev_class s3c2442_sysclass; diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/mach-s3c2410/devs.c index ca09ba516e4..ad3845e329b 100644 --- a/arch/arm/mach-s3c2410/devs.c +++ b/arch/arm/mach-s3c2410/devs.c @@ -38,10 +38,86 @@ #include <asm/arch/regs-serial.h> #include "devs.h" +#include "cpu.h" /* Serial port registrations */ -struct platform_device *s3c24xx_uart_devs[3]; +static struct resource s3c2410_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 s3c2410_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 s3c2410_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, + } +}; + +struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = { + [0] = { + .resources = s3c2410_uart0_resource, + .nr_resources = ARRAY_SIZE(s3c2410_uart0_resource), + }, + [1] = { + .resources = s3c2410_uart1_resource, + .nr_resources = ARRAY_SIZE(s3c2410_uart1_resource), + }, + [2] = { + .resources = s3c2410_uart2_resource, + .nr_resources = ARRAY_SIZE(s3c2410_uart2_resource), + }, +}; + +/* yart devices */ + +static struct platform_device s3c24xx_uart_device0 = { + .id = 0, +}; + +static struct platform_device s3c24xx_uart_device1 = { + .id = 1, +}; + +static struct platform_device s3c24xx_uart_device2 = { + .id = 2, +}; + +struct platform_device *s3c24xx_uart_src[3] = { + &s3c24xx_uart_device0, + &s3c24xx_uart_device1, + &s3c24xx_uart_device2, +}; + +struct platform_device *s3c24xx_uart_devs[3] = { +}; /* USB Host Controller */ diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h index 52c4bab5c76..fa124ed920e 100644 --- a/arch/arm/mach-s3c2410/devs.h +++ b/arch/arm/mach-s3c2410/devs.h @@ -17,7 +17,15 @@ #include <linux/config.h> #include <linux/platform_device.h> +struct s3c24xx_uart_resources { + struct resource *resources; + unsigned long nr_resources; +}; + +extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; + extern struct platform_device *s3c24xx_uart_devs[]; +extern struct platform_device *s3c24xx_uart_src[]; extern struct platform_device s3c_device_usb; extern struct platform_device s3c_device_lcd; diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c index 66d8c068e94..6822dc7f779 100644 --- a/arch/arm/mach-s3c2410/irq.c +++ b/arch/arm/mach-s3c2410/irq.c @@ -191,13 +191,9 @@ static struct irqchip s3c_irq_chip = { .ack = s3c_irq_ack, .mask = s3c_irq_mask, .unmask = s3c_irq_unmask, - .set_wake = s3c_irq_wake + .set_wake = s3c_irq_wake }; -/* S3C2410_EINTMASK - * S3C2410_EINTPEND - */ - static void s3c_irqext_mask(unsigned int irqno) { @@ -205,9 +201,9 @@ s3c_irqext_mask(unsigned int irqno) irqno -= EXTINT_OFF; - mask = __raw_readl(S3C2410_EINTMASK); + mask = __raw_readl(S3C24XX_EINTMASK); mask |= ( 1UL << irqno); - __raw_writel(mask, S3C2410_EINTMASK); + __raw_writel(mask, S3C24XX_EINTMASK); if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) { /* check to see if all need masking */ @@ -232,11 +228,11 @@ s3c_irqext_ack(unsigned int irqno) bit = 1UL << (irqno - EXTINT_OFF); - mask = __raw_readl(S3C2410_EINTMASK); + mask = __raw_readl(S3C24XX_EINTMASK); - __raw_writel(bit, S3C2410_EINTPEND); + __raw_writel(bit, S3C24XX_EINTPEND); - req = __raw_readl(S3C2410_EINTPEND); + req = __raw_readl(S3C24XX_EINTPEND); req &= ~mask; /* not sure if we should be acking the parent irq... */ @@ -257,9 +253,9 @@ s3c_irqext_unmask(unsigned int irqno) irqno -= EXTINT_OFF; - mask = __raw_readl(S3C2410_EINTMASK); + mask = __raw_readl(S3C24XX_EINTMASK); mask &= ~( 1UL << irqno); - __raw_writel(mask, S3C2410_EINTMASK); + __raw_writel(mask, S3C24XX_EINTMASK); s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23); } @@ -275,28 +271,28 @@ s3c_irqext_type(unsigned int irq, unsigned int type) if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) { gpcon_reg = S3C2410_GPFCON; - extint_reg = S3C2410_EXTINT0; + extint_reg = S3C24XX_EXTINT0; gpcon_offset = (irq - IRQ_EINT0) * 2; extint_offset = (irq - IRQ_EINT0) * 4; } else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) { gpcon_reg = S3C2410_GPFCON; - extint_reg = S3C2410_EXTINT0; + extint_reg = S3C24XX_EXTINT0; gpcon_offset = (irq - (EXTINT_OFF)) * 2; extint_offset = (irq - (EXTINT_OFF)) * 4; } else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) { gpcon_reg = S3C2410_GPGCON; - extint_reg = S3C2410_EXTINT1; + extint_reg = S3C24XX_EXTINT1; gpcon_offset = (irq - IRQ_EINT8) * 2; extint_offset = (irq - IRQ_EINT8) * 4; } else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) { gpcon_reg = S3C2410_GPGCON; - extint_reg = S3C2410_EXTINT2; + extint_reg = S3C24XX_EXTINT2; gpcon_offset = (irq - IRQ_EINT8) * 2; extint_offset = (irq - IRQ_EINT16) * 4; } else @@ -572,6 +568,23 @@ s3c_irq_demux_uart2(unsigned int irq, s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs); } +static void +s3c_irq_demux_extint(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND); + unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK); + + eintpnd &= ~eintmsk; + + if (eintpnd) { + irq = fls(eintpnd); + irq += (IRQ_EINT4 - (4 + 1)); + + desc_handle_irq(irq, irq_desc + irq, regs); + } +} /* s3c24xx_init_irq * @@ -591,12 +604,12 @@ void __init s3c24xx_init_irq(void) last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_EINTPEND); + pend = __raw_readl(S3C24XX_EINTPEND); if (pend == 0 || pend == last) break; - __raw_writel(pend, S3C2410_EINTPEND); + __raw_writel(pend, S3C24XX_EINTPEND); printk("irq: clearing pending ext status %08x\n", (int)pend); last = pend; } @@ -630,12 +643,14 @@ void __init s3c24xx_init_irq(void) irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); - for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) { + for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { /* deal with the special IRQs (cascaded) */ + case IRQ_EINT4t7: + case IRQ_EINT8t23: case IRQ_UART0: case IRQ_UART1: case IRQ_UART2: @@ -659,12 +674,14 @@ void __init s3c24xx_init_irq(void) /* setup the cascade irq handlers */ + set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint); + set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint); + 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++) { diff --git a/arch/arm/mach-s3c2410/mach-anubis.c b/arch/arm/mach-s3c2410/mach-anubis.c index cc97fbf6629..4a92d6f92d6 100644 --- a/arch/arm/mach-s3c2410/mach-anubis.c +++ b/arch/arm/mach-s3c2410/mach-anubis.c @@ -131,7 +131,7 @@ static struct s3c24xx_uart_clksrc anubis_serial_clocks[] = { }; -static struct s3c2410_uartcfg anubis_uartcfgs[] = { +static struct s3c2410_uartcfg anubis_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, @@ -239,6 +239,54 @@ static struct s3c2410_platform_nand anubis_nand_info = { .select_chip = anubis_nand_select, }; +/* IDE channels */ + +static struct resource anubis_ide0_resource[] = { + { + .start = S3C2410_CS3, + .end = S3C2410_CS3 + (8*32) - 1, + .flags = IORESOURCE_MEM, + }, { + .start = S3C2410_CS3 + (1<<26), + .end = S3C2410_CS3 + (1<<26) + (8*32) - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_IDE0, + .end = IRQ_IDE0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device anubis_device_ide0 = { + .name = "simtec-ide", + .id = 0, + .num_resources = ARRAY_SIZE(anubis_ide0_resource), + .resource = anubis_ide0_resource, +}; + +static struct resource anubis_ide1_resource[] = { + { + .start = S3C2410_CS4, + .end = S3C2410_CS4 + (8*32) - 1, + .flags = IORESOURCE_MEM, + }, { + .start = S3C2410_CS4 + (1<<26), + .end = S3C2410_CS4 + (1<<26) + (8*32) - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_IDE0, + .end = IRQ_IDE0, + .flags = IORESOURCE_IRQ, + }, +}; + + +static struct platform_device anubis_device_ide1 = { + .name = "simtec-ide", + .id = 1, + .num_resources = ARRAY_SIZE(anubis_ide1_resource), + .resource = anubis_ide1_resource, +}; /* Standard Anubis devices */ @@ -249,6 +297,8 @@ static struct platform_device *anubis_devices[] __initdata = { &s3c_device_i2c, &s3c_device_rtc, &s3c_device_nand, + &anubis_device_ide0, + &anubis_device_ide1, }; static struct clk *anubis_clocks[] = { diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c index 995bb8add33..947234df816 100644 --- a/arch/arm/mach-s3c2410/mach-bast.c +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -208,7 +208,7 @@ static struct s3c24xx_uart_clksrc bast_serial_clocks[] = { }; -static struct s3c2410_uartcfg bast_uartcfgs[] = { +static struct s3c2410_uartcfg bast_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 646a3a5d33a..aec431b2830 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -72,7 +72,7 @@ static struct map_desc h1940_iodesc[] __initdata = { #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[] = { +static struct s3c2410_uartcfg h1940_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2410/mach-nexcoder.c index 07d09509a62..065a1d4e860 100644 --- a/arch/arm/mach-s3c2410/mach-nexcoder.c +++ b/arch/arm/mach-s3c2410/mach-nexcoder.c @@ -51,7 +51,7 @@ static struct map_desc nexcoder_iodesc[] __initdata = { #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[] = { +static struct s3c2410_uartcfg nexcoder_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-osiris.c b/arch/arm/mach-s3c2410/mach-osiris.c index ae078755775..858fd03c6bc 100644 --- a/arch/arm/mach-s3c2410/mach-osiris.c +++ b/arch/arm/mach-s3c2410/mach-osiris.c @@ -95,8 +95,7 @@ static struct s3c24xx_uart_clksrc osiris_serial_clocks[] = { } }; - -static struct s3c2410_uartcfg osiris_uartcfgs[] = { +static struct s3c2410_uartcfg osiris_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, @@ -107,7 +106,7 @@ static struct s3c2410_uartcfg osiris_uartcfgs[] = { .clocks_size = ARRAY_SIZE(osiris_serial_clocks) }, [1] = { - .hwport = 2, + .hwport = 1, .flags = 0, .ucon = UCON, .ulcon = ULCON, diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c index b39daedf93c..c71673fd995 100644 --- a/arch/arm/mach-s3c2410/mach-otom.c +++ b/arch/arm/mach-s3c2410/mach-otom.c @@ -45,7 +45,7 @@ static struct map_desc otom11_iodesc[] __initdata = { #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[] = { +static struct s3c2410_uartcfg otom11_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index 2db932d72c5..25f7e9f4dce 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -65,7 +65,7 @@ static struct map_desc smdk2410_iodesc[] __initdata = { #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[] = { +static struct s3c2410_uartcfg smdk2410_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-smdk2413.c b/arch/arm/mach-s3c2410/mach-smdk2413.c new file mode 100644 index 00000000000..b7ef7d3c54a --- /dev/null +++ b/arch/arm/mach-s3c2410/mach-smdk2413.c @@ -0,0 +1,126 @@ +/* linux/arch/arm/mach-s3c2410/mach-smdk2413.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Thanks to Dimity Andric (TomTom) and Steven Ryu (Samsung) for the + * loans of SMDK2413 to work with. + * + * 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 <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/setup.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-lcd.h> + +#include <asm/arch/idle.h> +#include <asm/arch/fb.h> + +#include "s3c2410.h" +#include "s3c2412.h" +#include "clock.h" +#include "devs.h" +#include "cpu.h" + +#include "common-smdk.h" + +static struct map_desc smdk2413_iodesc[] __initdata = { +}; + +static struct s3c2410_uartcfg smdk2413_uartcfgs[] __initdata = { + [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 *smdk2413_devices[] __initdata = { + &s3c_device_usb, + //&s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c24xx_board smdk2413_board __initdata = { + .devices = smdk2413_devices, + .devices_count = ARRAY_SIZE(smdk2413_devices) +}; + +static void __init smdk2413_fixup(struct machine_desc *desc, + struct tag *tags, char **cmdline, + struct meminfo *mi) +{ + if (tags != phys_to_virt(S3C2410_SDRAM_PA + 0x100)) { + mi->nr_banks=1; + mi->bank[0].start = 0x30000000; + mi->bank[0].size = SZ_64M; + mi->bank[0].node = 0; + } +} + +static void __init smdk2413_map_io(void) +{ + s3c24xx_init_io(smdk2413_iodesc, ARRAY_SIZE(smdk2413_iodesc)); + s3c24xx_init_clocks(12000000); + s3c24xx_init_uarts(smdk2413_uartcfgs, ARRAY_SIZE(smdk2413_uartcfgs)); + s3c24xx_set_board(&smdk2413_board); +} + +static void __init smdk2413_machine_init(void) +{ + smdk_machine_init(); +} + +MACHINE_START(S3C2413, "SMDK2413") + /* Maintainer: Ben Dooks <ben@fluff.org> */ + .phys_io = S3C2410_PA_UART, + .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, + .boot_params = S3C2410_SDRAM_PA + 0x100, + + .fixup = smdk2413_fixup, + .init_irq = s3c24xx_init_irq, + .map_io = smdk2413_map_io, + .init_machine = smdk2413_machine_init, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c index 5fffd1d5104..d661c6b7ff5 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2440.c +++ b/arch/arm/mach-s3c2410/mach-smdk2440.c @@ -86,7 +86,7 @@ static struct map_desc smdk2440_iodesc[] __initdata = { #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[] = { +static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c index 785fc9cdcf7..d18efb279d3 100644 --- a/arch/arm/mach-s3c2410/mach-vr1000.c +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -166,7 +166,7 @@ static struct s3c24xx_uart_clksrc vr1000_serial_clocks[] = { } }; -static struct s3c2410_uartcfg vr1000_uartcfgs[] = { +static struct s3c2410_uartcfg vr1000_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, diff --git a/arch/arm/mach-s3c2410/pm-simtec.c b/arch/arm/mach-s3c2410/pm-simtec.c index 4c7ccef6c20..7b244566a43 100644 --- a/arch/arm/mach-s3c2410/pm-simtec.c +++ b/arch/arm/mach-s3c2410/pm-simtec.c @@ -48,7 +48,8 @@ static __init int pm_simtec_init(void) /* check which machine we are running on */ - if (!machine_is_bast() && !machine_is_vr1000() && !machine_is_anubis()) + if (!machine_is_bast() && !machine_is_vr1000() && + !machine_is_anubis() && !machine_is_osiris()) return 0; printk(KERN_INFO "Simtec Board Power Manangement" COPYRIGHT "\n"); diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c index fe57d966a34..43e9a550a20 100644 --- a/arch/arm/mach-s3c2410/pm.c +++ b/arch/arm/mach-s3c2410/pm.c @@ -58,7 +58,11 @@ unsigned long s3c_pm_flags; /* cache functions from arch/arm/mm/proc-arm920.S */ +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH extern void arm920_flush_kern_cache_all(void); +#else +static void arm920_flush_kern_cache_all(void) { } +#endif #define PFX "s3c24xx-pm: " diff --git a/arch/arm/mach-s3c2410/s3c2410-clock.c b/arch/arm/mach-s3c2410/s3c2410-clock.c new file mode 100644 index 00000000000..99718663318 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2410-clock.c @@ -0,0 +1,271 @@ +/* linux/arch/arm/mach-s3c2410/clock.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410,S3C2440,S3C2442 Clock control support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#include <asm/hardware.h> +#include <asm/io.h> + +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> + +#include "clock.h" +#include "cpu.h" + +int s3c2410_clkcon_enable(struct clk *clk, int enable) +{ + unsigned int clocks = clk->ctrlbit; + unsigned long clkcon; + + clkcon = __raw_readl(S3C2410_CLKCON); + + if (enable) + clkcon |= clocks; + else + clkcon &= ~clocks; + + /* ensure none of the special function bits set */ + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + + __raw_writel(clkcon, S3C2410_CLKCON); + + return 0; +} + +static int s3c2410_upll_enable(struct clk *clk, int enable) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long orig = clkslow; + + if (enable) + clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; + else + clkslow |= S3C2410_CLKSLOW_UCLK_OFF; + + __raw_writel(clkslow, S3C2410_CLKSLOW); + + /* if we started the UPLL, then allow to settle */ + + if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) + udelay(200); + + return 0; +} + +/* standard clock definitions */ + +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_NAND, + }, { + .name = "sdi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SDI, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_ADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIC, + }, { + .name = "iis", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIS, + }, { + .name = "spi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SPI, + } +}; + +static struct clk init_clocks[] = { + { + .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_LCDC, + }, { + .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_GPIO, + }, { + .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBH, + }, { + .name = "usb-device", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBD, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_PWMT, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART2, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = 0, + }, { + .name = "usb-bus-host", + .id = -1, + .parent = &clk_usb_bus, + }, { + .name = "usb-bus-gadget", + .id = -1, + .parent = &clk_usb_bus, + }, +}; + +/* s3c2410_baseclk_add() + * + * Add all the clocks used by the s3c2410 or compatible CPUs + * such as the S3C2440 and S3C2442. + * + * We cannot use a system device as we are needed before any + * of the init-calls that initialise the devices are actually + * done. +*/ + +int __init s3c2410_baseclk_add(void) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long clkcon = __raw_readl(S3C2410_CLKCON); + struct clk *clkp; + struct clk *xtal; + int ret; + int ptr; + + clk_upll.enable = s3c2410_upll_enable; + + if (s3c24xx_register_clock(&clk_usb_bus) < 0) + printk(KERN_ERR "failed to register usb bus clock\n"); + + /* register clocks from clock array */ + + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + /* ensure that we note the clock state */ + + clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + /* We must be careful disabling the clocks we are not intending to + * be using at boot time, as subsytems such as the LCD which do + * their own DMA requests to the bus can cause the system to lockup + * if they where in the middle of requesting bus access. + * + * Disabling the LCD clock if the LCD is active is very dangerous, + * and therefore the bootloader should be careful to not enable + * the LCD clock if it is not needed. + */ + + /* install (and disable) the clocks we do not need immediately */ + + clkp = init_clocks_disable; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + + s3c2410_clkcon_enable(clkp, 0); + } + + /* show the clock-slow value */ + + xtal = clk_get(NULL, "xtal"); + + printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", + print_mhz(clk_get_rate(xtal) / + ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), + (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", + (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", + (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); + + return 0; +} diff --git a/arch/arm/mach-s3c2410/s3c2410-gpio.c b/arch/arm/mach-s3c2410/s3c2410-gpio.c index d5e1caea1d2..471a7149001 100644 --- a/arch/arm/mach-s3c2410/s3c2410-gpio.c +++ b/arch/arm/mach-s3c2410/s3c2410-gpio.c @@ -18,9 +18,6 @@ * 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 - * 15-Jan-2006 LCVR Splitted from gpio.c */ #include <linux/kernel.h> @@ -38,7 +35,7 @@ int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, unsigned int config) { - void __iomem *reg = S3C2410_EINFLT0; + void __iomem *reg = S3C24XX_EINFLT0; unsigned long flags; unsigned long val; @@ -47,7 +44,7 @@ int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, config &= 0xff; - pin -= S3C2410_GPG8_EINT16; + pin -= S3C2410_GPG8; reg += pin & ~3; local_irq_save(flags); @@ -61,10 +58,10 @@ int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, /* update filter enable */ - val = __raw_readl(S3C2410_EXTINT2); + val = __raw_readl(S3C24XX_EXTINT2); val &= ~(1 << ((pin * 4) + 3)); val |= on << ((pin * 4) + 3); - __raw_writel(val, S3C2410_EXTINT2); + __raw_writel(val, S3C24XX_EXTINT2); local_irq_restore(flags); @@ -75,7 +72,7 @@ EXPORT_SYMBOL(s3c2410_gpio_irqfilter); int s3c2410_gpio_getirq(unsigned int pin) { - if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15_EINT23) + if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15) return -1; /* not valid interrupts */ if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index 0a2013a7654..a110cff9cf6 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -27,6 +27,7 @@ #include <linux/list.h> #include <linux/timer.h> #include <linux/init.h> +#include <linux/sysdev.h> #include <linux/platform_device.h> #include <asm/mach/arch.h> @@ -42,6 +43,7 @@ #include "s3c2410.h" #include "cpu.h" +#include "devs.h" #include "clock.h" /* Initial IO mappings */ @@ -55,93 +57,13 @@ static struct map_desc s3c2410_iodesc[] __initdata = { 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 -}; - -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; + s3c24xx_init_uartdevs("s3c2410-uart", s3c2410_uart_resources, cfg, no); } /* s3c2410_map_io @@ -187,11 +109,33 @@ void __init s3c2410_init_clocks(int xtal) */ s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); + s3c2410_baseclk_add(); } +struct sysdev_class s3c2410_sysclass = { + set_kset_name("s3c2410-core"), +}; + +static struct sys_device s3c2410_sysdev = { + .cls = &s3c2410_sysclass, +}; + +/* 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 s3c2410_core_init(void) +{ + return sysdev_class_register(&s3c2410_sysclass); +} + +core_initcall(s3c2410_core_init); + int __init s3c2410_init(void) { printk("S3C2410: Initialising architecture\n"); - return platform_add_devices(s3c24xx_uart_devs, s3c2410_uart_count); + return sysdev_register(&s3c2410_sysdev); } diff --git a/arch/arm/mach-s3c2410/s3c2410.h b/arch/arm/mach-s3c2410/s3c2410.h index 4d5312a4820..73f1a2474a6 100644 --- a/arch/arm/mach-s3c2410/s3c2410.h +++ b/arch/arm/mach-s3c2410/s3c2410.h @@ -29,6 +29,8 @@ extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void s3c2410_init_clocks(int xtal); +extern int s3c2410_baseclk_add(void); + #else #define s3c2410_init_clocks NULL #define s3c2410_init_uarts NULL diff --git a/arch/arm/mach-s3c2410/s3c2412-clock.c b/arch/arm/mach-s3c2410/s3c2412-clock.c new file mode 100644 index 00000000000..c95ed3e1858 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2412-clock.c @@ -0,0 +1,711 @@ +/* linux/arch/arm/mach-s3c2410/s3c2412-clock.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2412,S3C2413 Clock control support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#include <asm/hardware.h> +#include <asm/io.h> + +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> + +#include "clock.h" +#include "cpu.h" + +/* We currently have to assume that the system is running + * from the XTPll input, and that all ***REFCLKs are being + * fed from it, as we cannot read the state of OM[4] from + * software. + * + * It would be possible for each board initialisation to + * set the correct muxing at initialisation +*/ + +int s3c2412_clkcon_enable(struct clk *clk, int enable) +{ + unsigned int clocks = clk->ctrlbit; + unsigned long clkcon; + + clkcon = __raw_readl(S3C2410_CLKCON); + + if (enable) + clkcon |= clocks; + else + clkcon &= ~clocks; + + __raw_writel(clkcon, S3C2410_CLKCON); + + return 0; +} + +static int s3c2412_upll_enable(struct clk *clk, int enable) +{ + unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); + unsigned long orig = upllcon; + + if (!enable) + upllcon |= S3C2412_PLLCON_OFF; + else + upllcon &= ~S3C2412_PLLCON_OFF; + + __raw_writel(upllcon, S3C2410_UPLLCON); + + /* allow ~150uS for the PLL to settle and lock */ + + if (enable && (orig & S3C2412_PLLCON_OFF)) + udelay(150); + + return 0; +} + +/* clock selections */ + +/* CPU EXTCLK input */ +static struct clk clk_ext = { + .name = "extclk", + .id = -1, +}; + +static struct clk clk_erefclk = { + .name = "erefclk", + .id = -1, +}; + +static struct clk clk_urefclk = { + .name = "urefclk", + .id = -1, +}; + +static int s3c2412_setparent_usysclk(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_urefclk) + clksrc &= ~S3C2412_CLKSRC_USYSCLK_UPLL; + else if (parent == &clk_upll) + clksrc |= S3C2412_CLKSRC_USYSCLK_UPLL; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} + +static struct clk clk_usysclk = { + .name = "usysclk", + .id = -1, + .parent = &clk_xtal, + .set_parent = s3c2412_setparent_usysclk, +}; + +static struct clk clk_mrefclk = { + .name = "mrefclk", + .parent = &clk_xtal, + .id = -1, +}; + +static struct clk clk_mdivclk = { + .name = "mdivclk", + .parent = &clk_xtal, + .id = -1, +}; + +static int s3c2412_setparent_usbsrc(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_usysclk) + clksrc &= ~S3C2412_CLKSRC_USBCLK_HCLK; + else if (parent == &clk_h) + clksrc |= S3C2412_CLKSRC_USBCLK_HCLK; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} + +static unsigned long s3c2412_roundrate_usbsrc(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 > 2) + div = 2; + + return parent_rate / div; +} + +static unsigned long s3c2412_getrate_usbsrc(struct clk *clk) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long div = __raw_readl(S3C2410_CLKDIVN); + + return parent_rate / ((div & S3C2412_CLKDIVN_USB48DIV) ? 2 : 1); +} + +static int s3c2412_setrate_usbsrc(struct clk *clk, unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN); + + rate = s3c2412_roundrate_usbsrc(clk, rate); + + if ((parent_rate / rate) == 2) + clkdivn |= S3C2412_CLKDIVN_USB48DIV; + else + clkdivn &= ~S3C2412_CLKDIVN_USB48DIV; + + __raw_writel(clkdivn, S3C2410_CLKDIVN); + return 0; +} + +static struct clk clk_usbsrc = { + .name = "usbsrc", + .id = -1, + .get_rate = s3c2412_getrate_usbsrc, + .set_rate = s3c2412_setrate_usbsrc, + .round_rate = s3c2412_roundrate_usbsrc, + .set_parent = s3c2412_setparent_usbsrc, +}; + +static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_mdivclk) + clksrc &= ~S3C2412_CLKSRC_MSYSCLK_MPLL; + else if (parent == &clk_upll) + clksrc |= S3C2412_CLKSRC_MSYSCLK_MPLL; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} + +static struct clk clk_msysclk = { + .name = "msysclk", + .id = -1, + .set_parent = s3c2412_setparent_msysclk, +}; + +/* these next clocks have an divider immediately after them, + * so we can register them with their divider and leave out the + * intermediate clock stage +*/ +static unsigned long s3c2412_roundrate_clksrc(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + int div; + + if (rate > parent_rate) + return parent_rate; + + /* note, we remove the +/- 1 calculations as they cancel out */ + + div = (rate / parent_rate); + + if (div < 1) + div = 1; + else if (div > 16) + div = 16; + + return parent_rate / div; +} + +static int s3c2412_setparent_uart(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_erefclk) + clksrc &= ~S3C2412_CLKSRC_UARTCLK_MPLL; + else if (parent == &clk_mpll) + clksrc |= S3C2412_CLKSRC_UARTCLK_MPLL; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} + +static unsigned long s3c2412_getrate_uart(struct clk *clk) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long div = __raw_readl(S3C2410_CLKDIVN); + + div &= S3C2412_CLKDIVN_UARTDIV_MASK; + div >>= S3C2412_CLKDIVN_UARTDIV_SHIFT; + + return parent_rate / (div + 1); +} + +static int s3c2412_setrate_uart(struct clk *clk, unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN); + + rate = s3c2412_roundrate_clksrc(clk, rate); + + clkdivn &= ~S3C2412_CLKDIVN_UARTDIV_MASK; + clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_UARTDIV_SHIFT; + + __raw_writel(clkdivn, S3C2410_CLKDIVN); + return 0; +} + +static struct clk clk_uart = { + .name = "uartclk", + .id = -1, + .get_rate = s3c2412_getrate_uart, + .set_rate = s3c2412_setrate_uart, + .set_parent = s3c2412_setparent_uart, + .round_rate = s3c2412_roundrate_clksrc, +}; + +static int s3c2412_setparent_i2s(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_erefclk) + clksrc &= ~S3C2412_CLKSRC_I2SCLK_MPLL; + else if (parent == &clk_mpll) + clksrc |= S3C2412_CLKSRC_I2SCLK_MPLL; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} + +static unsigned long s3c2412_getrate_i2s(struct clk *clk) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long div = __raw_readl(S3C2410_CLKDIVN); + + div &= S3C2412_CLKDIVN_I2SDIV_MASK; + div >>= S3C2412_CLKDIVN_I2SDIV_SHIFT; + + return parent_rate / (div + 1); +} + +static int s3c2412_setrate_i2s(struct clk *clk, unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN); + + rate = s3c2412_roundrate_clksrc(clk, rate); + + clkdivn &= ~S3C2412_CLKDIVN_I2SDIV_MASK; + clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_I2SDIV_SHIFT; + + __raw_writel(clkdivn, S3C2410_CLKDIVN); + return 0; +} + +static struct clk clk_i2s = { + .name = "i2sclk", + .id = -1, + .get_rate = s3c2412_getrate_i2s, + .set_rate = s3c2412_setrate_i2s, + .set_parent = s3c2412_setparent_i2s, + .round_rate = s3c2412_roundrate_clksrc, +}; + +static int s3c2412_setparent_cam(struct clk *clk, struct clk *parent) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + + if (parent == &clk_usysclk) + clksrc &= ~S3C2412_CLKSRC_CAMCLK_HCLK; + else if (parent == &clk_h) + clksrc |= S3C2412_CLKSRC_CAMCLK_HCLK; + else + return -EINVAL; + + clk->parent = parent; + + __raw_writel(clksrc, S3C2412_CLKSRC); + return 0; +} +static unsigned long s3c2412_getrate_cam(struct clk *clk) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long div = __raw_readl(S3C2410_CLKDIVN); + + div &= S3C2412_CLKDIVN_CAMDIV_MASK; + div >>= S3C2412_CLKDIVN_CAMDIV_SHIFT; + + return parent_rate / (div + 1); +} + +static int s3c2412_setrate_cam(struct clk *clk, unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN); + + rate = s3c2412_roundrate_clksrc(clk, rate); + + clkdivn &= ~S3C2412_CLKDIVN_CAMDIV_MASK; + clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_CAMDIV_SHIFT; + + __raw_writel(clkdivn, S3C2410_CLKDIVN); + return 0; +} + +static struct clk clk_cam = { + .name = "camif-upll", /* same as 2440 name */ + .id = -1, + .get_rate = s3c2412_getrate_cam, + .set_rate = s3c2412_setrate_cam, + .set_parent = s3c2412_setparent_cam, + .round_rate = s3c2412_roundrate_clksrc, +}; + +/* standard clock definitions */ + +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_NAND, + }, { + .name = "sdi", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_SDI, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_ADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_IIC, + }, { + .name = "iis", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_IIS, + }, { + .name = "spi", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_SPI, + } +}; + +static struct clk init_clocks[] = { + { + .name = "dma", + .id = 0, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_DMA0, + }, { + .name = "dma", + .id = 1, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_DMA1, + }, { + .name = "dma", + .id = 2, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_DMA2, + }, { + .name = "dma", + .id = 3, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_DMA3, + }, { + .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_LCDC, + }, { + .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_GPIO, + }, { + .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_USBH, + }, { + .name = "usb-device", + .id = -1, + .parent = &clk_h, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_USBD, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_PWMT, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_UART2, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = 0, + }, { + .name = "usb-bus-gadget", + .id = -1, + .parent = &clk_usb_bus, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_USB_DEV48, + }, { + .name = "usb-bus-host", + .id = -1, + .parent = &clk_usb_bus, + .enable = s3c2412_clkcon_enable, + .ctrlbit = S3C2412_CLKCON_USB_HOST48, + } +}; + +/* clocks to add where we need to check their parentage */ + +struct clk_init { + struct clk *clk; + unsigned int bit; + struct clk *src_0; + struct clk *src_1; +}; + +struct clk_init clks_src[] __initdata = { + { + .clk = &clk_usysclk, + .bit = S3C2412_CLKSRC_USBCLK_HCLK, + .src_0 = &clk_urefclk, + .src_1 = &clk_upll, + }, { + .clk = &clk_i2s, + .bit = S3C2412_CLKSRC_I2SCLK_MPLL, + .src_0 = &clk_erefclk, + .src_1 = &clk_mpll, + }, { + .clk = &clk_cam, + .bit = S3C2412_CLKSRC_CAMCLK_HCLK, + .src_0 = &clk_usysclk, + .src_1 = &clk_h, + }, { + .clk = &clk_msysclk, + .bit = S3C2412_CLKSRC_MSYSCLK_MPLL, + .src_0 = &clk_mdivclk, + .src_1 = &clk_mpll, + }, { + .clk = &clk_uart, + .bit = S3C2412_CLKSRC_UARTCLK_MPLL, + .src_0 = &clk_erefclk, + .src_1 = &clk_mpll, + }, { + .clk = &clk_usbsrc, + .bit = S3C2412_CLKSRC_USBCLK_HCLK, + .src_0 = &clk_usysclk, + .src_1 = &clk_h, + }, +}; + +/* s3c2412_clk_initparents + * + * Initialise the parents for the clocks that we get at start-time +*/ + +static void __init s3c2412_clk_initparents(void) +{ + unsigned long clksrc = __raw_readl(S3C2412_CLKSRC); + struct clk_init *cip = clks_src; + struct clk *src; + int ptr; + int ret; + + for (ptr = 0; ptr < ARRAY_SIZE(clks_src); ptr++, cip++) { + ret = s3c24xx_register_clock(cip->clk); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + cip->clk->name, ret); + } + + src = (clksrc & cip->bit) ? cip->src_1 : cip->src_0; + + printk(KERN_INFO "%s: parent %s\n", cip->clk->name, src->name); + clk_set_parent(cip->clk, src); + } +} + +/* clocks to add straight away */ + +struct clk *clks[] __initdata = { + &clk_ext, + &clk_usb_bus, + &clk_erefclk, + &clk_urefclk, + &clk_mrefclk, +}; + +int __init s3c2412_baseclk_add(void) +{ + unsigned long clkcon = __raw_readl(S3C2410_CLKCON); + struct clk *clkp; + int ret; + int ptr; + + clk_upll.enable = s3c2412_upll_enable; + clk_usb_bus.parent = &clk_usbsrc; + clk_usb_bus.rate = 0x0; + + s3c2412_clk_initparents(); + + for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) { + clkp = clks[ptr]; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + /* ensure usb bus clock is within correct rate of 48MHz */ + + if (clk_get_rate(&clk_usb_bus) != (48 * 1000 * 1000)) { + printk(KERN_INFO "Warning: USB bus clock not at 48MHz\n"); + + /* for the moment, let's use the UPLL, and see if we can + * get 48MHz */ + + clk_set_parent(&clk_usysclk, &clk_upll); + clk_set_parent(&clk_usbsrc, &clk_usysclk); + clk_set_rate(&clk_usbsrc, 48*1000*1000); + } + + printk("S3C2412: upll %s, %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n", + (__raw_readl(S3C2410_UPLLCON) & S3C2412_PLLCON_OFF) ? "off":"on", + print_mhz(clk_get_rate(&clk_upll)), + print_mhz(clk_get_rate(&clk_usb_bus))); + + /* register clocks from clock array */ + + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + /* ensure that we note the clock state */ + + clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + /* We must be careful disabling the clocks we are not intending to + * be using at boot time, as subsytems such as the LCD which do + * their own DMA requests to the bus can cause the system to lockup + * if they where in the middle of requesting bus access. + * + * Disabling the LCD clock if the LCD is active is very dangerous, + * and therefore the bootloader should be careful to not enable + * the LCD clock if it is not needed. + */ + + /* install (and disable) the clocks we do not need immediately */ + + clkp = init_clocks_disable; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + + s3c2412_clkcon_enable(clkp, 0); + } + + return 0; +} diff --git a/arch/arm/mach-s3c2410/s3c2412.c b/arch/arm/mach-s3c2410/s3c2412.c new file mode 100644 index 00000000000..e24ffd5e478 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2412.c @@ -0,0 +1,195 @@ +/* linux/arch/arm/mach-s3c2410/s3c2412.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://armlinux.simtec.co.uk/. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * 13-Aug-2005 DA Removed UART from initial I/O mappings +*/ + +#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/sysdev.h> +#include <linux/platform_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 <asm/arch/regs-gpio.h> +#include <asm/arch/regs-gpioj.h> +#include <asm/arch/regs-dsc.h> + +#include "s3c2412.h" +#include "cpu.h" +#include "devs.h" +#include "clock.h" +#include "pm.h" + +#ifndef CONFIG_CPU_S3C2412_ONLY +void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO; +#endif + +/* Initial IO mappings */ + +static struct map_desc s3c2412_iodesc[] __initdata = { + IODESC_ENT(CLKPWR), + IODESC_ENT(LCD), + IODESC_ENT(TIMER), + IODESC_ENT(ADC), + IODESC_ENT(WATCHDOG), +}; + +/* uart registration process */ + +void __init s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + s3c24xx_init_uartdevs("s3c2412-uart", s3c2410_uart_resources, cfg, no); + + /* rename devices that are s3c2412/s3c2413 specific */ + s3c_device_sdi.name = "s3c2412-sdi"; + s3c_device_nand.name = "s3c2412-nand"; +} + +/* s3c2412_map_io + * + * register the standard cpu IO areas, and any passed in from the + * machine specific initialisation. +*/ + +void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size) +{ + /* move base of IO */ + + s3c24xx_va_gpio2 = S3C24XX_VA_GPIO + 0x10; + + /* register our io-tables */ + + iotable_init(s3c2412_iodesc, ARRAY_SIZE(s3c2412_iodesc)); + iotable_init(mach_desc, mach_size); +} + +void __init s3c2412_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*2); + + tmp = __raw_readl(S3C2410_CLKDIVN); + + /* work out clock scalings */ + + hclk = fclk / ((tmp & S3C2412_CLKDIVN_HDIVN_MASK) + 1); + hclk /= ((tmp & S3C2421_CLKDIVN_ARMDIVN) ? 2 : 1); + pclk = hclk / ((tmp & S3C2412_CLKDIVN_PDIVN) ? 2 : 1); + + /* print brieft summary of clocks, etc */ + + printk("S3C2412: 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); + s3c2412_baseclk_add(); +} + +/* 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 s3c2412 based system) + * as a driver which may support both 2410 and 2440 may try and use it. +*/ + +#ifdef CONFIG_PM +static struct sleep_save s3c2412_sleep[] = { + SAVE_ITEM(S3C2412_DSC0), + SAVE_ITEM(S3C2412_DSC1), + SAVE_ITEM(S3C2413_GPJDAT), + SAVE_ITEM(S3C2413_GPJCON), + SAVE_ITEM(S3C2413_GPJUP), + + /* save the sleep configuration anyway, just in case these + * get damaged during wakeup */ + + SAVE_ITEM(S3C2412_GPBSLPCON), + SAVE_ITEM(S3C2412_GPCSLPCON), + SAVE_ITEM(S3C2412_GPDSLPCON), + SAVE_ITEM(S3C2412_GPESLPCON), + SAVE_ITEM(S3C2412_GPFSLPCON), + SAVE_ITEM(S3C2412_GPGSLPCON), + SAVE_ITEM(S3C2412_GPHSLPCON), + SAVE_ITEM(S3C2413_GPJSLPCON), +}; + +static int s3c2412_suspend(struct sys_device *dev, pm_message_t state) +{ + s3c2410_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); + return 0; +} + +static int s3c2412_resume(struct sys_device *dev) +{ + s3c2410_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep)); + return 0; +} + +#else +#define s3c2412_suspend NULL +#define s3c2412_resume NULL +#endif + +struct sysdev_class s3c2412_sysclass = { + set_kset_name("s3c2412-core"), + .suspend = s3c2412_suspend, + .resume = s3c2412_resume +}; + +static int __init s3c2412_core_init(void) +{ + return sysdev_class_register(&s3c2412_sysclass); +} + +core_initcall(s3c2412_core_init); + +static struct sys_device s3c2412_sysdev = { + .cls = &s3c2412_sysclass, +}; + +int __init s3c2412_init(void) +{ + printk("S3C2412: Initialising architecture\n"); + + return sysdev_register(&s3c2412_sysdev); +} diff --git a/arch/arm/mach-s3c2410/s3c2412.h b/arch/arm/mach-s3c2410/s3c2412.h new file mode 100644 index 00000000000..c6e56032a6e --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2412.h @@ -0,0 +1,29 @@ +/* arch/arm/mach-s3c2410/s3c2412.h + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Header file for s3c2412 cpu support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifdef CONFIG_CPU_S3C2412 + +extern int s3c2412_init(void); + +extern void s3c2412_map_io(struct map_desc *mach_desc, int size); + +extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c2412_init_clocks(int xtal); + +extern int s3c2412_baseclk_add(void); +#else +#define s3c2412_init_clocks NULL +#define s3c2412_init_uarts NULL +#define s3c2412_map_io NULL +#define s3c2412_init NULL +#endif diff --git a/arch/arm/mach-s3c2410/s3c2440-clock.c b/arch/arm/mach-s3c2410/s3c2440-clock.c index d7a30ed6c32..15796864d01 100644 --- a/arch/arm/mach-s3c2410/s3c2440-clock.c +++ b/arch/arm/mach-s3c2410/s3c2440-clock.c @@ -91,7 +91,7 @@ static int s3c2440_camif_upll_setrate(struct clk *clk, unsigned long rate) static struct clk s3c2440_clk_cam = { .name = "camif", .id = -1, - .enable = s3c24xx_clkcon_enable, + .enable = s3c2410_clkcon_enable, .ctrlbit = S3C2440_CLKCON_CAMERA, }; @@ -105,7 +105,7 @@ static struct clk s3c2440_clk_cam_upll = { static struct clk s3c2440_clk_ac97 = { .name = "ac97", .id = -1, - .enable = s3c24xx_clkcon_enable, + .enable = s3c2410_clkcon_enable, .ctrlbit = S3C2440_CLKCON_CAMERA, }; diff --git a/arch/arm/mach-s3c2410/s3c2440-irq.c b/arch/arm/mach-s3c2410/s3c2440-irq.c index 278d0044c85..acfe3870727 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 b7fe6d9453f..0ab50f44f31 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,234 +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), -}; - -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 - -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) -{ - 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); + /* register our system device for everything else */ - return ret; + 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 00000000000..d9f54b5cab7 --- /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 = s3c2410_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 00000000000..debae243055 --- /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 00000000000..0ae37d24866 --- /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 00000000000..2aadca1ce7e --- /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 00000000000..838bc525e83 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c244x.c @@ -0,0 +1,184 @@ +/* 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 "s3c2410.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); + s3c2410_baseclk_add(); +} + +#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 00000000000..3e7f5f75134 --- /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 diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S index 73de2eaca22..5f6761ed96b 100644 --- a/arch/arm/mach-s3c2410/sleep.S +++ b/arch/arm/mach-s3c2410/sleep.S @@ -66,7 +66,9 @@ ENTRY(s3c2410_cpu_suspend) @@ flush the caches to ensure everything is back out to @@ SDRAM before the core powers down +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bl arm920_flush_kern_cache_all +#endif @@ prepare cpu to sleep |