Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
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,
+};