diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/boot/simple/mv64x60_tty.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc/boot/simple/mv64x60_tty.c')
-rw-r--r-- | arch/ppc/boot/simple/mv64x60_tty.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/arch/ppc/boot/simple/mv64x60_tty.c b/arch/ppc/boot/simple/mv64x60_tty.c new file mode 100644 index 000000000000..5b45eb46b669 --- /dev/null +++ b/arch/ppc/boot/simple/mv64x60_tty.c | |||
@@ -0,0 +1,360 @@ | |||
1 | /* | ||
2 | * arch/ppc/boot/simple/mv64x60_tty.c | ||
3 | * | ||
4 | * Bootloader version of the embedded MPSC/UART driver for the Marvell 64x60. | ||
5 | * Note: Due to a GT64260A erratum, DMA will be used for UART input (via SDMA). | ||
6 | * | ||
7 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
8 | * | ||
9 | * 2001 (c) MontaVista Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | |||
15 | /* This code assumes that the data cache has been disabled (L1, L2, L3). */ | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/serial_reg.h> | ||
20 | #include <asm/serial.h> | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/mv64x60_defs.h> | ||
23 | #include <mpsc_defs.h> | ||
24 | |||
25 | u32 mv64x60_console_baud = 9600; | ||
26 | u32 mv64x60_mpsc_clk_src = 8; /* TCLK */ | ||
27 | u32 mv64x60_mpsc_clk_freq = 100000000; | ||
28 | |||
29 | extern void udelay(long); | ||
30 | static void stop_dma(int chan); | ||
31 | |||
32 | static void __iomem *mv64x60_base = (void __iomem *)CONFIG_MV64X60_NEW_BASE; | ||
33 | |||
34 | struct sdma_regs { | ||
35 | u32 sdc; | ||
36 | u32 sdcm; | ||
37 | u32 rx_desc; | ||
38 | u32 rx_buf_ptr; | ||
39 | u32 scrdp; | ||
40 | u32 tx_desc; | ||
41 | u32 sctdp; | ||
42 | u32 sftdp; | ||
43 | }; | ||
44 | |||
45 | static struct sdma_regs sdma_regs[2]; | ||
46 | |||
47 | #define SDMA_REGS_INIT(s, reg_base) { \ | ||
48 | (s)->sdc = (reg_base) + SDMA_SDC; \ | ||
49 | (s)->sdcm = (reg_base) + SDMA_SDCM; \ | ||
50 | (s)->rx_desc = (reg_base) + SDMA_RX_DESC; \ | ||
51 | (s)->rx_buf_ptr = (reg_base) + SDMA_RX_BUF_PTR; \ | ||
52 | (s)->scrdp = (reg_base) + SDMA_SCRDP; \ | ||
53 | (s)->tx_desc = (reg_base) + SDMA_TX_DESC; \ | ||
54 | (s)->sctdp = (reg_base) + SDMA_SCTDP; \ | ||
55 | (s)->sftdp = (reg_base) + SDMA_SFTDP; \ | ||
56 | } | ||
57 | |||
58 | static u32 mpsc_base[2] = { MV64x60_MPSC_0_OFFSET, MV64x60_MPSC_1_OFFSET }; | ||
59 | |||
60 | struct mv64x60_rx_desc { | ||
61 | u16 bufsize; | ||
62 | u16 bytecnt; | ||
63 | u32 cmd_stat; | ||
64 | u32 next_desc_ptr; | ||
65 | u32 buffer; | ||
66 | }; | ||
67 | |||
68 | struct mv64x60_tx_desc { | ||
69 | u16 bytecnt; | ||
70 | u16 shadow; | ||
71 | u32 cmd_stat; | ||
72 | u32 next_desc_ptr; | ||
73 | u32 buffer; | ||
74 | }; | ||
75 | |||
76 | #define MAX_RESET_WAIT 10000 | ||
77 | #define MAX_TX_WAIT 10000 | ||
78 | |||
79 | #define RX_NUM_DESC 2 | ||
80 | #define TX_NUM_DESC 2 | ||
81 | |||
82 | #define RX_BUF_SIZE 32 | ||
83 | #define TX_BUF_SIZE 32 | ||
84 | |||
85 | static struct mv64x60_rx_desc rd[2][RX_NUM_DESC] __attribute__ ((aligned(32))); | ||
86 | static struct mv64x60_tx_desc td[2][TX_NUM_DESC] __attribute__ ((aligned(32))); | ||
87 | |||
88 | static char rx_buf[2][RX_NUM_DESC * RX_BUF_SIZE] __attribute__ ((aligned(32))); | ||
89 | static char tx_buf[2][TX_NUM_DESC * TX_BUF_SIZE] __attribute__ ((aligned(32))); | ||
90 | |||
91 | static int cur_rd[2] = { 0, 0 }; | ||
92 | static int cur_td[2] = { 0, 0 }; | ||
93 | |||
94 | static char chan_initialized[2] = { 0, 0 }; | ||
95 | |||
96 | |||
97 | #define RX_INIT_RDP(rdp) { \ | ||
98 | (rdp)->bufsize = 2; \ | ||
99 | (rdp)->bytecnt = 0; \ | ||
100 | (rdp)->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F | \ | ||
101 | SDMA_DESC_CMDSTAT_O; \ | ||
102 | } | ||
103 | |||
104 | #ifdef CONFIG_MV64360 | ||
105 | static u32 cpu2mem_tab[MV64x60_CPU2MEM_WINDOWS][2] = { | ||
106 | { MV64x60_CPU2MEM_0_BASE, MV64x60_CPU2MEM_0_SIZE }, | ||
107 | { MV64x60_CPU2MEM_1_BASE, MV64x60_CPU2MEM_1_SIZE }, | ||
108 | { MV64x60_CPU2MEM_2_BASE, MV64x60_CPU2MEM_2_SIZE }, | ||
109 | { MV64x60_CPU2MEM_3_BASE, MV64x60_CPU2MEM_3_SIZE } | ||
110 | }; | ||
111 | |||
112 | static u32 com2mem_tab[MV64x60_CPU2MEM_WINDOWS][2] = { | ||
113 | { MV64360_MPSC2MEM_0_BASE, MV64360_MPSC2MEM_0_SIZE }, | ||
114 | { MV64360_MPSC2MEM_1_BASE, MV64360_MPSC2MEM_1_SIZE }, | ||
115 | { MV64360_MPSC2MEM_2_BASE, MV64360_MPSC2MEM_2_SIZE }, | ||
116 | { MV64360_MPSC2MEM_3_BASE, MV64360_MPSC2MEM_3_SIZE } | ||
117 | }; | ||
118 | |||
119 | static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] = { 0xe, 0xd, 0xb, 0x7 }; | ||
120 | #endif | ||
121 | |||
122 | unsigned long | ||
123 | serial_init(int chan, void *ignored) | ||
124 | { | ||
125 | u32 mpsc_routing_base, sdma_base, brg_bcr, cdv; | ||
126 | int i; | ||
127 | |||
128 | chan = (chan == 1); /* default to chan 0 if anything but 1 */ | ||
129 | |||
130 | if (chan_initialized[chan]) | ||
131 | return chan; | ||
132 | |||
133 | chan_initialized[chan] = 1; | ||
134 | |||
135 | if (chan == 0) { | ||
136 | sdma_base = MV64x60_SDMA_0_OFFSET; | ||
137 | brg_bcr = MV64x60_BRG_0_OFFSET + BRG_BCR; | ||
138 | SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_0_OFFSET); | ||
139 | } else { | ||
140 | sdma_base = MV64x60_SDMA_1_OFFSET; | ||
141 | brg_bcr = MV64x60_BRG_1_OFFSET + BRG_BCR; | ||
142 | SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_1_OFFSET); | ||
143 | } | ||
144 | |||
145 | mpsc_routing_base = MV64x60_MPSC_ROUTING_OFFSET; | ||
146 | |||
147 | stop_dma(chan); | ||
148 | |||
149 | /* Set up ring buffers */ | ||
150 | for (i=0; i<RX_NUM_DESC; i++) { | ||
151 | RX_INIT_RDP(&rd[chan][i]); | ||
152 | rd[chan][i].buffer = (u32)&rx_buf[chan][i * RX_BUF_SIZE]; | ||
153 | rd[chan][i].next_desc_ptr = (u32)&rd[chan][i+1]; | ||
154 | } | ||
155 | rd[chan][RX_NUM_DESC - 1].next_desc_ptr = (u32)&rd[chan][0]; | ||
156 | |||
157 | for (i=0; i<TX_NUM_DESC; i++) { | ||
158 | td[chan][i].bytecnt = 0; | ||
159 | td[chan][i].shadow = 0; | ||
160 | td[chan][i].buffer = (u32)&tx_buf[chan][i * TX_BUF_SIZE]; | ||
161 | td[chan][i].cmd_stat = SDMA_DESC_CMDSTAT_F|SDMA_DESC_CMDSTAT_L; | ||
162 | td[chan][i].next_desc_ptr = (u32)&td[chan][i+1]; | ||
163 | } | ||
164 | td[chan][TX_NUM_DESC - 1].next_desc_ptr = (u32)&td[chan][0]; | ||
165 | |||
166 | /* Set MPSC Routing */ | ||
167 | out_le32(mv64x60_base + mpsc_routing_base + MPSC_MRR, 0x3ffffe38); | ||
168 | |||
169 | #ifdef CONFIG_GT64260 | ||
170 | out_le32(mv64x60_base + GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102); | ||
171 | #else /* Must be MV64360 or MV64460 */ | ||
172 | { | ||
173 | u32 enables, prot_bits, v; | ||
174 | |||
175 | /* Set up comm unit to memory mapping windows */ | ||
176 | /* Note: Assumes MV64x60_CPU2MEM_WINDOWS == 4 */ | ||
177 | |||
178 | enables = in_le32(mv64x60_base + MV64360_CPU_BAR_ENABLE) & 0xf; | ||
179 | prot_bits = 0; | ||
180 | |||
181 | for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) { | ||
182 | if (!(enables & (1 << i))) { | ||
183 | v = in_le32(mv64x60_base + cpu2mem_tab[i][0]); | ||
184 | v = ((v & 0xffff) << 16) | (dram_selects[i] << 8); | ||
185 | out_le32(mv64x60_base + com2mem_tab[i][0], v); | ||
186 | |||
187 | v = in_le32(mv64x60_base + cpu2mem_tab[i][1]); | ||
188 | v = (v & 0xffff) << 16; | ||
189 | out_le32(mv64x60_base + com2mem_tab[i][1], v); | ||
190 | |||
191 | prot_bits |= (0x3 << (i << 1)); /* r/w access */ | ||
192 | } | ||
193 | } | ||
194 | |||
195 | out_le32(mv64x60_base + MV64360_MPSC_0_REMAP, 0); | ||
196 | out_le32(mv64x60_base + MV64360_MPSC_1_REMAP, 0); | ||
197 | out_le32(mv64x60_base + MV64360_MPSC2MEM_ACC_PROT_0, prot_bits); | ||
198 | out_le32(mv64x60_base + MV64360_MPSC2MEM_ACC_PROT_1, prot_bits); | ||
199 | out_le32(mv64x60_base + MV64360_MPSC2MEM_BAR_ENABLE, enables); | ||
200 | } | ||
201 | #endif | ||
202 | |||
203 | /* MPSC 0/1 Rx & Tx get clocks BRG0/1 */ | ||
204 | out_le32(mv64x60_base + mpsc_routing_base + MPSC_RCRR, 0x00000100); | ||
205 | out_le32(mv64x60_base + mpsc_routing_base + MPSC_TCRR, 0x00000100); | ||
206 | |||
207 | /* clear pending interrupts */ | ||
208 | out_le32(mv64x60_base + MV64x60_SDMA_INTR_OFFSET + SDMA_INTR_MASK, 0); | ||
209 | |||
210 | out_le32(mv64x60_base + SDMA_SCRDP + sdma_base, (int)&rd[chan][0]); | ||
211 | out_le32(mv64x60_base + SDMA_SCTDP + sdma_base, | ||
212 | (int)&td[chan][TX_NUM_DESC - 1]); | ||
213 | out_le32(mv64x60_base + SDMA_SFTDP + sdma_base, | ||
214 | (int)&td[chan][TX_NUM_DESC - 1]); | ||
215 | |||
216 | out_le32(mv64x60_base + SDMA_SDC + sdma_base, | ||
217 | SDMA_SDC_RFT | SDMA_SDC_SFM | SDMA_SDC_BLMR | SDMA_SDC_BLMT | | ||
218 | (3 << 12)); | ||
219 | |||
220 | cdv = ((mv64x60_mpsc_clk_freq/(32*mv64x60_console_baud))-1); | ||
221 | out_le32(mv64x60_base + brg_bcr, | ||
222 | ((mv64x60_mpsc_clk_src << 18) | (1 << 16) | cdv)); | ||
223 | |||
224 | /* Put MPSC into UART mode, no null modem, 16x clock mode */ | ||
225 | out_le32(mv64x60_base + MPSC_MMCRL + mpsc_base[chan], 0x000004c4); | ||
226 | out_le32(mv64x60_base + MPSC_MMCRH + mpsc_base[chan], 0x04400400); | ||
227 | |||
228 | out_le32(mv64x60_base + MPSC_CHR_1 + mpsc_base[chan], 0); | ||
229 | out_le32(mv64x60_base + MPSC_CHR_9 + mpsc_base[chan], 0); | ||
230 | out_le32(mv64x60_base + MPSC_CHR_10 + mpsc_base[chan], 0); | ||
231 | out_le32(mv64x60_base + MPSC_CHR_3 + mpsc_base[chan], 4); | ||
232 | out_le32(mv64x60_base + MPSC_CHR_4 + mpsc_base[chan], 0); | ||
233 | out_le32(mv64x60_base + MPSC_CHR_5 + mpsc_base[chan], 0); | ||
234 | out_le32(mv64x60_base + MPSC_CHR_6 + mpsc_base[chan], 0); | ||
235 | out_le32(mv64x60_base + MPSC_CHR_7 + mpsc_base[chan], 0); | ||
236 | out_le32(mv64x60_base + MPSC_CHR_8 + mpsc_base[chan], 0); | ||
237 | |||
238 | /* 8 data bits, 1 stop bit */ | ||
239 | out_le32(mv64x60_base + MPSC_MPCR + mpsc_base[chan], (3 << 12)); | ||
240 | out_le32(mv64x60_base + SDMA_SDCM + sdma_base, SDMA_SDCM_ERD); | ||
241 | out_le32(mv64x60_base + MPSC_CHR_2 + mpsc_base[chan], MPSC_CHR_2_EH); | ||
242 | |||
243 | udelay(100); | ||
244 | |||
245 | return chan; | ||
246 | } | ||
247 | |||
248 | static void | ||
249 | stop_dma(int chan) | ||
250 | { | ||
251 | int i; | ||
252 | |||
253 | /* Abort MPSC Rx (aborting Tx messes things up) */ | ||
254 | out_le32(mv64x60_base + MPSC_CHR_2 + mpsc_base[chan], MPSC_CHR_2_RA); | ||
255 | |||
256 | /* Abort SDMA Rx, Tx */ | ||
257 | out_le32(mv64x60_base + sdma_regs[chan].sdcm, | ||
258 | SDMA_SDCM_AR | SDMA_SDCM_STD); | ||
259 | |||
260 | for (i=0; i<MAX_RESET_WAIT; i++) { | ||
261 | if ((in_le32(mv64x60_base + sdma_regs[chan].sdcm) & | ||
262 | (SDMA_SDCM_AR | SDMA_SDCM_AT)) == 0) | ||
263 | break; | ||
264 | |||
265 | udelay(100); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | static int | ||
270 | wait_for_ownership(int chan) | ||
271 | { | ||
272 | int i; | ||
273 | |||
274 | for (i=0; i<MAX_TX_WAIT; i++) { | ||
275 | if ((in_le32(mv64x60_base + sdma_regs[chan].sdcm) & | ||
276 | SDMA_SDCM_TXD) == 0) | ||
277 | break; | ||
278 | |||
279 | udelay(1000); | ||
280 | } | ||
281 | |||
282 | return (i < MAX_TX_WAIT); | ||
283 | } | ||
284 | |||
285 | void | ||
286 | serial_putc(unsigned long com_port, unsigned char c) | ||
287 | { | ||
288 | struct mv64x60_tx_desc *tdp; | ||
289 | |||
290 | if (wait_for_ownership(com_port) == 0) | ||
291 | return; | ||
292 | |||
293 | tdp = &td[com_port][cur_td[com_port]]; | ||
294 | if (++cur_td[com_port] >= TX_NUM_DESC) | ||
295 | cur_td[com_port] = 0; | ||
296 | |||
297 | *(unchar *)(tdp->buffer ^ 7) = c; | ||
298 | tdp->bytecnt = 1; | ||
299 | tdp->shadow = 1; | ||
300 | tdp->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F | | ||
301 | SDMA_DESC_CMDSTAT_O; | ||
302 | |||
303 | out_le32(mv64x60_base + sdma_regs[com_port].sctdp, (int)tdp); | ||
304 | out_le32(mv64x60_base + sdma_regs[com_port].sftdp, (int)tdp); | ||
305 | out_le32(mv64x60_base + sdma_regs[com_port].sdcm, | ||
306 | in_le32(mv64x60_base + sdma_regs[com_port].sdcm) | | ||
307 | SDMA_SDCM_TXD); | ||
308 | } | ||
309 | |||
310 | unsigned char | ||
311 | serial_getc(unsigned long com_port) | ||
312 | { | ||
313 | struct mv64x60_rx_desc *rdp; | ||
314 | unchar c = '\0'; | ||
315 | |||
316 | rdp = &rd[com_port][cur_rd[com_port]]; | ||
317 | |||
318 | if ((rdp->cmd_stat & (SDMA_DESC_CMDSTAT_O|SDMA_DESC_CMDSTAT_ES)) == 0) { | ||
319 | c = *(unchar *)(rdp->buffer ^ 7); | ||
320 | RX_INIT_RDP(rdp); | ||
321 | if (++cur_rd[com_port] >= RX_NUM_DESC) | ||
322 | cur_rd[com_port] = 0; | ||
323 | } | ||
324 | |||
325 | return c; | ||
326 | } | ||
327 | |||
328 | int | ||
329 | serial_tstc(unsigned long com_port) | ||
330 | { | ||
331 | struct mv64x60_rx_desc *rdp; | ||
332 | int loop_count = 0; | ||
333 | int rc = 0; | ||
334 | |||
335 | rdp = &rd[com_port][cur_rd[com_port]]; | ||
336 | |||
337 | /* Go thru rcv desc's until empty looking for one with data (no error)*/ | ||
338 | while (((rdp->cmd_stat & SDMA_DESC_CMDSTAT_O) == 0) && | ||
339 | (loop_count++ < RX_NUM_DESC)) { | ||
340 | |||
341 | /* If there was an error, reinit the desc & continue */ | ||
342 | if ((rdp->cmd_stat & SDMA_DESC_CMDSTAT_ES) != 0) { | ||
343 | RX_INIT_RDP(rdp); | ||
344 | if (++cur_rd[com_port] >= RX_NUM_DESC) | ||
345 | cur_rd[com_port] = 0; | ||
346 | rdp = (struct mv64x60_rx_desc *)rdp->next_desc_ptr; | ||
347 | } else { | ||
348 | rc = 1; | ||
349 | break; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | return rc; | ||
354 | } | ||
355 | |||
356 | void | ||
357 | serial_close(unsigned long com_port) | ||
358 | { | ||
359 | stop_dma(com_port); | ||
360 | } | ||