diff options
author | Tony Lindgren <tony@atomide.com> | 2005-07-10 14:58:15 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2005-07-10 14:58:15 -0400 |
commit | 5e1c5ff4783e0ddd241580c9996390508722190e (patch) | |
tree | 6c0a8a16046936f9046488790f817e0aa5884ac5 /arch/arm/plat-omap | |
parent | b91585560b59fd3ef4e20ca6f7d35aefda193774 (diff) |
[PATCH] ARM: 2812/1: OMAP update 7c/11: Move arch-omap to plat-omap
Patch from Tony Lindgren
This patch move common OMAP code from arch-omap to plat-omap
directory.
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-omap')
-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); | ||