[PATCH] ARM: 2812/1: OMAP update 7c/11: Move arch-omap to plat-omap
authorTony Lindgren <tony@atomide.com>
Sun, 10 Jul 2005 18:58:15 +0000 (19:58 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 10 Jul 2005 18:58:15 +0000 (19:58 +0100)
Patch from Tony Lindgren

This patch move common OMAP code from arch-omap to plat-omap
directory.

Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
12 files changed:
arch/arm/plat-omap/Kconfig [new file with mode: 0644]
arch/arm/plat-omap/Makefile [new file with mode: 0644]
arch/arm/plat-omap/common.c [new file with mode: 0644]
arch/arm/plat-omap/common.h [new file with mode: 0644]
arch/arm/plat-omap/dma.c [new file with mode: 0644]
arch/arm/plat-omap/gpio.c [new file with mode: 0644]
arch/arm/plat-omap/mcbsp.c [new file with mode: 0644]
arch/arm/plat-omap/mux.c [new file with mode: 0644]
arch/arm/plat-omap/ocpi.c [new file with mode: 0644]
arch/arm/plat-omap/pm.c [new file with mode: 0644]
arch/arm/plat-omap/sleep.S [new file with mode: 0644]
arch/arm/plat-omap/usb.c [new file with mode: 0644]

diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
new file mode 100644 (file)
index 0000000..a72fe55
--- /dev/null
@@ -0,0 +1,99 @@
+if ARCH_OMAP
+
+menu "TI OMAP Implementations"
+
+config ARCH_OMAP_OTG
+       bool
+
+choice
+       prompt "OMAP System Type"
+       default ARCH_OMAP1
+
+config ARCH_OMAP1
+       bool "TI OMAP1"
+
+config ARCH_OMAP2
+       bool "TI OMAP2"
+
+endchoice
+
+comment "OMAP Feature Selections"
+
+config OMAP_MUX
+       bool "OMAP multiplexing support"
+        depends on ARCH_OMAP
+       default y
+        help
+          Pin multiplexing support for OMAP boards. If your bootloader
+          sets the multiplexing correctly, say N. Otherwise, or if unsure,
+          say Y.
+
+config OMAP_MUX_DEBUG
+       bool "Multiplexing debug output"
+        depends on OMAP_MUX
+        default n
+        help
+          Makes the multiplexing functions print out a lot of debug info.
+          This is useful if you want to find out the correct values of the
+          multiplexing registers.
+
+config OMAP_MUX_WARNINGS
+       bool "Warn about pins the bootloader didn't set up"
+        depends on OMAP_MUX
+        default y
+        help
+         Choose Y here to warn whenever driver initialization logic needs
+         to change the pin multiplexing setup.  When there are no warnings
+         printed, it's safe to deselect OMAP_MUX for your product.
+
+choice
+        prompt "System timer"
+       default OMAP_MPU_TIMER
+
+config OMAP_MPU_TIMER
+       bool "Use mpu timer"
+       help
+         Select this option if you want to use the OMAP mpu timer. This
+         timer provides more intra-tick resolution than the 32KHz timer,
+         but consumes more power.
+
+config OMAP_32K_TIMER
+       bool "Use 32KHz timer"
+       depends on ARCH_OMAP16XX
+       help
+         Select this option if you want to enable the OMAP 32KHz timer.
+         This timer saves power compared to the OMAP_MPU_TIMER, and has
+         support for no tick during idle. The 32KHz timer provides less
+         intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is
+         currently only available for OMAP-16xx.
+
+endchoice
+
+config OMAP_32K_TIMER_HZ
+       int "Kernel internal timer frequency for 32KHz timer"
+       range 32 1024
+       depends on OMAP_32K_TIMER
+       default "128"
+       help
+         Kernel internal timer frequency should be a divisor of 32768,
+         such as 64 or 128.
+
+choice
+       prompt "Low-level debug console UART"
+       depends on ARCH_OMAP
+       default OMAP_LL_DEBUG_UART1
+
+config OMAP_LL_DEBUG_UART1
+       bool "UART1"
+
+config OMAP_LL_DEBUG_UART2
+       bool "UART2"
+
+config OMAP_LL_DEBUG_UART3
+       bool "UART3"
+
+endchoice
+
+endmenu
+
+endif
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
new file mode 100644 (file)
index 0000000..3be25eb
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Common support
+obj-y := common.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o
+obj-m :=
+obj-n :=
+obj-  :=
+
+# OCPI interconnect support for 1710, 1610 and 5912
+obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o
+
+# Power Management
+obj-$(CONFIG_PM) += pm.o sleep.o
+
diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c
new file mode 100644 (file)
index 0000000..ea967a8
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * linux/arch/arm/plat-omap/common.c
+ *
+ * Code common to all OMAP 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/console.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+
+#include <asm/hardware.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/clock.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/fpga.h>
+
+#include "clock.h"
+
+#define NO_LENGTH_CHECK 0xffffffff
+
+extern int omap_bootloader_tag_len;
+extern u8 omap_bootloader_tag[];
+
+struct omap_board_config_kernel *omap_board_config;
+int omap_board_config_size = 0;
+
+static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
+{
+       struct omap_board_config_kernel *kinfo = NULL;
+       int i;
+
+#ifdef CONFIG_OMAP_BOOT_TAG
+       struct omap_board_config_entry *info = NULL;
+
+       if (omap_bootloader_tag_len > 4)
+               info = (struct omap_board_config_entry *) omap_bootloader_tag;
+       while (info != NULL) {
+               u8 *next;
+
+               if (info->tag == tag) {
+                       if (skip == 0)
+                               break;
+                       skip--;
+               }
+
+               if ((info->len & 0x03) != 0) {
+                       /* We bail out to avoid an alignment fault */
+                       printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n",
+                              info->len, info->tag);
+                       return NULL;
+               }
+               next = (u8 *) info + sizeof(*info) + info->len;
+               if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
+                       info = NULL;
+               else
+                       info = (struct omap_board_config_entry *) next;
+       }
+       if (info != NULL) {
+               /* Check the length as a lame attempt to check for
+                * binary inconsistancy. */
+               if (len != NO_LENGTH_CHECK) {
+                       /* Word-align len */
+                       if (len & 0x03)
+                               len = (len + 3) & ~0x03;
+                       if (info->len != len) {
+                               printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
+                                      tag, len, info->len);
+                               return NULL;
+                       }
+               }
+               if (len_out != NULL)
+                       *len_out = info->len;
+               return info->data;
+       }
+#endif
+       /* Try to find the config from the board-specific structures
+        * in the kernel. */
+       for (i = 0; i < omap_board_config_size; i++) {
+               if (omap_board_config[i].tag == tag) {
+                       kinfo = &omap_board_config[i];
+                       break;
+               }
+       }
+       if (kinfo == NULL)
+               return NULL;
+       return kinfo->data;
+}
+
+const void *__omap_get_config(u16 tag, size_t len, int nr)
+{
+        return get_config(tag, len, nr, NULL);
+}
+EXPORT_SYMBOL(__omap_get_config);
+
+const void *omap_get_var_config(u16 tag, size_t *len)
+{
+        return get_config(tag, NO_LENGTH_CHECK, 0, len);
+}
+EXPORT_SYMBOL(omap_get_var_config);
+
+static int __init omap_add_serial_console(void)
+{
+       const struct omap_serial_console_config *info;
+
+       info = omap_get_config(OMAP_TAG_SERIAL_CONSOLE,
+                              struct omap_serial_console_config);
+       if (info != NULL && info->console_uart) {
+               static char speed[11], *opt = NULL;
+
+               if (info->console_speed) {
+                       snprintf(speed, sizeof(speed), "%u", info->console_speed);
+                       opt = speed;
+               }
+               return add_preferred_console("ttyS", info->console_uart - 1, opt);
+       }
+       return 0;
+}
+console_initcall(omap_add_serial_console);
diff --git a/arch/arm/plat-omap/common.h b/arch/arm/plat-omap/common.h
new file mode 100644 (file)
index 0000000..893964d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * linux/arch/arm/plat-omap/common.h
+ *
+ * Header for code common to all OMAP machines.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ARCH_ARM_MACH_OMAP_COMMON_H
+#define __ARCH_ARM_MACH_OMAP_COMMON_H
+
+struct sys_timer;
+
+extern void omap_map_common_io(void);
+extern struct sys_timer omap_timer;
+extern void omap_serial_init(int ports[]);
+
+#endif /* __ARCH_ARM_MACH_OMAP_COMMON_H */
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
new file mode 100644 (file)
index 0000000..015bd2c
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+ * linux/arch/arm/plat-omap/dma.c
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com>
+ * Graphics DMA and LCD DMA graphics tranformations
+ * by Imre Deak <imre.deak@nokia.com>
+ * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc.
+ *
+ * Support functions for the OMAP internal DMA channels.
+ *
+ * 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/sched.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include <asm/arch/tc.h>
+
+#define OMAP_DMA_ACTIVE                0x01
+
+#define OMAP_DMA_CCR_EN                (1 << 7)
+
+#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec)
+
+static int enable_1510_mode = 0;
+
+struct omap_dma_lch {
+       int next_lch;
+       int dev_id;
+       u16 saved_csr;
+       u16 enabled_irqs;
+       const char *dev_name;
+       void (* callback)(int lch, u16 ch_status, void *data);
+       void *data;
+       long flags;
+};
+
+static int dma_chan_count;
+
+static spinlock_t dma_chan_lock;
+static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT];
+
+const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = {
+       INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3,
+       INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7,
+       INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10,
+       INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13,
+       INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD
+};
+
+static inline int get_gdma_dev(int req)
+{
+       u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
+       int shift = ((req - 1) % 5) * 6;
+
+       return ((omap_readl(reg) >> shift) & 0x3f) + 1;
+}
+
+static inline void set_gdma_dev(int req, int dev)
+{
+       u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
+       int shift = ((req - 1) % 5) * 6;
+       u32 l;
+
+       l = omap_readl(reg);
+       l &= ~(0x3f << shift);
+       l |= (dev - 1) << shift;
+       omap_writel(l, reg);
+}
+
+static void clear_lch_regs(int lch)
+{
+       int i;
+       u32 lch_base = OMAP_DMA_BASE + lch * 0x40;
+
+       for (i = 0; i < 0x2c; i += 2)
+               omap_writew(0, lch_base + i);
+}
+
+void omap_set_dma_priority(int dst_port, int priority)
+{
+       unsigned long reg;
+       u32 l;
+
+       switch (dst_port) {
+       case OMAP_DMA_PORT_OCP_T1:      /* FFFECC00 */
+               reg = OMAP_TC_OCPT1_PRIOR;
+               break;
+       case OMAP_DMA_PORT_OCP_T2:      /* FFFECCD0 */
+               reg = OMAP_TC_OCPT2_PRIOR;
+               break;
+       case OMAP_DMA_PORT_EMIFF:       /* FFFECC08 */
+               reg = OMAP_TC_EMIFF_PRIOR;
+               break;
+       case OMAP_DMA_PORT_EMIFS:       /* FFFECC04 */
+               reg = OMAP_TC_EMIFS_PRIOR;
+               break;
+       default:
+               BUG();
+               return;
+       }
+       l = omap_readl(reg);
+       l &= ~(0xf << 8);
+       l |= (priority & 0xf) << 8;
+       omap_writel(l, reg);
+}
+
+void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
+                                 int frame_count, int sync_mode)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch));
+       w &= ~0x03;
+       w |= data_type;
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+
+       w = omap_readw(OMAP_DMA_CCR(lch));
+       w &= ~(1 << 5);
+       if (sync_mode == OMAP_DMA_SYNC_FRAME)
+               w |= 1 << 5;
+       omap_writew(w, OMAP_DMA_CCR(lch));
+
+       w = omap_readw(OMAP_DMA_CCR2(lch));
+       w &= ~(1 << 2);
+       if (sync_mode == OMAP_DMA_SYNC_BLOCK)
+               w |= 1 << 2;
+       omap_writew(w, OMAP_DMA_CCR2(lch));
+
+       omap_writew(elem_count, OMAP_DMA_CEN(lch));
+       omap_writew(frame_count, OMAP_DMA_CFN(lch));
+
+}
+void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color)
+{
+       u16 w;
+
+       BUG_ON(omap_dma_in_1510_mode());
+
+       w = omap_readw(OMAP_DMA_CCR2(lch)) & ~0x03;
+       switch (mode) {
+       case OMAP_DMA_CONSTANT_FILL:
+               w |= 0x01;
+               break;
+       case OMAP_DMA_TRANSPARENT_COPY:
+               w |= 0x02;
+               break;
+       case OMAP_DMA_COLOR_DIS:
+               break;
+       default:
+               BUG();
+       }
+       omap_writew(w, OMAP_DMA_CCR2(lch));
+
+       w = omap_readw(OMAP_DMA_LCH_CTRL(lch)) & ~0x0f;
+       /* Default is channel type 2D */
+       if (mode) {
+               omap_writew((u16)color, OMAP_DMA_COLOR_L(lch));
+               omap_writew((u16)(color >> 16), OMAP_DMA_COLOR_U(lch));
+               w |= 1;         /* Channel type G */
+       }
+       omap_writew(w, OMAP_DMA_LCH_CTRL(lch));
+}
+
+
+void omap_set_dma_src_params(int lch, int src_port, int src_amode,
+                            unsigned long src_start)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch));
+       w &= ~(0x1f << 2);
+       w |= src_port << 2;
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+
+       w = omap_readw(OMAP_DMA_CCR(lch));
+       w &= ~(0x03 << 12);
+       w |= src_amode << 12;
+       omap_writew(w, OMAP_DMA_CCR(lch));
+
+       omap_writew(src_start >> 16, OMAP_DMA_CSSA_U(lch));
+       omap_writew(src_start, OMAP_DMA_CSSA_L(lch));
+}
+
+void omap_set_dma_src_index(int lch, int eidx, int fidx)
+{
+       omap_writew(eidx, OMAP_DMA_CSEI(lch));
+       omap_writew(fidx, OMAP_DMA_CSFI(lch));
+}
+
+void omap_set_dma_src_data_pack(int lch, int enable)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 6);
+       w |= enable ? (1 << 6) : 0;
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+}
+
+void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 7);
+       switch (burst_mode) {
+       case OMAP_DMA_DATA_BURST_DIS:
+               break;
+       case OMAP_DMA_DATA_BURST_4:
+               w |= (0x01 << 7);
+               break;
+       case OMAP_DMA_DATA_BURST_8:
+               /* not supported by current hardware
+                * w |= (0x03 << 7);
+                * fall through
+                */
+       default:
+               BUG();
+       }
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+}
+
+void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
+                             unsigned long dest_start)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch));
+       w &= ~(0x1f << 9);
+       w |= dest_port << 9;
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+
+       w = omap_readw(OMAP_DMA_CCR(lch));
+       w &= ~(0x03 << 14);
+       w |= dest_amode << 14;
+       omap_writew(w, OMAP_DMA_CCR(lch));
+
+       omap_writew(dest_start >> 16, OMAP_DMA_CDSA_U(lch));
+       omap_writew(dest_start, OMAP_DMA_CDSA_L(lch));
+}
+
+void omap_set_dma_dest_index(int lch, int eidx, int fidx)
+{
+       omap_writew(eidx, OMAP_DMA_CDEI(lch));
+       omap_writew(fidx, OMAP_DMA_CDFI(lch));
+}
+
+void omap_set_dma_dest_data_pack(int lch, int enable)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 13);
+       w |= enable ? (1 << 13) : 0;
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+}
+
+void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
+{
+       u16 w;
+
+       w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 14);
+       switch (burst_mode) {
+       case OMAP_DMA_DATA_BURST_DIS:
+               break;
+       case OMAP_DMA_DATA_BURST_4:
+               w |= (0x01 << 14);
+               break;
+       case OMAP_DMA_DATA_BURST_8:
+               w |= (0x03 << 14);
+               break;
+       default:
+               printk(KERN_ERR "Invalid DMA burst mode\n");
+               BUG();
+               return;
+       }
+       omap_writew(w, OMAP_DMA_CSDP(lch));
+}
+
+static inline void init_intr(int lch)
+{
+       u16 w;
+
+       /* Read CSR to make sure it's cleared. */
+       w = omap_readw(OMAP_DMA_CSR(lch));
+       /* Enable some nice interrupts. */
+       omap_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR(lch));
+       dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
+}
+
+static inline void enable_lnk(int lch)
+{
+       u16 w;
+
+       /* Clear the STOP_LNK bits */
+       w = omap_readw(OMAP_DMA_CLNK_CTRL(lch));
+       w &= ~(1 << 14);
+       omap_writew(w, OMAP_DMA_CLNK_CTRL(lch));
+
+       /* And set the ENABLE_LNK bits */
+       if (dma_chan[lch].next_lch != -1)
+               omap_writew(dma_chan[lch].next_lch | (1 << 15),
+                           OMAP_DMA_CLNK_CTRL(lch));
+}
+
+static inline void disable_lnk(int lch)
+{
+       u16 w;
+
+       /* Disable interrupts */
+       omap_writew(0, OMAP_DMA_CICR(lch));
+
+       /* Set the STOP_LNK bit */
+       w = omap_readw(OMAP_DMA_CLNK_CTRL(lch));
+       w |= (1 << 14);
+       w = omap_writew(w, OMAP_DMA_CLNK_CTRL(lch));
+
+       dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
+}
+
+void omap_start_dma(int lch)
+{
+       u16 w;
+
+       if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
+               int next_lch, cur_lch;
+               char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT];
+
+               dma_chan_link_map[lch] = 1;
+               /* Set the link register of the first channel */
+               enable_lnk(lch);
+
+               memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
+               cur_lch = dma_chan[lch].next_lch;
+               do {
+                       next_lch = dma_chan[cur_lch].next_lch;
+
+                        /* The loop case: we've been here already */
+                       if (dma_chan_link_map[cur_lch])
+                               break;
+                       /* Mark the current channel */
+                       dma_chan_link_map[cur_lch] = 1;
+
+                       enable_lnk(cur_lch);
+                       init_intr(cur_lch);
+
+                       cur_lch = next_lch;
+               } while (next_lch != -1);
+       }
+
+       init_intr(lch);
+
+       w = omap_readw(OMAP_DMA_CCR(lch));
+       w |= OMAP_DMA_CCR_EN;
+       omap_writew(w, OMAP_DMA_CCR(lch));
+       dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
+}
+
+void omap_stop_dma(int lch)
+{
+       u16 w;
+
+       if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
+               int next_lch, cur_lch = lch;
+               char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT];
+
+               memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
+               do {
+                       /* The loop case: we've been here already */
+                       if (dma_chan_link_map[cur_lch])
+                               break;
+                       /* Mark the current channel */
+                       dma_chan_link_map[cur_lch] = 1;
+
+                       disable_lnk(cur_lch);
+
+                       next_lch = dma_chan[cur_lch].next_lch;
+                       cur_lch = next_lch;
+               } while (next_lch != -1);
+
+               return;
+       }
+       /* Disable all interrupts on the channel */
+       omap_writew(0, OMAP_DMA_CICR(lch));
+
+       w = omap_readw(OMAP_DMA_CCR(lch));
+       w &= ~OMAP_DMA_CCR_EN;
+       omap_writew(w, OMAP_DMA_CCR(lch));
+       dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
+}
+
+void omap_enable_dma_irq(int lch, u16 bits)
+{
+       dma_chan[lch].enabled_irqs |= bits;
+}
+
+void omap_disable_dma_irq(int lch, u16 bits)
+{
+       dma_chan[lch].enabled_irqs &= ~bits;
+}
+
+static int dma_handle_ch(int ch)
+{
+       u16 csr;
+
+       if (enable_1510_mode && ch >= 6) {
+               csr = dma_chan[ch].saved_csr;
+               dma_chan[ch].saved_csr = 0;
+       } else
+               csr = omap_readw(OMAP_DMA_CSR(ch));
+       if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) {
+               dma_chan[ch + 6].saved_csr = csr >> 7;
+               csr &= 0x7f;
+       }
+       if (!csr)
+               return 0;
+       if (unlikely(dma_chan[ch].dev_id == -1)) {
+               printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n",
+                      ch, csr);
+               return 0;
+       }
+       if (unlikely(csr & OMAP_DMA_TOUT_IRQ))
+               printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id);
+       if (unlikely(csr & OMAP_DMA_DROP_IRQ))
+               printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n",
+                      dma_chan[ch].dev_id);
+       if (likely(csr & OMAP_DMA_BLOCK_IRQ))
+               dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
+       if (likely(dma_chan[ch].callback != NULL))
+               dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
+       return 1;
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int ch = ((int) dev_id) - 1;
+       int handled = 0;
+
+       for (;;) {
+               int handled_now = 0;
+
+               handled_now += dma_handle_ch(ch);
+               if (enable_1510_mode && dma_chan[ch + 6].saved_csr)
+                       handled_now += dma_handle_ch(ch + 6);
+               if (!handled_now)
+                       break;
+               handled += handled_now;
+       }
+
+       return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+int omap_request_dma(int dev_id, const char *dev_name,
+                    void (* callback)(int lch, u16 ch_status, void *data),
+                    void *data, int *dma_ch_out)
+{
+       int ch, free_ch = -1;
+       unsigned long flags;
+       struct omap_dma_lch *chan;
+
+       spin_lock_irqsave(&dma_chan_lock, flags);
+       for (ch = 0; ch < dma_chan_count; ch++) {
+               if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
+                       free_ch = ch;
+                       if (dev_id == 0)
+                               break;
+               }
+       }
+       if (free_ch == -1) {
+               spin_unlock_irqrestore(&dma_chan_lock, flags);
+               return -EBUSY;
+       }
+       chan = dma_chan + free_ch;
+       chan->dev_id = dev_id;
+       clear_lch_regs(free_ch);
+       spin_unlock_irqrestore(&dma_chan_lock, flags);
+
+       chan->dev_id = dev_id;
+       chan->dev_name = dev_name;
+       chan->callback = callback;
+       chan->data = data;
+       chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
+
+       if (cpu_is_omap16xx()) {
+               /* If the sync device is set, configure it dynamically. */
+               if (dev_id != 0) {
+                       set_gdma_dev(free_ch + 1, dev_id);
+                       dev_id = free_ch + 1;
+               }
+               /* Disable the 1510 compatibility mode and set the sync device
+                * id. */
+               omap_writew(dev_id | (1 << 10), OMAP_DMA_CCR(free_ch));
+       } else {
+               omap_writew(dev_id, OMAP_DMA_CCR(free_ch));
+       }
+       *dma_ch_out = free_ch;
+
+       return 0;
+}
+
+void omap_free_dma(int ch)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dma_chan_lock, flags);
+       if (dma_chan[ch].dev_id == -1) {
+               printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch);
+               spin_unlock_irqrestore(&dma_chan_lock, flags);
+               return;
+       }
+       dma_chan[ch].dev_id = -1;
+       spin_unlock_irqrestore(&dma_chan_lock, flags);
+
+       /* Disable all DMA interrupts for the channel. */
+       omap_writew(0, OMAP_DMA_CICR(ch));
+       /* Make sure the DMA transfer is stopped. */
+       omap_writew(0, OMAP_DMA_CCR(ch));
+}
+
+int omap_dma_in_1510_mode(void)
+{
+       return enable_1510_mode;
+}
+
+/*
+ * lch_queue DMA will start right after lch_head one is finished.
+ * For this DMA link to start, you still need to start (see omap_start_dma)
+ * the first one. That will fire up the entire queue.
+ */
+void omap_dma_link_lch (int lch_head, int lch_queue)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
+               BUG();
+               return;
+       }
+
+       if ((dma_chan[lch_head].dev_id == -1) ||
+           (dma_chan[lch_queue].dev_id == -1)) {
+               printk(KERN_ERR "omap_dma: trying to link non requested channels\n");
+               dump_stack();
+       }
+
+       dma_chan[lch_head].next_lch = lch_queue;
+}
+
+/*
+ * Once the DMA queue is stopped, we can destroy it.
+ */
+void omap_dma_unlink_lch (int lch_head, int lch_queue)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
+               BUG();
+               return;
+       }
+
+       if (dma_chan[lch_head].next_lch != lch_queue ||
+           dma_chan[lch_head].next_lch == -1) {
+               printk(KERN_ERR "omap_dma: trying to unlink non linked channels\n");
+               dump_stack();
+       }
+
+
+       if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) ||
+           (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) {
+               printk(KERN_ERR "omap_dma: You need to stop the DMA channels before unlinking\n");
+               dump_stack();
+       }
+
+       dma_chan[lch_head].next_lch = -1;
+}
+
+
+static struct lcd_dma_info {
+       spinlock_t lock;
+       int reserved;
+       void (* callback)(u16 status, void *data);
+       void *cb_data;
+
+       int active;
+       unsigned long addr, size;
+       int rotate, data_type, xres, yres;
+       int vxres;
+       int mirror;
+       int xscale, yscale;
+       int ext_ctrl;
+       int src_port;
+       int single_transfer;
+} lcd_dma;
+
+void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres,
+                        int data_type)
+{
+       lcd_dma.addr = addr;
+       lcd_dma.data_type = data_type;
+       lcd_dma.xres = fb_xres;
+       lcd_dma.yres = fb_yres;
+}
+
+void omap_set_lcd_dma_src_port(int port)
+{
+       lcd_dma.src_port = port;
+}
+
+void omap_set_lcd_dma_ext_controller(int external)
+{
+       lcd_dma.ext_ctrl = external;
+}
+
+void omap_set_lcd_dma_single_transfer(int single)
+{
+       lcd_dma.single_transfer = single;
+}
+
+
+void omap_set_lcd_dma_b1_rotation(int rotate)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n");
+               BUG();
+               return;
+       }
+       lcd_dma.rotate = rotate;
+}
+
+void omap_set_lcd_dma_b1_mirror(int mirror)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n");
+               BUG();
+       }
+       lcd_dma.mirror = mirror;
+}
+
+void omap_set_lcd_dma_b1_vxres(unsigned long vxres)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA virtual resulotion is not supported "
+                               "in 1510 mode\n");
+               BUG();
+       }
+       lcd_dma.vxres = vxres;
+}
+
+void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale)
+{
+       if (omap_dma_in_1510_mode()) {
+               printk(KERN_ERR "DMA scale is not supported in 1510 mode\n");
+               BUG();
+       }
+       lcd_dma.xscale = xscale;
+       lcd_dma.yscale = yscale;
+}
+
+static void set_b1_regs(void)
+{
+       unsigned long top, bottom;
+       int es;
+       u16 w;
+       unsigned long en, fn;
+       long ei, fi;
+       unsigned long vxres;
+       unsigned int xscale, yscale;
+
+       switch (lcd_dma.data_type) {
+       case OMAP_DMA_DATA_TYPE_S8:
+               es = 1;
+               break;
+       case OMAP_DMA_DATA_TYPE_S16:
+               es = 2;
+               break;
+       case OMAP_DMA_DATA_TYPE_S32:
+               es = 4;
+               break;
+       default:
+               BUG();
+               return;
+       }
+
+       vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres;
+       xscale = lcd_dma.xscale ? lcd_dma.xscale : 1;
+       yscale = lcd_dma.yscale ? lcd_dma.yscale : 1;
+       BUG_ON(vxres < lcd_dma.xres);
+#define PIXADDR(x,y) (lcd_dma.addr + ((y) * vxres * yscale + (x) * xscale) * es)
+#define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1)
+       switch (lcd_dma.rotate) {
+       case 0:
+               if (!lcd_dma.mirror) {
+                       top = PIXADDR(0, 0);
+                       bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);
+                       /* 1510 DMA requires the bottom address to be 2 more
+                        * than the actual last memory access location. */
+                       if (omap_dma_in_1510_mode() &&
+                           lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32)
+                               bottom += 2;
+                       ei = PIXSTEP(0, 0, 1, 0);
+                       fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1);
+               } else {
+                       top = PIXADDR(lcd_dma.xres - 1, 0);
+                       bottom = PIXADDR(0, lcd_dma.yres - 1);
+                       ei = PIXSTEP(1, 0, 0, 0);
+                       fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1);
+               }
+               en = lcd_dma.xres;
+               fn = lcd_dma.yres;
+               break;
+       case 90:
+               if (!lcd_dma.mirror) {
+                       top = PIXADDR(0, lcd_dma.yres - 1);
+                       bottom = PIXADDR(lcd_dma.xres - 1, 0);
+                       ei = PIXSTEP(0, 1, 0, 0);
+                       fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1);
+               } else {
+                       top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);
+                       bottom = PIXADDR(0, 0);
+                       ei = PIXSTEP(0, 1, 0, 0);
+                       fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1);
+               }
+               en = lcd_dma.yres;
+               fn = lcd_dma.xres;
+               break;
+       case 180:
+               if (!lcd_dma.mirror) {
+                       top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);
+                       bottom = PIXADDR(0, 0);
+                       ei = PIXSTEP(1, 0, 0, 0);
+                       fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0);
+               } else {
+                       top = PIXADDR(0, lcd_dma.yres - 1);
+                       bottom = PIXADDR(lcd_dma.xres - 1, 0);
+                       ei = PIXSTEP(0, 0, 1, 0);
+                       fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0);
+               }
+               en = lcd_dma.xres;
+               fn = lcd_dma.yres;
+               break;
+       case 270:
+               if (!lcd_dma.mirror) {
+                       top = PIXADDR(lcd_dma.xres - 1, 0);
+                       bottom = PIXADDR(0, lcd_dma.yres - 1);
+                       ei = PIXSTEP(0, 0, 0, 1);
+                       fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0);
+               } else {
+                       top = PIXADDR(0, 0);
+                       bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);
+                       ei = PIXSTEP(0, 0, 0, 1);
+                       fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0);
+               }
+               en = lcd_dma.yres;
+               fn = lcd_dma.xres;
+               break;
+       default:
+               BUG();
+               return; /* Supress warning about uninitialized vars */
+       }
+
+       if (omap_dma_in_1510_mode()) {
+               omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U);
+               omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L);
+               omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U);
+               omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L);
+
+               return;
+       }
+
+       /* 1610 regs */
+       omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U);
+       omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L);
+       omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U);
+       omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L);
+
+       omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1);
+       omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1);
+
+       w = omap_readw(OMAP1610_DMA_LCD_CSDP);
+       w &= ~0x03;
+       w |= lcd_dma.data_type;
+       omap_writew(w, OMAP1610_DMA_LCD_CSDP);
+
+       w = omap_readw(OMAP1610_DMA_LCD_CTRL);
+       /* Always set the source port as SDRAM for now*/
+       w &= ~(0x03 << 6);
+       if (lcd_dma.ext_ctrl)
+               w |= 1 << 8;
+       else
+               w &= ~(1 << 8);
+       if (lcd_dma.callback != NULL)
+               w |= 1 << 1;            /* Block interrupt enable */
+       else
+               w &= ~(1 << 1);
+       omap_writew(w, OMAP1610_DMA_LCD_CTRL);
+
+       if (!(lcd_dma.rotate || lcd_dma.mirror ||
+             lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale))
+               return;
+
+       w = omap_readw(OMAP1610_DMA_LCD_CCR);
+       /* Set the double-indexed addressing mode */
+       w |= (0x03 << 12);
+       omap_writew(w, OMAP1610_DMA_LCD_CCR);
+
+       omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1);
+       omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U);
+       omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L);
+}
+
+static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u16 w;
+
+       w = omap_readw(OMAP1610_DMA_LCD_CTRL);
+       if (unlikely(!(w & (1 << 3)))) {
+               printk(KERN_WARNING "Spurious LCD DMA IRQ\n");
+               return IRQ_NONE;
+       }
+       /* Ack the IRQ */
+       w |= (1 << 3);
+       omap_writew(w, OMAP1610_DMA_LCD_CTRL);
+       lcd_dma.active = 0;
+       if (lcd_dma.callback != NULL)
+               lcd_dma.callback(w, lcd_dma.cb_data);
+
+       return IRQ_HANDLED;
+}
+
+int omap_request_lcd_dma(void (* callback)(u16 status, void *data),
+                        void *data)
+{
+       spin_lock_irq(&lcd_dma.lock);
+       if (lcd_dma.reserved) {
+               spin_unlock_irq(&lcd_dma.lock);
+               printk(KERN_ERR "LCD DMA channel already reserved\n");
+               BUG();
+               return -EBUSY;
+       }
+       lcd_dma.reserved = 1;
+       spin_unlock_irq(&lcd_dma.lock);
+       lcd_dma.callback = callback;
+       lcd_dma.cb_data = data;
+       lcd_dma.active = 0;
+       lcd_dma.single_transfer = 0;
+       lcd_dma.rotate = 0;
+       lcd_dma.vxres = 0;
+       lcd_dma.mirror = 0;
+       lcd_dma.xscale = 0;
+       lcd_dma.yscale = 0;
+       lcd_dma.ext_ctrl = 0;
+       lcd_dma.src_port = 0;
+
+       return 0;
+}
+
+void omap_free_lcd_dma(void)
+{
+       spin_lock(&lcd_dma.lock);
+       if (!lcd_dma.reserved) {
+               spin_unlock(&lcd_dma.lock);
+               printk(KERN_ERR "LCD DMA is not reserved\n");
+               BUG();
+               return;
+       }
+       if (!enable_1510_mode)
+               omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR);
+       lcd_dma.reserved = 0;
+       spin_unlock(&lcd_dma.lock);
+}
+
+void omap_enable_lcd_dma(void)
+{
+       u16 w;
+
+       /* Set the Enable bit only if an external controller is
+        * connected. Otherwise the OMAP internal controller will
+        * start the transfer when it gets enabled.
+        */
+       if (enable_1510_mode || !lcd_dma.ext_ctrl)
+               return;
+       w = omap_readw(OMAP1610_DMA_LCD_CCR);
+       w |= 1 << 7;
+       omap_writew(w, OMAP1610_DMA_LCD_CCR);
+       lcd_dma.active = 1;
+}
+
+void omap_setup_lcd_dma(void)
+{
+       BUG_ON(lcd_dma.active);
+       if (!enable_1510_mode) {
+               /* Set some reasonable defaults */
+               omap_writew(0x5440, OMAP1610_DMA_LCD_CCR);
+               omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP);
+               omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL);
+       }
+       set_b1_regs();
+       if (!enable_1510_mode) {
+               u16 w;
+
+               w = omap_readw(OMAP1610_DMA_LCD_CCR);
+               /* If DMA was already active set the end_prog bit to have
+                * the programmed register set loaded into the active
+                * register set.
+                */
+               w |= 1 << 11;           /* End_prog */
+               if (!lcd_dma.single_transfer)
+                       w |= (3 << 8);  /* Auto_init, repeat */
+               omap_writew(w, OMAP1610_DMA_LCD_CCR);
+       }
+}
+
+void omap_stop_lcd_dma(void)
+{
+       lcd_dma.active = 0;
+       if (!enable_1510_mode && lcd_dma.ext_ctrl)
+               omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~(1 << 7),
+                           OMAP1610_DMA_LCD_CCR);
+}
+
+/*
+ * Clears any DMA state so the DMA engine is ready to restart with new buffers
+ * through omap_start_dma(). Any buffers in flight are discarded.
+ */
+void omap_clear_dma(int lch)
+{
+       unsigned long flags;
+       int status;
+
+       local_irq_save(flags);
+       omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN,
+                   OMAP_DMA_CCR(lch));
+       status = OMAP_DMA_CSR(lch);     /* clear pending interrupts */
+       local_irq_restore(flags);
+}
+
+/*
+ * Returns current physical source address for the given DMA channel.
+ * If the channel is running the caller must disable interrupts prior calling
+ * this function and process the returned value before re-enabling interrupt to
+ * prevent races with the interrupt handler. Note that in continuous mode there
+ * is a chance for CSSA_L register overflow inbetween the two reads resulting
+ * in incorrect return value.
+ */
+dma_addr_t omap_get_dma_src_pos(int lch)
+{
+       return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) |
+                            (OMAP_DMA_CSSA_U(lch) << 16));
+}
+
+/*
+ * Returns current physical destination address for the given DMA channel.
+ * If the channel is running the caller must disable interrupts prior calling
+ * this function and process the returned value before re-enabling interrupt to
+ * prevent races with the interrupt handler. Note that in continuous mode there
+ * is a chance for CDSA_L register overflow inbetween the two reads resulting
+ * in incorrect return value.
+ */
+dma_addr_t omap_get_dma_dst_pos(int lch)
+{
+       return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) |
+                            (OMAP_DMA_CDSA_U(lch) << 16));
+}
+
+static int __init omap_init_dma(void)
+{
+       int ch, r;
+
+       if (cpu_is_omap1510()) {
+               printk(KERN_INFO "DMA support for OMAP1510 initialized\n");
+               dma_chan_count = 9;
+               enable_1510_mode = 1;
+       } else if (cpu_is_omap16xx() || cpu_is_omap730()) {
+               printk(KERN_INFO "OMAP DMA hardware version %d\n",
+                      omap_readw(OMAP_DMA_HW_ID));
+               printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n",
+                      (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L),
+                      (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L),
+                      omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3),
+                      omap_readw(OMAP_DMA_CAPS_4));
+               if (!enable_1510_mode) {
+                       u16 w;
+
+                       /* Disable OMAP 3.0/3.1 compatibility mode. */
+                       w = omap_readw(OMAP_DMA_GSCR);
+                       w |= 1 << 3;
+                       omap_writew(w, OMAP_DMA_GSCR);
+                       dma_chan_count = 16;
+               } else
+                       dma_chan_count = 9;
+       } else {
+               dma_chan_count = 0;
+               return 0;
+       }
+
+       memset(&lcd_dma, 0, sizeof(lcd_dma));
+       spin_lock_init(&lcd_dma.lock);
+       spin_lock_init(&dma_chan_lock);
+       memset(&dma_chan, 0, sizeof(dma_chan));
+
+       for (ch = 0; ch < dma_chan_count; ch++) {
+               dma_chan[ch].dev_id = -1;
+               dma_chan[ch].next_lch = -1;
+
+               if (ch >= 6 && enable_1510_mode)
+                       continue;
+
+               /* request_irq() doesn't like dev_id (ie. ch) being zero,
+                * so we have to kludge around this. */
+               r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA",
+                               (void *) (ch + 1));
+               if (r != 0) {
+                       int i;
+
+                       printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n",
+                              dma_irq[ch], r);
+                       for (i = 0; i < ch; i++)
+                               free_irq(dma_irq[i], (void *) (i + 1));
+                       return r;
+               }
+       }
+       r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL);
+       if (r != 0) {
+               int i;
+
+               printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r);
+               for (i = 0; i < dma_chan_count; i++)
+                       free_irq(dma_irq[i], (void *) (i + 1));
+               return r;
+       }
+       return 0;
+}
+
+arch_initcall(omap_init_dma);
+
+
+EXPORT_SYMBOL(omap_get_dma_src_pos);
+EXPORT_SYMBOL(omap_get_dma_dst_pos);
+EXPORT_SYMBOL(omap_clear_dma);
+EXPORT_SYMBOL(omap_set_dma_priority);
+EXPORT_SYMBOL(omap_request_dma);
+EXPORT_SYMBOL(omap_free_dma);
+EXPORT_SYMBOL(omap_start_dma);
+EXPORT_SYMBOL(omap_stop_dma);
+EXPORT_SYMBOL(omap_enable_dma_irq);
+EXPORT_SYMBOL(omap_disable_dma_irq);
+
+EXPORT_SYMBOL(omap_set_dma_transfer_params);
+EXPORT_SYMBOL(omap_set_dma_color_mode);
+
+EXPORT_SYMBOL(omap_set_dma_src_params);
+EXPORT_SYMBOL(omap_set_dma_src_index);
+EXPORT_SYMBOL(omap_set_dma_src_data_pack);
+EXPORT_SYMBOL(omap_set_dma_src_burst_mode);
+
+EXPORT_SYMBOL(omap_set_dma_dest_params);
+EXPORT_SYMBOL(omap_set_dma_dest_index);
+EXPORT_SYMBOL(omap_set_dma_dest_data_pack);
+EXPORT_SYMBOL(omap_set_dma_dest_burst_mode);
+
+EXPORT_SYMBOL(omap_dma_link_lch);
+EXPORT_SYMBOL(omap_dma_unlink_lch);
+
+EXPORT_SYMBOL(omap_request_lcd_dma);
+EXPORT_SYMBOL(omap_free_lcd_dma);
+EXPORT_SYMBOL(omap_enable_lcd_dma);
+EXPORT_SYMBOL(omap_setup_lcd_dma);
+EXPORT_SYMBOL(omap_stop_lcd_dma);
+EXPORT_SYMBOL(omap_set_lcd_dma_b1);
+EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer);
+EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller);
+EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation);
+EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres);
+EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale);
+EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror);
+
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c
new file mode 100644 (file)
index 0000000..1c85b4e
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ *  linux/arch/arm/plat-omap/gpio.c
+ *
+ * Support functions for OMAP GPIO
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * 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/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+#include <asm/mach/irq.h>
+
+#include <asm/io.h>
+
+/*
+ * OMAP1510 GPIO registers
+ */
+#define OMAP1510_GPIO_BASE             0xfffce000
+#define OMAP1510_GPIO_DATA_INPUT       0x00
+#define OMAP1510_GPIO_DATA_OUTPUT      0x04
+#define OMAP1510_GPIO_DIR_CONTROL      0x08
+#define OMAP1510_GPIO_INT_CONTROL      0x0c
+#define OMAP1510_GPIO_INT_MASK         0x10
+#define OMAP1510_GPIO_INT_STATUS       0x14
+#define OMAP1510_GPIO_PIN_CONTROL      0x18
+
+#define OMAP1510_IH_GPIO_BASE          64
+
+/*
+ * OMAP1610 specific GPIO registers
+ */
+#define OMAP1610_GPIO1_BASE            0xfffbe400
+#define OMAP1610_GPIO2_BASE            0xfffbec00
+#define OMAP1610_GPIO3_BASE            0xfffbb400
+#define OMAP1610_GPIO4_BASE            0xfffbbc00
+#define OMAP1610_GPIO_REVISION         0x0000
+#define OMAP1610_GPIO_SYSCONFIG                0x0010
+#define OMAP1610_GPIO_SYSSTATUS                0x0014
+#define OMAP1610_GPIO_IRQSTATUS1       0x0018
+#define OMAP1610_GPIO_IRQENABLE1       0x001c
+#define OMAP1610_GPIO_DATAIN           0x002c
+#define OMAP1610_GPIO_DATAOUT          0x0030
+#define OMAP1610_GPIO_DIRECTION                0x0034
+#define OMAP1610_GPIO_EDGE_CTRL1       0x0038
+#define OMAP1610_GPIO_EDGE_CTRL2       0x003c
+#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c
+#define OMAP1610_GPIO_CLEAR_DATAOUT    0x00b0
+#define OMAP1610_GPIO_SET_IRQENABLE1   0x00dc
+#define OMAP1610_GPIO_SET_DATAOUT      0x00f0
+
+/*
+ * OMAP730 specific GPIO registers
+ */
+#define OMAP730_GPIO1_BASE             0xfffbc000
+#define OMAP730_GPIO2_BASE             0xfffbc800
+#define OMAP730_GPIO3_BASE             0xfffbd000
+#define OMAP730_GPIO4_BASE             0xfffbd800
+#define OMAP730_GPIO5_BASE             0xfffbe000
+#define OMAP730_GPIO6_BASE             0xfffbe800
+#define OMAP730_GPIO_DATA_INPUT                0x00
+#define OMAP730_GPIO_DATA_OUTPUT       0x04
+#define OMAP730_GPIO_DIR_CONTROL       0x08
+#define OMAP730_GPIO_INT_CONTROL       0x0c
+#define OMAP730_GPIO_INT_MASK          0x10
+#define OMAP730_GPIO_INT_STATUS                0x14
+
+#define OMAP_MPUIO_MASK                (~OMAP_MAX_GPIO_LINES & 0xff)
+
+struct gpio_bank {
+       u32 base;
+       u16 irq;
+       u16 virtual_irq_start;
+       u8 method;
+       u32 reserved_map;
+       spinlock_t lock;
+};
+
+#define METHOD_MPUIO           0
+#define METHOD_GPIO_1510       1
+#define METHOD_GPIO_1610       2
+#define METHOD_GPIO_730                3
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+static struct gpio_bank gpio_bank_1610[5] = {
+       { OMAP_MPUIO_BASE,     INT_MPUIO,           IH_MPUIO_BASE,     METHOD_MPUIO},
+       { OMAP1610_GPIO1_BASE, INT_GPIO_BANK1,      IH_GPIO_BASE,      METHOD_GPIO_1610 },
+       { OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 },
+       { OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 },
+       { OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 },
+};
+#endif
+
+#ifdef CONFIG_ARCH_OMAP1510
+static struct gpio_bank gpio_bank_1510[2] = {
+       { OMAP_MPUIO_BASE,    INT_MPUIO,      IH_MPUIO_BASE, METHOD_MPUIO },
+       { OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE,  METHOD_GPIO_1510 }
+};
+#endif
+
+#ifdef CONFIG_ARCH_OMAP730
+static struct gpio_bank gpio_bank_730[7] = {
+       { OMAP_MPUIO_BASE,     INT_730_MPUIO,       IH_MPUIO_BASE,      METHOD_MPUIO },
+       { OMAP730_GPIO1_BASE,  INT_730_GPIO_BANK1,  IH_GPIO_BASE,       METHOD_GPIO_730 },
+       { OMAP730_GPIO2_BASE,  INT_730_GPIO_BANK2,  IH_GPIO_BASE + 32,  METHOD_GPIO_730 },
+       { OMAP730_GPIO3_BASE,  INT_730_GPIO_BANK3,  IH_GPIO_BASE + 64,  METHOD_GPIO_730 },
+       { OMAP730_GPIO4_BASE,  INT_730_GPIO_BANK4,  IH_GPIO_BASE + 96,  METHOD_GPIO_730 },
+       { OMAP730_GPIO5_BASE,  INT_730_GPIO_BANK5,  IH_GPIO_BASE + 128, METHOD_GPIO_730 },
+       { OMAP730_GPIO6_BASE,  INT_730_GPIO_BANK6,  IH_GPIO_BASE + 160, METHOD_GPIO_730 },
+};
+#endif
+
+static struct gpio_bank *gpio_bank;
+static int gpio_bank_count;
+
+static inline struct gpio_bank *get_gpio_bank(int gpio)
+{
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               if (OMAP_GPIO_IS_MPUIO(gpio))
+                       return &gpio_bank[0];
+               return &gpio_bank[1];
+       }
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+       if (cpu_is_omap16xx()) {
+               if (OMAP_GPIO_IS_MPUIO(gpio))
+                       return &gpio_bank[0];
+               return &gpio_bank[1 + (gpio >> 4)];
+       }
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+       if (cpu_is_omap730()) {
+               if (OMAP_GPIO_IS_MPUIO(gpio))
+                       return &gpio_bank[0];
+               return &gpio_bank[1 + (gpio >> 5)];
+       }
+#endif
+}
+
+static inline int get_gpio_index(int gpio)
+{
+       if (cpu_is_omap730())
+               return gpio & 0x1f;
+       else
+               return gpio & 0x0f;
+}
+
+static inline int gpio_valid(int gpio)
+{
+       if (gpio < 0)
+               return -1;
+       if (OMAP_GPIO_IS_MPUIO(gpio)) {
+               if ((gpio & OMAP_MPUIO_MASK) > 16)
+                       return -1;
+               return 0;
+       }
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510() && gpio < 16)
+               return 0;
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+       if ((cpu_is_omap16xx()) && gpio < 64)
+               return 0;
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+       if (cpu_is_omap730() && gpio < 192)
+               return 0;
+#endif
+       return -1;
+}
+
+static int check_gpio(int gpio)
+{
+       if (unlikely(gpio_valid(gpio)) < 0) {
+               printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio);
+               dump_stack();
+               return -1;
+       }
+       return 0;
+}
+
+static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
+{
+       u32 reg = bank->base;
+       u32 l;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_IO_CNTL;
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_DIR_CONTROL;
+               break;
+       case METHOD_GPIO_1610:
+               reg += OMAP1610_GPIO_DIRECTION;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_DIR_CONTROL;
+               break;
+       }
+       l = __raw_readl(reg);
+       if (is_input)
+               l |= 1 << gpio;
+       else
+               l &= ~(1 << gpio);
+       __raw_writel(l, reg);
+}
+
+void omap_set_gpio_direction(int gpio, int is_input)
+{
+       struct gpio_bank *bank;
+
+       if (check_gpio(gpio) < 0)
+               return;
+       bank = get_gpio_bank(gpio);
+       spin_lock(&bank->lock);
+       _set_gpio_direction(bank, get_gpio_index(gpio), is_input);
+       spin_unlock(&bank->lock);
+}
+
+static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
+{
+       u32 reg = bank->base;
+       u32 l = 0;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_OUTPUT;
+               l = __raw_readl(reg);
+               if (enable)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_DATA_OUTPUT;
+               l = __raw_readl(reg);
+               if (enable)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               break;
+       case METHOD_GPIO_1610:
+               if (enable)
+                       reg += OMAP1610_GPIO_SET_DATAOUT;
+               else
+                       reg += OMAP1610_GPIO_CLEAR_DATAOUT;
+               l = 1 << gpio;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_DATA_OUTPUT;
+               l = __raw_readl(reg);
+               if (enable)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               break;
+       default:
+               BUG();
+               return;
+       }
+       __raw_writel(l, reg);
+}
+
+void omap_set_gpio_dataout(int gpio, int enable)
+{
+       struct gpio_bank *bank;
+
+       if (check_gpio(gpio) < 0)
+               return;
+       bank = get_gpio_bank(gpio);
+       spin_lock(&bank->lock);
+       _set_gpio_dataout(bank, get_gpio_index(gpio), enable);
+       spin_unlock(&bank->lock);
+}
+
+int omap_get_gpio_datain(int gpio)
+{
+       struct gpio_bank *bank;
+       u32 reg;
+
+       if (check_gpio(gpio) < 0)
+               return -1;
+       bank = get_gpio_bank(gpio);
+       reg = bank->base;
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_INPUT_LATCH;
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_DATA_INPUT;
+               break;
+       case METHOD_GPIO_1610:
+               reg += OMAP1610_GPIO_DATAIN;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_DATA_INPUT;
+               break;
+       default:
+               BUG();
+               return -1;
+       }
+       return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0;
+}
+
+static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge)
+{
+       u32 reg = bank->base;
+       u32 l;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_GPIO_INT_EDGE;
+               l = __raw_readl(reg);
+               if (edge == OMAP_GPIO_RISING_EDGE)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               __raw_writel(l, reg);
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_INT_CONTROL;
+               l = __raw_readl(reg);
+               if (edge == OMAP_GPIO_RISING_EDGE)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               __raw_writel(l, reg);
+               break;
+       case METHOD_GPIO_1610:
+               edge &= 0x03;
+               if (gpio & 0x08)
+                       reg += OMAP1610_GPIO_EDGE_CTRL2;
+               else
+                       reg += OMAP1610_GPIO_EDGE_CTRL1;
+               gpio &= 0x07;
+               l = __raw_readl(reg);
+               l &= ~(3 << (gpio << 1));
+               l |= edge << (gpio << 1);
+               __raw_writel(l, reg);
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_INT_CONTROL;
+               l = __raw_readl(reg);
+               if (edge == OMAP_GPIO_RISING_EDGE)
+                       l |= 1 << gpio;
+               else
+                       l &= ~(1 << gpio);
+               __raw_writel(l, reg);
+               break;
+       default:
+               BUG();
+               return;
+       }
+}
+
+void omap_set_gpio_edge_ctrl(int gpio, int edge)
+{
+       struct gpio_bank *bank;
+
+       if (check_gpio(gpio) < 0)
+               return;
+       bank = get_gpio_bank(gpio);
+       spin_lock(&bank->lock);
+       _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge);
+       spin_unlock(&bank->lock);
+}
+
+
+static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio)
+{
+       u32 reg = bank->base, l;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE);
+               return (l & (1 << gpio)) ?
+                       OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
+       case METHOD_GPIO_1510:
+               l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL);
+               return (l & (1 << gpio)) ?
+                       OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
+       case METHOD_GPIO_1610:
+               if (gpio & 0x08)
+                       reg += OMAP1610_GPIO_EDGE_CTRL2;
+               else
+                       reg += OMAP1610_GPIO_EDGE_CTRL1;
+               return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03;
+       case METHOD_GPIO_730:
+               l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL);
+               return (l & (1 << gpio)) ?
+                       OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
+       default:
+               BUG();
+               return -1;
+       }
+}
+
+static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
+{
+       u32 reg = bank->base;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               /* MPUIO irqstatus is reset by reading the status register,
+                * so do nothing here */
+               return;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_INT_STATUS;
+               break;
+       case METHOD_GPIO_1610:
+               reg += OMAP1610_GPIO_IRQSTATUS1;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_INT_STATUS;
+               break;
+       default:
+               BUG();
+               return;
+       }
+       __raw_writel(gpio_mask, reg);
+}
+
+static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
+{
+       _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio));
+}
+
+static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable)
+{
+       u32 reg = bank->base;
+       u32 l;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_GPIO_MASKIT;
+               l = __raw_readl(reg);
+               if (enable)
+                       l &= ~(gpio_mask);
+               else
+                       l |= gpio_mask;
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_INT_MASK;
+               l = __raw_readl(reg);
+               if (enable)
+                       l &= ~(gpio_mask);
+               else
+                       l |= gpio_mask;
+               break;
+       case METHOD_GPIO_1610:
+               if (enable)
+                       reg += OMAP1610_GPIO_SET_IRQENABLE1;
+               else
+                       reg += OMAP1610_GPIO_CLEAR_IRQENABLE1;
+               l = gpio_mask;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_INT_MASK;
+               l = __raw_readl(reg);
+               if (enable)
+                       l &= ~(gpio_mask);
+               else
+                       l |= gpio_mask;
+               break;
+       default:
+               BUG();
+               return;
+       }
+       __raw_writel(l, reg);
+}
+
+static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
+{
+       _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable);
+}
+
+int omap_request_gpio(int gpio)
+{
+       struct gpio_bank *bank;
+
+       if (check_gpio(gpio) < 0)
+               return -EINVAL;
+
+       bank = get_gpio_bank(gpio);
+       spin_lock(&bank->lock);
+       if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) {
+               printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio);
+               dump_stack();
+               spin_unlock(&bank->lock);
+               return -1;
+       }
+       bank->reserved_map |= (1 << get_gpio_index(gpio));
+#ifdef CONFIG_ARCH_OMAP1510
+       if (bank->method == METHOD_GPIO_1510) {
+               u32 reg;
+
+               /* Claim the pin for the ARM */
+               reg = bank->base + OMAP1510_GPIO_PIN_CONTROL;
+               __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg);
+       }
+#endif
+       spin_unlock(&bank->lock);
+
+       return 0;
+}
+
+void omap_free_gpio(int gpio)
+{
+       struct gpio_bank *bank;
+
+       if (check_gpio(gpio) < 0)
+               return;
+       bank = get_gpio_bank(gpio);
+       spin_lock(&bank->lock);
+       if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) {
+               printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio);
+               dump_stack();
+               spin_unlock(&bank->lock);
+               return;
+       }
+       bank->reserved_map &= ~(1 << get_gpio_index(gpio));
+       _set_gpio_direction(bank, get_gpio_index(gpio), 1);
+       _set_gpio_irqenable(bank, gpio, 0);
+       _clear_gpio_irqstatus(bank, gpio);
+       spin_unlock(&bank->lock);
+}
+
+/*
+ * We need to unmask the GPIO bank interrupt as soon as possible to
+ * avoid missing GPIO interrupts for other lines in the bank.
+ * Then we need to mask-read-clear-unmask the triggered GPIO lines
+ * in the bank to avoid missing nested interrupts for a GPIO line.
+ * If we wait to unmask individual GPIO lines in the bank after the
+ * line's interrupt handler has been run, we may miss some nested
+ * interrupts.
+ */
+static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
+                            struct pt_regs *regs)
+{
+       u32 isr_reg = 0;
+       u32 isr;
+       unsigned int gpio_irq;
+       struct gpio_bank *bank;
+
+       desc->chip->ack(irq);
+
+       bank = (struct gpio_bank *) desc->data;
+       if (bank->method == METHOD_MPUIO)
+               isr_reg = bank->base + OMAP_MPUIO_GPIO_INT;
+#ifdef CONFIG_ARCH_OMAP1510
+       if (bank->method == METHOD_GPIO_1510)
+               isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS;
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+       if (bank->method == METHOD_GPIO_1610)
+               isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1;
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+       if (bank->method == METHOD_GPIO_730)
+               isr_reg = bank->base + OMAP730_GPIO_INT_STATUS;
+#endif
+
+       isr = __raw_readl(isr_reg);
+       _enable_gpio_irqbank(bank, isr, 0);
+       _clear_gpio_irqbank(bank, isr);
+       _enable_gpio_irqbank(bank, isr, 1);
+       desc->chip->unmask(irq);
+
+       if (unlikely(!isr))
+               return;
+
+       gpio_irq = bank->virtual_irq_start;
+       for (; isr != 0; isr >>= 1, gpio_irq++) {
+               struct irqdesc *d;
+               if (!(isr & 1))
+                       continue;
+               d = irq_desc + gpio_irq;
+               d->handle(gpio_irq, d, regs);
+       }
+}
+
+static void gpio_ack_irq(unsigned int irq)
+{
+       unsigned int gpio = irq - IH_GPIO_BASE;
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       _clear_gpio_irqstatus(bank, gpio);
+}
+
+static void gpio_mask_irq(unsigned int irq)
+{
+       unsigned int gpio = irq - IH_GPIO_BASE;
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       _set_gpio_irqenable(bank, gpio, 0);
+}
+
+static void gpio_unmask_irq(unsigned int irq)
+{
+       unsigned int gpio = irq - IH_GPIO_BASE;
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) {
+               printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n",
+                      gpio);
+               _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE);
+       }
+       _set_gpio_irqenable(bank, gpio, 1);
+}
+
+static void mpuio_ack_irq(unsigned int irq)
+{
+       /* The ISR is reset automatically, so do nothing here. */
+}
+
+static void mpuio_mask_irq(unsigned int irq)
+{
+       unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE);
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       _set_gpio_irqenable(bank, gpio, 0);
+}
+
+static void mpuio_unmask_irq(unsigned int irq)
+{
+       unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE);
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       _set_gpio_irqenable(bank, gpio, 1);
+}
+
+static struct irqchip gpio_irq_chip = {
+       .ack    = gpio_ack_irq,
+       .mask   = gpio_mask_irq,
+       .unmask = gpio_unmask_irq,
+};
+
+static struct irqchip mpuio_irq_chip = {
+       .ack    = mpuio_ack_irq,
+       .mask   = mpuio_mask_irq,
+       .unmask = mpuio_unmask_irq
+};
+
+static int initialized = 0;
+
+static int __init _omap_gpio_init(void)
+{
+       int i;
+       struct gpio_bank *bank;
+
+       initialized = 1;
+
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               printk(KERN_INFO "OMAP1510 GPIO hardware\n");
+               gpio_bank_count = 2;
+               gpio_bank = gpio_bank_1510;
+       }
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+       if (cpu_is_omap16xx()) {
+               int rev;
+
+               gpio_bank_count = 5;
+               gpio_bank = gpio_bank_1610;
+               rev = omap_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION);
+               printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n",
+                      (rev >> 4) & 0x0f, rev & 0x0f);
+       }
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+       if (cpu_is_omap730()) {
+               printk(KERN_INFO "OMAP730 GPIO hardware\n");
+               gpio_bank_count = 7;
+               gpio_bank = gpio_bank_730;
+       }
+#endif
+       for (i = 0; i < gpio_bank_count; i++) {
+               int j, gpio_count = 16;
+
+               bank = &gpio_bank[i];
+               bank->reserved_map = 0;
+               bank->base = IO_ADDRESS(bank->base);
+               spin_lock_init(&bank->lock);
+               if (bank->method == METHOD_MPUIO) {
+                       omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT);
+               }
+#ifdef CONFIG_ARCH_OMAP1510
+               if (bank->method == METHOD_GPIO_1510) {
+                       __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK);
+                       __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS);
+               }
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+               if (bank->method == METHOD_GPIO_1610) {
+                       __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1);
+                       __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1);
+               }
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+               if (bank->method == METHOD_GPIO_730) {
+                       __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK);
+                       __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS);
+
+                       gpio_count = 32; /* 730 has 32-bit GPIOs */
+               }
+#endif
+               for (j = bank->virtual_irq_start;
+                    j < bank->virtual_irq_start + gpio_count; j++) {
+                       if (bank->method == METHOD_MPUIO)
+                               set_irq_chip(j, &mpuio_irq_chip);
+                       else
+                               set_irq_chip(j, &gpio_irq_chip);
+                       set_irq_handler(j, do_simple_IRQ);
+                       set_irq_flags(j, IRQF_VALID);
+               }
+               set_irq_chained_handler(bank->irq, gpio_irq_handler);
+               set_irq_data(bank->irq, bank);
+       }
+
+       /* Enable system clock for GPIO module.
+        * The CAM_CLK_CTRL *is* really the right place. */
+       if (cpu_is_omap1610() || cpu_is_omap1710())
+               omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL);
+
+       return 0;
+}
+
+/*
+ * This may get called early from board specific init
+ */
+int omap_gpio_init(void)
+{
+       if (!initialized)
+               return _omap_gpio_init();
+       else
+               return 0;
+}
+
+EXPORT_SYMBOL(omap_request_gpio);
+EXPORT_SYMBOL(omap_free_gpio);
+EXPORT_SYMBOL(omap_set_gpio_direction);
+EXPORT_SYMBOL(omap_set_gpio_dataout);
+EXPORT_SYMBOL(omap_get_gpio_datain);
+EXPORT_SYMBOL(omap_set_gpio_edge_ctrl);
+
+arch_initcall(omap_gpio_init);
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
new file mode 100644 (file)
index 0000000..10c3f22
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * linux/arch/arm/plat-omap/mcbsp.c
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
+ *
+ *
+ * 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.
+ *
+ * Multichannel mode not supported.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/mcbsp.h>
+
+#include <asm/hardware/clock.h>
+
+#ifdef CONFIG_MCBSP_DEBUG
+#define DBG(x...)      printk(x)
+#else
+#define DBG(x...)      do { } while (0)
+#endif
+
+struct omap_mcbsp {
+       u32                          io_base;
+       u8                           id;
+       u8                           free;
+       omap_mcbsp_word_length       rx_word_length;
+       omap_mcbsp_word_length       tx_word_length;
+
+       /* IRQ based TX/RX */
+       int                          rx_irq;
+       int                          tx_irq;
+
+       /* DMA stuff */
+       u8                           dma_rx_sync;
+       short                        dma_rx_lch;
+       u8                           dma_tx_sync;
+       short                        dma_tx_lch;
+
+       /* Completion queues */
+       struct completion            tx_irq_completion;
+       struct completion            rx_irq_completion;
+       struct completion            tx_dma_completion;
+       struct completion            rx_dma_completion;
+
+       spinlock_t                   lock;
+};
+
+static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT];
+static struct clk *mcbsp_dsp_ck = 0;
+static struct clk *mcbsp_api_ck = 0;
+
+
+static void omap_mcbsp_dump_reg(u8 id)
+{
+       DBG("**** MCBSP%d regs ****\n", mcbsp[id].id);
+       DBG("DRR2:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2));
+       DBG("DRR1:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1));
+       DBG("DXR2:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2));
+       DBG("DXR1:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1));
+       DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2));
+       DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1));
+       DBG("RCR2:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2));
+       DBG("RCR1:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1));
+       DBG("XCR2:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2));
+       DBG("XCR1:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1));
+       DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2));
+       DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1));
+       DBG("PCR0:  0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0));
+       DBG("***********************\n");
+}
+
+
+static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id);
+
+       DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2));
+
+       complete(&mcbsp_tx->tx_irq_completion);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id);
+
+       DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2));
+
+       complete(&mcbsp_rx->rx_irq_completion);
+       return IRQ_HANDLED;
+}
+
+
+static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
+{
+       struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data);
+
+       DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2));
+
+       /* We can free the channels */
+       omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
+       mcbsp_dma_tx->dma_tx_lch = -1;
+
+       complete(&mcbsp_dma_tx->tx_dma_completion);
+}
+
+static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
+{
+       struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data);
+
+       DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2));
+
+       /* We can free the channels */
+       omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
+       mcbsp_dma_rx->dma_rx_lch = -1;
+
+       complete(&mcbsp_dma_rx->rx_dma_completion);
+}
+
+
+/*
+ * omap_mcbsp_config simply write a config to the
+ * appropriate McBSP.
+ * You either call this function or set the McBSP registers
+ * by yourself before calling omap_mcbsp_start().
+ */
+
+void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config)
+{
+       u32 io_base = mcbsp[id].io_base;
+
+       DBG("OMAP-McBSP: McBSP%d  io_base: 0x%8x\n", id+1, io_base);
+
+       /* We write the given config */
+       OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2);
+       OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1);
+       OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2);
+       OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1);
+       OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2);
+       OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1);
+       OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2);
+       OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1);
+       OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2);
+       OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1);
+       OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0);
+}
+
+
+
+static int omap_mcbsp_check(unsigned int id)
+{
+       if (cpu_is_omap730()) {
+               if (id > OMAP_MAX_MCBSP_COUNT - 1) {
+                      printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1);
+                      return -1;
+               }
+               return 0;
+       }
+
+       if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) {
+               if (id > OMAP_MAX_MCBSP_COUNT) {
+                       printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1);
+                       return -1;
+               }
+               return 0;
+       }
+
+       return -1;
+}
+
+#define EN_XORPCK              1
+#define DSP_RSTCT2              0xe1008014
+
+static void omap_mcbsp_dsp_request(void)
+{
+       if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) {
+               omap_writew((omap_readw(ARM_RSTCT1) | (1 << 1) | (1 << 2)),
+                           ARM_RSTCT1);
+               clk_enable(mcbsp_dsp_ck);
+               clk_enable(mcbsp_api_ck);
+
+               /* enable 12MHz clock to mcbsp 1 & 3 */
+               __raw_writew(__raw_readw(DSP_IDLECT2) | (1 << EN_XORPCK),
+                            DSP_IDLECT2);
+               __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1,
+                            DSP_RSTCT2);
+       }
+}
+
+static void omap_mcbsp_dsp_free(void)
+{
+       /* Useless for now */
+}
+
+
+int omap_mcbsp_request(unsigned int id)
+{
+       int err;
+
+       if (omap_mcbsp_check(id) < 0)
+               return -EINVAL;
+
+       /*
+        * On 1510, 1610 and 1710, McBSP1 and McBSP3
+        * are DSP public peripherals.
+        */
+       if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3)
+               omap_mcbsp_dsp_request();
+
+       spin_lock(&mcbsp[id].lock);
+       if (!mcbsp[id].free) {
+               printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1);
+               spin_unlock(&mcbsp[id].lock);
+               return -1;
+       }
+
+       mcbsp[id].free = 0;
+       spin_unlock(&mcbsp[id].lock);
+
+       /* We need to get IRQs here */
+       err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0,
+                         "McBSP",
+                         (void *) (&mcbsp[id]));
+       if (err != 0) {
+               printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n",
+                      mcbsp[id].tx_irq, mcbsp[id].id);
+               return err;
+       }
+
+       init_completion(&(mcbsp[id].tx_irq_completion));
+
+
+       err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0,
+                         "McBSP",
+                         (void *) (&mcbsp[id]));
+       if (err != 0) {
+               printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n",
+                      mcbsp[id].rx_irq, mcbsp[id].id);
+               free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id]));
+               return err;
+       }
+
+       init_completion(&(mcbsp[id].rx_irq_completion));
+       return 0;
+
+}
+
+void omap_mcbsp_free(unsigned int id)
+{
+       if (omap_mcbsp_check(id) < 0)
+               return;
+
+       if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3)
+               omap_mcbsp_dsp_free();
+
+       spin_lock(&mcbsp[id].lock);
+       if (mcbsp[id].free) {
+               printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1);
+               spin_unlock(&mcbsp[id].lock);
+               return;
+       }
+
+       mcbsp[id].free = 1;
+       spin_unlock(&mcbsp[id].lock);
+
+       /* Free IRQs */
+       free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id]));
+       free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id]));
+}
+
+/*
+ * Here we start the McBSP, by enabling the sample
+ * generator, both transmitter and receivers,
+ * and the frame sync.
+ */
+void omap_mcbsp_start(unsigned int id)
+{
+       u32 io_base;
+       u16 w;
+
+       if (omap_mcbsp_check(id) < 0)
+               return;
+
+       io_base = mcbsp[id].io_base;
+
+       mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7);
+       mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7);
+
+       /* Start the sample generator */
+       w = OMAP_MCBSP_READ(io_base, SPCR2);
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
+
+       /* Enable transmitter and receiver */
+       w = OMAP_MCBSP_READ(io_base, SPCR2);
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1);
+
+       w = OMAP_MCBSP_READ(io_base, SPCR1);
+       OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
+
+       udelay(100);
+
+       /* Start frame sync */
+       w = OMAP_MCBSP_READ(io_base, SPCR2);
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
+
+       /* Dump McBSP Regs */
+       omap_mcbsp_dump_reg(id);
+
+}
+
+void omap_mcbsp_stop(unsigned int id)
+{
+       u32 io_base;
+       u16 w;
+
+       if (omap_mcbsp_check(id) < 0)
+               return;
+
+       io_base = mcbsp[id].io_base;
+
+        /* Reset transmitter */
+       w = OMAP_MCBSP_READ(io_base, SPCR2);
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1));
+
+       /* Reset receiver */
+       w = OMAP_MCBSP_READ(io_base, SPCR1);
+       OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1));
+
+       /* Reset the sample rate generator */
+       w = OMAP_MCBSP_READ(io_base, SPCR2);
+       OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
+}
+
+
+/*
+ * IRQ based word transmission.
+ */
+void omap_mcbsp_xmit_word(unsigned int id, u32 word)
+{
+       u32 io_base;
+       omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length;
+
+       if (omap_mcbsp_check(id) < 0)
+               return;
+
+       io_base = mcbsp[id].io_base;
+
+       wait_for_completion(&(mcbsp[id].tx_irq_completion));
+
+       if (word_length > OMAP_MCBSP_WORD_16)
+               OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16);
+       OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff);
+}
+
+u32 omap_mcbsp_recv_word(unsigned int id)
+{
+       u32 io_base;
+       u16 word_lsb, word_msb = 0;
+       omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length;
+
+       if (omap_mcbsp_check(id) < 0)
+               return -EINVAL;
+
+       io_base = mcbsp[id].io_base;
+
+       wait_for_completion(&(mcbsp[id].rx_irq_completion));
+
+       if (word_length > OMAP_MCBSP_WORD_16)
+               word_msb = OMAP_MCBSP_READ(io_base, DRR2);
+       word_lsb = OMAP_MCBSP_READ(io_base, DRR1);
+
+       return (word_lsb | (word_msb << 16));
+}
+
+
+/*
+ * Simple DMA based buffer rx/tx routines.
+ * Nothing fancy, just a single buffer tx/rx through DMA.
+ * The DMA resources are released once the transfer is done.
+ * For anything fancier, you should use your own customized DMA
+ * routines and callbacks.
+ */
+int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length)
+{
+       int dma_tx_ch;
+
+       if (omap_mcbsp_check(id) < 0)
+               return -EINVAL;
+
+       if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback,
+                            &mcbsp[id],
+                            &dma_tx_ch)) {
+               printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1);
+               return -EAGAIN;
+       }
+       mcbsp[id].dma_tx_lch = dma_tx_ch;
+
+       DBG("TX DMA on channel %d\n", dma_tx_ch);
+
+       init_completion(&(mcbsp[id].tx_dma_completion));
+
+       omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch,
+                                    OMAP_DMA_DATA_TYPE_S16,
+                                    length >> 1, 1,
+                                    OMAP_DMA_SYNC_ELEMENT);
+
+       omap_set_dma_dest_params(mcbsp[id].dma_tx_lch,
+                                OMAP_DMA_PORT_TIPB,
+                                OMAP_DMA_AMODE_CONSTANT,
+                                mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1);
+
+       omap_set_dma_src_params(mcbsp[id].dma_tx_lch,
+                               OMAP_DMA_PORT_EMIFF,
+                               OMAP_DMA_AMODE_POST_INC,
+                               buffer);
+
+       omap_start_dma(mcbsp[id].dma_tx_lch);
+       wait_for_completion(&(mcbsp[id].tx_dma_completion));
+       return 0;
+}
+
+
+int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length)
+{
+       int dma_rx_ch;
+
+       if (omap_mcbsp_check(id) < 0)
+               return -EINVAL;
+
+       if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback,
+                            &mcbsp[id],
+                            &dma_rx_ch)) {
+               printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1);
+               return -EAGAIN;
+       }
+       mcbsp[id].dma_rx_lch = dma_rx_ch;
+
+       DBG("RX DMA on channel %d\n", dma_rx_ch);
+
+       init_completion(&(mcbsp[id].rx_dma_completion));
+
+       omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch,
+                                    OMAP_DMA_DATA_TYPE_S16,
+                                    length >> 1, 1,
+                                    OMAP_DMA_SYNC_ELEMENT);
+
+       omap_set_dma_src_params(mcbsp[id].dma_rx_lch,
+                               OMAP_DMA_PORT_TIPB,
+                               OMAP_DMA_AMODE_CONSTANT,
+                               mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1);
+
+       omap_set_dma_dest_params(mcbsp[id].dma_rx_lch,
+                                OMAP_DMA_PORT_EMIFF,
+                                OMAP_DMA_AMODE_POST_INC,
+                                buffer);
+
+       omap_start_dma(mcbsp[id].dma_rx_lch);
+       wait_for_completion(&(mcbsp[id].rx_dma_completion));
+       return 0;
+}
+
+
+/*
+ * SPI wrapper.
+ * Since SPI setup is much simpler than the generic McBSP one,
+ * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
+ * Once this is done, you can call omap_mcbsp_start().
+ */
+void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg)
+{
+       struct omap_mcbsp_reg_cfg mcbsp_cfg;
+
+       if (omap_mcbsp_check(id) < 0)
+               return;
+
+       memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
+
+       /* SPI has only one frame */
+       mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
+       mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
+
+        /* Clock stop mode */
+       if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
+               mcbsp_cfg.spcr1 |= (1 << 12);
+       else
+               mcbsp_cfg.spcr1 |= (3 << 11);
+
+       /* Set clock parities */
+       if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
+               mcbsp_cfg.pcr0 |= CLKRP;
+       else
+               mcbsp_cfg.pcr0 &= ~CLKRP;
+
+       if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
+               mcbsp_cfg.pcr0 &= ~CLKXP;
+       else
+               mcbsp_cfg.pcr0 |= CLKXP;
+
+       /* Set SCLKME to 0 and CLKSM to 1 */
+       mcbsp_cfg.pcr0 &= ~SCLKME;
+       mcbsp_cfg.srgr2 |= CLKSM;
+
+       /* Set FSXP */
+       if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
+               mcbsp_cfg.pcr0 &= ~FSXP;
+       else
+               mcbsp_cfg.pcr0 |= FSXP;
+
+       if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
+               mcbsp_cfg.pcr0 |= CLKXM;
+               mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1);
+               mcbsp_cfg.pcr0 |= FSXM;
+               mcbsp_cfg.srgr2 &= ~FSGM;
+               mcbsp_cfg.xcr2 |= XDATDLY(1);
+               mcbsp_cfg.rcr2 |= RDATDLY(1);
+       }
+       else {
+               mcbsp_cfg.pcr0 &= ~CLKXM;
+               mcbsp_cfg.srgr1 |= CLKGDV(1);
+               mcbsp_cfg.pcr0 &= ~FSXM;
+               mcbsp_cfg.xcr2 &= ~XDATDLY(3);
+               mcbsp_cfg.rcr2 &= ~RDATDLY(3);
+       }
+
+       mcbsp_cfg.xcr2 &= ~XPHASE;
+       mcbsp_cfg.rcr2 &= ~RPHASE;
+
+       omap_mcbsp_config(id, &mcbsp_cfg);
+}
+
+
+/*
+ * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
+ * 730 has only 2 McBSP, and both of them are MPU peripherals.
+ */
+struct omap_mcbsp_info {
+       u32 virt_base;
+       u8 dma_rx_sync, dma_tx_sync;
+       u16 rx_irq, tx_irq;
+};
+
+#ifdef CONFIG_ARCH_OMAP730
+static const struct omap_mcbsp_info mcbsp_730[] = {
+       [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE),
+               .dma_rx_sync = OMAP_DMA_MCBSP1_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP1_TX,
+               .rx_irq = INT_730_McBSP1RX,
+               .tx_irq = INT_730_McBSP1TX },
+       [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE),
+               .dma_rx_sync = OMAP_DMA_MCBSP3_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP3_TX,
+               .rx_irq = INT_730_McBSP2RX,
+               .tx_irq = INT_730_McBSP2TX },
+};
+#endif
+
+#ifdef CONFIG_ARCH_OMAP1510
+static const struct omap_mcbsp_info mcbsp_1510[] = {
+       [0] = { .virt_base = OMAP1510_MCBSP1_BASE,
+               .dma_rx_sync = OMAP_DMA_MCBSP1_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP1_TX,
+               .rx_irq = INT_McBSP1RX,
+               .tx_irq = INT_McBSP1TX },
+       [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE),
+               .dma_rx_sync = OMAP_DMA_MCBSP2_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP2_TX,
+               .rx_irq = INT_1510_SPI_RX,
+               .tx_irq = INT_1510_SPI_TX },
+       [2] = { .virt_base = OMAP1510_MCBSP3_BASE,
+               .dma_rx_sync = OMAP_DMA_MCBSP3_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP3_TX,
+               .rx_irq = INT_McBSP3RX,
+               .tx_irq = INT_McBSP3TX },
+};
+#endif
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+static const struct omap_mcbsp_info mcbsp_1610[] = {
+       [0] = { .virt_base = OMAP1610_MCBSP1_BASE,
+               .dma_rx_sync = OMAP_DMA_MCBSP1_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP1_TX,
+               .rx_irq = INT_McBSP1RX,
+               .tx_irq = INT_McBSP1TX },
+       [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE),
+               .dma_rx_sync = OMAP_DMA_MCBSP2_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP2_TX,
+               .rx_irq = INT_1610_McBSP2_RX,
+               .tx_irq = INT_1610_McBSP2_TX },
+       [2] = { .virt_base = OMAP1610_MCBSP3_BASE,
+               .dma_rx_sync = OMAP_DMA_MCBSP3_RX,
+               .dma_tx_sync = OMAP_DMA_MCBSP3_TX,
+               .rx_irq = INT_McBSP3RX,
+               .tx_irq = INT_McBSP3TX },
+};
+#endif
+
+static int __init omap_mcbsp_init(void)
+{
+       int mcbsp_count = 0, i;
+       static const struct omap_mcbsp_info *mcbsp_info;
+
+       printk("Initializing OMAP McBSP system\n");
+
+       mcbsp_dsp_ck = clk_get(0, "dsp_ck");
+       if (IS_ERR(mcbsp_dsp_ck)) {
+               printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n");
+               return PTR_ERR(mcbsp_dsp_ck);
+       }
+       mcbsp_api_ck = clk_get(0, "api_ck");
+       if (IS_ERR(mcbsp_dsp_ck)) {
+               printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n");
+               return PTR_ERR(mcbsp_api_ck);
+       }
+
+#ifdef CONFIG_ARCH_OMAP730
+       if (cpu_is_omap730()) {
+               mcbsp_info = mcbsp_730;
+               mcbsp_count = ARRAY_SIZE(mcbsp_730);
+       }
+#endif
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               mcbsp_info = mcbsp_1510;
+               mcbsp_count = ARRAY_SIZE(mcbsp_1510);
+       }
+#endif
+#if defined(CONFIG_ARCH_OMAP16XX)
+       if (cpu_is_omap1610() || cpu_is_omap1710()) {
+               mcbsp_info = mcbsp_1610;
+               mcbsp_count = ARRAY_SIZE(mcbsp_1610);
+       }
+#endif
+       for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) {
+               if (i >= mcbsp_count) {
+                       mcbsp[i].io_base = 0;
+                       mcbsp[i].free = 0;
+                        continue;
+               }
+               mcbsp[i].id = i + 1;
+               mcbsp[i].free = 1;
+               mcbsp[i].dma_tx_lch = -1;
+               mcbsp[i].dma_rx_lch = -1;
+
+               mcbsp[i].io_base = mcbsp_info[i].virt_base;
+               mcbsp[i].tx_irq = mcbsp_info[i].tx_irq;
+               mcbsp[i].rx_irq = mcbsp_info[i].rx_irq;
+               mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync;
+               mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync;
+               spin_lock_init(&mcbsp[i].lock);
+       }
+
+       return 0;
+}
+
+
+arch_initcall(omap_mcbsp_init);
+
+EXPORT_SYMBOL(omap_mcbsp_config);
+EXPORT_SYMBOL(omap_mcbsp_request);
+EXPORT_SYMBOL(omap_mcbsp_free);
+EXPORT_SYMBOL(omap_mcbsp_start);
+EXPORT_SYMBOL(omap_mcbsp_stop);
+EXPORT_SYMBOL(omap_mcbsp_xmit_word);
+EXPORT_SYMBOL(omap_mcbsp_recv_word);
+EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
+EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
+EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
diff --git a/arch/arm/plat-omap/mux.c b/arch/arm/plat-omap/mux.c
new file mode 100644 (file)
index 0000000..cbecd10
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * linux/arch/arm/plat-omap/mux.c
+ *
+ * Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ *
+ * Written by Tony Lindgren <tony.lindgren@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/spinlock.h>
+
+#define __MUX_C__
+#include <asm/arch/mux.h>
+
+#ifdef CONFIG_OMAP_MUX
+
+/*
+ * Sets the Omap MUX and PULL_DWN registers based on the table
+ */
+int __init_or_module
+omap_cfg_reg(const reg_cfg_t reg_cfg)
+{
+       static DEFINE_SPINLOCK(mux_spin_lock);
+
+       unsigned long flags;
+       reg_cfg_set *cfg;
+       unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0,
+               pull_orig = 0, pull = 0;
+       unsigned int mask, warn = 0;
+
+       if (reg_cfg > ARRAY_SIZE(reg_cfg_table)) {
+               printk(KERN_ERR "MUX: reg_cfg %d\n", reg_cfg);
+               return -EINVAL;
+       }
+
+       cfg = &reg_cfg_table[reg_cfg];
+
+       /*
+        * We do a pretty long section here with lock on, but pin muxing
+        * should only happen on driver init for each driver, so it's not time
+        * critical.
+        */
+       spin_lock_irqsave(&mux_spin_lock, flags);
+
+       /* Check the mux register in question */
+       if (cfg->mux_reg) {
+               unsigned        tmp1, tmp2;
+
+               reg_orig = omap_readl(cfg->mux_reg);
+
+               /* The mux registers always seem to be 3 bits long */
+               mask = (0x7 << cfg->mask_offset);
+               tmp1 = reg_orig & mask;
+               reg = reg_orig & ~mask;
+
+               tmp2 = (cfg->mask << cfg->mask_offset);
+               reg |= tmp2;
+
+               if (tmp1 != tmp2)
+                       warn = 1;
+
+               omap_writel(reg, cfg->mux_reg);
+       }
+
+       /* Check for pull up or pull down selection on 1610 */
+       if (!cpu_is_omap1510()) {
+               if (cfg->pu_pd_reg && cfg->pull_val) {
+                       pu_pd_orig = omap_readl(cfg->pu_pd_reg);
+                       mask = 1 << cfg->pull_bit;
+
+                       if (cfg->pu_pd_val) {
+                               if (!(pu_pd_orig & mask))
+                                       warn = 1;
+                               /* Use pull up */
+                               pu_pd = pu_pd_orig | mask;
+                       } else {
+                               if (pu_pd_orig & mask)
+                                       warn = 1;
+                               /* Use pull down */
+                               pu_pd = pu_pd_orig & ~mask;
+                       }
+                       omap_writel(pu_pd, cfg->pu_pd_reg);
+               }
+       }
+
+       /* Check for an associated pull down register */
+       if (cfg->pull_reg) {
+               pull_orig = omap_readl(cfg->pull_reg);
+               mask = 1 << cfg->pull_bit;
+
+               if (cfg->pull_val) {
+                       if (pull_orig & mask)
+                               warn = 1;
+                       /* Low bit = pull enabled */
+                       pull = pull_orig & ~mask;
+               } else {
+                       if (!(pull_orig & mask))
+                               warn = 1;
+                       /* High bit = pull disabled */
+                       pull = pull_orig | mask;
+               }
+
+               omap_writel(pull, cfg->pull_reg);
+       }
+
+       if (warn) {
+#ifdef CONFIG_OMAP_MUX_WARNINGS
+               printk(KERN_WARNING "MUX: initialized %s\n", cfg->name);
+#endif
+       }
+
+#ifdef CONFIG_OMAP_MUX_DEBUG
+       if (cfg->debug || warn) {
+               printk("MUX: Setting register %s\n", cfg->name);
+               printk("      %s (0x%08x) = 0x%08x -> 0x%08x\n",
+                      cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg);
+
+               if (!cpu_is_omap1510()) {
+                       if (cfg->pu_pd_reg && cfg->pull_val) {
+                               printk("      %s (0x%08x) = 0x%08x -> 0x%08x\n",
+                                      cfg->pu_pd_name, cfg->pu_pd_reg,
+                                      pu_pd_orig, pu_pd);
+                       }
+               }
+
+               if (cfg->pull_reg)
+                       printk("      %s (0x%08x) = 0x%08x -> 0x%08x\n",
+                              cfg->pull_name, cfg->pull_reg, pull_orig, pull);
+       }
+#endif
+
+       spin_unlock_irqrestore(&mux_spin_lock, flags);
+
+#ifdef CONFIG_OMAP_MUX_ERRORS
+       return warn ? -ETXTBSY : 0;
+#else
+       return 0;
+#endif
+}
+
+EXPORT_SYMBOL(omap_cfg_reg);
+
+#endif /* CONFIG_OMAP_MUX */
diff --git a/arch/arm/plat-omap/ocpi.c b/arch/arm/plat-omap/ocpi.c
new file mode 100644 (file)
index 0000000..1fb16f9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * linux/arch/arm/plat-omap/ocpi.c
+ *
+ * Minimal OCP bus support for omap16xx
+ *
+ * Copyright (C) 2003 - 2005 Nokia Corporation
+ * Written by Tony Lindgren <tony@atomide.com>
+ *
+ * Modified for clock framework by Paul Mundt <paul.mundt@nokia.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/hardware/clock.h>
+#include <asm/arch/hardware.h>
+
+#define OCPI_BASE              0xfffec320
+#define OCPI_FAULT             (OCPI_BASE + 0x00)
+#define OCPI_CMD_FAULT         (OCPI_BASE + 0x04)
+#define OCPI_SINT0             (OCPI_BASE + 0x08)
+#define OCPI_TABORT            (OCPI_BASE + 0x0c)
+#define OCPI_SINT1             (OCPI_BASE + 0x10)
+#define OCPI_PROT              (OCPI_BASE + 0x14)
+#define OCPI_SEC               (OCPI_BASE + 0x18)
+
+/* USB OHCI OCPI access error registers */
+#define HOSTUEADDR     0xfffba0e0
+#define HOSTUESTATUS   0xfffba0e4
+
+static struct clk *ocpi_ck;
+
+/*
+ * Enables device access to OMAP buses via the OCPI bridge
+ * FIXME: Add locking
+ */
+int ocpi_enable(void)
+{
+       unsigned int val;
+
+       if (!cpu_is_omap16xx())
+               return -ENODEV;
+
+       /* Make sure there's clock for OCPI */
+       clk_enable(ocpi_ck);
+
+       /* Enable access for OHCI in OCPI */
+       val = omap_readl(OCPI_PROT);
+       val &= ~0xff;
+       //val &= (1 << 0);      /* Allow access only to EMIFS */
+       omap_writel(val, OCPI_PROT);
+
+       val = omap_readl(OCPI_SEC);
+       val &= ~0xff;
+       omap_writel(val, OCPI_SEC);
+
+       return 0;
+}
+EXPORT_SYMBOL(ocpi_enable);
+
+static int __init omap_ocpi_init(void)
+{
+       if (!cpu_is_omap16xx())
+               return -ENODEV;
+
+       ocpi_ck = clk_get(NULL, "l3_ocpi_ck");
+       if (IS_ERR(ocpi_ck))
+               return PTR_ERR(ocpi_ck);
+
+       clk_use(ocpi_ck);
+       ocpi_enable();
+       printk("OMAP OCPI interconnect driver loaded\n");
+
+       return 0;
+}
+
+static void __exit omap_ocpi_exit(void)
+{
+       /* REVISIT: Disable OCPI */
+
+       if (!cpu_is_omap16xx())
+               return;
+
+       clk_unuse(ocpi_ck);
+       clk_put(ocpi_ck);
+}
+
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("OMAP OCPI bus controller module");
+MODULE_LICENSE("GPL");
+module_init(omap_ocpi_init);
+module_exit(omap_ocpi_exit);
diff --git a/arch/arm/plat-omap/pm.c b/arch/arm/plat-omap/pm.c
new file mode 100644 (file)
index 0000000..e6536b1
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * linux/arch/arm/plat-omap/pm.c
+ *
+ * OMAP Power Management Routines
+ *
+ * Original code for the SA11x0:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Modified for the PXA250 by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Modified for the OMAP1510 by David Singleton:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+
+#include <asm/io.h>
+#include <asm/mach/time.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/omap16xx.h>
+#include <asm/arch/pm.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/tc.h>
+#include <asm/arch/tps65010.h>
+
+#include "clock.h"
+
+static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE];
+static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE];
+static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE];
+static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE];
+
+/*
+ * Let's power down on idle, but only if we are really
+ * idle, because once we start down the path of
+ * going idle we continue to do idle even if we get
+ * a clock tick interrupt . .
+ */
+void omap_pm_idle(void)
+{
+       int (*func_ptr)(void) = 0;
+       unsigned int mask32 = 0;
+
+       /*
+        * If the DSP is being used let's just idle the CPU, the overhead
+        * to wake up from Big Sleep is big, milliseconds versus micro
+        * seconds for wait for interrupt.
+        */
+
+       local_irq_disable();
+       local_fiq_disable();
+       if (need_resched()) {
+               local_fiq_enable();
+               local_irq_enable();
+               return;
+       }
+       mask32 = omap_readl(ARM_SYSST);
+
+       /*
+        * Since an interrupt may set up a timer, we don't want to
+        * reprogram the hardware timer with interrupts enabled.
+        * Re-enable interrupts only after returning from idle.
+        */
+       timer_dyn_reprogram();
+
+       if ((mask32 & DSP_IDLE) == 0) {
+               __asm__ volatile ("mcr  p15, 0, r0, c7, c0, 4");
+       } else {
+
+               if (cpu_is_omap1510()) {
+                       func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND);
+               } else if (cpu_is_omap1610() || cpu_is_omap1710()) {
+                       func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND);
+               } else if (cpu_is_omap5912()) {
+                       func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND);
+               }
+
+               func_ptr();
+       }
+       local_fiq_enable();
+       local_irq_enable();
+}
+
+/*
+ * Configuration of the wakeup event is board specific. For the
+ * moment we put it into this helper function. Later it may move
+ * to board specific files.
+ */
+static void omap_pm_wakeup_setup(void)
+{
+       /*
+        * Enable ARM XOR clock and release peripheral from reset by
+        * writing 1 to PER_EN bit in ARM_RSTCT2, this is required
+        * for UART configuration to use UART2 to wake up.
+        */
+
+       omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2);
+       omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2);
+       omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL);
+
+       /*
+        * Turn off all interrupts except L1-2nd level cascade,
+        * and the L2 wakeup interrupts: keypad and UART2.
+        */
+
+       omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR);
+
+       if (cpu_is_omap1510()) {
+               omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD),  OMAP_IH2_MIR);
+       }
+
+       if (cpu_is_omap16xx()) {
+               omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR);
+
+               omap_writel(~0x0, OMAP_IH2_1_MIR);
+               omap_writel(~0x0, OMAP_IH2_2_MIR);
+               omap_writel(~0x0, OMAP_IH2_3_MIR);
+       }
+
+       /*  New IRQ agreement */
+       omap_writel(1, OMAP_IH1_CONTROL);
+
+       /* external PULL to down, bit 22 = 0 */
+       omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2);
+}
+
+void omap_pm_suspend(void)
+{
+       unsigned int mask32 = 0;
+       unsigned long arg0 = 0, arg1 = 0;
+       int (*func_ptr)(unsigned short, unsigned short) = 0;
+       unsigned short save_dsp_idlect2;
+
+       printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev);
+
+       if (machine_is_omap_osk()) {
+               /* Stop LED1 (D9) blink */
+               tps65010_set_led(LED1, OFF);
+       }
+
+       /*
+        * Step 1: turn off interrupts
+        */
+
+       local_irq_disable();
+       local_fiq_disable();
+
+       /*
+        * Step 2: save registers
+        *
+        * The omap is a strange/beautiful device. The caches, memory
+        * and register state are preserved across power saves.
+        * We have to save and restore very little register state to
+        * idle the omap.
+         *
+        * Save interrupt, MPUI, ARM and UPLD control registers.
+        */
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_SAVE(OMAP_IH1_MIR);
+               MPUI1510_SAVE(OMAP_IH2_MIR);
+               MPUI1510_SAVE(MPUI_CTRL);
+               MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1510_SAVE(EMIFS_CONFIG);
+               MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_SAVE(OMAP_IH1_MIR);
+               MPUI1610_SAVE(OMAP_IH2_0_MIR);
+               MPUI1610_SAVE(OMAP_IH2_1_MIR);
+               MPUI1610_SAVE(OMAP_IH2_2_MIR);
+               MPUI1610_SAVE(OMAP_IH2_3_MIR);
+               MPUI1610_SAVE(MPUI_CTRL);
+               MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1610_SAVE(EMIFS_CONFIG);
+               MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
+       }
+
+       ARM_SAVE(ARM_CKCTL);
+       ARM_SAVE(ARM_IDLECT1);
+       ARM_SAVE(ARM_IDLECT2);
+       ARM_SAVE(ARM_EWUPCT);
+       ARM_SAVE(ARM_RSTCT1);
+       ARM_SAVE(ARM_RSTCT2);
+       ARM_SAVE(ARM_SYSST);
+       ULPD_SAVE(ULPD_CLOCK_CTRL);
+       ULPD_SAVE(ULPD_STATUS_REQ);
+
+       /*
+        * Step 3: LOW_PWR signal enabling
+        *
+        * Allow the LOW_PWR signal to be visible on MPUIO5 ball.
+        */
+       if (cpu_is_omap1510()) {
+               /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) |
+                           OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       } else if (cpu_is_omap16xx()) {
+               /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) |
+                           OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       }
+
+       /* configure LOW_PWR pin */
+       omap_cfg_reg(T20_1610_LOW_PWR);
+
+       /*
+        * Step 4: OMAP DSP Shutdown
+        */
+
+       /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */
+       omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE,
+                   ARM_RSTCT1);
+
+       /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */
+        omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG);
+
+       /* Set EN_DSPCK = 0, stop DSP block clock */
+       omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL);
+
+       /* Stop any DSP domain clocks */
+       omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2);
+       save_dsp_idlect2 = __raw_readw(DSP_IDLECT2);
+       __raw_writew(0, DSP_IDLECT2);
+
+       /*
+        * Step 5: Wakeup Event Setup
+        */
+
+       omap_pm_wakeup_setup();
+
+       /*
+        * Step 6a: ARM and Traffic controller shutdown
+        *
+        * Step 6 starts here with clock and watchdog disable
+        */
+
+       /* stop clocks */
+       mask32 = omap_readl(ARM_IDLECT2);
+       mask32 &= ~(1<<EN_WDTCK);  /* bit 0 -> 0 (WDT clock) */
+       mask32 |=  (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */
+       mask32 &= ~(1<<EN_PERCK);  /* bit 2 -> 0 (MPUPER_CK clock) */
+       mask32 &= ~(1<<EN_LCDCK);  /* bit 3 -> 0 (LCDC clock) */
+       mask32 &= ~(1<<EN_LBCK);   /* bit 4 -> 0 (local bus clock) */
+       mask32 |=  (1<<EN_APICK);  /* bit 6 -> 1 (MPUI clock) */
+       mask32 &= ~(1<<EN_TIMCK);  /* bit 7 -> 0 (MPU timer clock) */
+       mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */
+       mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */
+       omap_writel(mask32, ARM_IDLECT2);
+
+       /* disable ARM watchdog */
+       omap_writel(0x00F5, OMAP_WDT_TIMER_MODE);
+       omap_writel(0x00A0, OMAP_WDT_TIMER_MODE);
+
+       /*
+        * Step 6b: ARM and Traffic controller shutdown
+        *
+        * Step 6 continues here. Prepare jump to power management
+        * assembly code in internal SRAM.
+        *
+        * Since the omap_cpu_suspend routine has been copied to
+        * SRAM, we'll do an indirect procedure call to it and pass the
+        * contents of arm_idlect1 and arm_idlect2 so it can restore
+        * them when it wakes up and it will return.
+        */
+
+       arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1];
+       arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2];
+
+       if (cpu_is_omap1510()) {
+               func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND);
+       } else if (cpu_is_omap1610() || cpu_is_omap1710()) {
+               func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND);
+       } else if (cpu_is_omap5912()) {
+               func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND);
+       }
+
+       /*
+        * Step 6c: ARM and Traffic controller shutdown
+        *
+        * Jump to assembly code. The processor will stay there
+        * until wake up.
+        */
+
+        func_ptr(arg0, arg1);
+
+       /*
+        * If we are here, processor is woken up!
+        */
+
+       if (cpu_is_omap1510()) {
+               /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) &
+                           ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       } else if (cpu_is_omap16xx()) {
+               /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) &
+                           ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       }
+
+
+       /* Restore DSP clocks */
+       omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2);
+       __raw_writew(save_dsp_idlect2, DSP_IDLECT2);
+       ARM_RESTORE(ARM_IDLECT2);
+
+       /*
+        * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did
+        */
+
+       ARM_RESTORE(ARM_CKCTL);
+       ARM_RESTORE(ARM_EWUPCT);
+       ARM_RESTORE(ARM_RSTCT1);
+       ARM_RESTORE(ARM_RSTCT2);
+       ARM_RESTORE(ARM_SYSST);
+       ULPD_RESTORE(ULPD_CLOCK_CTRL);
+       ULPD_RESTORE(ULPD_STATUS_REQ);
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_RESTORE(MPUI_CTRL);
+               MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_RESTORE(MPUI_DSP_API_CONFIG);
+               MPUI1510_RESTORE(EMIFS_CONFIG);
+               MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG);
+               MPUI1510_RESTORE(OMAP_IH1_MIR);
+               MPUI1510_RESTORE(OMAP_IH2_MIR);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_RESTORE(MPUI_CTRL);
+               MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_RESTORE(MPUI_DSP_API_CONFIG);
+               MPUI1610_RESTORE(EMIFS_CONFIG);
+               MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG);
+
+               MPUI1610_RESTORE(OMAP_IH1_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_0_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_1_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_2_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_3_MIR);
+       }
+
+       /*
+        * Reenable interrupts
+        */
+
+       local_irq_enable();
+       local_fiq_enable();
+
+       printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev);
+
+       if (machine_is_omap_osk()) {
+               /* Let LED1 (D9) blink again */
+               tps65010_set_led(LED1, BLINK);
+       }
+}
+
+#if defined(DEBUG) && defined(CONFIG_PROC_FS)
+static int g_read_completed;
+
+/*
+ * Read system PM registers for debugging
+ */
+static int omap_pm_read_proc(
+       char *page_buffer,
+       char **my_first_byte,
+       off_t virtual_start,
+       int length,
+       int *eof,
+       void *data)
+{
+       int my_buffer_offset = 0;
+       char * const my_base = page_buffer;
+
+       ARM_SAVE(ARM_CKCTL);
+       ARM_SAVE(ARM_IDLECT1);
+       ARM_SAVE(ARM_IDLECT2);
+       ARM_SAVE(ARM_EWUPCT);
+       ARM_SAVE(ARM_RSTCT1);
+       ARM_SAVE(ARM_RSTCT2);
+       ARM_SAVE(ARM_SYSST);
+
+       ULPD_SAVE(ULPD_IT_STATUS);
+       ULPD_SAVE(ULPD_CLOCK_CTRL);
+       ULPD_SAVE(ULPD_SOFT_REQ);
+       ULPD_SAVE(ULPD_STATUS_REQ);
+       ULPD_SAVE(ULPD_DPLL_CTRL);
+       ULPD_SAVE(ULPD_POWER_CTRL);
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_SAVE(MPUI_CTRL);
+               MPUI1510_SAVE(MPUI_DSP_STATUS);
+               MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
+               MPUI1510_SAVE(EMIFS_CONFIG);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_SAVE(MPUI_CTRL);
+               MPUI1610_SAVE(MPUI_DSP_STATUS);
+               MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
+               MPUI1610_SAVE(EMIFS_CONFIG);
+       }
+
+       if (virtual_start == 0) {
+               g_read_completed = 0;
+
+               my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                  "ARM_CKCTL_REG:            0x%-8x     \n"
+                  "ARM_IDLECT1_REG:          0x%-8x     \n"
+                  "ARM_IDLECT2_REG:          0x%-8x     \n"
+                  "ARM_EWUPCT_REG:           0x%-8x     \n"
+                  "ARM_RSTCT1_REG:           0x%-8x     \n"
+                  "ARM_RSTCT2_REG:           0x%-8x     \n"
+                  "ARM_SYSST_REG:            0x%-8x     \n"
+                  "ULPD_IT_STATUS_REG:       0x%-4x     \n"
+                  "ULPD_CLOCK_CTRL_REG:      0x%-4x     \n"
+                  "ULPD_SOFT_REQ_REG:        0x%-4x     \n"
+                  "ULPD_DPLL_CTRL_REG:       0x%-4x     \n"
+                  "ULPD_STATUS_REQ_REG:      0x%-4x     \n"
+                  "ULPD_POWER_CTRL_REG:      0x%-4x     \n",
+                  ARM_SHOW(ARM_CKCTL),
+                  ARM_SHOW(ARM_IDLECT1),
+                  ARM_SHOW(ARM_IDLECT2),
+                  ARM_SHOW(ARM_EWUPCT),
+                  ARM_SHOW(ARM_RSTCT1),
+                  ARM_SHOW(ARM_RSTCT2),
+                  ARM_SHOW(ARM_SYSST),
+                  ULPD_SHOW(ULPD_IT_STATUS),
+                  ULPD_SHOW(ULPD_CLOCK_CTRL),
+                  ULPD_SHOW(ULPD_SOFT_REQ),
+                  ULPD_SHOW(ULPD_DPLL_CTRL),
+                  ULPD_SHOW(ULPD_STATUS_REQ),
+                  ULPD_SHOW(ULPD_POWER_CTRL));
+
+               if (cpu_is_omap1510()) {
+                       my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                          "MPUI1510_CTRL_REG             0x%-8x \n"
+                          "MPUI1510_DSP_STATUS_REG:      0x%-8x \n"
+                          "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
+                          "MPUI1510_DSP_API_CONFIG_REG:  0x%-8x \n"
+                          "MPUI1510_SDRAM_CONFIG_REG:    0x%-8x \n"
+                          "MPUI1510_EMIFS_CONFIG_REG:    0x%-8x \n",
+                          MPUI1510_SHOW(MPUI_CTRL),
+                          MPUI1510_SHOW(MPUI_DSP_STATUS),
+                          MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG),
+                          MPUI1510_SHOW(MPUI_DSP_API_CONFIG),
+                          MPUI1510_SHOW(EMIFF_SDRAM_CONFIG),
+                          MPUI1510_SHOW(EMIFS_CONFIG));
+               } else if (cpu_is_omap16xx()) {
+                       my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                          "MPUI1610_CTRL_REG             0x%-8x \n"
+                          "MPUI1610_DSP_STATUS_REG:      0x%-8x \n"
+                          "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
+                          "MPUI1610_DSP_API_CONFIG_REG:  0x%-8x \n"
+                          "MPUI1610_SDRAM_CONFIG_REG:    0x%-8x \n"
+                          "MPUI1610_EMIFS_CONFIG_REG:    0x%-8x \n",
+                          MPUI1610_SHOW(MPUI_CTRL),
+                          MPUI1610_SHOW(MPUI_DSP_STATUS),
+                          MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG),
+                          MPUI1610_SHOW(MPUI_DSP_API_CONFIG),
+                          MPUI1610_SHOW(EMIFF_SDRAM_CONFIG),
+                          MPUI1610_SHOW(EMIFS_CONFIG));
+               }
+
+               g_read_completed++;
+       } else if (g_read_completed >= 1) {
+                *eof = 1;
+                return 0;
+       }
+       g_read_completed++;
+
+       *my_first_byte = page_buffer;
+       return  my_buffer_offset;
+}
+
+static void omap_pm_init_proc(void)
+{
+       struct proc_dir_entry *entry;
+
+       entry = create_proc_read_entry("driver/omap_pm",
+                                      S_IWUSR | S_IRUGO, NULL,
+                                      omap_pm_read_proc, 0);
+}
+
+#endif /* DEBUG && CONFIG_PROC_FS */
+
+/*
+ *     omap_pm_prepare - Do preliminary suspend work.
+ *     @state:         suspend state we're entering.
+ *
+ */
+//#include <asm/arch/hardware.h>
+
+static int omap_pm_prepare(suspend_state_t state)
+{
+       int error = 0;
+
+       switch (state)
+       {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               break;
+
+       case PM_SUSPEND_DISK:
+               return -ENOTSUPP;
+
+       default:
+               return -EINVAL;
+       }
+
+       return error;
+}
+
+
+/*
+ *     omap_pm_enter - Actually enter a sleep state.
+ *     @state:         State we're entering.
+ *
+ */
+
+static int omap_pm_enter(suspend_state_t state)
+{
+       switch (state)
+       {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               omap_pm_suspend();
+               break;
+
+       case PM_SUSPEND_DISK:
+               return -ENOTSUPP;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+/**
+ *     omap_pm_finish - Finish up suspend sequence.
+ *     @state:         State we're coming out of.
+ *
+ *     This is called after we wake back up (or if entering the sleep state
+ *     failed).
+ */
+
+static int omap_pm_finish(suspend_state_t state)
+{
+       return 0;
+}
+
+
+struct pm_ops omap_pm_ops ={
+       .pm_disk_mode = 0,
+        .prepare        = omap_pm_prepare,
+        .enter          = omap_pm_enter,
+        .finish         = omap_pm_finish,
+};
+
+static int __init omap_pm_init(void)
+{
+       printk("Power Management for TI OMAP.\n");
+       pm_idle = omap_pm_idle;
+       /*
+        * We copy the assembler sleep/wakeup routines to SRAM.
+        * These routines need to be in SRAM as that's the only
+        * memory the MPU can see when it wakes up.
+        */
+
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND,
+                      omap1510_idle_loop_suspend,
+                      omap1510_idle_loop_suspend_sz);
+               memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend,
+                      omap1510_cpu_suspend_sz);
+       } else
+#endif
+       if (cpu_is_omap1610() || cpu_is_omap1710()) {
+               memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND,
+                      omap1610_idle_loop_suspend,
+                      omap1610_idle_loop_suspend_sz);
+               memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend,
+                      omap1610_cpu_suspend_sz);
+       } else if (cpu_is_omap5912()) {
+               memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND,
+                      omap1610_idle_loop_suspend,
+                      omap1610_idle_loop_suspend_sz);
+               memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend,
+                      omap1610_cpu_suspend_sz);
+       }
+
+       pm_set_ops(&omap_pm_ops);
+
+#if defined(DEBUG) && defined(CONFIG_PROC_FS)
+       omap_pm_init_proc();
+#endif
+
+       return 0;
+}
+__initcall(omap_pm_init);
+
diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/plat-omap/sleep.S
new file mode 100644 (file)
index 0000000..279490c
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * linux/arch/arm/plat-omap/sleep.S
+ *
+ * Low-level OMAP1510/1610 sleep/wakeUp support
+ *
+ * Initial SA1110 code:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Adapted for PXA by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/arch/io.h>
+#include <asm/arch/pm.h>
+
+               .text
+
+/*
+ * Forces OMAP into idle state
+ *
+ * omapXXXX_idle_loop_suspend()
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of slightly different configuration values we have
+ *       processor specific functions here.
+ */
+
+#ifdef CONFIG_ARCH_OMAP1510
+ENTRY(omap1510_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510:        subs    r5, r5, #1
+       bne     l_1510
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}     @ restore regs and return
+
+ENTRY(omap1510_idle_loop_suspend_sz)
+       .word   . - omap1510_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP1510 */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1610:        subs    r5, r5, #1
+       bne     l_1610
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}     @ restore regs and return
+
+ENTRY(omap1610_idle_loop_suspend_sz)
+       .word   . - omap1610_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
+
+/*
+ * Forces OMAP into deep sleep state
+ *
+ * omapXXXX_cpu_suspend()
+ *
+ * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed
+ * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1
+ * in register r1.
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of errata work arounds we have processor specific functions
+ *       here. They are mostly the same, but slightly different.
+ *
+ */
+
+#ifdef CONFIG_ARCH_OMAP1510
+ENTRY(omap1510_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ load base address of Traffic Controller
+       mov     r4, #TCMIF_ASM_BASE & 0xff000000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ work around errata of OMAP1510 PDE bit for TC shut down
+       @ clear PDE bit
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       bic     r5, r5, #PDE_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ set PWD_EN bit
+       and     r5, r5, #PWD_EN_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #SELF_REFRESH_MODE & 0xff000000
+       orr     r5, r5, #SELF_REFRESH_MODE & 0x000000ff
+       str     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #IDLE_EMIFS_REQUEST & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff
+       orr     r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510_2:
+       subs    r5, r5, #1
+       bne     l_1510_2
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1510_cpu_suspend_sz)
+       .word   . - omap1510_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP1510 */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ load base address of Traffic Controller
+       mov     r4, #TCMIF_ASM_BASE & 0xff000000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #SELF_REFRESH_MODE & 0xff000000
+       orr     r5, r5, #SELF_REFRESH_MODE & 0x000000ff
+       str     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #IDLE_EMIFS_REQUEST & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       mov     r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ work around errata of OMAP1610/5912. Enable (!) peripheral
+       @ clock to let the chip go into deep sleep
+       ldrh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       orr     r5,r5, #EN_PERCK_BIT & 0xff
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff
+       orr     r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1610_2:
+       subs    r5, r5, #1
+       bne     l_1610_2
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1610_cpu_suspend_sz)
+       .word   . - omap1610_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
diff --git a/arch/arm/plat-omap/usb.c b/arch/arm/plat-omap/usb.c
new file mode 100644 (file)
index 0000000..ab38e4e
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ * arch/arm/plat-omap/usb.c -- platform level USB initialization
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * 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
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/usb_otg.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/mux.h>
+#include <asm/arch/usb.h>
+#include <asm/arch/board.h>
+
+/* These routines should handle the standard chip-specific modes
+ * for usb0/1/2 ports, covering basic mux and transceiver setup.
+ *
+ * Some board-*.c files will need to set up additional mux options,
+ * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup.
+ */
+
+/* TESTED ON:
+ *  - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables
+ *  - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables
+ *  - 5912 OSK UDC, with *nonstandard* A-to-A cable
+ *  - 1510 Innovator UDC with bundled usb0 cable
+ *  - 1510 Innovator OHCI with bundled usb1/usb2 cable
+ *  - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS
+ *  - 1710 custom development board using alternate pin group
+ *  - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_OMAP_OTG
+
+static struct otg_transceiver *xceiv;
+
+/**
+ * otg_get_transceiver - find the (single) OTG transceiver driver
+ *
+ * Returns the transceiver driver, after getting a refcount to it; or
+ * null if there is no such transceiver.  The caller is responsible for
+ * releasing that count.
+ */
+struct otg_transceiver *otg_get_transceiver(void)
+{
+       if (xceiv)
+               get_device(xceiv->dev);
+       return xceiv;
+}
+EXPORT_SYMBOL(otg_get_transceiver);
+
+int otg_set_transceiver(struct otg_transceiver *x)
+{
+       if (xceiv && x)
+               return -EBUSY;
+       xceiv = x;
+       return 0;
+}
+EXPORT_SYMBOL(otg_set_transceiver);
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device)
+{
+       u32     syscon1 = 0;
+
+       if (nwires == 0) {
+               if (!cpu_is_omap15xx()) {
+                       /* pulldown D+/D- */
+                       USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1);
+               }
+               return 0;
+       }
+
+       if (is_device)
+               omap_cfg_reg(W4_USB_PUEN);
+
+       /* internal transceiver */
+       if (nwires == 2) {
+               // omap_cfg_reg(P9_USB_DP);
+               // omap_cfg_reg(R8_USB_DM);
+
+               if (cpu_is_omap15xx()) {
+                       /* This works on 1510-Innovator */
+                       return 0;
+               }
+
+               /* NOTES:
+                *  - peripheral should configure VBUS detection!
+                *  - only peripherals may use the internal D+/D- pulldowns
+                *  - OTG support on this port not yet written
+                */
+
+               USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4);
+               if (!is_device)
+                       USB_TRANSCEIVER_CTRL_REG |= (3 << 1);
+
+               return 3 << 16;
+       }
+
+       /* alternate pin config, external transceiver */
+       if (cpu_is_omap15xx()) {
+               printk(KERN_ERR "no usb0 alt pin config on 15xx\n");
+               return 0;
+       }
+
+       omap_cfg_reg(V6_USB0_TXD);
+       omap_cfg_reg(W9_USB0_TXEN);
+       omap_cfg_reg(W5_USB0_SE0);
+
+       /* NOTE:  SPEED and SUSP aren't configured here */
+
+       if (nwires != 3)
+               omap_cfg_reg(Y5_USB0_RCV);
+       if (nwires != 6)
+               USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R;
+
+       switch (nwires) {
+       case 3:
+               syscon1 = 2;
+               break;
+       case 4:
+               syscon1 = 1;
+               break;
+       case 6:
+               syscon1 = 3;
+               omap_cfg_reg(AA9_USB0_VP);
+               omap_cfg_reg(R9_USB0_VM);
+               USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R;
+               break;
+       default:
+               printk(KERN_ERR "illegal usb%d %d-wire transceiver\n",
+                       0, nwires);
+       }
+       return syscon1 << 16;
+}
+
+static u32 __init omap_usb1_init(unsigned nwires)
+{
+       u32     syscon1 = 0;
+
+       if (nwires != 6 && !cpu_is_omap15xx())
+               USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R;
+       if (nwires == 0)
+               return 0;
+
+       /* external transceiver */
+       omap_cfg_reg(USB1_TXD);
+       omap_cfg_reg(USB1_TXEN);
+       if (cpu_is_omap15xx()) {
+               omap_cfg_reg(USB1_SEO);
+               omap_cfg_reg(USB1_SPEED);
+               // SUSP
+       } else if (cpu_is_omap1610() || cpu_is_omap5912()) {
+               omap_cfg_reg(W13_1610_USB1_SE0);
+               omap_cfg_reg(R13_1610_USB1_SPEED);
+               // SUSP
+       } else if (cpu_is_omap1710()) {
+               omap_cfg_reg(R13_1710_USB1_SE0);
+               // SUSP
+       } else {
+               pr_debug("usb unrecognized\n");
+       }
+       if (nwires != 3)
+               omap_cfg_reg(USB1_RCV);
+
+       switch (nwires) {
+       case 3:
+               syscon1 = 2;
+               break;
+       case 4:
+               syscon1 = 1;
+               break;
+       case 6:
+               syscon1 = 3;
+               omap_cfg_reg(USB1_VP);
+               omap_cfg_reg(USB1_VM);
+               if (!cpu_is_omap15xx())
+                       USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R;
+               break;
+       default:
+               printk(KERN_ERR "illegal usb%d %d-wire transceiver\n",
+                       1, nwires);
+       }
+       return syscon1 << 20;
+}
+
+static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup)
+{
+       u32     syscon1 = 0;
+
+       /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */
+       if (alt_pingroup || nwires == 0)
+               return 0;
+       if (nwires != 6 && !cpu_is_omap15xx())
+               USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R;
+
+       /* external transceiver */
+       if (cpu_is_omap15xx()) {
+               omap_cfg_reg(USB2_TXD);
+               omap_cfg_reg(USB2_TXEN);
+               omap_cfg_reg(USB2_SEO);
+               if (nwires != 3)
+                       omap_cfg_reg(USB2_RCV);
+               /* there is no USB2_SPEED */
+       } else if (cpu_is_omap16xx()) {
+               omap_cfg_reg(V6_USB2_TXD);
+               omap_cfg_reg(W9_USB2_TXEN);
+               omap_cfg_reg(W5_USB2_SE0);
+               if (nwires != 3)
+                       omap_cfg_reg(Y5_USB2_RCV);
+               // FIXME omap_cfg_reg(USB2_SPEED);
+       } else {
+               pr_debug("usb unrecognized\n");
+       }
+       // omap_cfg_reg(USB2_SUSP);
+
+       switch (nwires) {
+       case 3:
+               syscon1 = 2;
+               break;
+       case 4:
+               syscon1 = 1;
+               break;
+       case 6:
+               syscon1 = 3;
+               if (cpu_is_omap15xx()) {
+                       omap_cfg_reg(USB2_VP);
+                       omap_cfg_reg(USB2_VM);
+               } else {
+                       omap_cfg_reg(AA9_USB2_VP);
+                       omap_cfg_reg(R9_USB2_VM);
+                       USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R;
+               }
+               break;
+       default:
+               printk(KERN_ERR "illegal usb%d %d-wire transceiver\n",
+                       2, nwires);
+       }
+       return syscon1 << 24;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if    defined(CONFIG_USB_GADGET_OMAP) || \
+       defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \
+       (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG))
+static void usb_release(struct device *dev)
+{
+       /* normally not freed */
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_OMAP
+
+static struct resource udc_resources[] = {
+       /* order is significant! */
+       {               /* registers */
+               .start          = UDC_BASE,
+               .end            = UDC_BASE + 0xff,
+               .flags          = IORESOURCE_MEM,
+       }, {            /* general IRQ */
+               .start          = IH2_BASE + 20,
+               .flags          = IORESOURCE_IRQ,
+       }, {            /* PIO IRQ */
+               .start          = IH2_BASE + 30,
+               .flags          = IORESOURCE_IRQ,
+       }, {            /* SOF IRQ */
+               .start          = IH2_BASE + 29,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static u64 udc_dmamask = ~(u32)0;
+
+static struct platform_device udc_device = {
+       .name           = "omap_udc",
+       .id             = -1,
+       .dev = {
+               .release                = usb_release,
+               .dma_mask               = &udc_dmamask,
+               .coherent_dma_mask      = 0xffffffff,
+       },
+       .num_resources  = ARRAY_SIZE(udc_resources),
+       .resource       = udc_resources,
+};
+
+#endif
+
+#if    defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+
+/* The dmamask must be set for OHCI to work */
+static u64 ohci_dmamask = ~(u32)0;
+
+static struct resource ohci_resources[] = {
+       {
+               .start  = OMAP_OHCI_BASE,
+               .end    = OMAP_OHCI_BASE + 4096,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = INT_USB_HHC_1,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device ohci_device = {
+       .name                   = "ohci",
+       .id                     = -1,
+       .dev = {
+               .release                = usb_release,
+               .dma_mask               = &ohci_dmamask,
+               .coherent_dma_mask      = 0xffffffff,
+       },
+       .num_resources  = ARRAY_SIZE(ohci_resources),
+       .resource               = ohci_resources,
+};
+
+#endif
+
+#if    defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)
+
+static struct resource otg_resources[] = {
+       /* order is significant! */
+       {
+               .start          = OTG_BASE,
+               .end            = OTG_BASE + 0xff,
+               .flags          = IORESOURCE_MEM,
+       }, {
+               .start          = IH2_BASE + 8,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device otg_device = {
+       .name           = "omap_otg",
+       .id             = -1,
+       .dev = {
+               .release                = usb_release,
+       },
+       .num_resources  = ARRAY_SIZE(otg_resources),
+       .resource       = otg_resources,
+};
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define ULPD_CLOCK_CTRL_REG    __REG16(ULPD_CLOCK_CTRL)
+#define ULPD_SOFT_REQ_REG      __REG16(ULPD_SOFT_REQ)
+
+
+// FIXME correct answer depends on hmc_mode,
+// as does any nonzero value for config->otg port number
+#ifdef CONFIG_USB_GADGET_OMAP
+#define        is_usb0_device(config)  1
+#else
+#define        is_usb0_device(config)  0
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_OMAP_OTG
+
+void __init
+omap_otg_init(struct omap_usb_config *config)
+{
+       u32             syscon = OTG_SYSCON_1_REG & 0xffff;
+       int             status;
+       int             alt_pingroup = 0;
+
+       /* NOTE:  no bus or clock setup (yet?) */
+
+       syscon = OTG_SYSCON_1_REG & 0xffff;
+       if (!(syscon & OTG_RESET_DONE))
+               pr_debug("USB resets not complete?\n");
+
+       // OTG_IRQ_EN_REG = 0;
+
+       /* pin muxing and transceiver pinouts */
+       if (config->pins[0] > 2)        /* alt pingroup 2 */
+               alt_pingroup = 1;
+       syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config));
+       syscon |= omap_usb1_init(config->pins[1]);
+       syscon |= omap_usb2_init(config->pins[2], alt_pingroup);
+       pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon);
+       OTG_SYSCON_1_REG = syscon;
+
+       syscon = config->hmc_mode;
+       syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */;
+#ifdef CONFIG_USB_OTG
+       if (config->otg)
+               syscon |= OTG_EN;
+#endif
+       pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG);
+       pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon);
+       OTG_SYSCON_2_REG = syscon;
+
+       printk("USB: hmc %d", config->hmc_mode);
+       if (alt_pingroup)
+               printk(", usb2 alt %d wires", config->pins[2]);
+       else if (config->pins[0])
+               printk(", usb0 %d wires%s", config->pins[0],
+                       is_usb0_device(config) ? " (dev)" : "");
+       if (config->pins[1])
+               printk(", usb1 %d wires", config->pins[1]);
+       if (!alt_pingroup && config->pins[2])
+               printk(", usb2 %d wires", config->pins[2]);
+       if (config->otg)
+               printk(", Mini-AB on usb%d", config->otg - 1);
+       printk("\n");
+
+       /* leave USB clocks/controllers off until needed */
+       ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ;
+       ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN;
+       ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK;
+       syscon = OTG_SYSCON_1_REG;
+       syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN;
+
+#ifdef CONFIG_USB_GADGET_OMAP
+       if (config->otg || config->register_dev) {
+               syscon &= ~DEV_IDLE_EN;
+               udc_device.dev.platform_data = config;
+               /* FIXME patch IRQ numbers for omap730 */
+               status = platform_device_register(&udc_device);
+               if (status)
+                       pr_debug("can't register UDC device, %d\n", status);
+       }
+#endif
+
+#if    defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+       if (config->otg || config->register_host) {
+               syscon &= ~HST_IDLE_EN;
+               ohci_device.dev.platform_data = config;
+               if (cpu_is_omap730())
+                       ohci_resources[1].start = INT_730_USB_HHC_1;
+               status = platform_device_register(&ohci_device);
+               if (status)
+                       pr_debug("can't register OHCI device, %d\n", status);
+       }
+#endif
+
+#ifdef CONFIG_USB_OTG
+       if (config->otg) {
+               syscon &= ~OTG_IDLE_EN;
+               otg_device.dev.platform_data = config;
+               if (cpu_is_omap730())
+                       otg_resources[1].start = INT_730_USB_OTG;
+               status = platform_device_register(&otg_device);
+               if (status)
+                       pr_debug("can't register OTG device, %d\n", status);
+       }
+#endif
+       pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon);
+       OTG_SYSCON_1_REG = syscon;
+
+       status = 0;
+}
+
+#else
+static inline void omap_otg_init(struct omap_usb_config *config) {}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_OMAP1510
+
+#define ULPD_DPLL_CTRL_REG     __REG16(ULPD_DPLL_CTRL)
+#define DPLL_IOB               (1 << 13)
+#define DPLL_PLL_ENABLE                (1 << 4)
+#define DPLL_LOCK              (1 << 0)
+
+#define ULPD_APLL_CTRL_REG     __REG16(ULPD_APLL_CTRL)
+#define APLL_NDPLL_SWITCH      (1 << 0)
+
+
+static void __init omap_1510_usb_init(struct omap_usb_config *config)
+{
+       int status;
+       unsigned int val;
+
+       omap_usb0_init(config->pins[0], is_usb0_device(config));
+       omap_usb1_init(config->pins[1]);
+       omap_usb2_init(config->pins[2], 0);
+
+       val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1);
+       val |= (config->hmc_mode << 1);
+       omap_writel(val, MOD_CONF_CTRL_0);
+
+       printk("USB: hmc %d", config->hmc_mode);
+       if (config->pins[0])
+               printk(", usb0 %d wires%s", config->pins[0],
+                       is_usb0_device(config) ? " (dev)" : "");
+       if (config->pins[1])
+               printk(", usb1 %d wires", config->pins[1]);
+       if (config->pins[2])
+               printk(", usb2 %d wires", config->pins[2]);
+       printk("\n");
+
+       /* use DPLL for 48 MHz function clock */
+       pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG,
+                       ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG);
+       ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH;
+       ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE;
+       ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ;
+       while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK))
+               cpu_relax();
+
+#ifdef CONFIG_USB_GADGET_OMAP
+       if (config->register_dev) {
+               udc_device.dev.platform_data = config;
+               status = platform_device_register(&udc_device);
+               if (status)
+                       pr_debug("can't register UDC device, %d\n", status);
+               /* udc driver gates 48MHz by D+ pullup */
+       }
+#endif
+
+#if    defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+       if (config->register_host) {
+               ohci_device.dev.platform_data = config;
+               status = platform_device_register(&ohci_device);
+               if (status)
+                       pr_debug("can't register OHCI device, %d\n", status);
+               /* hcd explicitly gates 48MHz */
+       }
+#endif
+}
+
+#else
+static inline void omap_1510_usb_init(struct omap_usb_config *config) {}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct omap_usb_config platform_data;
+
+static int __init
+omap_usb_init(void)
+{
+       const struct omap_usb_config *config;
+
+       config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config);
+       if (config == NULL) {
+               printk(KERN_ERR "USB: No board-specific "
+                               "platform config found\n");
+               return -ENODEV;
+       }
+       platform_data = *config;
+
+       if (cpu_is_omap730() || cpu_is_omap16xx())
+               omap_otg_init(&platform_data);
+       else if (cpu_is_omap15xx())
+               omap_1510_usb_init(&platform_data);
+       else {
+               printk(KERN_ERR "USB: No init for your chip yet\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+subsys_initcall(omap_usb_init);