diff options
| -rw-r--r-- | arch/arm/plat-omap/Kconfig | 99 | ||||
| -rw-r--r-- | arch/arm/plat-omap/Makefile | 16 | ||||
| -rw-r--r-- | arch/arm/plat-omap/common.c | 135 | ||||
| -rw-r--r-- | arch/arm/plat-omap/common.h | 36 | ||||
| -rw-r--r-- | arch/arm/plat-omap/dma.c | 1086 | ||||
| -rw-r--r-- | arch/arm/plat-omap/gpio.c | 762 | ||||
| -rw-r--r-- | arch/arm/plat-omap/mcbsp.c | 685 | ||||
| -rw-r--r-- | arch/arm/plat-omap/mux.c | 163 | ||||
| -rw-r--r-- | arch/arm/plat-omap/ocpi.c | 114 | ||||
| -rw-r--r-- | arch/arm/plat-omap/pm.c | 632 | ||||
| -rw-r--r-- | arch/arm/plat-omap/sleep.S | 314 | ||||
| -rw-r--r-- | arch/arm/plat-omap/usb.c | 593 |
12 files changed, 4635 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig new file mode 100644 index 000000000000..a72fe55b513b --- /dev/null +++ b/arch/arm/plat-omap/Kconfig | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | if ARCH_OMAP | ||
| 2 | |||
| 3 | menu "TI OMAP Implementations" | ||
| 4 | |||
| 5 | config ARCH_OMAP_OTG | ||
| 6 | bool | ||
| 7 | |||
| 8 | choice | ||
| 9 | prompt "OMAP System Type" | ||
| 10 | default ARCH_OMAP1 | ||
| 11 | |||
| 12 | config ARCH_OMAP1 | ||
| 13 | bool "TI OMAP1" | ||
| 14 | |||
| 15 | config ARCH_OMAP2 | ||
| 16 | bool "TI OMAP2" | ||
| 17 | |||
| 18 | endchoice | ||
| 19 | |||
| 20 | comment "OMAP Feature Selections" | ||
| 21 | |||
| 22 | config OMAP_MUX | ||
| 23 | bool "OMAP multiplexing support" | ||
| 24 | depends on ARCH_OMAP | ||
| 25 | default y | ||
| 26 | help | ||
| 27 | Pin multiplexing support for OMAP boards. If your bootloader | ||
| 28 | sets the multiplexing correctly, say N. Otherwise, or if unsure, | ||
| 29 | say Y. | ||
| 30 | |||
| 31 | config OMAP_MUX_DEBUG | ||
| 32 | bool "Multiplexing debug output" | ||
| 33 | depends on OMAP_MUX | ||
| 34 | default n | ||
| 35 | help | ||
| 36 | Makes the multiplexing functions print out a lot of debug info. | ||
| 37 | This is useful if you want to find out the correct values of the | ||
| 38 | multiplexing registers. | ||
| 39 | |||
| 40 | config OMAP_MUX_WARNINGS | ||
| 41 | bool "Warn about pins the bootloader didn't set up" | ||
| 42 | depends on OMAP_MUX | ||
| 43 | default y | ||
| 44 | help | ||
| 45 | Choose Y here to warn whenever driver initialization logic needs | ||
| 46 | to change the pin multiplexing setup. When there are no warnings | ||
| 47 | printed, it's safe to deselect OMAP_MUX for your product. | ||
| 48 | |||
| 49 | choice | ||
| 50 | prompt "System timer" | ||
| 51 | default OMAP_MPU_TIMER | ||
| 52 | |||
| 53 | config OMAP_MPU_TIMER | ||
| 54 | bool "Use mpu timer" | ||
| 55 | help | ||
| 56 | Select this option if you want to use the OMAP mpu timer. This | ||
| 57 | timer provides more intra-tick resolution than the 32KHz timer, | ||
| 58 | but consumes more power. | ||
| 59 | |||
| 60 | config OMAP_32K_TIMER | ||
| 61 | bool "Use 32KHz timer" | ||
| 62 | depends on ARCH_OMAP16XX | ||
| 63 | help | ||
| 64 | Select this option if you want to enable the OMAP 32KHz timer. | ||
| 65 | This timer saves power compared to the OMAP_MPU_TIMER, and has | ||
| 66 | support for no tick during idle. The 32KHz timer provides less | ||
| 67 | intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is | ||
| 68 | currently only available for OMAP-16xx. | ||
| 69 | |||
| 70 | endchoice | ||
| 71 | |||
| 72 | config OMAP_32K_TIMER_HZ | ||
| 73 | int "Kernel internal timer frequency for 32KHz timer" | ||
| 74 | range 32 1024 | ||
| 75 | depends on OMAP_32K_TIMER | ||
| 76 | default "128" | ||
| 77 | help | ||
| 78 | Kernel internal timer frequency should be a divisor of 32768, | ||
| 79 | such as 64 or 128. | ||
| 80 | |||
| 81 | choice | ||
| 82 | prompt "Low-level debug console UART" | ||
| 83 | depends on ARCH_OMAP | ||
| 84 | default OMAP_LL_DEBUG_UART1 | ||
| 85 | |||
| 86 | config OMAP_LL_DEBUG_UART1 | ||
| 87 | bool "UART1" | ||
| 88 | |||
| 89 | config OMAP_LL_DEBUG_UART2 | ||
| 90 | bool "UART2" | ||
| 91 | |||
| 92 | config OMAP_LL_DEBUG_UART3 | ||
| 93 | bool "UART3" | ||
| 94 | |||
| 95 | endchoice | ||
| 96 | |||
| 97 | endmenu | ||
| 98 | |||
| 99 | endif | ||
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile new file mode 100644 index 000000000000..3be25ebfc45e --- /dev/null +++ b/arch/arm/plat-omap/Makefile | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the linux kernel. | ||
| 3 | # | ||
| 4 | |||
| 5 | # Common support | ||
| 6 | obj-y := common.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o | ||
| 7 | obj-m := | ||
| 8 | obj-n := | ||
| 9 | obj- := | ||
| 10 | |||
| 11 | # OCPI interconnect support for 1710, 1610 and 5912 | ||
| 12 | obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o | ||
| 13 | |||
| 14 | # Power Management | ||
| 15 | obj-$(CONFIG_PM) += pm.o sleep.o | ||
| 16 | |||
diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c new file mode 100644 index 000000000000..ea967a8f6ce5 --- /dev/null +++ b/arch/arm/plat-omap/common.c | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/common.c | ||
| 3 | * | ||
| 4 | * Code common to all OMAP machines. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | #include <linux/config.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/pm.h> | ||
| 16 | #include <linux/console.h> | ||
| 17 | #include <linux/serial.h> | ||
| 18 | #include <linux/tty.h> | ||
| 19 | #include <linux/serial_8250.h> | ||
| 20 | #include <linux/serial_reg.h> | ||
| 21 | |||
| 22 | #include <asm/hardware.h> | ||
| 23 | #include <asm/system.h> | ||
| 24 | #include <asm/pgtable.h> | ||
| 25 | #include <asm/mach/map.h> | ||
| 26 | #include <asm/hardware/clock.h> | ||
| 27 | #include <asm/io.h> | ||
| 28 | #include <asm/mach-types.h> | ||
| 29 | |||
| 30 | #include <asm/arch/board.h> | ||
| 31 | #include <asm/arch/mux.h> | ||
| 32 | #include <asm/arch/fpga.h> | ||
| 33 | |||
| 34 | #include "clock.h" | ||
| 35 | |||
| 36 | #define NO_LENGTH_CHECK 0xffffffff | ||
| 37 | |||
| 38 | extern int omap_bootloader_tag_len; | ||
| 39 | extern u8 omap_bootloader_tag[]; | ||
| 40 | |||
| 41 | struct omap_board_config_kernel *omap_board_config; | ||
| 42 | int omap_board_config_size = 0; | ||
| 43 | |||
| 44 | static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) | ||
| 45 | { | ||
| 46 | struct omap_board_config_kernel *kinfo = NULL; | ||
| 47 | int i; | ||
| 48 | |||
| 49 | #ifdef CONFIG_OMAP_BOOT_TAG | ||
| 50 | struct omap_board_config_entry *info = NULL; | ||
| 51 | |||
| 52 | if (omap_bootloader_tag_len > 4) | ||
| 53 | info = (struct omap_board_config_entry *) omap_bootloader_tag; | ||
| 54 | while (info != NULL) { | ||
| 55 | u8 *next; | ||
| 56 | |||
| 57 | if (info->tag == tag) { | ||
| 58 | if (skip == 0) | ||
| 59 | break; | ||
| 60 | skip--; | ||
| 61 | } | ||
| 62 | |||
| 63 | if ((info->len & 0x03) != 0) { | ||
| 64 | /* We bail out to avoid an alignment fault */ | ||
| 65 | printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", | ||
| 66 | info->len, info->tag); | ||
| 67 | return NULL; | ||
| 68 | } | ||
| 69 | next = (u8 *) info + sizeof(*info) + info->len; | ||
| 70 | if (next >= omap_bootloader_tag + omap_bootloader_tag_len) | ||
| 71 | info = NULL; | ||
| 72 | else | ||
| 73 | info = (struct omap_board_config_entry *) next; | ||
| 74 | } | ||
| 75 | if (info != NULL) { | ||
| 76 | /* Check the length as a lame attempt to check for | ||
| 77 | * binary inconsistancy. */ | ||
| 78 | if (len != NO_LENGTH_CHECK) { | ||
| 79 | /* Word-align len */ | ||
| 80 | if (len & 0x03) | ||
| 81 | len = (len + 3) & ~0x03; | ||
| 82 | if (info->len != len) { | ||
| 83 | printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", | ||
| 84 | tag, len, info->len); | ||
| 85 | return NULL; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | if (len_out != NULL) | ||
| 89 | *len_out = info->len; | ||
| 90 | return info->data; | ||
| 91 | } | ||
| 92 | #endif | ||
| 93 | /* Try to find the config from the board-specific structures | ||
| 94 | * in the kernel. */ | ||
| 95 | for (i = 0; i < omap_board_config_size; i++) { | ||
| 96 | if (omap_board_config[i].tag == tag) { | ||
| 97 | kinfo = &omap_board_config[i]; | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | if (kinfo == NULL) | ||
| 102 | return NULL; | ||
| 103 | return kinfo->data; | ||
| 104 | } | ||
| 105 | |||
| 106 | const void *__omap_get_config(u16 tag, size_t len, int nr) | ||
| 107 | { | ||
| 108 | return get_config(tag, len, nr, NULL); | ||
| 109 | } | ||
| 110 | EXPORT_SYMBOL(__omap_get_config); | ||
| 111 | |||
| 112 | const void *omap_get_var_config(u16 tag, size_t *len) | ||
| 113 | { | ||
| 114 | return get_config(tag, NO_LENGTH_CHECK, 0, len); | ||
| 115 | } | ||
| 116 | EXPORT_SYMBOL(omap_get_var_config); | ||
| 117 | |||
| 118 | static int __init omap_add_serial_console(void) | ||
| 119 | { | ||
| 120 | const struct omap_serial_console_config *info; | ||
| 121 | |||
| 122 | info = omap_get_config(OMAP_TAG_SERIAL_CONSOLE, | ||
| 123 | struct omap_serial_console_config); | ||
| 124 | if (info != NULL && info->console_uart) { | ||
| 125 | static char speed[11], *opt = NULL; | ||
| 126 | |||
| 127 | if (info->console_speed) { | ||
| 128 | snprintf(speed, sizeof(speed), "%u", info->console_speed); | ||
| 129 | opt = speed; | ||
| 130 | } | ||
| 131 | return add_preferred_console("ttyS", info->console_uart - 1, opt); | ||
| 132 | } | ||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | 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 index 000000000000..893964dfe18e --- /dev/null +++ b/arch/arm/plat-omap/common.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/common.h | ||
| 3 | * | ||
| 4 | * Header for code common to all OMAP machines. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | * | ||
| 11 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 12 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 13 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 14 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 15 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 16 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 17 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 18 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 19 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 20 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 25 | */ | ||
| 26 | |||
| 27 | #ifndef __ARCH_ARM_MACH_OMAP_COMMON_H | ||
| 28 | #define __ARCH_ARM_MACH_OMAP_COMMON_H | ||
| 29 | |||
| 30 | struct sys_timer; | ||
| 31 | |||
| 32 | extern void omap_map_common_io(void); | ||
| 33 | extern struct sys_timer omap_timer; | ||
| 34 | extern void omap_serial_init(int ports[]); | ||
| 35 | |||
| 36 | #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 index 000000000000..015bd2cf869f --- /dev/null +++ b/arch/arm/plat-omap/dma.c | |||
| @@ -0,0 +1,1086 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/dma.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2003 Nokia Corporation | ||
| 5 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> | ||
| 6 | * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> | ||
| 7 | * Graphics DMA and LCD DMA graphics tranformations | ||
| 8 | * by Imre Deak <imre.deak@nokia.com> | ||
| 9 | * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. | ||
| 10 | * | ||
| 11 | * Support functions for the OMAP internal DMA channels. | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License version 2 as | ||
| 15 | * published by the Free Software Foundation. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/spinlock.h> | ||
| 23 | #include <linux/errno.h> | ||
| 24 | #include <linux/interrupt.h> | ||
| 25 | |||
| 26 | #include <asm/system.h> | ||
| 27 | #include <asm/irq.h> | ||
| 28 | #include <asm/hardware.h> | ||
| 29 | #include <asm/dma.h> | ||
| 30 | #include <asm/io.h> | ||
| 31 | |||
| 32 | #include <asm/arch/tc.h> | ||
| 33 | |||
| 34 | #define OMAP_DMA_ACTIVE 0x01 | ||
| 35 | |||
| 36 | #define OMAP_DMA_CCR_EN (1 << 7) | ||
| 37 | |||
| 38 | #define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) | ||
| 39 | |||
| 40 | static int enable_1510_mode = 0; | ||
| 41 | |||
| 42 | struct omap_dma_lch { | ||
| 43 | int next_lch; | ||
| 44 | int dev_id; | ||
| 45 | u16 saved_csr; | ||
| 46 | u16 enabled_irqs; | ||
| 47 | const char *dev_name; | ||
| 48 | void (* callback)(int lch, u16 ch_status, void *data); | ||
| 49 | void *data; | ||
| 50 | long flags; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static int dma_chan_count; | ||
| 54 | |||
| 55 | static spinlock_t dma_chan_lock; | ||
| 56 | static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
| 57 | |||
| 58 | const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = { | ||
| 59 | INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3, | ||
| 60 | INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7, | ||
| 61 | INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10, | ||
| 62 | INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13, | ||
| 63 | INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD | ||
| 64 | }; | ||
| 65 | |||
| 66 | static inline int get_gdma_dev(int req) | ||
| 67 | { | ||
| 68 | u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; | ||
| 69 | int shift = ((req - 1) % 5) * 6; | ||
| 70 | |||
| 71 | return ((omap_readl(reg) >> shift) & 0x3f) + 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | static inline void set_gdma_dev(int req, int dev) | ||
| 75 | { | ||
| 76 | u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; | ||
| 77 | int shift = ((req - 1) % 5) * 6; | ||
| 78 | u32 l; | ||
| 79 | |||
| 80 | l = omap_readl(reg); | ||
| 81 | l &= ~(0x3f << shift); | ||
| 82 | l |= (dev - 1) << shift; | ||
| 83 | omap_writel(l, reg); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void clear_lch_regs(int lch) | ||
| 87 | { | ||
| 88 | int i; | ||
| 89 | u32 lch_base = OMAP_DMA_BASE + lch * 0x40; | ||
| 90 | |||
| 91 | for (i = 0; i < 0x2c; i += 2) | ||
| 92 | omap_writew(0, lch_base + i); | ||
| 93 | } | ||
| 94 | |||
| 95 | void omap_set_dma_priority(int dst_port, int priority) | ||
| 96 | { | ||
| 97 | unsigned long reg; | ||
| 98 | u32 l; | ||
| 99 | |||
| 100 | switch (dst_port) { | ||
| 101 | case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ | ||
| 102 | reg = OMAP_TC_OCPT1_PRIOR; | ||
| 103 | break; | ||
| 104 | case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ | ||
| 105 | reg = OMAP_TC_OCPT2_PRIOR; | ||
| 106 | break; | ||
| 107 | case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ | ||
| 108 | reg = OMAP_TC_EMIFF_PRIOR; | ||
| 109 | break; | ||
| 110 | case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ | ||
| 111 | reg = OMAP_TC_EMIFS_PRIOR; | ||
| 112 | break; | ||
| 113 | default: | ||
| 114 | BUG(); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | l = omap_readl(reg); | ||
| 118 | l &= ~(0xf << 8); | ||
| 119 | l |= (priority & 0xf) << 8; | ||
| 120 | omap_writel(l, reg); | ||
| 121 | } | ||
| 122 | |||
| 123 | void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, | ||
| 124 | int frame_count, int sync_mode) | ||
| 125 | { | ||
| 126 | u16 w; | ||
| 127 | |||
| 128 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
| 129 | w &= ~0x03; | ||
| 130 | w |= data_type; | ||
| 131 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 132 | |||
| 133 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
| 134 | w &= ~(1 << 5); | ||
| 135 | if (sync_mode == OMAP_DMA_SYNC_FRAME) | ||
| 136 | w |= 1 << 5; | ||
| 137 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
| 138 | |||
| 139 | w = omap_readw(OMAP_DMA_CCR2(lch)); | ||
| 140 | w &= ~(1 << 2); | ||
| 141 | if (sync_mode == OMAP_DMA_SYNC_BLOCK) | ||
| 142 | w |= 1 << 2; | ||
| 143 | omap_writew(w, OMAP_DMA_CCR2(lch)); | ||
| 144 | |||
| 145 | omap_writew(elem_count, OMAP_DMA_CEN(lch)); | ||
| 146 | omap_writew(frame_count, OMAP_DMA_CFN(lch)); | ||
| 147 | |||
| 148 | } | ||
| 149 | void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) | ||
| 150 | { | ||
| 151 | u16 w; | ||
| 152 | |||
| 153 | BUG_ON(omap_dma_in_1510_mode()); | ||
| 154 | |||
| 155 | w = omap_readw(OMAP_DMA_CCR2(lch)) & ~0x03; | ||
| 156 | switch (mode) { | ||
| 157 | case OMAP_DMA_CONSTANT_FILL: | ||
| 158 | w |= 0x01; | ||
| 159 | break; | ||
| 160 | case OMAP_DMA_TRANSPARENT_COPY: | ||
| 161 | w |= 0x02; | ||
| 162 | break; | ||
| 163 | case OMAP_DMA_COLOR_DIS: | ||
| 164 | break; | ||
| 165 | default: | ||
| 166 | BUG(); | ||
| 167 | } | ||
| 168 | omap_writew(w, OMAP_DMA_CCR2(lch)); | ||
| 169 | |||
| 170 | w = omap_readw(OMAP_DMA_LCH_CTRL(lch)) & ~0x0f; | ||
| 171 | /* Default is channel type 2D */ | ||
| 172 | if (mode) { | ||
| 173 | omap_writew((u16)color, OMAP_DMA_COLOR_L(lch)); | ||
| 174 | omap_writew((u16)(color >> 16), OMAP_DMA_COLOR_U(lch)); | ||
| 175 | w |= 1; /* Channel type G */ | ||
| 176 | } | ||
| 177 | omap_writew(w, OMAP_DMA_LCH_CTRL(lch)); | ||
| 178 | } | ||
| 179 | |||
| 180 | |||
| 181 | void omap_set_dma_src_params(int lch, int src_port, int src_amode, | ||
| 182 | unsigned long src_start) | ||
| 183 | { | ||
| 184 | u16 w; | ||
| 185 | |||
| 186 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
| 187 | w &= ~(0x1f << 2); | ||
| 188 | w |= src_port << 2; | ||
| 189 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 190 | |||
| 191 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
| 192 | w &= ~(0x03 << 12); | ||
| 193 | w |= src_amode << 12; | ||
| 194 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
| 195 | |||
| 196 | omap_writew(src_start >> 16, OMAP_DMA_CSSA_U(lch)); | ||
| 197 | omap_writew(src_start, OMAP_DMA_CSSA_L(lch)); | ||
| 198 | } | ||
| 199 | |||
| 200 | void omap_set_dma_src_index(int lch, int eidx, int fidx) | ||
| 201 | { | ||
| 202 | omap_writew(eidx, OMAP_DMA_CSEI(lch)); | ||
| 203 | omap_writew(fidx, OMAP_DMA_CSFI(lch)); | ||
| 204 | } | ||
| 205 | |||
| 206 | void omap_set_dma_src_data_pack(int lch, int enable) | ||
| 207 | { | ||
| 208 | u16 w; | ||
| 209 | |||
| 210 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 6); | ||
| 211 | w |= enable ? (1 << 6) : 0; | ||
| 212 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 213 | } | ||
| 214 | |||
| 215 | void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) | ||
| 216 | { | ||
| 217 | u16 w; | ||
| 218 | |||
| 219 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 7); | ||
| 220 | switch (burst_mode) { | ||
| 221 | case OMAP_DMA_DATA_BURST_DIS: | ||
| 222 | break; | ||
| 223 | case OMAP_DMA_DATA_BURST_4: | ||
| 224 | w |= (0x01 << 7); | ||
| 225 | break; | ||
| 226 | case OMAP_DMA_DATA_BURST_8: | ||
| 227 | /* not supported by current hardware | ||
| 228 | * w |= (0x03 << 7); | ||
| 229 | * fall through | ||
| 230 | */ | ||
| 231 | default: | ||
| 232 | BUG(); | ||
| 233 | } | ||
| 234 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 235 | } | ||
| 236 | |||
| 237 | void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, | ||
| 238 | unsigned long dest_start) | ||
| 239 | { | ||
| 240 | u16 w; | ||
| 241 | |||
| 242 | w = omap_readw(OMAP_DMA_CSDP(lch)); | ||
| 243 | w &= ~(0x1f << 9); | ||
| 244 | w |= dest_port << 9; | ||
| 245 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 246 | |||
| 247 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
| 248 | w &= ~(0x03 << 14); | ||
| 249 | w |= dest_amode << 14; | ||
| 250 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
| 251 | |||
| 252 | omap_writew(dest_start >> 16, OMAP_DMA_CDSA_U(lch)); | ||
| 253 | omap_writew(dest_start, OMAP_DMA_CDSA_L(lch)); | ||
| 254 | } | ||
| 255 | |||
| 256 | void omap_set_dma_dest_index(int lch, int eidx, int fidx) | ||
| 257 | { | ||
| 258 | omap_writew(eidx, OMAP_DMA_CDEI(lch)); | ||
| 259 | omap_writew(fidx, OMAP_DMA_CDFI(lch)); | ||
| 260 | } | ||
| 261 | |||
| 262 | void omap_set_dma_dest_data_pack(int lch, int enable) | ||
| 263 | { | ||
| 264 | u16 w; | ||
| 265 | |||
| 266 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 13); | ||
| 267 | w |= enable ? (1 << 13) : 0; | ||
| 268 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 269 | } | ||
| 270 | |||
| 271 | void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) | ||
| 272 | { | ||
| 273 | u16 w; | ||
| 274 | |||
| 275 | w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 14); | ||
| 276 | switch (burst_mode) { | ||
| 277 | case OMAP_DMA_DATA_BURST_DIS: | ||
| 278 | break; | ||
| 279 | case OMAP_DMA_DATA_BURST_4: | ||
| 280 | w |= (0x01 << 14); | ||
| 281 | break; | ||
| 282 | case OMAP_DMA_DATA_BURST_8: | ||
| 283 | w |= (0x03 << 14); | ||
| 284 | break; | ||
| 285 | default: | ||
| 286 | printk(KERN_ERR "Invalid DMA burst mode\n"); | ||
| 287 | BUG(); | ||
| 288 | return; | ||
| 289 | } | ||
| 290 | omap_writew(w, OMAP_DMA_CSDP(lch)); | ||
| 291 | } | ||
| 292 | |||
| 293 | static inline void init_intr(int lch) | ||
| 294 | { | ||
| 295 | u16 w; | ||
| 296 | |||
| 297 | /* Read CSR to make sure it's cleared. */ | ||
| 298 | w = omap_readw(OMAP_DMA_CSR(lch)); | ||
| 299 | /* Enable some nice interrupts. */ | ||
| 300 | omap_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR(lch)); | ||
| 301 | dma_chan[lch].flags |= OMAP_DMA_ACTIVE; | ||
| 302 | } | ||
| 303 | |||
| 304 | static inline void enable_lnk(int lch) | ||
| 305 | { | ||
| 306 | u16 w; | ||
| 307 | |||
| 308 | /* Clear the STOP_LNK bits */ | ||
| 309 | w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); | ||
| 310 | w &= ~(1 << 14); | ||
| 311 | omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); | ||
| 312 | |||
| 313 | /* And set the ENABLE_LNK bits */ | ||
| 314 | if (dma_chan[lch].next_lch != -1) | ||
| 315 | omap_writew(dma_chan[lch].next_lch | (1 << 15), | ||
| 316 | OMAP_DMA_CLNK_CTRL(lch)); | ||
| 317 | } | ||
| 318 | |||
| 319 | static inline void disable_lnk(int lch) | ||
| 320 | { | ||
| 321 | u16 w; | ||
| 322 | |||
| 323 | /* Disable interrupts */ | ||
| 324 | omap_writew(0, OMAP_DMA_CICR(lch)); | ||
| 325 | |||
| 326 | /* Set the STOP_LNK bit */ | ||
| 327 | w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); | ||
| 328 | w |= (1 << 14); | ||
| 329 | w = omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); | ||
| 330 | |||
| 331 | dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; | ||
| 332 | } | ||
| 333 | |||
| 334 | void omap_start_dma(int lch) | ||
| 335 | { | ||
| 336 | u16 w; | ||
| 337 | |||
| 338 | if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { | ||
| 339 | int next_lch, cur_lch; | ||
| 340 | char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
| 341 | |||
| 342 | dma_chan_link_map[lch] = 1; | ||
| 343 | /* Set the link register of the first channel */ | ||
| 344 | enable_lnk(lch); | ||
| 345 | |||
| 346 | memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); | ||
| 347 | cur_lch = dma_chan[lch].next_lch; | ||
| 348 | do { | ||
| 349 | next_lch = dma_chan[cur_lch].next_lch; | ||
| 350 | |||
| 351 | /* The loop case: we've been here already */ | ||
| 352 | if (dma_chan_link_map[cur_lch]) | ||
| 353 | break; | ||
| 354 | /* Mark the current channel */ | ||
| 355 | dma_chan_link_map[cur_lch] = 1; | ||
| 356 | |||
| 357 | enable_lnk(cur_lch); | ||
| 358 | init_intr(cur_lch); | ||
| 359 | |||
| 360 | cur_lch = next_lch; | ||
| 361 | } while (next_lch != -1); | ||
| 362 | } | ||
| 363 | |||
| 364 | init_intr(lch); | ||
| 365 | |||
| 366 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
| 367 | w |= OMAP_DMA_CCR_EN; | ||
| 368 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
| 369 | dma_chan[lch].flags |= OMAP_DMA_ACTIVE; | ||
| 370 | } | ||
| 371 | |||
| 372 | void omap_stop_dma(int lch) | ||
| 373 | { | ||
| 374 | u16 w; | ||
| 375 | |||
| 376 | if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { | ||
| 377 | int next_lch, cur_lch = lch; | ||
| 378 | char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; | ||
| 379 | |||
| 380 | memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); | ||
| 381 | do { | ||
| 382 | /* The loop case: we've been here already */ | ||
| 383 | if (dma_chan_link_map[cur_lch]) | ||
| 384 | break; | ||
| 385 | /* Mark the current channel */ | ||
| 386 | dma_chan_link_map[cur_lch] = 1; | ||
| 387 | |||
| 388 | disable_lnk(cur_lch); | ||
| 389 | |||
| 390 | next_lch = dma_chan[cur_lch].next_lch; | ||
| 391 | cur_lch = next_lch; | ||
| 392 | } while (next_lch != -1); | ||
| 393 | |||
| 394 | return; | ||
| 395 | } | ||
| 396 | /* Disable all interrupts on the channel */ | ||
| 397 | omap_writew(0, OMAP_DMA_CICR(lch)); | ||
| 398 | |||
| 399 | w = omap_readw(OMAP_DMA_CCR(lch)); | ||
| 400 | w &= ~OMAP_DMA_CCR_EN; | ||
| 401 | omap_writew(w, OMAP_DMA_CCR(lch)); | ||
| 402 | dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; | ||
| 403 | } | ||
| 404 | |||
| 405 | void omap_enable_dma_irq(int lch, u16 bits) | ||
| 406 | { | ||
| 407 | dma_chan[lch].enabled_irqs |= bits; | ||
| 408 | } | ||
| 409 | |||
| 410 | void omap_disable_dma_irq(int lch, u16 bits) | ||
| 411 | { | ||
| 412 | dma_chan[lch].enabled_irqs &= ~bits; | ||
| 413 | } | ||
| 414 | |||
| 415 | static int dma_handle_ch(int ch) | ||
| 416 | { | ||
| 417 | u16 csr; | ||
| 418 | |||
| 419 | if (enable_1510_mode && ch >= 6) { | ||
| 420 | csr = dma_chan[ch].saved_csr; | ||
| 421 | dma_chan[ch].saved_csr = 0; | ||
| 422 | } else | ||
| 423 | csr = omap_readw(OMAP_DMA_CSR(ch)); | ||
| 424 | if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { | ||
| 425 | dma_chan[ch + 6].saved_csr = csr >> 7; | ||
| 426 | csr &= 0x7f; | ||
| 427 | } | ||
| 428 | if (!csr) | ||
| 429 | return 0; | ||
| 430 | if (unlikely(dma_chan[ch].dev_id == -1)) { | ||
| 431 | printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n", | ||
| 432 | ch, csr); | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | if (unlikely(csr & OMAP_DMA_TOUT_IRQ)) | ||
| 436 | printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id); | ||
| 437 | if (unlikely(csr & OMAP_DMA_DROP_IRQ)) | ||
| 438 | printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n", | ||
| 439 | dma_chan[ch].dev_id); | ||
| 440 | if (likely(csr & OMAP_DMA_BLOCK_IRQ)) | ||
| 441 | dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; | ||
| 442 | if (likely(dma_chan[ch].callback != NULL)) | ||
| 443 | dma_chan[ch].callback(ch, csr, dma_chan[ch].data); | ||
| 444 | return 1; | ||
| 445 | } | ||
| 446 | |||
| 447 | static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
| 448 | { | ||
| 449 | int ch = ((int) dev_id) - 1; | ||
| 450 | int handled = 0; | ||
| 451 | |||
| 452 | for (;;) { | ||
| 453 | int handled_now = 0; | ||
| 454 | |||
| 455 | handled_now += dma_handle_ch(ch); | ||
| 456 | if (enable_1510_mode && dma_chan[ch + 6].saved_csr) | ||
| 457 | handled_now += dma_handle_ch(ch + 6); | ||
| 458 | if (!handled_now) | ||
| 459 | break; | ||
| 460 | handled += handled_now; | ||
| 461 | } | ||
| 462 | |||
| 463 | return handled ? IRQ_HANDLED : IRQ_NONE; | ||
| 464 | } | ||
| 465 | |||
| 466 | int omap_request_dma(int dev_id, const char *dev_name, | ||
| 467 | void (* callback)(int lch, u16 ch_status, void *data), | ||
| 468 | void *data, int *dma_ch_out) | ||
| 469 | { | ||
| 470 | int ch, free_ch = -1; | ||
| 471 | unsigned long flags; | ||
| 472 | struct omap_dma_lch *chan; | ||
| 473 | |||
| 474 | spin_lock_irqsave(&dma_chan_lock, flags); | ||
| 475 | for (ch = 0; ch < dma_chan_count; ch++) { | ||
| 476 | if (free_ch == -1 && dma_chan[ch].dev_id == -1) { | ||
| 477 | free_ch = ch; | ||
| 478 | if (dev_id == 0) | ||
| 479 | break; | ||
| 480 | } | ||
| 481 | } | ||
| 482 | if (free_ch == -1) { | ||
| 483 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
| 484 | return -EBUSY; | ||
| 485 | } | ||
| 486 | chan = dma_chan + free_ch; | ||
| 487 | chan->dev_id = dev_id; | ||
| 488 | clear_lch_regs(free_ch); | ||
| 489 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
| 490 | |||
| 491 | chan->dev_id = dev_id; | ||
| 492 | chan->dev_name = dev_name; | ||
| 493 | chan->callback = callback; | ||
| 494 | chan->data = data; | ||
| 495 | chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; | ||
| 496 | |||
| 497 | if (cpu_is_omap16xx()) { | ||
| 498 | /* If the sync device is set, configure it dynamically. */ | ||
| 499 | if (dev_id != 0) { | ||
| 500 | set_gdma_dev(free_ch + 1, dev_id); | ||
| 501 | dev_id = free_ch + 1; | ||
| 502 | } | ||
| 503 | /* Disable the 1510 compatibility mode and set the sync device | ||
| 504 | * id. */ | ||
| 505 | omap_writew(dev_id | (1 << 10), OMAP_DMA_CCR(free_ch)); | ||
| 506 | } else { | ||
| 507 | omap_writew(dev_id, OMAP_DMA_CCR(free_ch)); | ||
| 508 | } | ||
| 509 | *dma_ch_out = free_ch; | ||
| 510 | |||
| 511 | return 0; | ||
| 512 | } | ||
| 513 | |||
| 514 | void omap_free_dma(int ch) | ||
| 515 | { | ||
| 516 | unsigned long flags; | ||
| 517 | |||
| 518 | spin_lock_irqsave(&dma_chan_lock, flags); | ||
| 519 | if (dma_chan[ch].dev_id == -1) { | ||
| 520 | printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch); | ||
| 521 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
| 522 | return; | ||
| 523 | } | ||
| 524 | dma_chan[ch].dev_id = -1; | ||
| 525 | spin_unlock_irqrestore(&dma_chan_lock, flags); | ||
| 526 | |||
| 527 | /* Disable all DMA interrupts for the channel. */ | ||
| 528 | omap_writew(0, OMAP_DMA_CICR(ch)); | ||
| 529 | /* Make sure the DMA transfer is stopped. */ | ||
| 530 | omap_writew(0, OMAP_DMA_CCR(ch)); | ||
| 531 | } | ||
| 532 | |||
| 533 | int omap_dma_in_1510_mode(void) | ||
| 534 | { | ||
| 535 | return enable_1510_mode; | ||
| 536 | } | ||
| 537 | |||
| 538 | /* | ||
| 539 | * lch_queue DMA will start right after lch_head one is finished. | ||
| 540 | * For this DMA link to start, you still need to start (see omap_start_dma) | ||
| 541 | * the first one. That will fire up the entire queue. | ||
| 542 | */ | ||
| 543 | void omap_dma_link_lch (int lch_head, int lch_queue) | ||
| 544 | { | ||
| 545 | if (omap_dma_in_1510_mode()) { | ||
| 546 | printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||
| 547 | BUG(); | ||
| 548 | return; | ||
| 549 | } | ||
| 550 | |||
| 551 | if ((dma_chan[lch_head].dev_id == -1) || | ||
| 552 | (dma_chan[lch_queue].dev_id == -1)) { | ||
| 553 | printk(KERN_ERR "omap_dma: trying to link non requested channels\n"); | ||
| 554 | dump_stack(); | ||
| 555 | } | ||
| 556 | |||
| 557 | dma_chan[lch_head].next_lch = lch_queue; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* | ||
| 561 | * Once the DMA queue is stopped, we can destroy it. | ||
| 562 | */ | ||
| 563 | void omap_dma_unlink_lch (int lch_head, int lch_queue) | ||
| 564 | { | ||
| 565 | if (omap_dma_in_1510_mode()) { | ||
| 566 | printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); | ||
| 567 | BUG(); | ||
| 568 | return; | ||
| 569 | } | ||
| 570 | |||
| 571 | if (dma_chan[lch_head].next_lch != lch_queue || | ||
| 572 | dma_chan[lch_head].next_lch == -1) { | ||
| 573 | printk(KERN_ERR "omap_dma: trying to unlink non linked channels\n"); | ||
| 574 | dump_stack(); | ||
| 575 | } | ||
| 576 | |||
| 577 | |||
| 578 | if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || | ||
| 579 | (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) { | ||
| 580 | printk(KERN_ERR "omap_dma: You need to stop the DMA channels before unlinking\n"); | ||
| 581 | dump_stack(); | ||
| 582 | } | ||
| 583 | |||
| 584 | dma_chan[lch_head].next_lch = -1; | ||
| 585 | } | ||
| 586 | |||
| 587 | |||
| 588 | static struct lcd_dma_info { | ||
| 589 | spinlock_t lock; | ||
| 590 | int reserved; | ||
| 591 | void (* callback)(u16 status, void *data); | ||
| 592 | void *cb_data; | ||
| 593 | |||
| 594 | int active; | ||
| 595 | unsigned long addr, size; | ||
| 596 | int rotate, data_type, xres, yres; | ||
| 597 | int vxres; | ||
| 598 | int mirror; | ||
| 599 | int xscale, yscale; | ||
| 600 | int ext_ctrl; | ||
| 601 | int src_port; | ||
| 602 | int single_transfer; | ||
| 603 | } lcd_dma; | ||
| 604 | |||
| 605 | void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, | ||
| 606 | int data_type) | ||
| 607 | { | ||
| 608 | lcd_dma.addr = addr; | ||
| 609 | lcd_dma.data_type = data_type; | ||
| 610 | lcd_dma.xres = fb_xres; | ||
| 611 | lcd_dma.yres = fb_yres; | ||
| 612 | } | ||
| 613 | |||
| 614 | void omap_set_lcd_dma_src_port(int port) | ||
| 615 | { | ||
| 616 | lcd_dma.src_port = port; | ||
| 617 | } | ||
| 618 | |||
| 619 | void omap_set_lcd_dma_ext_controller(int external) | ||
| 620 | { | ||
| 621 | lcd_dma.ext_ctrl = external; | ||
| 622 | } | ||
| 623 | |||
| 624 | void omap_set_lcd_dma_single_transfer(int single) | ||
| 625 | { | ||
| 626 | lcd_dma.single_transfer = single; | ||
| 627 | } | ||
| 628 | |||
| 629 | |||
| 630 | void omap_set_lcd_dma_b1_rotation(int rotate) | ||
| 631 | { | ||
| 632 | if (omap_dma_in_1510_mode()) { | ||
| 633 | printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); | ||
| 634 | BUG(); | ||
| 635 | return; | ||
| 636 | } | ||
| 637 | lcd_dma.rotate = rotate; | ||
| 638 | } | ||
| 639 | |||
| 640 | void omap_set_lcd_dma_b1_mirror(int mirror) | ||
| 641 | { | ||
| 642 | if (omap_dma_in_1510_mode()) { | ||
| 643 | printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); | ||
| 644 | BUG(); | ||
| 645 | } | ||
| 646 | lcd_dma.mirror = mirror; | ||
| 647 | } | ||
| 648 | |||
| 649 | void omap_set_lcd_dma_b1_vxres(unsigned long vxres) | ||
| 650 | { | ||
| 651 | if (omap_dma_in_1510_mode()) { | ||
| 652 | printk(KERN_ERR "DMA virtual resulotion is not supported " | ||
| 653 | "in 1510 mode\n"); | ||
| 654 | BUG(); | ||
| 655 | } | ||
| 656 | lcd_dma.vxres = vxres; | ||
| 657 | } | ||
| 658 | |||
| 659 | void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) | ||
| 660 | { | ||
| 661 | if (omap_dma_in_1510_mode()) { | ||
| 662 | printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); | ||
| 663 | BUG(); | ||
| 664 | } | ||
| 665 | lcd_dma.xscale = xscale; | ||
| 666 | lcd_dma.yscale = yscale; | ||
| 667 | } | ||
| 668 | |||
| 669 | static void set_b1_regs(void) | ||
| 670 | { | ||
| 671 | unsigned long top, bottom; | ||
| 672 | int es; | ||
| 673 | u16 w; | ||
| 674 | unsigned long en, fn; | ||
| 675 | long ei, fi; | ||
| 676 | unsigned long vxres; | ||
| 677 | unsigned int xscale, yscale; | ||
| 678 | |||
| 679 | switch (lcd_dma.data_type) { | ||
| 680 | case OMAP_DMA_DATA_TYPE_S8: | ||
| 681 | es = 1; | ||
| 682 | break; | ||
| 683 | case OMAP_DMA_DATA_TYPE_S16: | ||
| 684 | es = 2; | ||
| 685 | break; | ||
| 686 | case OMAP_DMA_DATA_TYPE_S32: | ||
| 687 | es = 4; | ||
| 688 | break; | ||
| 689 | default: | ||
| 690 | BUG(); | ||
| 691 | return; | ||
| 692 | } | ||
| 693 | |||
| 694 | vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres; | ||
| 695 | xscale = lcd_dma.xscale ? lcd_dma.xscale : 1; | ||
| 696 | yscale = lcd_dma.yscale ? lcd_dma.yscale : 1; | ||
| 697 | BUG_ON(vxres < lcd_dma.xres); | ||
| 698 | #define PIXADDR(x,y) (lcd_dma.addr + ((y) * vxres * yscale + (x) * xscale) * es) | ||
| 699 | #define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1) | ||
| 700 | switch (lcd_dma.rotate) { | ||
| 701 | case 0: | ||
| 702 | if (!lcd_dma.mirror) { | ||
| 703 | top = PIXADDR(0, 0); | ||
| 704 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
| 705 | /* 1510 DMA requires the bottom address to be 2 more | ||
| 706 | * than the actual last memory access location. */ | ||
| 707 | if (omap_dma_in_1510_mode() && | ||
| 708 | lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) | ||
| 709 | bottom += 2; | ||
| 710 | ei = PIXSTEP(0, 0, 1, 0); | ||
| 711 | fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1); | ||
| 712 | } else { | ||
| 713 | top = PIXADDR(lcd_dma.xres - 1, 0); | ||
| 714 | bottom = PIXADDR(0, lcd_dma.yres - 1); | ||
| 715 | ei = PIXSTEP(1, 0, 0, 0); | ||
| 716 | fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1); | ||
| 717 | } | ||
| 718 | en = lcd_dma.xres; | ||
| 719 | fn = lcd_dma.yres; | ||
| 720 | break; | ||
| 721 | case 90: | ||
| 722 | if (!lcd_dma.mirror) { | ||
| 723 | top = PIXADDR(0, lcd_dma.yres - 1); | ||
| 724 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | ||
| 725 | ei = PIXSTEP(0, 1, 0, 0); | ||
| 726 | fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1); | ||
| 727 | } else { | ||
| 728 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
| 729 | bottom = PIXADDR(0, 0); | ||
| 730 | ei = PIXSTEP(0, 1, 0, 0); | ||
| 731 | fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1); | ||
| 732 | } | ||
| 733 | en = lcd_dma.yres; | ||
| 734 | fn = lcd_dma.xres; | ||
| 735 | break; | ||
| 736 | case 180: | ||
| 737 | if (!lcd_dma.mirror) { | ||
| 738 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
| 739 | bottom = PIXADDR(0, 0); | ||
| 740 | ei = PIXSTEP(1, 0, 0, 0); | ||
| 741 | fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0); | ||
| 742 | } else { | ||
| 743 | top = PIXADDR(0, lcd_dma.yres - 1); | ||
| 744 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | ||
| 745 | ei = PIXSTEP(0, 0, 1, 0); | ||
| 746 | fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0); | ||
| 747 | } | ||
| 748 | en = lcd_dma.xres; | ||
| 749 | fn = lcd_dma.yres; | ||
| 750 | break; | ||
| 751 | case 270: | ||
| 752 | if (!lcd_dma.mirror) { | ||
| 753 | top = PIXADDR(lcd_dma.xres - 1, 0); | ||
| 754 | bottom = PIXADDR(0, lcd_dma.yres - 1); | ||
| 755 | ei = PIXSTEP(0, 0, 0, 1); | ||
| 756 | fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0); | ||
| 757 | } else { | ||
| 758 | top = PIXADDR(0, 0); | ||
| 759 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | ||
| 760 | ei = PIXSTEP(0, 0, 0, 1); | ||
| 761 | fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0); | ||
| 762 | } | ||
| 763 | en = lcd_dma.yres; | ||
| 764 | fn = lcd_dma.xres; | ||
| 765 | break; | ||
| 766 | default: | ||
| 767 | BUG(); | ||
| 768 | return; /* Supress warning about uninitialized vars */ | ||
| 769 | } | ||
| 770 | |||
| 771 | if (omap_dma_in_1510_mode()) { | ||
| 772 | omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); | ||
| 773 | omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); | ||
| 774 | omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); | ||
| 775 | omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L); | ||
| 776 | |||
| 777 | return; | ||
| 778 | } | ||
| 779 | |||
| 780 | /* 1610 regs */ | ||
| 781 | omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U); | ||
| 782 | omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L); | ||
| 783 | omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U); | ||
| 784 | omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L); | ||
| 785 | |||
| 786 | omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1); | ||
| 787 | omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1); | ||
| 788 | |||
| 789 | w = omap_readw(OMAP1610_DMA_LCD_CSDP); | ||
| 790 | w &= ~0x03; | ||
| 791 | w |= lcd_dma.data_type; | ||
| 792 | omap_writew(w, OMAP1610_DMA_LCD_CSDP); | ||
| 793 | |||
| 794 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
| 795 | /* Always set the source port as SDRAM for now*/ | ||
| 796 | w &= ~(0x03 << 6); | ||
| 797 | if (lcd_dma.ext_ctrl) | ||
| 798 | w |= 1 << 8; | ||
| 799 | else | ||
| 800 | w &= ~(1 << 8); | ||
| 801 | if (lcd_dma.callback != NULL) | ||
| 802 | w |= 1 << 1; /* Block interrupt enable */ | ||
| 803 | else | ||
| 804 | w &= ~(1 << 1); | ||
| 805 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
| 806 | |||
| 807 | if (!(lcd_dma.rotate || lcd_dma.mirror || | ||
| 808 | lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) | ||
| 809 | return; | ||
| 810 | |||
| 811 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
| 812 | /* Set the double-indexed addressing mode */ | ||
| 813 | w |= (0x03 << 12); | ||
| 814 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
| 815 | |||
| 816 | omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); | ||
| 817 | omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); | ||
| 818 | omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); | ||
| 819 | } | ||
| 820 | |||
| 821 | static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
| 822 | { | ||
| 823 | u16 w; | ||
| 824 | |||
| 825 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | ||
| 826 | if (unlikely(!(w & (1 << 3)))) { | ||
| 827 | printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); | ||
| 828 | return IRQ_NONE; | ||
| 829 | } | ||
| 830 | /* Ack the IRQ */ | ||
| 831 | w |= (1 << 3); | ||
| 832 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | ||
| 833 | lcd_dma.active = 0; | ||
| 834 | if (lcd_dma.callback != NULL) | ||
| 835 | lcd_dma.callback(w, lcd_dma.cb_data); | ||
| 836 | |||
| 837 | return IRQ_HANDLED; | ||
| 838 | } | ||
| 839 | |||
| 840 | int omap_request_lcd_dma(void (* callback)(u16 status, void *data), | ||
| 841 | void *data) | ||
| 842 | { | ||
| 843 | spin_lock_irq(&lcd_dma.lock); | ||
| 844 | if (lcd_dma.reserved) { | ||
| 845 | spin_unlock_irq(&lcd_dma.lock); | ||
| 846 | printk(KERN_ERR "LCD DMA channel already reserved\n"); | ||
| 847 | BUG(); | ||
| 848 | return -EBUSY; | ||
| 849 | } | ||
| 850 | lcd_dma.reserved = 1; | ||
| 851 | spin_unlock_irq(&lcd_dma.lock); | ||
| 852 | lcd_dma.callback = callback; | ||
| 853 | lcd_dma.cb_data = data; | ||
| 854 | lcd_dma.active = 0; | ||
| 855 | lcd_dma.single_transfer = 0; | ||
| 856 | lcd_dma.rotate = 0; | ||
| 857 | lcd_dma.vxres = 0; | ||
| 858 | lcd_dma.mirror = 0; | ||
| 859 | lcd_dma.xscale = 0; | ||
| 860 | lcd_dma.yscale = 0; | ||
| 861 | lcd_dma.ext_ctrl = 0; | ||
| 862 | lcd_dma.src_port = 0; | ||
| 863 | |||
| 864 | return 0; | ||
| 865 | } | ||
| 866 | |||
| 867 | void omap_free_lcd_dma(void) | ||
| 868 | { | ||
| 869 | spin_lock(&lcd_dma.lock); | ||
| 870 | if (!lcd_dma.reserved) { | ||
| 871 | spin_unlock(&lcd_dma.lock); | ||
| 872 | printk(KERN_ERR "LCD DMA is not reserved\n"); | ||
| 873 | BUG(); | ||
| 874 | return; | ||
| 875 | } | ||
| 876 | if (!enable_1510_mode) | ||
| 877 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); | ||
| 878 | lcd_dma.reserved = 0; | ||
| 879 | spin_unlock(&lcd_dma.lock); | ||
| 880 | } | ||
| 881 | |||
| 882 | void omap_enable_lcd_dma(void) | ||
| 883 | { | ||
| 884 | u16 w; | ||
| 885 | |||
| 886 | /* Set the Enable bit only if an external controller is | ||
| 887 | * connected. Otherwise the OMAP internal controller will | ||
| 888 | * start the transfer when it gets enabled. | ||
| 889 | */ | ||
| 890 | if (enable_1510_mode || !lcd_dma.ext_ctrl) | ||
| 891 | return; | ||
| 892 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
| 893 | w |= 1 << 7; | ||
| 894 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
| 895 | lcd_dma.active = 1; | ||
| 896 | } | ||
| 897 | |||
| 898 | void omap_setup_lcd_dma(void) | ||
| 899 | { | ||
| 900 | BUG_ON(lcd_dma.active); | ||
| 901 | if (!enable_1510_mode) { | ||
| 902 | /* Set some reasonable defaults */ | ||
| 903 | omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); | ||
| 904 | omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); | ||
| 905 | omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); | ||
| 906 | } | ||
| 907 | set_b1_regs(); | ||
| 908 | if (!enable_1510_mode) { | ||
| 909 | u16 w; | ||
| 910 | |||
| 911 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | ||
| 912 | /* If DMA was already active set the end_prog bit to have | ||
| 913 | * the programmed register set loaded into the active | ||
| 914 | * register set. | ||
| 915 | */ | ||
| 916 | w |= 1 << 11; /* End_prog */ | ||
| 917 | if (!lcd_dma.single_transfer) | ||
| 918 | w |= (3 << 8); /* Auto_init, repeat */ | ||
| 919 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | void omap_stop_lcd_dma(void) | ||
| 924 | { | ||
| 925 | lcd_dma.active = 0; | ||
| 926 | if (!enable_1510_mode && lcd_dma.ext_ctrl) | ||
| 927 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~(1 << 7), | ||
| 928 | OMAP1610_DMA_LCD_CCR); | ||
| 929 | } | ||
| 930 | |||
| 931 | /* | ||
| 932 | * Clears any DMA state so the DMA engine is ready to restart with new buffers | ||
| 933 | * through omap_start_dma(). Any buffers in flight are discarded. | ||
| 934 | */ | ||
| 935 | void omap_clear_dma(int lch) | ||
| 936 | { | ||
| 937 | unsigned long flags; | ||
| 938 | int status; | ||
| 939 | |||
| 940 | local_irq_save(flags); | ||
| 941 | omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN, | ||
| 942 | OMAP_DMA_CCR(lch)); | ||
| 943 | status = OMAP_DMA_CSR(lch); /* clear pending interrupts */ | ||
| 944 | local_irq_restore(flags); | ||
| 945 | } | ||
| 946 | |||
| 947 | /* | ||
| 948 | * Returns current physical source address for the given DMA channel. | ||
| 949 | * If the channel is running the caller must disable interrupts prior calling | ||
| 950 | * this function and process the returned value before re-enabling interrupt to | ||
| 951 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
| 952 | * is a chance for CSSA_L register overflow inbetween the two reads resulting | ||
| 953 | * in incorrect return value. | ||
| 954 | */ | ||
| 955 | dma_addr_t omap_get_dma_src_pos(int lch) | ||
| 956 | { | ||
| 957 | return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) | | ||
| 958 | (OMAP_DMA_CSSA_U(lch) << 16)); | ||
| 959 | } | ||
| 960 | |||
| 961 | /* | ||
| 962 | * Returns current physical destination address for the given DMA channel. | ||
| 963 | * If the channel is running the caller must disable interrupts prior calling | ||
| 964 | * this function and process the returned value before re-enabling interrupt to | ||
| 965 | * prevent races with the interrupt handler. Note that in continuous mode there | ||
| 966 | * is a chance for CDSA_L register overflow inbetween the two reads resulting | ||
| 967 | * in incorrect return value. | ||
| 968 | */ | ||
| 969 | dma_addr_t omap_get_dma_dst_pos(int lch) | ||
| 970 | { | ||
| 971 | return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) | | ||
| 972 | (OMAP_DMA_CDSA_U(lch) << 16)); | ||
| 973 | } | ||
| 974 | |||
| 975 | static int __init omap_init_dma(void) | ||
| 976 | { | ||
| 977 | int ch, r; | ||
| 978 | |||
| 979 | if (cpu_is_omap1510()) { | ||
| 980 | printk(KERN_INFO "DMA support for OMAP1510 initialized\n"); | ||
| 981 | dma_chan_count = 9; | ||
| 982 | enable_1510_mode = 1; | ||
| 983 | } else if (cpu_is_omap16xx() || cpu_is_omap730()) { | ||
| 984 | printk(KERN_INFO "OMAP DMA hardware version %d\n", | ||
| 985 | omap_readw(OMAP_DMA_HW_ID)); | ||
| 986 | printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", | ||
| 987 | (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L), | ||
| 988 | (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L), | ||
| 989 | omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3), | ||
| 990 | omap_readw(OMAP_DMA_CAPS_4)); | ||
| 991 | if (!enable_1510_mode) { | ||
| 992 | u16 w; | ||
| 993 | |||
| 994 | /* Disable OMAP 3.0/3.1 compatibility mode. */ | ||
| 995 | w = omap_readw(OMAP_DMA_GSCR); | ||
| 996 | w |= 1 << 3; | ||
| 997 | omap_writew(w, OMAP_DMA_GSCR); | ||
| 998 | dma_chan_count = 16; | ||
| 999 | } else | ||
| 1000 | dma_chan_count = 9; | ||
| 1001 | } else { | ||
| 1002 | dma_chan_count = 0; | ||
| 1003 | return 0; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | memset(&lcd_dma, 0, sizeof(lcd_dma)); | ||
| 1007 | spin_lock_init(&lcd_dma.lock); | ||
| 1008 | spin_lock_init(&dma_chan_lock); | ||
| 1009 | memset(&dma_chan, 0, sizeof(dma_chan)); | ||
| 1010 | |||
| 1011 | for (ch = 0; ch < dma_chan_count; ch++) { | ||
| 1012 | dma_chan[ch].dev_id = -1; | ||
| 1013 | dma_chan[ch].next_lch = -1; | ||
| 1014 | |||
| 1015 | if (ch >= 6 && enable_1510_mode) | ||
| 1016 | continue; | ||
| 1017 | |||
| 1018 | /* request_irq() doesn't like dev_id (ie. ch) being zero, | ||
| 1019 | * so we have to kludge around this. */ | ||
| 1020 | r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA", | ||
| 1021 | (void *) (ch + 1)); | ||
| 1022 | if (r != 0) { | ||
| 1023 | int i; | ||
| 1024 | |||
| 1025 | printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n", | ||
| 1026 | dma_irq[ch], r); | ||
| 1027 | for (i = 0; i < ch; i++) | ||
| 1028 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
| 1029 | return r; | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL); | ||
| 1033 | if (r != 0) { | ||
| 1034 | int i; | ||
| 1035 | |||
| 1036 | printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r); | ||
| 1037 | for (i = 0; i < dma_chan_count; i++) | ||
| 1038 | free_irq(dma_irq[i], (void *) (i + 1)); | ||
| 1039 | return r; | ||
| 1040 | } | ||
| 1041 | return 0; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | arch_initcall(omap_init_dma); | ||
| 1045 | |||
| 1046 | |||
| 1047 | EXPORT_SYMBOL(omap_get_dma_src_pos); | ||
| 1048 | EXPORT_SYMBOL(omap_get_dma_dst_pos); | ||
| 1049 | EXPORT_SYMBOL(omap_clear_dma); | ||
| 1050 | EXPORT_SYMBOL(omap_set_dma_priority); | ||
| 1051 | EXPORT_SYMBOL(omap_request_dma); | ||
| 1052 | EXPORT_SYMBOL(omap_free_dma); | ||
| 1053 | EXPORT_SYMBOL(omap_start_dma); | ||
| 1054 | EXPORT_SYMBOL(omap_stop_dma); | ||
| 1055 | EXPORT_SYMBOL(omap_enable_dma_irq); | ||
| 1056 | EXPORT_SYMBOL(omap_disable_dma_irq); | ||
| 1057 | |||
| 1058 | EXPORT_SYMBOL(omap_set_dma_transfer_params); | ||
| 1059 | EXPORT_SYMBOL(omap_set_dma_color_mode); | ||
| 1060 | |||
| 1061 | EXPORT_SYMBOL(omap_set_dma_src_params); | ||
| 1062 | EXPORT_SYMBOL(omap_set_dma_src_index); | ||
| 1063 | EXPORT_SYMBOL(omap_set_dma_src_data_pack); | ||
| 1064 | EXPORT_SYMBOL(omap_set_dma_src_burst_mode); | ||
| 1065 | |||
| 1066 | EXPORT_SYMBOL(omap_set_dma_dest_params); | ||
| 1067 | EXPORT_SYMBOL(omap_set_dma_dest_index); | ||
| 1068 | EXPORT_SYMBOL(omap_set_dma_dest_data_pack); | ||
| 1069 | EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); | ||
| 1070 | |||
| 1071 | EXPORT_SYMBOL(omap_dma_link_lch); | ||
| 1072 | EXPORT_SYMBOL(omap_dma_unlink_lch); | ||
| 1073 | |||
| 1074 | EXPORT_SYMBOL(omap_request_lcd_dma); | ||
| 1075 | EXPORT_SYMBOL(omap_free_lcd_dma); | ||
| 1076 | EXPORT_SYMBOL(omap_enable_lcd_dma); | ||
| 1077 | EXPORT_SYMBOL(omap_setup_lcd_dma); | ||
| 1078 | EXPORT_SYMBOL(omap_stop_lcd_dma); | ||
| 1079 | EXPORT_SYMBOL(omap_set_lcd_dma_b1); | ||
| 1080 | EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); | ||
| 1081 | EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); | ||
| 1082 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); | ||
| 1083 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); | ||
| 1084 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); | ||
| 1085 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); | ||
| 1086 | |||
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c new file mode 100644 index 000000000000..1c85b4e536c2 --- /dev/null +++ b/arch/arm/plat-omap/gpio.c | |||
| @@ -0,0 +1,762 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/gpio.c | ||
| 3 | * | ||
| 4 | * Support functions for OMAP GPIO | ||
| 5 | * | ||
| 6 | * Copyright (C) 2003 Nokia Corporation | ||
| 7 | * Written by Juha Yrjölä <juha.yrjola@nokia.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/config.h> | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/sched.h> | ||
| 18 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/ptrace.h> | ||
| 20 | |||
| 21 | #include <asm/hardware.h> | ||
| 22 | #include <asm/irq.h> | ||
| 23 | #include <asm/arch/irqs.h> | ||
| 24 | #include <asm/arch/gpio.h> | ||
| 25 | #include <asm/mach/irq.h> | ||
| 26 | |||
| 27 | #include <asm/io.h> | ||
| 28 | |||
| 29 | /* | ||
| 30 | * OMAP1510 GPIO registers | ||
| 31 | */ | ||
| 32 | #define OMAP1510_GPIO_BASE 0xfffce000 | ||
| 33 | #define OMAP1510_GPIO_DATA_INPUT 0x00 | ||
| 34 | #define OMAP1510_GPIO_DATA_OUTPUT 0x04 | ||
| 35 | #define OMAP1510_GPIO_DIR_CONTROL 0x08 | ||
| 36 | #define OMAP1510_GPIO_INT_CONTROL 0x0c | ||
| 37 | #define OMAP1510_GPIO_INT_MASK 0x10 | ||
| 38 | #define OMAP1510_GPIO_INT_STATUS 0x14 | ||
| 39 | #define OMAP1510_GPIO_PIN_CONTROL 0x18 | ||
| 40 | |||
| 41 | #define OMAP1510_IH_GPIO_BASE 64 | ||
| 42 | |||
| 43 | /* | ||
| 44 | * OMAP1610 specific GPIO registers | ||
| 45 | */ | ||
| 46 | #define OMAP1610_GPIO1_BASE 0xfffbe400 | ||
| 47 | #define OMAP1610_GPIO2_BASE 0xfffbec00 | ||
| 48 | #define OMAP1610_GPIO3_BASE 0xfffbb400 | ||
| 49 | #define OMAP1610_GPIO4_BASE 0xfffbbc00 | ||
| 50 | #define OMAP1610_GPIO_REVISION 0x0000 | ||
| 51 | #define OMAP1610_GPIO_SYSCONFIG 0x0010 | ||
| 52 | #define OMAP1610_GPIO_SYSSTATUS 0x0014 | ||
| 53 | #define OMAP1610_GPIO_IRQSTATUS1 0x0018 | ||
| 54 | #define OMAP1610_GPIO_IRQENABLE1 0x001c | ||
| 55 | #define OMAP1610_GPIO_DATAIN 0x002c | ||
| 56 | #define OMAP1610_GPIO_DATAOUT 0x0030 | ||
| 57 | #define OMAP1610_GPIO_DIRECTION 0x0034 | ||
| 58 | #define OMAP1610_GPIO_EDGE_CTRL1 0x0038 | ||
| 59 | #define OMAP1610_GPIO_EDGE_CTRL2 0x003c | ||
| 60 | #define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c | ||
| 61 | #define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 | ||
| 62 | #define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc | ||
| 63 | #define OMAP1610_GPIO_SET_DATAOUT 0x00f0 | ||
| 64 | |||
| 65 | /* | ||
| 66 | * OMAP730 specific GPIO registers | ||
| 67 | */ | ||
| 68 | #define OMAP730_GPIO1_BASE 0xfffbc000 | ||
| 69 | #define OMAP730_GPIO2_BASE 0xfffbc800 | ||
| 70 | #define OMAP730_GPIO3_BASE 0xfffbd000 | ||
| 71 | #define OMAP730_GPIO4_BASE 0xfffbd800 | ||
| 72 | #define OMAP730_GPIO5_BASE 0xfffbe000 | ||
| 73 | #define OMAP730_GPIO6_BASE 0xfffbe800 | ||
| 74 | #define OMAP730_GPIO_DATA_INPUT 0x00 | ||
| 75 | #define OMAP730_GPIO_DATA_OUTPUT 0x04 | ||
| 76 | #define OMAP730_GPIO_DIR_CONTROL 0x08 | ||
| 77 | #define OMAP730_GPIO_INT_CONTROL 0x0c | ||
| 78 | #define OMAP730_GPIO_INT_MASK 0x10 | ||
| 79 | #define OMAP730_GPIO_INT_STATUS 0x14 | ||
| 80 | |||
| 81 | #define OMAP_MPUIO_MASK (~OMAP_MAX_GPIO_LINES & 0xff) | ||
| 82 | |||
| 83 | struct gpio_bank { | ||
| 84 | u32 base; | ||
| 85 | u16 irq; | ||
| 86 | u16 virtual_irq_start; | ||
| 87 | u8 method; | ||
| 88 | u32 reserved_map; | ||
| 89 | spinlock_t lock; | ||
| 90 | }; | ||
| 91 | |||
| 92 | #define METHOD_MPUIO 0 | ||
| 93 | #define METHOD_GPIO_1510 1 | ||
| 94 | #define METHOD_GPIO_1610 2 | ||
| 95 | #define METHOD_GPIO_730 3 | ||
| 96 | |||
| 97 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 98 | static struct gpio_bank gpio_bank_1610[5] = { | ||
| 99 | { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO}, | ||
| 100 | { OMAP1610_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1610 }, | ||
| 101 | { OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 }, | ||
| 102 | { OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 }, | ||
| 103 | { OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 }, | ||
| 104 | }; | ||
| 105 | #endif | ||
| 106 | |||
| 107 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 108 | static struct gpio_bank gpio_bank_1510[2] = { | ||
| 109 | { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, | ||
| 110 | { OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1510 } | ||
| 111 | }; | ||
| 112 | #endif | ||
| 113 | |||
| 114 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 115 | static struct gpio_bank gpio_bank_730[7] = { | ||
| 116 | { OMAP_MPUIO_BASE, INT_730_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, | ||
| 117 | { OMAP730_GPIO1_BASE, INT_730_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_730 }, | ||
| 118 | { OMAP730_GPIO2_BASE, INT_730_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_730 }, | ||
| 119 | { OMAP730_GPIO3_BASE, INT_730_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_730 }, | ||
| 120 | { OMAP730_GPIO4_BASE, INT_730_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_730 }, | ||
| 121 | { OMAP730_GPIO5_BASE, INT_730_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_730 }, | ||
| 122 | { OMAP730_GPIO6_BASE, INT_730_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_730 }, | ||
| 123 | }; | ||
| 124 | #endif | ||
| 125 | |||
| 126 | static struct gpio_bank *gpio_bank; | ||
| 127 | static int gpio_bank_count; | ||
| 128 | |||
| 129 | static inline struct gpio_bank *get_gpio_bank(int gpio) | ||
| 130 | { | ||
| 131 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 132 | if (cpu_is_omap1510()) { | ||
| 133 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
| 134 | return &gpio_bank[0]; | ||
| 135 | return &gpio_bank[1]; | ||
| 136 | } | ||
| 137 | #endif | ||
| 138 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 139 | if (cpu_is_omap16xx()) { | ||
| 140 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
| 141 | return &gpio_bank[0]; | ||
| 142 | return &gpio_bank[1 + (gpio >> 4)]; | ||
| 143 | } | ||
| 144 | #endif | ||
| 145 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 146 | if (cpu_is_omap730()) { | ||
| 147 | if (OMAP_GPIO_IS_MPUIO(gpio)) | ||
| 148 | return &gpio_bank[0]; | ||
| 149 | return &gpio_bank[1 + (gpio >> 5)]; | ||
| 150 | } | ||
| 151 | #endif | ||
| 152 | } | ||
| 153 | |||
| 154 | static inline int get_gpio_index(int gpio) | ||
| 155 | { | ||
| 156 | if (cpu_is_omap730()) | ||
| 157 | return gpio & 0x1f; | ||
| 158 | else | ||
| 159 | return gpio & 0x0f; | ||
| 160 | } | ||
| 161 | |||
| 162 | static inline int gpio_valid(int gpio) | ||
| 163 | { | ||
| 164 | if (gpio < 0) | ||
| 165 | return -1; | ||
| 166 | if (OMAP_GPIO_IS_MPUIO(gpio)) { | ||
| 167 | if ((gpio & OMAP_MPUIO_MASK) > 16) | ||
| 168 | return -1; | ||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 172 | if (cpu_is_omap1510() && gpio < 16) | ||
| 173 | return 0; | ||
| 174 | #endif | ||
| 175 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 176 | if ((cpu_is_omap16xx()) && gpio < 64) | ||
| 177 | return 0; | ||
| 178 | #endif | ||
| 179 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 180 | if (cpu_is_omap730() && gpio < 192) | ||
| 181 | return 0; | ||
| 182 | #endif | ||
| 183 | return -1; | ||
| 184 | } | ||
| 185 | |||
| 186 | static int check_gpio(int gpio) | ||
| 187 | { | ||
| 188 | if (unlikely(gpio_valid(gpio)) < 0) { | ||
| 189 | printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); | ||
| 190 | dump_stack(); | ||
| 191 | return -1; | ||
| 192 | } | ||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) | ||
| 197 | { | ||
| 198 | u32 reg = bank->base; | ||
| 199 | u32 l; | ||
| 200 | |||
| 201 | switch (bank->method) { | ||
| 202 | case METHOD_MPUIO: | ||
| 203 | reg += OMAP_MPUIO_IO_CNTL; | ||
| 204 | break; | ||
| 205 | case METHOD_GPIO_1510: | ||
| 206 | reg += OMAP1510_GPIO_DIR_CONTROL; | ||
| 207 | break; | ||
| 208 | case METHOD_GPIO_1610: | ||
| 209 | reg += OMAP1610_GPIO_DIRECTION; | ||
| 210 | break; | ||
| 211 | case METHOD_GPIO_730: | ||
| 212 | reg += OMAP730_GPIO_DIR_CONTROL; | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | l = __raw_readl(reg); | ||
| 216 | if (is_input) | ||
| 217 | l |= 1 << gpio; | ||
| 218 | else | ||
| 219 | l &= ~(1 << gpio); | ||
| 220 | __raw_writel(l, reg); | ||
| 221 | } | ||
| 222 | |||
| 223 | void omap_set_gpio_direction(int gpio, int is_input) | ||
| 224 | { | ||
| 225 | struct gpio_bank *bank; | ||
| 226 | |||
| 227 | if (check_gpio(gpio) < 0) | ||
| 228 | return; | ||
| 229 | bank = get_gpio_bank(gpio); | ||
| 230 | spin_lock(&bank->lock); | ||
| 231 | _set_gpio_direction(bank, get_gpio_index(gpio), is_input); | ||
| 232 | spin_unlock(&bank->lock); | ||
| 233 | } | ||
| 234 | |||
| 235 | static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) | ||
| 236 | { | ||
| 237 | u32 reg = bank->base; | ||
| 238 | u32 l = 0; | ||
| 239 | |||
| 240 | switch (bank->method) { | ||
| 241 | case METHOD_MPUIO: | ||
| 242 | reg += OMAP_MPUIO_OUTPUT; | ||
| 243 | l = __raw_readl(reg); | ||
| 244 | if (enable) | ||
| 245 | l |= 1 << gpio; | ||
| 246 | else | ||
| 247 | l &= ~(1 << gpio); | ||
| 248 | break; | ||
| 249 | case METHOD_GPIO_1510: | ||
| 250 | reg += OMAP1510_GPIO_DATA_OUTPUT; | ||
| 251 | l = __raw_readl(reg); | ||
| 252 | if (enable) | ||
| 253 | l |= 1 << gpio; | ||
| 254 | else | ||
| 255 | l &= ~(1 << gpio); | ||
| 256 | break; | ||
| 257 | case METHOD_GPIO_1610: | ||
| 258 | if (enable) | ||
| 259 | reg += OMAP1610_GPIO_SET_DATAOUT; | ||
| 260 | else | ||
| 261 | reg += OMAP1610_GPIO_CLEAR_DATAOUT; | ||
| 262 | l = 1 << gpio; | ||
| 263 | break; | ||
| 264 | case METHOD_GPIO_730: | ||
| 265 | reg += OMAP730_GPIO_DATA_OUTPUT; | ||
| 266 | l = __raw_readl(reg); | ||
| 267 | if (enable) | ||
| 268 | l |= 1 << gpio; | ||
| 269 | else | ||
| 270 | l &= ~(1 << gpio); | ||
| 271 | break; | ||
| 272 | default: | ||
| 273 | BUG(); | ||
| 274 | return; | ||
| 275 | } | ||
| 276 | __raw_writel(l, reg); | ||
| 277 | } | ||
| 278 | |||
| 279 | void omap_set_gpio_dataout(int gpio, int enable) | ||
| 280 | { | ||
| 281 | struct gpio_bank *bank; | ||
| 282 | |||
| 283 | if (check_gpio(gpio) < 0) | ||
| 284 | return; | ||
| 285 | bank = get_gpio_bank(gpio); | ||
| 286 | spin_lock(&bank->lock); | ||
| 287 | _set_gpio_dataout(bank, get_gpio_index(gpio), enable); | ||
| 288 | spin_unlock(&bank->lock); | ||
| 289 | } | ||
| 290 | |||
| 291 | int omap_get_gpio_datain(int gpio) | ||
| 292 | { | ||
| 293 | struct gpio_bank *bank; | ||
| 294 | u32 reg; | ||
| 295 | |||
| 296 | if (check_gpio(gpio) < 0) | ||
| 297 | return -1; | ||
| 298 | bank = get_gpio_bank(gpio); | ||
| 299 | reg = bank->base; | ||
| 300 | switch (bank->method) { | ||
| 301 | case METHOD_MPUIO: | ||
| 302 | reg += OMAP_MPUIO_INPUT_LATCH; | ||
| 303 | break; | ||
| 304 | case METHOD_GPIO_1510: | ||
| 305 | reg += OMAP1510_GPIO_DATA_INPUT; | ||
| 306 | break; | ||
| 307 | case METHOD_GPIO_1610: | ||
| 308 | reg += OMAP1610_GPIO_DATAIN; | ||
| 309 | break; | ||
| 310 | case METHOD_GPIO_730: | ||
| 311 | reg += OMAP730_GPIO_DATA_INPUT; | ||
| 312 | break; | ||
| 313 | default: | ||
| 314 | BUG(); | ||
| 315 | return -1; | ||
| 316 | } | ||
| 317 | return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge) | ||
| 321 | { | ||
| 322 | u32 reg = bank->base; | ||
| 323 | u32 l; | ||
| 324 | |||
| 325 | switch (bank->method) { | ||
| 326 | case METHOD_MPUIO: | ||
| 327 | reg += OMAP_MPUIO_GPIO_INT_EDGE; | ||
| 328 | l = __raw_readl(reg); | ||
| 329 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
| 330 | l |= 1 << gpio; | ||
| 331 | else | ||
| 332 | l &= ~(1 << gpio); | ||
| 333 | __raw_writel(l, reg); | ||
| 334 | break; | ||
| 335 | case METHOD_GPIO_1510: | ||
| 336 | reg += OMAP1510_GPIO_INT_CONTROL; | ||
| 337 | l = __raw_readl(reg); | ||
| 338 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
| 339 | l |= 1 << gpio; | ||
| 340 | else | ||
| 341 | l &= ~(1 << gpio); | ||
| 342 | __raw_writel(l, reg); | ||
| 343 | break; | ||
| 344 | case METHOD_GPIO_1610: | ||
| 345 | edge &= 0x03; | ||
| 346 | if (gpio & 0x08) | ||
| 347 | reg += OMAP1610_GPIO_EDGE_CTRL2; | ||
| 348 | else | ||
| 349 | reg += OMAP1610_GPIO_EDGE_CTRL1; | ||
| 350 | gpio &= 0x07; | ||
| 351 | l = __raw_readl(reg); | ||
| 352 | l &= ~(3 << (gpio << 1)); | ||
| 353 | l |= edge << (gpio << 1); | ||
| 354 | __raw_writel(l, reg); | ||
| 355 | break; | ||
| 356 | case METHOD_GPIO_730: | ||
| 357 | reg += OMAP730_GPIO_INT_CONTROL; | ||
| 358 | l = __raw_readl(reg); | ||
| 359 | if (edge == OMAP_GPIO_RISING_EDGE) | ||
| 360 | l |= 1 << gpio; | ||
| 361 | else | ||
| 362 | l &= ~(1 << gpio); | ||
| 363 | __raw_writel(l, reg); | ||
| 364 | break; | ||
| 365 | default: | ||
| 366 | BUG(); | ||
| 367 | return; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | void omap_set_gpio_edge_ctrl(int gpio, int edge) | ||
| 372 | { | ||
| 373 | struct gpio_bank *bank; | ||
| 374 | |||
| 375 | if (check_gpio(gpio) < 0) | ||
| 376 | return; | ||
| 377 | bank = get_gpio_bank(gpio); | ||
| 378 | spin_lock(&bank->lock); | ||
| 379 | _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge); | ||
| 380 | spin_unlock(&bank->lock); | ||
| 381 | } | ||
| 382 | |||
| 383 | |||
| 384 | static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio) | ||
| 385 | { | ||
| 386 | u32 reg = bank->base, l; | ||
| 387 | |||
| 388 | switch (bank->method) { | ||
| 389 | case METHOD_MPUIO: | ||
| 390 | l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE); | ||
| 391 | return (l & (1 << gpio)) ? | ||
| 392 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
| 393 | case METHOD_GPIO_1510: | ||
| 394 | l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL); | ||
| 395 | return (l & (1 << gpio)) ? | ||
| 396 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
| 397 | case METHOD_GPIO_1610: | ||
| 398 | if (gpio & 0x08) | ||
| 399 | reg += OMAP1610_GPIO_EDGE_CTRL2; | ||
| 400 | else | ||
| 401 | reg += OMAP1610_GPIO_EDGE_CTRL1; | ||
| 402 | return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03; | ||
| 403 | case METHOD_GPIO_730: | ||
| 404 | l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL); | ||
| 405 | return (l & (1 << gpio)) ? | ||
| 406 | OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; | ||
| 407 | default: | ||
| 408 | BUG(); | ||
| 409 | return -1; | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) | ||
| 414 | { | ||
| 415 | u32 reg = bank->base; | ||
| 416 | |||
| 417 | switch (bank->method) { | ||
| 418 | case METHOD_MPUIO: | ||
| 419 | /* MPUIO irqstatus is reset by reading the status register, | ||
| 420 | * so do nothing here */ | ||
| 421 | return; | ||
| 422 | case METHOD_GPIO_1510: | ||
| 423 | reg += OMAP1510_GPIO_INT_STATUS; | ||
| 424 | break; | ||
| 425 | case METHOD_GPIO_1610: | ||
| 426 | reg += OMAP1610_GPIO_IRQSTATUS1; | ||
| 427 | break; | ||
| 428 | case METHOD_GPIO_730: | ||
| 429 | reg += OMAP730_GPIO_INT_STATUS; | ||
| 430 | break; | ||
| 431 | default: | ||
| 432 | BUG(); | ||
| 433 | return; | ||
| 434 | } | ||
| 435 | __raw_writel(gpio_mask, reg); | ||
| 436 | } | ||
| 437 | |||
| 438 | static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) | ||
| 439 | { | ||
| 440 | _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); | ||
| 441 | } | ||
| 442 | |||
| 443 | static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) | ||
| 444 | { | ||
| 445 | u32 reg = bank->base; | ||
| 446 | u32 l; | ||
| 447 | |||
| 448 | switch (bank->method) { | ||
| 449 | case METHOD_MPUIO: | ||
| 450 | reg += OMAP_MPUIO_GPIO_MASKIT; | ||
| 451 | l = __raw_readl(reg); | ||
| 452 | if (enable) | ||
| 453 | l &= ~(gpio_mask); | ||
| 454 | else | ||
| 455 | l |= gpio_mask; | ||
| 456 | break; | ||
| 457 | case METHOD_GPIO_1510: | ||
| 458 | reg += OMAP1510_GPIO_INT_MASK; | ||
| 459 | l = __raw_readl(reg); | ||
| 460 | if (enable) | ||
| 461 | l &= ~(gpio_mask); | ||
| 462 | else | ||
| 463 | l |= gpio_mask; | ||
| 464 | break; | ||
| 465 | case METHOD_GPIO_1610: | ||
| 466 | if (enable) | ||
| 467 | reg += OMAP1610_GPIO_SET_IRQENABLE1; | ||
| 468 | else | ||
| 469 | reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; | ||
| 470 | l = gpio_mask; | ||
| 471 | break; | ||
| 472 | case METHOD_GPIO_730: | ||
| 473 | reg += OMAP730_GPIO_INT_MASK; | ||
| 474 | l = __raw_readl(reg); | ||
| 475 | if (enable) | ||
| 476 | l &= ~(gpio_mask); | ||
| 477 | else | ||
| 478 | l |= gpio_mask; | ||
| 479 | break; | ||
| 480 | default: | ||
| 481 | BUG(); | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | __raw_writel(l, reg); | ||
| 485 | } | ||
| 486 | |||
| 487 | static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) | ||
| 488 | { | ||
| 489 | _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); | ||
| 490 | } | ||
| 491 | |||
| 492 | int omap_request_gpio(int gpio) | ||
| 493 | { | ||
| 494 | struct gpio_bank *bank; | ||
| 495 | |||
| 496 | if (check_gpio(gpio) < 0) | ||
| 497 | return -EINVAL; | ||
| 498 | |||
| 499 | bank = get_gpio_bank(gpio); | ||
| 500 | spin_lock(&bank->lock); | ||
| 501 | if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) { | ||
| 502 | printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio); | ||
| 503 | dump_stack(); | ||
| 504 | spin_unlock(&bank->lock); | ||
| 505 | return -1; | ||
| 506 | } | ||
| 507 | bank->reserved_map |= (1 << get_gpio_index(gpio)); | ||
| 508 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 509 | if (bank->method == METHOD_GPIO_1510) { | ||
| 510 | u32 reg; | ||
| 511 | |||
| 512 | /* Claim the pin for the ARM */ | ||
| 513 | reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; | ||
| 514 | __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg); | ||
| 515 | } | ||
| 516 | #endif | ||
| 517 | spin_unlock(&bank->lock); | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | void omap_free_gpio(int gpio) | ||
| 523 | { | ||
| 524 | struct gpio_bank *bank; | ||
| 525 | |||
| 526 | if (check_gpio(gpio) < 0) | ||
| 527 | return; | ||
| 528 | bank = get_gpio_bank(gpio); | ||
| 529 | spin_lock(&bank->lock); | ||
| 530 | if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) { | ||
| 531 | printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio); | ||
| 532 | dump_stack(); | ||
| 533 | spin_unlock(&bank->lock); | ||
| 534 | return; | ||
| 535 | } | ||
| 536 | bank->reserved_map &= ~(1 << get_gpio_index(gpio)); | ||
| 537 | _set_gpio_direction(bank, get_gpio_index(gpio), 1); | ||
| 538 | _set_gpio_irqenable(bank, gpio, 0); | ||
| 539 | _clear_gpio_irqstatus(bank, gpio); | ||
| 540 | spin_unlock(&bank->lock); | ||
| 541 | } | ||
| 542 | |||
| 543 | /* | ||
| 544 | * We need to unmask the GPIO bank interrupt as soon as possible to | ||
| 545 | * avoid missing GPIO interrupts for other lines in the bank. | ||
| 546 | * Then we need to mask-read-clear-unmask the triggered GPIO lines | ||
| 547 | * in the bank to avoid missing nested interrupts for a GPIO line. | ||
| 548 | * If we wait to unmask individual GPIO lines in the bank after the | ||
| 549 | * line's interrupt handler has been run, we may miss some nested | ||
| 550 | * interrupts. | ||
| 551 | */ | ||
| 552 | static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, | ||
| 553 | struct pt_regs *regs) | ||
| 554 | { | ||
| 555 | u32 isr_reg = 0; | ||
| 556 | u32 isr; | ||
| 557 | unsigned int gpio_irq; | ||
| 558 | struct gpio_bank *bank; | ||
| 559 | |||
| 560 | desc->chip->ack(irq); | ||
| 561 | |||
| 562 | bank = (struct gpio_bank *) desc->data; | ||
| 563 | if (bank->method == METHOD_MPUIO) | ||
| 564 | isr_reg = bank->base + OMAP_MPUIO_GPIO_INT; | ||
| 565 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 566 | if (bank->method == METHOD_GPIO_1510) | ||
| 567 | isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; | ||
| 568 | #endif | ||
| 569 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 570 | if (bank->method == METHOD_GPIO_1610) | ||
| 571 | isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; | ||
| 572 | #endif | ||
| 573 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 574 | if (bank->method == METHOD_GPIO_730) | ||
| 575 | isr_reg = bank->base + OMAP730_GPIO_INT_STATUS; | ||
| 576 | #endif | ||
| 577 | |||
| 578 | isr = __raw_readl(isr_reg); | ||
| 579 | _enable_gpio_irqbank(bank, isr, 0); | ||
| 580 | _clear_gpio_irqbank(bank, isr); | ||
| 581 | _enable_gpio_irqbank(bank, isr, 1); | ||
| 582 | desc->chip->unmask(irq); | ||
| 583 | |||
| 584 | if (unlikely(!isr)) | ||
| 585 | return; | ||
| 586 | |||
| 587 | gpio_irq = bank->virtual_irq_start; | ||
| 588 | for (; isr != 0; isr >>= 1, gpio_irq++) { | ||
| 589 | struct irqdesc *d; | ||
| 590 | if (!(isr & 1)) | ||
| 591 | continue; | ||
| 592 | d = irq_desc + gpio_irq; | ||
| 593 | d->handle(gpio_irq, d, regs); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | static void gpio_ack_irq(unsigned int irq) | ||
| 598 | { | ||
| 599 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
| 600 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
| 601 | |||
| 602 | _clear_gpio_irqstatus(bank, gpio); | ||
| 603 | } | ||
| 604 | |||
| 605 | static void gpio_mask_irq(unsigned int irq) | ||
| 606 | { | ||
| 607 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
| 608 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
| 609 | |||
| 610 | _set_gpio_irqenable(bank, gpio, 0); | ||
| 611 | } | ||
| 612 | |||
| 613 | static void gpio_unmask_irq(unsigned int irq) | ||
| 614 | { | ||
| 615 | unsigned int gpio = irq - IH_GPIO_BASE; | ||
| 616 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
| 617 | |||
| 618 | if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) { | ||
| 619 | printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n", | ||
| 620 | gpio); | ||
| 621 | _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE); | ||
| 622 | } | ||
| 623 | _set_gpio_irqenable(bank, gpio, 1); | ||
| 624 | } | ||
| 625 | |||
| 626 | static void mpuio_ack_irq(unsigned int irq) | ||
| 627 | { | ||
| 628 | /* The ISR is reset automatically, so do nothing here. */ | ||
| 629 | } | ||
| 630 | |||
| 631 | static void mpuio_mask_irq(unsigned int irq) | ||
| 632 | { | ||
| 633 | unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); | ||
| 634 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
| 635 | |||
| 636 | _set_gpio_irqenable(bank, gpio, 0); | ||
| 637 | } | ||
| 638 | |||
| 639 | static void mpuio_unmask_irq(unsigned int irq) | ||
| 640 | { | ||
| 641 | unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); | ||
| 642 | struct gpio_bank *bank = get_gpio_bank(gpio); | ||
| 643 | |||
| 644 | _set_gpio_irqenable(bank, gpio, 1); | ||
| 645 | } | ||
| 646 | |||
| 647 | static struct irqchip gpio_irq_chip = { | ||
| 648 | .ack = gpio_ack_irq, | ||
| 649 | .mask = gpio_mask_irq, | ||
| 650 | .unmask = gpio_unmask_irq, | ||
| 651 | }; | ||
| 652 | |||
| 653 | static struct irqchip mpuio_irq_chip = { | ||
| 654 | .ack = mpuio_ack_irq, | ||
| 655 | .mask = mpuio_mask_irq, | ||
| 656 | .unmask = mpuio_unmask_irq | ||
| 657 | }; | ||
| 658 | |||
| 659 | static int initialized = 0; | ||
| 660 | |||
| 661 | static int __init _omap_gpio_init(void) | ||
| 662 | { | ||
| 663 | int i; | ||
| 664 | struct gpio_bank *bank; | ||
| 665 | |||
| 666 | initialized = 1; | ||
| 667 | |||
| 668 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 669 | if (cpu_is_omap1510()) { | ||
| 670 | printk(KERN_INFO "OMAP1510 GPIO hardware\n"); | ||
| 671 | gpio_bank_count = 2; | ||
| 672 | gpio_bank = gpio_bank_1510; | ||
| 673 | } | ||
| 674 | #endif | ||
| 675 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 676 | if (cpu_is_omap16xx()) { | ||
| 677 | int rev; | ||
| 678 | |||
| 679 | gpio_bank_count = 5; | ||
| 680 | gpio_bank = gpio_bank_1610; | ||
| 681 | rev = omap_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION); | ||
| 682 | printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", | ||
| 683 | (rev >> 4) & 0x0f, rev & 0x0f); | ||
| 684 | } | ||
| 685 | #endif | ||
| 686 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 687 | if (cpu_is_omap730()) { | ||
| 688 | printk(KERN_INFO "OMAP730 GPIO hardware\n"); | ||
| 689 | gpio_bank_count = 7; | ||
| 690 | gpio_bank = gpio_bank_730; | ||
| 691 | } | ||
| 692 | #endif | ||
| 693 | for (i = 0; i < gpio_bank_count; i++) { | ||
| 694 | int j, gpio_count = 16; | ||
| 695 | |||
| 696 | bank = &gpio_bank[i]; | ||
| 697 | bank->reserved_map = 0; | ||
| 698 | bank->base = IO_ADDRESS(bank->base); | ||
| 699 | spin_lock_init(&bank->lock); | ||
| 700 | if (bank->method == METHOD_MPUIO) { | ||
| 701 | omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT); | ||
| 702 | } | ||
| 703 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 704 | if (bank->method == METHOD_GPIO_1510) { | ||
| 705 | __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); | ||
| 706 | __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); | ||
| 707 | } | ||
| 708 | #endif | ||
| 709 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 710 | if (bank->method == METHOD_GPIO_1610) { | ||
| 711 | __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); | ||
| 712 | __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); | ||
| 713 | } | ||
| 714 | #endif | ||
| 715 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 716 | if (bank->method == METHOD_GPIO_730) { | ||
| 717 | __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK); | ||
| 718 | __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS); | ||
| 719 | |||
| 720 | gpio_count = 32; /* 730 has 32-bit GPIOs */ | ||
| 721 | } | ||
| 722 | #endif | ||
| 723 | for (j = bank->virtual_irq_start; | ||
| 724 | j < bank->virtual_irq_start + gpio_count; j++) { | ||
| 725 | if (bank->method == METHOD_MPUIO) | ||
| 726 | set_irq_chip(j, &mpuio_irq_chip); | ||
| 727 | else | ||
| 728 | set_irq_chip(j, &gpio_irq_chip); | ||
| 729 | set_irq_handler(j, do_simple_IRQ); | ||
| 730 | set_irq_flags(j, IRQF_VALID); | ||
| 731 | } | ||
| 732 | set_irq_chained_handler(bank->irq, gpio_irq_handler); | ||
| 733 | set_irq_data(bank->irq, bank); | ||
| 734 | } | ||
| 735 | |||
| 736 | /* Enable system clock for GPIO module. | ||
| 737 | * The CAM_CLK_CTRL *is* really the right place. */ | ||
| 738 | if (cpu_is_omap1610() || cpu_is_omap1710()) | ||
| 739 | omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); | ||
| 740 | |||
| 741 | return 0; | ||
| 742 | } | ||
| 743 | |||
| 744 | /* | ||
| 745 | * This may get called early from board specific init | ||
| 746 | */ | ||
| 747 | int omap_gpio_init(void) | ||
| 748 | { | ||
| 749 | if (!initialized) | ||
| 750 | return _omap_gpio_init(); | ||
| 751 | else | ||
| 752 | return 0; | ||
| 753 | } | ||
| 754 | |||
| 755 | EXPORT_SYMBOL(omap_request_gpio); | ||
| 756 | EXPORT_SYMBOL(omap_free_gpio); | ||
| 757 | EXPORT_SYMBOL(omap_set_gpio_direction); | ||
| 758 | EXPORT_SYMBOL(omap_set_gpio_dataout); | ||
| 759 | EXPORT_SYMBOL(omap_get_gpio_datain); | ||
| 760 | EXPORT_SYMBOL(omap_set_gpio_edge_ctrl); | ||
| 761 | |||
| 762 | 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 index 000000000000..10c3f22f9c6a --- /dev/null +++ b/arch/arm/plat-omap/mcbsp.c | |||
| @@ -0,0 +1,685 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/mcbsp.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004 Nokia Corporation | ||
| 5 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> | ||
| 6 | * | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * Multichannel mode not supported. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/wait.h> | ||
| 19 | #include <linux/completion.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include <linux/err.h> | ||
| 22 | |||
| 23 | #include <asm/delay.h> | ||
| 24 | #include <asm/io.h> | ||
| 25 | #include <asm/irq.h> | ||
| 26 | |||
| 27 | #include <asm/arch/dma.h> | ||
| 28 | #include <asm/arch/mux.h> | ||
| 29 | #include <asm/arch/irqs.h> | ||
| 30 | #include <asm/arch/mcbsp.h> | ||
| 31 | |||
| 32 | #include <asm/hardware/clock.h> | ||
| 33 | |||
| 34 | #ifdef CONFIG_MCBSP_DEBUG | ||
| 35 | #define DBG(x...) printk(x) | ||
| 36 | #else | ||
| 37 | #define DBG(x...) do { } while (0) | ||
| 38 | #endif | ||
| 39 | |||
| 40 | struct omap_mcbsp { | ||
| 41 | u32 io_base; | ||
| 42 | u8 id; | ||
| 43 | u8 free; | ||
| 44 | omap_mcbsp_word_length rx_word_length; | ||
| 45 | omap_mcbsp_word_length tx_word_length; | ||
| 46 | |||
| 47 | /* IRQ based TX/RX */ | ||
| 48 | int rx_irq; | ||
| 49 | int tx_irq; | ||
| 50 | |||
| 51 | /* DMA stuff */ | ||
| 52 | u8 dma_rx_sync; | ||
| 53 | short dma_rx_lch; | ||
| 54 | u8 dma_tx_sync; | ||
| 55 | short dma_tx_lch; | ||
| 56 | |||
| 57 | /* Completion queues */ | ||
| 58 | struct completion tx_irq_completion; | ||
| 59 | struct completion rx_irq_completion; | ||
| 60 | struct completion tx_dma_completion; | ||
| 61 | struct completion rx_dma_completion; | ||
| 62 | |||
| 63 | spinlock_t lock; | ||
| 64 | }; | ||
| 65 | |||
| 66 | static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; | ||
| 67 | static struct clk *mcbsp_dsp_ck = 0; | ||
| 68 | static struct clk *mcbsp_api_ck = 0; | ||
| 69 | |||
| 70 | |||
| 71 | static void omap_mcbsp_dump_reg(u8 id) | ||
| 72 | { | ||
| 73 | DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); | ||
| 74 | DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); | ||
| 75 | DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); | ||
| 76 | DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); | ||
| 77 | DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); | ||
| 78 | DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); | ||
| 79 | DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); | ||
| 80 | DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); | ||
| 81 | DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); | ||
| 82 | DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); | ||
| 83 | DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); | ||
| 84 | DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); | ||
| 85 | DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); | ||
| 86 | DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); | ||
| 87 | DBG("***********************\n"); | ||
| 88 | } | ||
| 89 | |||
| 90 | |||
| 91 | static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
| 92 | { | ||
| 93 | struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); | ||
| 94 | |||
| 95 | DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); | ||
| 96 | |||
| 97 | complete(&mcbsp_tx->tx_irq_completion); | ||
| 98 | return IRQ_HANDLED; | ||
| 99 | } | ||
| 100 | |||
| 101 | static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
| 102 | { | ||
| 103 | struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); | ||
| 104 | |||
| 105 | DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); | ||
| 106 | |||
| 107 | complete(&mcbsp_rx->rx_irq_completion); | ||
| 108 | return IRQ_HANDLED; | ||
| 109 | } | ||
| 110 | |||
| 111 | |||
| 112 | static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) | ||
| 113 | { | ||
| 114 | struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); | ||
| 115 | |||
| 116 | DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); | ||
| 117 | |||
| 118 | /* We can free the channels */ | ||
| 119 | omap_free_dma(mcbsp_dma_tx->dma_tx_lch); | ||
| 120 | mcbsp_dma_tx->dma_tx_lch = -1; | ||
| 121 | |||
| 122 | complete(&mcbsp_dma_tx->tx_dma_completion); | ||
| 123 | } | ||
| 124 | |||
| 125 | static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) | ||
| 126 | { | ||
| 127 | struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); | ||
| 128 | |||
| 129 | DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); | ||
| 130 | |||
| 131 | /* We can free the channels */ | ||
| 132 | omap_free_dma(mcbsp_dma_rx->dma_rx_lch); | ||
| 133 | mcbsp_dma_rx->dma_rx_lch = -1; | ||
| 134 | |||
| 135 | complete(&mcbsp_dma_rx->rx_dma_completion); | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 139 | /* | ||
| 140 | * omap_mcbsp_config simply write a config to the | ||
| 141 | * appropriate McBSP. | ||
| 142 | * You either call this function or set the McBSP registers | ||
| 143 | * by yourself before calling omap_mcbsp_start(). | ||
| 144 | */ | ||
| 145 | |||
| 146 | void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) | ||
| 147 | { | ||
| 148 | u32 io_base = mcbsp[id].io_base; | ||
| 149 | |||
| 150 | DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); | ||
| 151 | |||
| 152 | /* We write the given config */ | ||
| 153 | OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); | ||
| 154 | OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); | ||
| 155 | OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); | ||
| 156 | OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); | ||
| 157 | OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); | ||
| 158 | OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); | ||
| 159 | OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); | ||
| 160 | OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); | ||
| 161 | OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); | ||
| 162 | OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); | ||
| 163 | OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | static int omap_mcbsp_check(unsigned int id) | ||
| 169 | { | ||
| 170 | if (cpu_is_omap730()) { | ||
| 171 | if (id > OMAP_MAX_MCBSP_COUNT - 1) { | ||
| 172 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
| 173 | return -1; | ||
| 174 | } | ||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 179 | if (id > OMAP_MAX_MCBSP_COUNT) { | ||
| 180 | printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); | ||
| 181 | return -1; | ||
| 182 | } | ||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | return -1; | ||
| 187 | } | ||
| 188 | |||
| 189 | #define EN_XORPCK 1 | ||
| 190 | #define DSP_RSTCT2 0xe1008014 | ||
| 191 | |||
| 192 | static void omap_mcbsp_dsp_request(void) | ||
| 193 | { | ||
| 194 | if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 195 | omap_writew((omap_readw(ARM_RSTCT1) | (1 << 1) | (1 << 2)), | ||
| 196 | ARM_RSTCT1); | ||
| 197 | clk_enable(mcbsp_dsp_ck); | ||
| 198 | clk_enable(mcbsp_api_ck); | ||
| 199 | |||
| 200 | /* enable 12MHz clock to mcbsp 1 & 3 */ | ||
| 201 | __raw_writew(__raw_readw(DSP_IDLECT2) | (1 << EN_XORPCK), | ||
| 202 | DSP_IDLECT2); | ||
| 203 | __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, | ||
| 204 | DSP_RSTCT2); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | static void omap_mcbsp_dsp_free(void) | ||
| 209 | { | ||
| 210 | /* Useless for now */ | ||
| 211 | } | ||
| 212 | |||
| 213 | |||
| 214 | int omap_mcbsp_request(unsigned int id) | ||
| 215 | { | ||
| 216 | int err; | ||
| 217 | |||
| 218 | if (omap_mcbsp_check(id) < 0) | ||
| 219 | return -EINVAL; | ||
| 220 | |||
| 221 | /* | ||
| 222 | * On 1510, 1610 and 1710, McBSP1 and McBSP3 | ||
| 223 | * are DSP public peripherals. | ||
| 224 | */ | ||
| 225 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
| 226 | omap_mcbsp_dsp_request(); | ||
| 227 | |||
| 228 | spin_lock(&mcbsp[id].lock); | ||
| 229 | if (!mcbsp[id].free) { | ||
| 230 | printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); | ||
| 231 | spin_unlock(&mcbsp[id].lock); | ||
| 232 | return -1; | ||
| 233 | } | ||
| 234 | |||
| 235 | mcbsp[id].free = 0; | ||
| 236 | spin_unlock(&mcbsp[id].lock); | ||
| 237 | |||
| 238 | /* We need to get IRQs here */ | ||
| 239 | err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, | ||
| 240 | "McBSP", | ||
| 241 | (void *) (&mcbsp[id])); | ||
| 242 | if (err != 0) { | ||
| 243 | printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", | ||
| 244 | mcbsp[id].tx_irq, mcbsp[id].id); | ||
| 245 | return err; | ||
| 246 | } | ||
| 247 | |||
| 248 | init_completion(&(mcbsp[id].tx_irq_completion)); | ||
| 249 | |||
| 250 | |||
| 251 | err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, | ||
| 252 | "McBSP", | ||
| 253 | (void *) (&mcbsp[id])); | ||
| 254 | if (err != 0) { | ||
| 255 | printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", | ||
| 256 | mcbsp[id].rx_irq, mcbsp[id].id); | ||
| 257 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
| 258 | return err; | ||
| 259 | } | ||
| 260 | |||
| 261 | init_completion(&(mcbsp[id].rx_irq_completion)); | ||
| 262 | return 0; | ||
| 263 | |||
| 264 | } | ||
| 265 | |||
| 266 | void omap_mcbsp_free(unsigned int id) | ||
| 267 | { | ||
| 268 | if (omap_mcbsp_check(id) < 0) | ||
| 269 | return; | ||
| 270 | |||
| 271 | if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) | ||
| 272 | omap_mcbsp_dsp_free(); | ||
| 273 | |||
| 274 | spin_lock(&mcbsp[id].lock); | ||
| 275 | if (mcbsp[id].free) { | ||
| 276 | printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); | ||
| 277 | spin_unlock(&mcbsp[id].lock); | ||
| 278 | return; | ||
| 279 | } | ||
| 280 | |||
| 281 | mcbsp[id].free = 1; | ||
| 282 | spin_unlock(&mcbsp[id].lock); | ||
| 283 | |||
| 284 | /* Free IRQs */ | ||
| 285 | free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); | ||
| 286 | free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); | ||
| 287 | } | ||
| 288 | |||
| 289 | /* | ||
| 290 | * Here we start the McBSP, by enabling the sample | ||
| 291 | * generator, both transmitter and receivers, | ||
| 292 | * and the frame sync. | ||
| 293 | */ | ||
| 294 | void omap_mcbsp_start(unsigned int id) | ||
| 295 | { | ||
| 296 | u32 io_base; | ||
| 297 | u16 w; | ||
| 298 | |||
| 299 | if (omap_mcbsp_check(id) < 0) | ||
| 300 | return; | ||
| 301 | |||
| 302 | io_base = mcbsp[id].io_base; | ||
| 303 | |||
| 304 | mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); | ||
| 305 | mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); | ||
| 306 | |||
| 307 | /* Start the sample generator */ | ||
| 308 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
| 309 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); | ||
| 310 | |||
| 311 | /* Enable transmitter and receiver */ | ||
| 312 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
| 313 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); | ||
| 314 | |||
| 315 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
| 316 | OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); | ||
| 317 | |||
| 318 | udelay(100); | ||
| 319 | |||
| 320 | /* Start frame sync */ | ||
| 321 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
| 322 | OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); | ||
| 323 | |||
| 324 | /* Dump McBSP Regs */ | ||
| 325 | omap_mcbsp_dump_reg(id); | ||
| 326 | |||
| 327 | } | ||
| 328 | |||
| 329 | void omap_mcbsp_stop(unsigned int id) | ||
| 330 | { | ||
| 331 | u32 io_base; | ||
| 332 | u16 w; | ||
| 333 | |||
| 334 | if (omap_mcbsp_check(id) < 0) | ||
| 335 | return; | ||
| 336 | |||
| 337 | io_base = mcbsp[id].io_base; | ||
| 338 | |||
| 339 | /* Reset transmitter */ | ||
| 340 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
| 341 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); | ||
| 342 | |||
| 343 | /* Reset receiver */ | ||
| 344 | w = OMAP_MCBSP_READ(io_base, SPCR1); | ||
| 345 | OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); | ||
| 346 | |||
| 347 | /* Reset the sample rate generator */ | ||
| 348 | w = OMAP_MCBSP_READ(io_base, SPCR2); | ||
| 349 | OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); | ||
| 350 | } | ||
| 351 | |||
| 352 | |||
| 353 | /* | ||
| 354 | * IRQ based word transmission. | ||
| 355 | */ | ||
| 356 | void omap_mcbsp_xmit_word(unsigned int id, u32 word) | ||
| 357 | { | ||
| 358 | u32 io_base; | ||
| 359 | omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; | ||
| 360 | |||
| 361 | if (omap_mcbsp_check(id) < 0) | ||
| 362 | return; | ||
| 363 | |||
| 364 | io_base = mcbsp[id].io_base; | ||
| 365 | |||
| 366 | wait_for_completion(&(mcbsp[id].tx_irq_completion)); | ||
| 367 | |||
| 368 | if (word_length > OMAP_MCBSP_WORD_16) | ||
| 369 | OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); | ||
| 370 | OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); | ||
| 371 | } | ||
| 372 | |||
| 373 | u32 omap_mcbsp_recv_word(unsigned int id) | ||
| 374 | { | ||
| 375 | u32 io_base; | ||
| 376 | u16 word_lsb, word_msb = 0; | ||
| 377 | omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; | ||
| 378 | |||
| 379 | if (omap_mcbsp_check(id) < 0) | ||
| 380 | return -EINVAL; | ||
| 381 | |||
| 382 | io_base = mcbsp[id].io_base; | ||
| 383 | |||
| 384 | wait_for_completion(&(mcbsp[id].rx_irq_completion)); | ||
| 385 | |||
| 386 | if (word_length > OMAP_MCBSP_WORD_16) | ||
| 387 | word_msb = OMAP_MCBSP_READ(io_base, DRR2); | ||
| 388 | word_lsb = OMAP_MCBSP_READ(io_base, DRR1); | ||
| 389 | |||
| 390 | return (word_lsb | (word_msb << 16)); | ||
| 391 | } | ||
| 392 | |||
| 393 | |||
| 394 | /* | ||
| 395 | * Simple DMA based buffer rx/tx routines. | ||
| 396 | * Nothing fancy, just a single buffer tx/rx through DMA. | ||
| 397 | * The DMA resources are released once the transfer is done. | ||
| 398 | * For anything fancier, you should use your own customized DMA | ||
| 399 | * routines and callbacks. | ||
| 400 | */ | ||
| 401 | int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
| 402 | { | ||
| 403 | int dma_tx_ch; | ||
| 404 | |||
| 405 | if (omap_mcbsp_check(id) < 0) | ||
| 406 | return -EINVAL; | ||
| 407 | |||
| 408 | if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, | ||
| 409 | &mcbsp[id], | ||
| 410 | &dma_tx_ch)) { | ||
| 411 | printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); | ||
| 412 | return -EAGAIN; | ||
| 413 | } | ||
| 414 | mcbsp[id].dma_tx_lch = dma_tx_ch; | ||
| 415 | |||
| 416 | DBG("TX DMA on channel %d\n", dma_tx_ch); | ||
| 417 | |||
| 418 | init_completion(&(mcbsp[id].tx_dma_completion)); | ||
| 419 | |||
| 420 | omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, | ||
| 421 | OMAP_DMA_DATA_TYPE_S16, | ||
| 422 | length >> 1, 1, | ||
| 423 | OMAP_DMA_SYNC_ELEMENT); | ||
| 424 | |||
| 425 | omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, | ||
| 426 | OMAP_DMA_PORT_TIPB, | ||
| 427 | OMAP_DMA_AMODE_CONSTANT, | ||
| 428 | mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); | ||
| 429 | |||
| 430 | omap_set_dma_src_params(mcbsp[id].dma_tx_lch, | ||
| 431 | OMAP_DMA_PORT_EMIFF, | ||
| 432 | OMAP_DMA_AMODE_POST_INC, | ||
| 433 | buffer); | ||
| 434 | |||
| 435 | omap_start_dma(mcbsp[id].dma_tx_lch); | ||
| 436 | wait_for_completion(&(mcbsp[id].tx_dma_completion)); | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | |||
| 441 | int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) | ||
| 442 | { | ||
| 443 | int dma_rx_ch; | ||
| 444 | |||
| 445 | if (omap_mcbsp_check(id) < 0) | ||
| 446 | return -EINVAL; | ||
| 447 | |||
| 448 | if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, | ||
| 449 | &mcbsp[id], | ||
| 450 | &dma_rx_ch)) { | ||
| 451 | printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); | ||
| 452 | return -EAGAIN; | ||
| 453 | } | ||
| 454 | mcbsp[id].dma_rx_lch = dma_rx_ch; | ||
| 455 | |||
| 456 | DBG("RX DMA on channel %d\n", dma_rx_ch); | ||
| 457 | |||
| 458 | init_completion(&(mcbsp[id].rx_dma_completion)); | ||
| 459 | |||
| 460 | omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, | ||
| 461 | OMAP_DMA_DATA_TYPE_S16, | ||
| 462 | length >> 1, 1, | ||
| 463 | OMAP_DMA_SYNC_ELEMENT); | ||
| 464 | |||
| 465 | omap_set_dma_src_params(mcbsp[id].dma_rx_lch, | ||
| 466 | OMAP_DMA_PORT_TIPB, | ||
| 467 | OMAP_DMA_AMODE_CONSTANT, | ||
| 468 | mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); | ||
| 469 | |||
| 470 | omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, | ||
| 471 | OMAP_DMA_PORT_EMIFF, | ||
| 472 | OMAP_DMA_AMODE_POST_INC, | ||
| 473 | buffer); | ||
| 474 | |||
| 475 | omap_start_dma(mcbsp[id].dma_rx_lch); | ||
| 476 | wait_for_completion(&(mcbsp[id].rx_dma_completion)); | ||
| 477 | return 0; | ||
| 478 | } | ||
| 479 | |||
| 480 | |||
| 481 | /* | ||
| 482 | * SPI wrapper. | ||
| 483 | * Since SPI setup is much simpler than the generic McBSP one, | ||
| 484 | * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. | ||
| 485 | * Once this is done, you can call omap_mcbsp_start(). | ||
| 486 | */ | ||
| 487 | void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) | ||
| 488 | { | ||
| 489 | struct omap_mcbsp_reg_cfg mcbsp_cfg; | ||
| 490 | |||
| 491 | if (omap_mcbsp_check(id) < 0) | ||
| 492 | return; | ||
| 493 | |||
| 494 | memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); | ||
| 495 | |||
| 496 | /* SPI has only one frame */ | ||
| 497 | mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); | ||
| 498 | mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); | ||
| 499 | |||
| 500 | /* Clock stop mode */ | ||
| 501 | if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) | ||
| 502 | mcbsp_cfg.spcr1 |= (1 << 12); | ||
| 503 | else | ||
| 504 | mcbsp_cfg.spcr1 |= (3 << 11); | ||
| 505 | |||
| 506 | /* Set clock parities */ | ||
| 507 | if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
| 508 | mcbsp_cfg.pcr0 |= CLKRP; | ||
| 509 | else | ||
| 510 | mcbsp_cfg.pcr0 &= ~CLKRP; | ||
| 511 | |||
| 512 | if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) | ||
| 513 | mcbsp_cfg.pcr0 &= ~CLKXP; | ||
| 514 | else | ||
| 515 | mcbsp_cfg.pcr0 |= CLKXP; | ||
| 516 | |||
| 517 | /* Set SCLKME to 0 and CLKSM to 1 */ | ||
| 518 | mcbsp_cfg.pcr0 &= ~SCLKME; | ||
| 519 | mcbsp_cfg.srgr2 |= CLKSM; | ||
| 520 | |||
| 521 | /* Set FSXP */ | ||
| 522 | if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) | ||
| 523 | mcbsp_cfg.pcr0 &= ~FSXP; | ||
| 524 | else | ||
| 525 | mcbsp_cfg.pcr0 |= FSXP; | ||
| 526 | |||
| 527 | if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { | ||
| 528 | mcbsp_cfg.pcr0 |= CLKXM; | ||
| 529 | mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); | ||
| 530 | mcbsp_cfg.pcr0 |= FSXM; | ||
| 531 | mcbsp_cfg.srgr2 &= ~FSGM; | ||
| 532 | mcbsp_cfg.xcr2 |= XDATDLY(1); | ||
| 533 | mcbsp_cfg.rcr2 |= RDATDLY(1); | ||
| 534 | } | ||
| 535 | else { | ||
| 536 | mcbsp_cfg.pcr0 &= ~CLKXM; | ||
| 537 | mcbsp_cfg.srgr1 |= CLKGDV(1); | ||
| 538 | mcbsp_cfg.pcr0 &= ~FSXM; | ||
| 539 | mcbsp_cfg.xcr2 &= ~XDATDLY(3); | ||
| 540 | mcbsp_cfg.rcr2 &= ~RDATDLY(3); | ||
| 541 | } | ||
| 542 | |||
| 543 | mcbsp_cfg.xcr2 &= ~XPHASE; | ||
| 544 | mcbsp_cfg.rcr2 &= ~RPHASE; | ||
| 545 | |||
| 546 | omap_mcbsp_config(id, &mcbsp_cfg); | ||
| 547 | } | ||
| 548 | |||
| 549 | |||
| 550 | /* | ||
| 551 | * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. | ||
| 552 | * 730 has only 2 McBSP, and both of them are MPU peripherals. | ||
| 553 | */ | ||
| 554 | struct omap_mcbsp_info { | ||
| 555 | u32 virt_base; | ||
| 556 | u8 dma_rx_sync, dma_tx_sync; | ||
| 557 | u16 rx_irq, tx_irq; | ||
| 558 | }; | ||
| 559 | |||
| 560 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 561 | static const struct omap_mcbsp_info mcbsp_730[] = { | ||
| 562 | [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), | ||
| 563 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
| 564 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
| 565 | .rx_irq = INT_730_McBSP1RX, | ||
| 566 | .tx_irq = INT_730_McBSP1TX }, | ||
| 567 | [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), | ||
| 568 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
| 569 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
| 570 | .rx_irq = INT_730_McBSP2RX, | ||
| 571 | .tx_irq = INT_730_McBSP2TX }, | ||
| 572 | }; | ||
| 573 | #endif | ||
| 574 | |||
| 575 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 576 | static const struct omap_mcbsp_info mcbsp_1510[] = { | ||
| 577 | [0] = { .virt_base = OMAP1510_MCBSP1_BASE, | ||
| 578 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
| 579 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
| 580 | .rx_irq = INT_McBSP1RX, | ||
| 581 | .tx_irq = INT_McBSP1TX }, | ||
| 582 | [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), | ||
| 583 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
| 584 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
| 585 | .rx_irq = INT_1510_SPI_RX, | ||
| 586 | .tx_irq = INT_1510_SPI_TX }, | ||
| 587 | [2] = { .virt_base = OMAP1510_MCBSP3_BASE, | ||
| 588 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
| 589 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
| 590 | .rx_irq = INT_McBSP3RX, | ||
| 591 | .tx_irq = INT_McBSP3TX }, | ||
| 592 | }; | ||
| 593 | #endif | ||
| 594 | |||
| 595 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 596 | static const struct omap_mcbsp_info mcbsp_1610[] = { | ||
| 597 | [0] = { .virt_base = OMAP1610_MCBSP1_BASE, | ||
| 598 | .dma_rx_sync = OMAP_DMA_MCBSP1_RX, | ||
| 599 | .dma_tx_sync = OMAP_DMA_MCBSP1_TX, | ||
| 600 | .rx_irq = INT_McBSP1RX, | ||
| 601 | .tx_irq = INT_McBSP1TX }, | ||
| 602 | [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), | ||
| 603 | .dma_rx_sync = OMAP_DMA_MCBSP2_RX, | ||
| 604 | .dma_tx_sync = OMAP_DMA_MCBSP2_TX, | ||
| 605 | .rx_irq = INT_1610_McBSP2_RX, | ||
| 606 | .tx_irq = INT_1610_McBSP2_TX }, | ||
| 607 | [2] = { .virt_base = OMAP1610_MCBSP3_BASE, | ||
| 608 | .dma_rx_sync = OMAP_DMA_MCBSP3_RX, | ||
| 609 | .dma_tx_sync = OMAP_DMA_MCBSP3_TX, | ||
| 610 | .rx_irq = INT_McBSP3RX, | ||
| 611 | .tx_irq = INT_McBSP3TX }, | ||
| 612 | }; | ||
| 613 | #endif | ||
| 614 | |||
| 615 | static int __init omap_mcbsp_init(void) | ||
| 616 | { | ||
| 617 | int mcbsp_count = 0, i; | ||
| 618 | static const struct omap_mcbsp_info *mcbsp_info; | ||
| 619 | |||
| 620 | printk("Initializing OMAP McBSP system\n"); | ||
| 621 | |||
| 622 | mcbsp_dsp_ck = clk_get(0, "dsp_ck"); | ||
| 623 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
| 624 | printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); | ||
| 625 | return PTR_ERR(mcbsp_dsp_ck); | ||
| 626 | } | ||
| 627 | mcbsp_api_ck = clk_get(0, "api_ck"); | ||
| 628 | if (IS_ERR(mcbsp_dsp_ck)) { | ||
| 629 | printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); | ||
| 630 | return PTR_ERR(mcbsp_api_ck); | ||
| 631 | } | ||
| 632 | |||
| 633 | #ifdef CONFIG_ARCH_OMAP730 | ||
| 634 | if (cpu_is_omap730()) { | ||
| 635 | mcbsp_info = mcbsp_730; | ||
| 636 | mcbsp_count = ARRAY_SIZE(mcbsp_730); | ||
| 637 | } | ||
| 638 | #endif | ||
| 639 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 640 | if (cpu_is_omap1510()) { | ||
| 641 | mcbsp_info = mcbsp_1510; | ||
| 642 | mcbsp_count = ARRAY_SIZE(mcbsp_1510); | ||
| 643 | } | ||
| 644 | #endif | ||
| 645 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 646 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 647 | mcbsp_info = mcbsp_1610; | ||
| 648 | mcbsp_count = ARRAY_SIZE(mcbsp_1610); | ||
| 649 | } | ||
| 650 | #endif | ||
| 651 | for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { | ||
| 652 | if (i >= mcbsp_count) { | ||
| 653 | mcbsp[i].io_base = 0; | ||
| 654 | mcbsp[i].free = 0; | ||
| 655 | continue; | ||
| 656 | } | ||
| 657 | mcbsp[i].id = i + 1; | ||
| 658 | mcbsp[i].free = 1; | ||
| 659 | mcbsp[i].dma_tx_lch = -1; | ||
| 660 | mcbsp[i].dma_rx_lch = -1; | ||
| 661 | |||
| 662 | mcbsp[i].io_base = mcbsp_info[i].virt_base; | ||
| 663 | mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; | ||
| 664 | mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; | ||
| 665 | mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; | ||
| 666 | mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; | ||
| 667 | spin_lock_init(&mcbsp[i].lock); | ||
| 668 | } | ||
| 669 | |||
| 670 | return 0; | ||
| 671 | } | ||
| 672 | |||
| 673 | |||
| 674 | arch_initcall(omap_mcbsp_init); | ||
| 675 | |||
| 676 | EXPORT_SYMBOL(omap_mcbsp_config); | ||
| 677 | EXPORT_SYMBOL(omap_mcbsp_request); | ||
| 678 | EXPORT_SYMBOL(omap_mcbsp_free); | ||
| 679 | EXPORT_SYMBOL(omap_mcbsp_start); | ||
| 680 | EXPORT_SYMBOL(omap_mcbsp_stop); | ||
| 681 | EXPORT_SYMBOL(omap_mcbsp_xmit_word); | ||
| 682 | EXPORT_SYMBOL(omap_mcbsp_recv_word); | ||
| 683 | EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); | ||
| 684 | EXPORT_SYMBOL(omap_mcbsp_recv_buffer); | ||
| 685 | 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 index 000000000000..cbecd10d0b6c --- /dev/null +++ b/arch/arm/plat-omap/mux.c | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/mux.c | ||
| 3 | * | ||
| 4 | * Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h | ||
| 5 | * | ||
| 6 | * Copyright (C) 2003 Nokia Corporation | ||
| 7 | * | ||
| 8 | * Written by Tony Lindgren <tony.lindgren@nokia.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to the Free Software | ||
| 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | #include <linux/config.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <asm/system.h> | ||
| 29 | #include <asm/io.h> | ||
| 30 | #include <linux/spinlock.h> | ||
| 31 | |||
| 32 | #define __MUX_C__ | ||
| 33 | #include <asm/arch/mux.h> | ||
| 34 | |||
| 35 | #ifdef CONFIG_OMAP_MUX | ||
| 36 | |||
| 37 | /* | ||
| 38 | * Sets the Omap MUX and PULL_DWN registers based on the table | ||
| 39 | */ | ||
| 40 | int __init_or_module | ||
| 41 | omap_cfg_reg(const reg_cfg_t reg_cfg) | ||
| 42 | { | ||
| 43 | static DEFINE_SPINLOCK(mux_spin_lock); | ||
| 44 | |||
| 45 | unsigned long flags; | ||
| 46 | reg_cfg_set *cfg; | ||
| 47 | unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, | ||
| 48 | pull_orig = 0, pull = 0; | ||
| 49 | unsigned int mask, warn = 0; | ||
| 50 | |||
| 51 | if (reg_cfg > ARRAY_SIZE(reg_cfg_table)) { | ||
| 52 | printk(KERN_ERR "MUX: reg_cfg %d\n", reg_cfg); | ||
| 53 | return -EINVAL; | ||
| 54 | } | ||
| 55 | |||
| 56 | cfg = ®_cfg_table[reg_cfg]; | ||
| 57 | |||
| 58 | /* | ||
| 59 | * We do a pretty long section here with lock on, but pin muxing | ||
| 60 | * should only happen on driver init for each driver, so it's not time | ||
| 61 | * critical. | ||
| 62 | */ | ||
| 63 | spin_lock_irqsave(&mux_spin_lock, flags); | ||
| 64 | |||
| 65 | /* Check the mux register in question */ | ||
| 66 | if (cfg->mux_reg) { | ||
| 67 | unsigned tmp1, tmp2; | ||
| 68 | |||
| 69 | reg_orig = omap_readl(cfg->mux_reg); | ||
| 70 | |||
| 71 | /* The mux registers always seem to be 3 bits long */ | ||
| 72 | mask = (0x7 << cfg->mask_offset); | ||
| 73 | tmp1 = reg_orig & mask; | ||
| 74 | reg = reg_orig & ~mask; | ||
| 75 | |||
| 76 | tmp2 = (cfg->mask << cfg->mask_offset); | ||
| 77 | reg |= tmp2; | ||
| 78 | |||
| 79 | if (tmp1 != tmp2) | ||
| 80 | warn = 1; | ||
| 81 | |||
| 82 | omap_writel(reg, cfg->mux_reg); | ||
| 83 | } | ||
| 84 | |||
| 85 | /* Check for pull up or pull down selection on 1610 */ | ||
| 86 | if (!cpu_is_omap1510()) { | ||
| 87 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
| 88 | pu_pd_orig = omap_readl(cfg->pu_pd_reg); | ||
| 89 | mask = 1 << cfg->pull_bit; | ||
| 90 | |||
| 91 | if (cfg->pu_pd_val) { | ||
| 92 | if (!(pu_pd_orig & mask)) | ||
| 93 | warn = 1; | ||
| 94 | /* Use pull up */ | ||
| 95 | pu_pd = pu_pd_orig | mask; | ||
| 96 | } else { | ||
| 97 | if (pu_pd_orig & mask) | ||
| 98 | warn = 1; | ||
| 99 | /* Use pull down */ | ||
| 100 | pu_pd = pu_pd_orig & ~mask; | ||
| 101 | } | ||
| 102 | omap_writel(pu_pd, cfg->pu_pd_reg); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Check for an associated pull down register */ | ||
| 107 | if (cfg->pull_reg) { | ||
| 108 | pull_orig = omap_readl(cfg->pull_reg); | ||
| 109 | mask = 1 << cfg->pull_bit; | ||
| 110 | |||
| 111 | if (cfg->pull_val) { | ||
| 112 | if (pull_orig & mask) | ||
| 113 | warn = 1; | ||
| 114 | /* Low bit = pull enabled */ | ||
| 115 | pull = pull_orig & ~mask; | ||
| 116 | } else { | ||
| 117 | if (!(pull_orig & mask)) | ||
| 118 | warn = 1; | ||
| 119 | /* High bit = pull disabled */ | ||
| 120 | pull = pull_orig | mask; | ||
| 121 | } | ||
| 122 | |||
| 123 | omap_writel(pull, cfg->pull_reg); | ||
| 124 | } | ||
| 125 | |||
| 126 | if (warn) { | ||
| 127 | #ifdef CONFIG_OMAP_MUX_WARNINGS | ||
| 128 | printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); | ||
| 129 | #endif | ||
| 130 | } | ||
| 131 | |||
| 132 | #ifdef CONFIG_OMAP_MUX_DEBUG | ||
| 133 | if (cfg->debug || warn) { | ||
| 134 | printk("MUX: Setting register %s\n", cfg->name); | ||
| 135 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
| 136 | cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); | ||
| 137 | |||
| 138 | if (!cpu_is_omap1510()) { | ||
| 139 | if (cfg->pu_pd_reg && cfg->pull_val) { | ||
| 140 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
| 141 | cfg->pu_pd_name, cfg->pu_pd_reg, | ||
| 142 | pu_pd_orig, pu_pd); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | if (cfg->pull_reg) | ||
| 147 | printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", | ||
| 148 | cfg->pull_name, cfg->pull_reg, pull_orig, pull); | ||
| 149 | } | ||
| 150 | #endif | ||
| 151 | |||
| 152 | spin_unlock_irqrestore(&mux_spin_lock, flags); | ||
| 153 | |||
| 154 | #ifdef CONFIG_OMAP_MUX_ERRORS | ||
| 155 | return warn ? -ETXTBSY : 0; | ||
| 156 | #else | ||
| 157 | return 0; | ||
| 158 | #endif | ||
| 159 | } | ||
| 160 | |||
| 161 | EXPORT_SYMBOL(omap_cfg_reg); | ||
| 162 | |||
| 163 | #endif /* CONFIG_OMAP_MUX */ | ||
diff --git a/arch/arm/plat-omap/ocpi.c b/arch/arm/plat-omap/ocpi.c new file mode 100644 index 000000000000..1fb16f9edfd5 --- /dev/null +++ b/arch/arm/plat-omap/ocpi.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/ocpi.c | ||
| 3 | * | ||
| 4 | * Minimal OCP bus support for omap16xx | ||
| 5 | * | ||
| 6 | * Copyright (C) 2003 - 2005 Nokia Corporation | ||
| 7 | * Written by Tony Lindgren <tony@atomide.com> | ||
| 8 | * | ||
| 9 | * Modified for clock framework by Paul Mundt <paul.mundt@nokia.com>. | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; either version 2 of the License, or | ||
| 14 | * (at your option) any later version. | ||
| 15 | * | ||
| 16 | * This program is distributed in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | * GNU General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU General Public License | ||
| 22 | * along with this program; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/config.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/version.h> | ||
| 29 | #include <linux/types.h> | ||
| 30 | #include <linux/errno.h> | ||
| 31 | #include <linux/kernel.h> | ||
| 32 | #include <linux/init.h> | ||
| 33 | #include <linux/spinlock.h> | ||
| 34 | #include <linux/err.h> | ||
| 35 | |||
| 36 | #include <asm/io.h> | ||
| 37 | #include <asm/hardware/clock.h> | ||
| 38 | #include <asm/arch/hardware.h> | ||
| 39 | |||
| 40 | #define OCPI_BASE 0xfffec320 | ||
| 41 | #define OCPI_FAULT (OCPI_BASE + 0x00) | ||
| 42 | #define OCPI_CMD_FAULT (OCPI_BASE + 0x04) | ||
| 43 | #define OCPI_SINT0 (OCPI_BASE + 0x08) | ||
| 44 | #define OCPI_TABORT (OCPI_BASE + 0x0c) | ||
| 45 | #define OCPI_SINT1 (OCPI_BASE + 0x10) | ||
| 46 | #define OCPI_PROT (OCPI_BASE + 0x14) | ||
| 47 | #define OCPI_SEC (OCPI_BASE + 0x18) | ||
| 48 | |||
| 49 | /* USB OHCI OCPI access error registers */ | ||
| 50 | #define HOSTUEADDR 0xfffba0e0 | ||
| 51 | #define HOSTUESTATUS 0xfffba0e4 | ||
| 52 | |||
| 53 | static struct clk *ocpi_ck; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * Enables device access to OMAP buses via the OCPI bridge | ||
| 57 | * FIXME: Add locking | ||
| 58 | */ | ||
| 59 | int ocpi_enable(void) | ||
| 60 | { | ||
| 61 | unsigned int val; | ||
| 62 | |||
| 63 | if (!cpu_is_omap16xx()) | ||
| 64 | return -ENODEV; | ||
| 65 | |||
| 66 | /* Make sure there's clock for OCPI */ | ||
| 67 | clk_enable(ocpi_ck); | ||
| 68 | |||
| 69 | /* Enable access for OHCI in OCPI */ | ||
| 70 | val = omap_readl(OCPI_PROT); | ||
| 71 | val &= ~0xff; | ||
| 72 | //val &= (1 << 0); /* Allow access only to EMIFS */ | ||
| 73 | omap_writel(val, OCPI_PROT); | ||
| 74 | |||
| 75 | val = omap_readl(OCPI_SEC); | ||
| 76 | val &= ~0xff; | ||
| 77 | omap_writel(val, OCPI_SEC); | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | EXPORT_SYMBOL(ocpi_enable); | ||
| 82 | |||
| 83 | static int __init omap_ocpi_init(void) | ||
| 84 | { | ||
| 85 | if (!cpu_is_omap16xx()) | ||
| 86 | return -ENODEV; | ||
| 87 | |||
| 88 | ocpi_ck = clk_get(NULL, "l3_ocpi_ck"); | ||
| 89 | if (IS_ERR(ocpi_ck)) | ||
| 90 | return PTR_ERR(ocpi_ck); | ||
| 91 | |||
| 92 | clk_use(ocpi_ck); | ||
| 93 | ocpi_enable(); | ||
| 94 | printk("OMAP OCPI interconnect driver loaded\n"); | ||
| 95 | |||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | static void __exit omap_ocpi_exit(void) | ||
| 100 | { | ||
| 101 | /* REVISIT: Disable OCPI */ | ||
| 102 | |||
| 103 | if (!cpu_is_omap16xx()) | ||
| 104 | return; | ||
| 105 | |||
| 106 | clk_unuse(ocpi_ck); | ||
| 107 | clk_put(ocpi_ck); | ||
| 108 | } | ||
| 109 | |||
| 110 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); | ||
| 111 | MODULE_DESCRIPTION("OMAP OCPI bus controller module"); | ||
| 112 | MODULE_LICENSE("GPL"); | ||
| 113 | module_init(omap_ocpi_init); | ||
| 114 | 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 index 000000000000..e6536b16c385 --- /dev/null +++ b/arch/arm/plat-omap/pm.c | |||
| @@ -0,0 +1,632 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/pm.c | ||
| 3 | * | ||
| 4 | * OMAP Power Management Routines | ||
| 5 | * | ||
| 6 | * Original code for the SA11x0: | ||
| 7 | * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> | ||
| 8 | * | ||
| 9 | * Modified for the PXA250 by Nicolas Pitre: | ||
| 10 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
| 11 | * | ||
| 12 | * Modified for the OMAP1510 by David Singleton: | ||
| 13 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
| 14 | * | ||
| 15 | * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> | ||
| 16 | * | ||
| 17 | * This program is free software; you can redistribute it and/or modify it | ||
| 18 | * under the terms of the GNU General Public License as published by the | ||
| 19 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 20 | * option) any later version. | ||
| 21 | * | ||
| 22 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 25 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 28 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 29 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 32 | * | ||
| 33 | * You should have received a copy of the GNU General Public License along | ||
| 34 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 35 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 36 | */ | ||
| 37 | |||
| 38 | #include <linux/pm.h> | ||
| 39 | #include <linux/sched.h> | ||
| 40 | #include <linux/proc_fs.h> | ||
| 41 | #include <linux/pm.h> | ||
| 42 | |||
| 43 | #include <asm/io.h> | ||
| 44 | #include <asm/mach/time.h> | ||
| 45 | #include <asm/mach-types.h> | ||
| 46 | |||
| 47 | #include <asm/arch/omap16xx.h> | ||
| 48 | #include <asm/arch/pm.h> | ||
| 49 | #include <asm/arch/mux.h> | ||
| 50 | #include <asm/arch/tc.h> | ||
| 51 | #include <asm/arch/tps65010.h> | ||
| 52 | |||
| 53 | #include "clock.h" | ||
| 54 | |||
| 55 | static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; | ||
| 56 | static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; | ||
| 57 | static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; | ||
| 58 | static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; | ||
| 59 | |||
| 60 | /* | ||
| 61 | * Let's power down on idle, but only if we are really | ||
| 62 | * idle, because once we start down the path of | ||
| 63 | * going idle we continue to do idle even if we get | ||
| 64 | * a clock tick interrupt . . | ||
| 65 | */ | ||
| 66 | void omap_pm_idle(void) | ||
| 67 | { | ||
| 68 | int (*func_ptr)(void) = 0; | ||
| 69 | unsigned int mask32 = 0; | ||
| 70 | |||
| 71 | /* | ||
| 72 | * If the DSP is being used let's just idle the CPU, the overhead | ||
| 73 | * to wake up from Big Sleep is big, milliseconds versus micro | ||
| 74 | * seconds for wait for interrupt. | ||
| 75 | */ | ||
| 76 | |||
| 77 | local_irq_disable(); | ||
| 78 | local_fiq_disable(); | ||
| 79 | if (need_resched()) { | ||
| 80 | local_fiq_enable(); | ||
| 81 | local_irq_enable(); | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | mask32 = omap_readl(ARM_SYSST); | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Since an interrupt may set up a timer, we don't want to | ||
| 88 | * reprogram the hardware timer with interrupts enabled. | ||
| 89 | * Re-enable interrupts only after returning from idle. | ||
| 90 | */ | ||
| 91 | timer_dyn_reprogram(); | ||
| 92 | |||
| 93 | if ((mask32 & DSP_IDLE) == 0) { | ||
| 94 | __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); | ||
| 95 | } else { | ||
| 96 | |||
| 97 | if (cpu_is_omap1510()) { | ||
| 98 | func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); | ||
| 99 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 100 | func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); | ||
| 101 | } else if (cpu_is_omap5912()) { | ||
| 102 | func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); | ||
| 103 | } | ||
| 104 | |||
| 105 | func_ptr(); | ||
| 106 | } | ||
| 107 | local_fiq_enable(); | ||
| 108 | local_irq_enable(); | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Configuration of the wakeup event is board specific. For the | ||
| 113 | * moment we put it into this helper function. Later it may move | ||
| 114 | * to board specific files. | ||
| 115 | */ | ||
| 116 | static void omap_pm_wakeup_setup(void) | ||
| 117 | { | ||
| 118 | /* | ||
| 119 | * Enable ARM XOR clock and release peripheral from reset by | ||
| 120 | * writing 1 to PER_EN bit in ARM_RSTCT2, this is required | ||
| 121 | * for UART configuration to use UART2 to wake up. | ||
| 122 | */ | ||
| 123 | |||
| 124 | omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); | ||
| 125 | omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); | ||
| 126 | omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Turn off all interrupts except L1-2nd level cascade, | ||
| 130 | * and the L2 wakeup interrupts: keypad and UART2. | ||
| 131 | */ | ||
| 132 | |||
| 133 | omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); | ||
| 134 | |||
| 135 | if (cpu_is_omap1510()) { | ||
| 136 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); | ||
| 137 | } | ||
| 138 | |||
| 139 | if (cpu_is_omap16xx()) { | ||
| 140 | omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); | ||
| 141 | |||
| 142 | omap_writel(~0x0, OMAP_IH2_1_MIR); | ||
| 143 | omap_writel(~0x0, OMAP_IH2_2_MIR); | ||
| 144 | omap_writel(~0x0, OMAP_IH2_3_MIR); | ||
| 145 | } | ||
| 146 | |||
| 147 | /* New IRQ agreement */ | ||
| 148 | omap_writel(1, OMAP_IH1_CONTROL); | ||
| 149 | |||
| 150 | /* external PULL to down, bit 22 = 0 */ | ||
| 151 | omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2); | ||
| 152 | } | ||
| 153 | |||
| 154 | void omap_pm_suspend(void) | ||
| 155 | { | ||
| 156 | unsigned int mask32 = 0; | ||
| 157 | unsigned long arg0 = 0, arg1 = 0; | ||
| 158 | int (*func_ptr)(unsigned short, unsigned short) = 0; | ||
| 159 | unsigned short save_dsp_idlect2; | ||
| 160 | |||
| 161 | printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); | ||
| 162 | |||
| 163 | if (machine_is_omap_osk()) { | ||
| 164 | /* Stop LED1 (D9) blink */ | ||
| 165 | tps65010_set_led(LED1, OFF); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* | ||
| 169 | * Step 1: turn off interrupts | ||
| 170 | */ | ||
| 171 | |||
| 172 | local_irq_disable(); | ||
| 173 | local_fiq_disable(); | ||
| 174 | |||
| 175 | /* | ||
| 176 | * Step 2: save registers | ||
| 177 | * | ||
| 178 | * The omap is a strange/beautiful device. The caches, memory | ||
| 179 | * and register state are preserved across power saves. | ||
| 180 | * We have to save and restore very little register state to | ||
| 181 | * idle the omap. | ||
| 182 | * | ||
| 183 | * Save interrupt, MPUI, ARM and UPLD control registers. | ||
| 184 | */ | ||
| 185 | |||
| 186 | if (cpu_is_omap1510()) { | ||
| 187 | MPUI1510_SAVE(OMAP_IH1_MIR); | ||
| 188 | MPUI1510_SAVE(OMAP_IH2_MIR); | ||
| 189 | MPUI1510_SAVE(MPUI_CTRL); | ||
| 190 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
| 191 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
| 192 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
| 193 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
| 194 | } else if (cpu_is_omap16xx()) { | ||
| 195 | MPUI1610_SAVE(OMAP_IH1_MIR); | ||
| 196 | MPUI1610_SAVE(OMAP_IH2_0_MIR); | ||
| 197 | MPUI1610_SAVE(OMAP_IH2_1_MIR); | ||
| 198 | MPUI1610_SAVE(OMAP_IH2_2_MIR); | ||
| 199 | MPUI1610_SAVE(OMAP_IH2_3_MIR); | ||
| 200 | MPUI1610_SAVE(MPUI_CTRL); | ||
| 201 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
| 202 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
| 203 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
| 204 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
| 205 | } | ||
| 206 | |||
| 207 | ARM_SAVE(ARM_CKCTL); | ||
| 208 | ARM_SAVE(ARM_IDLECT1); | ||
| 209 | ARM_SAVE(ARM_IDLECT2); | ||
| 210 | ARM_SAVE(ARM_EWUPCT); | ||
| 211 | ARM_SAVE(ARM_RSTCT1); | ||
| 212 | ARM_SAVE(ARM_RSTCT2); | ||
| 213 | ARM_SAVE(ARM_SYSST); | ||
| 214 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
| 215 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
| 216 | |||
| 217 | /* | ||
| 218 | * Step 3: LOW_PWR signal enabling | ||
| 219 | * | ||
| 220 | * Allow the LOW_PWR signal to be visible on MPUIO5 ball. | ||
| 221 | */ | ||
| 222 | if (cpu_is_omap1510()) { | ||
| 223 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
| 224 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
| 225 | OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
| 226 | } else if (cpu_is_omap16xx()) { | ||
| 227 | /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ | ||
| 228 | omap_writew(omap_readw(ULPD_POWER_CTRL) | | ||
| 229 | OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
| 230 | } | ||
| 231 | |||
| 232 | /* configure LOW_PWR pin */ | ||
| 233 | omap_cfg_reg(T20_1610_LOW_PWR); | ||
| 234 | |||
| 235 | /* | ||
| 236 | * Step 4: OMAP DSP Shutdown | ||
| 237 | */ | ||
| 238 | |||
| 239 | /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ | ||
| 240 | omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, | ||
| 241 | ARM_RSTCT1); | ||
| 242 | |||
| 243 | /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ | ||
| 244 | omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); | ||
| 245 | |||
| 246 | /* Set EN_DSPCK = 0, stop DSP block clock */ | ||
| 247 | omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); | ||
| 248 | |||
| 249 | /* Stop any DSP domain clocks */ | ||
| 250 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
| 251 | save_dsp_idlect2 = __raw_readw(DSP_IDLECT2); | ||
| 252 | __raw_writew(0, DSP_IDLECT2); | ||
| 253 | |||
| 254 | /* | ||
| 255 | * Step 5: Wakeup Event Setup | ||
| 256 | */ | ||
| 257 | |||
| 258 | omap_pm_wakeup_setup(); | ||
| 259 | |||
| 260 | /* | ||
| 261 | * Step 6a: ARM and Traffic controller shutdown | ||
| 262 | * | ||
| 263 | * Step 6 starts here with clock and watchdog disable | ||
| 264 | */ | ||
| 265 | |||
| 266 | /* stop clocks */ | ||
| 267 | mask32 = omap_readl(ARM_IDLECT2); | ||
| 268 | mask32 &= ~(1<<EN_WDTCK); /* bit 0 -> 0 (WDT clock) */ | ||
| 269 | mask32 |= (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */ | ||
| 270 | mask32 &= ~(1<<EN_PERCK); /* bit 2 -> 0 (MPUPER_CK clock) */ | ||
| 271 | mask32 &= ~(1<<EN_LCDCK); /* bit 3 -> 0 (LCDC clock) */ | ||
| 272 | mask32 &= ~(1<<EN_LBCK); /* bit 4 -> 0 (local bus clock) */ | ||
| 273 | mask32 |= (1<<EN_APICK); /* bit 6 -> 1 (MPUI clock) */ | ||
| 274 | mask32 &= ~(1<<EN_TIMCK); /* bit 7 -> 0 (MPU timer clock) */ | ||
| 275 | mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */ | ||
| 276 | mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */ | ||
| 277 | omap_writel(mask32, ARM_IDLECT2); | ||
| 278 | |||
| 279 | /* disable ARM watchdog */ | ||
| 280 | omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); | ||
| 281 | omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); | ||
| 282 | |||
| 283 | /* | ||
| 284 | * Step 6b: ARM and Traffic controller shutdown | ||
| 285 | * | ||
| 286 | * Step 6 continues here. Prepare jump to power management | ||
| 287 | * assembly code in internal SRAM. | ||
| 288 | * | ||
| 289 | * Since the omap_cpu_suspend routine has been copied to | ||
| 290 | * SRAM, we'll do an indirect procedure call to it and pass the | ||
| 291 | * contents of arm_idlect1 and arm_idlect2 so it can restore | ||
| 292 | * them when it wakes up and it will return. | ||
| 293 | */ | ||
| 294 | |||
| 295 | arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; | ||
| 296 | arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; | ||
| 297 | |||
| 298 | if (cpu_is_omap1510()) { | ||
| 299 | func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); | ||
| 300 | } else if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 301 | func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); | ||
| 302 | } else if (cpu_is_omap5912()) { | ||
| 303 | func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); | ||
| 304 | } | ||
| 305 | |||
| 306 | /* | ||
| 307 | * Step 6c: ARM and Traffic controller shutdown | ||
| 308 | * | ||
| 309 | * Jump to assembly code. The processor will stay there | ||
| 310 | * until wake up. | ||
| 311 | */ | ||
| 312 | |||
| 313 | func_ptr(arg0, arg1); | ||
| 314 | |||
| 315 | /* | ||
| 316 | * If we are here, processor is woken up! | ||
| 317 | */ | ||
| 318 | |||
| 319 | if (cpu_is_omap1510()) { | ||
| 320 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
| 321 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
| 322 | ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
| 323 | } else if (cpu_is_omap16xx()) { | ||
| 324 | /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ | ||
| 325 | omap_writew(omap_readw(ULPD_POWER_CTRL) & | ||
| 326 | ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); | ||
| 327 | } | ||
| 328 | |||
| 329 | |||
| 330 | /* Restore DSP clocks */ | ||
| 331 | omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); | ||
| 332 | __raw_writew(save_dsp_idlect2, DSP_IDLECT2); | ||
| 333 | ARM_RESTORE(ARM_IDLECT2); | ||
| 334 | |||
| 335 | /* | ||
| 336 | * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did | ||
| 337 | */ | ||
| 338 | |||
| 339 | ARM_RESTORE(ARM_CKCTL); | ||
| 340 | ARM_RESTORE(ARM_EWUPCT); | ||
| 341 | ARM_RESTORE(ARM_RSTCT1); | ||
| 342 | ARM_RESTORE(ARM_RSTCT2); | ||
| 343 | ARM_RESTORE(ARM_SYSST); | ||
| 344 | ULPD_RESTORE(ULPD_CLOCK_CTRL); | ||
| 345 | ULPD_RESTORE(ULPD_STATUS_REQ); | ||
| 346 | |||
| 347 | if (cpu_is_omap1510()) { | ||
| 348 | MPUI1510_RESTORE(MPUI_CTRL); | ||
| 349 | MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
| 350 | MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); | ||
| 351 | MPUI1510_RESTORE(EMIFS_CONFIG); | ||
| 352 | MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); | ||
| 353 | MPUI1510_RESTORE(OMAP_IH1_MIR); | ||
| 354 | MPUI1510_RESTORE(OMAP_IH2_MIR); | ||
| 355 | } else if (cpu_is_omap16xx()) { | ||
| 356 | MPUI1610_RESTORE(MPUI_CTRL); | ||
| 357 | MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); | ||
| 358 | MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); | ||
| 359 | MPUI1610_RESTORE(EMIFS_CONFIG); | ||
| 360 | MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); | ||
| 361 | |||
| 362 | MPUI1610_RESTORE(OMAP_IH1_MIR); | ||
| 363 | MPUI1610_RESTORE(OMAP_IH2_0_MIR); | ||
| 364 | MPUI1610_RESTORE(OMAP_IH2_1_MIR); | ||
| 365 | MPUI1610_RESTORE(OMAP_IH2_2_MIR); | ||
| 366 | MPUI1610_RESTORE(OMAP_IH2_3_MIR); | ||
| 367 | } | ||
| 368 | |||
| 369 | /* | ||
| 370 | * Reenable interrupts | ||
| 371 | */ | ||
| 372 | |||
| 373 | local_irq_enable(); | ||
| 374 | local_fiq_enable(); | ||
| 375 | |||
| 376 | printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev); | ||
| 377 | |||
| 378 | if (machine_is_omap_osk()) { | ||
| 379 | /* Let LED1 (D9) blink again */ | ||
| 380 | tps65010_set_led(LED1, BLINK); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
| 385 | static int g_read_completed; | ||
| 386 | |||
| 387 | /* | ||
| 388 | * Read system PM registers for debugging | ||
| 389 | */ | ||
| 390 | static int omap_pm_read_proc( | ||
| 391 | char *page_buffer, | ||
| 392 | char **my_first_byte, | ||
| 393 | off_t virtual_start, | ||
| 394 | int length, | ||
| 395 | int *eof, | ||
| 396 | void *data) | ||
| 397 | { | ||
| 398 | int my_buffer_offset = 0; | ||
| 399 | char * const my_base = page_buffer; | ||
| 400 | |||
| 401 | ARM_SAVE(ARM_CKCTL); | ||
| 402 | ARM_SAVE(ARM_IDLECT1); | ||
| 403 | ARM_SAVE(ARM_IDLECT2); | ||
| 404 | ARM_SAVE(ARM_EWUPCT); | ||
| 405 | ARM_SAVE(ARM_RSTCT1); | ||
| 406 | ARM_SAVE(ARM_RSTCT2); | ||
| 407 | ARM_SAVE(ARM_SYSST); | ||
| 408 | |||
| 409 | ULPD_SAVE(ULPD_IT_STATUS); | ||
| 410 | ULPD_SAVE(ULPD_CLOCK_CTRL); | ||
| 411 | ULPD_SAVE(ULPD_SOFT_REQ); | ||
| 412 | ULPD_SAVE(ULPD_STATUS_REQ); | ||
| 413 | ULPD_SAVE(ULPD_DPLL_CTRL); | ||
| 414 | ULPD_SAVE(ULPD_POWER_CTRL); | ||
| 415 | |||
| 416 | if (cpu_is_omap1510()) { | ||
| 417 | MPUI1510_SAVE(MPUI_CTRL); | ||
| 418 | MPUI1510_SAVE(MPUI_DSP_STATUS); | ||
| 419 | MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
| 420 | MPUI1510_SAVE(MPUI_DSP_API_CONFIG); | ||
| 421 | MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); | ||
| 422 | MPUI1510_SAVE(EMIFS_CONFIG); | ||
| 423 | } else if (cpu_is_omap16xx()) { | ||
| 424 | MPUI1610_SAVE(MPUI_CTRL); | ||
| 425 | MPUI1610_SAVE(MPUI_DSP_STATUS); | ||
| 426 | MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); | ||
| 427 | MPUI1610_SAVE(MPUI_DSP_API_CONFIG); | ||
| 428 | MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); | ||
| 429 | MPUI1610_SAVE(EMIFS_CONFIG); | ||
| 430 | } | ||
| 431 | |||
| 432 | if (virtual_start == 0) { | ||
| 433 | g_read_completed = 0; | ||
| 434 | |||
| 435 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
| 436 | "ARM_CKCTL_REG: 0x%-8x \n" | ||
| 437 | "ARM_IDLECT1_REG: 0x%-8x \n" | ||
| 438 | "ARM_IDLECT2_REG: 0x%-8x \n" | ||
| 439 | "ARM_EWUPCT_REG: 0x%-8x \n" | ||
| 440 | "ARM_RSTCT1_REG: 0x%-8x \n" | ||
| 441 | "ARM_RSTCT2_REG: 0x%-8x \n" | ||
| 442 | "ARM_SYSST_REG: 0x%-8x \n" | ||
| 443 | "ULPD_IT_STATUS_REG: 0x%-4x \n" | ||
| 444 | "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" | ||
| 445 | "ULPD_SOFT_REQ_REG: 0x%-4x \n" | ||
| 446 | "ULPD_DPLL_CTRL_REG: 0x%-4x \n" | ||
| 447 | "ULPD_STATUS_REQ_REG: 0x%-4x \n" | ||
| 448 | "ULPD_POWER_CTRL_REG: 0x%-4x \n", | ||
| 449 | ARM_SHOW(ARM_CKCTL), | ||
| 450 | ARM_SHOW(ARM_IDLECT1), | ||
| 451 | ARM_SHOW(ARM_IDLECT2), | ||
| 452 | ARM_SHOW(ARM_EWUPCT), | ||
| 453 | ARM_SHOW(ARM_RSTCT1), | ||
| 454 | ARM_SHOW(ARM_RSTCT2), | ||
| 455 | ARM_SHOW(ARM_SYSST), | ||
| 456 | ULPD_SHOW(ULPD_IT_STATUS), | ||
| 457 | ULPD_SHOW(ULPD_CLOCK_CTRL), | ||
| 458 | ULPD_SHOW(ULPD_SOFT_REQ), | ||
| 459 | ULPD_SHOW(ULPD_DPLL_CTRL), | ||
| 460 | ULPD_SHOW(ULPD_STATUS_REQ), | ||
| 461 | ULPD_SHOW(ULPD_POWER_CTRL)); | ||
| 462 | |||
| 463 | if (cpu_is_omap1510()) { | ||
| 464 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
| 465 | "MPUI1510_CTRL_REG 0x%-8x \n" | ||
| 466 | "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" | ||
| 467 | "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
| 468 | "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
| 469 | "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
| 470 | "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
| 471 | MPUI1510_SHOW(MPUI_CTRL), | ||
| 472 | MPUI1510_SHOW(MPUI_DSP_STATUS), | ||
| 473 | MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
| 474 | MPUI1510_SHOW(MPUI_DSP_API_CONFIG), | ||
| 475 | MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), | ||
| 476 | MPUI1510_SHOW(EMIFS_CONFIG)); | ||
| 477 | } else if (cpu_is_omap16xx()) { | ||
| 478 | my_buffer_offset += sprintf(my_base + my_buffer_offset, | ||
| 479 | "MPUI1610_CTRL_REG 0x%-8x \n" | ||
| 480 | "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" | ||
| 481 | "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" | ||
| 482 | "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" | ||
| 483 | "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" | ||
| 484 | "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", | ||
| 485 | MPUI1610_SHOW(MPUI_CTRL), | ||
| 486 | MPUI1610_SHOW(MPUI_DSP_STATUS), | ||
| 487 | MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), | ||
| 488 | MPUI1610_SHOW(MPUI_DSP_API_CONFIG), | ||
| 489 | MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), | ||
| 490 | MPUI1610_SHOW(EMIFS_CONFIG)); | ||
| 491 | } | ||
| 492 | |||
| 493 | g_read_completed++; | ||
| 494 | } else if (g_read_completed >= 1) { | ||
| 495 | *eof = 1; | ||
| 496 | return 0; | ||
| 497 | } | ||
| 498 | g_read_completed++; | ||
| 499 | |||
| 500 | *my_first_byte = page_buffer; | ||
| 501 | return my_buffer_offset; | ||
| 502 | } | ||
| 503 | |||
| 504 | static void omap_pm_init_proc(void) | ||
| 505 | { | ||
| 506 | struct proc_dir_entry *entry; | ||
| 507 | |||
| 508 | entry = create_proc_read_entry("driver/omap_pm", | ||
| 509 | S_IWUSR | S_IRUGO, NULL, | ||
| 510 | omap_pm_read_proc, 0); | ||
| 511 | } | ||
| 512 | |||
| 513 | #endif /* DEBUG && CONFIG_PROC_FS */ | ||
| 514 | |||
| 515 | /* | ||
| 516 | * omap_pm_prepare - Do preliminary suspend work. | ||
| 517 | * @state: suspend state we're entering. | ||
| 518 | * | ||
| 519 | */ | ||
| 520 | //#include <asm/arch/hardware.h> | ||
| 521 | |||
| 522 | static int omap_pm_prepare(suspend_state_t state) | ||
| 523 | { | ||
| 524 | int error = 0; | ||
| 525 | |||
| 526 | switch (state) | ||
| 527 | { | ||
| 528 | case PM_SUSPEND_STANDBY: | ||
| 529 | case PM_SUSPEND_MEM: | ||
| 530 | break; | ||
| 531 | |||
| 532 | case PM_SUSPEND_DISK: | ||
| 533 | return -ENOTSUPP; | ||
| 534 | |||
| 535 | default: | ||
| 536 | return -EINVAL; | ||
| 537 | } | ||
| 538 | |||
| 539 | return error; | ||
| 540 | } | ||
| 541 | |||
| 542 | |||
| 543 | /* | ||
| 544 | * omap_pm_enter - Actually enter a sleep state. | ||
| 545 | * @state: State we're entering. | ||
| 546 | * | ||
| 547 | */ | ||
| 548 | |||
| 549 | static int omap_pm_enter(suspend_state_t state) | ||
| 550 | { | ||
| 551 | switch (state) | ||
| 552 | { | ||
| 553 | case PM_SUSPEND_STANDBY: | ||
| 554 | case PM_SUSPEND_MEM: | ||
| 555 | omap_pm_suspend(); | ||
| 556 | break; | ||
| 557 | |||
| 558 | case PM_SUSPEND_DISK: | ||
| 559 | return -ENOTSUPP; | ||
| 560 | |||
| 561 | default: | ||
| 562 | return -EINVAL; | ||
| 563 | } | ||
| 564 | |||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | |||
| 569 | /** | ||
| 570 | * omap_pm_finish - Finish up suspend sequence. | ||
| 571 | * @state: State we're coming out of. | ||
| 572 | * | ||
| 573 | * This is called after we wake back up (or if entering the sleep state | ||
| 574 | * failed). | ||
| 575 | */ | ||
| 576 | |||
| 577 | static int omap_pm_finish(suspend_state_t state) | ||
| 578 | { | ||
| 579 | return 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | |||
| 583 | struct pm_ops omap_pm_ops ={ | ||
| 584 | .pm_disk_mode = 0, | ||
| 585 | .prepare = omap_pm_prepare, | ||
| 586 | .enter = omap_pm_enter, | ||
| 587 | .finish = omap_pm_finish, | ||
| 588 | }; | ||
| 589 | |||
| 590 | static int __init omap_pm_init(void) | ||
| 591 | { | ||
| 592 | printk("Power Management for TI OMAP.\n"); | ||
| 593 | pm_idle = omap_pm_idle; | ||
| 594 | /* | ||
| 595 | * We copy the assembler sleep/wakeup routines to SRAM. | ||
| 596 | * These routines need to be in SRAM as that's the only | ||
| 597 | * memory the MPU can see when it wakes up. | ||
| 598 | */ | ||
| 599 | |||
| 600 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 601 | if (cpu_is_omap1510()) { | ||
| 602 | memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND, | ||
| 603 | omap1510_idle_loop_suspend, | ||
| 604 | omap1510_idle_loop_suspend_sz); | ||
| 605 | memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend, | ||
| 606 | omap1510_cpu_suspend_sz); | ||
| 607 | } else | ||
| 608 | #endif | ||
| 609 | if (cpu_is_omap1610() || cpu_is_omap1710()) { | ||
| 610 | memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND, | ||
| 611 | omap1610_idle_loop_suspend, | ||
| 612 | omap1610_idle_loop_suspend_sz); | ||
| 613 | memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
| 614 | omap1610_cpu_suspend_sz); | ||
| 615 | } else if (cpu_is_omap5912()) { | ||
| 616 | memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND, | ||
| 617 | omap1610_idle_loop_suspend, | ||
| 618 | omap1610_idle_loop_suspend_sz); | ||
| 619 | memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend, | ||
| 620 | omap1610_cpu_suspend_sz); | ||
| 621 | } | ||
| 622 | |||
| 623 | pm_set_ops(&omap_pm_ops); | ||
| 624 | |||
| 625 | #if defined(DEBUG) && defined(CONFIG_PROC_FS) | ||
| 626 | omap_pm_init_proc(); | ||
| 627 | #endif | ||
| 628 | |||
| 629 | return 0; | ||
| 630 | } | ||
| 631 | __initcall(omap_pm_init); | ||
| 632 | |||
diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/plat-omap/sleep.S new file mode 100644 index 000000000000..279490ce772b --- /dev/null +++ b/arch/arm/plat-omap/sleep.S | |||
| @@ -0,0 +1,314 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/plat-omap/sleep.S | ||
| 3 | * | ||
| 4 | * Low-level OMAP1510/1610 sleep/wakeUp support | ||
| 5 | * | ||
| 6 | * Initial SA1110 code: | ||
| 7 | * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> | ||
| 8 | * | ||
| 9 | * Adapted for PXA by Nicolas Pitre: | ||
| 10 | * Copyright (c) 2002 Monta Vista Software, Inc. | ||
| 11 | * | ||
| 12 | * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify it | ||
| 15 | * under the terms of the GNU General Public License as published by the | ||
| 16 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 17 | * option) any later version. | ||
| 18 | * | ||
| 19 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
| 20 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
| 22 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
| 25 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | * | ||
| 30 | * You should have received a copy of the GNU General Public License along | ||
| 31 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 32 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 33 | */ | ||
| 34 | |||
| 35 | #include <linux/config.h> | ||
| 36 | #include <linux/linkage.h> | ||
| 37 | #include <asm/assembler.h> | ||
| 38 | #include <asm/arch/io.h> | ||
| 39 | #include <asm/arch/pm.h> | ||
| 40 | |||
| 41 | .text | ||
| 42 | |||
| 43 | /* | ||
| 44 | * Forces OMAP into idle state | ||
| 45 | * | ||
| 46 | * omapXXXX_idle_loop_suspend() | ||
| 47 | * | ||
| 48 | * Note: This code get's copied to internal SRAM at boot. When the OMAP | ||
| 49 | * wakes up it continues execution at the point it went to sleep. | ||
| 50 | * | ||
| 51 | * Note: Because of slightly different configuration values we have | ||
| 52 | * processor specific functions here. | ||
| 53 | */ | ||
| 54 | |||
| 55 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 56 | ENTRY(omap1510_idle_loop_suspend) | ||
| 57 | |||
| 58 | stmfd sp!, {r0 - r12, lr} @ save registers on stack | ||
| 59 | |||
| 60 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
| 61 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
| 62 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
| 63 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
| 64 | |||
| 65 | @ turn off clock domains | ||
| 66 | @ get ARM_IDLECT2 into r2 | ||
| 67 | ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 68 | mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff | ||
| 69 | orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 | ||
| 70 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 71 | |||
| 72 | @ request ARM idle | ||
| 73 | @ get ARM_IDLECT1 into r1 | ||
| 74 | ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 75 | orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff | ||
| 76 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 77 | |||
| 78 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
| 79 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
| 80 | l_1510: subs r5, r5, #1 | ||
| 81 | bne l_1510 | ||
| 82 | /* | ||
| 83 | * Let's wait for the next clock tick to wake us up. | ||
| 84 | */ | ||
| 85 | mov r0, #0 | ||
| 86 | mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt | ||
| 87 | /* | ||
| 88 | * omap1510_idle_loop_suspend()'s resume point. | ||
| 89 | * | ||
| 90 | * It will just start executing here, so we'll restore stuff from the | ||
| 91 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
| 92 | */ | ||
| 93 | |||
| 94 | @ restore ARM_IDLECT1 and ARM_IDLECT2 and return | ||
| 95 | @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 | ||
| 96 | strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 97 | strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 98 | |||
| 99 | ldmfd sp!, {r0 - r12, pc} @ restore regs and return | ||
| 100 | |||
| 101 | ENTRY(omap1510_idle_loop_suspend_sz) | ||
| 102 | .word . - omap1510_idle_loop_suspend | ||
| 103 | #endif /* CONFIG_ARCH_OMAP1510 */ | ||
| 104 | |||
| 105 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 106 | ENTRY(omap1610_idle_loop_suspend) | ||
| 107 | |||
| 108 | stmfd sp!, {r0 - r12, lr} @ save registers on stack | ||
| 109 | |||
| 110 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
| 111 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
| 112 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
| 113 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
| 114 | |||
| 115 | @ turn off clock domains | ||
| 116 | @ get ARM_IDLECT2 into r2 | ||
| 117 | ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 118 | mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff | ||
| 119 | orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 | ||
| 120 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 121 | |||
| 122 | @ request ARM idle | ||
| 123 | @ get ARM_IDLECT1 into r1 | ||
| 124 | ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 125 | orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff | ||
| 126 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 127 | |||
| 128 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
| 129 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
| 130 | l_1610: subs r5, r5, #1 | ||
| 131 | bne l_1610 | ||
| 132 | /* | ||
| 133 | * Let's wait for the next clock tick to wake us up. | ||
| 134 | */ | ||
| 135 | mov r0, #0 | ||
| 136 | mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt | ||
| 137 | /* | ||
| 138 | * omap1610_idle_loop_suspend()'s resume point. | ||
| 139 | * | ||
| 140 | * It will just start executing here, so we'll restore stuff from the | ||
| 141 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
| 142 | */ | ||
| 143 | |||
| 144 | @ restore ARM_IDLECT1 and ARM_IDLECT2 and return | ||
| 145 | @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 | ||
| 146 | strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 147 | strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 148 | |||
| 149 | ldmfd sp!, {r0 - r12, pc} @ restore regs and return | ||
| 150 | |||
| 151 | ENTRY(omap1610_idle_loop_suspend_sz) | ||
| 152 | .word . - omap1610_idle_loop_suspend | ||
| 153 | #endif /* CONFIG_ARCH_OMAP16XX */ | ||
| 154 | |||
| 155 | /* | ||
| 156 | * Forces OMAP into deep sleep state | ||
| 157 | * | ||
| 158 | * omapXXXX_cpu_suspend() | ||
| 159 | * | ||
| 160 | * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed | ||
| 161 | * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 | ||
| 162 | * in register r1. | ||
| 163 | * | ||
| 164 | * Note: This code get's copied to internal SRAM at boot. When the OMAP | ||
| 165 | * wakes up it continues execution at the point it went to sleep. | ||
| 166 | * | ||
| 167 | * Note: Because of errata work arounds we have processor specific functions | ||
| 168 | * here. They are mostly the same, but slightly different. | ||
| 169 | * | ||
| 170 | */ | ||
| 171 | |||
| 172 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 173 | ENTRY(omap1510_cpu_suspend) | ||
| 174 | |||
| 175 | @ save registers on stack | ||
| 176 | stmfd sp!, {r0 - r12, lr} | ||
| 177 | |||
| 178 | @ load base address of Traffic Controller | ||
| 179 | mov r4, #TCMIF_ASM_BASE & 0xff000000 | ||
| 180 | orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 | ||
| 181 | orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 | ||
| 182 | |||
| 183 | @ work around errata of OMAP1510 PDE bit for TC shut down | ||
| 184 | @ clear PDE bit | ||
| 185 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 186 | bic r5, r5, #PDE_BIT & 0xff | ||
| 187 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 188 | |||
| 189 | @ set PWD_EN bit | ||
| 190 | and r5, r5, #PWD_EN_BIT & 0xff | ||
| 191 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 192 | |||
| 193 | @ prepare to put SDRAM into self-refresh manually | ||
| 194 | ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
| 195 | orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 | ||
| 196 | orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff | ||
| 197 | str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
| 198 | |||
| 199 | @ prepare to put EMIFS to Sleep | ||
| 200 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 201 | orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff | ||
| 202 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 203 | |||
| 204 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
| 205 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
| 206 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
| 207 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
| 208 | |||
| 209 | @ turn off clock domains | ||
| 210 | mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff | ||
| 211 | orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 | ||
| 212 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 213 | |||
| 214 | @ request ARM idle | ||
| 215 | mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff | ||
| 216 | orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 | ||
| 217 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 218 | |||
| 219 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
| 220 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
| 221 | l_1510_2: | ||
| 222 | subs r5, r5, #1 | ||
| 223 | bne l_1510_2 | ||
| 224 | /* | ||
| 225 | * Let's wait for the next wake up event to wake us up. r0 can't be | ||
| 226 | * used here because r0 holds ARM_IDLECT1 | ||
| 227 | */ | ||
| 228 | mov r2, #0 | ||
| 229 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt | ||
| 230 | /* | ||
| 231 | * omap1510_cpu_suspend()'s resume point. | ||
| 232 | * | ||
| 233 | * It will just start executing here, so we'll restore stuff from the | ||
| 234 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
| 235 | */ | ||
| 236 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 237 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 238 | |||
| 239 | @ restore regs and return | ||
| 240 | ldmfd sp!, {r0 - r12, pc} | ||
| 241 | |||
| 242 | ENTRY(omap1510_cpu_suspend_sz) | ||
| 243 | .word . - omap1510_cpu_suspend | ||
| 244 | #endif /* CONFIG_ARCH_OMAP1510 */ | ||
| 245 | |||
| 246 | #if defined(CONFIG_ARCH_OMAP16XX) | ||
| 247 | ENTRY(omap1610_cpu_suspend) | ||
| 248 | |||
| 249 | @ save registers on stack | ||
| 250 | stmfd sp!, {r0 - r12, lr} | ||
| 251 | |||
| 252 | @ load base address of Traffic Controller | ||
| 253 | mov r4, #TCMIF_ASM_BASE & 0xff000000 | ||
| 254 | orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 | ||
| 255 | orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 | ||
| 256 | |||
| 257 | @ prepare to put SDRAM into self-refresh manually | ||
| 258 | ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
| 259 | orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 | ||
| 260 | orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff | ||
| 261 | str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] | ||
| 262 | |||
| 263 | @ prepare to put EMIFS to Sleep | ||
| 264 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 265 | orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff | ||
| 266 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] | ||
| 267 | |||
| 268 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 | ||
| 269 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 | ||
| 270 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 | ||
| 271 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 | ||
| 272 | |||
| 273 | @ turn off clock domains | ||
| 274 | mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff | ||
| 275 | orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 | ||
| 276 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 277 | |||
| 278 | @ work around errata of OMAP1610/5912. Enable (!) peripheral | ||
| 279 | @ clock to let the chip go into deep sleep | ||
| 280 | ldrh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 281 | orr r5,r5, #EN_PERCK_BIT & 0xff | ||
| 282 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 283 | |||
| 284 | @ request ARM idle | ||
| 285 | mov r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff | ||
| 286 | orr r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00 | ||
| 287 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 288 | |||
| 289 | mov r5, #IDLE_WAIT_CYCLES & 0xff | ||
| 290 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 | ||
| 291 | l_1610_2: | ||
| 292 | subs r5, r5, #1 | ||
| 293 | bne l_1610_2 | ||
| 294 | /* | ||
| 295 | * Let's wait for the next wake up event to wake us up. r0 can't be | ||
| 296 | * used here because r0 holds ARM_IDLECT1 | ||
| 297 | */ | ||
| 298 | mov r2, #0 | ||
| 299 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt | ||
| 300 | /* | ||
| 301 | * omap1610_cpu_suspend()'s resume point. | ||
| 302 | * | ||
| 303 | * It will just start executing here, so we'll restore stuff from the | ||
| 304 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. | ||
| 305 | */ | ||
| 306 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] | ||
| 307 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] | ||
| 308 | |||
| 309 | @ restore regs and return | ||
| 310 | ldmfd sp!, {r0 - r12, pc} | ||
| 311 | |||
| 312 | ENTRY(omap1610_cpu_suspend_sz) | ||
| 313 | .word . - omap1610_cpu_suspend | ||
| 314 | #endif /* CONFIG_ARCH_OMAP16XX */ | ||
diff --git a/arch/arm/plat-omap/usb.c b/arch/arm/plat-omap/usb.c new file mode 100644 index 000000000000..ab38e4eb3130 --- /dev/null +++ b/arch/arm/plat-omap/usb.c | |||
| @@ -0,0 +1,593 @@ | |||
| 1 | /* | ||
| 2 | * arch/arm/plat-omap/usb.c -- platform level USB initialization | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004 Texas Instruments, Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #undef DEBUG | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/init.h> | ||
| 29 | #include <linux/device.h> | ||
| 30 | #include <linux/usb_otg.h> | ||
| 31 | |||
| 32 | #include <asm/io.h> | ||
| 33 | #include <asm/irq.h> | ||
| 34 | #include <asm/system.h> | ||
| 35 | #include <asm/hardware.h> | ||
| 36 | #include <asm/mach-types.h> | ||
| 37 | |||
| 38 | #include <asm/arch/mux.h> | ||
| 39 | #include <asm/arch/usb.h> | ||
| 40 | #include <asm/arch/board.h> | ||
| 41 | |||
| 42 | /* These routines should handle the standard chip-specific modes | ||
| 43 | * for usb0/1/2 ports, covering basic mux and transceiver setup. | ||
| 44 | * | ||
| 45 | * Some board-*.c files will need to set up additional mux options, | ||
| 46 | * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. | ||
| 47 | */ | ||
| 48 | |||
| 49 | /* TESTED ON: | ||
| 50 | * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
| 51 | * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables | ||
| 52 | * - 5912 OSK UDC, with *nonstandard* A-to-A cable | ||
| 53 | * - 1510 Innovator UDC with bundled usb0 cable | ||
| 54 | * - 1510 Innovator OHCI with bundled usb1/usb2 cable | ||
| 55 | * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS | ||
| 56 | * - 1710 custom development board using alternate pin group | ||
| 57 | * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables | ||
| 58 | */ | ||
| 59 | |||
| 60 | /*-------------------------------------------------------------------------*/ | ||
| 61 | |||
| 62 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
| 63 | |||
| 64 | static struct otg_transceiver *xceiv; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * otg_get_transceiver - find the (single) OTG transceiver driver | ||
| 68 | * | ||
| 69 | * Returns the transceiver driver, after getting a refcount to it; or | ||
| 70 | * null if there is no such transceiver. The caller is responsible for | ||
| 71 | * releasing that count. | ||
| 72 | */ | ||
| 73 | struct otg_transceiver *otg_get_transceiver(void) | ||
| 74 | { | ||
| 75 | if (xceiv) | ||
| 76 | get_device(xceiv->dev); | ||
| 77 | return xceiv; | ||
| 78 | } | ||
| 79 | EXPORT_SYMBOL(otg_get_transceiver); | ||
| 80 | |||
| 81 | int otg_set_transceiver(struct otg_transceiver *x) | ||
| 82 | { | ||
| 83 | if (xceiv && x) | ||
| 84 | return -EBUSY; | ||
| 85 | xceiv = x; | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | EXPORT_SYMBOL(otg_set_transceiver); | ||
| 89 | |||
| 90 | #endif | ||
| 91 | |||
| 92 | /*-------------------------------------------------------------------------*/ | ||
| 93 | |||
| 94 | static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) | ||
| 95 | { | ||
| 96 | u32 syscon1 = 0; | ||
| 97 | |||
| 98 | if (nwires == 0) { | ||
| 99 | if (!cpu_is_omap15xx()) { | ||
| 100 | /* pulldown D+/D- */ | ||
| 101 | USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); | ||
| 102 | } | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | if (is_device) | ||
| 107 | omap_cfg_reg(W4_USB_PUEN); | ||
| 108 | |||
| 109 | /* internal transceiver */ | ||
| 110 | if (nwires == 2) { | ||
| 111 | // omap_cfg_reg(P9_USB_DP); | ||
| 112 | // omap_cfg_reg(R8_USB_DM); | ||
| 113 | |||
| 114 | if (cpu_is_omap15xx()) { | ||
| 115 | /* This works on 1510-Innovator */ | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* NOTES: | ||
| 120 | * - peripheral should configure VBUS detection! | ||
| 121 | * - only peripherals may use the internal D+/D- pulldowns | ||
| 122 | * - OTG support on this port not yet written | ||
| 123 | */ | ||
| 124 | |||
| 125 | USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4); | ||
| 126 | if (!is_device) | ||
| 127 | USB_TRANSCEIVER_CTRL_REG |= (3 << 1); | ||
| 128 | |||
| 129 | return 3 << 16; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* alternate pin config, external transceiver */ | ||
| 133 | if (cpu_is_omap15xx()) { | ||
| 134 | printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | omap_cfg_reg(V6_USB0_TXD); | ||
| 139 | omap_cfg_reg(W9_USB0_TXEN); | ||
| 140 | omap_cfg_reg(W5_USB0_SE0); | ||
| 141 | |||
| 142 | /* NOTE: SPEED and SUSP aren't configured here */ | ||
| 143 | |||
| 144 | if (nwires != 3) | ||
| 145 | omap_cfg_reg(Y5_USB0_RCV); | ||
| 146 | if (nwires != 6) | ||
| 147 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
| 148 | |||
| 149 | switch (nwires) { | ||
| 150 | case 3: | ||
| 151 | syscon1 = 2; | ||
| 152 | break; | ||
| 153 | case 4: | ||
| 154 | syscon1 = 1; | ||
| 155 | break; | ||
| 156 | case 6: | ||
| 157 | syscon1 = 3; | ||
| 158 | omap_cfg_reg(AA9_USB0_VP); | ||
| 159 | omap_cfg_reg(R9_USB0_VM); | ||
| 160 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
| 161 | break; | ||
| 162 | default: | ||
| 163 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
| 164 | 0, nwires); | ||
| 165 | } | ||
| 166 | return syscon1 << 16; | ||
| 167 | } | ||
| 168 | |||
| 169 | static u32 __init omap_usb1_init(unsigned nwires) | ||
| 170 | { | ||
| 171 | u32 syscon1 = 0; | ||
| 172 | |||
| 173 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
| 174 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; | ||
| 175 | if (nwires == 0) | ||
| 176 | return 0; | ||
| 177 | |||
| 178 | /* external transceiver */ | ||
| 179 | omap_cfg_reg(USB1_TXD); | ||
| 180 | omap_cfg_reg(USB1_TXEN); | ||
| 181 | if (cpu_is_omap15xx()) { | ||
| 182 | omap_cfg_reg(USB1_SEO); | ||
| 183 | omap_cfg_reg(USB1_SPEED); | ||
| 184 | // SUSP | ||
| 185 | } else if (cpu_is_omap1610() || cpu_is_omap5912()) { | ||
| 186 | omap_cfg_reg(W13_1610_USB1_SE0); | ||
| 187 | omap_cfg_reg(R13_1610_USB1_SPEED); | ||
| 188 | // SUSP | ||
| 189 | } else if (cpu_is_omap1710()) { | ||
| 190 | omap_cfg_reg(R13_1710_USB1_SE0); | ||
| 191 | // SUSP | ||
| 192 | } else { | ||
| 193 | pr_debug("usb unrecognized\n"); | ||
| 194 | } | ||
| 195 | if (nwires != 3) | ||
| 196 | omap_cfg_reg(USB1_RCV); | ||
| 197 | |||
| 198 | switch (nwires) { | ||
| 199 | case 3: | ||
| 200 | syscon1 = 2; | ||
| 201 | break; | ||
| 202 | case 4: | ||
| 203 | syscon1 = 1; | ||
| 204 | break; | ||
| 205 | case 6: | ||
| 206 | syscon1 = 3; | ||
| 207 | omap_cfg_reg(USB1_VP); | ||
| 208 | omap_cfg_reg(USB1_VM); | ||
| 209 | if (!cpu_is_omap15xx()) | ||
| 210 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; | ||
| 211 | break; | ||
| 212 | default: | ||
| 213 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
| 214 | 1, nwires); | ||
| 215 | } | ||
| 216 | return syscon1 << 20; | ||
| 217 | } | ||
| 218 | |||
| 219 | static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) | ||
| 220 | { | ||
| 221 | u32 syscon1 = 0; | ||
| 222 | |||
| 223 | /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ | ||
| 224 | if (alt_pingroup || nwires == 0) | ||
| 225 | return 0; | ||
| 226 | if (nwires != 6 && !cpu_is_omap15xx()) | ||
| 227 | USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; | ||
| 228 | |||
| 229 | /* external transceiver */ | ||
| 230 | if (cpu_is_omap15xx()) { | ||
| 231 | omap_cfg_reg(USB2_TXD); | ||
| 232 | omap_cfg_reg(USB2_TXEN); | ||
| 233 | omap_cfg_reg(USB2_SEO); | ||
| 234 | if (nwires != 3) | ||
| 235 | omap_cfg_reg(USB2_RCV); | ||
| 236 | /* there is no USB2_SPEED */ | ||
| 237 | } else if (cpu_is_omap16xx()) { | ||
| 238 | omap_cfg_reg(V6_USB2_TXD); | ||
| 239 | omap_cfg_reg(W9_USB2_TXEN); | ||
| 240 | omap_cfg_reg(W5_USB2_SE0); | ||
| 241 | if (nwires != 3) | ||
| 242 | omap_cfg_reg(Y5_USB2_RCV); | ||
| 243 | // FIXME omap_cfg_reg(USB2_SPEED); | ||
| 244 | } else { | ||
| 245 | pr_debug("usb unrecognized\n"); | ||
| 246 | } | ||
| 247 | // omap_cfg_reg(USB2_SUSP); | ||
| 248 | |||
| 249 | switch (nwires) { | ||
| 250 | case 3: | ||
| 251 | syscon1 = 2; | ||
| 252 | break; | ||
| 253 | case 4: | ||
| 254 | syscon1 = 1; | ||
| 255 | break; | ||
| 256 | case 6: | ||
| 257 | syscon1 = 3; | ||
| 258 | if (cpu_is_omap15xx()) { | ||
| 259 | omap_cfg_reg(USB2_VP); | ||
| 260 | omap_cfg_reg(USB2_VM); | ||
| 261 | } else { | ||
| 262 | omap_cfg_reg(AA9_USB2_VP); | ||
| 263 | omap_cfg_reg(R9_USB2_VM); | ||
| 264 | USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; | ||
| 265 | } | ||
| 266 | break; | ||
| 267 | default: | ||
| 268 | printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", | ||
| 269 | 2, nwires); | ||
| 270 | } | ||
| 271 | return syscon1 << 24; | ||
| 272 | } | ||
| 273 | |||
| 274 | /*-------------------------------------------------------------------------*/ | ||
| 275 | |||
| 276 | #if defined(CONFIG_USB_GADGET_OMAP) || \ | ||
| 277 | defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ | ||
| 278 | (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) | ||
| 279 | static void usb_release(struct device *dev) | ||
| 280 | { | ||
| 281 | /* normally not freed */ | ||
| 282 | } | ||
| 283 | #endif | ||
| 284 | |||
| 285 | #ifdef CONFIG_USB_GADGET_OMAP | ||
| 286 | |||
| 287 | static struct resource udc_resources[] = { | ||
| 288 | /* order is significant! */ | ||
| 289 | { /* registers */ | ||
| 290 | .start = UDC_BASE, | ||
| 291 | .end = UDC_BASE + 0xff, | ||
| 292 | .flags = IORESOURCE_MEM, | ||
| 293 | }, { /* general IRQ */ | ||
| 294 | .start = IH2_BASE + 20, | ||
| 295 | .flags = IORESOURCE_IRQ, | ||
| 296 | }, { /* PIO IRQ */ | ||
| 297 | .start = IH2_BASE + 30, | ||
| 298 | .flags = IORESOURCE_IRQ, | ||
| 299 | }, { /* SOF IRQ */ | ||
| 300 | .start = IH2_BASE + 29, | ||
| 301 | .flags = IORESOURCE_IRQ, | ||
| 302 | }, | ||
| 303 | }; | ||
| 304 | |||
| 305 | static u64 udc_dmamask = ~(u32)0; | ||
| 306 | |||
| 307 | static struct platform_device udc_device = { | ||
| 308 | .name = "omap_udc", | ||
| 309 | .id = -1, | ||
| 310 | .dev = { | ||
| 311 | .release = usb_release, | ||
| 312 | .dma_mask = &udc_dmamask, | ||
| 313 | .coherent_dma_mask = 0xffffffff, | ||
| 314 | }, | ||
| 315 | .num_resources = ARRAY_SIZE(udc_resources), | ||
| 316 | .resource = udc_resources, | ||
| 317 | }; | ||
| 318 | |||
| 319 | #endif | ||
| 320 | |||
| 321 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
| 322 | |||
| 323 | /* The dmamask must be set for OHCI to work */ | ||
| 324 | static u64 ohci_dmamask = ~(u32)0; | ||
| 325 | |||
| 326 | static struct resource ohci_resources[] = { | ||
| 327 | { | ||
| 328 | .start = OMAP_OHCI_BASE, | ||
| 329 | .end = OMAP_OHCI_BASE + 4096, | ||
| 330 | .flags = IORESOURCE_MEM, | ||
| 331 | }, | ||
| 332 | { | ||
| 333 | .start = INT_USB_HHC_1, | ||
| 334 | .flags = IORESOURCE_IRQ, | ||
| 335 | }, | ||
| 336 | }; | ||
| 337 | |||
| 338 | static struct platform_device ohci_device = { | ||
| 339 | .name = "ohci", | ||
| 340 | .id = -1, | ||
| 341 | .dev = { | ||
| 342 | .release = usb_release, | ||
| 343 | .dma_mask = &ohci_dmamask, | ||
| 344 | .coherent_dma_mask = 0xffffffff, | ||
| 345 | }, | ||
| 346 | .num_resources = ARRAY_SIZE(ohci_resources), | ||
| 347 | .resource = ohci_resources, | ||
| 348 | }; | ||
| 349 | |||
| 350 | #endif | ||
| 351 | |||
| 352 | #if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) | ||
| 353 | |||
| 354 | static struct resource otg_resources[] = { | ||
| 355 | /* order is significant! */ | ||
| 356 | { | ||
| 357 | .start = OTG_BASE, | ||
| 358 | .end = OTG_BASE + 0xff, | ||
| 359 | .flags = IORESOURCE_MEM, | ||
| 360 | }, { | ||
| 361 | .start = IH2_BASE + 8, | ||
| 362 | .flags = IORESOURCE_IRQ, | ||
| 363 | }, | ||
| 364 | }; | ||
| 365 | |||
| 366 | static struct platform_device otg_device = { | ||
| 367 | .name = "omap_otg", | ||
| 368 | .id = -1, | ||
| 369 | .dev = { | ||
| 370 | .release = usb_release, | ||
| 371 | }, | ||
| 372 | .num_resources = ARRAY_SIZE(otg_resources), | ||
| 373 | .resource = otg_resources, | ||
| 374 | }; | ||
| 375 | |||
| 376 | #endif | ||
| 377 | |||
| 378 | /*-------------------------------------------------------------------------*/ | ||
| 379 | |||
| 380 | #define ULPD_CLOCK_CTRL_REG __REG16(ULPD_CLOCK_CTRL) | ||
| 381 | #define ULPD_SOFT_REQ_REG __REG16(ULPD_SOFT_REQ) | ||
| 382 | |||
| 383 | |||
| 384 | // FIXME correct answer depends on hmc_mode, | ||
| 385 | // as does any nonzero value for config->otg port number | ||
| 386 | #ifdef CONFIG_USB_GADGET_OMAP | ||
| 387 | #define is_usb0_device(config) 1 | ||
| 388 | #else | ||
| 389 | #define is_usb0_device(config) 0 | ||
| 390 | #endif | ||
| 391 | |||
| 392 | /*-------------------------------------------------------------------------*/ | ||
| 393 | |||
| 394 | #ifdef CONFIG_ARCH_OMAP_OTG | ||
| 395 | |||
| 396 | void __init | ||
| 397 | omap_otg_init(struct omap_usb_config *config) | ||
| 398 | { | ||
| 399 | u32 syscon = OTG_SYSCON_1_REG & 0xffff; | ||
| 400 | int status; | ||
| 401 | int alt_pingroup = 0; | ||
| 402 | |||
| 403 | /* NOTE: no bus or clock setup (yet?) */ | ||
| 404 | |||
| 405 | syscon = OTG_SYSCON_1_REG & 0xffff; | ||
| 406 | if (!(syscon & OTG_RESET_DONE)) | ||
| 407 | pr_debug("USB resets not complete?\n"); | ||
| 408 | |||
| 409 | // OTG_IRQ_EN_REG = 0; | ||
| 410 | |||
| 411 | /* pin muxing and transceiver pinouts */ | ||
| 412 | if (config->pins[0] > 2) /* alt pingroup 2 */ | ||
| 413 | alt_pingroup = 1; | ||
| 414 | syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
| 415 | syscon |= omap_usb1_init(config->pins[1]); | ||
| 416 | syscon |= omap_usb2_init(config->pins[2], alt_pingroup); | ||
| 417 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
| 418 | OTG_SYSCON_1_REG = syscon; | ||
| 419 | |||
| 420 | syscon = config->hmc_mode; | ||
| 421 | syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; | ||
| 422 | #ifdef CONFIG_USB_OTG | ||
| 423 | if (config->otg) | ||
| 424 | syscon |= OTG_EN; | ||
| 425 | #endif | ||
| 426 | pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); | ||
| 427 | pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); | ||
| 428 | OTG_SYSCON_2_REG = syscon; | ||
| 429 | |||
| 430 | printk("USB: hmc %d", config->hmc_mode); | ||
| 431 | if (alt_pingroup) | ||
| 432 | printk(", usb2 alt %d wires", config->pins[2]); | ||
| 433 | else if (config->pins[0]) | ||
| 434 | printk(", usb0 %d wires%s", config->pins[0], | ||
| 435 | is_usb0_device(config) ? " (dev)" : ""); | ||
| 436 | if (config->pins[1]) | ||
| 437 | printk(", usb1 %d wires", config->pins[1]); | ||
| 438 | if (!alt_pingroup && config->pins[2]) | ||
| 439 | printk(", usb2 %d wires", config->pins[2]); | ||
| 440 | if (config->otg) | ||
| 441 | printk(", Mini-AB on usb%d", config->otg - 1); | ||
| 442 | printk("\n"); | ||
| 443 | |||
| 444 | /* leave USB clocks/controllers off until needed */ | ||
| 445 | ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; | ||
| 446 | ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; | ||
| 447 | ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; | ||
| 448 | syscon = OTG_SYSCON_1_REG; | ||
| 449 | syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; | ||
| 450 | |||
| 451 | #ifdef CONFIG_USB_GADGET_OMAP | ||
| 452 | if (config->otg || config->register_dev) { | ||
| 453 | syscon &= ~DEV_IDLE_EN; | ||
| 454 | udc_device.dev.platform_data = config; | ||
| 455 | /* FIXME patch IRQ numbers for omap730 */ | ||
| 456 | status = platform_device_register(&udc_device); | ||
| 457 | if (status) | ||
| 458 | pr_debug("can't register UDC device, %d\n", status); | ||
| 459 | } | ||
| 460 | #endif | ||
| 461 | |||
| 462 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
| 463 | if (config->otg || config->register_host) { | ||
| 464 | syscon &= ~HST_IDLE_EN; | ||
| 465 | ohci_device.dev.platform_data = config; | ||
| 466 | if (cpu_is_omap730()) | ||
| 467 | ohci_resources[1].start = INT_730_USB_HHC_1; | ||
| 468 | status = platform_device_register(&ohci_device); | ||
| 469 | if (status) | ||
| 470 | pr_debug("can't register OHCI device, %d\n", status); | ||
| 471 | } | ||
| 472 | #endif | ||
| 473 | |||
| 474 | #ifdef CONFIG_USB_OTG | ||
| 475 | if (config->otg) { | ||
| 476 | syscon &= ~OTG_IDLE_EN; | ||
| 477 | otg_device.dev.platform_data = config; | ||
| 478 | if (cpu_is_omap730()) | ||
| 479 | otg_resources[1].start = INT_730_USB_OTG; | ||
| 480 | status = platform_device_register(&otg_device); | ||
| 481 | if (status) | ||
| 482 | pr_debug("can't register OTG device, %d\n", status); | ||
| 483 | } | ||
| 484 | #endif | ||
| 485 | pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); | ||
| 486 | OTG_SYSCON_1_REG = syscon; | ||
| 487 | |||
| 488 | status = 0; | ||
| 489 | } | ||
| 490 | |||
| 491 | #else | ||
| 492 | static inline void omap_otg_init(struct omap_usb_config *config) {} | ||
| 493 | #endif | ||
| 494 | |||
| 495 | /*-------------------------------------------------------------------------*/ | ||
| 496 | |||
| 497 | #ifdef CONFIG_ARCH_OMAP1510 | ||
| 498 | |||
| 499 | #define ULPD_DPLL_CTRL_REG __REG16(ULPD_DPLL_CTRL) | ||
| 500 | #define DPLL_IOB (1 << 13) | ||
| 501 | #define DPLL_PLL_ENABLE (1 << 4) | ||
| 502 | #define DPLL_LOCK (1 << 0) | ||
| 503 | |||
| 504 | #define ULPD_APLL_CTRL_REG __REG16(ULPD_APLL_CTRL) | ||
| 505 | #define APLL_NDPLL_SWITCH (1 << 0) | ||
| 506 | |||
| 507 | |||
| 508 | static void __init omap_1510_usb_init(struct omap_usb_config *config) | ||
| 509 | { | ||
| 510 | int status; | ||
| 511 | unsigned int val; | ||
| 512 | |||
| 513 | omap_usb0_init(config->pins[0], is_usb0_device(config)); | ||
| 514 | omap_usb1_init(config->pins[1]); | ||
| 515 | omap_usb2_init(config->pins[2], 0); | ||
| 516 | |||
| 517 | val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); | ||
| 518 | val |= (config->hmc_mode << 1); | ||
| 519 | omap_writel(val, MOD_CONF_CTRL_0); | ||
| 520 | |||
| 521 | printk("USB: hmc %d", config->hmc_mode); | ||
| 522 | if (config->pins[0]) | ||
| 523 | printk(", usb0 %d wires%s", config->pins[0], | ||
| 524 | is_usb0_device(config) ? " (dev)" : ""); | ||
| 525 | if (config->pins[1]) | ||
| 526 | printk(", usb1 %d wires", config->pins[1]); | ||
| 527 | if (config->pins[2]) | ||
| 528 | printk(", usb2 %d wires", config->pins[2]); | ||
| 529 | printk("\n"); | ||
| 530 | |||
| 531 | /* use DPLL for 48 MHz function clock */ | ||
| 532 | pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG, | ||
| 533 | ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG); | ||
| 534 | ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH; | ||
| 535 | ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE; | ||
| 536 | ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ; | ||
| 537 | while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK)) | ||
| 538 | cpu_relax(); | ||
| 539 | |||
| 540 | #ifdef CONFIG_USB_GADGET_OMAP | ||
| 541 | if (config->register_dev) { | ||
| 542 | udc_device.dev.platform_data = config; | ||
| 543 | status = platform_device_register(&udc_device); | ||
| 544 | if (status) | ||
| 545 | pr_debug("can't register UDC device, %d\n", status); | ||
| 546 | /* udc driver gates 48MHz by D+ pullup */ | ||
| 547 | } | ||
| 548 | #endif | ||
| 549 | |||
| 550 | #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) | ||
| 551 | if (config->register_host) { | ||
| 552 | ohci_device.dev.platform_data = config; | ||
| 553 | status = platform_device_register(&ohci_device); | ||
| 554 | if (status) | ||
| 555 | pr_debug("can't register OHCI device, %d\n", status); | ||
| 556 | /* hcd explicitly gates 48MHz */ | ||
| 557 | } | ||
| 558 | #endif | ||
| 559 | } | ||
| 560 | |||
| 561 | #else | ||
| 562 | static inline void omap_1510_usb_init(struct omap_usb_config *config) {} | ||
| 563 | #endif | ||
| 564 | |||
| 565 | /*-------------------------------------------------------------------------*/ | ||
| 566 | |||
| 567 | static struct omap_usb_config platform_data; | ||
| 568 | |||
| 569 | static int __init | ||
| 570 | omap_usb_init(void) | ||
| 571 | { | ||
| 572 | const struct omap_usb_config *config; | ||
| 573 | |||
| 574 | config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); | ||
| 575 | if (config == NULL) { | ||
| 576 | printk(KERN_ERR "USB: No board-specific " | ||
| 577 | "platform config found\n"); | ||
| 578 | return -ENODEV; | ||
| 579 | } | ||
| 580 | platform_data = *config; | ||
| 581 | |||
| 582 | if (cpu_is_omap730() || cpu_is_omap16xx()) | ||
| 583 | omap_otg_init(&platform_data); | ||
| 584 | else if (cpu_is_omap15xx()) | ||
| 585 | omap_1510_usb_init(&platform_data); | ||
| 586 | else { | ||
| 587 | printk(KERN_ERR "USB: No init for your chip yet\n"); | ||
| 588 | return -ENODEV; | ||
| 589 | } | ||
| 590 | return 0; | ||
| 591 | } | ||
| 592 | |||
| 593 | subsys_initcall(omap_usb_init); | ||
