diff options
Diffstat (limited to 'arch/arm/mach-pxa')
-rw-r--r-- | arch/arm/mach-pxa/Kconfig | 77 | ||||
-rw-r--r-- | arch/arm/mach-pxa/Makefile | 26 | ||||
-rw-r--r-- | arch/arm/mach-pxa/Makefile.boot | 2 | ||||
-rw-r--r-- | arch/arm/mach-pxa/corgi.c | 320 | ||||
-rw-r--r-- | arch/arm/mach-pxa/corgi_ssp.c | 248 | ||||
-rw-r--r-- | arch/arm/mach-pxa/dma.c | 133 | ||||
-rw-r--r-- | arch/arm/mach-pxa/generic.c | 237 | ||||
-rw-r--r-- | arch/arm/mach-pxa/generic.h | 24 | ||||
-rw-r--r-- | arch/arm/mach-pxa/idp.c | 190 | ||||
-rw-r--r-- | arch/arm/mach-pxa/irq.c | 313 | ||||
-rw-r--r-- | arch/arm/mach-pxa/leds-idp.c | 117 | ||||
-rw-r--r-- | arch/arm/mach-pxa/leds-lubbock.c | 126 | ||||
-rw-r--r-- | arch/arm/mach-pxa/leds-mainstone.c | 121 | ||||
-rw-r--r-- | arch/arm/mach-pxa/leds.c | 32 | ||||
-rw-r--r-- | arch/arm/mach-pxa/leds.h | 12 | ||||
-rw-r--r-- | arch/arm/mach-pxa/lubbock.c | 247 | ||||
-rw-r--r-- | arch/arm/mach-pxa/mainstone.c | 316 | ||||
-rw-r--r-- | arch/arm/mach-pxa/pm.c | 227 | ||||
-rw-r--r-- | arch/arm/mach-pxa/poodle.c | 189 | ||||
-rw-r--r-- | arch/arm/mach-pxa/pxa25x.c | 104 | ||||
-rw-r--r-- | arch/arm/mach-pxa/pxa27x.c | 163 | ||||
-rw-r--r-- | arch/arm/mach-pxa/sleep.S | 194 | ||||
-rw-r--r-- | arch/arm/mach-pxa/ssp.c | 363 | ||||
-rw-r--r-- | arch/arm/mach-pxa/time.c | 164 |
24 files changed, 3945 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig new file mode 100644 index 00000000000..405a55f2287 --- /dev/null +++ b/arch/arm/mach-pxa/Kconfig @@ -0,0 +1,77 @@ +if ARCH_PXA + +menu "Intel PXA2xx Implementations" + +choice + prompt "Select target board" + +config ARCH_LUBBOCK + bool "Intel DBPXA250 Development Platform" + select PXA25x + select SA1111 + +config MACH_MAINSTONE + bool "Intel HCDDBBVA0 Development Platform" + select PXA27x + select IWMMXT + +config ARCH_PXA_IDP + bool "Accelent Xscale IDP" + select PXA25x + +config PXA_SHARPSL + bool "SHARP SL-5600 and SL-C7xx Models" + select PXA25x + select SHARP_SCOOP + select SHARP_PARAM + help + Say Y here if you intend to run this kernel on a + Sharp SL-5600 (Poodle), Sharp SL-C700 (Corgi), + SL-C750 (Shepherd) or a Sharp SL-C760 (Husky) + handheld computer. + +endchoice + +endmenu + +config MACH_POODLE + bool "Enable Sharp SL-5600 (Poodle) Support" + depends PXA_SHARPSL + select SHARP_LOCOMO + +config MACH_CORGI + bool "Enable Sharp SL-C700 (Corgi) Support" + depends PXA_SHARPSL + select PXA_SHARP_C7xx + +config MACH_SHEPHERD + bool "Enable Sharp SL-C750 (Shepherd) Support" + depends PXA_SHARPSL + select PXA_SHARP_C7xx + +config MACH_HUSKY + bool "Enable Sharp SL-C760 (Husky) Support" + depends PXA_SHARPSL + select PXA_SHARP_C7xx + +config PXA25x + bool + help + Select code specific to PXA21x/25x/26x variants + +config PXA27x + bool + help + Select code specific to PXA27x variants + +config IWMMXT + bool + help + Enable support for iWMMXt + +config PXA_SHARP_C7xx + bool + help + Enable support for all Sharp C7xx models + +endif diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile new file mode 100644 index 00000000000..c4e6d252358 --- /dev/null +++ b/arch/arm/mach-pxa/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the linux kernel. +# + +# Common support (must be linked before board specific support) +obj-y += generic.o irq.o dma.o time.o +obj-$(CONFIG_PXA25x) += pxa25x.o +obj-$(CONFIG_PXA27x) += pxa27x.o + +# Specific board support +obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o +obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o +obj-$(CONFIG_ARCH_PXA_IDP) += idp.o +obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o corgi_ssp.o ssp.o +obj-$(CONFIG_MACH_POODLE) += poodle.o + +# Support for blinky lights +led-y := leds.o +led-$(CONFIG_ARCH_LUBBOCK) += leds-lubbock.o +led-$(CONFIG_MACH_MAINSTONE) += leds-mainstone.o +led-$(CONFIG_ARCH_PXA_IDP) += leds-idp.o + +obj-$(CONFIG_LEDS) += $(led-y) + +# Misc features +obj-$(CONFIG_PM) += pm.o sleep.o diff --git a/arch/arm/mach-pxa/Makefile.boot b/arch/arm/mach-pxa/Makefile.boot new file mode 100644 index 00000000000..1ead67178ec --- /dev/null +++ b/arch/arm/mach-pxa/Makefile.boot @@ -0,0 +1,2 @@ + zreladdr-y := 0xa0008000 + diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c new file mode 100644 index 00000000000..f691cf77d39 --- /dev/null +++ b/arch/arm/mach-pxa/corgi.c @@ -0,0 +1,320 @@ +/* + * Support for Sharp SL-C7xx PDAs + * Models: SL-C700 (Corgi), SL-C750 (Shepherd), SL-C760 (Husky) + * + * Copyright (c) 2004-2005 Richard Purdie + * + * Based on Sharp's 2.4 kernel patches/lubbock.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/mmc/host.h> + +#include <asm/setup.h> +#include <asm/memory.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/irq.h> +#include <asm/arch/mmc.h> +#include <asm/arch/udc.h> +#include <asm/arch/corgi.h> + +#include <asm/mach/sharpsl_param.h> +#include <asm/hardware/scoop.h> +#include <video/w100fb.h> + +#include "generic.h" + + +/* + * Corgi SCOOP Device + */ +static struct resource corgi_scoop_resources[] = { + [0] = { + .start = 0x10800000, + .end = 0x10800fff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct scoop_config corgi_scoop_setup = { + .io_dir = CORGI_SCOOP_IO_DIR, + .io_out = CORGI_SCOOP_IO_OUT, +}; + +struct platform_device corgiscoop_device = { + .name = "sharp-scoop", + .id = -1, + .dev = { + .platform_data = &corgi_scoop_setup, + }, + .num_resources = ARRAY_SIZE(corgi_scoop_resources), + .resource = corgi_scoop_resources, +}; + + +/* + * Corgi SSP Device + * + * Set the parent as the scoop device because a lot of SSP devices + * also use scoop functions and this makes the power up/down order + * work correctly. + */ +static struct platform_device corgissp_device = { + .name = "corgi-ssp", + .dev = { + .parent = &corgiscoop_device.dev, + }, + .id = -1, +}; + + +/* + * Corgi w100 Frame Buffer Device + */ +static struct w100fb_mach_info corgi_fb_info = { + .w100fb_ssp_send = corgi_ssp_lcdtg_send, + .comadj = -1, + .phadadj = -1, +}; + +static struct resource corgi_fb_resources[] = { + [0] = { + .start = 0x08000000, + .end = 0x08ffffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device corgifb_device = { + .name = "w100fb", + .id = -1, + .dev = { + .platform_data = &corgi_fb_info, + .parent = &corgissp_device.dev, + }, + .num_resources = ARRAY_SIZE(corgi_fb_resources), + .resource = corgi_fb_resources, +}; + + +/* + * Corgi Backlight Device + */ +static struct platform_device corgibl_device = { + .name = "corgi-bl", + .dev = { + .parent = &corgifb_device.dev, + }, + .id = -1, +}; + + +/* + * MMC/SD Device + * + * The card detect interrupt isn't debounced so we delay it by HZ/4 + * to give the card a chance to fully insert/eject. + */ +static struct mmc_detect { + struct timer_list detect_timer; + void *devid; +} mmc_detect; + +static void mmc_detect_callback(unsigned long data) +{ + mmc_detect_change(mmc_detect.devid); +} + +static irqreturn_t corgi_mmc_detect_int(int irq, void *devid, struct pt_regs *regs) +{ + mmc_detect.devid=devid; + mod_timer(&mmc_detect.detect_timer, jiffies + HZ/4); + return IRQ_HANDLED; +} + +static int corgi_mci_init(struct device *dev, irqreturn_t (*unused_detect_int)(int, void *, struct pt_regs *), void *data) +{ + int err; + + /* setup GPIO for PXA25x MMC controller */ + pxa_gpio_mode(GPIO6_MMCCLK_MD); + pxa_gpio_mode(GPIO8_MMCCS0_MD); + pxa_gpio_mode(CORGI_GPIO_nSD_DETECT | GPIO_IN); + pxa_gpio_mode(CORGI_GPIO_SD_PWR | GPIO_OUT); + + init_timer(&mmc_detect.detect_timer); + mmc_detect.detect_timer.function = mmc_detect_callback; + mmc_detect.detect_timer.data = (unsigned long) &mmc_detect; + + err = request_irq(CORGI_IRQ_GPIO_nSD_DETECT, corgi_mmc_detect_int, SA_INTERRUPT, + "MMC card detect", data); + if (err) { + printk(KERN_ERR "corgi_mci_init: MMC/SD: can't request MMC card detect IRQ\n"); + return -1; + } + + set_irq_type(CORGI_IRQ_GPIO_nSD_DETECT, IRQT_BOTHEDGE); + + return 0; +} + +static void corgi_mci_setpower(struct device *dev, unsigned int vdd) +{ + struct pxamci_platform_data* p_d = dev->platform_data; + + if (( 1 << vdd) & p_d->ocr_mask) { + printk(KERN_DEBUG "%s: on\n", __FUNCTION__); + GPSR1 = GPIO_bit(CORGI_GPIO_SD_PWR); + } else { + printk(KERN_DEBUG "%s: off\n", __FUNCTION__); + GPCR1 = GPIO_bit(CORGI_GPIO_SD_PWR); + } +} + +static void corgi_mci_exit(struct device *dev, void *data) +{ + free_irq(CORGI_IRQ_GPIO_nSD_DETECT, data); + del_timer(&mmc_detect.detect_timer); +} + +static struct pxamci_platform_data corgi_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = corgi_mci_init, + .setpower = corgi_mci_setpower, + .exit = corgi_mci_exit, +}; + + +/* + * USB Device Controller + */ +static void corgi_udc_command(int cmd) +{ + switch(cmd) { + case PXA2XX_UDC_CMD_CONNECT: + GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP); + break; + case PXA2XX_UDC_CMD_DISCONNECT: + GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP); + break; + } +} + +static struct pxa2xx_udc_mach_info udc_info __initdata = { + /* no connect GPIO; corgi can't tell connection status */ + .udc_command = corgi_udc_command, +}; + + +static struct platform_device *devices[] __initdata = { + &corgiscoop_device, + &corgissp_device, + &corgifb_device, + &corgibl_device, +}; + +static void __init corgi_init(void) +{ + corgi_fb_info.comadj=sharpsl_param.comadj; + corgi_fb_info.phadadj=sharpsl_param.phadadj; + + pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT); + pxa_set_udc_info(&udc_info); + pxa_set_mci_info(&corgi_mci_platform_data); + + platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static void __init fixup_corgi(struct machine_desc *desc, + struct tag *tags, char **cmdline, struct meminfo *mi) +{ + sharpsl_save_param(); + mi->nr_banks=1; + mi->bank[0].start = 0xa0000000; + mi->bank[0].node = 0; + if (machine_is_corgi()) + mi->bank[0].size = (32*1024*1024); + else + mi->bank[0].size = (64*1024*1024); +} + +static void __init corgi_init_irq(void) +{ + pxa_init_irq(); +} + +static struct map_desc corgi_io_desc[] __initdata = { +/* virtual physical length */ +/* { 0xf1000000, 0x08000000, 0x01000000, MT_DEVICE },*/ /* LCDC (readable for Qt driver) */ +/* { 0xef700000, 0x10800000, 0x00001000, MT_DEVICE },*/ /* SCOOP */ + { 0xef800000, 0x00000000, 0x00800000, MT_DEVICE }, /* Boot Flash */ +}; + +static void __init corgi_map_io(void) +{ + pxa_map_io(); + iotable_init(corgi_io_desc,ARRAY_SIZE(corgi_io_desc)); + + /* setup sleep mode values */ + PWER = 0x00000002; + PFER = 0x00000000; + PRER = 0x00000002; + PGSR0 = 0x0158C000; + PGSR1 = 0x00FF0080; + PGSR2 = 0x0001C004; + /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ + PCFR |= PCFR_OPDE; +} + +#ifdef CONFIG_MACH_CORGI +MACHINE_START(CORGI, "SHARP Corgi") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + FIXUP(fixup_corgi) + MAPIO(corgi_map_io) + INITIRQ(corgi_init_irq) + .init_machine = corgi_init, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_SHEPHERD +MACHINE_START(SHEPHERD, "SHARP Shepherd") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + FIXUP(fixup_corgi) + MAPIO(corgi_map_io) + INITIRQ(corgi_init_irq) + .init_machine = corgi_init, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_HUSKY +MACHINE_START(HUSKY, "SHARP Husky") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + FIXUP(fixup_corgi) + MAPIO(corgi_map_io) + INITIRQ(corgi_init_irq) + .init_machine = corgi_init, + .timer = &pxa_timer, +MACHINE_END +#endif + diff --git a/arch/arm/mach-pxa/corgi_ssp.c b/arch/arm/mach-pxa/corgi_ssp.c new file mode 100644 index 00000000000..8ccffba0018 --- /dev/null +++ b/arch/arm/mach-pxa/corgi_ssp.c @@ -0,0 +1,248 @@ +/* + * SSP control code for Sharp Corgi devices + * + * Copyright (c) 2004 Richard Purdie + * + * 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/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <asm/hardware.h> + +#include <asm/arch/ssp.h> +#include <asm/arch/corgi.h> +#include <asm/arch/pxa-regs.h> + +static spinlock_t corgi_ssp_lock = SPIN_LOCK_UNLOCKED; +static struct ssp_dev corgi_ssp_dev; +static struct ssp_state corgi_ssp_state; + +/* + * There are three devices connected to the SSP interface: + * 1. A touchscreen controller (TI ADS7846 compatible) + * 2. An LCD contoller (with some Backlight functionality) + * 3. A battery moinitoring IC (Maxim MAX1111) + * + * Each device uses a different speed/mode of communication. + * + * The touchscreen is very sensitive and the most frequently used + * so the port is left configured for this. + * + * Devices are selected using Chip Selects on GPIOs. + */ + +/* + * ADS7846 Routines + */ +unsigned long corgi_ssp_ads7846_putget(ulong data) +{ + unsigned long ret,flag; + + spin_lock_irqsave(&corgi_ssp_lock, flag); + GPCR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); + + ssp_write_word(&corgi_ssp_dev,data); + ret = ssp_read_word(&corgi_ssp_dev); + + GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); + spin_unlock_irqrestore(&corgi_ssp_lock, flag); + + return ret; +} + +/* + * NOTE: These functions should always be called in interrupt context + * and use the _lock and _unlock functions. They are very time sensitive. + */ +void corgi_ssp_ads7846_lock(void) +{ + spin_lock(&corgi_ssp_lock); + GPCR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); +} + +void corgi_ssp_ads7846_unlock(void) +{ + GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); + spin_unlock(&corgi_ssp_lock); +} + +void corgi_ssp_ads7846_put(ulong data) +{ + ssp_write_word(&corgi_ssp_dev,data); +} + +unsigned long corgi_ssp_ads7846_get(void) +{ + return ssp_read_word(&corgi_ssp_dev); +} + +EXPORT_SYMBOL(corgi_ssp_ads7846_putget); +EXPORT_SYMBOL(corgi_ssp_ads7846_lock); +EXPORT_SYMBOL(corgi_ssp_ads7846_unlock); +EXPORT_SYMBOL(corgi_ssp_ads7846_put); +EXPORT_SYMBOL(corgi_ssp_ads7846_get); + + +/* + * LCD/Backlight Routines + */ +unsigned long corgi_ssp_dac_put(ulong data) +{ + unsigned long flag; + + spin_lock_irqsave(&corgi_ssp_lock, flag); + GPCR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS); + + ssp_disable(&corgi_ssp_dev); + ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), SSCR1_SPH, 0, SSCR0_SerClkDiv(76)); + ssp_enable(&corgi_ssp_dev); + + ssp_write_word(&corgi_ssp_dev,data); + /* Read null data back from device to prevent SSP overflow */ + ssp_read_word(&corgi_ssp_dev); + + ssp_disable(&corgi_ssp_dev); + ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2)); + ssp_enable(&corgi_ssp_dev); + GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS); + spin_unlock_irqrestore(&corgi_ssp_lock, flag); + + return 0; +} + +void corgi_ssp_lcdtg_send(u8 adrs, u8 data) +{ + corgi_ssp_dac_put(((adrs & 0x07) << 5) | (data & 0x1f)); +} + +void corgi_ssp_blduty_set(int duty) +{ + corgi_ssp_lcdtg_send(0x02,duty); +} + +EXPORT_SYMBOL(corgi_ssp_lcdtg_send); +EXPORT_SYMBOL(corgi_ssp_blduty_set); + +/* + * Max1111 Routines + */ +int corgi_ssp_max1111_get(ulong data) +{ + unsigned long flag; + int voltage,voltage1,voltage2; + + spin_lock_irqsave(&corgi_ssp_lock, flag); + GPCR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS); + ssp_disable(&corgi_ssp_dev); + ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(8)); + ssp_enable(&corgi_ssp_dev); + + udelay(1); + + /* TB1/RB1 */ + ssp_write_word(&corgi_ssp_dev,data); + ssp_read_word(&corgi_ssp_dev); /* null read */ + + /* TB12/RB2 */ + ssp_write_word(&corgi_ssp_dev,0); + voltage1=ssp_read_word(&corgi_ssp_dev); + + /* TB13/RB3*/ + ssp_write_word(&corgi_ssp_dev,0); + voltage2=ssp_read_word(&corgi_ssp_dev); + + ssp_disable(&corgi_ssp_dev); + ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2)); + ssp_enable(&corgi_ssp_dev); + GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS); + spin_unlock_irqrestore(&corgi_ssp_lock, flag); + + if (voltage1 & 0xc0 || voltage2 & 0x3f) + voltage = -1; + else + voltage = ((voltage1 << 2) & 0xfc) | ((voltage2 >> 6) & 0x03); + + return voltage; +} + +EXPORT_SYMBOL(corgi_ssp_max1111_get); + +/* + * Support Routines + */ +int __init corgi_ssp_probe(struct device *dev) +{ + int ret; + + /* Chip Select - Disable All */ + GPDR0 |= GPIO_bit(CORGI_GPIO_LCDCON_CS); /* output */ + GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS); /* High - Disable LCD Control/Timing Gen */ + GPDR0 |= GPIO_bit(CORGI_GPIO_MAX1111_CS); /* output */ + GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS); /* High - Disable MAX1111*/ + GPDR0 |= GPIO_bit(CORGI_GPIO_ADS7846_CS); /* output */ + GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); /* High - Disable ADS7846*/ + + ret=ssp_init(&corgi_ssp_dev,1); + + if (ret) + printk(KERN_ERR "Unable to register SSP handler!\n"); + else { + ssp_disable(&corgi_ssp_dev); + ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2)); + ssp_enable(&corgi_ssp_dev); + } + + return ret; +} + +static int corgi_ssp_remove(struct device *dev) +{ + ssp_exit(&corgi_ssp_dev); + return 0; +} + +static int corgi_ssp_suspend(struct device *dev, pm_message_t state, u32 level) +{ + if (level == SUSPEND_POWER_DOWN) { + ssp_flush(&corgi_ssp_dev); + ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state); + } + return 0; +} + +static int corgi_ssp_resume(struct device *dev, u32 level) +{ + if (level == RESUME_POWER_ON) { + GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS); /* High - Disable LCD Control/Timing Gen */ + GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS); /* High - Disable MAX1111*/ + GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); /* High - Disable ADS7846*/ + ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state); + ssp_enable(&corgi_ssp_dev); + } + return 0; +} + +static struct device_driver corgissp_driver = { + .name = "corgi-ssp", + .bus = &platform_bus_type, + .probe = corgi_ssp_probe, + .remove = corgi_ssp_remove, + .suspend = corgi_ssp_suspend, + .resume = corgi_ssp_resume, +}; + +int __init corgi_ssp_init(void) +{ + return driver_register(&corgissp_driver); +} + +arch_initcall(corgi_ssp_init); diff --git a/arch/arm/mach-pxa/dma.c b/arch/arm/mach-pxa/dma.c new file mode 100644 index 00000000000..458112b21e2 --- /dev/null +++ b/arch/arm/mach-pxa/dma.c @@ -0,0 +1,133 @@ +/* + * linux/arch/arm/mach-pxa/dma.c + * + * PXA DMA registration and IRQ dispatching + * + * Author: Nicolas Pitre + * Created: Nov 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/errno.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/dma.h> + +#include <asm/arch/pxa-regs.h> + +static struct dma_channel { + char *name; + void (*irq_handler)(int, void *, struct pt_regs *); + void *data; +} dma_channels[PXA_DMA_CHANNELS]; + + +int pxa_request_dma (char *name, pxa_dma_prio prio, + void (*irq_handler)(int, void *, struct pt_regs *), + void *data) +{ + unsigned long flags; + int i, found = 0; + + /* basic sanity checks */ + if (!name || !irq_handler) + return -EINVAL; + + local_irq_save(flags); + + /* try grabbing a DMA channel with the requested priority */ + for (i = prio; i < prio + PXA_DMA_NBCH(prio); i++) { + if (!dma_channels[i].name) { + found = 1; + break; + } + } + + if (!found) { + /* requested prio group is full, try hier priorities */ + for (i = prio-1; i >= 0; i--) { + if (!dma_channels[i].name) { + found = 1; + break; + } + } + } + + if (found) { + DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + dma_channels[i].name = name; + dma_channels[i].irq_handler = irq_handler; + dma_channels[i].data = data; + } else { + printk (KERN_WARNING "No more available DMA channels for %s\n", name); + i = -ENODEV; + } + + local_irq_restore(flags); + return i; +} + +void pxa_free_dma (int dma_ch) +{ + unsigned long flags; + + if (!dma_channels[dma_ch].name) { + printk (KERN_CRIT + "%s: trying to free channel %d which is already freed\n", + __FUNCTION__, dma_ch); + return; + } + + local_irq_save(flags); + DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + dma_channels[dma_ch].name = NULL; + local_irq_restore(flags); +} + +static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int i, dint = DINT; + + for (i = 0; i < PXA_DMA_CHANNELS; i++) { + if (dint & (1 << i)) { + struct dma_channel *channel = &dma_channels[i]; + if (channel->name && channel->irq_handler) { + channel->irq_handler(i, channel->data, regs); + } else { + /* + * IRQ for an unregistered DMA channel: + * let's clear the interrupts and disable it. + */ + printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i); + DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + } + } + } + return IRQ_HANDLED; +} + +static int __init pxa_dma_init (void) +{ + int ret; + + ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL); + if (ret) + printk (KERN_CRIT "Wow! Can't register IRQ for DMA\n"); + return ret; +} + +arch_initcall(pxa_dma_init); + +EXPORT_SYMBOL(pxa_request_dma); +EXPORT_SYMBOL(pxa_free_dma); + diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c new file mode 100644 index 00000000000..b1575b8dc1c --- /dev/null +++ b/arch/arm/mach-pxa/generic.c @@ -0,0 +1,237 @@ +/* + * linux/arch/arm/mach-pxa/generic.c + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * Code common to all PXA machines. + * + * 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. + * + * Since this file should be linked before any other machine specific file, + * the __initcall() here will be executed first. This serves as default + * initialization stuff for PXA machines which can be overridden later if + * need be. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/ioport.h> +#include <linux/pm.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/mach/map.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/udc.h> +#include <asm/arch/pxafb.h> +#include <asm/arch/mmc.h> + +#include "generic.h" + +/* + * Handy function to set GPIO alternate functions + */ + +void pxa_gpio_mode(int gpio_mode) +{ + unsigned long flags; + int gpio = gpio_mode & GPIO_MD_MASK_NR; + int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8; + int gafr; + + local_irq_save(flags); + if (gpio_mode & GPIO_DFLT_LOW) + GPCR(gpio) = GPIO_bit(gpio); + else if (gpio_mode & GPIO_DFLT_HIGH) + GPSR(gpio) = GPIO_bit(gpio); + if (gpio_mode & GPIO_MD_MASK_DIR) + GPDR(gpio) |= GPIO_bit(gpio); + else + GPDR(gpio) &= ~GPIO_bit(gpio); + gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2)); + GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2)); + local_irq_restore(flags); +} + +EXPORT_SYMBOL(pxa_gpio_mode); + +/* + * Routine to safely enable or disable a clock in the CKEN + */ +void pxa_set_cken(int clock, int enable) +{ + unsigned long flags; + local_irq_save(flags); + + if (enable) + CKEN |= clock; + else + CKEN &= ~clock; + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(pxa_set_cken); + +/* + * Intel PXA2xx internal register mapping. + * + * Note 1: not all PXA2xx variants implement all those addresses. + * + * Note 2: virtual 0xfffe0000-0xffffffff is reserved for the vector table + * and cache flush area. + */ +static struct map_desc standard_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf2000000, 0x40000000, 0x02000000, MT_DEVICE }, /* Devs */ + { 0xf4000000, 0x44000000, 0x00100000, MT_DEVICE }, /* LCD */ + { 0xf6000000, 0x48000000, 0x00100000, MT_DEVICE }, /* Mem Ctl */ + { 0xf8000000, 0x4c000000, 0x00100000, MT_DEVICE }, /* USB host */ + { 0xfa000000, 0x50000000, 0x00100000, MT_DEVICE }, /* Camera */ + { 0xfe000000, 0x58000000, 0x00100000, MT_DEVICE }, /* IMem ctl */ + { 0xff000000, 0x00000000, 0x00100000, MT_DEVICE } /* UNCACHED_PHYS_0 */ +}; + +void __init pxa_map_io(void) +{ + iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc)); + get_clk_frequency_khz(1); +} + + +static struct resource pxamci_resources[] = { + [0] = { + .start = 0x41100000, + .end = 0x41100fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_MMC, + .end = IRQ_MMC, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxamci_dmamask = 0xffffffffUL; + +static struct platform_device pxamci_device = { + .name = "pxa2xx-mci", + .id = -1, + .dev = { + .dma_mask = &pxamci_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxamci_resources), + .resource = pxamci_resources, +}; + +void __init pxa_set_mci_info(struct pxamci_platform_data *info) +{ + pxamci_device.dev.platform_data = info; +} + + +static struct pxa2xx_udc_mach_info pxa_udc_info; + +void __init pxa_set_udc_info(struct pxa2xx_udc_mach_info *info) +{ + memcpy(&pxa_udc_info, info, sizeof *info); +} + +static struct resource pxa2xx_udc_resources[] = { + [0] = { + .start = 0x40600000, + .end = 0x4060ffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USB, + .end = IRQ_USB, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dma_mask = ~(u32)0; + +static struct platform_device udc_device = { + .name = "pxa2xx-udc", + .id = -1, + .resource = pxa2xx_udc_resources, + .num_resources = ARRAY_SIZE(pxa2xx_udc_resources), + .dev = { + .platform_data = &pxa_udc_info, + .dma_mask = &udc_dma_mask, + } +}; + +static struct pxafb_mach_info pxa_fb_info; + +void __init set_pxa_fb_info(struct pxafb_mach_info *hard_pxa_fb_info) +{ + memcpy(&pxa_fb_info,hard_pxa_fb_info,sizeof(struct pxafb_mach_info)); +} + +static struct resource pxafb_resources[] = { + [0] = { + .start = 0x44000000, + .end = 0x4400ffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD, + .end = IRQ_LCD, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 fb_dma_mask = ~(u64)0; + +static struct platform_device pxafb_device = { + .name = "pxa2xx-fb", + .id = -1, + .dev = { + .platform_data = &pxa_fb_info, + .dma_mask = &fb_dma_mask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxafb_resources), + .resource = pxafb_resources, +}; + +static struct platform_device ffuart_device = { + .name = "pxa2xx-uart", + .id = 0, +}; +static struct platform_device btuart_device = { + .name = "pxa2xx-uart", + .id = 1, +}; +static struct platform_device stuart_device = { + .name = "pxa2xx-uart", + .id = 2, +}; + +static struct platform_device *devices[] __initdata = { + &pxamci_device, + &udc_device, + &pxafb_device, + &ffuart_device, + &btuart_device, + &stuart_device, +}; + +static int __init pxa_init(void) +{ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(pxa_init); diff --git a/arch/arm/mach-pxa/generic.h b/arch/arm/mach-pxa/generic.h new file mode 100644 index 00000000000..e54a8dd63c1 --- /dev/null +++ b/arch/arm/mach-pxa/generic.h @@ -0,0 +1,24 @@ +/* + * linux/arch/arm/mach-pxa/generic.h + * + * Author: Nicolas Pitre + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct sys_timer; + +extern struct sys_timer pxa_timer; +extern void __init pxa_map_io(void); +extern void __init pxa_init_irq(void); + +extern unsigned int get_clk_frequency_khz(int info); + +#define SET_BANK(__nr,__start,__size) \ + mi->bank[__nr].start = (__start), \ + mi->bank[__nr].size = (__size), \ + mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27) + diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c new file mode 100644 index 00000000000..c5a66bf4d3d --- /dev/null +++ b/arch/arm/mach-pxa/idp.c @@ -0,0 +1,190 @@ +/* + * linux/arch/arm/mach-pxa/idp.c + * + * 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. + * + * Copyright (c) 2001 Cliff Brake, Accelent Systems Inc. + * + * 2001-09-13: Cliff Brake <cbrake@accelent.com> + * Initial code + * + * 2005-02-15: Cliff Brake <cliff.brake@gmail.com> + * <http://www.vibren.com> <http://bec-systems.com> + * Updated for 2.6 kernel + * + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/fb.h> + +#include <asm/setup.h> +#include <asm/memory.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/irq.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/idp.h> +#include <asm/arch/pxafb.h> +#include <asm/arch/bitfield.h> +#include <asm/arch/mmc.h> + +#include "generic.h" + +/* TODO: + * - add pxa2xx_audio_ops_t device structure + * - Ethernet interrupt + */ + +static struct resource smc91x_resources[] = { + [0] = { + .start = (IDP_ETH_PHYS + 0x300), + .end = (IDP_ETH_PHYS + 0xfffff), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO(4), + .end = IRQ_GPIO(4), + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static void idp_backlight_power(int on) +{ + if (on) { + IDP_CPLD_LCD |= (1<<1); + } else { + IDP_CPLD_LCD &= ~(1<<1); + } +} + +static void idp_vlcd(int on) +{ + if (on) { + IDP_CPLD_LCD |= (1<<2); + } else { + IDP_CPLD_LCD &= ~(1<<2); + } +} + +static void idp_lcd_power(int on) +{ + if (on) { + IDP_CPLD_LCD |= (1<<0); + } else { + IDP_CPLD_LCD &= ~(1<<0); + } + + /* call idp_vlcd for now as core driver does not support + * both power and vlcd hooks. Note, this is not technically + * the correct sequence, but seems to work. Disclaimer: + * this may eventually damage the display. + */ + + idp_vlcd(on); +} + +static struct pxafb_mach_info sharp_lm8v31 __initdata = { + .pixclock = 270000, + .xres = 640, + .yres = 480, + .bpp = 16, + .hsync_len = 1, + .left_margin = 3, + .right_margin = 3, + .vsync_len = 1, + .upper_margin = 0, + .lower_margin = 0, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + .lccr0 = LCCR0_SDS, + .lccr3 = LCCR3_PCP | LCCR3_Acb(255), + .pxafb_backlight_power = &idp_backlight_power, + .pxafb_lcd_power = &idp_lcd_power +}; + +static int idp_mci_init(struct device *dev, irqreturn_t (*idp_detect_int)(int, void *, struct pt_regs *), void *data) +{ + /* setup GPIO for PXA25x MMC controller */ + pxa_gpio_mode(GPIO6_MMCCLK_MD); + pxa_gpio_mode(GPIO8_MMCCS0_MD); + + return 0; +} + +static struct pxamci_platform_data idp_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = idp_mci_init, +}; + +static void __init idp_init(void) +{ + printk("idp_init()\n"); + + platform_device_register(&smc91x_device); + //platform_device_register(&mst_audio_device); + set_pxa_fb_info(&sharp_lm8v31); + pxa_set_mci_info(&idp_mci_platform_data); +} + +static void __init idp_init_irq(void) +{ + + pxa_init_irq(); + + set_irq_type(TOUCH_PANEL_IRQ, TOUCH_PANEL_IRQ_EDGE); +} + +static struct map_desc idp_io_desc[] __initdata = { + /* virtual physical length type */ + + { IDP_COREVOLT_VIRT, + IDP_COREVOLT_PHYS, + IDP_COREVOLT_SIZE, + MT_DEVICE }, + { IDP_CPLD_VIRT, + IDP_CPLD_PHYS, + IDP_CPLD_SIZE, + MT_DEVICE } +}; + +static void __init idp_map_io(void) +{ + pxa_map_io(); + iotable_init(idp_io_desc, ARRAY_SIZE(idp_io_desc)); + + // serial ports 2 & 3 + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + +} + + +MACHINE_START(PXA_IDP, "Vibren PXA255 IDP") + MAINTAINER("Vibren Technologies") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + MAPIO(idp_map_io) + INITIRQ(idp_init_irq) + .timer = &pxa_timer, + INIT_MACHINE(idp_init) +MACHINE_END diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c new file mode 100644 index 00000000000..f3cac43124a --- /dev/null +++ b/arch/arm/mach-pxa/irq.c @@ -0,0 +1,313 @@ +/* + * linux/arch/arm/mach-pxa/irq.c + * + * Generic PXA IRQ handling, GPIO IRQ demultiplexing, etc. + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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/ptrace.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/arch/pxa-regs.h> + +#include "generic.h" + + +/* + * This is for peripheral IRQs internal to the PXA chip. + */ + +static void pxa_mask_low_irq(unsigned int irq) +{ + ICMR &= ~(1 << (irq + PXA_IRQ_SKIP)); +} + +static void pxa_unmask_low_irq(unsigned int irq) +{ + ICMR |= (1 << (irq + PXA_IRQ_SKIP)); +} + +static struct irqchip pxa_internal_chip_low = { + .ack = pxa_mask_low_irq, + .mask = pxa_mask_low_irq, + .unmask = pxa_unmask_low_irq, +}; + +#if PXA_INTERNAL_IRQS > 32 + +/* + * This is for the second set of internal IRQs as found on the PXA27x. + */ + +static void pxa_mask_high_irq(unsigned int irq) +{ + ICMR2 &= ~(1 << (irq - 32 + PXA_IRQ_SKIP)); +} + +static void pxa_unmask_high_irq(unsigned int irq) +{ + ICMR2 |= (1 << (irq - 32 + PXA_IRQ_SKIP)); +} + +static struct irqchip pxa_internal_chip_high = { + .ack = pxa_mask_high_irq, + .mask = pxa_mask_high_irq, + .unmask = pxa_unmask_high_irq, +}; + +#endif + +/* + * PXA GPIO edge detection for IRQs: + * IRQs are generated on Falling-Edge, Rising-Edge, or both. + * Use this instead of directly setting GRER/GFER. + */ + +static long GPIO_IRQ_rising_edge[4]; +static long GPIO_IRQ_falling_edge[4]; +static long GPIO_IRQ_mask[4]; + +static int pxa_gpio_irq_type(unsigned int irq, unsigned int type) +{ + int gpio, idx; + + gpio = IRQ_TO_GPIO(irq); + idx = gpio >> 5; + + if (type == IRQT_PROBE) { + /* Don't mess with enabled GPIOs using preconfigured edges or + GPIOs set to alternate function during probe */ + if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) & + GPIO_bit(gpio)) + return 0; + if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2))) + return 0; + type = __IRQT_RISEDGE | __IRQT_FALEDGE; + } + + /* printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); */ + + pxa_gpio_mode(gpio | GPIO_IN); + + if (type & __IRQT_RISEDGE) { + /* printk("rising "); */ + __set_bit (gpio, GPIO_IRQ_rising_edge); + } else + __clear_bit (gpio, GPIO_IRQ_rising_edge); + + if (type & __IRQT_FALEDGE) { + /* printk("falling "); */ + __set_bit (gpio, GPIO_IRQ_falling_edge); + } else + __clear_bit (gpio, GPIO_IRQ_falling_edge); + + /* printk("edges\n"); */ + + GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; + GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; + return 0; +} + +/* + * GPIO IRQs must be acknowledged. This is for GPIO 0 and 1. + */ + +static void pxa_ack_low_gpio(unsigned int irq) +{ + GEDR0 = (1 << (irq - IRQ_GPIO0)); +} + +static struct irqchip pxa_low_gpio_chip = { + .ack = pxa_ack_low_gpio, + .mask = pxa_mask_low_irq, + .unmask = pxa_unmask_low_irq, + .type = pxa_gpio_irq_type, +}; + +/* + * Demux handler for GPIO>=2 edge detect interrupts + */ + +static void pxa_gpio_demux_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask; + int loop; + + do { + loop = 0; + + mask = GEDR0 & ~3; + if (mask) { + GEDR0 = mask; + irq = IRQ_GPIO(2); + desc = irq_desc + irq; + mask >>= 2; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + irq++; + desc++; + mask >>= 1; + } while (mask); + loop = 1; + } + + mask = GEDR1; + if (mask) { + GEDR1 = mask; + irq = IRQ_GPIO(32); + desc = irq_desc + irq; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + irq++; + desc++; + mask >>= 1; + } while (mask); + loop = 1; + } + + mask = GEDR2; + if (mask) { + GEDR2 = mask; + irq = IRQ_GPIO(64); + desc = irq_desc + irq; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + irq++; + desc++; + mask >>= 1; + } while (mask); + loop = 1; + } + +#if PXA_LAST_GPIO >= 96 + mask = GEDR3; + if (mask) { + GEDR3 = mask; + irq = IRQ_GPIO(96); + desc = irq_desc + irq; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + irq++; + desc++; + mask >>= 1; + } while (mask); + loop = 1; + } +#endif + } while (loop); +} + +static void pxa_ack_muxed_gpio(unsigned int irq) +{ + int gpio = irq - IRQ_GPIO(2) + 2; + GEDR(gpio) = GPIO_bit(gpio); +} + +static void pxa_mask_muxed_gpio(unsigned int irq) +{ + int gpio = irq - IRQ_GPIO(2) + 2; + __clear_bit(gpio, GPIO_IRQ_mask); + GRER(gpio) &= ~GPIO_bit(gpio); + GFER(gpio) &= ~GPIO_bit(gpio); +} + +static void pxa_unmask_muxed_gpio(unsigned int irq) +{ + int gpio = irq - IRQ_GPIO(2) + 2; + int idx = gpio >> 5; + __set_bit(gpio, GPIO_IRQ_mask); + GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; + GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; +} + +static struct irqchip pxa_muxed_gpio_chip = { + .ack = pxa_ack_muxed_gpio, + .mask = pxa_mask_muxed_gpio, + .unmask = pxa_unmask_muxed_gpio, + .type = pxa_gpio_irq_type, +}; + + +void __init pxa_init_irq(void) +{ + int irq; + + /* disable all IRQs */ + ICMR = 0; + + /* all IRQs are IRQ, not FIQ */ + ICLR = 0; + + /* clear all GPIO edge detects */ + GFER0 = 0; + GFER1 = 0; + GFER2 = 0; + GRER0 = 0; + GRER1 = 0; + GRER2 = 0; + GEDR0 = GEDR0; + GEDR1 = GEDR1; + GEDR2 = GEDR2; + +#ifdef CONFIG_PXA27x + /* And similarly for the extra regs on the PXA27x */ + ICMR2 = 0; + ICLR2 = 0; + GFER3 = 0; + GRER3 = 0; + GEDR3 = GEDR3; +#endif + + /* only unmasked interrupts kick us out of idle */ + ICCR = 1; + + /* GPIO 0 and 1 must have their mask bit always set */ + GPIO_IRQ_mask[0] = 3; + + for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq <= PXA_IRQ(31); irq++) { + set_irq_chip(irq, &pxa_internal_chip_low); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + +#if PXA_INTERNAL_IRQS > 32 + for (irq = PXA_IRQ(32); irq < PXA_IRQ(PXA_INTERNAL_IRQS); irq++) { + set_irq_chip(irq, &pxa_internal_chip_high); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } +#endif + + for (irq = IRQ_GPIO0; irq <= IRQ_GPIO1; irq++) { + set_irq_chip(irq, &pxa_low_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + for (irq = IRQ_GPIO(2); irq <= IRQ_GPIO(PXA_LAST_GPIO); irq++) { + set_irq_chip(irq, &pxa_muxed_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + /* Install handler for GPIO>=2 edge detect interrupts */ + set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low); + set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler); +} diff --git a/arch/arm/mach-pxa/leds-idp.c b/arch/arm/mach-pxa/leds-idp.c new file mode 100644 index 00000000000..5eba6ea0b0f --- /dev/null +++ b/arch/arm/mach-pxa/leds-idp.c @@ -0,0 +1,117 @@ +/* + * linux/arch/arm/mach-pxa/leds-idp.c + * + * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu> + * + * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com> + * + * Original (leds-footbridge.c) by Russell King + * + * Macros for actual LED manipulation should be in machine specific + * files in this 'mach' directory. + */ + + +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/idp.h> + +#include "leds.h" + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +void idp_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = IDP_HB_LED | IDP_BUSY_LED; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = IDP_HB_LED | IDP_BUSY_LED; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = IDP_HB_LED | IDP_BUSY_LED; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= IDP_HB_LED; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~IDP_BUSY_LED; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= IDP_BUSY_LED; + break; +#endif + + case led_halted: + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= IDP_HB_LED; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~IDP_HB_LED; + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= IDP_BUSY_LED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~IDP_BUSY_LED; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + IDP_CPLD_LED_CONTROL = ( (IDP_CPLD_LED_CONTROL | IDP_LEDS_MASK) & ~hw_led_state); + else + IDP_CPLD_LED_CONTROL |= IDP_LEDS_MASK; + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-pxa/leds-lubbock.c b/arch/arm/mach-pxa/leds-lubbock.c new file mode 100644 index 00000000000..05cf56059a0 --- /dev/null +++ b/arch/arm/mach-pxa/leds-lubbock.c @@ -0,0 +1,126 @@ +/* + * linux/arch/arm/mach-pxa/leds-lubbock.c + * + * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu> + * + * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com> + * + * Original (leds-footbridge.c) by Russell King + * + * Major surgery on April 2004 by Nicolas Pitre for less global + * namespace collision. Mostly adapted the Mainstone version. + */ + +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/lubbock.h> + +#include "leds.h" + +/* + * 8 discrete leds available for general use: + * + * Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays + * so be sure to not monkey with them here. + */ + +#define D28 (1 << 0) +#define D27 (1 << 1) +#define D26 (1 << 2) +#define D25 (1 << 3) +#define D24 (1 << 4) +#define D23 (1 << 5) +#define D22 (1 << 6) +#define D21 (1 << 7) + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +void lubbock_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = 0; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + hw_led_state ^= D26; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + hw_led_state &= ~D27; + break; + + case led_idle_end: + hw_led_state |= D27; + break; +#endif + + case led_halted: + break; + + case led_green_on: + hw_led_state |= D21; + break; + + case led_green_off: + hw_led_state &= ~D21; + break; + + case led_amber_on: + hw_led_state |= D22; + break; + + case led_amber_off: + hw_led_state &= ~D22; + break; + + case led_red_on: + hw_led_state |= D23; + break; + + case led_red_off: + hw_led_state &= ~D23; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + LUB_DISC_BLNK_LED = (LUB_DISC_BLNK_LED | 0xff) & ~hw_led_state; + else + LUB_DISC_BLNK_LED |= 0xff; + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-pxa/leds-mainstone.c b/arch/arm/mach-pxa/leds-mainstone.c new file mode 100644 index 00000000000..bbd3f87a9fc --- /dev/null +++ b/arch/arm/mach-pxa/leds-mainstone.c @@ -0,0 +1,121 @@ +/* + * linux/arch/arm/mach-pxa/leds-mainstone.c + * + * Author: Nicolas Pitre + * Created: Nov 05, 2002 + * Copyright: MontaVista Software Inc. + * + * 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/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/mainstone.h> + +#include "leds.h" + + +/* 8 discrete leds available for general use: */ +#define D28 (1 << 0) +#define D27 (1 << 1) +#define D26 (1 << 2) +#define D25 (1 << 3) +#define D24 (1 << 4) +#define D23 (1 << 5) +#define D22 (1 << 6) +#define D21 (1 << 7) + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +void mainstone_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = 0; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + hw_led_state ^= D26; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + hw_led_state &= ~D27; + break; + + case led_idle_end: + hw_led_state |= D27; + break; +#endif + + case led_halted: + break; + + case led_green_on: + hw_led_state |= D21;; + break; + + case led_green_off: + hw_led_state &= ~D21; + break; + + case led_amber_on: + hw_led_state |= D22;; + break; + + case led_amber_off: + hw_led_state &= ~D22; + break; + + case led_red_on: + hw_led_state |= D23;; + break; + + case led_red_off: + hw_led_state &= ~D23; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + MST_LEDCTRL = (MST_LEDCTRL | 0xff) & ~hw_led_state; + else + MST_LEDCTRL |= 0xff; + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-pxa/leds.c b/arch/arm/mach-pxa/leds.c new file mode 100644 index 00000000000..bbe4d5f6afa --- /dev/null +++ b/arch/arm/mach-pxa/leds.c @@ -0,0 +1,32 @@ +/* + * linux/arch/arm/mach-pxa/leds.c + * + * xscale LEDs dispatcher + * + * Copyright (C) 2001 Nicolas Pitre + * + * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc. + */ +#include <linux/compiler.h> +#include <linux/init.h> + +#include <asm/leds.h> +#include <asm/mach-types.h> + +#include "leds.h" + +static int __init +pxa_leds_init(void) +{ + if (machine_is_lubbock()) + leds_event = lubbock_leds_event; + if (machine_is_mainstone()) + leds_event = mainstone_leds_event; + if (machine_is_pxa_idp()) + leds_event = idp_leds_event; + + leds_event(led_start); + return 0; +} + +core_initcall(pxa_leds_init); diff --git a/arch/arm/mach-pxa/leds.h b/arch/arm/mach-pxa/leds.h new file mode 100644 index 00000000000..d98f6e93c12 --- /dev/null +++ b/arch/arm/mach-pxa/leds.h @@ -0,0 +1,12 @@ +/* + * include/asm-arm/arch-pxa/leds.h + * + * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc. + * + * blinky lights for various PXA-based systems: + * + */ + +extern void idp_leds_event(led_event_t evt); +extern void lubbock_leds_event(led_event_t evt); +extern void mainstone_leds_event(led_event_t evt); diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c new file mode 100644 index 00000000000..dd012d6e2f5 --- /dev/null +++ b/arch/arm/mach-pxa/lubbock.c @@ -0,0 +1,247 @@ +/* + * linux/arch/arm/mach-pxa/lubbock.c + * + * Support for the Intel DBPXA250 Development Platform. + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/major.h> +#include <linux/fb.h> +#include <linux/interrupt.h> + +#include <asm/setup.h> +#include <asm/memory.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/irq.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/hardware/sa1111.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/lubbock.h> +#include <asm/arch/udc.h> +#include <asm/arch/pxafb.h> +#include <asm/arch/mmc.h> + +#include "generic.h" + + +#define LUB_MISC_WR __LUB_REG(LUBBOCK_FPGA_PHYS + 0x080) + +void lubbock_set_misc_wr(unsigned int mask, unsigned int set) +{ + unsigned long flags; + + local_irq_save(flags); + LUB_MISC_WR = (LUB_MISC_WR & ~mask) | (set & mask); + local_irq_restore(flags); +} +EXPORT_SYMBOL(lubbock_set_misc_wr); + +static unsigned long lubbock_irq_enabled; + +static void lubbock_mask_irq(unsigned int irq) +{ + int lubbock_irq = (irq - LUBBOCK_IRQ(0)); + LUB_IRQ_MASK_EN = (lubbock_irq_enabled &= ~(1 << lubbock_irq)); +} + +static void lubbock_unmask_irq(unsigned int irq) +{ + int lubbock_irq = (irq - LUBBOCK_IRQ(0)); + /* the irq can be acknowledged only if deasserted, so it's done here */ + LUB_IRQ_SET_CLR &= ~(1 << lubbock_irq); + LUB_IRQ_MASK_EN = (lubbock_irq_enabled |= (1 << lubbock_irq)); +} + +static struct irqchip lubbock_irq_chip = { + .ack = lubbock_mask_irq, + .mask = lubbock_mask_irq, + .unmask = lubbock_unmask_irq, +}; + +static void lubbock_irq_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned long pending = LUB_IRQ_SET_CLR & lubbock_irq_enabled; + do { + GEDR(0) = GPIO_bit(0); /* clear our parent irq */ + if (likely(pending)) { + irq = LUBBOCK_IRQ(0) + __ffs(pending); + desc = irq_desc + irq; + desc->handle(irq, desc, regs); + } + pending = LUB_IRQ_SET_CLR & lubbock_irq_enabled; + } while (pending); +} + +static void __init lubbock_init_irq(void) +{ + int irq; + + pxa_init_irq(); + + /* setup extra lubbock irqs */ + for (irq = LUBBOCK_IRQ(0); irq <= LUBBOCK_LAST_IRQ; irq++) { + set_irq_chip(irq, &lubbock_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + set_irq_chained_handler(IRQ_GPIO(0), lubbock_irq_handler); + set_irq_type(IRQ_GPIO(0), IRQT_FALLING); +} + +static int lubbock_udc_is_connected(void) +{ + return (LUB_MISC_RD & (1 << 9)) == 0; +} + +static struct pxa2xx_udc_mach_info udc_info __initdata = { + .udc_is_connected = lubbock_udc_is_connected, + // no D+ pullup; lubbock can't connect/disconnect in software +}; + +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x10000000, + .end = 0x10001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = LUBBOCK_SA1111_IRQ, + .end = LUBBOCK_SA1111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = -1, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .name = "smc91x-regs", + .start = 0x0c000000, + .end = 0x0c0fffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = LUBBOCK_ETH_IRQ, + .end = LUBBOCK_ETH_IRQ, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "smc91x-attrib", + .start = 0x0e000000, + .end = 0x0e0fffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = -1, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, + &smc91x_device, +}; + +static struct pxafb_mach_info sharp_lm8v31 __initdata = { + .pixclock = 270000, + .xres = 640, + .yres = 480, + .bpp = 16, + .hsync_len = 1, + .left_margin = 3, + .right_margin = 3, + .vsync_len = 1, + .upper_margin = 0, + .lower_margin = 0, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .cmap_greyscale = 0, + .cmap_inverse = 0, + .cmap_static = 0, + .lccr0 = LCCR0_SDS, + .lccr3 = LCCR3_PCP | LCCR3_Acb(255), +}; + +static int lubbock_mci_init(struct device *dev, irqreturn_t (*lubbock_detect_int)(int, void *, struct pt_regs *), void *data) +{ + /* setup GPIO for PXA25x MMC controller */ + pxa_gpio_mode(GPIO6_MMCCLK_MD); + pxa_gpio_mode(GPIO8_MMCCS0_MD); + + return 0; +} + +static struct pxamci_platform_data lubbock_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = lubbock_mci_init, +}; + +static void __init lubbock_init(void) +{ + pxa_set_udc_info(&udc_info); + set_pxa_fb_info(&sharp_lm8v31); + pxa_set_mci_info(&lubbock_mci_platform_data); + (void) platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static struct map_desc lubbock_io_desc[] __initdata = { + { LUBBOCK_FPGA_VIRT, LUBBOCK_FPGA_PHYS, 0x00100000, MT_DEVICE }, /* CPLD */ +}; + +static void __init lubbock_map_io(void) +{ + pxa_map_io(); + iotable_init(lubbock_io_desc, ARRAY_SIZE(lubbock_io_desc)); + + /* This enables the BTUART */ + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + + /* This is for the SMC chip select */ + pxa_gpio_mode(GPIO79_nCS_3_MD); + + /* setup sleep mode values */ + PWER = 0x00000002; + PFER = 0x00000000; + PRER = 0x00000002; + PGSR0 = 0x00008000; + PGSR1 = 0x003F0202; + PGSR2 = 0x0001C000; + PCFR |= PCFR_OPDE; +} + +MACHINE_START(LUBBOCK, "Intel DBPXA250 Development Platform (aka Lubbock)") + MAINTAINER("MontaVista Software Inc.") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + MAPIO(lubbock_map_io) + INITIRQ(lubbock_init_irq) + .timer = &pxa_timer, + INIT_MACHINE(lubbock_init) +MACHINE_END diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c new file mode 100644 index 00000000000..3f952237ae3 --- /dev/null +++ b/arch/arm/mach-pxa/mainstone.c @@ -0,0 +1,316 @@ +/* + * linux/arch/arm/mach-pxa/mainstone.c + * + * Support for the Intel HCDDBBVA0 Development Platform. + * (go figure how they came up with such name...) + * + * Author: Nicolas Pitre + * Created: Nov 05, 2002 + * Copyright: MontaVista Software Inc. + * + * 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/device.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/bitops.h> +#include <linux/fb.h> + +#include <asm/types.h> +#include <asm/setup.h> +#include <asm/memory.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/irq.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/mainstone.h> +#include <asm/arch/audio.h> +#include <asm/arch/pxafb.h> +#include <asm/arch/mmc.h> + +#include "generic.h" + + +static unsigned long mainstone_irq_enabled; + +static void mainstone_mask_irq(unsigned int irq) +{ + int mainstone_irq = (irq - MAINSTONE_IRQ(0)); + MST_INTMSKENA = (mainstone_irq_enabled &= ~(1 << mainstone_irq)); +} + +static void mainstone_unmask_irq(unsigned int irq) +{ + int mainstone_irq = (irq - MAINSTONE_IRQ(0)); + /* the irq can be acknowledged only if deasserted, so it's done here */ + MST_INTSETCLR &= ~(1 << mainstone_irq); + MST_INTMSKENA = (mainstone_irq_enabled |= (1 << mainstone_irq)); +} + +static struct irqchip mainstone_irq_chip = { + .ack = mainstone_mask_irq, + .mask = mainstone_mask_irq, + .unmask = mainstone_unmask_irq, +}; + + +static void mainstone_irq_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned long pending = MST_INTSETCLR & mainstone_irq_enabled; + do { + GEDR(0) = GPIO_bit(0); /* clear useless edge notification */ + if (likely(pending)) { + irq = MAINSTONE_IRQ(0) + __ffs(pending); + desc = irq_desc + irq; + desc->handle(irq, desc, regs); + } + pending = MST_INTSETCLR & mainstone_irq_enabled; + } while (pending); +} + +static void __init mainstone_init_irq(void) +{ + int irq; + + pxa_init_irq(); + + /* setup extra Mainstone irqs */ + for(irq = MAINSTONE_IRQ(0); irq <= MAINSTONE_IRQ(15); irq++) { + set_irq_chip(irq, &mainstone_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + set_irq_flags(MAINSTONE_IRQ(8), 0); + set_irq_flags(MAINSTONE_IRQ(12), 0); + + MST_INTMSKENA = 0; + MST_INTSETCLR = 0; + + set_irq_chained_handler(IRQ_GPIO(0), mainstone_irq_handler); + set_irq_type(IRQ_GPIO(0), IRQT_FALLING); +} + + +static struct resource smc91x_resources[] = { + [0] = { + .start = (MST_ETH_PHYS + 0x300), + .end = (MST_ETH_PHYS + 0xfffff), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MAINSTONE_IRQ(3), + .end = MAINSTONE_IRQ(3), + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int mst_audio_startup(snd_pcm_substream_t *substream, void *priv) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF; + return 0; +} + +static void mst_audio_shutdown(snd_pcm_substream_t *substream, void *priv) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF; +} + +static long mst_audio_suspend_mask; + +static void mst_audio_suspend(void *priv) +{ + mst_audio_suspend_mask = MST_MSCWR2; + MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF; +} + +static void mst_audio_resume(void *priv) +{ + MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF; +} + +static pxa2xx_audio_ops_t mst_audio_ops = { + .startup = mst_audio_startup, + .shutdown = mst_audio_shutdown, + .suspend = mst_audio_suspend, + .resume = mst_audio_resume, +}; + +static struct platform_device mst_audio_device = { + .name = "pxa2xx-ac97", + .id = -1, + .dev = { .platform_data = &mst_audio_ops }, +}; + +static void mainstone_backlight_power(int on) +{ + if (on) { + pxa_gpio_mode(GPIO16_PWM0_MD); + pxa_set_cken(CKEN0_PWM0, 1); + PWM_CTRL0 = 0; + PWM_PWDUTY0 = 0x3ff; + PWM_PERVAL0 = 0x3ff; + } else { + PWM_CTRL0 = 0; + PWM_PWDUTY0 = 0x0; + PWM_PERVAL0 = 0x3FF; + pxa_set_cken(CKEN0_PWM0, 0); + } +} + +static struct pxafb_mach_info toshiba_ltm04c380k __initdata = { + .pixclock = 50000, + .xres = 640, + .yres = 480, + .bpp = 16, + .hsync_len = 1, + .left_margin = 0x9f, + .right_margin = 1, + .vsync_len = 44, + .upper_margin = 0, + .lower_margin = 0, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, + .lccr0 = LCCR0_Act, + .lccr3 = LCCR3_PCP, + .pxafb_backlight_power = mainstone_backlight_power, +}; + +static struct pxafb_mach_info toshiba_ltm035a776c __initdata = { + .pixclock = 110000, + .xres = 240, + .yres = 320, + .bpp = 16, + .hsync_len = 4, + .left_margin = 8, + .right_margin = 20, + .vsync_len = 3, + .upper_margin = 1, + .lower_margin = 10, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, + .lccr0 = LCCR0_Act, + .lccr3 = LCCR3_PCP, + .pxafb_backlight_power = mainstone_backlight_power, +}; + +static int mainstone_mci_init(struct device *dev, irqreturn_t (*mstone_detect_int)(int, void *, struct pt_regs *), void *data) +{ + int err; + + /* + * setup GPIO for PXA27x MMC controller + */ + pxa_gpio_mode(GPIO32_MMCCLK_MD); + pxa_gpio_mode(GPIO112_MMCCMD_MD); + pxa_gpio_mode(GPIO92_MMCDAT0_MD); + pxa_gpio_mode(GPIO109_MMCDAT1_MD); + pxa_gpio_mode(GPIO110_MMCDAT2_MD); + pxa_gpio_mode(GPIO111_MMCDAT3_MD); + + /* make sure SD/Memory Stick multiplexer's signals + * are routed to MMC controller + */ + MST_MSCWR1 &= ~MST_MSCWR1_MS_SEL; + + err = request_irq(MAINSTONE_MMC_IRQ, mstone_detect_int, SA_INTERRUPT, + "MMC card detect", data); + if (err) { + printk(KERN_ERR "mainstone_mci_init: MMC/SD: can't request MMC card detect IRQ\n"); + return -1; + } + + return 0; +} + +static void mainstone_mci_setpower(struct device *dev, unsigned int vdd) +{ + struct pxamci_platform_data* p_d = dev->platform_data; + + if (( 1 << vdd) & p_d->ocr_mask) { + printk(KERN_DEBUG "%s: on\n", __FUNCTION__); + MST_MSCWR1 |= MST_MSCWR1_MMC_ON; + MST_MSCWR1 &= ~MST_MSCWR1_MS_SEL; + } else { + printk(KERN_DEBUG "%s: off\n", __FUNCTION__); + MST_MSCWR1 &= ~MST_MSCWR1_MMC_ON; + } +} + +static void mainstone_mci_exit(struct device *dev, void *data) +{ + free_irq(MAINSTONE_MMC_IRQ, data); +} + +static struct pxamci_platform_data mainstone_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = mainstone_mci_init, + .setpower = mainstone_mci_setpower, + .exit = mainstone_mci_exit, +}; + +static void __init mainstone_init(void) +{ + /* + * On Mainstone, we route AC97_SYSCLK via GPIO45 to + * the audio daughter card + */ + pxa_gpio_mode(GPIO45_SYSCLK_AC97_MD); + + platform_device_register(&smc91x_device); + platform_device_register(&mst_audio_device); + + /* reading Mainstone's "Virtual Configuration Register" + might be handy to select LCD type here */ + if (0) + set_pxa_fb_info(&toshiba_ltm04c380k); + else + set_pxa_fb_info(&toshiba_ltm035a776c); + + pxa_set_mci_info(&mainstone_mci_platform_data); +} + + +static struct map_desc mainstone_io_desc[] __initdata = { + { MST_FPGA_VIRT, MST_FPGA_PHYS, 0x00100000, MT_DEVICE }, /* CPLD */ +}; + +static void __init mainstone_map_io(void) +{ + pxa_map_io(); + iotable_init(mainstone_io_desc, ARRAY_SIZE(mainstone_io_desc)); + + /* initialize sleep mode regs (wake-up sources, etc) */ + PGSR0 = 0x00008800; + PGSR1 = 0x00000002; + PGSR2 = 0x0001FC00; + PGSR3 = 0x00001F81; + PWER = 0xC0000002; + PRER = 0x00000002; + PFER = 0x00000002; +} + +MACHINE_START(MAINSTONE, "Intel HCDDBBVA0 Development Platform (aka Mainstone)") + MAINTAINER("MontaVista Software Inc.") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + MAPIO(mainstone_map_io) + INITIRQ(mainstone_init_irq) + .timer = &pxa_timer, + INIT_MACHINE(mainstone_init) +MACHINE_END diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c new file mode 100644 index 00000000000..82a4bf34c25 --- /dev/null +++ b/arch/arm/mach-pxa/pm.c @@ -0,0 +1,227 @@ +/* + * PXA250/210 Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/time.h> + +#include <asm/hardware.h> +#include <asm/memory.h> +#include <asm/system.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/lubbock.h> +#include <asm/mach/time.h> + + +/* + * Debug macros + */ +#undef DEBUG + +extern void pxa_cpu_suspend(void); +extern void pxa_cpu_resume(void); + +#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x +#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] + +#define RESTORE_GPLEVEL(n) do { \ + GPSR##n = sleep_save[SLEEP_SAVE_GPLR##n]; \ + GPCR##n = ~sleep_save[SLEEP_SAVE_GPLR##n]; \ +} while (0) + +/* + * List of global PXA peripheral registers to preserve. + * More ones like CP and general purpose register values are preserved + * with the stack pointer in sleep.S. + */ +enum { SLEEP_SAVE_START = 0, + + SLEEP_SAVE_GPLR0, SLEEP_SAVE_GPLR1, SLEEP_SAVE_GPLR2, SLEEP_SAVE_GPLR3, + SLEEP_SAVE_GPDR0, SLEEP_SAVE_GPDR1, SLEEP_SAVE_GPDR2, SLEEP_SAVE_GPDR3, + SLEEP_SAVE_GRER0, SLEEP_SAVE_GRER1, SLEEP_SAVE_GRER2, SLEEP_SAVE_GRER3, + SLEEP_SAVE_GFER0, SLEEP_SAVE_GFER1, SLEEP_SAVE_GFER2, SLEEP_SAVE_GFER3, + SLEEP_SAVE_PGSR0, SLEEP_SAVE_PGSR1, SLEEP_SAVE_PGSR2, SLEEP_SAVE_PGSR3, + + SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR0_U, + SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR1_U, + SLEEP_SAVE_GAFR2_L, SLEEP_SAVE_GAFR2_U, + SLEEP_SAVE_GAFR3_L, SLEEP_SAVE_GAFR3_U, + + SLEEP_SAVE_PSTR, + + SLEEP_SAVE_ICMR, + SLEEP_SAVE_CKEN, + + SLEEP_SAVE_CKSUM, + + SLEEP_SAVE_SIZE +}; + + +static int pxa_pm_enter(suspend_state_t state) +{ + unsigned long sleep_save[SLEEP_SAVE_SIZE]; + unsigned long checksum = 0; + struct timespec delta, rtc; + int i; + + if (state != PM_SUSPEND_MEM) + return -EINVAL; + +#ifdef CONFIG_IWMMXT + /* force any iWMMXt context to ram **/ + iwmmxt_task_disable(NULL); +#endif + + /* preserve current time */ + rtc.tv_sec = RCNR; + rtc.tv_nsec = 0; + save_time_delta(&delta, &rtc); + + SAVE(GPLR0); SAVE(GPLR1); SAVE(GPLR2); + SAVE(GPDR0); SAVE(GPDR1); SAVE(GPDR2); + SAVE(GRER0); SAVE(GRER1); SAVE(GRER2); + SAVE(GFER0); SAVE(GFER1); SAVE(GFER2); + SAVE(PGSR0); SAVE(PGSR1); SAVE(PGSR2); + + SAVE(GAFR0_L); SAVE(GAFR0_U); + SAVE(GAFR1_L); SAVE(GAFR1_U); + SAVE(GAFR2_L); SAVE(GAFR2_U); + +#ifdef CONFIG_PXA27x + SAVE(GPLR3); SAVE(GPDR3); SAVE(GRER3); SAVE(GFER3); SAVE(PGSR3); + SAVE(GAFR3_L); SAVE(GAFR3_U); +#endif + + SAVE(ICMR); + ICMR = 0; + + SAVE(CKEN); + CKEN = 0; + + SAVE(PSTR); + + /* Note: wake up source are set up in each machine specific files */ + + /* clear GPIO transition detect bits */ + GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; +#ifdef CONFIG_PXA27x + GEDR3 = GEDR3; +#endif + + /* Clear sleep reset status */ + RCSR = RCSR_SMR; + + /* set resume return address */ + PSPR = virt_to_phys(pxa_cpu_resume); + + /* before sleeping, calculate and save a checksum */ + for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) + checksum += sleep_save[i]; + sleep_save[SLEEP_SAVE_CKSUM] = checksum; + + /* *** go zzz *** */ + pxa_cpu_suspend(); + + /* after sleeping, validate the checksum */ + checksum = 0; + for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) + checksum += sleep_save[i]; + + /* if invalid, display message and wait for a hardware reset */ + if (checksum != sleep_save[SLEEP_SAVE_CKSUM]) { +#ifdef CONFIG_ARCH_LUBBOCK + LUB_HEXLED = 0xbadbadc5; +#endif + while (1) + pxa_cpu_suspend(); + } + + /* ensure not to come back here if it wasn't intended */ + PSPR = 0; + + /* restore registers */ + RESTORE(GAFR0_L); RESTORE(GAFR0_U); + RESTORE(GAFR1_L); RESTORE(GAFR1_U); + RESTORE(GAFR2_L); RESTORE(GAFR2_U); + RESTORE_GPLEVEL(0); RESTORE_GPLEVEL(1); RESTORE_GPLEVEL(2); + RESTORE(GPDR0); RESTORE(GPDR1); RESTORE(GPDR2); + RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2); + RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2); + RESTORE(PGSR0); RESTORE(PGSR1); RESTORE(PGSR2); + +#ifdef CONFIG_PXA27x + RESTORE(GAFR3_L); RESTORE(GAFR3_U); RESTORE_GPLEVEL(3); + RESTORE(GPDR3); RESTORE(GRER3); RESTORE(GFER3); RESTORE(PGSR3); +#endif + + PSSR = PSSR_RDH | PSSR_PH; + + RESTORE(CKEN); + + ICLR = 0; + ICCR = 1; + RESTORE(ICMR); + + RESTORE(PSTR); + + /* restore current time */ + rtc.tv_sec = RCNR; + restore_time_delta(&delta, &rtc); + +#ifdef DEBUG + printk(KERN_DEBUG "*** made it back from resume\n"); +#endif + + return 0; +} + +unsigned long sleep_phys_sp(void *sp) +{ + return virt_to_phys(sp); +} + +/* + * Called after processes are frozen, but before we shut down devices. + */ +static int pxa_pm_prepare(suspend_state_t state) +{ + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static int pxa_pm_finish(suspend_state_t state) +{ + return 0; +} + +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct pm_ops pxa_pm_ops = { + .pm_disk_mode = PM_DISK_FIRMWARE, + .prepare = pxa_pm_prepare, + .enter = pxa_pm_enter, + .finish = pxa_pm_finish, +}; + +static int __init pxa_pm_init(void) +{ + pm_set_ops(&pxa_pm_ops); + return 0; +} + +late_initcall(pxa_pm_init); diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c new file mode 100644 index 00000000000..b6c746ea383 --- /dev/null +++ b/arch/arm/mach-pxa/poodle.c @@ -0,0 +1,189 @@ +/* + * linux/arch/arm/mach-pxa/poodle.c + * + * Support for the SHARP Poodle Board. + * + * Based on: + * linux/arch/arm/mach-pxa/lubbock.c Author: Nicolas Pitre + * + * 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. + * + * Change Log + * 12-Dec-2002 Sharp Corporation for Poodle + * John Lenz <lenz@cs.wisc.edu> updates to 2.6 + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/fb.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/irq.h> +#include <asm/setup.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/irq.h> +#include <asm/arch/poodle.h> +#include <asm/arch/pxafb.h> + +#include <asm/hardware/scoop.h> +#include <asm/hardware/locomo.h> +#include <asm/mach/sharpsl_param.h> + +#include "generic.h" + +static struct resource poodle_scoop_resources[] = { + [0] = { + .start = 0x10800000, + .end = 0x10800fff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct scoop_config poodle_scoop_setup = { + .io_dir = POODLE_SCOOP_IO_DIR, + .io_out = POODLE_SCOOP_IO_OUT, +}; + +struct platform_device poodle_scoop_device = { + .name = "sharp-scoop", + .id = -1, + .dev = { + .platform_data = &poodle_scoop_setup, + }, + .num_resources = ARRAY_SIZE(poodle_scoop_resources), + .resource = poodle_scoop_resources, +}; + + +/* LoCoMo device */ +static struct resource locomo_resources[] = { + [0] = { + .start = 0x10000000, + .end = 0x10001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO(10), + .end = IRQ_GPIO(10), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device locomo_device = { + .name = "locomo", + .id = 0, + .num_resources = ARRAY_SIZE(locomo_resources), + .resource = locomo_resources, +}; + +/* PXAFB device */ +static struct pxafb_mach_info poodle_fb_info __initdata = { + .pixclock = 144700, + + .xres = 320, + .yres = 240, + .bpp = 16, + + .hsync_len = 7, + .left_margin = 11, + .right_margin = 30, + + .vsync_len = 2, + .upper_margin = 2, + .lower_margin = 0, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + + .lccr0 = LCCR0_Act | LCCR0_Sngl | LCCR0_Color, + .lccr3 = 0, + + .pxafb_backlight_power = NULL, + .pxafb_lcd_power = NULL, +}; + +static struct platform_device *devices[] __initdata = { + &locomo_device, + &poodle_scoop_device, +}; + +static void __init poodle_init(void) +{ + int ret = 0; + + /* cpu initialize */ + /* Pgsr Register */ + PGSR0 = 0x0146dd80; + PGSR1 = 0x03bf0890; + PGSR2 = 0x0001c000; + + /* Alternate Register */ + GAFR0_L = 0x01001000; + GAFR0_U = 0x591a8010; + GAFR1_L = 0x900a8451; + GAFR1_U = 0xaaa5aaaa; + GAFR2_L = 0x8aaaaaaa; + GAFR2_U = 0x00000002; + + /* Direction Register */ + GPDR0 = 0xd3f0904c; + GPDR1 = 0xfcffb7d3; + GPDR2 = 0x0001ffff; + + /* Output Register */ + GPCR0 = 0x00000000; + GPCR1 = 0x00000000; + GPCR2 = 0x00000000; + + GPSR0 = 0x00400000; + GPSR1 = 0x00000000; + GPSR2 = 0x00000000; + + set_pxa_fb_info(&poodle_fb_info); + + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if (ret) { + printk(KERN_WARNING "poodle: Unable to register LoCoMo device\n"); + } +} + +static void __init fixup_poodle(struct machine_desc *desc, + struct tag *tags, char **cmdline, struct meminfo *mi) +{ + sharpsl_save_param(); +} + +static struct map_desc poodle_io_desc[] __initdata = { + /* virtual physical length */ + { 0xef800000, 0x00000000, 0x00800000, MT_DEVICE }, /* Boot Flash */ +}; + +static void __init poodle_map_io(void) +{ + pxa_map_io(); + iotable_init(poodle_io_desc, ARRAY_SIZE(poodle_io_desc)); + + /* setup sleep mode values */ + PWER = 0x00000002; + PFER = 0x00000000; + PRER = 0x00000002; + PGSR0 = 0x00008000; + PGSR1 = 0x003F0202; + PGSR2 = 0x0001C000; + PCFR |= PCFR_OPDE; +} + +MACHINE_START(POODLE, "SHARP Poodle") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + FIXUP(fixup_poodle) + MAPIO(poodle_map_io) + INITIRQ(pxa_init_irq) + .timer = &pxa_timer, + .init_machine = poodle_init, +MACHINE_END diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c new file mode 100644 index 00000000000..e887b7175ef --- /dev/null +++ b/arch/arm/mach-pxa/pxa25x.c @@ -0,0 +1,104 @@ +/* + * linux/arch/arm/mach-pxa/pxa25x.c + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * Code specific to PXA21x/25x/26x variants. + * + * 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. + * + * Since this file should be linked before any other machine specific file, + * the __initcall() here will be executed first. This serves as default + * initialization stuff for PXA machines which can be overridden later if + * need be. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pm.h> + +#include <asm/hardware.h> +#include <asm/arch/pxa-regs.h> + +#include "generic.h" + +/* + * Various clock factors driven by the CCCR register. + */ + +/* Crystal Frequency to Memory Frequency Multiplier (L) */ +static unsigned char L_clk_mult[32] = { 0, 27, 32, 36, 40, 45, 0, }; + +/* Memory Frequency to Run Mode Frequency Multiplier (M) */ +static unsigned char M_clk_mult[4] = { 0, 1, 2, 4 }; + +/* Run Mode Frequency to Turbo Mode Frequency Multiplier (N) */ +/* Note: we store the value N * 2 here. */ +static unsigned char N2_clk_mult[8] = { 0, 0, 2, 3, 4, 0, 6, 0 }; + +/* Crystal clock */ +#define BASE_CLK 3686400 + +/* + * Get the clock frequency as reflected by CCCR and the turbo flag. + * We assume these values have been applied via a fcs. + * If info is not 0 we also display the current settings. + */ +unsigned int get_clk_frequency_khz(int info) +{ + unsigned long cccr, turbo; + unsigned int l, L, m, M, n2, N; + + cccr = CCCR; + asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (turbo) ); + + l = L_clk_mult[(cccr >> 0) & 0x1f]; + m = M_clk_mult[(cccr >> 5) & 0x03]; + n2 = N2_clk_mult[(cccr >> 7) & 0x07]; + + L = l * BASE_CLK; + M = m * L; + N = n2 * M / 2; + + if(info) + { + L += 5000; + printk( KERN_INFO "Memory clock: %d.%02dMHz (*%d)\n", + L / 1000000, (L % 1000000) / 10000, l ); + M += 5000; + printk( KERN_INFO "Run Mode clock: %d.%02dMHz (*%d)\n", + M / 1000000, (M % 1000000) / 10000, m ); + N += 5000; + printk( KERN_INFO "Turbo Mode clock: %d.%02dMHz (*%d.%d, %sactive)\n", + N / 1000000, (N % 1000000) / 10000, n2 / 2, (n2 % 2) * 5, + (turbo & 1) ? "" : "in" ); + } + + return (turbo & 1) ? (N/1000) : (M/1000); +} + +EXPORT_SYMBOL(get_clk_frequency_khz); + +/* + * Return the current memory clock frequency in units of 10kHz + */ +unsigned int get_memclk_frequency_10khz(void) +{ + return L_clk_mult[(CCCR >> 0) & 0x1f] * BASE_CLK / 10000; +} + +EXPORT_SYMBOL(get_memclk_frequency_10khz); + +/* + * Return the current LCD clock frequency in units of 10kHz + */ +unsigned int get_lcdclk_frequency_10khz(void) +{ + return get_memclk_frequency_10khz(); +} + +EXPORT_SYMBOL(get_lcdclk_frequency_10khz); diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c new file mode 100644 index 00000000000..7e863afefb5 --- /dev/null +++ b/arch/arm/mach-pxa/pxa27x.c @@ -0,0 +1,163 @@ +/* + * linux/arch/arm/mach-pxa/pxa27x.c + * + * Author: Nicolas Pitre + * Created: Nov 05, 2002 + * Copyright: MontaVista Software Inc. + * + * Code specific to PXA27x aka Bulverde. + * + * 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/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/device.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/arch/pxa-regs.h> + +#include "generic.h" + +/* Crystal clock: 13MHz */ +#define BASE_CLK 13000000 + +/* + * Get the clock frequency as reflected by CCSR and the turbo flag. + * We assume these values have been applied via a fcs. + * If info is not 0 we also display the current settings. + */ +unsigned int get_clk_frequency_khz( int info) +{ + unsigned long ccsr, clkcfg; + unsigned int l, L, m, M, n2, N, S; + int cccr_a, t, ht, b; + + ccsr = CCSR; + cccr_a = CCCR & (1 << 25); + + /* Read clkcfg register: it has turbo, b, half-turbo (and f) */ + asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg) ); + t = clkcfg & (1 << 1); + ht = clkcfg & (1 << 2); + b = clkcfg & (1 << 3); + + l = ccsr & 0x1f; + n2 = (ccsr>>7) & 0xf; + m = (l <= 10) ? 1 : (l <= 20) ? 2 : 4; + + L = l * BASE_CLK; + N = (L * n2) / 2; + M = (!cccr_a) ? (L/m) : ((b) ? L : (L/2)); + S = (b) ? L : (L/2); + + if (info) { + printk( KERN_INFO "Run Mode clock: %d.%02dMHz (*%d)\n", + L / 1000000, (L % 1000000) / 10000, l ); + printk( KERN_INFO "Turbo Mode clock: %d.%02dMHz (*%d.%d, %sactive)\n", + N / 1000000, (N % 1000000)/10000, n2 / 2, (n2 % 2)*5, + (t) ? "" : "in" ); + printk( KERN_INFO "Memory clock: %d.%02dMHz (/%d)\n", + M / 1000000, (M % 1000000) / 10000, m ); + printk( KERN_INFO "System bus clock: %d.%02dMHz \n", + S / 1000000, (S % 1000000) / 10000 ); + } + + return (t) ? (N/1000) : (L/1000); +} + +/* + * Return the current mem clock frequency in units of 10kHz as + * reflected by CCCR[A], B, and L + */ +unsigned int get_memclk_frequency_10khz(void) +{ + unsigned long ccsr, clkcfg; + unsigned int l, L, m, M; + int cccr_a, b; + + ccsr = CCSR; + cccr_a = CCCR & (1 << 25); + + /* Read clkcfg register: it has turbo, b, half-turbo (and f) */ + asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg) ); + b = clkcfg & (1 << 3); + + l = ccsr & 0x1f; + m = (l <= 10) ? 1 : (l <= 20) ? 2 : 4; + + L = l * BASE_CLK; + M = (!cccr_a) ? (L/m) : ((b) ? L : (L/2)); + + return (M / 10000); +} + +/* + * Return the current LCD clock frequency in units of 10kHz as + */ +unsigned int get_lcdclk_frequency_10khz(void) +{ + unsigned long ccsr; + unsigned int l, L, k, K; + + ccsr = CCSR; + + l = ccsr & 0x1f; + k = (l <= 7) ? 1 : (l <= 16) ? 2 : 4; + + L = l * BASE_CLK; + K = L / k; + + return (K / 10000); +} + +EXPORT_SYMBOL(get_clk_frequency_khz); +EXPORT_SYMBOL(get_memclk_frequency_10khz); +EXPORT_SYMBOL(get_lcdclk_frequency_10khz); + + +/* + * device registration specific to PXA27x. + */ + +static u64 pxa27x_dmamask = 0xffffffffUL; + +static struct resource pxa27x_ohci_resources[] = { + [0] = { + .start = 0x4C000000, + .end = 0x4C00ff6f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBH1, + .end = IRQ_USBH1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci_device = { + .name = "pxa27x-ohci", + .id = -1, + .dev = { + .dma_mask = &pxa27x_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxa27x_ohci_resources), + .resource = pxa27x_ohci_resources, +}; + +static struct platform_device *devices[] __initdata = { + &ohci_device, +}; + +static int __init pxa27x_init(void) +{ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(pxa27x_init); diff --git a/arch/arm/mach-pxa/sleep.S b/arch/arm/mach-pxa/sleep.S new file mode 100644 index 00000000000..16cad2c2497 --- /dev/null +++ b/arch/arm/mach-pxa/sleep.S @@ -0,0 +1,194 @@ +/* + * Low-level PXA250/210 sleep/wakeUp support + * + * Initial SA1110 code: + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * Adapted for PXA by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + +#include <linux/config.h> +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/hardware.h> + +#include <asm/arch/pxa-regs.h> + + .text + +/* + * pxa_cpu_suspend() + * + * Forces CPU into sleep state + */ + +ENTRY(pxa_cpu_suspend) + + mra r2, r3, acc0 + stmfd sp!, {r2 - r12, lr} @ save registers on stack + + @ get coprocessor registers + mrc p14, 0, r3, c6, c0, 0 @ clock configuration, for turbo mode + mrc p15, 0, r4, c15, c1, 0 @ CP access reg + mrc p15, 0, r5, c13, c0, 0 @ PID + mrc p15, 0, r6, c3, c0, 0 @ domain ID + mrc p15, 0, r7, c2, c0, 0 @ translation table base addr + mrc p15, 0, r8, c1, c1, 0 @ auxiliary control reg + mrc p15, 0, r9, c1, c0, 0 @ control reg + + bic r3, r3, #2 @ clear frequency change bit + + @ store them plus current virtual stack ptr on stack + mov r10, sp + stmfd sp!, {r3 - r10} + + @ preserve phys address of stack + mov r0, sp + bl sleep_phys_sp + ldr r1, =sleep_save_sp + str r0, [r1] + + @ clean data cache + bl xscale_flush_kern_cache_all + + @ Put the processor to sleep + @ (also workaround for sighting 28071) + + @ prepare value for sleep mode + mov r1, #3 @ sleep mode + + @ prepare to put SDRAM into self-refresh manually + ldr r4, =MDREFR + ldr r5, [r4] + orr r5, r5, #MDREFR_SLFRSH + + @ prepare pointer to physical address 0 (virtual mapping in generic.c) + mov r2, #UNCACHED_PHYS_0 + + @ Intel PXA255 Specification Update notes problems + @ about suspending with PXBus operating above 133MHz + @ (see Errata 31, GPIO output signals, ... unpredictable in sleep + @ + @ We keep the change-down close to the actual suspend on SDRAM + @ as possible to eliminate messing about with the refresh clock + @ as the system will restore with the original speed settings + @ + @ Ben Dooks, 13-Sep-2004 + + ldr r6, =CCCR + ldr r8, [r6] @ keep original value for resume + + @ ensure x1 for run and turbo mode with memory clock + bic r7, r8, #CCCR_M_MASK | CCCR_N_MASK + orr r7, r7, #(1<<5) | (2<<7) + + @ check that the memory frequency is within limits + and r14, r7, #CCCR_L_MASK + teq r14, #1 + bicne r7, r7, #CCCR_L_MASK + orrne r7, r7, #1 @@ 99.53MHz + + @ get ready for the change + + @ note, turbo is not preserved over sleep so there is no + @ point in preserving it here. we save it on the stack with the + @ other CP registers instead. + mov r0, #0 + mcr p14, 0, r0, c6, c0, 0 + orr r0, r0, #2 @ initiate change bit + + @ align execution to a cache line + b 1f + + .ltorg + .align 5 +1: + + @ All needed values are now in registers. + @ These last instructions should be in cache + + @ initiate the frequency change... + str r7, [r6] + mcr p14, 0, r0, c6, c0, 0 + + @ restore the original cpu speed value for resume + str r8, [r6] + + @ put SDRAM into self-refresh + str r5, [r4] + + @ force address lines low by reading at physical address 0 + ldr r3, [r2] + + @ enter sleep mode + mcr p14, 0, r1, c7, c0, 0 + +20: b 20b @ loop waiting for sleep + +/* + * cpu_pxa_resume() + * + * entry point from bootloader into kernel during resume + * + * Note: Yes, part of the following code is located into the .data section. + * This is to allow sleep_save_sp to be accessed with a relative load + * while we can't rely on any MMU translation. We could have put + * sleep_save_sp in the .text section as well, but some setups might + * insist on it to be truly read-only. + */ + + .data + .align 5 +ENTRY(pxa_cpu_resume) + mov r0, #PSR_I_BIT | PSR_F_BIT | MODE_SVC @ set SVC, irqs off + msr cpsr_c, r0 + + ldr r0, sleep_save_sp @ stack phys addr + ldr r2, =resume_after_mmu @ its absolute virtual address + ldmfd r0, {r3 - r9, sp} @ CP regs + virt stack ptr + + mov r1, #0 + mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs + mcr p15, 0, r1, c7, c7, 0 @ invalidate I & D caches, BTB + +#ifdef CONFIG_XSCALE_CACHE_ERRATA + bic r9, r9, #0x0004 @ see cpu_xscale_proc_init +#endif + + mcr p14, 0, r3, c6, c0, 0 @ clock configuration, turbo mode. + mcr p15, 0, r4, c15, c1, 0 @ CP access reg + mcr p15, 0, r5, c13, c0, 0 @ PID + mcr p15, 0, r6, c3, c0, 0 @ domain ID + mcr p15, 0, r7, c2, c0, 0 @ translation table base addr + mcr p15, 0, r8, c1, c1, 0 @ auxiliary control reg + b resume_turn_on_mmu @ cache align execution + + .align 5 +resume_turn_on_mmu: + mcr p15, 0, r9, c1, c0, 0 @ turn on MMU, caches, etc. + + @ Let us ensure we jump to resume_after_mmu only when the mcr above + @ actually took effect. They call it the "cpwait" operation. + mrc p15, 0, r1, c2, c0, 0 @ queue a dependency on CP15 + sub pc, r2, r1, lsr #32 @ jump to virtual addr + nop + nop + nop + +sleep_save_sp: + .word 0 @ preserve stack phys ptr here + + .text +resume_after_mmu: +#ifdef CONFIG_XSCALE_CACHE_ERRATA + bl cpu_xscale_proc_init +#endif + ldmfd sp!, {r2, r3} + mar acc0, r2, r3 + ldmfd sp!, {r4 - r12, pc} @ return to caller + + diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c new file mode 100644 index 00000000000..4d826c02131 --- /dev/null +++ b/arch/arm/mach-pxa/ssp.c @@ -0,0 +1,363 @@ +/* + * linux/arch/arm/mach-pxa/ssp.c + * + * based on linux/arch/arm/mach-sa1100/ssp.c by Russell King + * + * Copyright (C) 2003 Russell King. + * Copyright (C) 2003 Wolfson Microelectronics PLC + * + * 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. + * + * PXA2xx SSP driver. This provides the generic core for simple + * IO-based SSP applications and allows easy port setup for DMA access. + * + * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * + * Revision history: + * 22nd Aug 2003 Initial version. + * 20th Dec 2004 Added ssp_config for changing port config without + * closing the port. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/arch/ssp.h> +#include <asm/arch/pxa-regs.h> + +#define PXA_SSP_PORTS 3 + +static DECLARE_MUTEX(sem); +static int use_count[PXA_SSP_PORTS] = {0, 0, 0}; + +static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ssp_dev *dev = (struct ssp_dev*) dev_id; + unsigned int status = SSSR_P(dev->port); + + SSSR_P(dev->port) = status; /* clear status bits */ + + if (status & SSSR_ROR) + printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port); + + if (status & SSSR_TUR) + printk(KERN_WARNING "SSP(%d): transmitter underrun\n", dev->port); + + if (status & SSSR_BCE) + printk(KERN_WARNING "SSP(%d): bit count error\n", dev->port); + + return IRQ_HANDLED; +} + +/** + * ssp_write_word - write a word to the SSP port + * @data: 32-bit, MSB justified data to write. + * + * Wait for a free entry in the SSP transmit FIFO, and write a data + * word to the SSP port. + * + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred (for future) + * 0 success + */ +int ssp_write_word(struct ssp_dev *dev, u32 data) +{ + while (!(SSSR_P(dev->port) & SSSR_TNF)) + cpu_relax(); + + SSDR_P(dev->port) = data; + + return 0; +} + +/** + * ssp_read_word - read a word from the SSP port + * + * Wait for a data word in the SSP receive FIFO, and return the + * received data. Data is LSB justified. + * + * Note: Currently, if data is not expected to be received, this + * function will wait for ever. + * + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred (for future) + * 32-bit data success + */ +int ssp_read_word(struct ssp_dev *dev) +{ + while (!(SSSR_P(dev->port) & SSSR_RNE)) + cpu_relax(); + + return SSDR_P(dev->port); +} + +/** + * ssp_flush - flush the transmit and receive FIFOs + * + * Wait for the SSP to idle, and ensure that the receive FIFO + * is empty. + * + * The caller is expected to perform the necessary locking. + */ +void ssp_flush(struct ssp_dev *dev) +{ + do { + while (SSSR_P(dev->port) & SSSR_RNE) { + (void) SSDR_P(dev->port); + } + } while (SSSR_P(dev->port) & SSSR_BSY); +} + +/** + * ssp_enable - enable the SSP port + * + * Turn on the SSP port. + */ +void ssp_enable(struct ssp_dev *dev) +{ + SSCR0_P(dev->port) |= SSCR0_SSE; +} + +/** + * ssp_disable - shut down the SSP port + * + * Turn off the SSP port, optionally powering it down. + */ +void ssp_disable(struct ssp_dev *dev) +{ + SSCR0_P(dev->port) &= ~SSCR0_SSE; +} + +/** + * ssp_save_state - save the SSP configuration + * @ssp: pointer to structure to save SSP configuration + * + * Save the configured SSP state for suspend. + */ +void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp) +{ + ssp->cr0 = SSCR0_P(dev->port); + ssp->cr1 = SSCR1_P(dev->port); + ssp->to = SSTO_P(dev->port); + ssp->psp = SSPSP_P(dev->port); + + SSCR0_P(dev->port) &= ~SSCR0_SSE; +} + +/** + * ssp_restore_state - restore a previously saved SSP configuration + * @ssp: pointer to configuration saved by ssp_save_state + * + * Restore the SSP configuration saved previously by ssp_save_state. + */ +void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp) +{ + SSSR_P(dev->port) = SSSR_ROR | SSSR_TUR | SSSR_BCE; + + SSCR0_P(dev->port) = ssp->cr0 & ~SSCR0_SSE; + SSCR1_P(dev->port) = ssp->cr1; + SSTO_P(dev->port) = ssp->to; + SSPSP_P(dev->port) = ssp->psp; + + SSCR0_P(dev->port) = ssp->cr0; +} + +/** + * ssp_config - configure SSP port settings + * @mode: port operating mode + * @flags: port config flags + * @psp_flags: port PSP config flags + * @speed: port speed + * + * Port MUST be disabled by ssp_disable before making any config changes. + */ +int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed) +{ + dev->mode = mode; + dev->flags = flags; + dev->psp_flags = psp_flags; + dev->speed = speed; + + /* set up port type, speed, port settings */ + SSCR0_P(dev->port) = (dev->speed | dev->mode); + SSCR1_P(dev->port) = dev->flags; + SSPSP_P(dev->port) = dev->psp_flags; + + return 0; +} + +/** + * ssp_init - setup the SSP port + * + * initialise and claim resources for the SSP port. + * + * Returns: + * %-ENODEV if the SSP port is unavailable + * %-EBUSY if the resources are already in use + * %0 on success + */ +int ssp_init(struct ssp_dev *dev, u32 port) +{ + int ret, irq; + + if (port > PXA_SSP_PORTS || port == 0) + return -ENODEV; + + down(&sem); + if (use_count[port - 1]) { + up(&sem); + return -EBUSY; + } + use_count[port - 1]++; + + if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { + use_count[port - 1]--; + up(&sem); + return -EBUSY; + } + + switch (port) { + case 1: + irq = IRQ_SSP; + break; +#if defined (CONFIG_PXA27x) + case 2: + irq = IRQ_SSP2; + break; + case 3: + irq = IRQ_SSP3; + break; +#else + case 2: + irq = IRQ_NSSP; + break; + case 3: + irq = IRQ_ASSP; + break; +#endif + default: + return -ENODEV; + } + + dev->port = port; + + ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev); + if (ret) + goto out_region; + + /* turn on SSP port clock */ + switch (dev->port) { +#if defined (CONFIG_PXA27x) + case 1: + pxa_set_cken(CKEN23_SSP1, 1); + break; + case 2: + pxa_set_cken(CKEN3_SSP2, 1); + break; + case 3: + pxa_set_cken(CKEN4_SSP3, 1); + break; +#else + case 1: + pxa_set_cken(CKEN3_SSP, 1); + break; + case 2: + pxa_set_cken(CKEN9_NSSP, 1); + break; + case 3: + pxa_set_cken(CKEN10_ASSP, 1); + break; +#endif + } + + up(&sem); + return 0; + +out_region: + release_mem_region(__PREG(SSCR0_P(port)), 0x2c); + use_count[port - 1]--; + up(&sem); + return ret; +} + +/** + * ssp_exit - undo the effects of ssp_init + * + * release and free resources for the SSP port. + */ +void ssp_exit(struct ssp_dev *dev) +{ + int irq; + + down(&sem); + SSCR0_P(dev->port) &= ~SSCR0_SSE; + + /* find irq, save power and turn off SSP port clock */ + switch (dev->port) { +#if defined (CONFIG_PXA27x) + case 1: + irq = IRQ_SSP; + pxa_set_cken(CKEN23_SSP1, 0); + break; + case 2: + irq = IRQ_SSP2; + pxa_set_cken(CKEN3_SSP2, 0); + break; + case 3: + irq = IRQ_SSP3; + pxa_set_cken(CKEN4_SSP3, 0); + break; +#else + case 1: + irq = IRQ_SSP; + pxa_set_cken(CKEN3_SSP, 0); + break; + case 2: + irq = IRQ_NSSP; + pxa_set_cken(CKEN9_NSSP, 0); + break; + case 3: + irq = IRQ_ASSP; + pxa_set_cken(CKEN10_ASSP, 0); + break; +#endif + default: + printk(KERN_WARNING "SSP: tried to close invalid port\n"); + return; + } + + free_irq(irq, dev); + release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); + use_count[dev->port - 1]--; + up(&sem); +} + +EXPORT_SYMBOL(ssp_write_word); +EXPORT_SYMBOL(ssp_read_word); +EXPORT_SYMBOL(ssp_flush); +EXPORT_SYMBOL(ssp_enable); +EXPORT_SYMBOL(ssp_disable); +EXPORT_SYMBOL(ssp_save_state); +EXPORT_SYMBOL(ssp_restore_state); +EXPORT_SYMBOL(ssp_init); +EXPORT_SYMBOL(ssp_exit); +EXPORT_SYMBOL(ssp_config); + +MODULE_DESCRIPTION("PXA SSP driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c new file mode 100644 index 00000000000..473fb6173f7 --- /dev/null +++ b/arch/arm/mach-pxa/time.c @@ -0,0 +1,164 @@ +/* + * arch/arm/mach-pxa/time.c + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include <asm/system.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/leds.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> +#include <asm/mach/time.h> +#include <asm/arch/pxa-regs.h> + + +static inline unsigned long pxa_get_rtc_time(void) +{ + return RCNR; +} + +static int pxa_set_rtc(void) +{ + unsigned long current_time = xtime.tv_sec; + + if (RTSR & RTSR_ALE) { + /* make sure not to forward the clock over an alarm */ + unsigned long alarm = RTAR; + if (current_time >= alarm && alarm >= RCNR) + return -ERESTARTSYS; + } + RCNR = current_time; + return 0; +} + +/* IRQs are disabled before entering here from do_gettimeofday() */ +static unsigned long pxa_gettimeoffset (void) +{ + long ticks_to_match, elapsed, usec; + + /* Get ticks before next timer match */ + ticks_to_match = OSMR0 - OSCR; + + /* We need elapsed ticks since last match */ + elapsed = LATCH - ticks_to_match; + + /* don't get fooled by the workaround in pxa_timer_interrupt() */ + if (elapsed <= 0) + return 0; + + /* Now convert them to usec */ + usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; + + return usec; +} + +static irqreturn_t +pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int next_match; + + write_seqlock(&xtime_lock); + + /* Loop until we get ahead of the free running timer. + * This ensures an exact clock tick count and time accuracy. + * IRQs are disabled inside the loop to ensure coherence between + * lost_ticks (updated in do_timer()) and the match reg value, so we + * can use do_gettimeofday() from interrupt handlers. + * + * HACK ALERT: it seems that the PXA timer regs aren't updated right + * away in all cases when a write occurs. We therefore compare with + * 8 instead of 0 in the while() condition below to avoid missing a + * match if OSCR has already reached the next OSMR value. + * Experience has shown that up to 6 ticks are needed to work around + * this problem, but let's use 8 to be conservative. Note that this + * affect things only when the timer IRQ has been delayed by nearly + * exactly one tick period which should be a pretty rare event. + */ + do { + timer_tick(regs); + OSSR = OSSR_M0; /* Clear match on timer 0 */ + next_match = (OSMR0 += LATCH); + } while( (signed long)(next_match - OSCR) <= 8 ); + + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction pxa_timer_irq = { + .name = "PXA Timer Tick", + .flags = SA_INTERRUPT, + .handler = pxa_timer_interrupt +}; + +static void __init pxa_timer_init(void) +{ + struct timespec tv; + + set_rtc = pxa_set_rtc; + + tv.tv_nsec = 0; + tv.tv_sec = pxa_get_rtc_time(); + do_settimeofday(&tv); + + OSMR0 = 0; /* set initial match at 0 */ + OSSR = 0xf; /* clear status on all timers */ + setup_irq(IRQ_OST0, &pxa_timer_irq); + OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */ + OSCR = 0; /* initialize free-running timer, force first match */ +} + +#ifdef CONFIG_PM +static unsigned long osmr[4], oier; + +static void pxa_timer_suspend(void) +{ + osmr[0] = OSMR0; + osmr[1] = OSMR1; + osmr[2] = OSMR2; + osmr[3] = OSMR3; + oier = OIER; +} + +static void pxa_timer_resume(void) +{ + OSMR0 = osmr[0]; + OSMR1 = osmr[1]; + OSMR2 = osmr[2]; + OSMR3 = osmr[3]; + OIER = oier; + + /* + * OSMR0 is the system timer: make sure OSCR is sufficiently behind + */ + OSCR = OSMR0 - LATCH; +} +#else +#define pxa_timer_suspend NULL +#define pxa_timer_resume NULL +#endif + +struct sys_timer pxa_timer = { + .init = pxa_timer_init, + .suspend = pxa_timer_suspend, + .resume = pxa_timer_resume, + .offset = pxa_gettimeoffset, +}; |