diff options
Diffstat (limited to 'arch/arm/mach-sa1100')
33 files changed, 6559 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/Kconfig b/arch/arm/mach-sa1100/Kconfig new file mode 100644 index 0000000..50cde57 --- /dev/null +++ b/arch/arm/mach-sa1100/Kconfig @@ -0,0 +1,162 @@ +if ARCH_SA1100 + +menu "SA11x0 Implementations" + +config SA1100_ASSABET + bool "Assabet" + help + Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 + Microprocessor Development Board (also known as the Assabet). + +config ASSABET_NEPONSET + bool "Include support for Neponset" + depends on SA1100_ASSABET + select SA1111 + help + Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 + Microprocessor Development Board (Assabet) with the SA-1111 + Development Board (Nepon). + +config SA1100_CERF + bool "CerfBoard" + help + The Intrinsyc CerfBoard is based on the StrongARM 1110 (Discontinued). + More information is available at: + <http://www.intrinsyc.com/products/cerfboard/>. + + Say Y if configuring for an Intrinsyc CerfBoard. + Say N otherwise. + +choice + prompt "Cerf Flash available" + depends on SA1100_CERF + default SA1100_CERF_FLASH_8MB + +config SA1100_CERF_FLASH_8MB + bool "8MB" + +config SA1100_CERF_FLASH_16MB + bool "16MB" + +config SA1100_CERF_FLASH_32MB + bool "32MB" + +endchoice + +config SA1100_COLLIE + bool "Sharp Zaurus SL5500" + select SHARP_LOCOMO + select SHARP_SCOOP + select SHARP_PARAM + help + Say Y here to support the Sharp Zaurus SL5500 PDAs. + +config SA1100_H3100 + bool "Compaq iPAQ H3100" + help + Say Y here if you intend to run this kernel on the Compaq iPAQ + H3100 handheld computer. Information about this machine and the + Linux port to this machine can be found at: + + <http://www.handhelds.org/Compaq/index.html#iPAQ_H3100> + <http://www.compaq.com/products/handhelds/pocketpc/> + +config SA1100_H3600 + bool "Compaq iPAQ H3600/H3700" + help + Say Y here if you intend to run this kernel on the Compaq iPAQ + H3600 handheld computer. Information about this machine and the + Linux port to this machine can be found at: + + <http://www.handhelds.org/Compaq/index.html#iPAQ_H3600> + <http://www.compaq.com/products/handhelds/pocketpc/> + +config SA1100_H3800 + bool "Compaq iPAQ H3800" + help + Say Y here if you intend to run this kernel on the Compaq iPAQ H3800 + series handheld computer. Information about this machine and the + Linux port to this machine can be found at: + + <http://www.handhelds.org/Compaq/index.html#iPAQ_H3800> + <http://www.compaq.com/products/handhelds/pocketpc/> + +config SA1100_H3XXX + bool + depends on SA1100_H3100 || SA1100_H3600 || SA1100_H3800 + default y + +config SA1100_BADGE4 + bool "HP Labs BadgePAD 4" + select SA1111 + help + Say Y here if you want to build a kernel for the HP Laboratories + BadgePAD 4. + +config SA1100_JORNADA720 + bool "HP Jornada 720" + select SA1111 + help + Say Y here if you want to build a kernel for the HP Jornada 720 + handheld computer. See <http://www.hp.com/jornada/products/720> + for details. + +config SA1100_HACKKIT + bool "HackKit Core CPU Board" + help + Say Y here to support the HackKit Core CPU Board + <http://hackkit.eletztrick.de>; + +config SA1100_LART + bool "LART" + help + Say Y here if you are using the Linux Advanced Radio Terminal + (also known as the LART). See <http://www.lart.tudelft.nl/> for + information on the LART. + +config SA1100_PLEB + bool "PLEB" + help + Say Y here if you are using version 1 of the Portable Linux + Embedded Board (also known as PLEB). + See <http://www.disy.cse.unsw.edu.au/Hardware/PLEB/> + for more information. + +config SA1100_SHANNON + bool "Shannon" + help + The Shannon (also known as a Tuxscreen, and also as a IS2630) was a + limited edition webphone produced by Philips. The Shannon is a SA1100 + platform with a 640x480 LCD, touchscreen, CIR keyboard, PCMCIA slots, + and a telco interface. + +config SA1100_SIMPAD + bool "Simpad" + help + The SIEMENS webpad SIMpad is based on the StrongARM 1110. There + are two different versions CL4 and SL4. CL4 has 32MB RAM and 16MB + FLASH. The SL4 version got 64 MB RAM and 32 MB FLASH and a + PCMCIA-Slot. The version for the Germany Telecom (DTAG) is the same + like CL4 in additional it has a PCMCIA-Slot. For more information + visit <http://www.my-siemens.com/> or <http://www.siemens.ch/>. + +config SA1100_SSP + tristate "Generic PIO SSP" + help + Say Y here to enable support for the generic PIO SSP driver. + This isn't for audio support, but for attached sensors and + other devices, eg for BadgePAD 4 sensor support, or Jornada + 720 touchscreen support. + +config H3600_SLEEVE + tristate "Compaq iPAQ Handheld sleeve support" + depends on SA1100_H3600 + help + Choose this option to enable support for extension packs (sleeves) + for the Compaq iPAQ H3XXX series of handheld computers. This option + is required for the CF, PCMCIA, Bluetooth and GSM/GPRS extension + packs. + +endmenu + +endif diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile new file mode 100644 index 0000000..e4a4a3e --- /dev/null +++ b/arch/arm/mach-sa1100/Makefile @@ -0,0 +1,53 @@ +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := generic.o irq.o dma.o time.o +obj-m := +obj-n := +obj- := +led-y := leds.o + +obj-$(CONFIG_CPU_FREQ_SA1100) += cpu-sa1100.o +obj-$(CONFIG_CPU_FREQ_SA1110) += cpu-sa1110.o + +# Specific board support +obj-$(CONFIG_SA1100_ASSABET) += assabet.o +led-$(CONFIG_SA1100_ASSABET) += leds-assabet.o +obj-$(CONFIG_ASSABET_NEPONSET) += neponset.o + +obj-$(CONFIG_SA1100_BADGE4) += badge4.o +led-$(CONFIG_SA1100_BADGE4) += leds-badge4.o + +obj-$(CONFIG_SA1100_CERF) += cerf.o +led-$(CONFIG_SA1100_CERF) += leds-cerf.o + +obj-$(CONFIG_SA1100_COLLIE) += collie.o + +obj-$(CONFIG_SA1100_H3600) += h3600.o + +obj-$(CONFIG_SA1100_HACKKIT) += hackkit.o +led-$(CONFIG_SA1100_HACKKIT) += leds-hackkit.o + +obj-$(CONFIG_SA1100_JORNADA720) += jornada720.o + +obj-$(CONFIG_SA1100_LART) += lart.o +led-$(CONFIG_SA1100_LART) += leds-lart.o + +obj-$(CONFIG_SA1100_PLEB) += pleb.o + +obj-$(CONFIG_SA1100_SHANNON) += shannon.o + +obj-$(CONFIG_SA1100_SIMPAD) += simpad.o +led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o + +# LEDs support +obj-$(CONFIG_LEDS) += $(led-y) + +# SA1110 USB client support +#obj-$(CONFIG_SA1100_USB) += usb/ + +# Miscelaneous functions +obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_SA1100_SSP) += ssp.o diff --git a/arch/arm/mach-sa1100/Makefile.boot b/arch/arm/mach-sa1100/Makefile.boot new file mode 100644 index 0000000..a56ad04 --- /dev/null +++ b/arch/arm/mach-sa1100/Makefile.boot @@ -0,0 +1,7 @@ + zreladdr-y := 0xc0008000 +ifeq ($(CONFIG_ARCH_SA1100),y) + zreladdr-$(CONFIG_SA1111) := 0xc0208000 +endif +params_phys-y := 0xc0000100 +initrd_phys-y := 0xc0800000 + diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c new file mode 100644 index 0000000..bedf88f --- /dev/null +++ b/arch/arm/mach-sa1100/assabet.c @@ -0,0 +1,441 @@ +/* + * linux/arch/arm/mach-sa1100/assabet.c + * + * Author: Nicolas Pitre + * + * This file contains all Assabet-specific tweaks. + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/serial_core.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> +#include <linux/mm.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/irda.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> +#include <asm/arch/assabet.h> + +#include "generic.h" + +#define ASSABET_BCR_DB1110 \ + (ASSABET_BCR_SPK_OFF | ASSABET_BCR_QMUTE | \ + ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED | \ + ASSABET_BCR_RS232EN | ASSABET_BCR_LCD_12RGB | \ + ASSABET_BCR_IRDA_MD0) + +#define ASSABET_BCR_DB1111 \ + (ASSABET_BCR_SPK_OFF | ASSABET_BCR_QMUTE | \ + ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED | \ + ASSABET_BCR_RS232EN | ASSABET_BCR_LCD_12RGB | \ + ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_STEREO_LB | \ + ASSABET_BCR_IRDA_MD0 | ASSABET_BCR_CF_RST) + +unsigned long SCR_value = ASSABET_SCR_INIT; +EXPORT_SYMBOL(SCR_value); + +static unsigned long BCR_value = ASSABET_BCR_DB1110; + +void ASSABET_BCR_frob(unsigned int mask, unsigned int val) +{ + unsigned long flags; + + local_irq_save(flags); + BCR_value = (BCR_value & ~mask) | val; + ASSABET_BCR = BCR_value; + local_irq_restore(flags); +} + +EXPORT_SYMBOL(ASSABET_BCR_frob); + +static void assabet_backlight_power(int on) +{ +#ifndef ASSABET_PAL_VIDEO + if (on) + ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON); + else +#endif + ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON); +} + +/* + * Turn on/off the backlight. When turning the backlight on, + * we wait 500us after turning it on so we don't cause the + * supplies to droop when we enable the LCD controller (and + * cause a hard reset.) + */ +static void assabet_lcd_power(int on) +{ +#ifndef ASSABET_PAL_VIDEO + if (on) { + ASSABET_BCR_set(ASSABET_BCR_LCD_ON); + udelay(500); + } else +#endif + ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); +} + + +/* + * Assabet flash support code. + */ + +#ifdef ASSABET_REV_4 +/* + * Phase 4 Assabet has two 28F160B3 flash parts in bank 0: + */ +static struct mtd_partition assabet_partitions[] = { + { + .name = "bootloader", + .size = 0x00020000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "bootloader params", + .size = 0x00020000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#else +/* + * Phase 5 Assabet has two 28F128J3A flash parts in bank 0: + */ +static struct mtd_partition assabet_partitions[] = { + { + .name = "bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + +static struct flash_platform_data assabet_flash_data = { + .map_name = "cfi_probe", + .parts = assabet_partitions, + .nr_parts = ARRAY_SIZE(assabet_partitions), +}; + +static struct resource assabet_flash_resources[] = { + { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, + }, { + .start = SA1100_CS1_PHYS, + .end = SA1100_CS1_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, + } +}; + + +/* + * Assabet IrDA support code. + */ + +static int assabet_irda_set_power(struct device *dev, unsigned int state) +{ + static unsigned int bcr_state[4] = { + ASSABET_BCR_IRDA_MD0, + ASSABET_BCR_IRDA_MD1|ASSABET_BCR_IRDA_MD0, + ASSABET_BCR_IRDA_MD1, + 0 + }; + + if (state < 4) { + state = bcr_state[state]; + ASSABET_BCR_clear(state ^ (ASSABET_BCR_IRDA_MD1| + ASSABET_BCR_IRDA_MD0)); + ASSABET_BCR_set(state); + } + return 0; +} + +static void assabet_irda_set_speed(struct device *dev, unsigned int speed) +{ + if (speed < 4000000) + ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL); + else + ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL); +} + +static struct irda_platform_data assabet_irda_data = { + .set_power = assabet_irda_set_power, + .set_speed = assabet_irda_set_speed, +}; + +static void __init assabet_init(void) +{ + /* + * Ensure that the power supply is in "high power" mode. + */ + GPDR |= GPIO_GPIO16; + GPSR = GPIO_GPIO16; + + /* + * Ensure that these pins are set as outputs and are driving + * logic 0. This ensures that we won't inadvertently toggle + * the WS latch in the CPLD, and we don't float causing + * excessive power drain. --rmk + */ + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPCR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + + /* + * Set up registers for sleep mode. + */ + PWER = PWER_GPIO0; + PGSR = 0; + PCFR = 0; + PSDR = 0; + PPDR |= PPC_TXD3 | PPC_TXD1; + PPSR |= PPC_TXD3 | PPC_TXD1; + + sa1100fb_lcd_power = assabet_lcd_power; + sa1100fb_backlight_power = assabet_backlight_power; + + if (machine_has_neponset()) { + /* + * Angel sets this, but other bootloaders may not. + * + * This must precede any driver calls to BCR_set() + * or BCR_clear(). + */ + ASSABET_BCR = BCR_value = ASSABET_BCR_DB1111; + +#ifndef CONFIG_ASSABET_NEPONSET + printk( "Warning: Neponset detected but full support " + "hasn't been configured in the kernel\n" ); +#endif + } + + sa11x0_set_flash_data(&assabet_flash_data, assabet_flash_resources, + ARRAY_SIZE(assabet_flash_resources)); + sa11x0_set_irda_data(&assabet_irda_data); +} + +/* + * On Assabet, we must probe for the Neponset board _before_ + * paging_init() has occurred to actually determine the amount + * of RAM available. To do so, we map the appropriate IO section + * in the page table here in order to access GPIO registers. + */ +static void __init map_sa1100_gpio_regs( void ) +{ + unsigned long phys = __PREG(GPLR) & PMD_MASK; + unsigned long virt = io_p2v(phys); + int prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO); + pmd_t *pmd; + + pmd = pmd_offset(pgd_offset_k(virt), virt); + *pmd = __pmd(phys | prot); + flush_pmd_entry(pmd); +} + +/* + * Read System Configuration "Register" + * (taken from "Intel StrongARM SA-1110 Microprocessor Development Board + * User's Guide", section 4.4.1) + * + * This same scan is performed in arch/arm/boot/compressed/head-sa1100.S + * to set up the serial port for decompression status messages. We + * repeat it here because the kernel may not be loaded as a zImage, and + * also because it's a hassle to communicate the SCR value to the kernel + * from the decompressor. + * + * Note that IRQs are guaranteed to be disabled. + */ +static void __init get_assabet_scr(void) +{ + unsigned long scr, i; + + GPDR |= 0x3fc; /* Configure GPIO 9:2 as outputs */ + GPSR = 0x3fc; /* Write 0xFF to GPIO 9:2 */ + GPDR &= ~(0x3fc); /* Configure GPIO 9:2 as inputs */ + for(i = 100; i--; scr = GPLR); /* Read GPIO 9:2 */ + GPDR |= 0x3fc; /* restore correct pin direction */ + scr &= 0x3fc; /* save as system configuration byte. */ + SCR_value = scr; +} + +static void __init +fixup_assabet(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + /* This must be done before any call to machine_has_neponset() */ + map_sa1100_gpio_regs(); + get_assabet_scr(); + + if (machine_has_neponset()) + printk("Neponset expansion board detected\n"); +} + + +static void assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (port->mapbase == _Ser1UTCR0) { + if (state) + ASSABET_BCR_clear(ASSABET_BCR_RS232EN | + ASSABET_BCR_COM_RTS | + ASSABET_BCR_COM_DTR); + else + ASSABET_BCR_set(ASSABET_BCR_RS232EN | + ASSABET_BCR_COM_RTS | + ASSABET_BCR_COM_DTR); + } +} + +/* + * Assabet uses COM_RTS and COM_DTR for both UART1 (com port) + * and UART3 (radio module). We only handle them for UART1 here. + */ +static void assabet_set_mctrl(struct uart_port *port, u_int mctrl) +{ + if (port->mapbase == _Ser1UTCR0) { + u_int set = 0, clear = 0; + + if (mctrl & TIOCM_RTS) + clear |= ASSABET_BCR_COM_RTS; + else + set |= ASSABET_BCR_COM_RTS; + + if (mctrl & TIOCM_DTR) + clear |= ASSABET_BCR_COM_DTR; + else + set |= ASSABET_BCR_COM_DTR; + + ASSABET_BCR_clear(clear); + ASSABET_BCR_set(set); + } +} + +static u_int assabet_get_mctrl(struct uart_port *port) +{ + u_int ret = 0; + u_int bsr = ASSABET_BSR; + + /* need 2 reads to read current value */ + bsr = ASSABET_BSR; + + if (port->mapbase == _Ser1UTCR0) { + if (bsr & ASSABET_BSR_COM_DCD) + ret |= TIOCM_CD; + if (bsr & ASSABET_BSR_COM_CTS) + ret |= TIOCM_CTS; + if (bsr & ASSABET_BSR_COM_DSR) + ret |= TIOCM_DSR; + } else if (port->mapbase == _Ser3UTCR0) { + if (bsr & ASSABET_BSR_RAD_DCD) + ret |= TIOCM_CD; + if (bsr & ASSABET_BSR_RAD_CTS) + ret |= TIOCM_CTS; + if (bsr & ASSABET_BSR_RAD_DSR) + ret |= TIOCM_DSR; + if (bsr & ASSABET_BSR_RAD_RI) + ret |= TIOCM_RI; + } else { + ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; + } + + return ret; +} + +static struct sa1100_port_fns assabet_port_fns __initdata = { + .set_mctrl = assabet_set_mctrl, + .get_mctrl = assabet_get_mctrl, + .pm = assabet_uart_pm, +}; + +static struct map_desc assabet_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf1000000, 0x12000000, 0x00100000, MT_DEVICE }, /* Board Control Register */ + { 0xf2800000, 0x4b800000, 0x00800000, MT_DEVICE } /* MQ200 */ +}; + +static void __init assabet_map_io(void) +{ + sa1100_map_io(); + iotable_init(assabet_io_desc, ARRAY_SIZE(assabet_io_desc)); + + /* + * Set SUS bit in SDCR0 so serial port 1 functions. + * Its called GPCLKR0 in my SA1110 manual. + */ + Ser1SDCR0 |= SDCR0_SUS; + + if (machine_has_neponset()) { +#ifdef CONFIG_ASSABET_NEPONSET + extern void neponset_map_io(void); + + /* + * We map Neponset registers even if it isn't present since + * many drivers will try to probe their stuff (and fail). + * This is still more friendly than a kernel paging request + * crash. + */ + neponset_map_io(); +#endif + } else { + sa1100_register_uart_fns(&assabet_port_fns); + } + + /* + * When Neponset is attached, the first UART should be + * UART3. That's what Angel is doing and many documents + * are stating this. + * + * We do the Neponset mapping even if Neponset support + * isn't compiled in so the user will still get something on + * the expected physical serial port. + * + * We no longer do this; not all boot loaders support it, + * and UART3 appears to be somewhat unreliable with blob. + */ + sa1100_register_uart(0, 1); + sa1100_register_uart(2, 3); +} + + +MACHINE_START(ASSABET, "Intel-Assabet") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + FIXUP(fixup_assabet) + MAPIO(assabet_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = assabet_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c new file mode 100644 index 0000000..6a60b49 --- /dev/null +++ b/arch/arm/mach-sa1100/badge4.c @@ -0,0 +1,293 @@ +/* + * linux/arch/arm/mach-sa1100/badge4.c + * + * BadgePAD 4 specific initialization + * + * Tim Connors <connors@hpl.hp.com> + * Christopher Hoover <ch@hpl.hp.com> + * + * Copyright (C) 2002 Hewlett-Packard Company + * + * 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/device.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/errno.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/setup.h> +#include <asm/arch/irqs.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/hardware/sa1111.h> +#include <asm/mach/serial_sa1100.h> + +#include <asm/arch/badge4.h> + +#include "generic.h" + +static struct resource sa1111_resources[] = { + [0] = { + .start = BADGE4_SA1111_BASE, + .end = BADGE4_SA1111_BASE + 0x00001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = BADGE4_IRQ_GPIO_SA1111, + .end = BADGE4_IRQ_GPIO_SA1111, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .dma_mask = &sa1111_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + +static int __init badge4_sa1111_init(void) +{ + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state + */ + sa1110_mb_disable(); + + /* + * Probe for SA1111. + */ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + + +/* + * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) + * + * <or> + * + * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + */ +static struct mtd_partition badge4_partitions[] = { + { + .name = "BLOB boot loader", + .offset = 0, + .size = 0x0000A000 + }, { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 0x00006000 + }, { + .name = "root", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static struct flash_platform_data badge4_flash_data = { + .map_name = "cfi_probe", + .parts = badge4_partitions, + .nr_parts = ARRAY_SIZE(badge4_partitions), +}; + +static struct resource badge4_flash_resource = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_64M - 1, + .flags = IORESOURCE_MEM, +}; + +static int five_v_on __initdata = 0; + +static int __init five_v_on_setup(char *ignore) +{ + five_v_on = 1; + return 1; +} +__setup("five_v_on", five_v_on_setup); + + +static int __init badge4_init(void) +{ + int ret; + + if (!machine_is_badge4()) + return -ENODEV; + + /* LCD */ + GPCR = (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 | + BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 | + BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 | + BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 | + BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID | + BADGE4_GPIO_GPC_VID); + GPDR &= ~BADGE4_GPIO_INT_VID; + GPDR |= (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 | + BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 | + BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 | + BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 | + BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID | + BADGE4_GPIO_GPC_VID); + + /* SDRAM SPD i2c */ + GPCR = (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL); + GPDR |= (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL); + + /* uart */ + GPCR = (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2); + GPDR |= (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2); + + /* CPLD muxsel0 input for mux/adc chip select */ + GPCR = BADGE4_GPIO_MUXSEL0; + GPDR |= BADGE4_GPIO_MUXSEL0; + + /* test points: J5, J6 as inputs, J7 outputs */ + GPDR &= ~(BADGE4_GPIO_TESTPT_J5 | BADGE4_GPIO_TESTPT_J6); + GPCR = BADGE4_GPIO_TESTPT_J7; + GPDR |= BADGE4_GPIO_TESTPT_J7; + + /* 5V supply rail. */ + GPCR = BADGE4_GPIO_PCMEN5V; /* initially off */ + GPDR |= BADGE4_GPIO_PCMEN5V; + + /* CPLD sdram type inputs; set up by blob */ + //GPDR |= (BADGE4_GPIO_SDTYP1 | BADGE4_GPIO_SDTYP0); + printk(KERN_DEBUG __FILE__ ": SDRAM CPLD typ1=%d typ0=%d\n", + !!(GPLR & BADGE4_GPIO_SDTYP1), + !!(GPLR & BADGE4_GPIO_SDTYP0)); + + /* SA1111 reset pin; set up by blob */ + //GPSR = BADGE4_GPIO_SA1111_NRST; + //GPDR |= BADGE4_GPIO_SA1111_NRST; + + + /* power management cruft */ + PGSR = 0; + PWER = 0; + PCFR = 0; + PSDR = 0; + + PWER |= PWER_GPIO26; /* wake up on an edge from TESTPT_J5 */ + PWER |= PWER_RTC; /* wake up if rtc fires */ + + /* drive sa1111_nrst during sleep */ + PGSR |= BADGE4_GPIO_SA1111_NRST; + /* drive CPLD as is during sleep */ + PGSR |= (GPLR & (BADGE4_GPIO_SDTYP0|BADGE4_GPIO_SDTYP1)); + + + /* Now bring up the SA-1111. */ + ret = badge4_sa1111_init(); + if (ret < 0) + printk(KERN_ERR + "%s: SA-1111 initialization failed (%d)\n", + __FUNCTION__, ret); + + + /* maybe turn on 5v0 from the start */ + badge4_set_5V(BADGE4_5V_INITIALLY, five_v_on); + + sa11x0_set_flash_data(&badge4_flash_data, &badge4_flash_resource, 1); + + return 0; +} + +arch_initcall(badge4_init); + + +static unsigned badge4_5V_bitmap = 0; + +void badge4_set_5V(unsigned subsystem, int on) +{ + unsigned long flags; + unsigned old_5V_bitmap; + + local_irq_save(flags); + + old_5V_bitmap = badge4_5V_bitmap; + + if (on) { + badge4_5V_bitmap |= subsystem; + } else { + badge4_5V_bitmap &= ~subsystem; + } + + /* detect on->off and off->on transitions */ + if ((!old_5V_bitmap) && (badge4_5V_bitmap)) { + /* was off, now on */ + printk(KERN_INFO "%s: enabling 5V supply rail\n", __FUNCTION__); + GPSR = BADGE4_GPIO_PCMEN5V; + } else if ((old_5V_bitmap) && (!badge4_5V_bitmap)) { + /* was on, now off */ + printk(KERN_INFO "%s: disabling 5V supply rail\n", __FUNCTION__); + GPCR = BADGE4_GPIO_PCMEN5V; + } + + local_irq_restore(flags); +} +EXPORT_SYMBOL(badge4_set_5V); + + +static struct map_desc badge4_io_desc[] __initdata = { + /* virtual physical length type */ + {0xf1000000, 0x08000000, 0x00100000, MT_DEVICE },/* SRAM bank 1 */ + {0xf2000000, 0x10000000, 0x00100000, MT_DEVICE },/* SRAM bank 2 */ + {0xf4000000, 0x48000000, 0x00100000, MT_DEVICE } /* SA-1111 */ +}; + +static void +badge4_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (!state) { + Ser1SDCR0 |= SDCR0_UART; + } +} + +static struct sa1100_port_fns badge4_port_fns __initdata = { + //.get_mctrl = badge4_get_mctrl, + //.set_mctrl = badge4_set_mctrl, + .pm = badge4_uart_pm, +}; + +static void __init badge4_map_io(void) +{ + sa1100_map_io(); + iotable_init(badge4_io_desc, ARRAY_SIZE(badge4_io_desc)); + + sa1100_register_uart_fns(&badge4_port_fns); + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); +} + +MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(badge4_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, +MACHINE_END diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c new file mode 100644 index 0000000..f8edde5 --- /dev/null +++ b/arch/arm/mach-sa1100/cerf.c @@ -0,0 +1,132 @@ +/* + * linux/arch/arm/mach-sa1100/cerf.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. + * + * Apr-2003 : Removed some old PDA crud [FB] + * Oct-2003 : Added uart2 resource [FB] + * Jan-2004 : Removed io map for flash [FB] + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/setup.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> + +#include <asm/arch/cerf.h> +#include "generic.h" + +static struct resource cerfuart2_resources[] = { + [0] = { + .start = 0x80030000, + .end = 0x8003ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device cerfuart2_device = { + .name = "sa11x0-uart", + .id = 2, + .num_resources = ARRAY_SIZE(cerfuart2_resources), + .resource = cerfuart2_resources, +}; + +static struct platform_device *cerf_devices[] __initdata = { + &cerfuart2_device, +}; + +#ifdef CONFIG_SA1100_CERF_FLASH_32MB +# define CERF_FLASH_SIZE 0x02000000 +#elif defined CONFIG_SA1100_CERF_FLASH_16MB +# define CERF_FLASH_SIZE 0x01000000 +#elif defined CONFIG_SA1100_CERF_FLASH_8MB +# define CERF_FLASH_SIZE 0x00800000 +#else +# error "Undefined flash size for CERF" +#endif + +static struct mtd_partition cerf_partitions[] = { + { + .name = "Bootloader", + .size = 0x00020000, + .offset = 0x00000000, + }, { + .name = "Params", + .size = 0x00040000, + .offset = 0x00020000, + }, { + .name = "Kernel", + .size = 0x00100000, + .offset = 0x00060000, + }, { + .name = "Filesystem", + .size = CERF_FLASH_SIZE-0x00160000, + .offset = 0x00160000, + } +}; + +static struct flash_platform_data cerf_flash_data = { + .map_name = "cfi_probe", + .parts = cerf_partitions, + .nr_parts = ARRAY_SIZE(cerf_partitions), +}; + +static struct resource cerf_flash_resource = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static void __init cerf_init_irq(void) +{ + sa1100_init_irq(); + set_irq_type(CERF_ETH_IRQ, IRQT_RISING); +} + +static struct map_desc cerf_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf0000000, 0x08000000, 0x00100000, MT_DEVICE } /* Crystal Ethernet Chip */ +}; + +static void __init cerf_map_io(void) +{ + sa1100_map_io(); + iotable_init(cerf_io_desc, ARRAY_SIZE(cerf_io_desc)); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 2); /* disable this and the uart2 device for sa1100_fir */ + sa1100_register_uart(2, 1); + + /* set some GPDR bits here while it's safe */ + GPDR |= CERF_GPIO_CF_RESET; +} + +static void __init cerf_init(void) +{ + platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices)); + sa11x0_set_flash_data(&cerf_flash_data, &cerf_flash_resource, 1); +} + +MACHINE_START(CERF, "Intrinsyc CerfBoard/CerfCube") + MAINTAINER("support@intrinsyc.com") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + MAPIO(cerf_map_io) + INITIRQ(cerf_init_irq) + .timer = &sa1100_timer, + .init_machine = cerf_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c new file mode 100644 index 0000000..9928789 --- /dev/null +++ b/arch/arm/mach-sa1100/collie.c @@ -0,0 +1,192 @@ +/* + * linux/arch/arm/mach-sa1100/collie.c + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * This file contains all Collie-specific tweaks. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ChangeLog: + * 03-06-2004 John Lenz <jelenz@wisc.edu> + * 06-04-2002 Chris Larson <kergoth@digitalnemesis.net> + * 04-16-2001 Lineo Japan,Inc. ... + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/timer.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/arch/collie.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> + +#include <asm/hardware/scoop.h> +#include <asm/mach/sharpsl_param.h> +#include <asm/hardware/locomo.h> + +#include "generic.h" + +static struct resource collie_scoop_resources[] = { + [0] = { + .start = 0x40800000, + .end = 0x40800fff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct scoop_config collie_scoop_setup = { + .io_dir = COLLIE_SCOOP_IO_DIR, + .io_out = COLLIE_SCOOP_IO_OUT, +}; + +struct platform_device colliescoop_device = { + .name = "sharp-scoop", + .id = -1, + .dev = { + .platform_data = &collie_scoop_setup, + }, + .num_resources = ARRAY_SIZE(collie_scoop_resources), + .resource = collie_scoop_resources, +}; + + +static struct resource locomo_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO25, + .end = IRQ_GPIO25, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device locomo_device = { + .name = "locomo", + .id = 0, + .num_resources = ARRAY_SIZE(locomo_resources), + .resource = locomo_resources, +}; + +static struct platform_device *devices[] __initdata = { + &locomo_device, + &colliescoop_device, +}; + +static struct mtd_partition collie_partitions[] = { + { + .name = "bootloader", + .offset = 0, + .size = 0x000C0000, + .mask_flags = MTD_WRITEABLE + }, { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0x00100000, + }, { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = 0x00e20000, + } +}; + +static void collie_set_vpp(int vpp) +{ + write_scoop_reg(SCOOP_GPCR, read_scoop_reg(SCOOP_GPCR) | COLLIE_SCP_VPEN); + if (vpp) { + write_scoop_reg(SCOOP_GPWR, read_scoop_reg(SCOOP_GPWR) | COLLIE_SCP_VPEN); + } else { + write_scoop_reg(SCOOP_GPWR, read_scoop_reg(SCOOP_GPWR) & ~COLLIE_SCP_VPEN); + } +} + +static struct flash_platform_data collie_flash_data = { + .map_name = "cfi_probe", + .set_vpp = collie_set_vpp, + .parts = collie_partitions, + .nr_parts = ARRAY_SIZE(collie_partitions), +}; + +static struct resource collie_flash_resources[] = { + { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, + } +}; + +static void __init collie_init(void) +{ + int ret = 0; + + /* cpu initialize */ + GAFR = ( GPIO_SSP_TXD | \ + GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SSP_CLK | GPIO_TIC_ACK | \ + GPIO_32_768kHz ); + + GPDR = ( GPIO_LDD8 | GPIO_LDD9 | GPIO_LDD10 | GPIO_LDD11 | GPIO_LDD12 | \ + GPIO_LDD13 | GPIO_LDD14 | GPIO_LDD15 | GPIO_SSP_TXD | \ + GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SDLC_SCLK | \ + GPIO_SDLC_AAF | GPIO_UART_SCLK1 | GPIO_32_768kHz ); + GPLR = GPIO_GPIO18; + + // PPC pin setting + PPDR = ( PPC_LDD0 | PPC_LDD1 | PPC_LDD2 | PPC_LDD3 | PPC_LDD4 | PPC_LDD5 | \ + PPC_LDD6 | PPC_LDD7 | PPC_L_PCLK | PPC_L_LCLK | PPC_L_FCLK | PPC_L_BIAS | \ + PPC_TXD1 | PPC_TXD2 | PPC_RXD2 | PPC_TXD3 | PPC_TXD4 | PPC_SCLK | PPC_SFRM ); + + PSDR = ( PPC_RXD1 | PPC_RXD2 | PPC_RXD3 | PPC_RXD4 ); + + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; + TUCR = TUCR_32_768kHz; + + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if (ret) { + printk(KERN_WARNING "collie: Unable to register LoCoMo device\n"); + } + + sa11x0_set_flash_data(&collie_flash_data, collie_flash_resources, + ARRAY_SIZE(collie_flash_resources)); + + sharpsl_save_param(); +} + +static struct map_desc collie_io_desc[] __initdata = { + /* virtual physical length type */ + {0xe8000000, 0x00000000, 0x02000000, MT_DEVICE}, /* 32M main flash (cs0) */ + {0xea000000, 0x08000000, 0x02000000, MT_DEVICE}, /* 32M boot flash (cs1) */ +}; + +static void __init collie_map_io(void) +{ + sa1100_map_io(); + iotable_init(collie_io_desc, ARRAY_SIZE(collie_io_desc)); +} + +MACHINE_START(COLLIE, "Sharp-Collie") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + MAPIO(collie_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = collie_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c new file mode 100644 index 0000000..6435b2e --- /dev/null +++ b/arch/arm/mach-sa1100/cpu-sa1100.c @@ -0,0 +1,249 @@ +/* + * cpu-sa1100.c: clock scaling for the SA1100 + * + * Copyright (C) 2000 2001, The Delft University of Technology + * + * Authors: + * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version + * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl): + * - major rewrite for linux-2.3.99 + * - rewritten for the more generic power management scheme in + * linux-2.4.5-rmk1 + * + * This software has been developed while working on the LART + * computing board (http://www.lart.tudelft.nl/), which is + * sponsored by the Mobile Multi-media Communications + * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications + * (http://www.ubicom.tudelft.nl/) projects. + * + * The authors can be reached at: + * + * Erik Mouw + * Information and Communication Theory Group + * Faculty of Information Technology and Systems + * Delft University of Technology + * P.O. Box 5031 + * 2600 GA Delft + * The Netherlands + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Theory of operations + * ==================== + * + * Clock scaling can be used to lower the power consumption of the CPU + * core. This will give you a somewhat longer running time. + * + * The SA-1100 has a single register to change the core clock speed: + * + * PPCR 0x90020014 PLL config + * + * However, the DRAM timings are closely related to the core clock + * speed, so we need to change these, too. The used registers are: + * + * MDCNFG 0xA0000000 DRAM config + * MDCAS0 0xA0000004 Access waveform + * MDCAS1 0xA0000008 Access waveform + * MDCAS2 0xA000000C Access waveform + * + * Care must be taken to change the DRAM parameters the correct way, + * because otherwise the DRAM becomes unusable and the kernel will + * crash. + * + * The simple solution to avoid a kernel crash is to put the actual + * clock change in ROM and jump to that code from the kernel. The main + * disadvantage is that the ROM has to be modified, which is not + * possible on all SA-1100 platforms. Another disadvantage is that + * jumping to ROM makes clock switching unecessary complicated. + * + * The idea behind this driver is that the memory configuration can be + * changed while running from DRAM (even with interrupts turned on!) + * as long as all re-configuration steps yield a valid DRAM + * configuration. The advantages are clear: it will run on all SA-1100 + * platforms, and the code is very simple. + * + * If you really want to understand what is going on in + * sa1100_update_dram_timings(), you'll have to read sections 8.2, + * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor + * Developers Manual" (available for free from Intel). + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/cpufreq.h> + +#include <asm/hardware.h> + +#include "generic.h" + +typedef struct { + int speed; + u32 mdcnfg; + u32 mdcas0; + u32 mdcas1; + u32 mdcas2; +} sa1100_dram_regs_t; + + +static struct cpufreq_driver sa1100_driver; + +static sa1100_dram_regs_t sa1100_dram_settings[] = +{ + /* speed, mdcnfg, mdcas0, mdcas1, mdcas2 clock frequency */ + { 59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 59.0 MHz */ + { 73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 73.7 MHz */ + { 88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 88.5 MHz */ + { 103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 103.2 MHz */ + { 118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff }, /* 118.0 MHz */ + { 132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff }, /* 132.7 MHz */ + { 147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff }, /* 147.5 MHz */ + { 162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff }, /* 162.2 MHz */ + { 176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff }, /* 176.9 MHz */ + { 191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff }, /* 191.7 MHz */ + { 206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff }, /* 206.4 MHz */ + { 221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff }, /* 221.2 MHz */ + { 235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1 }, /* 235.9 MHz */ + { 250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3 }, /* 250.7 MHz */ + { 265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3 }, /* 265.4 MHz */ + { 280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87 }, /* 280.2 MHz */ + { 0, 0, 0, 0, 0 } /* last entry */ +}; + +static void sa1100_update_dram_timings(int current_speed, int new_speed) +{ + sa1100_dram_regs_t *settings = sa1100_dram_settings; + + /* find speed */ + while (settings->speed != 0) { + if(new_speed == settings->speed) + break; + + settings++; + } + + if (settings->speed == 0) { + panic("%s: couldn't find dram setting for speed %d\n", + __FUNCTION__, new_speed); + } + + /* No risk, no fun: run with interrupts on! */ + if (new_speed > current_speed) { + /* We're going FASTER, so first relax the memory + * timings before changing the core frequency + */ + + /* Half the memory access clock */ + MDCNFG |= MDCNFG_CDB2; + + /* The order of these statements IS important, keep 8 + * pulses!! + */ + MDCAS2 = settings->mdcas2; + MDCAS1 = settings->mdcas1; + MDCAS0 = settings->mdcas0; + MDCNFG = settings->mdcnfg; + } else { + /* We're going SLOWER: first decrease the core + * frequency and then tighten the memory settings. + */ + + /* Half the memory access clock */ + MDCNFG |= MDCNFG_CDB2; + + /* The order of these statements IS important, keep 8 + * pulses!! + */ + MDCAS0 = settings->mdcas0; + MDCAS1 = settings->mdcas1; + MDCAS2 = settings->mdcas2; + MDCNFG = settings->mdcnfg; + } +} + +static int sa1100_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int cur = sa11x0_getspeed(0); + unsigned int new_ppcr; + + struct cpufreq_freqs freqs; + switch(relation){ + case CPUFREQ_RELATION_L: + new_ppcr = sa11x0_freq_to_ppcr(target_freq); + if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max) + new_ppcr--; + break; + case CPUFREQ_RELATION_H: + new_ppcr = sa11x0_freq_to_ppcr(target_freq); + if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) && + (sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min)) + new_ppcr--; + break; + } + + freqs.old = cur; + freqs.new = sa11x0_ppcr_to_freq(new_ppcr); + freqs.cpu = 0; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (freqs.new > cur) + sa1100_update_dram_timings(cur, freqs.new); + + PPCR = new_ppcr; + + if (freqs.new < cur) + sa1100_update_dram_timings(cur, freqs.new); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int __init sa1100_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + policy->cur = policy->min = policy->max = sa11x0_getspeed(0); + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.min_freq = 59000; + policy->cpuinfo.max_freq = 287000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + return 0; +} + +static struct cpufreq_driver sa1100_driver = { + .flags = CPUFREQ_STICKY, + .verify = sa11x0_verify_speed, + .target = sa1100_target, + .get = sa11x0_getspeed, + .init = sa1100_cpu_init, + .name = "sa1100", +}; + +static int __init sa1100_dram_init(void) +{ + if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) + return cpufreq_register_driver(&sa1100_driver); + else + return -ENODEV; +} + +arch_initcall(sa1100_dram_init); diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c new file mode 100644 index 0000000..8d2a89a --- /dev/null +++ b/arch/arm/mach-sa1100/cpu-sa1110.c @@ -0,0 +1,367 @@ +/* + * linux/arch/arm/mach-sa1100/cpu-sa1110.c + * + * Copyright (C) 2001 Russell King + * + * $Id: cpu-sa1110.c,v 1.9 2002/07/06 16:53:18 rmk Exp $ + * + * 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. + * + * Note: there are two erratas that apply to the SA1110 here: + * 7 - SDRAM auto-power-up failure (rev A0) + * 13 - Corruption of internal register reads/writes following + * SDRAM reads (rev A0, B0, B1) + * + * We ignore rev. A0 and B0 devices; I don't think they're worth supporting. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/io.h> +#include <asm/system.h> + +#include "generic.h" + +#undef DEBUG + +static struct cpufreq_driver sa1110_driver; + +struct sdram_params { + u_char rows; /* bits */ + u_char cas_latency; /* cycles */ + u_char tck; /* clock cycle time (ns) */ + u_char trcd; /* activate to r/w (ns) */ + u_char trp; /* precharge to activate (ns) */ + u_char twr; /* write recovery time (ns) */ + u_short refresh; /* refresh time for array (us) */ +}; + +struct sdram_info { + u_int mdcnfg; + u_int mdrefr; + u_int mdcas[3]; +}; + +static struct sdram_params tc59sm716_cl2_params __initdata = { + .rows = 12, + .tck = 10, + .trcd = 20, + .trp = 20, + .twr = 10, + .refresh = 64000, + .cas_latency = 2, +}; + +static struct sdram_params tc59sm716_cl3_params __initdata = { + .rows = 12, + .tck = 8, + .trcd = 20, + .trp = 20, + .twr = 8, + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params samsung_k4s641632d_tc75 __initdata = { + .rows = 14, + .tck = 9, + .trcd = 27, + .trp = 20, + .twr = 9, + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params samsung_km416s4030ct __initdata = { + .rows = 13, + .tck = 8, + .trcd = 24, /* 3 CLKs */ + .trp = 24, /* 3 CLKs */ + .twr = 16, /* Trdl: 2 CLKs */ + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params wbond_w982516ah75l_cl3_params __initdata = { + .rows = 16, + .tck = 8, + .trcd = 20, + .trp = 20, + .twr = 8, + .refresh = 64000, + .cas_latency = 3, +}; + +static struct sdram_params sdram_params; + +/* + * Given a period in ns and frequency in khz, calculate the number of + * cycles of frequency in period. Note that we round up to the next + * cycle, even if we are only slightly over. + */ +static inline u_int ns_to_cycles(u_int ns, u_int khz) +{ + return (ns * khz + 999999) / 1000000; +} + +/* + * Create the MDCAS register bit pattern. + */ +static inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd) +{ + u_int shift; + + rcd = 2 * rcd - 1; + shift = delayed + 1 + rcd; + + mdcas[0] = (1 << rcd) - 1; + mdcas[0] |= 0x55555555 << shift; + mdcas[1] = mdcas[2] = 0x55555555 << (shift & 1); +} + +static void +sdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz, + struct sdram_params *sdram) +{ + u_int mem_khz, sd_khz, trp, twr; + + mem_khz = cpu_khz / 2; + sd_khz = mem_khz; + + /* + * If SDCLK would invalidate the SDRAM timings, + * run SDCLK at half speed. + * + * CPU steppings prior to B2 must either run the memory at + * half speed or use delayed read latching (errata 13). + */ + if ((ns_to_cycles(sdram->tck, sd_khz) > 1) || + (CPU_REVISION < CPU_SA1110_B2 && sd_khz < 62000)) + sd_khz /= 2; + + sd->mdcnfg = MDCNFG & 0x007f007f; + + twr = ns_to_cycles(sdram->twr, mem_khz); + + /* trp should always be >1 */ + trp = ns_to_cycles(sdram->trp, mem_khz) - 1; + if (trp < 1) + trp = 1; + + sd->mdcnfg |= trp << 8; + sd->mdcnfg |= trp << 24; + sd->mdcnfg |= sdram->cas_latency << 12; + sd->mdcnfg |= sdram->cas_latency << 28; + sd->mdcnfg |= twr << 14; + sd->mdcnfg |= twr << 30; + + sd->mdrefr = MDREFR & 0xffbffff0; + sd->mdrefr |= 7; + + if (sd_khz != mem_khz) + sd->mdrefr |= MDREFR_K1DB2; + + /* initial number of '1's in MDCAS + 1 */ + set_mdcas(sd->mdcas, sd_khz >= 62000, ns_to_cycles(sdram->trcd, mem_khz)); + +#ifdef DEBUG + printk("MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n", + sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1], sd->mdcas[2]); +#endif +} + +/* + * Set the SDRAM refresh rate. + */ +static inline void sdram_set_refresh(u_int dri) +{ + MDREFR = (MDREFR & 0xffff000f) | (dri << 4); + (void) MDREFR; +} + +/* + * Update the refresh period. We do this such that we always refresh + * the SDRAMs within their permissible period. The refresh period is + * always a multiple of the memory clock (fixed at cpu_clock / 2). + * + * FIXME: we don't currently take account of burst accesses here, + * but neither do Intels DM nor Angel. + */ +static void +sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram) +{ + u_int ns_row = (sdram->refresh * 1000) >> sdram->rows; + u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32; + +#ifdef DEBUG + mdelay(250); + printk("new dri value = %d\n", dri); +#endif + + sdram_set_refresh(dri); +} + +/* + * Ok, set the CPU frequency. + */ +static int sa1110_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct sdram_params *sdram = &sdram_params; + struct cpufreq_freqs freqs; + struct sdram_info sd; + unsigned long flags; + unsigned int ppcr, unused; + + switch(relation){ + case CPUFREQ_RELATION_L: + ppcr = sa11x0_freq_to_ppcr(target_freq); + if (sa11x0_ppcr_to_freq(ppcr) > policy->max) + ppcr--; + break; + case CPUFREQ_RELATION_H: + ppcr = sa11x0_freq_to_ppcr(target_freq); + if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) && + (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min)) + ppcr--; + break; + default: + return -EINVAL; + } + + freqs.old = sa11x0_getspeed(0); + freqs.new = sa11x0_ppcr_to_freq(ppcr); + freqs.cpu = 0; + + sdram_calculate_timing(&sd, freqs.new, sdram); + +#if 0 + /* + * These values are wrong according to the SA1110 documentation + * and errata, but they seem to work. Need to get a storage + * scope on to the SDRAM signals to work out why. + */ + if (policy->max < 147500) { + sd.mdrefr |= MDREFR_K1DB2; + sd.mdcas[0] = 0xaaaaaa7f; + } else { + sd.mdrefr &= ~MDREFR_K1DB2; + sd.mdcas[0] = 0xaaaaaa9f; + } + sd.mdcas[1] = 0xaaaaaaaa; + sd.mdcas[2] = 0xaaaaaaaa; +#endif + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* + * The clock could be going away for some time. Set the SDRAMs + * to refresh rapidly (every 64 memory clock cycles). To get + * through the whole array, we need to wait 262144 mclk cycles. + * We wait 20ms to be safe. + */ + sdram_set_refresh(2); + if (!irqs_disabled()) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(20 * HZ / 1000); + } else { + mdelay(20); + } + + /* + * Reprogram the DRAM timings with interrupts disabled, and + * ensure that we are doing this within a complete cache line. + * This means that we won't access SDRAM for the duration of + * the programming. + */ + local_irq_save(flags); + asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); + udelay(10); + __asm__ __volatile__(" \n\ + b 2f \n\ + .align 5 \n\ +1: str %3, [%1, #0] @ MDCNFG \n\ + str %4, [%1, #28] @ MDREFR \n\ + str %5, [%1, #4] @ MDCAS0 \n\ + str %6, [%1, #8] @ MDCAS1 \n\ + str %7, [%1, #12] @ MDCAS2 \n\ + str %8, [%2, #0] @ PPCR \n\ + ldr %0, [%1, #0] \n\ + b 3f \n\ +2: b 1b \n\ +3: nop \n\ + nop" + : "=&r" (unused) + : "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg), + "r" (sd.mdrefr), "r" (sd.mdcas[0]), + "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr)); + local_irq_restore(flags); + + /* + * Now, return the SDRAM refresh back to normal. + */ + sdram_update_refresh(freqs.new, sdram); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int __init sa1110_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -EINVAL; + policy->cur = policy->min = policy->max = sa11x0_getspeed(0); + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.min_freq = 59000; + policy->cpuinfo.max_freq = 287000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + return 0; +} + +static struct cpufreq_driver sa1110_driver = { + .flags = CPUFREQ_STICKY, + .verify = sa11x0_verify_speed, + .target = sa1110_target, + .get = sa11x0_getspeed, + .init = sa1110_cpu_init, + .name = "sa1110", +}; + +static int __init sa1110_clk_init(void) +{ + struct sdram_params *sdram = NULL; + + if (machine_is_assabet()) + sdram = &tc59sm716_cl3_params; + + if (machine_is_pt_system3()) + sdram = &samsung_k4s641632d_tc75; + + if (machine_is_h3100()) + sdram = &samsung_km416s4030ct; + + if (sdram) { + printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d" + " twr: %d refresh: %d cas_latency: %d\n", + sdram->tck, sdram->trcd, sdram->trp, + sdram->twr, sdram->refresh, sdram->cas_latency); + + memcpy(&sdram_params, sdram, sizeof(sdram_params)); + + return cpufreq_register_driver(&sa1110_driver); + } + + return 0; +} + +arch_initcall(sa1110_clk_init); diff --git a/arch/arm/mach-sa1100/dma.c b/arch/arm/mach-sa1100/dma.c new file mode 100644 index 0000000..be0e442 --- /dev/null +++ b/arch/arm/mach-sa1100/dma.c @@ -0,0 +1,348 @@ +/* + * arch/arm/kernel/dma-sa1100.c + * + * Support functions for the SA11x0 internal DMA channels. + * + * Copyright (C) 2000, 2001 by 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. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/errno.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/dma.h> + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( s, arg... ) printk( "dma<%p>: " s, regs , ##arg ) +#else +#define DPRINTK( x... ) +#endif + + +typedef struct { + const char *device_id; /* device name */ + u_long device; /* this channel device, 0 if unused*/ + dma_callback_t callback; /* to call when DMA completes */ + void *data; /* ... with private data ptr */ +} sa1100_dma_t; + +static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS]; + +static spinlock_t dma_list_lock; + + +static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + dma_regs_t *dma_regs = dev_id; + sa1100_dma_t *dma = dma_chan + (((u_int)dma_regs >> 5) & 7); + int status = dma_regs->RdDCSR; + + if (status & (DCSR_ERROR)) { + printk(KERN_CRIT "DMA on \"%s\" caused an error\n", dma->device_id); + dma_regs->ClrDCSR = DCSR_ERROR; + } + + dma_regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB); + if (dma->callback) { + if (status & DCSR_DONEA) + dma->callback(dma->data); + if (status & DCSR_DONEB) + dma->callback(dma->data); + } + return IRQ_HANDLED; +} + + +/** + * sa1100_request_dma - allocate one of the SA11x0's DMA chanels + * @device: The SA11x0 peripheral targeted by this request + * @device_id: An ascii name for the claiming device + * @callback: Function to be called when the DMA completes + * @data: A cookie passed back to the callback function + * @dma_regs: Pointer to the location of the allocated channel's identifier + * + * This function will search for a free DMA channel and returns the + * address of the hardware registers for that channel as the channel + * identifier. This identifier is written to the location pointed by + * @dma_regs. The list of possible values for @device are listed into + * linux/include/asm-arm/arch-sa1100/dma.h as a dma_device_t enum. + * + * Note that reading from a port and writing to the same port are + * actually considered as two different streams requiring separate + * DMA registrations. + * + * The @callback function is called from interrupt context when one + * of the two possible DMA buffers in flight has terminated. That + * function has to be small and efficient while posponing more complex + * processing to a lower priority execution context. + * + * If no channels are available, or if the desired @device is already in + * use by another DMA channel, then an error code is returned. This + * function must be called before any other DMA calls. + **/ + +int sa1100_request_dma (dma_device_t device, const char *device_id, + dma_callback_t callback, void *data, + dma_regs_t **dma_regs) +{ + sa1100_dma_t *dma = NULL; + dma_regs_t *regs; + int i, err; + + *dma_regs = NULL; + + err = 0; + spin_lock(&dma_list_lock); + for (i = 0; i < SA1100_DMA_CHANNELS; i++) { + if (dma_chan[i].device == device) { + err = -EBUSY; + break; + } else if (!dma_chan[i].device && !dma) { + dma = &dma_chan[i]; + } + } + if (!err) { + if (dma) + dma->device = device; + else + err = -ENOSR; + } + spin_unlock(&dma_list_lock); + if (err) + return err; + + i = dma - dma_chan; + regs = (dma_regs_t *)&DDAR(i); + err = request_irq(IRQ_DMA0 + i, dma_irq_handler, SA_INTERRUPT, + device_id, regs); + if (err) { + printk(KERN_ERR + "%s: unable to request IRQ %d for %s\n", + __FUNCTION__, IRQ_DMA0 + i, device_id); + dma->device = 0; + return err; + } + + *dma_regs = regs; + dma->device_id = device_id; + dma->callback = callback; + dma->data = data; + + regs->ClrDCSR = + (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | + DCSR_IE | DCSR_ERROR | DCSR_RUN); + regs->DDAR = device; + + return 0; +} + + +/** + * sa1100_free_dma - free a SA11x0 DMA channel + * @regs: identifier for the channel to free + * + * This clears all activities on a given DMA channel and releases it + * for future requests. The @regs identifier is provided by a + * successful call to sa1100_request_dma(). + **/ + +void sa1100_free_dma(dma_regs_t *regs) +{ + int i; + + for (i = 0; i < SA1100_DMA_CHANNELS; i++) + if (regs == (dma_regs_t *)&DDAR(i)) + break; + if (i >= SA1100_DMA_CHANNELS) { + printk(KERN_ERR "%s: bad DMA identifier\n", __FUNCTION__); + return; + } + + if (!dma_chan[i].device) { + printk(KERN_ERR "%s: Trying to free free DMA\n", __FUNCTION__); + return; + } + + regs->ClrDCSR = + (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | + DCSR_IE | DCSR_ERROR | DCSR_RUN); + free_irq(IRQ_DMA0 + i, regs); + dma_chan[i].device = 0; +} + + +/** + * sa1100_start_dma - submit a data buffer for DMA + * @regs: identifier for the channel to use + * @dma_ptr: buffer physical (or bus) start address + * @size: buffer size + * + * This function hands the given data buffer to the hardware for DMA + * access. If another buffer is already in flight then this buffer + * will be queued so the DMA engine will switch to it automatically + * when the previous one is done. The DMA engine is actually toggling + * between two buffers so at most 2 successful calls can be made before + * one of them terminates and the callback function is called. + * + * The @regs identifier is provided by a successful call to + * sa1100_request_dma(). + * + * The @size must not be larger than %MAX_DMA_SIZE. If a given buffer + * is larger than that then it's the caller's responsibility to split + * it into smaller chunks and submit them separately. If this is the + * case then a @size of %CUT_DMA_SIZE is recommended to avoid ending + * up with too small chunks. The callback function can be used to chain + * submissions of buffer chunks. + * + * Error return values: + * %-EOVERFLOW: Given buffer size is too big. + * %-EBUSY: Both DMA buffers are already in use. + * %-EAGAIN: Both buffers were busy but one of them just completed + * but the interrupt handler has to execute first. + * + * This function returs 0 on success. + **/ + +int sa1100_start_dma(dma_regs_t *regs, dma_addr_t dma_ptr, u_int size) +{ + unsigned long flags; + u_long status; + int ret; + + if (dma_ptr & 3) + printk(KERN_WARNING "DMA: unaligned start address (0x%08lx)\n", + (unsigned long)dma_ptr); + + if (size > MAX_DMA_SIZE) + return -EOVERFLOW; + + local_irq_save(flags); + status = regs->RdDCSR; + + /* If both DMA buffers are started, there's nothing else we can do. */ + if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) { + DPRINTK("start: st %#x busy\n", status); + ret = -EBUSY; + goto out; + } + + if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || + (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) { + if (status & DCSR_DONEA) { + /* give a chance for the interrupt to be processed */ + ret = -EAGAIN; + goto out; + } + regs->DBSA = dma_ptr; + regs->DBTA = size; + regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN; + DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size); + } else { + if (status & DCSR_DONEB) { + /* give a chance for the interrupt to be processed */ + ret = -EAGAIN; + goto out; + } + regs->DBSB = dma_ptr; + regs->DBTB = size; + regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN; + DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size); + } + ret = 0; + +out: + local_irq_restore(flags); + return ret; +} + + +/** + * sa1100_get_dma_pos - return current DMA position + * @regs: identifier for the channel to use + * + * This function returns the current physical (or bus) address for the + * given DMA channel. If the channel is running i.e. not in a stopped + * state then the caller must disable interrupts prior calling this + * function and process the returned value before re-enabling them to + * prevent races with the completion interrupt handler and the callback + * function. The validation of the returned value is the caller's + * responsibility as well -- the hardware seems to return out of range + * values when the DMA engine completes a buffer. + * + * The @regs identifier is provided by a successful call to + * sa1100_request_dma(). + **/ + +dma_addr_t sa1100_get_dma_pos(dma_regs_t *regs) +{ + int status; + + /* + * We must determine whether buffer A or B is active. + * Two possibilities: either we are in the middle of + * a buffer, or the DMA controller just switched to the + * next toggle but the interrupt hasn't been serviced yet. + * The former case is straight forward. In the later case, + * we'll do like if DMA is just at the end of the previous + * toggle since all registers haven't been reset yet. + * This goes around the edge case and since we're always + * a little behind anyways it shouldn't make a big difference. + * If DMA has been stopped prior calling this then the + * position is exact. + */ + status = regs->RdDCSR; + if ((!(status & DCSR_BIU) && (status & DCSR_STRTA)) || + ( (status & DCSR_BIU) && !(status & DCSR_STRTB))) + return regs->DBSA; + else + return regs->DBSB; +} + + +/** + * sa1100_reset_dma - reset a DMA channel + * @regs: identifier for the channel to use + * + * This function resets and reconfigure the given DMA channel. This is + * particularly useful after a sleep/wakeup event. + * + * The @regs identifier is provided by a successful call to + * sa1100_request_dma(). + **/ + +void sa1100_reset_dma(dma_regs_t *regs) +{ + int i; + + for (i = 0; i < SA1100_DMA_CHANNELS; i++) + if (regs == (dma_regs_t *)&DDAR(i)) + break; + if (i >= SA1100_DMA_CHANNELS) { + printk(KERN_ERR "%s: bad DMA identifier\n", __FUNCTION__); + return; + } + + regs->ClrDCSR = + (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB | + DCSR_IE | DCSR_ERROR | DCSR_RUN); + regs->DDAR = dma_chan[i].device; +} + + +EXPORT_SYMBOL(sa1100_request_dma); +EXPORT_SYMBOL(sa1100_free_dma); +EXPORT_SYMBOL(sa1100_start_dma); +EXPORT_SYMBOL(sa1100_get_dma_pos); +EXPORT_SYMBOL(sa1100_reset_dma); + diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c new file mode 100644 index 0000000..95ae217 --- /dev/null +++ b/arch/arm/mach-sa1100/generic.c @@ -0,0 +1,419 @@ +/* + * linux/arch/arm/mach-sa1100/generic.c + * + * Author: Nicolas Pitre + * + * Code common to all SA11x0 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. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/cpufreq.h> +#include <linux/ioport.h> + +#include <asm/div64.h> +#include <asm/hardware.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/mach/map.h> +#include <asm/irq.h> + +#include "generic.h" + +#define NR_FREQS 16 + +/* + * This table is setup for a 3.6864MHz Crystal. + */ +static const unsigned short cclk_frequency_100khz[NR_FREQS] = { + 590, /* 59.0 MHz */ + 737, /* 73.7 MHz */ + 885, /* 88.5 MHz */ + 1032, /* 103.2 MHz */ + 1180, /* 118.0 MHz */ + 1327, /* 132.7 MHz */ + 1475, /* 147.5 MHz */ + 1622, /* 162.2 MHz */ + 1769, /* 176.9 MHz */ + 1917, /* 191.7 MHz */ + 2064, /* 206.4 MHz */ + 2212, /* 221.2 MHz */ + 2359, /* 235.9 MHz */ + 2507, /* 250.7 MHz */ + 2654, /* 265.4 MHz */ + 2802 /* 280.2 MHz */ +}; + +#if defined(CONFIG_CPU_FREQ_SA1100) || defined(CONFIG_CPU_FREQ_SA1110) +/* rounds up(!) */ +unsigned int sa11x0_freq_to_ppcr(unsigned int khz) +{ + int i; + + khz /= 100; + + for (i = 0; i < NR_FREQS; i++) + if (cclk_frequency_100khz[i] >= khz) + break; + + return i; +} + +unsigned int sa11x0_ppcr_to_freq(unsigned int idx) +{ + unsigned int freq = 0; + if (idx < NR_FREQS) + freq = cclk_frequency_100khz[idx] * 100; + return freq; +} + + +/* make sure that only the "userspace" governor is run -- anything else wouldn't make sense on + * this platform, anyway. + */ +int sa11x0_verify_speed(struct cpufreq_policy *policy) +{ + unsigned int tmp; + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + + /* make sure that at least one frequency is within the policy */ + tmp = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->min)] * 100; + if (tmp > policy->max) + policy->max = tmp; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + + return 0; +} + +unsigned int sa11x0_getspeed(unsigned int cpu) +{ + if (cpu) + return 0; + return cclk_frequency_100khz[PPCR & 0xf] * 100; +} + +#else +/* + * We still need to provide this so building without cpufreq works. + */ +unsigned int cpufreq_get(unsigned int cpu) +{ + return cclk_frequency_100khz[PPCR & 0xf] * 100; +} +EXPORT_SYMBOL(cpufreq_get); +#endif + +/* + * This is the SA11x0 sched_clock implementation. This has + * a resolution of 271ns, and a maximum value of 1165s. + * ( * 1E9 / 3686400 => * 78125 / 288) + */ +unsigned long long sched_clock(void) +{ + unsigned long long v; + + v = (unsigned long long)OSCR * 78125; + do_div(v, 288); + + return v; +} + +/* + * Default power-off for SA1100 + */ +static void sa1100_power_off(void) +{ + mdelay(100); + local_irq_disable(); + /* disable internal oscillator, float CS lines */ + PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS); + /* enable wake-up on GPIO0 (Assabet...) */ + PWER = GFER = GRER = 1; + /* + * set scratchpad to zero, just in case it is used as a + * restart address by the bootloader. + */ + PSPR = 0; + /* enter sleep mode */ + PMCR = PMCR_SF; +} + +static struct resource sa11x0udc_resources[] = { + [0] = { + .start = 0x80000000, + .end = 0x8000ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa11x0udc_dma_mask = 0xffffffffUL; + +static struct platform_device sa11x0udc_device = { + .name = "sa11x0-udc", + .id = -1, + .dev = { + .dma_mask = &sa11x0udc_dma_mask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa11x0udc_resources), + .resource = sa11x0udc_resources, +}; + +static struct resource sa11x0uart1_resources[] = { + [0] = { + .start = 0x80010000, + .end = 0x8001ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sa11x0uart1_device = { + .name = "sa11x0-uart", + .id = 1, + .num_resources = ARRAY_SIZE(sa11x0uart1_resources), + .resource = sa11x0uart1_resources, +}; + +static struct resource sa11x0uart3_resources[] = { + [0] = { + .start = 0x80050000, + .end = 0x8005ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sa11x0uart3_device = { + .name = "sa11x0-uart", + .id = 3, + .num_resources = ARRAY_SIZE(sa11x0uart3_resources), + .resource = sa11x0uart3_resources, +}; + +static struct resource sa11x0mcp_resources[] = { + [0] = { + .start = 0x80060000, + .end = 0x8006ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa11x0mcp_dma_mask = 0xffffffffUL; + +static struct platform_device sa11x0mcp_device = { + .name = "sa11x0-mcp", + .id = -1, + .dev = { + .dma_mask = &sa11x0mcp_dma_mask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa11x0mcp_resources), + .resource = sa11x0mcp_resources, +}; + +static struct resource sa11x0ssp_resources[] = { + [0] = { + .start = 0x80070000, + .end = 0x8007ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa11x0ssp_dma_mask = 0xffffffffUL; + +static struct platform_device sa11x0ssp_device = { + .name = "sa11x0-ssp", + .id = -1, + .dev = { + .dma_mask = &sa11x0ssp_dma_mask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa11x0ssp_resources), + .resource = sa11x0ssp_resources, +}; + +static struct resource sa11x0fb_resources[] = { + [0] = { + .start = 0xb0100000, + .end = 0xb010ffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD, + .end = IRQ_LCD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sa11x0fb_device = { + .name = "sa11x0-fb", + .id = -1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa11x0fb_resources), + .resource = sa11x0fb_resources, +}; + +static struct platform_device sa11x0pcmcia_device = { + .name = "sa11x0-pcmcia", + .id = -1, +}; + +static struct platform_device sa11x0mtd_device = { + .name = "flash", + .id = -1, +}; + +void sa11x0_set_flash_data(struct flash_platform_data *flash, + struct resource *res, int nr) +{ + sa11x0mtd_device.dev.platform_data = flash; + sa11x0mtd_device.resource = res; + sa11x0mtd_device.num_resources = nr; +} + +static struct resource sa11x0ir_resources[] = { + { + .start = __PREG(Ser2UTCR0), + .end = __PREG(Ser2UTCR0) + 0x24 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = __PREG(Ser2HSCR0), + .end = __PREG(Ser2HSCR0) + 0x1c - 1, + .flags = IORESOURCE_MEM, + }, { + .start = __PREG(Ser2HSCR2), + .end = __PREG(Ser2HSCR2) + 0x04 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_Ser2ICP, + .end = IRQ_Ser2ICP, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sa11x0ir_device = { + .name = "sa11x0-ir", + .id = -1, + .num_resources = ARRAY_SIZE(sa11x0ir_resources), + .resource = sa11x0ir_resources, +}; + +void sa11x0_set_irda_data(struct irda_platform_data *irda) +{ + sa11x0ir_device.dev.platform_data = irda; +} + +static struct platform_device *sa11x0_devices[] __initdata = { + &sa11x0udc_device, + &sa11x0uart1_device, + &sa11x0uart3_device, + &sa11x0mcp_device, + &sa11x0ssp_device, + &sa11x0pcmcia_device, + &sa11x0fb_device, + &sa11x0mtd_device, +}; + +static int __init sa1100_init(void) +{ + pm_power_off = sa1100_power_off; + + if (sa11x0ir_device.dev.platform_data) + platform_device_register(&sa11x0ir_device); + + return platform_add_devices(sa11x0_devices, ARRAY_SIZE(sa11x0_devices)); +} + +arch_initcall(sa1100_init); + +void (*sa1100fb_backlight_power)(int on); +void (*sa1100fb_lcd_power)(int on); + +EXPORT_SYMBOL(sa1100fb_backlight_power); +EXPORT_SYMBOL(sa1100fb_lcd_power); + + +/* + * Common I/O mapping: + * + * Typically, static virtual address mappings are as follow: + * + * 0xf0000000-0xf3ffffff: miscellaneous stuff (CPLDs, etc.) + * 0xf4000000-0xf4ffffff: SA-1111 + * 0xf5000000-0xf5ffffff: reserved (used by cache flushing area) + * 0xf6000000-0xfffeffff: reserved (internal SA1100 IO defined above) + * 0xffff0000-0xffff0fff: SA1100 exception vectors + * 0xffff2000-0xffff2fff: Minicache copy_user_page area + * + * Below 0xe8000000 is reserved for vm allocation. + * + * The machine specific code must provide the extra mapping beside the + * default mapping provided here. + */ + +static struct map_desc standard_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf8000000, 0x80000000, 0x00100000, MT_DEVICE }, /* PCM */ + { 0xfa000000, 0x90000000, 0x00100000, MT_DEVICE }, /* SCM */ + { 0xfc000000, 0xa0000000, 0x00100000, MT_DEVICE }, /* MER */ + { 0xfe000000, 0xb0000000, 0x00200000, MT_DEVICE } /* LCD + DMA */ +}; + +void __init sa1100_map_io(void) +{ + iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc)); +} + +/* + * Disable the memory bus request/grant signals on the SA1110 to + * ensure that we don't receive spurious memory requests. We set + * the MBGNT signal false to ensure the SA1111 doesn't own the + * SDRAM bus. + */ +void __init sa1110_mb_disable(void) +{ + unsigned long flags; + + local_irq_save(flags); + + PGSR &= ~GPIO_MBGNT; + GPCR = GPIO_MBGNT; + GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT; + + GAFR &= ~(GPIO_MBGNT | GPIO_MBREQ); + + local_irq_restore(flags); +} + +/* + * If the system is going to use the SA-1111 DMA engines, set up + * the memory bus request/grant pins. + */ +void __init sa1110_mb_enable(void) +{ + unsigned long flags; + + local_irq_save(flags); + + PGSR &= ~GPIO_MBGNT; + GPCR = GPIO_MBGNT; + GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT; + + GAFR |= (GPIO_MBGNT | GPIO_MBREQ); + TUCR |= TUCR_MR; + + local_irq_restore(flags); +} + diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h new file mode 100644 index 0000000..bfe41da --- /dev/null +++ b/arch/arm/mach-sa1100/generic.h @@ -0,0 +1,38 @@ +/* + * linux/arch/arm/mach-sa1100/generic.h + * + * Author: Nicolas Pitre + */ + +struct sys_timer; + +extern struct sys_timer sa1100_timer; +extern void __init sa1100_map_io(void); +extern void __init sa1100_init_irq(void); + +#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) + +extern void (*sa1100fb_backlight_power)(int on); +extern void (*sa1100fb_lcd_power)(int on); + +extern void sa1110_mb_enable(void); +extern void sa1110_mb_disable(void); + +struct cpufreq_policy; + +extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz); +extern int sa11x0_verify_speed(struct cpufreq_policy *policy); +extern unsigned int sa11x0_getspeed(unsigned int cpu); +extern unsigned int sa11x0_ppcr_to_freq(unsigned int idx); + +struct flash_platform_data; +struct resource; + +extern void sa11x0_set_flash_data(struct flash_platform_data *flash, + struct resource *res, int nr); + +struct irda_platform_data; +void sa11x0_set_irda_data(struct irda_platform_data *irda); diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c new file mode 100644 index 0000000..9788d3a --- /dev/null +++ b/arch/arm/mach-sa1100/h3600.c @@ -0,0 +1,892 @@ +/* + * Hardware definitions for Compaq iPAQ H3xxx Handheld Computers + * + * Copyright 2000,1 Compaq Computer Corporation. + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Author: Jamey Hicks. + * + * History: + * + * 2001-10-?? Andrew Christian Added support for iPAQ H3800 + * and abstracted EGPIO interface. + * + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/serial_core.h> + +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/setup.h> + +#include <asm/mach/irq.h> +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/irda.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> + +#include <asm/arch/h3600.h> + +#if defined (CONFIG_SA1100_H3600) || defined (CONFIG_SA1100_H3100) +#include <asm/arch/h3600_gpio.h> +#endif + +#ifdef CONFIG_SA1100_H3800 +#include <asm/arch/h3600_asic.h> +#endif + +#include "generic.h" + +struct ipaq_model_ops ipaq_model_ops; +EXPORT_SYMBOL(ipaq_model_ops); + +static struct mtd_partition h3xxx_partitions[] = { + { + .name = "H3XXX boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +#ifdef CONFIG_MTD_2PARTS_IPAQ + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00040000, +#else + .name = "H3XXX kernel", + .size = 0x00080000, + .offset = 0x00040000, + }, { + .name = "H3XXX params", + .size = 0x00040000, + .offset = 0x000C0000, + }, { +#ifdef CONFIG_JFFS2_FS + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00100000, +#else + .name = "H3XXX initrd", + .size = 0x00100000, + .offset = 0x00100000, + }, { + .name = "H3XXX root cramfs", + .size = 0x00300000, + .offset = 0x00200000, + }, { + .name = "H3XXX usr cramfs", + .size = 0x00800000, + .offset = 0x00500000, + }, { + .name = "H3XXX usr local", + .size = MTDPART_SIZ_FULL, + .offset = 0x00d00000, +#endif +#endif + } +}; + +static void h3xxx_set_vpp(int vpp) +{ + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); +} + +static struct flash_platform_data h3xxx_flash_data = { + .map_name = "cfi_probe", + .set_vpp = h3xxx_set_vpp, + .parts = h3xxx_partitions, + .nr_parts = ARRAY_SIZE(h3xxx_partitions), +}; + +static struct resource h3xxx_flash_resource = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +/* + * This turns the IRDA power on or off on the Compaq H3600 + */ +static int h3600_irda_set_power(struct device *dev, unsigned int state) +{ + assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); + + return 0; +} + +static void h3600_irda_set_speed(struct device *dev, int speed) +{ + if (speed < 4000000) { + clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + } else { + set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + } +} + +static struct irda_platform_data h3600_irda_data = { + .set_power = h3600_irda_set_power, + .set_speed = h3600_irda_set_speed, +}; + +static void h3xxx_mach_init(void) +{ + sa11x0_set_flash_data(&h3xxx_flash_data, &h3xxx_flash_resource, 1); + sa11x0_set_irda_data(&h3600_irda_data); +} + +/* + * low-level UART features + */ + +static void h3600_uart_set_mctrl(struct uart_port *port, u_int mctrl) +{ + if (port->mapbase == _Ser3UTCR0) { + if (mctrl & TIOCM_RTS) + GPCR = GPIO_H3600_COM_RTS; + else + GPSR = GPIO_H3600_COM_RTS; + } +} + +static u_int h3600_uart_get_mctrl(struct uart_port *port) +{ + u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; + + if (port->mapbase == _Ser3UTCR0) { + int gplr = GPLR; + /* DCD and CTS bits are inverted in GPLR by RS232 transceiver */ + if (gplr & GPIO_H3600_COM_DCD) + ret &= ~TIOCM_CD; + if (gplr & GPIO_H3600_COM_CTS) + ret &= ~TIOCM_CTS; + } + + return ret; +} + +static void h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (port->mapbase == _Ser2UTCR0) { /* TODO: REMOVE THIS */ + assign_h3600_egpio(IPAQ_EGPIO_IR_ON, !state); + } else if (port->mapbase == _Ser3UTCR0) { + assign_h3600_egpio(IPAQ_EGPIO_RS232_ON, !state); + } +} + +/* + * Enable/Disable wake up events for this serial port. + * Obviously, we only support this on the normal COM port. + */ +static int h3600_uart_set_wake(struct uart_port *port, u_int enable) +{ + int err = -EINVAL; + + if (port->mapbase == _Ser3UTCR0) { + if (enable) + PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */ + else + PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */ + err = 0; + } + return err; +} + +static struct sa1100_port_fns h3600_port_fns __initdata = { + .set_mctrl = h3600_uart_set_mctrl, + .get_mctrl = h3600_uart_get_mctrl, + .pm = h3600_uart_pm, + .set_wake = h3600_uart_set_wake, +}; + +/* + * helper for sa1100fb + */ +static void h3xxx_lcd_power(int enable) +{ + assign_h3600_egpio(IPAQ_EGPIO_LCD_POWER, enable); +} + +static struct map_desc h3600_io_desc[] __initdata = { + /* virtual physical length type */ + { H3600_BANK_2_VIRT, SA1100_CS2_PHYS, 0x02800000, MT_DEVICE }, /* static memory bank 2 CS#2 */ + { H3600_BANK_4_VIRT, SA1100_CS4_PHYS, 0x00800000, MT_DEVICE }, /* static memory bank 4 CS#4 */ + { H3600_EGPIO_VIRT, H3600_EGPIO_PHYS, 0x01000000, MT_DEVICE }, /* EGPIO 0 CS#5 */ +}; + +/* + * Common map_io initialization + */ + +static void __init h3xxx_map_io(void) +{ + sa1100_map_io(); + iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc)); + + sa1100_register_uart_fns(&h3600_port_fns); + sa1100_register_uart(0, 3); /* Common serial port */ +// sa1100_register_uart(1, 1); /* Microcontroller on 3100/3600 */ + + /* Ensure those pins are outputs and driving low */ + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + /* Configure suspend conditions */ + PGSR = 0; + PWER = PWER_GPIO0 | PWER_RTC; + PCFR = PCFR_OPDE; + PSDR = 0; + + sa1100fb_lcd_power = h3xxx_lcd_power; +} + +static __inline__ void do_blank(int setp) +{ + if (ipaq_model_ops.blank_callback) + ipaq_model_ops.blank_callback(1-setp); +} + +/************************* H3100 *************************/ + +#ifdef CONFIG_SA1100_H3100 + +#define H3100_EGPIO (*(volatile unsigned int *)H3600_EGPIO_VIRT) +static unsigned int h3100_egpio = 0; + +static void h3100_control_egpio(enum ipaq_egpio_type x, int setp) +{ + unsigned int egpio = 0; + long gpio = 0; + unsigned long flags; + + switch (x) { + case IPAQ_EGPIO_LCD_POWER: + egpio |= EGPIO_H3600_LCD_ON; + gpio |= GPIO_H3100_LCD_3V_ON; + do_blank(setp); + break; + case IPAQ_EGPIO_LCD_ENABLE: + break; + case IPAQ_EGPIO_CODEC_NRESET: + egpio |= EGPIO_H3600_CODEC_NRESET; + break; + case IPAQ_EGPIO_AUDIO_ON: + gpio |= GPIO_H3100_AUD_PWR_ON + | GPIO_H3100_AUD_ON; + break; + case IPAQ_EGPIO_QMUTE: + gpio |= GPIO_H3100_QMUTE; + break; + case IPAQ_EGPIO_OPT_NVRAM_ON: + egpio |= EGPIO_H3600_OPT_NVRAM_ON; + break; + case IPAQ_EGPIO_OPT_ON: + egpio |= EGPIO_H3600_OPT_ON; + break; + case IPAQ_EGPIO_CARD_RESET: + egpio |= EGPIO_H3600_CARD_RESET; + break; + case IPAQ_EGPIO_OPT_RESET: + egpio |= EGPIO_H3600_OPT_RESET; + break; + case IPAQ_EGPIO_IR_ON: + gpio |= GPIO_H3100_IR_ON; + break; + case IPAQ_EGPIO_IR_FSEL: + gpio |= GPIO_H3100_IR_FSEL; + break; + case IPAQ_EGPIO_RS232_ON: + egpio |= EGPIO_H3600_RS232_ON; + break; + case IPAQ_EGPIO_VPP_ON: + egpio |= EGPIO_H3600_VPP_ON; + break; + } + + if (egpio || gpio) { + local_irq_save(flags); + if (setp) { + h3100_egpio |= egpio; + GPSR = gpio; + } else { + h3100_egpio &= ~egpio; + GPCR = gpio; + } + H3100_EGPIO = h3100_egpio; + local_irq_restore(flags); + } +} + +static unsigned long h3100_read_egpio(void) +{ + return h3100_egpio; +} + +static int h3100_pm_callback(int req) +{ + if (ipaq_model_ops.pm_callback_aux) + return ipaq_model_ops.pm_callback_aux(req); + return 0; +} + +static struct ipaq_model_ops h3100_model_ops __initdata = { + .generic_name = "3100", + .control = h3100_control_egpio, + .read = h3100_read_egpio, + .pm_callback = h3100_pm_callback +}; + +#define H3100_DIRECT_EGPIO (GPIO_H3100_BT_ON \ + | GPIO_H3100_GPIO3 \ + | GPIO_H3100_QMUTE \ + | GPIO_H3100_LCD_3V_ON \ + | GPIO_H3100_AUD_ON \ + | GPIO_H3100_AUD_PWR_ON \ + | GPIO_H3100_IR_ON \ + | GPIO_H3100_IR_FSEL) + +static void __init h3100_map_io(void) +{ + h3xxx_map_io(); + + /* Initialize h3100-specific values here */ + GPCR = 0x0fffffff; /* All outputs are set low by default */ + GPDR = GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | + GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | + GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 | + H3100_DIRECT_EGPIO; + + /* Older bootldrs put GPIO2-9 in alternate mode on the + assumption that they are used for video */ + GAFR &= ~H3100_DIRECT_EGPIO; + + H3100_EGPIO = h3100_egpio; + ipaq_model_ops = h3100_model_ops; +} + +MACHINE_START(H3100, "Compaq iPAQ H3100") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(h3100_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = h3xxx_mach_init, +MACHINE_END + +#endif /* CONFIG_SA1100_H3100 */ + +/************************* H3600 *************************/ + +#ifdef CONFIG_SA1100_H3600 + +#define H3600_EGPIO (*(volatile unsigned int *)H3600_EGPIO_VIRT) +static unsigned int h3600_egpio = EGPIO_H3600_RS232_ON; + +static void h3600_control_egpio(enum ipaq_egpio_type x, int setp) +{ + unsigned int egpio = 0; + unsigned long flags; + + switch (x) { + case IPAQ_EGPIO_LCD_POWER: + egpio |= EGPIO_H3600_LCD_ON | + EGPIO_H3600_LCD_PCI | + EGPIO_H3600_LCD_5V_ON | + EGPIO_H3600_LVDD_ON; + do_blank(setp); + break; + case IPAQ_EGPIO_LCD_ENABLE: + break; + case IPAQ_EGPIO_CODEC_NRESET: + egpio |= EGPIO_H3600_CODEC_NRESET; + break; + case IPAQ_EGPIO_AUDIO_ON: + egpio |= EGPIO_H3600_AUD_AMP_ON | + EGPIO_H3600_AUD_PWR_ON; + break; + case IPAQ_EGPIO_QMUTE: + egpio |= EGPIO_H3600_QMUTE; + break; + case IPAQ_EGPIO_OPT_NVRAM_ON: + egpio |= EGPIO_H3600_OPT_NVRAM_ON; + break; + case IPAQ_EGPIO_OPT_ON: + egpio |= EGPIO_H3600_OPT_ON; + break; + case IPAQ_EGPIO_CARD_RESET: + egpio |= EGPIO_H3600_CARD_RESET; + break; + case IPAQ_EGPIO_OPT_RESET: + egpio |= EGPIO_H3600_OPT_RESET; + break; + case IPAQ_EGPIO_IR_ON: + egpio |= EGPIO_H3600_IR_ON; + break; + case IPAQ_EGPIO_IR_FSEL: + egpio |= EGPIO_H3600_IR_FSEL; + break; + case IPAQ_EGPIO_RS232_ON: + egpio |= EGPIO_H3600_RS232_ON; + break; + case IPAQ_EGPIO_VPP_ON: + egpio |= EGPIO_H3600_VPP_ON; + break; + } + + if (egpio) { + local_irq_save(flags); + if (setp) + h3600_egpio |= egpio; + else + h3600_egpio &= ~egpio; + H3600_EGPIO = h3600_egpio; + local_irq_restore(flags); + } +} + +static unsigned long h3600_read_egpio(void) +{ + return h3600_egpio; +} + +static int h3600_pm_callback(int req) +{ + if (ipaq_model_ops.pm_callback_aux) + return ipaq_model_ops.pm_callback_aux(req); + return 0; +} + +static struct ipaq_model_ops h3600_model_ops __initdata = { + .generic_name = "3600", + .control = h3600_control_egpio, + .read = h3600_read_egpio, + .pm_callback = h3600_pm_callback +}; + +static void __init h3600_map_io(void) +{ + h3xxx_map_io(); + + /* Initialize h3600-specific values here */ + + GPCR = 0x0fffffff; /* All outputs are set low by default */ + GPDR = GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | + GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | + GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 | + GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | + GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; + + H3600_EGPIO = h3600_egpio; /* Maintains across sleep? */ + ipaq_model_ops = h3600_model_ops; +} + +MACHINE_START(H3600, "Compaq iPAQ H3600") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(h3600_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = h3xxx_mach_init, +MACHINE_END + +#endif /* CONFIG_SA1100_H3600 */ + +#ifdef CONFIG_SA1100_H3800 + +#define SET_ASIC1(x) \ + do {if (setp) { H3800_ASIC1_GPIO_OUT |= (x); } else { H3800_ASIC1_GPIO_OUT &= ~(x); }} while(0) + +#define SET_ASIC2(x) \ + do {if (setp) { H3800_ASIC2_GPIOPIOD |= (x); } else { H3800_ASIC2_GPIOPIOD &= ~(x); }} while(0) + +#define CLEAR_ASIC1(x) \ + do {if (setp) { H3800_ASIC1_GPIO_OUT &= ~(x); } else { H3800_ASIC1_GPIO_OUT |= (x); }} while(0) + +#define CLEAR_ASIC2(x) \ + do {if (setp) { H3800_ASIC2_GPIOPIOD &= ~(x); } else { H3800_ASIC2_GPIOPIOD |= (x); }} while(0) + + +/* + On screen enable, we get + + h3800_video_power_on(1) + LCD controller starts + h3800_video_lcd_enable(1) + + On screen disable, we get + + h3800_video_lcd_enable(0) + LCD controller stops + h3800_video_power_on(0) +*/ + + +static void h3800_video_power_on(int setp) +{ + if (setp) { + H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_ON; + msleep(30); + H3800_ASIC1_GPIO_OUT |= GPIO1_VGL_ON; + msleep(5); + H3800_ASIC1_GPIO_OUT |= GPIO1_VGH_ON; + msleep(50); + H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_5V_ON; + msleep(5); + } else { + msleep(5); + H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_5V_ON; + msleep(50); + H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGL_ON; + msleep(5); + H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGH_ON; + msleep(100); + H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_ON; + } +} + +static void h3800_video_lcd_enable(int setp) +{ + if (setp) { + msleep(17); // Wait one from before turning on + H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_PCI; + } else { + H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_PCI; + msleep(30); // Wait before turning off + } +} + + +static void h3800_control_egpio(enum ipaq_egpio_type x, int setp) +{ + switch (x) { + case IPAQ_EGPIO_LCD_POWER: + h3800_video_power_on(setp); + break; + case IPAQ_EGPIO_LCD_ENABLE: + h3800_video_lcd_enable(setp); + break; + case IPAQ_EGPIO_CODEC_NRESET: + case IPAQ_EGPIO_AUDIO_ON: + case IPAQ_EGPIO_QMUTE: + printk("%s: error - should not be called\n", __FUNCTION__); + break; + case IPAQ_EGPIO_OPT_NVRAM_ON: + SET_ASIC2(GPIO2_OPT_ON_NVRAM); + break; + case IPAQ_EGPIO_OPT_ON: + SET_ASIC2(GPIO2_OPT_ON); + break; + case IPAQ_EGPIO_CARD_RESET: + SET_ASIC2(GPIO2_OPT_PCM_RESET); + break; + case IPAQ_EGPIO_OPT_RESET: + SET_ASIC2(GPIO2_OPT_RESET); + break; + case IPAQ_EGPIO_IR_ON: + CLEAR_ASIC1(GPIO1_IR_ON_N); + break; + case IPAQ_EGPIO_IR_FSEL: + break; + case IPAQ_EGPIO_RS232_ON: + SET_ASIC1(GPIO1_RS232_ON); + break; + case IPAQ_EGPIO_VPP_ON: + H3800_ASIC2_FlashWP_VPP_ON = setp; + break; + } +} + +static unsigned long h3800_read_egpio(void) +{ + return H3800_ASIC1_GPIO_OUT | (H3800_ASIC2_GPIOPIOD << 16); +} + +/* We need to fix ASIC2 GPIO over suspend/resume. At the moment, + it doesn't appear that ASIC1 GPIO has the same problem */ + +static int h3800_pm_callback(int req) +{ + static u16 asic1_data; + static u16 asic2_data; + int result = 0; + + printk("%s %d\n", __FUNCTION__, req); + + switch (req) { + case PM_RESUME: + MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000; /* Set MSC2 correctly */ + + H3800_ASIC2_GPIOPIOD = asic2_data; + H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ + | GPIO2_SD_DETECT + | GPIO2_EAR_IN_N + | GPIO2_USB_DETECT_N + | GPIO2_SD_CON_SLT; + + H3800_ASIC1_GPIO_OUT = asic1_data; + + if (ipaq_model_ops.pm_callback_aux) + result = ipaq_model_ops.pm_callback_aux(req); + break; + + case PM_SUSPEND: + if (ipaq_model_ops.pm_callback_aux && + ((result = ipaq_model_ops.pm_callback_aux(req)) != 0)) + return result; + + asic1_data = H3800_ASIC1_GPIO_OUT; + asic2_data = H3800_ASIC2_GPIOPIOD; + break; + default: + printk("%s: unrecognized PM callback\n", __FUNCTION__); + break; + } + return result; +} + +static struct ipaq_model_ops h3800_model_ops __initdata = { + .generic_name = "3800", + .control = h3800_control_egpio, + .read = h3800_read_egpio, + .pm_callback = h3800_pm_callback +}; + +#define MAX_ASIC_ISR_LOOPS 20 + +/* The order of these is important - see #include <asm/arch/irqs.h> */ +static u32 kpio_irq_mask[] = { + KPIO_KEY_ALL, + KPIO_SPI_INT, + KPIO_OWM_INT, + KPIO_ADC_INT, + KPIO_UART_0_INT, + KPIO_UART_1_INT, + KPIO_TIMER_0_INT, + KPIO_TIMER_1_INT, + KPIO_TIMER_2_INT +}; + +static u32 gpio_irq_mask[] = { + GPIO2_PEN_IRQ, + GPIO2_SD_DETECT, + GPIO2_EAR_IN_N, + GPIO2_USB_DETECT_N, + GPIO2_SD_CON_SLT, +}; + +static void h3800_IRQ_demux(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + int i; + + if (0) printk("%s: interrupt received\n", __FUNCTION__); + + desc->chip->ack(irq); + + for (i = 0; i < MAX_ASIC_ISR_LOOPS && (GPLR & GPIO_H3800_ASIC); i++) { + u32 irq; + int j; + + /* KPIO */ + irq = H3800_ASIC2_KPIINTFLAG; + if (0) printk("%s KPIO 0x%08X\n", __FUNCTION__, irq); + for (j = 0; j < H3800_KPIO_IRQ_COUNT; j++) + if (irq & kpio_irq_mask[j]) + do_edge_IRQ(H3800_KPIO_IRQ_COUNT + j, irq_desc + H3800_KPIO_IRQ_COUNT + j, regs); + + /* GPIO2 */ + irq = H3800_ASIC2_GPIINTFLAG; + if (0) printk("%s GPIO 0x%08X\n", __FUNCTION__, irq); + for (j = 0; j < H3800_GPIO_IRQ_COUNT; j++) + if (irq & gpio_irq_mask[j]) + do_edge_IRQ(H3800_GPIO_IRQ_COUNT + j, irq_desc + H3800_GPIO_IRQ_COUNT + j , regs); + } + + if (i >= MAX_ASIC_ISR_LOOPS) + printk("%s: interrupt processing overrun\n", __FUNCTION__); + + /* For level-based interrupts */ + desc->chip->unmask(irq); + +} + +static struct irqaction h3800_irq = { + .name = "h3800_asic", + .handler = h3800_IRQ_demux, + .flags = SA_INTERRUPT, +}; + +u32 kpio_int_shadow = 0; + + +/* mask_ack <- IRQ is first serviced. + mask <- IRQ is disabled. + unmask <- IRQ is enabled + + The INTCLR registers are poorly documented. I believe that writing + a "1" to the register clears the specific interrupt, but the documentation + indicates writing a "0" clears the interrupt. In any case, they shouldn't + be read (that's the INTFLAG register) + */ + +static void h3800_mask_ack_kpio_irq(unsigned int irq) +{ + u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; + kpio_int_shadow &= ~mask; + H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; + H3800_ASIC2_KPIINTCLR = mask; +} + +static void h3800_mask_kpio_irq(unsigned int irq) +{ + u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; + kpio_int_shadow &= ~mask; + H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; +} + +static void h3800_unmask_kpio_irq(unsigned int irq) +{ + u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START]; + kpio_int_shadow |= mask; + H3800_ASIC2_KPIINTSTAT = kpio_int_shadow; +} + +static void h3800_mask_ack_gpio_irq(unsigned int irq) +{ + u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; + H3800_ASIC2_GPIINTSTAT &= ~mask; + H3800_ASIC2_GPIINTCLR = mask; +} + +static void h3800_mask_gpio_irq(unsigned int irq) +{ + u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; + H3800_ASIC2_GPIINTSTAT &= ~mask; + } + +static void h3800_unmask_gpio_irq(unsigned int irq) +{ + u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START]; + H3800_ASIC2_GPIINTSTAT |= mask; +} + +static void __init h3800_init_irq(void) +{ + int i; + + /* Initialize standard IRQs */ + sa1100_init_irq(); + + /* Disable all IRQs and set up clock */ + H3800_ASIC2_KPIINTSTAT = 0; /* Disable all interrupts */ + H3800_ASIC2_GPIINTSTAT = 0; + + H3800_ASIC2_KPIINTCLR = 0; /* Clear all KPIO interrupts */ + H3800_ASIC2_GPIINTCLR = 0; /* Clear all GPIO interrupts */ + +// H3800_ASIC2_KPIINTCLR = 0xffff; /* Clear all KPIO interrupts */ +// H3800_ASIC2_GPIINTCLR = 0xffff; /* Clear all GPIO interrupts */ + + H3800_ASIC2_CLOCK_Enable |= ASIC2_CLOCK_EX0; /* 32 kHZ crystal on */ + H3800_ASIC2_INTR_ClockPrescale |= ASIC2_INTCPS_SET; + H3800_ASIC2_INTR_ClockPrescale = ASIC2_INTCPS_CPS(0x0e) | ASIC2_INTCPS_SET; + H3800_ASIC2_INTR_TimerSet = 1; + +#if 0 + for (i = 0; i < H3800_KPIO_IRQ_COUNT; i++) { + int irq = i + H3800_KPIO_IRQ_START; + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + set_irq_chip(irq, &h3800_kpio_irqchip); + } + + for (i = 0; i < H3800_GPIO_IRQ_COUNT; i++) { + int irq = i + H3800_GPIO_IRQ_START; + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + set_irq_chip(irq, &h3800_gpio_irqchip); + } +#endif + set_irq_type(IRQ_GPIO_H3800_ASIC, IRQT_RISING); + set_irq_chained_handler(IRQ_GPIO_H3800_ASIC, &h3800_IRQ_demux); +} + + +#define ASIC1_OUTPUTS 0x7fff /* First 15 bits are used */ + +static void __init h3800_map_io(void) +{ + h3xxx_map_io(); + + /* Add wakeup on AC plug/unplug */ + PWER |= PWER_GPIO12; + + /* Initialize h3800-specific values here */ + GPCR = 0x0fffffff; /* All outputs are set low by default */ + GAFR = GPIO_H3800_CLK_OUT | + GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | + GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; + GPDR = GPIO_H3800_CLK_OUT | + GPIO_H3600_COM_RTS | GPIO_H3600_L3_CLOCK | + GPIO_H3600_L3_MODE | GPIO_H3600_L3_DATA | + GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | + GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; + TUCR = TUCR_3_6864MHz; /* Seems to be used only for the Bluetooth UART */ + + /* Fix the memory bus */ + MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000; + + /* Set up ASIC #1 */ + H3800_ASIC1_GPIO_DIR = ASIC1_OUTPUTS; /* All outputs */ + H3800_ASIC1_GPIO_MASK = ASIC1_OUTPUTS; /* No interrupts */ + H3800_ASIC1_GPIO_SLEEP_MASK = ASIC1_OUTPUTS; + H3800_ASIC1_GPIO_SLEEP_DIR = ASIC1_OUTPUTS; + H3800_ASIC1_GPIO_SLEEP_OUT = GPIO1_EAR_ON_N; + H3800_ASIC1_GPIO_BATT_FAULT_DIR = ASIC1_OUTPUTS; + H3800_ASIC1_GPIO_BATT_FAULT_OUT = GPIO1_EAR_ON_N; + + H3800_ASIC1_GPIO_OUT = GPIO1_IR_ON_N + | GPIO1_RS232_ON + | GPIO1_EAR_ON_N; + + /* Set up ASIC #2 */ + H3800_ASIC2_GPIOPIOD = GPIO2_IN_Y1_N | GPIO2_IN_X1_N; + H3800_ASIC2_GPOBFSTAT = GPIO2_IN_Y1_N | GPIO2_IN_X1_N; + + H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ + | GPIO2_SD_DETECT + | GPIO2_EAR_IN_N + | GPIO2_USB_DETECT_N + | GPIO2_SD_CON_SLT; + + /* TODO : Set sleep states & battery fault states */ + + /* Clear VPP Enable */ + H3800_ASIC2_FlashWP_VPP_ON = 0; + ipaq_model_ops = h3800_model_ops; +} + +MACHINE_START(H3800, "Compaq iPAQ H3800") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(h3800_map_io) + INITIRQ(h3800_init_irq) + .timer = &sa1100_timer, + .init_machine = h3xxx_mach_init, +MACHINE_END + +#endif /* CONFIG_SA1100_H3800 */ diff --git a/arch/arm/mach-sa1100/hackkit.c b/arch/arm/mach-sa1100/hackkit.c new file mode 100644 index 0000000..5708417 --- /dev/null +++ b/arch/arm/mach-sa1100/hackkit.c @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/mach-sa1100/hackkit.c + * + * Copyright (C) 2002 Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de> + * + * This file contains all HackKit tweaks. Based on original work from + * Nicolas Pitre's assabet fixes + * + * 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/kernel.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/cpufreq.h> +#include <linux/serial_core.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/setup.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/irq.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> +#include <asm/mach/serial_sa1100.h> + +#include "generic.h" + +/********************************************************************** + * prototypes + */ + +/* init funcs */ +static void __init hackkit_map_io(void); + +static u_int hackkit_get_mctrl(struct uart_port *port); +static void hackkit_set_mctrl(struct uart_port *port, u_int mctrl); +static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate); + +/********************************************************************** + * global data + */ + +/********************************************************************** + * static data + */ + +static struct map_desc hackkit_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xe8000000, 0x00000000, 0x01000000, MT_DEVICE } /* Flash bank 0 */ +}; + +static struct sa1100_port_fns hackkit_port_fns __initdata = { + .set_mctrl = hackkit_set_mctrl, + .get_mctrl = hackkit_get_mctrl, + .pm = hackkit_uart_pm, +}; + +/********************************************************************** + * Static functions + */ + +static void __init hackkit_map_io(void) +{ + sa1100_map_io(); + iotable_init(hackkit_io_desc, ARRAY_SIZE(hackkit_io_desc)); + + sa1100_register_uart_fns(&hackkit_port_fns); + sa1100_register_uart(0, 1); /* com port */ + sa1100_register_uart(1, 2); + sa1100_register_uart(2, 3); /* radio module */ + + Ser1SDCR0 |= SDCR0_SUS; +} + +/** + * hackkit_uart_pm - powermgmt callback function for system 3 UART + * @port: uart port structure + * @state: pm state + * @oldstate: old pm state + * + */ +static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + /* TODO: switch on/off uart in powersave mode */ +} + +/* + * Note! this can be called from IRQ context. + * FIXME: No modem ctrl lines yet. + */ +static void hackkit_set_mctrl(struct uart_port *port, u_int mctrl) +{ +#if 0 + if (port->mapbase == _Ser1UTCR0) { + u_int set = 0, clear = 0; + + if (mctrl & TIOCM_RTS) + set |= PT_CTRL2_RS1_RTS; + else + clear |= PT_CTRL2_RS1_RTS; + + if (mctrl & TIOCM_DTR) + set |= PT_CTRL2_RS1_DTR; + else + clear |= PT_CTRL2_RS1_DTR; + + PTCTRL2_clear(clear); + PTCTRL2_set(set); + } +#endif +} + +static u_int hackkit_get_mctrl(struct uart_port *port) +{ + u_int ret = 0; +#if 0 + u_int irqsr = PT_IRQSR; + + /* need 2 reads to read current value */ + irqsr = PT_IRQSR; + + /* TODO: check IRQ source register for modem/com + status lines and set them correctly. */ +#endif + + ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; + + return ret; +} + +static struct mtd_partition hackkit_partitions[] = { + { + .name = "BLOB", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "config", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "initrd", + .size = 0x00180000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "rootfs", + .size = 0x700000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "data", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct flash_platform_data hackkit_flash_data = { + .map_name = "cfi_probe", + .parts = hackkit_partitions, + .nr_parts = ARRAY_SIZE(hackkit_partitions), +}; + +static struct resource hackkit_flash_resource = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_32M, + .flags = IORESOURCE_MEM, +}; + +static void __init hackkit_init(void) +{ + sa11x0_set_flash_data(&hackkit_flash_data, &hackkit_flash_resource, 1); +} + +/********************************************************************** + * Exported Functions + */ + +MACHINE_START(HACKKIT, "HackKit Cpu Board") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(hackkit_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = hackkit_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/irq.c b/arch/arm/mach-sa1100/irq.c new file mode 100644 index 0000000..66a929c --- /dev/null +++ b/arch/arm/mach-sa1100/irq.c @@ -0,0 +1,332 @@ +/* + * linux/arch/arm/mach-sa1100/irq.c + * + * Copyright (C) 1999-2001 Nicolas Pitre + * + * Generic IRQ handling for the SA11x0, GPIO 11-27 IRQ demultiplexing. + * + * 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/ioport.h> +#include <linux/ptrace.h> +#include <linux/sysdev.h> + +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/mach/irq.h> + +#include "generic.h" + + +/* + * SA1100 GPIO edge detection for IRQs: + * IRQs are generated on Falling-Edge, Rising-Edge, or both. + * Use this instead of directly setting GRER/GFER. + */ +static int GPIO_IRQ_rising_edge; +static int GPIO_IRQ_falling_edge; +static int GPIO_IRQ_mask = (1 << 11) - 1; + +/* + * To get the GPIO number from an IRQ number + */ +#define GPIO_11_27_IRQ(i) ((i) - 21) +#define GPIO11_27_MASK(irq) (1 << GPIO_11_27_IRQ(irq)) + +static int sa1100_gpio_type(unsigned int irq, unsigned int type) +{ + unsigned int mask; + + if (irq <= 10) + mask = 1 << irq; + else + mask = GPIO11_27_MASK(irq); + + if (type == IRQT_PROBE) { + if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask) + return 0; + type = __IRQT_RISEDGE | __IRQT_FALEDGE; + } + + if (type & __IRQT_RISEDGE) { + GPIO_IRQ_rising_edge |= mask; + } else + GPIO_IRQ_rising_edge &= ~mask; + if (type & __IRQT_FALEDGE) { + GPIO_IRQ_falling_edge |= mask; + } else + GPIO_IRQ_falling_edge &= ~mask; + + GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; + GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; + + return 0; +} + +/* + * GPIO IRQs must be acknowledged. This is for IRQs from 0 to 10. + */ +static void sa1100_low_gpio_ack(unsigned int irq) +{ + GEDR = (1 << irq); +} + +static void sa1100_low_gpio_mask(unsigned int irq) +{ + ICMR &= ~(1 << irq); +} + +static void sa1100_low_gpio_unmask(unsigned int irq) +{ + ICMR |= 1 << irq; +} + +static int sa1100_low_gpio_wake(unsigned int irq, unsigned int on) +{ + if (on) + PWER |= 1 << irq; + else + PWER &= ~(1 << irq); + return 0; +} + +static struct irqchip sa1100_low_gpio_chip = { + .ack = sa1100_low_gpio_ack, + .mask = sa1100_low_gpio_mask, + .unmask = sa1100_low_gpio_unmask, + .type = sa1100_gpio_type, + .wake = sa1100_low_gpio_wake, +}; + +/* + * IRQ11 (GPIO11 through 27) handler. We enter here with the + * irq_controller_lock held, and IRQs disabled. Decode the IRQ + * and call the handler. + */ +static void +sa1100_high_gpio_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + unsigned int mask; + + mask = GEDR & 0xfffff800; + do { + /* + * clear down all currently active IRQ sources. + * We will be processing them all. + */ + GEDR = mask; + + irq = IRQ_GPIO11; + desc = irq_desc + irq; + mask >>= 11; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + mask >>= 1; + irq++; + desc++; + } while (mask); + + mask = GEDR & 0xfffff800; + } while (mask); +} + +/* + * Like GPIO0 to 10, GPIO11-27 IRQs need to be handled specially. + * In addition, the IRQs are all collected up into one bit in the + * interrupt controller registers. + */ +static void sa1100_high_gpio_ack(unsigned int irq) +{ + unsigned int mask = GPIO11_27_MASK(irq); + + GEDR = mask; +} + +static void sa1100_high_gpio_mask(unsigned int irq) +{ + unsigned int mask = GPIO11_27_MASK(irq); + + GPIO_IRQ_mask &= ~mask; + + GRER &= ~mask; + GFER &= ~mask; +} + +static void sa1100_high_gpio_unmask(unsigned int irq) +{ + unsigned int mask = GPIO11_27_MASK(irq); + + GPIO_IRQ_mask |= mask; + + GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; + GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; +} + +static int sa1100_high_gpio_wake(unsigned int irq, unsigned int on) +{ + if (on) + PWER |= GPIO11_27_MASK(irq); + else + PWER &= ~GPIO11_27_MASK(irq); + return 0; +} + +static struct irqchip sa1100_high_gpio_chip = { + .ack = sa1100_high_gpio_ack, + .mask = sa1100_high_gpio_mask, + .unmask = sa1100_high_gpio_unmask, + .type = sa1100_gpio_type, + .wake = sa1100_high_gpio_wake, +}; + +/* + * We don't need to ACK IRQs on the SA1100 unless they're GPIOs + * this is for internal IRQs i.e. from 11 to 31. + */ +static void sa1100_mask_irq(unsigned int irq) +{ + ICMR &= ~(1 << irq); +} + +static void sa1100_unmask_irq(unsigned int irq) +{ + ICMR |= (1 << irq); +} + +static struct irqchip sa1100_normal_chip = { + .ack = sa1100_mask_irq, + .mask = sa1100_mask_irq, + .unmask = sa1100_unmask_irq, +}; + +static struct resource irq_resource = { + .name = "irqs", + .start = 0x90050000, + .end = 0x9005ffff, +}; + +static struct sa1100irq_state { + unsigned int saved; + unsigned int icmr; + unsigned int iclr; + unsigned int iccr; +} sa1100irq_state; + +static int sa1100irq_suspend(struct sys_device *dev, pm_message_t state) +{ + struct sa1100irq_state *st = &sa1100irq_state; + + st->saved = 1; + st->icmr = ICMR; + st->iclr = ICLR; + st->iccr = ICCR; + + /* + * Disable all GPIO-based interrupts. + */ + ICMR &= ~(IC_GPIO11_27|IC_GPIO10|IC_GPIO9|IC_GPIO8|IC_GPIO7| + IC_GPIO6|IC_GPIO5|IC_GPIO4|IC_GPIO3|IC_GPIO2| + IC_GPIO1|IC_GPIO0); + + /* + * Set the appropriate edges for wakeup. + */ + GRER = PWER & GPIO_IRQ_rising_edge; + GFER = PWER & GPIO_IRQ_falling_edge; + + /* + * Clear any pending GPIO interrupts. + */ + GEDR = GEDR; + + return 0; +} + +static int sa1100irq_resume(struct sys_device *dev) +{ + struct sa1100irq_state *st = &sa1100irq_state; + + if (st->saved) { + ICCR = st->iccr; + ICLR = st->iclr; + + GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask; + GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask; + + ICMR = st->icmr; + } + return 0; +} + +static struct sysdev_class sa1100irq_sysclass = { + set_kset_name("sa11x0-irq"), + .suspend = sa1100irq_suspend, + .resume = sa1100irq_resume, +}; + +static struct sys_device sa1100irq_device = { + .id = 0, + .cls = &sa1100irq_sysclass, +}; + +static int __init sa1100irq_init_devicefs(void) +{ + sysdev_class_register(&sa1100irq_sysclass); + return sysdev_register(&sa1100irq_device); +} + +device_initcall(sa1100irq_init_devicefs); + +void __init sa1100_init_irq(void) +{ + unsigned int irq; + + request_resource(&iomem_resource, &irq_resource); + + /* disable all IRQs */ + ICMR = 0; + + /* all IRQs are IRQ, not FIQ */ + ICLR = 0; + + /* clear all GPIO edge detects */ + GFER = 0; + GRER = 0; + GEDR = -1; + + /* + * Whatever the doc says, this has to be set for the wait-on-irq + * instruction to work... on a SA1100 rev 9 at least. + */ + ICCR = 1; + + for (irq = 0; irq <= 10; irq++) { + set_irq_chip(irq, &sa1100_low_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + for (irq = 12; irq <= 31; irq++) { + set_irq_chip(irq, &sa1100_normal_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + + for (irq = 32; irq <= 48; irq++) { + set_irq_chip(irq, &sa1100_high_gpio_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + /* + * Install handler for GPIO 11-27 edge detect interrupts + */ + set_irq_chip(IRQ_GPIO11_27, &sa1100_normal_chip); + set_irq_chained_handler(IRQ_GPIO11_27, sa1100_high_gpio_handler); +} diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c new file mode 100644 index 0000000..6be7829 --- /dev/null +++ b/arch/arm/mach-sa1100/jornada720.c @@ -0,0 +1,105 @@ +/* + * linux/arch/arm/mach-sa1100/jornada720.c + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/ioport.h> + +#include <asm/hardware.h> +#include <asm/hardware/sa1111.h> +#include <asm/irq.h> +#include <asm/mach-types.h> +#include <asm/setup.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> + +#include "generic.h" + + +#define JORTUCR_VAL 0x20000400 + +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO1, + .end = IRQ_GPIO1, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .dma_mask = &sa1111_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + +static int __init jornada720_init(void) +{ + int ret = -ENODEV; + + if (machine_is_jornada720()) { + GPDR |= GPIO_GPIO20; + TUCR = JORTUCR_VAL; /* set the oscillator out to the SA-1101 */ + + GPSR = GPIO_GPIO20; + udelay(1); + GPCR = GPIO_GPIO20; + udelay(1); + GPSR = GPIO_GPIO20; + udelay(20); + + /* LDD4 is speaker, LDD3 is microphone */ + PPSR &= ~(PPC_LDD3 | PPC_LDD4); + PPDR |= PPC_LDD3 | PPC_LDD4; + + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + } + return ret; +} + +arch_initcall(jornada720_init); + +static struct map_desc jornada720_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf0000000, 0x48000000, 0x00100000, MT_DEVICE }, /* Epson registers */ + { 0xf1000000, 0x48200000, 0x00100000, MT_DEVICE }, /* Epson frame buffer */ + { 0xf4000000, 0x40000000, 0x00100000, MT_DEVICE } /* SA-1111 */ +}; + +static void __init jornada720_map_io(void) +{ + sa1100_map_io(); + iotable_init(jornada720_io_desc, ARRAY_SIZE(jornada720_io_desc)); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); +} + +MACHINE_START(JORNADA720, "HP Jornada 720") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(jornada720_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, +MACHINE_END diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c new file mode 100644 index 0000000..51c08cc --- /dev/null +++ b/arch/arm/mach-sa1100/lart.c @@ -0,0 +1,49 @@ +/* + * linux/arch/arm/mach-sa1100/lart.c + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> + +#include <asm/hardware.h> +#include <asm/setup.h> +#include <asm/mach-types.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> + +#include "generic.h" + + +#warning "include/asm/arch-sa1100/ide.h needs fixing for lart" + +static struct map_desc lart_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xe8000000, 0x00000000, 0x00400000, MT_DEVICE }, /* main flash memory */ + { 0xec000000, 0x08000000, 0x00400000, MT_DEVICE } /* main flash, alternative location */ +}; + +static void __init lart_map_io(void) +{ + sa1100_map_io(); + iotable_init(lart_io_desc, ARRAY_SIZE(lart_io_desc)); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + sa1100_register_uart(2, 2); + + GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD); + GPDR |= GPIO_UART_TXD; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; +} + +MACHINE_START(LART, "LART") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(lart_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, +MACHINE_END diff --git a/arch/arm/mach-sa1100/leds-assabet.c b/arch/arm/mach-sa1100/leds-assabet.c new file mode 100644 index 0000000..e9aa9df --- /dev/null +++ b/arch/arm/mach-sa1100/leds-assabet.c @@ -0,0 +1,115 @@ +/* + * linux/arch/arm/mach-sa1100/leds-assabet.c + * + * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu> + * + * Original (leds-footbridge.c) by Russell King + * + * Assabet uses the LEDs as follows: + * - Green - toggles state every 50 timer interrupts + * - Red - on if system is not idle + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> +#include <asm/arch/assabet.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; + +#define ASSABET_BCR_LED_MASK (ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED) + +void assabet_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN; + ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state); + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= ASSABET_BCR_LED_GREEN; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= ASSABET_BCR_LED_RED; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~ASSABET_BCR_LED_RED; + break; +#endif + + case led_halted: + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~ASSABET_BCR_LED_GREEN; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= ASSABET_BCR_LED_GREEN; + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~ASSABET_BCR_LED_RED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= ASSABET_BCR_LED_RED; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state); + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-sa1100/leds-badge4.c b/arch/arm/mach-sa1100/leds-badge4.c new file mode 100644 index 0000000..0a8f87b --- /dev/null +++ b/arch/arm/mach-sa1100/leds-badge4.c @@ -0,0 +1,112 @@ +/* + * linux/arch/arm/mach-sa1100/leds-badge4.c + * + * Author: Christopher Hoover <ch@hpl.hp.com> + * Copyright (C) 2002 Hewlett-Packard Company + * + * 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 "leds.h" + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +#define LED_RED GPIO_GPIO(7) +#define LED_GREEN GPIO_GPIO(9) +#define LED_MASK (LED_RED|LED_GREEN) + +#define LED_IDLE LED_GREEN +#define LED_TIMER LED_RED + +void badge4_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + GPDR |= LED_MASK; + hw_led_state = LED_MASK; + 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 = LED_MASK; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_TIMER; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + /* LED off when system is idle */ + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_IDLE; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_IDLE; + break; +#endif + + case led_red_on: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_RED; + break; + + case led_red_off: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_RED; + break; + + case led_green_on: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_GREEN; + break; + + case led_green_off: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_GREEN; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) { + GPSR = hw_led_state; + GPCR = hw_led_state ^ LED_MASK; + } + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-sa1100/leds-cerf.c b/arch/arm/mach-sa1100/leds-cerf.c new file mode 100644 index 0000000..f6635a2 --- /dev/null +++ b/arch/arm/mach-sa1100/leds-cerf.c @@ -0,0 +1,111 @@ +/* + * linux/arch/arm/mach-sa1100/leds-cerf.c + * + * Author: ??? + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.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; + +#define LED_D0 GPIO_GPIO(0) +#define LED_D1 GPIO_GPIO(1) +#define LED_D2 GPIO_GPIO(2) +#define LED_D3 GPIO_GPIO(3) +#define LED_MASK (LED_D0|LED_D1|LED_D2|LED_D3) + +void cerf_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = LED_MASK; + 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 = LED_MASK; + break; + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_D0; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_D1; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_D1; + break; +#endif + case led_green_on: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_D2; + break; + + case led_green_off: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_D2; + break; + + case led_amber_on: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_D3; + break; + + case led_amber_off: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_D3; + break; + + case led_red_on: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_D1; + break; + + case led_red_off: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_D1; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) { + GPSR = hw_led_state; + GPCR = hw_led_state ^ LED_MASK; + } + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-sa1100/leds-hackkit.c b/arch/arm/mach-sa1100/leds-hackkit.c new file mode 100644 index 0000000..2e5fa14 --- /dev/null +++ b/arch/arm/mach-sa1100/leds-hackkit.c @@ -0,0 +1,113 @@ +/* + * linux/arch/arm/mach-sa1100/leds-hackkit.c + * + * based on leds-lart.c + * + * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000 + * (C) Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>, 2002 + * + * The HackKit has two leds (GPIO 22/23). The red led (gpio 22) is used + * as cpu led, the green one is used as timer led. + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.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; + +#define LED_GREEN GPIO_GPIO23 +#define LED_RED GPIO_GPIO22 +#define LED_MASK (LED_RED | LED_GREEN) + +void hackkit_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch(evt) { + case led_start: + /* pin 22/23 are outputs */ + GPDR |= LED_MASK; + hw_led_state = LED_MASK; + 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 = LED_MASK; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_GREEN; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + /* The LART people like the LED to be off when the + system is idle... */ + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_RED; + break; + + case led_idle_end: + /* ... and on if the system is not idle */ + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_RED; + break; +#endif + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_RED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_RED; + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_GREEN; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_GREEN; + break; + + default: + break; + } + + /* Now set the GPIO state, or nothing will happen at all */ + if (led_state & LED_STATE_ENABLED) { + GPSR = hw_led_state; + GPCR = hw_led_state ^ LED_MASK; + } + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-sa1100/leds-lart.c b/arch/arm/mach-sa1100/leds-lart.c new file mode 100644 index 0000000..1875014 --- /dev/null +++ b/arch/arm/mach-sa1100/leds-lart.c @@ -0,0 +1,102 @@ +/* + * linux/arch/arm/mach-sa1100/leds-lart.c + * + * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000 + * + * LART uses the LED as follows: + * - GPIO23 is the LED, on if system is not idle + * You can use both CONFIG_LEDS_CPU and CONFIG_LEDS_TIMER at the same + * time, but in that case the timer events will still dictate the + * pace of the LED. + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.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; + +#define LED_23 GPIO_GPIO23 +#define LED_MASK (LED_23) + +void lart_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch(evt) { + case led_start: + /* pin 23 is output pin */ + GPDR |= LED_23; + hw_led_state = LED_MASK; + 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 = LED_MASK; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_MASK; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_23; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + /* The LART people like the LED to be off when the + system is idle... */ + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_23; + break; + + case led_idle_end: + /* ... and on if the system is not idle */ + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_23; + break; +#endif + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_23; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_23; + break; + + default: + break; + } + + /* Now set the GPIO state, or nothing will happen at all */ + if (led_state & LED_STATE_ENABLED) { + GPSR = hw_led_state; + GPCR = hw_led_state ^ LED_MASK; + } + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-sa1100/leds-simpad.c b/arch/arm/mach-sa1100/leds-simpad.c new file mode 100644 index 0000000..6a27a2d --- /dev/null +++ b/arch/arm/mach-sa1100/leds-simpad.c @@ -0,0 +1,101 @@ +/* + * linux/arch/arm/mach-sa1100/leds-simpad.c + * + * Author: Juergen Messerer <juergen.messerer@siemens.ch> + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/hardware.h> +#include <asm/leds.h> +#include <asm/system.h> +#include <asm/arch/simpad.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; + +#define LED_GREEN (1) +#define LED_MASK (1) + +extern void set_cs3_bit(int value); +extern void clear_cs3_bit(int value); + +void simpad_leds_event(led_event_t evt) +{ + switch (evt) + { + case led_start: + hw_led_state = LED_GREEN; + 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 = LED_GREEN; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = LED_GREEN; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= LED_GREEN; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + break; + + case led_idle_end: + break; +#endif + + case led_halted: + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_GREEN; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_GREEN; + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + break; + + case led_red_off: + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + set_cs3_bit(LED2_ON); + else + clear_cs3_bit(LED2_ON); +} + diff --git a/arch/arm/mach-sa1100/leds.c b/arch/arm/mach-sa1100/leds.c new file mode 100644 index 0000000..4cf7c56 --- /dev/null +++ b/arch/arm/mach-sa1100/leds.c @@ -0,0 +1,52 @@ +/* + * linux/arch/arm/mach-sa1100/leds.c + * + * SA1100 LEDs dispatcher + * + * Copyright (C) 2001 Nicolas Pitre + */ +#include <linux/compiler.h> +#include <linux/init.h> + +#include <asm/leds.h> +#include <asm/mach-types.h> + +#include "leds.h" + +static int __init +sa1100_leds_init(void) +{ + if (machine_is_assabet()) + leds_event = assabet_leds_event; + if (machine_is_consus()) + leds_event = consus_leds_event; + if (machine_is_badge4()) + leds_event = badge4_leds_event; + if (machine_is_brutus()) + leds_event = brutus_leds_event; + if (machine_is_cerf()) + leds_event = cerf_leds_event; + if (machine_is_flexanet()) + leds_event = flexanet_leds_event; + if (machine_is_graphicsclient()) + leds_event = graphicsclient_leds_event; + if (machine_is_hackkit()) + leds_event = hackkit_leds_event; + if (machine_is_lart()) + leds_event = lart_leds_event; + if (machine_is_pfs168()) + leds_event = pfs168_leds_event; + if (machine_is_graphicsmaster()) + leds_event = graphicsmaster_leds_event; + if (machine_is_adsbitsy()) + leds_event = adsbitsy_leds_event; + if (machine_is_pt_system3()) + leds_event = system3_leds_event; + if (machine_is_simpad()) + leds_event = simpad_leds_event; /* what about machine registry? including led, apm... -zecke */ + + leds_event(led_start); + return 0; +} + +core_initcall(sa1100_leds_init); diff --git a/arch/arm/mach-sa1100/leds.h b/arch/arm/mach-sa1100/leds.h new file mode 100644 index 0000000..68cc9f7 --- /dev/null +++ b/arch/arm/mach-sa1100/leds.h @@ -0,0 +1,14 @@ +extern void assabet_leds_event(led_event_t evt); +extern void badge4_leds_event(led_event_t evt); +extern void consus_leds_event(led_event_t evt); +extern void brutus_leds_event(led_event_t evt); +extern void cerf_leds_event(led_event_t evt); +extern void flexanet_leds_event(led_event_t evt); +extern void graphicsclient_leds_event(led_event_t evt); +extern void hackkit_leds_event(led_event_t evt); +extern void lart_leds_event(led_event_t evt); +extern void pfs168_leds_event(led_event_t evt); +extern void graphicsmaster_leds_event(led_event_t evt); +extern void adsbitsy_leds_event(led_event_t evt); +extern void system3_leds_event(led_event_t evt); +extern void simpad_leds_event(led_event_t evt); diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c new file mode 100644 index 0000000..1405383 --- /dev/null +++ b/arch/arm/mach-sa1100/neponset.c @@ -0,0 +1,342 @@ +/* + * linux/arch/arm/mach-sa1100/neponset.c + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ptrace.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/serial_core.h> +#include <linux/device.h> +#include <linux/slab.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/irq.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> +#include <asm/mach/serial_sa1100.h> +#include <asm/arch/assabet.h> +#include <asm/arch/neponset.h> +#include <asm/hardware/sa1111.h> +#include <asm/sizes.h> + +/* + * Install handler for Neponset IRQ. Note that we have to loop here + * since the ETHERNET and USAR IRQs are level based, and we need to + * ensure that the IRQ signal is deasserted before returning. This + * is rather unfortunate. + */ +static void +neponset_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + unsigned int irr; + + while (1) { + struct irqdesc *d; + + /* + * Acknowledge the parent IRQ. + */ + desc->chip->ack(irq); + + /* + * Read the interrupt reason register. Let's have all + * active IRQ bits high. Note: there is a typo in the + * Neponset user's guide for the SA1111 IRR level. + */ + irr = IRR ^ (IRR_ETHERNET | IRR_USAR); + + if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) + break; + + /* + * Since there is no individual mask, we have to + * mask the parent IRQ. This is safe, since we'll + * recheck the register for any pending IRQs. + */ + if (irr & (IRR_ETHERNET | IRR_USAR)) { + desc->chip->mask(irq); + + if (irr & IRR_ETHERNET) { + d = irq_desc + IRQ_NEPONSET_SMC9196; + d->handle(IRQ_NEPONSET_SMC9196, d, regs); + } + + if (irr & IRR_USAR) { + d = irq_desc + IRQ_NEPONSET_USAR; + d->handle(IRQ_NEPONSET_USAR, d, regs); + } + + desc->chip->unmask(irq); + } + + if (irr & IRR_SA1111) { + d = irq_desc + IRQ_NEPONSET_SA1111; + d->handle(IRQ_NEPONSET_SA1111, d, regs); + } + } +} + +static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) +{ + u_int mdm_ctl0 = MDM_CTL_0; + + if (port->mapbase == _Ser1UTCR0) { + if (mctrl & TIOCM_RTS) + mdm_ctl0 &= ~MDM_CTL0_RTS2; + else + mdm_ctl0 |= MDM_CTL0_RTS2; + + if (mctrl & TIOCM_DTR) + mdm_ctl0 &= ~MDM_CTL0_DTR2; + else + mdm_ctl0 |= MDM_CTL0_DTR2; + } else if (port->mapbase == _Ser3UTCR0) { + if (mctrl & TIOCM_RTS) + mdm_ctl0 &= ~MDM_CTL0_RTS1; + else + mdm_ctl0 |= MDM_CTL0_RTS1; + + if (mctrl & TIOCM_DTR) + mdm_ctl0 &= ~MDM_CTL0_DTR1; + else + mdm_ctl0 |= MDM_CTL0_DTR1; + } + + MDM_CTL_0 = mdm_ctl0; +} + +static u_int neponset_get_mctrl(struct uart_port *port) +{ + u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; + u_int mdm_ctl1 = MDM_CTL_1; + + if (port->mapbase == _Ser1UTCR0) { + if (mdm_ctl1 & MDM_CTL1_DCD2) + ret &= ~TIOCM_CD; + if (mdm_ctl1 & MDM_CTL1_CTS2) + ret &= ~TIOCM_CTS; + if (mdm_ctl1 & MDM_CTL1_DSR2) + ret &= ~TIOCM_DSR; + } else if (port->mapbase == _Ser3UTCR0) { + if (mdm_ctl1 & MDM_CTL1_DCD1) + ret &= ~TIOCM_CD; + if (mdm_ctl1 & MDM_CTL1_CTS1) + ret &= ~TIOCM_CTS; + if (mdm_ctl1 & MDM_CTL1_DSR1) + ret &= ~TIOCM_DSR; + } + + return ret; +} + +static struct sa1100_port_fns neponset_port_fns __initdata = { + .set_mctrl = neponset_set_mctrl, + .get_mctrl = neponset_get_mctrl, +}; + +static int neponset_probe(struct device *dev) +{ + sa1100_register_uart_fns(&neponset_port_fns); + + /* + * Install handler for GPIO25. + */ + set_irq_type(IRQ_GPIO25, IRQT_RISING); + set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler); + + /* + * We would set IRQ_GPIO25 to be a wake-up IRQ, but + * unfortunately something on the Neponset activates + * this IRQ on sleep (ethernet?) + */ +#if 0 + enable_irq_wake(IRQ_GPIO25); +#endif + + /* + * Setup other Neponset IRQs. SA1111 will be done by the + * generic SA1111 code. + */ + set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); + set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); + + /* + * Disable GPIO 0/1 drivers so the buttons work on the module. + */ + NCR_0 = NCR_GP01_OFF; + + return 0; +} + +#ifdef CONFIG_PM + +/* + * LDM power management. + */ +static int neponset_suspend(struct device *dev, pm_message_t state, u32 level) +{ + /* + * Save state. + */ + if (level == SUSPEND_SAVE_STATE || + level == SUSPEND_DISABLE || + level == SUSPEND_POWER_DOWN) { + if (!dev->power.saved_state) + dev->power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!dev->power.saved_state) + return -ENOMEM; + + *(unsigned int *)dev->power.saved_state = NCR_0; + } + + return 0; +} + +static int neponset_resume(struct device *dev, u32 level) +{ + if (level == RESUME_RESTORE_STATE || level == RESUME_ENABLE) { + if (dev->power.saved_state) { + NCR_0 = *(unsigned int *)dev->power.saved_state; + kfree(dev->power.saved_state); + dev->power.saved_state = NULL; + } + } + + return 0; +} + +#else +#define neponset_suspend NULL +#define neponset_resume NULL +#endif + +static struct device_driver neponset_device_driver = { + .name = "neponset", + .bus = &platform_bus_type, + .probe = neponset_probe, + .suspend = neponset_suspend, + .resume = neponset_resume, +}; + +static struct resource neponset_resources[] = { + [0] = { + .start = 0x10000000, + .end = 0x17ffffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device neponset_device = { + .name = "neponset", + .id = 0, + .num_resources = ARRAY_SIZE(neponset_resources), + .resource = neponset_resources, +}; + +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_NEPONSET_SA1111, + .end = IRQ_NEPONSET_SA1111, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .dma_mask = &sa1111_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .name = "smc91x-regs", + .start = SA1100_CS3_PHYS, + .end = SA1100_CS3_PHYS + 0x01ffffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_NEPONSET_SMC9196, + .end = IRQ_NEPONSET_SMC9196, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .name = "smc91x-attrib", + .start = SA1100_CS3_PHYS + 0x02000000, + .end = SA1100_CS3_PHYS + 0x03ffffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &neponset_device, + &sa1111_device, + &smc91x_device, +}; + +static int __init neponset_init(void) +{ + driver_register(&neponset_device_driver); + + /* + * The Neponset is only present on the Assabet machine type. + */ + if (!machine_is_assabet()) + return -ENODEV; + + /* + * Ensure that the memory bus request/grant signals are setup, + * and the grant is held in its inactive state, whether or not + * we actually have a Neponset attached. + */ + sa1110_mb_disable(); + + if (!machine_has_neponset()) { + printk(KERN_DEBUG "Neponset expansion board not present\n"); + return -ENODEV; + } + + if (WHOAMI != 0x11) { + printk(KERN_WARNING "Neponset board detected, but " + "wrong ID: %02x\n", WHOAMI); + return -ENODEV; + } + + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(neponset_init); + +static struct map_desc neponset_io_desc[] __initdata = { + /* virtual physical length type */ + { 0xf3000000, 0x10000000, SZ_1M, MT_DEVICE }, /* System Registers */ + { 0xf4000000, 0x40000000, SZ_1M, MT_DEVICE } /* SA-1111 */ +}; + +void __init neponset_map_io(void) +{ + iotable_init(neponset_io_desc, ARRAY_SIZE(neponset_io_desc)); +} diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c new file mode 100644 index 0000000..5606bd7 --- /dev/null +++ b/arch/arm/mach-sa1100/pleb.c @@ -0,0 +1,154 @@ +/* + * linux/arch/arm/mach-sa1100/pleb.c + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/device.h> + +#include <linux/mtd/partitions.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/mach-types.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> +#include <asm/mach/serial_sa1100.h> +#include <asm/arch/irqs.h> + +#include "generic.h" + + +/* + * Ethernet IRQ mappings + */ + +#define PLEB_ETH0_P (0x20000300) /* Ethernet 0 in PCMCIA0 IO */ +#define PLEB_ETH0_V (0xf6000300) + +#define GPIO_ETH0_IRQ GPIO_GPIO(21) +#define GPIO_ETH0_EN GPIO_GPIO(26) + +#define IRQ_GPIO_ETH0_IRQ IRQ_GPIO21 + +static struct resource smc91x_resources[] = { + [0] = { + .start = PLEB_ETH0_P, + .end = PLEB_ETH0_P | 0x03ffffff, + .flags = IORESOURCE_MEM, + }, +#if 0 /* Autoprobe instead, to get rising/falling edge characteristic right */ + [1] = { + .start = IRQ_GPIO_ETH0_IRQ, + .end = IRQ_GPIO_ETH0_IRQ, + .flags = IORESOURCE_IRQ, + }, +#endif +}; + + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &smc91x_device, +}; + + +/* + * Pleb's memory map + * has flash memory (typically 4 or 8 meg) selected by + * the two SA1100 lowest chip select outputs. + */ +static struct resource pleb_flash_resources[] = { + [0] = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_8M - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SA1100_CS1_PHYS, + .end = SA1100_CS1_PHYS + SZ_8M - 1, + .flags = IORESOURCE_MEM, + } +}; + + +static struct mtd_partition pleb_partitions[] = { + { + .name = "blob", + .offset = 0, + .size = 0x00020000, + }, { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0x000e0000, + }, { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = 0x00300000, + } +}; + + +static struct flash_platform_data pleb_flash_data = { + .map_name = "cfi_probe", + .parts = pleb_partitions, + .nr_parts = ARRAY_SIZE(pleb_partitions), +}; + + +static void __init pleb_init(void) +{ + sa11x0_set_flash_data(&pleb_flash_data, pleb_flash_resources, + ARRAY_SIZE(pleb_flash_resources)); + + + platform_add_devices(devices, ARRAY_SIZE(devices)); +} + + +static void __init pleb_map_io(void) +{ + sa1100_map_io(); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + + GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD); + GPDR |= GPIO_UART_TXD; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; + + /* + * Fix expansion memory timing for network card + */ + MECR = ((2<<10) | (2<<5) | (2<<0)); + + /* + * Enable the SMC ethernet controller + */ + GPDR |= GPIO_ETH0_EN; /* set to output */ + GPCR = GPIO_ETH0_EN; /* clear MCLK (enable smc) */ + + GPDR &= ~GPIO_ETH0_IRQ; + + set_irq_type(GPIO_ETH0_IRQ, IRQT_FALLING); +} + +MACHINE_START(PLEB, "PLEB") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + MAPIO(pleb_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = pleb_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c new file mode 100644 index 0000000..379ea5e --- /dev/null +++ b/arch/arm/mach-sa1100/pm.c @@ -0,0 +1,167 @@ +/* + * SA1100 Power Management Routines + * + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2001-02-06: Cliff Brake Initial code + * + * 2001-02-25: Sukjae Cho <sjcho@east.isi.edu> & + * Chester Kuo <chester@linux.org.tw> + * Save more value for the resume function! Support + * Bitsy/Assabet/Freebird board + * + * 2001-08-29: Nicolas Pitre <nico@cam.org> + * Cleaned up, pushed platform dependent stuff + * in the platform specific files. + * + * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array. + * Storage is local on the stack now. + */ +#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/mach/time.h> + +extern void sa1100_cpu_suspend(void); +extern void sa1100_cpu_resume(void); + +#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x +#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] + +/* + * List of global SA11x0 peripheral registers to preserve. + * More ones like CP and general purpose register values are preserved + * on the stack and then the stack pointer is stored last in sleep.S. + */ +enum { SLEEP_SAVE_SP = 0, + + SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR, + SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR, + + SLEEP_SAVE_Ser1SDCR0, + + SLEEP_SAVE_SIZE +}; + + +static int sa11x0_pm_enter(suspend_state_t state) +{ + unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE]; + struct timespec delta, rtc; + + if (state != PM_SUSPEND_MEM) + return -EINVAL; + + /* preserve current time */ + rtc.tv_sec = RCNR; + rtc.tv_nsec = 0; + save_time_delta(&delta, &rtc); + gpio = GPLR; + + /* save vital registers */ + SAVE(GPDR); + SAVE(GAFR); + + SAVE(PPDR); + SAVE(PPSR); + SAVE(PPAR); + SAVE(PSDR); + + SAVE(Ser1SDCR0); + + /* Clear previous reset status */ + RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR; + + /* set resume return address */ + PSPR = virt_to_phys(sa1100_cpu_resume); + + /* go zzz */ + sa1100_cpu_suspend(); + + /* + * Ensure not to come back here if it wasn't intended + */ + PSPR = 0; + + /* + * Ensure interrupt sources are disabled; we will re-init + * the interrupt subsystem via the device manager. + */ + ICLR = 0; + ICCR = 1; + ICMR = 0; + + /* restore registers */ + RESTORE(GPDR); + RESTORE(GAFR); + + RESTORE(PPDR); + RESTORE(PPSR); + RESTORE(PPAR); + RESTORE(PSDR); + + RESTORE(Ser1SDCR0); + + GPSR = gpio; + GPCR = ~gpio; + + /* + * Clear the peripheral sleep-hold bit. + */ + PSSR = PSSR_PH; + + /* restore current time */ + rtc.tv_sec = RCNR; + restore_time_delta(&delta, &rtc); + + 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 sa11x0_pm_prepare(suspend_state_t state) +{ + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static int sa11x0_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 sa11x0_pm_ops = { + .pm_disk_mode = PM_DISK_FIRMWARE, + .prepare = sa11x0_pm_prepare, + .enter = sa11x0_pm_enter, + .finish = sa11x0_pm_finish, +}; + +static int __init sa11x0_pm_init(void) +{ + pm_set_ops(&sa11x0_pm_ops); + return 0; +} + +late_initcall(sa11x0_pm_init); diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c new file mode 100644 index 0000000..edddd55 --- /dev/null +++ b/arch/arm/mach-sa1100/shannon.c @@ -0,0 +1,85 @@ +/* + * linux/arch/arm/mach-sa1100/shannon.c + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/setup.h> + +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> +#include <asm/arch/shannon.h> + +#include "generic.h" + +static struct mtd_partition shannon_partitions[] = { + { + .name = "BLOB boot loader", + .offset = 0, + .size = 0x20000 + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0xe0000 + }, + { + .name = "initrd", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static struct flash_platform_data shannon_flash_data = { + .map_name = "cfi_probe", + .parts = shannon_partitions, + .nr_parts = ARRAY_SIZE(shannon_partitions), +}; + +static struct resource shannon_flash_resource = { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_4M - 1, + .flags = IORESOURCE_MEM, +}; + +static void __init shannon_init(void) +{ + sa11x0_set_flash_data(&shannon_flash_data, &shannon_flash_resource, 1); +} + +static void __init shannon_map_io(void) +{ + sa1100_map_io(); + + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); + + Ser1SDCR0 |= SDCR0_SUS; + GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD); + GPDR |= GPIO_UART_TXD | SHANNON_GPIO_CODEC_RESET; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; + + /* reset the codec */ + GPCR = SHANNON_GPIO_CODEC_RESET; + GPSR = SHANNON_GPIO_CODEC_RESET; +} + +MACHINE_START(SHANNON, "Shannon (AKA: Tuxscreen)") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(shannon_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, + .init_machine = shannon_init, +MACHINE_END diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c new file mode 100644 index 0000000..8d113d6 --- /dev/null +++ b/arch/arm/mach-sa1100/simpad.c @@ -0,0 +1,224 @@ +/* + * linux/arch/arm/mach-sa1100/simpad.c + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/proc_fs.h> +#include <linux/string.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/setup.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/map.h> +#include <asm/mach/serial_sa1100.h> +#include <asm/arch/simpad.h> + +#include <linux/serial_core.h> +#include <linux/ioport.h> +#include <asm/io.h> + +#include "generic.h" + +long cs3_shadow; + +long get_cs3_shadow(void) +{ + return cs3_shadow; +} + +void set_cs3(long value) +{ + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow = value; +} + +void set_cs3_bit(int value) +{ + cs3_shadow |= value; + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow; +} + +void clear_cs3_bit(int value) +{ + cs3_shadow &= ~value; + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow; +} + +EXPORT_SYMBOL(set_cs3_bit); +EXPORT_SYMBOL(clear_cs3_bit); + +static struct map_desc simpad_io_desc[] __initdata = { + /* virtual physical length type */ + /* MQ200 */ + { 0xf2800000, 0x4b800000, 0x00800000, MT_DEVICE }, + /* Paules CS3, write only */ + { 0xf1000000, 0x18000000, 0x00100000, MT_DEVICE }, +}; + + +static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (port->mapbase == (u_int)&Ser1UTCR0) { + if (state) + { + clear_cs3_bit(RS232_ON); + clear_cs3_bit(DECT_POWER_ON); + }else + { + set_cs3_bit(RS232_ON); + set_cs3_bit(DECT_POWER_ON); + } + } +} + +static struct sa1100_port_fns simpad_port_fns __initdata = { + .pm = simpad_uart_pm, +}; + + +static struct mtd_partition simpad_partitions[] = { + { + .name = "SIMpad boot firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "SIMpad kernel", + .size = 0x0010000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "SIMpad root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct flash_platform_data simpad_flash_data = { + .map_name = "cfi_probe", + .parts = simpad_partitions, + .nr_parts = ARRAY_SIZE(simpad_partitions), +}; + + +static struct resource simpad_flash_resources [] = { + { + .start = SA1100_CS0_PHYS, + .end = SA1100_CS0_PHYS + SZ_16M -1, + .flags = IORESOURCE_MEM, + }, { + .start = SA1100_CS1_PHYS, + .end = SA1100_CS1_PHYS + SZ_16M -1, + .flags = IORESOURCE_MEM, + } +}; + + + +static void __init simpad_map_io(void) +{ + sa1100_map_io(); + + iotable_init(simpad_io_desc, ARRAY_SIZE(simpad_io_desc)); + + set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON | + ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON); + + + sa1100_register_uart_fns(&simpad_port_fns); + sa1100_register_uart(0, 3); /* serial interface */ + sa1100_register_uart(1, 1); /* DECT */ + + // Reassign UART 1 pins + GAFR |= GPIO_UART_TXD | GPIO_UART_RXD; + GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; + + /* + * Set up registers for sleep mode. + */ + + + PWER = PWER_GPIO0| PWER_RTC; + PGSR = 0x818; + PCFR = 0; + PSDR = 0; + + sa11x0_set_flash_data(&simpad_flash_data, simpad_flash_resources, + ARRAY_SIZE(simpad_flash_resources)); +} + +static void simpad_power_off(void) +{ + local_irq_disable(); // was cli + set_cs3(0x800); /* only SD_MEDIAQ */ + + /* disable internal oscillator, float CS lines */ + PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS); + /* enable wake-up on GPIO0 (Assabet...) */ + PWER = GFER = GRER = 1; + /* + * set scratchpad to zero, just in case it is used as a + * restart address by the bootloader. + */ + PSPR = 0; + PGSR = 0; + /* enter sleep mode */ + PMCR = PMCR_SF; + while(1); + + local_irq_enable(); /* we won't ever call it */ + + +} + + +/* + * MediaQ Video Device + */ +static struct platform_device simpad_mq200fb = { + .name = "simpad-mq200", + .id = 0, +}; + +static struct platform_device *devices[] __initdata = { + &simpad_mq200fb +}; + + + +static int __init simpad_init(void) +{ + int ret; + + pm_power_off = simpad_power_off; + + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if(ret) + printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device"); + + return 0; +} + +arch_initcall(simpad_init); + + +MACHINE_START(SIMPAD, "Simpad") + MAINTAINER("Holger Freyther") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + MAPIO(simpad_map_io) + INITIRQ(sa1100_init_irq) + .timer = &sa1100_timer, +MACHINE_END diff --git a/arch/arm/mach-sa1100/sleep.S b/arch/arm/mach-sa1100/sleep.S new file mode 100644 index 0000000..2fa1e28 --- /dev/null +++ b/arch/arm/mach-sa1100/sleep.S @@ -0,0 +1,215 @@ +/* + * SA11x0 Assembler Sleep/WakeUp Management Routines + * + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2001-02-06: Cliff Brake Initial code + * + * 2001-08-29: Nicolas Pitre Simplified. + * + * 2002-05-27: Nicolas Pitre Revisited, more cleanup and simplification. + * Storage is on the stack now. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/hardware.h> + + + + .text + + + +/* + * sa1100_cpu_suspend() + * + * Causes sa11x0 to enter sleep state + * + */ + +ENTRY(sa1100_cpu_suspend) + + stmfd sp!, {r4 - r12, lr} @ save registers on stack + + @ get coprocessor registers + mrc p15, 0, r4, c3, c0, 0 @ domain ID + mrc p15, 0, r5, c2, c0, 0 @ translation table base addr + mrc p15, 0, r6, c13, c0, 0 @ PID + mrc p15, 0, r7, c1, c0, 0 @ control reg + + @ store them plus current virtual stack ptr on stack + mov r8, sp + stmfd sp!, {r4 - r8} + + @ preserve phys address of stack + mov r0, sp + bl sleep_phys_sp + ldr r1, =sleep_save_sp + str r0, [r1] + + @ clean data cache and invalidate WB + bl v4wb_flush_kern_cache_all + + @ disable clock switching + mcr p15, 0, r1, c15, c2, 2 + + @ Adjust memory timing before lowering CPU clock + @ Clock speed adjustment without changing memory timing makes + @ CPU hang in some cases + ldr r0, =MDREFR + ldr r1, [r0] + orr r1, r1, #MDREFR_K1DB2 + str r1, [r0] + + @ delay 90us and set CPU PLL to lowest speed + @ fixes resume problem on high speed SA1110 + mov r0, #90 + bl __udelay + ldr r0, =PPCR + mov r1, #0 + str r1, [r0] + mov r0, #90 + bl __udelay + + /* + * SA1110 SDRAM controller workaround. register values: + * + * r0 = &MSC0 + * r1 = &MSC1 + * r2 = &MSC2 + * r3 = MSC0 value + * r4 = MSC1 value + * r5 = MSC2 value + * r6 = &MDREFR + * r7 = first MDREFR value + * r8 = second MDREFR value + * r9 = &MDCNFG + * r10 = MDCNFG value + * r11 = third MDREFR value + * r12 = &PMCR + * r13 = PMCR value (1) + */ + + ldr r0, =MSC0 + ldr r1, =MSC1 + ldr r2, =MSC2 + + ldr r3, [r0] + bic r3, r3, #FMsk(MSC_RT) + bic r3, r3, #FMsk(MSC_RT)<<16 + + ldr r4, [r1] + bic r4, r4, #FMsk(MSC_RT) + bic r4, r4, #FMsk(MSC_RT)<<16 + + ldr r5, [r2] + bic r5, r5, #FMsk(MSC_RT) + bic r5, r5, #FMsk(MSC_RT)<<16 + + ldr r6, =MDREFR + + ldr r7, [r6] + bic r7, r7, #0x0000FF00 + bic r7, r7, #0x000000F0 + orr r8, r7, #MDREFR_SLFRSH + + ldr r9, =MDCNFG + ldr r10, [r9] + bic r10, r10, #(MDCNFG_DE0+MDCNFG_DE1) + bic r10, r10, #(MDCNFG_DE2+MDCNFG_DE3) + + bic r11, r8, #MDREFR_SLFRSH + bic r11, r11, #MDREFR_E1PIN + + ldr r12, =PMCR + + mov r13, #PMCR_SF + + b sa1110_sdram_controller_fix + + .align 5 +sa1110_sdram_controller_fix: + + @ Step 1 clear RT field of all MSCx registers + str r3, [r0] + str r4, [r1] + str r5, [r2] + + @ Step 2 clear DRI field in MDREFR + str r7, [r6] + + @ Step 3 set SLFRSH bit in MDREFR + str r8, [r6] + + @ Step 4 clear DE bis in MDCNFG + str r10, [r9] + + @ Step 5 clear DRAM refresh control register + str r11, [r6] + + @ Wow, now the hardware suspend request pins can be used, that makes them functional for + @ about 7 ns out of the entire time that the CPU is running! + + @ Step 6 set force sleep bit in PMCR + + str r13, [r12] + +20: b 20b @ loop waiting for sleep + +/* + * cpu_sa1100_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(sa1100_cpu_resume) + mov r0, #PSR_F_BIT | PSR_I_BIT | MODE_SVC + msr cpsr_c, r0 @ set SVC, irqs off + + ldr r0, sleep_save_sp @ stack phys addr + ldr r2, =resume_after_mmu @ its absolute virtual address + ldmfd r0, {r4 - r7, sp} @ CP regs + virt stack ptr + + mov r1, #0 + mcr p15, 0, r1, c8, c7, 0 @ flush I+D TLBs + mcr p15, 0, r1, c7, c7, 0 @ flush I&D cache + mcr p15, 0, r1, c9, c0, 0 @ invalidate RB + mcr p15, 0, r1, c9, c0, 5 @ allow user space to use RB + + mcr p15, 0, r4, c3, c0, 0 @ domain ID + mcr p15, 0, r5, c2, c0, 0 @ translation table base addr + mcr p15, 0, r6, c13, c0, 0 @ PID + b resume_turn_on_mmu @ cache align execution + + .align 5 +resume_turn_on_mmu: + mcr p15, 0, r7, c1, c0, 0 @ turn on MMU, caches, etc. + nop + mov pc, r2 @ jump to virtual addr + nop + nop + nop + +sleep_save_sp: + .word 0 @ preserve stack phys ptr here + + .text +resume_after_mmu: + mcr p15, 0, r1, c15, c1, 2 @ enable clock switching + ldmfd sp!, {r4 - r12, pc} @ return to caller + + diff --git a/arch/arm/mach-sa1100/ssp.c b/arch/arm/mach-sa1100/ssp.c new file mode 100644 index 0000000..1604dad --- /dev/null +++ b/arch/arm/mach-sa1100/ssp.c @@ -0,0 +1,214 @@ +/* + * linux/arch/arm/mach-sa1100/ssp.c + * + * Copyright (C) 2003 Russell King. + * + * 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. + * + * Generic SSP driver. This provides the generic core for simple + * IO-based SSP applications. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.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/hardware/ssp.h> + +static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status = Ser4SSSR; + + if (status & SSSR_ROR) { + printk(KERN_WARNING "SSP: receiver overrun\n"); + } + + Ser4SSSR = SSSR_ROR; + + return status ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * ssp_write_word - write a word to the SSP port + * @data: 16-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. Wait for the SSP port to start sending + * the data. + * + * The caller is expected to perform the necessary locking. + * + * Returns: + * %-ETIMEDOUT timeout occurred (for future) + * 0 success + */ +int ssp_write_word(u16 data) +{ + while (!(Ser4SSSR & SSSR_TNF)) + cpu_relax(); + + Ser4SSDR = data; + + while (!(Ser4SSSR & SSSR_BSY)) + cpu_relax(); + + 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) + * 16-bit data success + */ +int ssp_read_word(void) +{ + while (!(Ser4SSSR & SSSR_RNE)) + cpu_relax(); + + return Ser4SSDR; +} + +/** + * 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(void) +{ + do { + while (Ser4SSSR & SSSR_RNE) { + (void) Ser4SSDR; + } + } while (Ser4SSSR & SSSR_BSY); +} + +/** + * ssp_enable - enable the SSP port + * + * Turn on the SSP port. + */ +void ssp_enable(void) +{ + Ser4SSCR0 |= SSCR0_SSE; +} + +/** + * ssp_disable - shut down the SSP port + * + * Turn off the SSP port, optionally powering it down. + */ +void ssp_disable(void) +{ + Ser4SSCR0 &= ~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_state *ssp) +{ + ssp->cr0 = Ser4SSCR0; + ssp->cr1 = Ser4SSCR1; + + Ser4SSCR0 &= ~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_state *ssp) +{ + Ser4SSSR = SSSR_ROR; + + Ser4SSCR0 = ssp->cr0 & ~SSCR0_SSE; + Ser4SSCR1 = ssp->cr1; + Ser4SSCR0 = ssp->cr0; +} + +/** + * 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(void) +{ + int ret; + + if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE)) + return -ENODEV; + + if (!request_mem_region(__PREG(Ser4SSCR0), 0x18, "SSP")) { + return -EBUSY; + } + + Ser4SSSR = SSSR_ROR; + + ret = request_irq(IRQ_Ser4SSP, ssp_interrupt, 0, "SSP", NULL); + if (ret) + goto out_region; + + return 0; + + out_region: + release_mem_region(__PREG(Ser4SSCR0), 0x18); + return ret; +} + +/** + * ssp_exit - undo the effects of ssp_init + * + * release and free resources for the SSP port. + */ +void ssp_exit(void) +{ + Ser4SSCR0 &= ~SSCR0_SSE; + + free_irq(IRQ_Ser4SSP, NULL); + release_mem_region(__PREG(Ser4SSCR0), 0x18); +} + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SA11x0 SSP PIO driver"); +MODULE_LICENSE("GPL"); + +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); diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c new file mode 100644 index 0000000..19b0c0f --- /dev/null +++ b/arch/arm/mach-sa1100/time.c @@ -0,0 +1,159 @@ +/* + * linux/arch/arm/mach-sa1100/time.c + * + * Copyright (C) 1998 Deborah Wallach. + * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com> + * + * 2000/03/29 (C) Nicolas Pitre <nico@cam.org> + * Rewritten: big cleanup, much simpler, better HZ accuracy. + * + */ +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/signal.h> + +#include <asm/mach/time.h> +#include <asm/hardware.h> + +#define RTC_DEF_DIVIDER (32768 - 1) +#define RTC_DEF_TRIM 0 + +static unsigned long __init sa1100_get_rtc_time(void) +{ + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + printk(KERN_WARNING "Warning: uninitialized Real Time Clock\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + return 0; + } + return RCNR; +} + +static int sa1100_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 sa1100_gettimeoffset (void) +{ + unsigned 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; + + /* Now convert them to usec */ + usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; + + return usec; +} + +/* + * We will be entered with IRQs enabled. + * + * 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. + */ +static irqreturn_t +sa1100_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int next_match; + + write_seqlock(&xtime_lock); + + do { + timer_tick(regs); + OSSR = OSSR_M0; /* Clear match on timer 0 */ + next_match = (OSMR0 += LATCH); + } while ((signed long)(next_match - OSCR) <= 0); + + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction sa1100_timer_irq = { + .name = "SA11xx Timer Tick", + .flags = SA_INTERRUPT, + .handler = sa1100_timer_interrupt +}; + +static void __init sa1100_timer_init(void) +{ + struct timespec tv; + + set_rtc = sa1100_set_rtc; + + tv.tv_nsec = 0; + tv.tv_sec = sa1100_get_rtc_time(); + do_settimeofday(&tv); + + OSMR0 = 0; /* set initial match at 0 */ + OSSR = 0xf; /* clear status on all timers */ + setup_irq(IRQ_OST0, &sa1100_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 +unsigned long osmr[4], oier; + +static void sa1100_timer_suspend(void) +{ + osmr[0] = OSMR0; + osmr[1] = OSMR1; + osmr[2] = OSMR2; + osmr[3] = OSMR3; + oier = OIER; +} + +static void sa1100_timer_resume(void) +{ + OSSR = 0x0f; + 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 sa1100_timer_suspend NULL +#define sa1100_timer_resume NULL +#endif + +struct sys_timer sa1100_timer = { + .init = sa1100_timer_init, + .suspend = sa1100_timer_suspend, + .resume = sa1100_timer_resume, + .offset = sa1100_gettimeoffset, +}; |