diff options
Diffstat (limited to 'arch/arm/mach-s3c2440')
22 files changed, 2026 insertions, 42 deletions
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index 80879358eb2..7f465265cf0 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig @@ -15,14 +15,67 @@ config CPU_S3C2440 help Support for S3C2440 Samsung Mobile CPU based systems. +config CPU_S3C2442 + bool + depends on ARCH_S3C2410 + select CPU_ARM920T + select S3C2410_CLOCK + select S3C2410_GPIO + select S3C2410_PM if PM + select CPU_S3C244X + select CPU_LLSERIAL_S3C2440 + help + Support for S3C2442 Samsung Mobile CPU based systems. + +config CPU_S3C244X + bool + depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) + help + Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems. + + + +config S3C2440_CPUFREQ + bool "S3C2440/S3C2442 CPU Frequency scaling support" + depends on CPU_FREQ_S3C24XX && (CPU_S3C2440 || CPU_S3C2442) + select S3C2410_CPUFREQ_UTILS + default y + help + CPU Frequency scaling support for S3C2440 and S3C2442 SoC CPUs. + +config S3C2440_XTAL_12000000 + bool + help + Indicate that the build needs to support 12MHz system + crystal. + +config S3C2440_XTAL_16934400 + bool + help + Indicate that the build needs to support 16.9344MHz system + crystal. + +config S3C2440_PLL_12000000 + bool + depends on S3C2440_CPUFREQ && S3C2440_XTAL_12000000 + default y if CPU_FREQ_S3C24XX_PLL + help + PLL tables for S3C2440 or S3C2442 CPUs with 12MHz crystals. + +config S3C2440_PLL_16934400 + bool + depends on S3C2440_CPUFREQ && S3C2440_XTAL_16934400 + default y if CPU_FREQ_S3C24XX_PLL + help + PLL tables for S3C2440 or S3C2442 CPUs with 16.934MHz crystals. + config S3C2440_DMA bool depends on ARCH_S3C2410 && CPU_S3C24405B help Support for S3C2440 specific DMA code5A - -menu "S3C2440 Machines" +menu "S3C2440 and S3C2442 Machines" config MACH_ANUBIS bool "Simtec Electronics ANUBIS" @@ -37,6 +90,18 @@ config MACH_ANUBIS Say Y here if you are using the Simtec Electronics ANUBIS development system +config MACH_NEO1973_GTA02 + bool "Openmoko GTA02 / Freerunner phone" + select CPU_S3C2442 + select MFD_PCF50633 + select PCF50633_GPIO + select I2C + select POWER_SUPPLY + select MACH_NEO1973 + select S3C2410_PWM + help + Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone + config MACH_OSIRIS bool "Simtec IM2440D20 (OSIRIS) module" select CPU_S3C2440 @@ -94,11 +159,14 @@ config MACH_NEXCODER_2440 config SMDK2440_CPU2440 bool "SMDK2440 with S3C2440 CPU module" - depends on ARCH_S3C2440 default y if ARCH_S3C2440 select S3C2440_XTAL_16934400 select CPU_S3C2440 +config SMDK2440_CPU2442 + bool "SMDM2440 with S3C2442 CPU module" + select CPU_S3C2442 + config MACH_AT2440EVB bool "Avantech AT2440EVB development board" select CPU_S3C2440 diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile index 5f322453188..c85ba32d895 100644 --- a/arch/arm/mach-s3c2440/Makefile +++ b/arch/arm/mach-s3c2440/Makefile @@ -10,10 +10,20 @@ obj-n := obj- := obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o +obj-$(CONFIG_CPU_S3C2442) += s3c2442.o + obj-$(CONFIG_CPU_S3C2440) += irq.o obj-$(CONFIG_CPU_S3C2440) += clock.o obj-$(CONFIG_S3C2440_DMA) += dma.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o +obj-$(CONFIG_CPU_S3C244X) += s3c244x-clock.o +obj-$(CONFIG_S3C2440_CPUFREQ) += s3c2440-cpufreq.o + +obj-$(CONFIG_S3C2440_PLL_12000000) += s3c2440-pll-12000000.o +obj-$(CONFIG_S3C2440_PLL_16934400) += s3c2440-pll-16934400.o + # Machine support obj-$(CONFIG_MACH_ANUBIS) += mach-anubis.o @@ -23,6 +33,7 @@ obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o obj-$(CONFIG_MACH_AT2440EVB) += mach-at2440evb.o obj-$(CONFIG_MACH_MINI2440) += mach-mini2440.o +obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o # extra machine support diff --git a/arch/arm/mach-s3c2440/clock.c b/arch/arm/mach-s3c2440/clock.c index d1c29b2537c..3dc2426e234 100644 --- a/arch/arm/mach-s3c2440/clock.c +++ b/arch/arm/mach-s3c2440/clock.c @@ -98,8 +98,10 @@ static struct clk s3c2440_clk_cam = { static struct clk s3c2440_clk_cam_upll = { .name = "camif-upll", .id = -1, - .set_rate = s3c2440_camif_upll_setrate, - .round_rate = s3c2440_camif_upll_round, + .ops = &(struct clk_ops) { + .set_rate = s3c2440_camif_upll_setrate, + .round_rate = s3c2440_camif_upll_round, + }, }; static struct clk s3c2440_clk_ac97 = { diff --git a/arch/arm/mach-s3c2440/dma.c b/arch/arm/mach-s3c2440/dma.c index e08e081430f..3b0529f54e9 100644 --- a/arch/arm/mach-s3c2440/dma.c +++ b/arch/arm/mach-s3c2440/dma.c @@ -20,7 +20,7 @@ #include <mach/map.h> #include <mach/dma.h> -#include <plat/dma-plat.h> +#include <plat/dma-s3c24xx.h> #include <plat/cpu.h> #include <plat/regs-serial.h> diff --git a/arch/arm/mach-s3c2440/dsc.c b/arch/arm/mach-s3c2440/dsc.c index 55404427277..9ea66e31f62 100644 --- a/arch/arm/mach-s3c2440/dsc.c +++ b/arch/arm/mach-s3c2440/dsc.c @@ -28,7 +28,7 @@ #include <mach/regs-dsc.h> #include <plat/cpu.h> -#include <plat/s3c2440.h> +#include <plat/s3c244x.h> int s3c2440_set_dsc(unsigned int pin, unsigned int value) { diff --git a/arch/arm/mach-s3c2440/include/mach/gta02.h b/arch/arm/mach-s3c2440/include/mach/gta02.h new file mode 100644 index 00000000000..953331d8d56 --- /dev/null +++ b/arch/arm/mach-s3c2440/include/mach/gta02.h @@ -0,0 +1,84 @@ +#ifndef _GTA02_H +#define _GTA02_H + +#include <mach/regs-gpio.h> + +/* Different hardware revisions, passed in ATAG_REVISION by u-boot */ +#define GTA02v1_SYSTEM_REV 0x00000310 +#define GTA02v2_SYSTEM_REV 0x00000320 +#define GTA02v3_SYSTEM_REV 0x00000330 +#define GTA02v4_SYSTEM_REV 0x00000340 +#define GTA02v5_SYSTEM_REV 0x00000350 +/* since A7 is basically same as A6, we use A6 PCB ID */ +#define GTA02v6_SYSTEM_REV 0x00000360 + +#define GTA02_GPIO_n3DL_GSM S3C2410_GPA(13) /* v1 + v2 + v3 only */ + +#define GTA02_GPIO_PWR_LED1 S3C2410_GPB(0) +#define GTA02_GPIO_PWR_LED2 S3C2410_GPB(1) +#define GTA02_GPIO_AUX_LED S3C2410_GPB(2) +#define GTA02_GPIO_VIBRATOR_ON S3C2410_GPB(3) +#define GTA02_GPIO_MODEM_RST S3C2410_GPB(5) +#define GTA02_GPIO_BT_EN S3C2410_GPB(6) +#define GTA02_GPIO_MODEM_ON S3C2410_GPB(7) +#define GTA02_GPIO_EXTINT8 S3C2410_GPB(8) +#define GTA02_GPIO_USB_PULLUP S3C2410_GPB(9) + +#define GTA02_GPIO_PIO5 S3C2410_GPC(5) /* v3 + v4 only */ + +#define GTA02v3_GPIO_nG1_CS S3C2410_GPD(12) /* v3 + v4 only */ +#define GTA02v3_GPIO_nG2_CS S3C2410_GPD(13) /* v3 + v4 only */ +#define GTA02v5_GPIO_HDQ S3C2410_GPD(14) /* v5 + */ + +#define GTA02_GPIO_nG1_INT S3C2410_GPF(0) +#define GTA02_GPIO_IO1 S3C2410_GPF(1) +#define GTA02_GPIO_PIO_2 S3C2410_GPF(2) /* v2 + v3 + v4 only */ +#define GTA02_GPIO_JACK_INSERT S3C2410_GPF(4) +#define GTA02_GPIO_WLAN_GPIO1 S3C2410_GPF(5) /* v2 + v3 + v4 only */ +#define GTA02_GPIO_AUX_KEY S3C2410_GPF(6) +#define GTA02_GPIO_HOLD_KEY S3C2410_GPF(7) + +#define GTA02_GPIO_3D_IRQ S3C2410_GPG(4) +#define GTA02v2_GPIO_nG2_INT S3C2410_GPG(8) /* v2 + v3 + v4 only */ +#define GTA02v3_GPIO_nUSB_OC S3C2410_GPG(9) /* v3 + v4 only */ +#define GTA02v3_GPIO_nUSB_FLT S3C2410_GPG(10) /* v3 + v4 only */ +#define GTA02v3_GPIO_nGSM_OC S3C2410_GPG(11) /* v3 + v4 only */ + +#define GTA02_GPIO_AMP_SHUT S3C2440_GPJ1 /* v2 + v3 + v4 only */ +#define GTA02v1_GPIO_WLAN_GPIO10 S3C2440_GPJ2 +#define GTA02_GPIO_HP_IN S3C2440_GPJ2 /* v2 + v3 + v4 only */ +#define GTA02_GPIO_INT0 S3C2440_GPJ3 /* v2 + v3 + v4 only */ +#define GTA02_GPIO_nGSM_EN S3C2440_GPJ4 +#define GTA02_GPIO_3D_RESET S3C2440_GPJ5 +#define GTA02_GPIO_nDL_GSM S3C2440_GPJ6 /* v4 + v5 only */ +#define GTA02_GPIO_WLAN_GPIO0 S3C2440_GPJ7 +#define GTA02v1_GPIO_BAT_ID S3C2440_GPJ8 +#define GTA02_GPIO_KEEPACT S3C2440_GPJ8 +#define GTA02v1_GPIO_HP_IN S3C2440_GPJ10 +#define GTA02_CHIP_PWD S3C2440_GPJ11 /* v2 + v3 + v4 only */ +#define GTA02_GPIO_nWLAN_RESET S3C2440_GPJ12 /* v2 + v3 + v4 only */ + +#define GTA02_IRQ_GSENSOR_1 IRQ_EINT0 +#define GTA02_IRQ_MODEM IRQ_EINT1 +#define GTA02_IRQ_PIO_2 IRQ_EINT2 /* v2 + v3 + v4 only */ +#define GTA02_IRQ_nJACK_INSERT IRQ_EINT4 +#define GTA02_IRQ_WLAN_GPIO1 IRQ_EINT5 +#define GTA02_IRQ_AUX IRQ_EINT6 +#define GTA02_IRQ_nHOLD IRQ_EINT7 +#define GTA02_IRQ_PCF50633 IRQ_EINT9 +#define GTA02_IRQ_3D IRQ_EINT12 +#define GTA02_IRQ_GSENSOR_2 IRQ_EINT16 /* v2 + v3 + v4 only */ +#define GTA02v3_IRQ_nUSB_OC IRQ_EINT17 /* v3 + v4 only */ +#define GTA02v3_IRQ_nUSB_FLT IRQ_EINT18 /* v3 + v4 only */ +#define GTA02v3_IRQ_nGSM_OC IRQ_EINT19 /* v3 + v4 only */ + +/* returns 00 000 on GTA02 A5 and earlier, A6 returns 01 001 */ +#define GTA02_PCB_ID1_0 S3C2410_GPC(13) +#define GTA02_PCB_ID1_1 S3C2410_GPC(15) +#define GTA02_PCB_ID1_2 S3C2410_GPD(0) +#define GTA02_PCB_ID2_0 S3C2410_GPD(3) +#define GTA02_PCB_ID2_1 S3C2410_GPD(4) + +int gta02_get_pcb_revision(void); + +#endif /* _GTA02_H */ diff --git a/arch/arm/mach-s3c2440/mach-anubis.c b/arch/arm/mach-s3c2440/mach-anubis.c index 62a4c3eba97..b73f78a9da5 100644 --- a/arch/arm/mach-s3c2440/mach-anubis.c +++ b/arch/arm/mach-s3c2440/mach-anubis.c @@ -409,7 +409,7 @@ static struct platform_device anubis_device_sm501 = { /* Standard Anubis devices */ static struct platform_device *anubis_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_adc, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-at2440evb.c b/arch/arm/mach-s3c2440/mach-at2440evb.c index aa69290e04c..84725791e6b 100644 --- a/arch/arm/mach-s3c2440/mach-at2440evb.c +++ b/arch/arm/mach-s3c2440/mach-at2440evb.c @@ -165,7 +165,7 @@ static struct platform_device at2440evb_device_eth = { }, }; -static struct s3c24xx_mci_pdata at2440evb_mci_pdata = { +static struct s3c24xx_mci_pdata at2440evb_mci_pdata __initdata = { .gpio_detect = S3C2410_GPG(10), }; @@ -203,7 +203,7 @@ static struct s3c2410fb_mach_info at2440evb_fb_info __initdata = { }; static struct platform_device *at2440evb_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_adc, &s3c_device_i2c0, @@ -216,8 +216,6 @@ static struct platform_device *at2440evb_devices[] __initdata = { static void __init at2440evb_map_io(void) { - s3c_device_sdi.dev.platform_data = &at2440evb_mci_pdata; - s3c24xx_init_io(at2440evb_iodesc, ARRAY_SIZE(at2440evb_iodesc)); s3c24xx_init_clocks(16934400); s3c24xx_init_uarts(at2440evb_uartcfgs, ARRAY_SIZE(at2440evb_uartcfgs)); @@ -226,6 +224,7 @@ static void __init at2440evb_map_io(void) static void __init at2440evb_init(void) { s3c24xx_fb_set_platdata(&at2440evb_fb_info); + s3c24xx_mci_set_platdata(&at2440evb_mci_pdata); s3c_nand_set_platdata(&at2440evb_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c new file mode 100644 index 00000000000..45799c608d8 --- /dev/null +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -0,0 +1,647 @@ +/* + * linux/arch/arm/mach-s3c2442/mach-gta02.c + * + * S3C2442 Machine Support for Openmoko GTA02 / FreeRunner. + * + * Copyright (C) 2006-2009 by Openmoko, Inc. + * Authors: Harald Welte <laforge@openmoko.org> + * Andy Green <andy@openmoko.org> + * Werner Almesberger <werner@openmoko.org> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/spi/spi.h> + +#include <linux/mmc/host.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/io.h> + +#include <linux/i2c.h> +#include <linux/backlight.h> +#include <linux/regulator/machine.h> + +#include <linux/mfd/pcf50633/core.h> +#include <linux/mfd/pcf50633/mbc.h> +#include <linux/mfd/pcf50633/adc.h> +#include <linux/mfd/pcf50633/gpio.h> +#include <linux/mfd/pcf50633/pmic.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/irq.h> +#include <asm/mach-types.h> + +#include <mach/regs-irq.h> +#include <mach/regs-gpio.h> +#include <mach/regs-gpioj.h> +#include <mach/fb.h> + +#include <mach/spi.h> +#include <mach/spi-gpio.h> +#include <plat/usb-control.h> +#include <mach/regs-mem.h> +#include <mach/hardware.h> + +#include <mach/gta02.h> + +#include <plat/regs-serial.h> +#include <plat/nand.h> +#include <plat/devs.h> +#include <plat/cpu.h> +#include <plat/pm.h> +#include <plat/udc.h> +#include <plat/gpio-cfg.h> +#include <plat/iic.h> + +static struct pcf50633 *gta02_pcf; + +/* + * This gets called every 1ms when we paniced. + */ + +static long gta02_panic_blink(long count) +{ + long delay = 0; + static long last_blink; + static char led; + + /* Fast blink: 200ms period. */ + if (count - last_blink < 100) + return 0; + + led ^= 1; + gpio_direction_output(GTA02_GPIO_AUX_LED, led); + + last_blink = count; + + return delay; +} + + +static struct map_desc gta02_iodesc[] __initdata = { + { + .virtual = 0xe0000000, + .pfn = __phys_to_pfn(S3C2410_CS3 + 0x01000000), + .length = SZ_1M, + .type = MT_DEVICE + }, +}; + +#define UCON (S3C2410_UCON_DEFAULT | S3C2443_UCON_RXERR_IRQEN) +#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB) +#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE) + +static struct s3c2410_uartcfg gta02_uartcfgs[] = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = UCON, + .ulcon = ULCON, + .ufcon = UFCON, + }, +}; + +#ifdef CONFIG_CHARGER_PCF50633 +/* + * On GTA02 the 1A charger features a 48K resistor to 0V on the ID pin. + * We use this to recognize that we can pull 1A from the USB socket. + * + * These constants are the measured pcf50633 ADC levels with the 1A + * charger / 48K resistor, and with no pulldown resistor. + */ + +#define ADC_NOM_CHG_DETECT_1A 6 +#define ADC_NOM_CHG_DETECT_USB 43 + +static void +gta02_configure_pmu_for_charger(struct pcf50633 *pcf, void *unused, int res) +{ + int ma; + + /* Interpret charger type */ + if (res < ((ADC_NOM_CHG_DETECT_USB + ADC_NOM_CHG_DETECT_1A) / 2)) { + + /* + * Sanity - stop GPO driving out now that we have a 1A charger + * GPO controls USB Host power generation on GTA02 + */ + pcf50633_gpio_set(pcf, PCF50633_GPO, 0); + + ma = 1000; + } else + ma = 100; + + pcf50633_mbc_usb_curlim_set(pcf, ma); +} + +static struct delayed_work gta02_charger_work; +static int gta02_usb_vbus_draw; + +static void gta02_charger_worker(struct work_struct *work) +{ + if (gta02_usb_vbus_draw) { + pcf50633_mbc_usb_curlim_set(gta02_pcf, gta02_usb_vbus_draw); + return; + } + +#ifdef CONFIG_PCF50633_ADC + pcf50633_adc_async_read(gta02_pcf, + PCF50633_ADCC1_MUX_ADCIN1, + PCF50633_ADCC1_AVERAGE_16, + gta02_configure_pmu_for_charger, + NULL); +#else + /* + * If the PCF50633 ADC is disabled we fallback to a + * 100mA limit for safety. + */ + pcf50633_mbc_usb_curlim_set(pcf, 100); +#endif +} + +#define GTA02_CHARGER_CONFIGURE_TIMEOUT ((3000 * HZ) / 1000) + +static void gta02_pmu_event_callback(struct pcf50633 *pcf, int irq) +{ + if (irq == PCF50633_IRQ_USBINS) { + schedule_delayed_work(>a02_charger_work, + GTA02_CHARGER_CONFIGURE_TIMEOUT); + + return; + } + + if (irq == PCF50633_IRQ_USBREM) { + cancel_delayed_work_sync(>a02_charger_work); + gta02_usb_vbus_draw = 0; + } +} + +static void gta02_udc_vbus_draw(unsigned int ma) +{ + if (!gta02_pcf) + return; + + gta02_usb_vbus_draw = ma; + + schedule_delayed_work(>a02_charger_work, + GTA02_CHARGER_CONFIGURE_TIMEOUT); +} +#else /* !CONFIG_CHARGER_PCF50633 */ +#define gta02_pmu_event_callback NULL +#define gta02_udc_vbus_draw NULL +#endif + +/* + * This is called when pc50633 is probed, unfortunately quite late in the + * day since it is an I2C bus device. Here we can belatedly define some + * platform devices with the advantage that we can mark the pcf50633 as the + * parent. This makes them get suspended and resumed with their parent + * the pcf50633 still around. + */ + +static void gta02_pmu_attach_child_devices(struct pcf50633 *pcf); + + +static char *gta02_batteries[] = { + "battery", +}; + +struct pcf50633_platform_data gta02_pcf_pdata = { + .resumers = { + [0] = PCF50633_INT1_USBINS | + PCF50633_INT1_USBREM | + PCF50633_INT1_ALARM, + [1] = PCF50633_INT2_ONKEYF, + [2] = PCF50633_INT3_ONKEY1S, + [3] = PCF50633_INT4_LOWSYS | + PCF50633_INT4_LOWBAT | + PCF50633_INT4_HIGHTMP, + }, + + .batteries = gta02_batteries, + .num_batteries = ARRAY_SIZE(gta02_batteries), + + .charger_reference_current_ma = 1000, + + .reg_init_data = { + [PCF50633_REGULATOR_AUTO] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .always_on = 1, + .apply_uV = 1, + .state_mem = { + .enabled = 1, + }, + }, + }, + [PCF50633_REGULATOR_DOWN1] = { + .constraints = { + .min_uV = 1300000, + .max_uV = 1600000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .always_on = 1, + .apply_uV = 1, + }, + }, + [PCF50633_REGULATOR_DOWN2] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .enabled = 1, + }, + }, + }, + [PCF50633_REGULATOR_HCLDO] = { + .constraints = { + .min_uV = 2000000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .always_on = 1, + }, + }, + [PCF50633_REGULATOR_LDO1] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .state_mem = { + .enabled = 0, + }, + }, + }, + [PCF50633_REGULATOR_LDO2] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + }, + [PCF50633_REGULATOR_LDO3] = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + }, + [PCF50633_REGULATOR_LDO4] = { + .constraints = { + .min_uV = 3200000, + .max_uV = 3200000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + }, + [PCF50633_REGULATOR_LDO5] = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .state_mem = { + .enabled = 1, + }, + }, + }, + [PCF50633_REGULATOR_LDO6] = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + }, + }, + [PCF50633_REGULATOR_MEMLDO] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .state_mem = { + .enabled = 1, + }, + }, + }, + + }, + .probe_done = gta02_pmu_attach_child_devices, + .mbc_event_callback = gta02_pmu_event_callback, +}; + + +/* NOR Flash. */ + +#define GTA02_FLASH_BASE 0x18000000 /* GCS3 */ +#define GTA02_FLASH_SIZE 0x200000 /* 2MBytes */ + +static struct physmap_flash_data gta02_nor_flash_data = { + .width = 2, +}; + +static struct resource gta02_nor_flash_resource = { + .start = GTA02_FLASH_BASE, + .end = GTA02_FLASH_BASE + GTA02_FLASH_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device gta02_nor_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = >a02_nor_flash_data, + }, + .resource = >a02_nor_flash_resource, + .num_resources = 1, +}; + + +struct platform_device s3c24xx_pwm_device = { + .name = "s3c24xx_pwm", + .num_resources = 0, +}; + +static struct i2c_board_info gta02_i2c_devs[] __initdata = { + { + I2C_BOARD_INFO("pcf50633", 0x73), + .irq = GTA02_IRQ_PCF50633, + .platform_data = >a02_pcf_pdata, + }, + { + I2C_BOARD_INFO("wm8753", 0x1a), + }, +}; + +static struct s3c2410_nand_set __initdata gta02_nand_sets[] = { + [0] = { + /* + * This name is also hard-coded in the boot loaders, so + * changing it would would require all users to upgrade + * their boot loaders, some of which are stored in a NOR + * that is considered to be immutable. + */ + .name = "neo1973-nand", + .nr_chips = 1, + .flash_bbt = 1, + }, +}; + +/* + * Choose a set of timings derived from S3C@2442B MCP54 + * data sheet (K5D2G13ACM-D075 MCP Memory). + */ + +static struct s3c2410_platform_nand __initdata gta02_nand_info = { + .tacls = 0, + .twrph0 = 25, + .twrph1 = 15, + .nr_sets = ARRAY_SIZE(gta02_nand_sets), + .sets = gta02_nand_sets, +}; + + +static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd) +{ + switch (cmd) { + case S3C2410_UDC_P_ENABLE: + pr_debug("%s S3C2410_UDC_P_ENABLE\n", __func__); + gpio_direction_output(GTA02_GPIO_USB_PULLUP, 1); + break; + case S3C2410_UDC_P_DISABLE: + pr_debug("%s S3C2410_UDC_P_DISABLE\n", __func__); + gpio_direction_output(GTA02_GPIO_USB_PULLUP, 0); + break; + case S3C2410_UDC_P_RESET: + pr_debug("%s S3C2410_UDC_P_RESET\n", __func__); + /* FIXME: Do something here. */ + } +} + +/* Get PMU to set USB current limit accordingly. */ +static struct s3c2410_udc_mach_info gta02_udc_cfg = { + .vbus_draw = gta02_udc_vbus_draw, + .udc_command = gta02_udc_command, + +}; + + + +static void gta02_bl_set_intensity(int intensity) +{ + struct pcf50633 *pcf = gta02_pcf; + int old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT); + + /* We map 8-bit intensity to 6-bit intensity in hardware. */ + intensity >>= 2; + + /* + * This can happen during, eg, print of panic on blanked console, + * but we can't service i2c without interrupts active, so abort. + */ + if (in_atomic()) { + printk(KERN_ERR "gta02_bl_set_intensity called while atomic\n"); + return; + } + + old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT); + if (intensity == old_intensity) + return; + + /* We can't do this anywhere else. */ + pcf50633_reg_write(pcf, PCF50633_REG_LEDDIM, 5); + + if (!(pcf50633_reg_read(pcf, PCF50633_REG_LEDENA) & 3)) + old_intensity = 0; + + /* + * The PCF50633 cannot handle LEDOUT = 0 (datasheet p60) + * if seen, you have to re-enable the LED unit. + */ + if (!intensity || !old_intensity) + pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 0); + + /* Illegal to set LEDOUT to 0. */ + if (!intensity) + pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f, 2); + else + pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f, + intensity); + + if (intensity) + pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 2); + +} + +static struct generic_bl_info gta02_bl_info = { + .name = "gta02-bl", + .max_intensity = 0xff, + .default_intensity = 0xff, + .set_bl_intensity = gta02_bl_set_intensity, +}; + +static struct platform_device gta02_bl_dev = { + .name = "generic-bl", + .id = 1, + .dev = { + .platform_data = >a02_bl_info, + }, +}; + + + +/* USB */ +static struct s3c2410_hcd_info gta02_usb_info __initdata = { + .port[0] = { + .flags = S3C_HCDFLG_USED, + }, + .port[1] = { + .flags = 0, + }, +}; + + +static void __init gta02_map_io(void) +{ + s3c24xx_init_io(gta02_iodesc, ARRAY_SIZE(gta02_iodesc)); + s3c24xx_init_clocks(12000000); + s3c24xx_init_uarts(gta02_uartcfgs, ARRAY_SIZE(gta02_uartcfgs)); +} + + +/* These are the guys that don't need to be children of PMU. */ + +static struct platform_device *gta02_devices[] __initdata = { + &s3c_device_ohci, + &s3c_device_wdt, + &s3c_device_sdi, + &s3c_device_usbgadget, + &s3c_device_nand, + >a02_nor_flash, + &s3c24xx_pwm_device, + &s3c_device_iis, + &s3c_device_i2c0, +}; + +/* These guys DO need to be children of PMU. */ + +static struct platform_device *gta02_devices_pmu_children[] = { + >a02_bl_dev, +}; + + +/* + * This is called when pc50633 is probed, quite late in the day since it is an + * I2C bus device. Here we can define platform devices with the advantage that + * we can mark the pcf50633 as the parent. This makes them get suspended and + * resumed with their parent the pcf50633 still around. All devices whose + * operation depends on something from pcf50633 must have this relationship + * made explicit like this, or suspend and resume will become an unreliable + * hellworld. + */ + +static void gta02_pmu_attach_child_devices(struct pcf50633 *pcf) +{ + int n; + + /* Grab a copy of the now probed PMU pointer. */ + gta02_pcf = pcf; + + for (n = 0; n < ARRAY_SIZE(gta02_devices_pmu_children); n++) + gta02_devices_pmu_children[n]->dev.parent = pcf->dev; + + platform_add_devices(gta02_devices_pmu_children, + ARRAY_SIZE(gta02_devices_pmu_children)); +} + +static void gta02_poweroff(void) +{ + pcf50633_reg_set_bit_mask(gta02_pcf, PCF50633_REG_OOCSHDWN, 1, 1); +} + +static void __init gta02_machine_init(void) +{ + /* Set the panic callback to make AUX LED blink at ~5Hz. */ + panic_blink = gta02_panic_blink; + + s3c_pm_init(); + +#ifdef CONFIG_CHARGER_PCF50633 + INIT_DELAYED_WORK(>a02_charger_work, gta02_charger_worker); +#endif + + s3c24xx_udc_set_platdata(>a02_udc_cfg); + s3c_ohci_set_platdata(>a02_usb_info); + s3c_nand_set_platdata(>a02_nand_info); + s3c_i2c0_set_platdata(NULL); + + i2c_register_board_info(0, gta02_i2c_devs, ARRAY_SIZE(gta02_i2c_devs)); + + platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices)); + pm_power_off = gta02_poweroff; +} + + +MACHINE_START(NEO1973_GTA02, "GTA02") + /* Maintainer: Nelson Castillo <arhuaco@freaks-unidos.net> */ + .phys_io = S3C2410_PA_UART, + .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, + .boot_params = S3C2410_SDRAM_PA + 0x100, + .map_io = gta02_map_io, + .init_irq = s3c24xx_init_irq, + .init_machine = gta02_machine_init, + .timer = &s3c24xx_timer, +MACHINE_END diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index 547d4fc9913..571b17683d9 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -288,7 +288,7 @@ static struct s3c2410_platform_nand mini2440_nand_info __initdata = { /* DM9000AEP 10/100 ethernet controller */ -static struct resource mini2440_dm9k_resource[] __initdata = { +static struct resource mini2440_dm9k_resource[] = { [0] = { .start = MACH_MINI2440_DM9K_BASE, .end = MACH_MINI2440_DM9K_BASE + 3, @@ -310,11 +310,11 @@ static struct resource mini2440_dm9k_resource[] __initdata = { * The DM9000 has no eeprom, and it's MAC address is set by * the bootloader before starting the kernel. */ -static struct dm9000_plat_data mini2440_dm9k_pdata __initdata = { +static struct dm9000_plat_data mini2440_dm9k_pdata = { .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), }; -static struct platform_device mini2440_device_eth __initdata = { +static struct platform_device mini2440_device_eth = { .name = "dm9000", .id = -1, .num_resources = ARRAY_SIZE(mini2440_dm9k_resource), @@ -341,7 +341,7 @@ static struct platform_device mini2440_device_eth __initdata = { * | | +----+ +----+ * ..... */ -static struct gpio_keys_button mini2440_buttons[] __initdata = { +static struct gpio_keys_button mini2440_buttons[] = { { .gpio = S3C2410_GPG(0), /* K1 */ .code = KEY_F1, @@ -384,12 +384,12 @@ static struct gpio_keys_button mini2440_buttons[] __initdata = { #endif }; -static struct gpio_keys_platform_data mini2440_button_data __initdata = { +static struct gpio_keys_platform_data mini2440_button_data = { .buttons = mini2440_buttons, .nbuttons = ARRAY_SIZE(mini2440_buttons), }; -static struct platform_device mini2440_button_device __initdata = { +static struct platform_device mini2440_button_device = { .name = "gpio-keys", .id = -1, .dev = { @@ -399,41 +399,41 @@ static struct platform_device mini2440_button_device __initdata = { /* LEDS */ -static struct s3c24xx_led_platdata mini2440_led1_pdata __initdata = { +static struct s3c24xx_led_platdata mini2440_led1_pdata = { .name = "led1", .gpio = S3C2410_GPB(5), .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, .def_trigger = "heartbeat", }; -static struct s3c24xx_led_platdata mini2440_led2_pdata __initdata = { +static struct s3c24xx_led_platdata mini2440_led2_pdata = { .name = "led2", .gpio = S3C2410_GPB(6), .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, .def_trigger = "nand-disk", }; -static struct s3c24xx_led_platdata mini2440_led3_pdata __initdata = { +static struct s3c24xx_led_platdata mini2440_led3_pdata = { .name = "led3", .gpio = S3C2410_GPB(7), .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, .def_trigger = "mmc0", }; -static struct s3c24xx_led_platdata mini2440_led4_pdata __initdata = { +static struct s3c24xx_led_platdata mini2440_led4_pdata = { .name = "led4", .gpio = S3C2410_GPB(8), .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE, .def_trigger = "", }; -static struct s3c24xx_led_platdata mini2440_led_backlight_pdata __initdata = { +static struct s3c24xx_led_platdata mini2440_led_backlight_pdata = { .name = "backlight", .gpio = S3C2410_GPG(4), .def_trigger = "backlight", }; -static struct platform_device mini2440_led1 __initdata = { +static struct platform_device mini2440_led1 = { .name = "s3c24xx_led", .id = 1, .dev = { @@ -441,7 +441,7 @@ static struct platform_device mini2440_led1 __initdata = { }, }; -static struct platform_device mini2440_led2 __initdata = { +static struct platform_device mini2440_led2 = { .name = "s3c24xx_led", .id = 2, .dev = { @@ -449,7 +449,7 @@ static struct platform_device mini2440_led2 __initdata = { }, }; -static struct platform_device mini2440_led3 __initdata = { +static struct platform_device mini2440_led3 = { .name = "s3c24xx_led", .id = 3, .dev = { @@ -457,7 +457,7 @@ static struct platform_device mini2440_led3 __initdata = { }, }; -static struct platform_device mini2440_led4 __initdata = { +static struct platform_device mini2440_led4 = { .name = "s3c24xx_led", .id = 4, .dev = { @@ -465,7 +465,7 @@ static struct platform_device mini2440_led4 __initdata = { }, }; -static struct platform_device mini2440_led_backlight __initdata = { +static struct platform_device mini2440_led_backlight = { .name = "s3c24xx_led", .id = 5, .dev = { @@ -475,14 +475,14 @@ static struct platform_device mini2440_led_backlight __initdata = { /* AUDIO */ -static struct s3c24xx_uda134x_platform_data mini2440_audio_pins __initdata = { +static struct s3c24xx_uda134x_platform_data mini2440_audio_pins = { .l3_clk = S3C2410_GPB(4), .l3_mode = S3C2410_GPB(2), .l3_data = S3C2410_GPB(3), .model = UDA134X_UDA1341 }; -static struct platform_device mini2440_audio __initdata = { +static struct platform_device mini2440_audio = { .name = "s3c24xx_uda134x", .id = 0, .dev = { @@ -506,9 +506,8 @@ static struct i2c_board_info mini2440_i2c_devs[] __initdata = { }; static struct platform_device *mini2440_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, -/* &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */ &s3c_device_i2c0, &s3c_device_rtc, &s3c_device_usbgadget, @@ -522,8 +521,6 @@ static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_sdi, &s3c_device_iis, &mini2440_audio, -/* &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */ - /* remaining devices are optional */ }; static void __init mini2440_map_io(void) @@ -531,8 +528,6 @@ static void __init mini2440_map_io(void) s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc)); s3c24xx_init_clocks(12000000); s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs)); - - s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; } /* @@ -678,6 +673,7 @@ static void __init mini2440_init(void) } s3c24xx_udc_set_platdata(&mini2440_udc_cfg); + s3c24xx_mci_set_platdata(&mini2440_mmc_cfg); s3c_nand_set_platdata(&mini2440_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/mach-s3c2440/mach-nexcoder.c b/arch/arm/mach-s3c2440/mach-nexcoder.c index d43edede590..342041593f2 100644 --- a/arch/arm/mach-s3c2440/mach-nexcoder.c +++ b/arch/arm/mach-s3c2440/mach-nexcoder.c @@ -41,7 +41,7 @@ #include <plat/iic.h> #include <plat/s3c2410.h> -#include <plat/s3c2440.h> +#include <plat/s3c244x.h> #include <plat/clock.h> #include <plat/devs.h> #include <plat/cpu.h> @@ -106,7 +106,7 @@ static struct platform_device nexcoder_device_nor = { /* Standard Nexcoder devices */ static struct platform_device *nexcoder_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-osiris.c b/arch/arm/mach-s3c2440/mach-osiris.c index 015dfb2a80d..f35371db33f 100644 --- a/arch/arm/mach-s3c2440/mach-osiris.c +++ b/arch/arm/mach-s3c2440/mach-osiris.c @@ -208,6 +208,7 @@ static struct s3c2410_nand_set __initdata osiris_nand_sets[] = { .name = "External", .nr_chips = 1, .nr_map = external_map, + .options = NAND_SCAN_SILENT_NODEV, .nr_partitions = ARRAY_SIZE(osiris_default_nand_part), .partitions = osiris_default_nand_part, }, @@ -222,6 +223,7 @@ static struct s3c2410_nand_set __initdata osiris_nand_sets[] = { .name = "chip1", .nr_chips = 1, .nr_map = chip1_map, + .options = NAND_SCAN_SILENT_NODEV, .nr_partitions = ARRAY_SIZE(osiris_default_nand_part), .partitions = osiris_default_nand_part, }, diff --git a/arch/arm/mach-s3c2440/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c index a952a13afb1..1e836e506f8 100644 --- a/arch/arm/mach-s3c2440/mach-rx3715.c +++ b/arch/arm/mach-s3c2440/mach-rx3715.c @@ -176,7 +176,7 @@ static struct s3c2410_platform_nand __initdata rx3715_nand_info = { }; static struct platform_device *rx3715_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-smdk2440.c b/arch/arm/mach-s3c2440/mach-smdk2440.c index ec13e748ccc..3ac3d636d61 100644 --- a/arch/arm/mach-s3c2440/mach-smdk2440.c +++ b/arch/arm/mach-s3c2440/mach-smdk2440.c @@ -40,7 +40,7 @@ #include <plat/iic.h> #include <plat/s3c2410.h> -#include <plat/s3c2440.h> +#include <plat/s3c244x.h> #include <plat/clock.h> #include <plat/devs.h> #include <plat/cpu.h> @@ -150,7 +150,7 @@ static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = { }; static struct platform_device *smdk2440_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/s3c2440-cpufreq.c b/arch/arm/mach-s3c2440/s3c2440-cpufreq.c new file mode 100644 index 00000000000..976002fb1b8 --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c2440-cpufreq.c @@ -0,0 +1,311 @@ +/* linux/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c + * + * Copyright (c) 2006-2009 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * Vincent Sanders <vince@simtec.co.uk> + * + * S3C2440/S3C2442 CPU Frequency scaling + * + * 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/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/cpufreq.h> +#include <linux/sysdev.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/hardware.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <mach/regs-clock.h> + +#include <plat/cpu.h> +#include <plat/cpu-freq-core.h> +#include <plat/clock.h> + +static struct clk *xtal; +static struct clk *fclk; +static struct clk *hclk; +static struct clk *armclk; + +/* HDIV: 1, 2, 3, 4, 6, 8 */ + +static inline int within_khz(unsigned long a, unsigned long b) +{ + long diff = a - b; + + return (diff >= -1000 && diff <= 1000); +} + +/** + * s3c2440_cpufreq_calcdivs - calculate divider settings + * @cfg: The cpu frequency settings. + * + * Calcualte the divider values for the given frequency settings + * specified in @cfg. The values are stored in @cfg for later use + * by the relevant set routine if the request settings can be reached. + */ +int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) +{ + unsigned int hdiv, pdiv; + unsigned long hclk, fclk, armclk; + unsigned long hclk_max; + + fclk = cfg->freq.fclk; + armclk = cfg->freq.armclk; + hclk_max = cfg->max.hclk; + + s3c_freq_dbg("%s: fclk is %lu, armclk %lu, max hclk %lu\n", + __func__, fclk, armclk, hclk_max); + + if (armclk > fclk) { + printk(KERN_WARNING "%s: armclk > fclk\n", __func__); + armclk = fclk; + } + + /* if we are in DVS, we need HCLK to be <= ARMCLK */ + if (armclk < fclk && armclk < hclk_max) + hclk_max = armclk; + + for (hdiv = 1; hdiv < 9; hdiv++) { + if (hdiv == 5 || hdiv == 7) + hdiv++; + + hclk = (fclk / hdiv); + if (hclk <= hclk_max || within_khz(hclk, hclk_max)) + break; + } + + s3c_freq_dbg("%s: hclk %lu, div %d\n", __func__, hclk, hdiv); + + if (hdiv > 8) + goto invalid; + + pdiv = (hclk > cfg->max.pclk) ? 2 : 1; + + if ((hclk / pdiv) > cfg->max.pclk) + pdiv++; + + s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv); + + if (pdiv > 2) + goto invalid; + + pdiv *= hdiv; + + /* calculate a valid armclk */ + + if (armclk < hclk) + armclk = hclk; + + /* if we're running armclk lower than fclk, this really means + * that the system should go into dvs mode, which means that + * armclk is connected to hclk. */ + if (armclk < fclk) { + cfg->divs.dvs = 1; + armclk = hclk; + } else + cfg->divs.dvs = 0; + + cfg->freq.armclk = armclk; + + /* store the result, and then return */ + + cfg->divs.h_divisor = hdiv; + cfg->divs.p_divisor = pdiv; + + return 0; + + invalid: + return -EINVAL; +} + +#define CAMDIVN_HCLK_HALF (S3C2440_CAMDIVN_HCLK3_HALF | \ + S3C2440_CAMDIVN_HCLK4_HALF) + +/** + * s3c2440_cpufreq_setdivs - set the cpu frequency divider settings + * @cfg: The cpu frequency settings. + * + * Set the divisors from the settings in @cfg, which where generated + * during the calculation phase by s3c2440_cpufreq_calcdivs(). + */ +static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) +{ + unsigned long clkdiv, camdiv; + + s3c_freq_dbg("%s: divsiors: h=%d, p=%d\n", __func__, + cfg->divs.h_divisor, cfg->divs.p_divisor); + + clkdiv = __raw_readl(S3C2410_CLKDIVN); + camdiv = __raw_readl(S3C2440_CAMDIVN); + + clkdiv &= ~(S3C2440_CLKDIVN_HDIVN_MASK | S3C2440_CLKDIVN_PDIVN); + camdiv &= ~CAMDIVN_HCLK_HALF; + + switch (cfg->divs.h_divisor) { + case 1: + clkdiv |= S3C2440_CLKDIVN_HDIVN_1; + break; + + case 2: + clkdiv |= S3C2440_CLKDIVN_HDIVN_2; + break; + + case 6: + camdiv |= S3C2440_CAMDIVN_HCLK3_HALF; + case 3: + clkdiv |= S3C2440_CLKDIVN_HDIVN_3_6; + break; + + case 8: + camdiv |= S3C2440_CAMDIVN_HCLK4_HALF; + case 4: + clkdiv |= S3C2440_CLKDIVN_HDIVN_4_8; + break; + + default: + BUG(); /* we don't expect to get here. */ + } + + if (cfg->divs.p_divisor != cfg->divs.h_divisor) + clkdiv |= S3C2440_CLKDIVN_PDIVN; + + /* todo - set pclk. */ + + /* Write the divisors first with hclk intentionally halved so that + * when we write clkdiv we will under-frequency instead of over. We + * then make a short delay and remove the hclk halving if necessary. + */ + + __raw_writel(camdiv | CAMDIVN_HCLK_HALF, S3C2440_CAMDIVN); + __raw_writel(clkdiv, S3C2410_CLKDIVN); + + ndelay(20); + __raw_writel(camdiv, S3C2440_CAMDIVN); + + clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk); +} + +static int run_freq_for(unsigned long max_hclk, unsigned long fclk, + int *divs, + struct cpufreq_frequency_table *table, + size_t table_size) +{ + unsigned long freq; + int index = 0; + int div; + + for (div = *divs; div > 0; div = *divs++) { + freq = fclk / div; + + if (freq > max_hclk && div != 1) + continue; + + freq /= 1000; /* table is in kHz */ + index = s3c_cpufreq_addfreq(table, index, table_size, freq); + if (index < 0) + break; + } + + return index; +} + +static int hclk_divs[] = { 1, 2, 3, 4, 6, 8, -1 }; + +static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg, + struct cpufreq_frequency_table *table, + size_t table_size) +{ + int ret; + + WARN_ON(cfg->info == NULL); + WARN_ON(cfg->board == NULL); + + ret = run_freq_for(cfg->info->max.hclk, + cfg->info->max.fclk, + hclk_divs, + table, table_size); + + s3c_freq_dbg("%s: returning %d\n", __func__, ret); + + return ret; +} + +struct s3c_cpufreq_info s3c2440_cpufreq_info = { + .max = { + .fclk = 400000000, + .hclk = 133333333, + .pclk = 66666666, + }, + + .locktime_m = 300, + .locktime_u = 300, + .locktime_bits = 16, + + .name = "s3c244x", + .calc_iotiming = s3c2410_iotiming_calc, + .set_iotiming = s3c2410_iotiming_set, + .get_iotiming = s3c2410_iotiming_get, + .set_fvco = s3c2410_set_fvco, + + .set_refresh = s3c2410_cpufreq_setrefresh, + .set_divs = s3c2440_cpufreq_setdivs, + .calc_divs = s3c2440_cpufreq_calcdivs, + .calc_freqtable = s3c2440_cpufreq_calctable, + + .resume_clocks = s3c244x_setup_clocks, + + .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs), +}; + +static int s3c2440_cpufreq_add(struct sys_device *sysdev) +{ + xtal = s3c_cpufreq_clk_get(NULL, "xtal"); + hclk = s3c_cpufreq_clk_get(NULL, "hclk"); + fclk = s3c_cpufreq_clk_get(NULL, "fclk"); + armclk = s3c_cpufreq_clk_get(NULL, "armclk"); + + if (IS_ERR(xtal) || IS_ERR(hclk) || IS_ERR(fclk) || IS_ERR(armclk)) { + printk(KERN_ERR "%s: failed to get clocks\n", __func__); + return -ENOENT; + } + + return s3c_cpufreq_register(&s3c2440_cpufreq_info); +} + +static struct sysdev_driver s3c2440_cpufreq_driver = { + .add = s3c2440_cpufreq_add, +}; + +static int s3c2440_cpufreq_init(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, + &s3c2440_cpufreq_driver); +} + +/* arch_initcall adds the clocks we need, so use subsys_initcall. */ +subsys_initcall(s3c2440_cpufreq_init); + +static struct sysdev_driver s3c2442_cpufreq_driver = { + .add = s3c2440_cpufreq_add, +}; + +static int s3c2442_cpufreq_init(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, + &s3c2442_cpufreq_driver); +} + +subsys_initcall(s3c2442_cpufreq_init); diff --git a/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c b/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c new file mode 100644 index 00000000000..f105d5e8c47 --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c @@ -0,0 +1,97 @@ +/* arch/arm/mach-s3c2440/s3c2440-pll-12000000.c + * + * Copyright (c) 2006-2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * Vincent Sanders <vince@arm.linux.org.uk> + * + * S3C2440/S3C2442 CPU PLL tables (12MHz Crystal) + * + * 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/types.h> +#include <linux/kernel.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <plat/cpu.h> +#include <plat/cpu-freq-core.h> + +static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = { + { .frequency = 75000000, .index = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */ + { .frequency = 80000000, .index = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */ + { .frequency = 90000000, .index = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */ + { .frequency = 100000000, .index = PLLVAL(0x5c, 1, 3), }, /* FVco 800.000000 */ + { .frequency = 110000000, .index = PLLVAL(0x66, 1, 3), }, /* FVco 880.000000 */ + { .frequency = 120000000, .index = PLLVAL(0x70, 1, 3), }, /* FVco 960.000000 */ + { .frequency = 150000000, .index = PLLVAL(0x75, 3, 2), }, /* FVco 600.000000 */ + { .frequency = 160000000, .index = PLLVAL(0x98, 4, 2), }, /* FVco 640.000000 */ + { .frequency = 170000000, .index = PLLVAL(0x4d, 1, 2), }, /* FVco 680.000000 */ + { .frequency = 180000000, .index = PLLVAL(0x70, 2, 2), }, /* FVco 720.000000 */ + { .frequency = 190000000, .index = PLLVAL(0x57, 1, 2), }, /* FVco 760.000000 */ + { .frequency = 200000000, .index = PLLVAL(0x5c, 1, 2), }, /* FVco 800.000000 */ + { .frequency = 210000000, .index = PLLVAL(0x84, 2, 2), }, /* FVco 840.000000 */ + { .frequency = 220000000, .index = PLLVAL(0x66, 1, 2), }, /* FVco 880.000000 */ + { .frequency = 230000000, .index = PLLVAL(0x6b, 1, 2), }, /* FVco 920.000000 */ + { .frequency = 240000000, .index = PLLVAL(0x70, 1, 2), }, /* FVco 960.000000 */ + { .frequency = 300000000, .index = PLLVAL(0x75, 3, 1), }, /* FVco 600.000000 */ + { .frequency = 310000000, .index = PLLVAL(0x93, 4, 1), }, /* FVco 620.000000 */ + { .frequency = 320000000, .index = PLLVAL(0x98, 4, 1), }, /* FVco 640.000000 */ + { .frequency = 330000000, .index = PLLVAL(0x66, 2, 1), }, /* FVco 660.000000 */ + { .frequency = 340000000, .index = PLLVAL(0x4d, 1, 1), }, /* FVco 680.000000 */ + { .frequency = 350000000, .index = PLLVAL(0xa7, 4, 1), }, /* FVco 700.000000 */ + { .frequency = 360000000, .index = PLLVAL(0x70, 2, 1), }, /* FVco 720.000000 */ + { .frequency = 370000000, .index = PLLVAL(0xb1, 4, 1), }, /* FVco 740.000000 */ + { .frequency = 380000000, .index = PLLVAL(0x57, 1, 1), }, /* FVco 760.000000 */ + { .frequency = 390000000, .index = PLLVAL(0x7a, 2, 1), }, /* FVco 780.000000 */ + { .frequency = 400000000, .index = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */ +}; + +static int s3c2440_plls12_add(struct sys_device *dev) +{ + struct clk *xtal_clk; + unsigned long xtal; + + xtal_clk = clk_get(NULL, "xtal"); + if (IS_ERR(xtal_clk)) + return PTR_ERR(xtal_clk); + + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + + if (xtal == 12000000) { + printk(KERN_INFO "Using PLL table for 12MHz crystal\n"); + return s3c_plltab_register(s3c2440_plls_12, + ARRAY_SIZE(s3c2440_plls_12)); + } + + return 0; +} + +static struct sysdev_driver s3c2440_plls12_drv = { + .add = s3c2440_plls12_add, +}; + +static int __init s3c2440_pll_12mhz(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_plls12_drv); + +} + +arch_initcall(s3c2440_pll_12mhz); + +static struct sysdev_driver s3c2442_plls12_drv = { + .add = s3c2440_plls12_add, +}; + +static int __init s3c2442_pll_12mhz(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_plls12_drv); + +} + +arch_initcall(s3c2442_pll_12mhz); diff --git a/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c b/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c new file mode 100644 index 00000000000..c8a8f90ef38 --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c @@ -0,0 +1,127 @@ +/* arch/arm/mach-s3c2440/s3c2440-pll-16934400.c + * + * Copyright (c) 2006-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * Vincent Sanders <vince@arm.linux.org.uk> + * + * S3C2440/S3C2442 CPU PLL tables (16.93444MHz Crystal) + * + * 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/types.h> +#include <linux/kernel.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <plat/cpu.h> +#include <plat/cpu-freq-core.h> + +static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = { + { .frequency = 78019200, .index = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */ + { .frequency = 84067200, .index = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */ + { .frequency = 90115200, .index = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */ + { .frequency = 96163200, .index = PLLVAL(151, 5, 3), }, /* FVco 769.305600 */ + { .frequency = 102135600, .index = PLLVAL(185, 6, 3), }, /* FVco 817.084800 */ + { .frequency = 108259200, .index = PLLVAL(171, 5, 3), }, /* FVco 866.073600 */ + { .frequency = 114307200, .index = PLLVAL(127, 3, 3), }, /* FVco 914.457600 */ + { .frequency = 120234240, .index = PLLVAL(134, 3, 3), }, /* FVco 961.873920 */ + { .frequency = 126161280, .index = PLLVAL(141, 3, 3), }, /* FVco 1009.290240 */ + { .frequency = 132088320, .index = PLLVAL(148, 3, 3), }, /* FVco 1056.706560 */ + { .frequency = 138015360, .index = PLLVAL(155, 3, 3), }, /* FVco 1104.122880 */ + { .frequency = 144789120, .index = PLLVAL(163, 3, 3), }, /* FVco 1158.312960 */ + { .frequency = 150100363, .index = PLLVAL(187, 9, 2), }, /* FVco 600.401454 */ + { .frequency = 156038400, .index = PLLVAL(121, 5, 2), }, /* FVco 624.153600 */ + { .frequency = 162086400, .index = PLLVAL(126, 5, 2), }, /* FVco 648.345600 */ + { .frequency = 168134400, .index = PLLVAL(131, 5, 2), }, /* FVco 672.537600 */ + { .frequency = 174048000, .index = PLLVAL(177, 7, 2), }, /* FVco 696.192000 */ + { .frequency = 180230400, .index = PLLVAL(141, 5, 2), }, /* FVco 720.921600 */ + { .frequency = 186278400, .index = PLLVAL(124, 4, 2), }, /* FVco 745.113600 */ + { .frequency = 192326400, .index = PLLVAL(151, 5, 2), }, /* FVco 769.305600 */ + { .frequency = 198132480, .index = PLLVAL(109, 3, 2), }, /* FVco 792.529920 */ + { .frequency = 204271200, .index = PLLVAL(185, 6, 2), }, /* FVco 817.084800 */ + { .frequency = 210268800, .index = PLLVAL(141, 4, 2), }, /* FVco 841.075200 */ + { .frequency = 216518400, .index = PLLVAL(171, 5, 2), }, /* FVco 866.073600 */ + { .frequency = 222264000, .index = PLLVAL(97, 2, 2), }, /* FVco 889.056000 */ + { .frequency = 228614400, .index = PLLVAL(127, 3, 2), }, /* FVco 914.457600 */ + { .frequency = 234259200, .index = PLLVAL(158, 4, 2), }, /* FVco 937.036800 */ + { .frequency = 240468480, .index = PLLVAL(134, 3, 2), }, /* FVco 961.873920 */ + { .frequency = 246960000, .index = PLLVAL(167, 4, 2), }, /* FVco 987.840000 */ + { .frequency = 252322560, .index = PLLVAL(141, 3, 2), }, /* FVco 1009.290240 */ + { .frequency = 258249600, .index = PLLVAL(114, 2, 2), }, /* FVco 1032.998400 */ + { .frequency = 264176640, .index = PLLVAL(148, 3, 2), }, /* FVco 1056.706560 */ + { .frequency = 270950400, .index = PLLVAL(120, 2, 2), }, /* FVco 1083.801600 */ + { .frequency = 276030720, .index = PLLVAL(155, 3, 2), }, /* FVco 1104.122880 */ + { .frequency = 282240000, .index = PLLVAL(92, 1, 2), }, /* FVco 1128.960000 */ + { .frequency = 289578240, .index = PLLVAL(163, 3, 2), }, /* FVco 1158.312960 */ + { .frequency = 294235200, .index = PLLVAL(131, 2, 2), }, /* FVco 1176.940800 */ + { .frequency = 300200727, .index = PLLVAL(187, 9, 1), }, /* FVco 600.401454 */ + { .frequency = 306358690, .index = PLLVAL(191, 9, 1), }, /* FVco 612.717380 */ + { .frequency = 312076800, .index = PLLVAL(121, 5, 1), }, /* FVco 624.153600 */ + { .frequency = 318366720, .index = PLLVAL(86, 3, 1), }, /* FVco 636.733440 */ + { .frequency = 324172800, .index = PLLVAL(126, 5, 1), }, /* FVco 648.345600 */ + { .frequency = 330220800, .index = PLLVAL(109, 4, 1), }, /* FVco 660.441600 */ + { .frequency = 336268800, .index = PLLVAL(131, 5, 1), }, /* FVco 672.537600 */ + { .frequency = 342074880, .index = PLLVAL(93, 3, 1), }, /* FVco 684.149760 */ + { .frequency = 348096000, .index = PLLVAL(177, 7, 1), }, /* FVco 696.192000 */ + { .frequency = 355622400, .index = PLLVAL(118, 4, 1), }, /* FVco 711.244800 */ + { .frequency = 360460800, .index = PLLVAL(141, 5, 1), }, /* FVco 720.921600 */ + { .frequency = 366206400, .index = PLLVAL(165, 6, 1), }, /* FVco 732.412800 */ + { .frequency = 372556800, .index = PLLVAL(124, 4, 1), }, /* FVco 745.113600 */ + { .frequency = 378201600, .index = PLLVAL(126, 4, 1), }, /* FVco 756.403200 */ + { .frequency = 384652800, .index = PLLVAL(151, 5, 1), }, /* FVco 769.305600 */ + { .frequency = 391608000, .index = PLLVAL(177, 6, 1), }, /* FVco 783.216000 */ + { .frequency = 396264960, .index = PLLVAL(109, 3, 1), }, /* FVco 792.529920 */ + { .frequency = 402192000, .index = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */ +}; + +static int s3c2440_plls169344_add(struct sys_device *dev) +{ + struct clk *xtal_clk; + unsigned long xtal; + + xtal_clk = clk_get(NULL, "xtal"); + if (IS_ERR(xtal_clk)) + return PTR_ERR(xtal_clk); + + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + + if (xtal == 169344000) { + printk(KERN_INFO "Using PLL table for 16.9344MHz crystal\n"); + return s3c_plltab_register(s3c2440_plls_169344, + ARRAY_SIZE(s3c2440_plls_169344)); + } + + return 0; +} + +static struct sysdev_driver s3c2440_plls169344_drv = { + .add = s3c2440_plls169344_add, +}; + +static int __init s3c2440_pll_16934400(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, + &s3c2440_plls169344_drv); + +} + +arch_initcall(s3c2440_pll_16934400); + +static struct sysdev_driver s3c2442_plls169344_drv = { + .add = s3c2440_plls169344_add, +}; + +static int __init s3c2442_pll_16934400(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, + &s3c2442_plls169344_drv); + +} + +arch_initcall(s3c2442_pll_16934400); diff --git a/arch/arm/mach-s3c2440/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c index ac1f7ea5f40..2b68f7ea45a 100644 --- a/arch/arm/mach-s3c2440/s3c2440.c +++ b/arch/arm/mach-s3c2440/s3c2440.c @@ -29,9 +29,9 @@ #include <mach/hardware.h> #include <asm/irq.h> -#include <plat/s3c2440.h> #include <plat/devs.h> #include <plat/cpu.h> +#include <plat/s3c244x.h> static struct sys_device s3c2440_sysdev = { .cls = &s3c2440_sysclass, diff --git a/arch/arm/mach-s3c2440/s3c2442.c b/arch/arm/mach-s3c2440/s3c2442.c new file mode 100644 index 00000000000..188ad1e57dc --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c2442.c @@ -0,0 +1,165 @@ +/* linux/arch/arm/mach-s3c2442/s3c2442.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2442 core and lock 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 <linux/io.h> + +#include <mach/hardware.h> +#include <asm/atomic.h> +#include <asm/irq.h> + +#include <mach/regs-clock.h> + +#include <plat/clock.h> +#include <plat/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, + .ops = &(struct clk_ops) { + .set_rate = s3c2442_camif_upll_setrate, + .round_rate = s3c2442_camif_upll_round, + }, +}; + +static int s3c2442_clk_add(struct sys_device *sysdev) +{ + struct clk *clock_upll; + struct clk *clock_h; + struct clk *clock_p; + + clock_p = clk_get(NULL, "pclk"); + clock_h = clk_get(NULL, "hclk"); + clock_upll = clk_get(NULL, "upll"); + + if (IS_ERR(clock_p) || IS_ERR(clock_h) || IS_ERR(clock_upll)) { + printk(KERN_ERR "S3C2442: Failed to get parent clocks\n"); + return -EINVAL; + } + + s3c2442_clk_cam.parent = clock_h; + s3c2442_clk_cam_upll.parent = clock_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); + + +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-s3c2440/s3c244x-clock.c b/arch/arm/mach-s3c2440/s3c244x-clock.c new file mode 100644 index 00000000000..f8d96130d1d --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c244x-clock.c @@ -0,0 +1,138 @@ +/* linux/arch/arm/plat-s3c24xx/s3c24xx-clock.c + * + * Copyright (c) 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2440/S3C2442 Common 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/clk.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <asm/atomic.h> +#include <asm/irq.h> + +#include <mach/regs-clock.h> + +#include <plat/clock.h> +#include <plat/cpu.h> + +static int s3c2440_setparent_armclk(struct clk *clk, struct clk *parent) +{ + unsigned long camdivn; + unsigned long dvs; + + if (parent == &clk_f) + dvs = 0; + else if (parent == &clk_h) + dvs = S3C2440_CAMDIVN_DVSEN; + else + return -EINVAL; + + clk->parent = parent; + + camdivn = __raw_readl(S3C2440_CAMDIVN); + camdivn &= ~S3C2440_CAMDIVN_DVSEN; + camdivn |= dvs; + __raw_writel(camdivn, S3C2440_CAMDIVN); + + return 0; +} + +static struct clk clk_arm = { + .name = "armclk", + .id = -1, + .ops = &(struct clk_ops) { + .set_parent = s3c2440_setparent_armclk, + }, +}; + +static int s3c244x_clk_add(struct sys_device *sysdev) +{ + unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); + unsigned long clkdivn; + struct clk *clock_upll; + int ret; + + printk("S3C244X: Clock Support, DVS %s\n", + (camdivn & S3C2440_CAMDIVN_DVSEN) ? "on" : "off"); + + clk_arm.parent = (camdivn & S3C2440_CAMDIVN_DVSEN) ? &clk_h : &clk_f; + + ret = s3c24xx_register_clock(&clk_arm); + if (ret < 0) { + printk(KERN_ERR "S3C24XX: Failed to add armclk (%d)\n", ret); + return ret; + } + + clock_upll = clk_get(NULL, "upll"); + if (IS_ERR(clock_upll)) { + printk(KERN_ERR "S3C244X: Failed to get upll clock\n"); + return -ENOENT; + } + + /* 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(clock_upll) > (94 * MHZ)) { + clk_usb_bus.rate = clk_get_rate(clock_upll) / 2; + + spin_lock(&clocks_lock); + + clkdivn = __raw_readl(S3C2410_CLKDIVN); + clkdivn |= S3C2440_CLKDIVN_UCLK; + __raw_writel(clkdivn, S3C2410_CLKDIVN); + + spin_unlock(&clocks_lock); + } + + return 0; +} + +static struct sysdev_driver s3c2440_clk_driver = { + .add = s3c244x_clk_add, +}; + +static int s3c2440_clk_init(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_clk_driver); +} + +arch_initcall(s3c2440_clk_init); + +static struct sysdev_driver s3c2442_clk_driver = { + .add = s3c244x_clk_add, +}; + +static 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-s3c2440/s3c244x-irq.c b/arch/arm/mach-s3c2440/s3c244x-irq.c new file mode 100644 index 00000000000..a75c0c2431e --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c244x-irq.c @@ -0,0 +1,142 @@ +/* linux/arch/arm/plat-s3c24xx/s3c244x-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 + * +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/sysdev.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <asm/irq.h> + +#include <asm/mach/irq.h> + +#include <mach/regs-irq.h> +#include <mach/regs-gpio.h> + +#include <plat/cpu.h> +#include <plat/pm.h> +#include <plat/irq.h> + +/* camera irq */ + +static void s3c_irq_demux_cam(unsigned int irq, + struct irq_desc *desc) +{ + unsigned int subsrc, submsk; + + /* 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) { + generic_handle_irq(IRQ_S3C2440_CAM_C); + } + if (subsrc & 2) { + generic_handle_irq(IRQ_S3C2440_CAM_P); + } + } +} + +#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 irq_chip 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, handle_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, handle_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, handle_level_irq); + set_irq_flags(irqno, IRQF_VALID); + } + + return 0; +} + +static struct sysdev_driver s3c2440_irq_driver = { + .add = s3c244x_irq_add, + .suspend = s3c24xx_irq_suspend, + .resume = s3c24xx_irq_resume, +}; + +static int s3c2440_irq_init(void) +{ + return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_irq_driver); +} + +arch_initcall(s3c2440_irq_init); + +static struct sysdev_driver s3c2442_irq_driver = { + .add = s3c244x_irq_add, + .suspend = s3c24xx_irq_suspend, + .resume = s3c24xx_irq_resume, +}; + + +static int s3c2442_irq_init(void) +{ + return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_irq_driver); +} + +arch_initcall(s3c2442_irq_init); diff --git a/arch/arm/mach-s3c2440/s3c244x.c b/arch/arm/mach-s3c2440/s3c244x.c new file mode 100644 index 00000000000..5e4a97e7653 --- /dev/null +++ b/arch/arm/mach-s3c2440/s3c244x.c @@ -0,0 +1,195 @@ +/* linux/arch/arm/plat-s3c24xx/s3c244x.c + * + * Copyright (c) 2004-2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Samsung S3C2440 and S3C2442 Mobile CPU support (not S3C2443) + * + * 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/serial_core.h> +#include <linux/platform_device.h> +#include <linux/sysdev.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <mach/hardware.h> +#include <asm/irq.h> + +#include <plat/cpu-freq.h> + +#include <mach/regs-clock.h> +#include <plat/regs-serial.h> +#include <mach/regs-gpio.h> +#include <mach/regs-gpioj.h> +#include <mach/regs-dsc.h> + +#include <plat/s3c2410.h> +#include <plat/s3c244x.h> +#include <plat/clock.h> +#include <plat/devs.h> +#include <plat/cpu.h> +#include <plat/pm.h> +#include <plat/pll.h> + +static struct map_desc s3c244x_iodesc[] __initdata = { + IODESC_ENT(CLKPWR), + IODESC_ENT(TIMER), + IODESC_ENT(WATCHDOG), +}; + +/* 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(void) +{ + /* register our io-tables */ + + iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc)); + + /* rename any peripherals used differing from the s3c2410 */ + + s3c_device_sdi.name = "s3c2440-sdi"; + s3c_device_i2c0.name = "s3c2440-i2c"; + s3c_device_nand.name = "s3c2440-nand"; + s3c_device_ts.name = "s3c2440-ts"; + s3c_device_usbgadget.name = "s3c2440-usbgadget"; +} + +void __init_or_cpufreq s3c244x_setup_clocks(void) +{ + struct clk *xtal_clk; + unsigned long clkdiv; + unsigned long camdiv; + unsigned long xtal; + unsigned long hclk, fclk, pclk; + int hdiv = 1; + + xtal_clk = clk_get(NULL, "xtal"); + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + + fclk = s3c24xx_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)); + + s3c24xx_setup_clocks(fclk, hclk, pclk); +} + +void __init s3c244x_init_clocks(int xtal) +{ + /* initialise the clocks here, to allow other things like the + * console to use them, and to add new ones after the initialisation + */ + + s3c24xx_register_baseclocks(xtal); + s3c244x_setup_clocks(); + 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) +{ + s3c_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep)); + return 0; +} + +static int s3c244x_resume(struct sys_device *dev) +{ + s3c_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 = { + .name = "s3c2440-core", + .suspend = s3c244x_suspend, + .resume = s3c244x_resume +}; + +struct sysdev_class s3c2442_sysclass = { + .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); |