Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/drivers/char/vme_scc.c
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 28988 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 28988 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports |
3 | * implementation. |
4 | * Copyright 1999 Richard Hirst <richard@sleepie.demon.co.uk> |
5 | * |
6 | * Based on atari_SCC.c which was |
7 | * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> |
8 | * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o |
9 | * |
10 | * This file is subject to the terms and conditions of the GNU General Public |
11 | * License. See the file COPYING in the main directory of this archive |
12 | * for more details. |
13 | * |
14 | */ |
15 | |
16 | #include <linux/module.h> |
17 | #include <linux/config.h> |
18 | #include <linux/kdev_t.h> |
19 | #include <asm/io.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/sched.h> |
22 | #include <linux/ioport.h> |
23 | #include <linux/interrupt.h> |
24 | #include <linux/errno.h> |
25 | #include <linux/tty.h> |
26 | #include <linux/tty_flip.h> |
27 | #include <linux/mm.h> |
28 | #include <linux/serial.h> |
29 | #include <linux/fcntl.h> |
30 | #include <linux/major.h> |
31 | #include <linux/delay.h> |
32 | #include <linux/slab.h> |
33 | #include <linux/miscdevice.h> |
34 | #include <linux/console.h> |
35 | #include <linux/init.h> |
36 | #include <asm/setup.h> |
37 | #include <asm/bootinfo.h> |
38 | |
39 | #ifdef CONFIG_MVME147_SCC |
40 | #include <asm/mvme147hw.h> |
41 | #endif |
42 | #ifdef CONFIG_MVME162_SCC |
43 | #include <asm/mvme16xhw.h> |
44 | #endif |
45 | #ifdef CONFIG_BVME6000_SCC |
46 | #include <asm/bvme6000hw.h> |
47 | #endif |
48 | |
49 | #include <linux/generic_serial.h> |
50 | #include "scc.h" |
51 | |
52 | |
53 | #define CHANNEL_A 0 |
54 | #define CHANNEL_B 1 |
55 | |
56 | #define SCC_MINOR_BASE 64 |
57 | |
58 | /* Shadows for all SCC write registers */ |
59 | static unsigned char scc_shadow[2][16]; |
60 | |
61 | /* Location to access for SCC register access delay */ |
62 | static volatile unsigned char *scc_del = NULL; |
63 | |
64 | /* To keep track of STATUS_REG state for detection of Ext/Status int source */ |
65 | static unsigned char scc_last_status_reg[2]; |
66 | |
67 | /***************************** Prototypes *****************************/ |
68 | |
69 | /* Function prototypes */ |
70 | static void scc_disable_tx_interrupts(void * ptr); |
71 | static void scc_enable_tx_interrupts(void * ptr); |
72 | static void scc_disable_rx_interrupts(void * ptr); |
73 | static void scc_enable_rx_interrupts(void * ptr); |
74 | static int scc_get_CD(void * ptr); |
75 | static void scc_shutdown_port(void * ptr); |
76 | static int scc_set_real_termios(void *ptr); |
77 | static void scc_hungup(void *ptr); |
78 | static void scc_close(void *ptr); |
79 | static int scc_chars_in_buffer(void * ptr); |
80 | static int scc_open(struct tty_struct * tty, struct file * filp); |
81 | static int scc_ioctl(struct tty_struct * tty, struct file * filp, |
82 | unsigned int cmd, unsigned long arg); |
83 | static void scc_throttle(struct tty_struct *tty); |
84 | static void scc_unthrottle(struct tty_struct *tty); |
85 | static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp); |
86 | static irqreturn_t scc_rx_int(int irq, void *data, struct pt_regs *fp); |
87 | static irqreturn_t scc_stat_int(int irq, void *data, struct pt_regs *fp); |
88 | static irqreturn_t scc_spcond_int(int irq, void *data, struct pt_regs *fp); |
89 | static void scc_setsignals(struct scc_port *port, int dtr, int rts); |
90 | static void scc_break_ctl(struct tty_struct *tty, int break_state); |
91 | |
92 | static struct tty_driver *scc_driver; |
93 | |
94 | struct scc_port scc_ports[2]; |
95 | |
96 | int scc_initialized = 0; |
97 | |
98 | /*--------------------------------------------------------------------------- |
99 | * Interface from generic_serial.c back here |
100 | *--------------------------------------------------------------------------*/ |
101 | |
102 | static struct real_driver scc_real_driver = { |
103 | scc_disable_tx_interrupts, |
104 | scc_enable_tx_interrupts, |
105 | scc_disable_rx_interrupts, |
106 | scc_enable_rx_interrupts, |
107 | scc_get_CD, |
108 | scc_shutdown_port, |
109 | scc_set_real_termios, |
110 | scc_chars_in_buffer, |
111 | scc_close, |
112 | scc_hungup, |
113 | NULL |
114 | }; |
115 | |
116 | |
117 | static struct tty_operations scc_ops = { |
118 | .open = scc_open, |
119 | .close = gs_close, |
120 | .write = gs_write, |
121 | .put_char = gs_put_char, |
122 | .flush_chars = gs_flush_chars, |
123 | .write_room = gs_write_room, |
124 | .chars_in_buffer = gs_chars_in_buffer, |
125 | .flush_buffer = gs_flush_buffer, |
126 | .ioctl = scc_ioctl, |
127 | .throttle = scc_throttle, |
128 | .unthrottle = scc_unthrottle, |
129 | .set_termios = gs_set_termios, |
130 | .stop = gs_stop, |
131 | .start = gs_start, |
132 | .hangup = gs_hangup, |
133 | .break_ctl = scc_break_ctl, |
134 | }; |
135 | |
136 | /*---------------------------------------------------------------------------- |
137 | * vme_scc_init() and support functions |
138 | *---------------------------------------------------------------------------*/ |
139 | |
140 | static int scc_init_drivers(void) |
141 | { |
142 | int error; |
143 | |
144 | scc_driver = alloc_tty_driver(2); |
145 | if (!scc_driver) |
146 | return -ENOMEM; |
147 | scc_driver->owner = THIS_MODULE; |
148 | scc_driver->driver_name = "scc"; |
149 | scc_driver->name = "ttyS"; |
150 | scc_driver->devfs_name = "tts/"; |
151 | scc_driver->major = TTY_MAJOR; |
152 | scc_driver->minor_start = SCC_MINOR_BASE; |
153 | scc_driver->type = TTY_DRIVER_TYPE_SERIAL; |
154 | scc_driver->subtype = SERIAL_TYPE_NORMAL; |
155 | scc_driver->init_termios = tty_std_termios; |
156 | scc_driver->init_termios.c_cflag = |
157 | B9600 | CS8 | CREAD | HUPCL | CLOCAL; |
158 | scc_driver->flags = TTY_DRIVER_REAL_RAW; |
159 | tty_set_operations(scc_driver, &scc_ops); |
160 | |
161 | if ((error = tty_register_driver(scc_driver))) { |
162 | printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n", |
163 | error); |
164 | put_tty_driver(scc_driver); |
165 | return 1; |
166 | } |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | |
172 | /* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1). |
173 | */ |
174 | |
175 | static void scc_init_portstructs(void) |
176 | { |
177 | struct scc_port *port; |
178 | int i; |
179 | |
180 | for (i = 0; i < 2; i++) { |
181 | port = scc_ports + i; |
182 | port->gs.magic = SCC_MAGIC; |
183 | port->gs.close_delay = HZ/2; |
184 | port->gs.closing_wait = 30 * HZ; |
185 | port->gs.rd = &scc_real_driver; |
186 | #ifdef NEW_WRITE_LOCKING |
187 | port->gs.port_write_sem = MUTEX; |
188 | #endif |
189 | init_waitqueue_head(&port->gs.open_wait); |
190 | init_waitqueue_head(&port->gs.close_wait); |
191 | } |
192 | } |
193 | |
194 | |
195 | #ifdef CONFIG_MVME147_SCC |
196 | static int mvme147_scc_init(void) |
197 | { |
198 | struct scc_port *port; |
199 | |
200 | printk(KERN_INFO "SCC: MVME147 Serial Driver\n"); |
201 | /* Init channel A */ |
202 | port = &scc_ports[0]; |
203 | port->channel = CHANNEL_A; |
204 | port->ctrlp = (volatile unsigned char *)M147_SCC_A_ADDR; |
205 | port->datap = port->ctrlp + 1; |
206 | port->port_a = &scc_ports[0]; |
207 | port->port_b = &scc_ports[1]; |
208 | request_irq(MVME147_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, |
209 | "SCC-A TX", port); |
210 | request_irq(MVME147_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, |
211 | "SCC-A status", port); |
212 | request_irq(MVME147_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, |
213 | "SCC-A RX", port); |
214 | request_irq(MVME147_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, |
215 | "SCC-A special cond", port); |
216 | { |
217 | SCC_ACCESS_INIT(port); |
218 | |
219 | /* disable interrupts for this channel */ |
220 | SCCwrite(INT_AND_DMA_REG, 0); |
221 | /* Set the interrupt vector */ |
222 | SCCwrite(INT_VECTOR_REG, MVME147_IRQ_SCC_BASE); |
223 | /* Interrupt parameters: vector includes status, status low */ |
224 | SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); |
225 | SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); |
226 | } |
227 | |
228 | /* Init channel B */ |
229 | port = &scc_ports[1]; |
230 | port->channel = CHANNEL_B; |
231 | port->ctrlp = (volatile unsigned char *)M147_SCC_B_ADDR; |
232 | port->datap = port->ctrlp + 1; |
233 | port->port_a = &scc_ports[0]; |
234 | port->port_b = &scc_ports[1]; |
235 | request_irq(MVME147_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, |
236 | "SCC-B TX", port); |
237 | request_irq(MVME147_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, |
238 | "SCC-B status", port); |
239 | request_irq(MVME147_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, |
240 | "SCC-B RX", port); |
241 | request_irq(MVME147_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, |
242 | "SCC-B special cond", port); |
243 | { |
244 | SCC_ACCESS_INIT(port); |
245 | |
246 | /* disable interrupts for this channel */ |
247 | SCCwrite(INT_AND_DMA_REG, 0); |
248 | } |
249 | |
250 | /* Ensure interrupts are enabled in the PCC chip */ |
251 | m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB; |
252 | |
253 | /* Initialise the tty driver structures and register */ |
254 | scc_init_portstructs(); |
255 | scc_init_drivers(); |
256 | |
257 | return 0; |
258 | } |
259 | #endif |
260 | |
261 | |
262 | #ifdef CONFIG_MVME162_SCC |
263 | static int mvme162_scc_init(void) |
264 | { |
265 | struct scc_port *port; |
266 | |
267 | if (!(mvme16x_config & MVME16x_CONFIG_GOT_SCCA)) |
268 | return (-ENODEV); |
269 | |
270 | printk(KERN_INFO "SCC: MVME162 Serial Driver\n"); |
271 | /* Init channel A */ |
272 | port = &scc_ports[0]; |
273 | port->channel = CHANNEL_A; |
274 | port->ctrlp = (volatile unsigned char *)MVME_SCC_A_ADDR; |
275 | port->datap = port->ctrlp + 2; |
276 | port->port_a = &scc_ports[0]; |
277 | port->port_b = &scc_ports[1]; |
278 | request_irq(MVME162_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, |
279 | "SCC-A TX", port); |
280 | request_irq(MVME162_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, |
281 | "SCC-A status", port); |
282 | request_irq(MVME162_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, |
283 | "SCC-A RX", port); |
284 | request_irq(MVME162_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, |
285 | "SCC-A special cond", port); |
286 | { |
287 | SCC_ACCESS_INIT(port); |
288 | |
289 | /* disable interrupts for this channel */ |
290 | SCCwrite(INT_AND_DMA_REG, 0); |
291 | /* Set the interrupt vector */ |
292 | SCCwrite(INT_VECTOR_REG, MVME162_IRQ_SCC_BASE); |
293 | /* Interrupt parameters: vector includes status, status low */ |
294 | SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); |
295 | SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); |
296 | } |
297 | |
298 | /* Init channel B */ |
299 | port = &scc_ports[1]; |
300 | port->channel = CHANNEL_B; |
301 | port->ctrlp = (volatile unsigned char *)MVME_SCC_B_ADDR; |
302 | port->datap = port->ctrlp + 2; |
303 | port->port_a = &scc_ports[0]; |
304 | port->port_b = &scc_ports[1]; |
305 | request_irq(MVME162_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, |
306 | "SCC-B TX", port); |
307 | request_irq(MVME162_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, |
308 | "SCC-B status", port); |
309 | request_irq(MVME162_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, |
310 | "SCC-B RX", port); |
311 | request_irq(MVME162_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, |
312 | "SCC-B special cond", port); |
313 | |
314 | { |
315 | SCC_ACCESS_INIT(port); /* Either channel will do */ |
316 | |
317 | /* disable interrupts for this channel */ |
318 | SCCwrite(INT_AND_DMA_REG, 0); |
319 | } |
320 | |
321 | /* Ensure interrupts are enabled in the MC2 chip */ |
322 | *(volatile char *)0xfff4201d = 0x14; |
323 | |
324 | /* Initialise the tty driver structures and register */ |
325 | scc_init_portstructs(); |
326 | scc_init_drivers(); |
327 | |
328 | return 0; |
329 | } |
330 | #endif |
331 | |
332 | |
333 | #ifdef CONFIG_BVME6000_SCC |
334 | static int bvme6000_scc_init(void) |
335 | { |
336 | struct scc_port *port; |
337 | |
338 | printk(KERN_INFO "SCC: BVME6000 Serial Driver\n"); |
339 | /* Init channel A */ |
340 | port = &scc_ports[0]; |
341 | port->channel = CHANNEL_A; |
342 | port->ctrlp = (volatile unsigned char *)BVME_SCC_A_ADDR; |
343 | port->datap = port->ctrlp + 4; |
344 | port->port_a = &scc_ports[0]; |
345 | port->port_b = &scc_ports[1]; |
346 | request_irq(BVME_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, |
347 | "SCC-A TX", port); |
348 | request_irq(BVME_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, |
349 | "SCC-A status", port); |
350 | request_irq(BVME_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, |
351 | "SCC-A RX", port); |
352 | request_irq(BVME_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, |
353 | "SCC-A special cond", port); |
354 | { |
355 | SCC_ACCESS_INIT(port); |
356 | |
357 | /* disable interrupts for this channel */ |
358 | SCCwrite(INT_AND_DMA_REG, 0); |
359 | /* Set the interrupt vector */ |
360 | SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE); |
361 | /* Interrupt parameters: vector includes status, status low */ |
362 | SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); |
363 | SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); |
364 | } |
365 | |
366 | /* Init channel B */ |
367 | port = &scc_ports[1]; |
368 | port->channel = CHANNEL_B; |
369 | port->ctrlp = (volatile unsigned char *)BVME_SCC_B_ADDR; |
370 | port->datap = port->ctrlp + 4; |
371 | port->port_a = &scc_ports[0]; |
372 | port->port_b = &scc_ports[1]; |
373 | request_irq(BVME_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, |
374 | "SCC-B TX", port); |
375 | request_irq(BVME_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, |
376 | "SCC-B status", port); |
377 | request_irq(BVME_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, |
378 | "SCC-B RX", port); |
379 | request_irq(BVME_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, |
380 | "SCC-B special cond", port); |
381 | |
382 | { |
383 | SCC_ACCESS_INIT(port); /* Either channel will do */ |
384 | |
385 | /* disable interrupts for this channel */ |
386 | SCCwrite(INT_AND_DMA_REG, 0); |
387 | } |
388 | |
389 | /* Initialise the tty driver structures and register */ |
390 | scc_init_portstructs(); |
391 | scc_init_drivers(); |
392 | |
393 | return 0; |
394 | } |
395 | #endif |
396 | |
397 | |
398 | static int vme_scc_init(void) |
399 | { |
400 | int res = -ENODEV; |
401 | |
402 | #ifdef CONFIG_MVME147_SCC |
403 | if (MACH_IS_MVME147) |
404 | res = mvme147_scc_init(); |
405 | #endif |
406 | #ifdef CONFIG_MVME162_SCC |
407 | if (MACH_IS_MVME16x) |
408 | res = mvme162_scc_init(); |
409 | #endif |
410 | #ifdef CONFIG_BVME6000_SCC |
411 | if (MACH_IS_BVME6000) |
412 | res = bvme6000_scc_init(); |
413 | #endif |
414 | return res; |
415 | } |
416 | |
417 | module_init(vme_scc_init); |
418 | |
419 | |
420 | /*--------------------------------------------------------------------------- |
421 | * Interrupt handlers |
422 | *--------------------------------------------------------------------------*/ |
423 | |
424 | static irqreturn_t scc_rx_int(int irq, void *data, struct pt_regs *fp) |
425 | { |
426 | unsigned char ch; |
427 | struct scc_port *port = data; |
428 | struct tty_struct *tty = port->gs.tty; |
429 | SCC_ACCESS_INIT(port); |
430 | |
431 | ch = SCCread_NB(RX_DATA_REG); |
432 | if (!tty) { |
433 | printk(KERN_WARNING "scc_rx_int with NULL tty!\n"); |
434 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
435 | return IRQ_HANDLED; |
436 | } |
437 | if (tty->flip.count < TTY_FLIPBUF_SIZE) { |
438 | *tty->flip.char_buf_ptr = ch; |
439 | *tty->flip.flag_buf_ptr = 0; |
440 | tty->flip.flag_buf_ptr++; |
441 | tty->flip.char_buf_ptr++; |
442 | tty->flip.count++; |
443 | } |
444 | |
445 | /* Check if another character is already ready; in that case, the |
446 | * spcond_int() function must be used, because this character may have an |
447 | * error condition that isn't signalled by the interrupt vector used! |
448 | */ |
449 | if (SCCread(INT_PENDING_REG) & |
450 | (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) { |
451 | scc_spcond_int (irq, data, fp); |
452 | return IRQ_HANDLED; |
453 | } |
454 | |
455 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
456 | |
457 | tty_flip_buffer_push(tty); |
458 | return IRQ_HANDLED; |
459 | } |
460 | |
461 | |
462 | static irqreturn_t scc_spcond_int(int irq, void *data, struct pt_regs *fp) |
463 | { |
464 | struct scc_port *port = data; |
465 | struct tty_struct *tty = port->gs.tty; |
466 | unsigned char stat, ch, err; |
467 | int int_pending_mask = port->channel == CHANNEL_A ? |
468 | IPR_A_RX : IPR_B_RX; |
469 | SCC_ACCESS_INIT(port); |
470 | |
471 | if (!tty) { |
472 | printk(KERN_WARNING "scc_spcond_int with NULL tty!\n"); |
473 | SCCwrite(COMMAND_REG, CR_ERROR_RESET); |
474 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
475 | return IRQ_HANDLED; |
476 | } |
477 | do { |
478 | stat = SCCread(SPCOND_STATUS_REG); |
479 | ch = SCCread_NB(RX_DATA_REG); |
480 | |
481 | if (stat & SCSR_RX_OVERRUN) |
482 | err = TTY_OVERRUN; |
483 | else if (stat & SCSR_PARITY_ERR) |
484 | err = TTY_PARITY; |
485 | else if (stat & SCSR_CRC_FRAME_ERR) |
486 | err = TTY_FRAME; |
487 | else |
488 | err = 0; |
489 | |
490 | if (tty->flip.count < TTY_FLIPBUF_SIZE) { |
491 | *tty->flip.char_buf_ptr = ch; |
492 | *tty->flip.flag_buf_ptr = err; |
493 | tty->flip.flag_buf_ptr++; |
494 | tty->flip.char_buf_ptr++; |
495 | tty->flip.count++; |
496 | } |
497 | |
498 | /* ++TeSche: *All* errors have to be cleared manually, |
499 | * else the condition persists for the next chars |
500 | */ |
501 | if (err) |
502 | SCCwrite(COMMAND_REG, CR_ERROR_RESET); |
503 | |
504 | } while(SCCread(INT_PENDING_REG) & int_pending_mask); |
505 | |
506 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
507 | |
508 | tty_flip_buffer_push(tty); |
509 | return IRQ_HANDLED; |
510 | } |
511 | |
512 | |
513 | static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp) |
514 | { |
515 | struct scc_port *port = data; |
516 | SCC_ACCESS_INIT(port); |
517 | |
518 | if (!port->gs.tty) { |
519 | printk(KERN_WARNING "scc_tx_int with NULL tty!\n"); |
520 | SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); |
521 | SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); |
522 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
523 | return IRQ_HANDLED; |
524 | } |
525 | while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) { |
526 | if (port->x_char) { |
527 | SCCwrite(TX_DATA_REG, port->x_char); |
528 | port->x_char = 0; |
529 | } |
530 | else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || |
531 | port->gs.tty->hw_stopped) |
532 | break; |
533 | else { |
534 | SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]); |
535 | port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1); |
536 | if (--port->gs.xmit_cnt <= 0) |
537 | break; |
538 | } |
539 | } |
540 | if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || |
541 | port->gs.tty->hw_stopped) { |
542 | /* disable tx interrupts */ |
543 | SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); |
544 | SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ |
545 | port->gs.flags &= ~GS_TX_INTEN; |
546 | } |
547 | if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) |
548 | tty_wakeup(port->gs.tty); |
549 | |
550 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
551 | return IRQ_HANDLED; |
552 | } |
553 | |
554 | |
555 | static irqreturn_t scc_stat_int(int irq, void *data, struct pt_regs *fp) |
556 | { |
557 | struct scc_port *port = data; |
558 | unsigned channel = port->channel; |
559 | unsigned char last_sr, sr, changed; |
560 | SCC_ACCESS_INIT(port); |
561 | |
562 | last_sr = scc_last_status_reg[channel]; |
563 | sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG); |
564 | changed = last_sr ^ sr; |
565 | |
566 | if (changed & SR_DCD) { |
567 | port->c_dcd = !!(sr & SR_DCD); |
568 | if (!(port->gs.flags & ASYNC_CHECK_CD)) |
569 | ; /* Don't report DCD changes */ |
570 | else if (port->c_dcd) { |
571 | wake_up_interruptible(&port->gs.open_wait); |
572 | } |
573 | else { |
574 | if (port->gs.tty) |
575 | tty_hangup (port->gs.tty); |
576 | } |
577 | } |
578 | SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET); |
579 | SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); |
580 | return IRQ_HANDLED; |
581 | } |
582 | |
583 | |
584 | /*--------------------------------------------------------------------------- |
585 | * generic_serial.c callback funtions |
586 | *--------------------------------------------------------------------------*/ |
587 | |
588 | static void scc_disable_tx_interrupts(void *ptr) |
589 | { |
590 | struct scc_port *port = ptr; |
591 | unsigned long flags; |
592 | SCC_ACCESS_INIT(port); |
593 | |
594 | local_irq_save(flags); |
595 | SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); |
596 | port->gs.flags &= ~GS_TX_INTEN; |
597 | local_irq_restore(flags); |
598 | } |
599 | |
600 | |
601 | static void scc_enable_tx_interrupts(void *ptr) |
602 | { |
603 | struct scc_port *port = ptr; |
604 | unsigned long flags; |
605 | SCC_ACCESS_INIT(port); |
606 | |
607 | local_irq_save(flags); |
608 | SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB); |
609 | /* restart the transmitter */ |
610 | scc_tx_int (0, port, 0); |
611 | local_irq_restore(flags); |
612 | } |
613 | |
614 | |
615 | static void scc_disable_rx_interrupts(void *ptr) |
616 | { |
617 | struct scc_port *port = ptr; |
618 | unsigned long flags; |
619 | SCC_ACCESS_INIT(port); |
620 | |
621 | local_irq_save(flags); |
622 | SCCmod(INT_AND_DMA_REG, |
623 | ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0); |
624 | local_irq_restore(flags); |
625 | } |
626 | |
627 | |
628 | static void scc_enable_rx_interrupts(void *ptr) |
629 | { |
630 | struct scc_port *port = ptr; |
631 | unsigned long flags; |
632 | SCC_ACCESS_INIT(port); |
633 | |
634 | local_irq_save(flags); |
635 | SCCmod(INT_AND_DMA_REG, 0xff, |
636 | IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL); |
637 | local_irq_restore(flags); |
638 | } |
639 | |
640 | |
641 | static int scc_get_CD(void *ptr) |
642 | { |
643 | struct scc_port *port = ptr; |
644 | unsigned channel = port->channel; |
645 | |
646 | return !!(scc_last_status_reg[channel] & SR_DCD); |
647 | } |
648 | |
649 | |
650 | static void scc_shutdown_port(void *ptr) |
651 | { |
652 | struct scc_port *port = ptr; |
653 | |
654 | port->gs.flags &= ~ GS_ACTIVE; |
655 | if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { |
656 | scc_setsignals (port, 0, 0); |
657 | } |
658 | } |
659 | |
660 | |
661 | static int scc_set_real_termios (void *ptr) |
662 | { |
663 | /* the SCC has char sizes 5,7,6,8 in that order! */ |
664 | static int chsize_map[4] = { 0, 2, 1, 3 }; |
665 | unsigned cflag, baud, chsize, channel, brgval = 0; |
666 | unsigned long flags; |
667 | struct scc_port *port = ptr; |
668 | SCC_ACCESS_INIT(port); |
669 | |
670 | if (!port->gs.tty || !port->gs.tty->termios) return 0; |
671 | |
672 | channel = port->channel; |
673 | |
674 | if (channel == CHANNEL_A) |
675 | return 0; /* Settings controlled by boot PROM */ |
676 | |
677 | cflag = port->gs.tty->termios->c_cflag; |
678 | baud = port->gs.baud; |
679 | chsize = (cflag & CSIZE) >> 4; |
680 | |
681 | if (baud == 0) { |
682 | /* speed == 0 -> drop DTR */ |
683 | local_irq_save(flags); |
684 | SCCmod(TX_CTRL_REG, ~TCR_DTR, 0); |
685 | local_irq_restore(flags); |
686 | return 0; |
687 | } |
688 | else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) || |
689 | (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) || |
690 | (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) { |
691 | printk(KERN_NOTICE "SCC: Bad speed requested, %d\n", baud); |
692 | return 0; |
693 | } |
694 | |
695 | if (cflag & CLOCAL) |
696 | port->gs.flags &= ~ASYNC_CHECK_CD; |
697 | else |
698 | port->gs.flags |= ASYNC_CHECK_CD; |
699 | |
700 | #ifdef CONFIG_MVME147_SCC |
701 | if (MACH_IS_MVME147) |
702 | brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; |
703 | #endif |
704 | #ifdef CONFIG_MVME162_SCC |
705 | if (MACH_IS_MVME16x) |
706 | brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; |
707 | #endif |
708 | #ifdef CONFIG_BVME6000_SCC |
709 | if (MACH_IS_BVME6000) |
710 | brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2; |
711 | #endif |
712 | /* Now we have all parameters and can go to set them: */ |
713 | local_irq_save(flags); |
714 | |
715 | /* receiver's character size and auto-enables */ |
716 | SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE), |
717 | (chsize_map[chsize] << 6) | |
718 | ((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0)); |
719 | /* parity and stop bits (both, Tx and Rx), clock mode never changes */ |
720 | SCCmod (AUX1_CTRL_REG, |
721 | ~(A1CR_PARITY_MASK | A1CR_MODE_MASK), |
722 | ((cflag & PARENB |
723 | ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN) |
724 | : A1CR_PARITY_NONE) |
725 | | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1))); |
726 | /* sender's character size, set DTR for valid baud rate */ |
727 | SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR); |
728 | /* clock sources never change */ |
729 | /* disable BRG before changing the value */ |
730 | SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0); |
731 | /* BRG value */ |
732 | SCCwrite(TIMER_LOW_REG, brgval & 0xff); |
733 | SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff); |
734 | /* BRG enable, and clock source never changes */ |
735 | SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB); |
736 | |
737 | local_irq_restore(flags); |
738 | |
739 | return 0; |
740 | } |
741 | |
742 | |
743 | static int scc_chars_in_buffer (void *ptr) |
744 | { |
745 | struct scc_port *port = ptr; |
746 | SCC_ACCESS_INIT(port); |
747 | |
748 | return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1; |
749 | } |
750 | |
751 | |
752 | /* Comment taken from sx.c (2.4.0): |
753 | I haven't the foggiest why the decrement use count has to happen |
754 | here. The whole linux serial drivers stuff needs to be redesigned. |
755 | My guess is that this is a hack to minimize the impact of a bug |
756 | elsewhere. Thinking about it some more. (try it sometime) Try |
757 | running minicom on a serial port that is driven by a modularized |
758 | driver. Have the modem hangup. Then remove the driver module. Then |
759 | exit minicom. I expect an "oops". -- REW */ |
760 | |
761 | static void scc_hungup(void *ptr) |
762 | { |
763 | scc_disable_tx_interrupts(ptr); |
764 | scc_disable_rx_interrupts(ptr); |
765 | } |
766 | |
767 | |
768 | static void scc_close(void *ptr) |
769 | { |
770 | scc_disable_tx_interrupts(ptr); |
771 | scc_disable_rx_interrupts(ptr); |
772 | } |
773 | |
774 | |
775 | /*--------------------------------------------------------------------------- |
776 | * Internal support functions |
777 | *--------------------------------------------------------------------------*/ |
778 | |
779 | static void scc_setsignals(struct scc_port *port, int dtr, int rts) |
780 | { |
781 | unsigned long flags; |
782 | unsigned char t; |
783 | SCC_ACCESS_INIT(port); |
784 | |
785 | local_irq_save(flags); |
786 | t = SCCread(TX_CTRL_REG); |
787 | if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR); |
788 | if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS); |
789 | SCCwrite(TX_CTRL_REG, t); |
790 | local_irq_restore(flags); |
791 | } |
792 | |
793 | |
794 | static void scc_send_xchar(struct tty_struct *tty, char ch) |
795 | { |
796 | struct scc_port *port = (struct scc_port *)tty->driver_data; |
797 | |
798 | port->x_char = ch; |
799 | if (ch) |
800 | scc_enable_tx_interrupts(port); |
801 | } |
802 | |
803 | |
804 | /*--------------------------------------------------------------------------- |
805 | * Driver entrypoints referenced from above |
806 | *--------------------------------------------------------------------------*/ |
807 | |
808 | static int scc_open (struct tty_struct * tty, struct file * filp) |
809 | { |
810 | int line = tty->index; |
811 | int retval; |
812 | struct scc_port *port = &scc_ports[line]; |
813 | int i, channel = port->channel; |
814 | unsigned long flags; |
815 | SCC_ACCESS_INIT(port); |
816 | #if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC) |
817 | static const struct { |
818 | unsigned reg, val; |
819 | } mvme_init_tab[] = { |
820 | /* Values for MVME162 and MVME147 */ |
821 | /* no parity, 1 stop bit, async, 1:16 */ |
822 | { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, |
823 | /* parity error is special cond, ints disabled, no DMA */ |
824 | { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, |
825 | /* Rx 8 bits/char, no auto enable, Rx off */ |
826 | { RX_CTRL_REG, RCR_CHSIZE_8 }, |
827 | /* DTR off, Tx 8 bits/char, RTS off, Tx off */ |
828 | { TX_CTRL_REG, TCR_CHSIZE_8 }, |
829 | /* special features off */ |
830 | { AUX2_CTRL_REG, 0 }, |
831 | { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, |
832 | { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, |
833 | /* Start Rx */ |
834 | { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, |
835 | /* Start Tx */ |
836 | { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, |
837 | /* Ext/Stat ints: DCD only */ |
838 | { INT_CTRL_REG, ICR_ENAB_DCD_INT }, |
839 | /* Reset Ext/Stat ints */ |
840 | { COMMAND_REG, CR_EXTSTAT_RESET }, |
841 | /* ...again */ |
842 | { COMMAND_REG, CR_EXTSTAT_RESET }, |
843 | }; |
844 | #endif |
845 | #if defined(CONFIG_BVME6000_SCC) |
846 | static const struct { |
847 | unsigned reg, val; |
848 | } bvme_init_tab[] = { |
849 | /* Values for BVME6000 */ |
850 | /* no parity, 1 stop bit, async, 1:16 */ |
851 | { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, |
852 | /* parity error is special cond, ints disabled, no DMA */ |
853 | { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, |
854 | /* Rx 8 bits/char, no auto enable, Rx off */ |
855 | { RX_CTRL_REG, RCR_CHSIZE_8 }, |
856 | /* DTR off, Tx 8 bits/char, RTS off, Tx off */ |
857 | { TX_CTRL_REG, TCR_CHSIZE_8 }, |
858 | /* special features off */ |
859 | { AUX2_CTRL_REG, 0 }, |
860 | { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG }, |
861 | { DPLL_CTRL_REG, DCR_BRG_ENAB }, |
862 | /* Start Rx */ |
863 | { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, |
864 | /* Start Tx */ |
865 | { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, |
866 | /* Ext/Stat ints: DCD only */ |
867 | { INT_CTRL_REG, ICR_ENAB_DCD_INT }, |
868 | /* Reset Ext/Stat ints */ |
869 | { COMMAND_REG, CR_EXTSTAT_RESET }, |
870 | /* ...again */ |
871 | { COMMAND_REG, CR_EXTSTAT_RESET }, |
872 | }; |
873 | #endif |
874 | if (!(port->gs.flags & ASYNC_INITIALIZED)) { |
875 | local_irq_save(flags); |
876 | #if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC) |
877 | if (MACH_IS_MVME147 || MACH_IS_MVME16x) { |
878 | for (i=0; i<sizeof(mvme_init_tab)/sizeof(*mvme_init_tab); ++i) |
879 | SCCwrite(mvme_init_tab[i].reg, mvme_init_tab[i].val); |
880 | } |
881 | #endif |
882 | #if defined(CONFIG_BVME6000_SCC) |
883 | if (MACH_IS_BVME6000) { |
884 | for (i=0; i<sizeof(bvme_init_tab)/sizeof(*bvme_init_tab); ++i) |
885 | SCCwrite(bvme_init_tab[i].reg, bvme_init_tab[i].val); |
886 | } |
887 | #endif |
888 | |
889 | /* remember status register for detection of DCD and CTS changes */ |
890 | scc_last_status_reg[channel] = SCCread(STATUS_REG); |
891 | |
892 | port->c_dcd = 0; /* Prevent initial 1->0 interrupt */ |
893 | scc_setsignals (port, 1,1); |
894 | local_irq_restore(flags); |
895 | } |
896 | |
897 | tty->driver_data = port; |
898 | port->gs.tty = tty; |
899 | port->gs.count++; |
900 | retval = gs_init_port(&port->gs); |
901 | if (retval) { |
902 | port->gs.count--; |
903 | return retval; |
904 | } |
905 | port->gs.flags |= GS_ACTIVE; |
906 | retval = gs_block_til_ready(port, filp); |
907 | |
908 | if (retval) { |
909 | port->gs.count--; |
910 | return retval; |
911 | } |
912 | |
913 | port->c_dcd = scc_get_CD (port); |
914 | |
915 | scc_enable_rx_interrupts(port); |
916 | |
917 | return 0; |
918 | } |
919 | |
920 | |
921 | static void scc_throttle (struct tty_struct * tty) |
922 | { |
923 | struct scc_port *port = (struct scc_port *)tty->driver_data; |
924 | unsigned long flags; |
925 | SCC_ACCESS_INIT(port); |
926 | |
927 | if (tty->termios->c_cflag & CRTSCTS) { |
928 | local_irq_save(flags); |
929 | SCCmod(TX_CTRL_REG, ~TCR_RTS, 0); |
930 | local_irq_restore(flags); |
931 | } |
932 | if (I_IXOFF(tty)) |
933 | scc_send_xchar(tty, STOP_CHAR(tty)); |
934 | } |
935 | |
936 | |
937 | static void scc_unthrottle (struct tty_struct * tty) |
938 | { |
939 | struct scc_port *port = (struct scc_port *)tty->driver_data; |
940 | unsigned long flags; |
941 | SCC_ACCESS_INIT(port); |
942 | |
943 | if (tty->termios->c_cflag & CRTSCTS) { |
944 | local_irq_save(flags); |
945 | SCCmod(TX_CTRL_REG, 0xff, TCR_RTS); |
946 | local_irq_restore(flags); |
947 | } |
948 | if (I_IXOFF(tty)) |
949 | scc_send_xchar(tty, START_CHAR(tty)); |
950 | } |
951 | |
952 | |
953 | static int scc_ioctl(struct tty_struct *tty, struct file *file, |
954 | unsigned int cmd, unsigned long arg) |
955 | { |
956 | return -ENOIOCTLCMD; |
957 | } |
958 | |
959 | |
960 | static void scc_break_ctl(struct tty_struct *tty, int break_state) |
961 | { |
962 | struct scc_port *port = (struct scc_port *)tty->driver_data; |
963 | unsigned long flags; |
964 | SCC_ACCESS_INIT(port); |
965 | |
966 | local_irq_save(flags); |
967 | SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, |
968 | break_state ? TCR_SEND_BREAK : 0); |
969 | local_irq_restore(flags); |
970 | } |
971 | |
972 | |
973 | /*--------------------------------------------------------------------------- |
974 | * Serial console stuff... |
975 | *--------------------------------------------------------------------------*/ |
976 | |
977 | #define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0) |
978 | |
979 | static void scc_ch_write (char ch) |
980 | { |
981 | volatile char *p = NULL; |
982 | |
983 | #ifdef CONFIG_MVME147_SCC |
984 | if (MACH_IS_MVME147) |
985 | p = (volatile char *)M147_SCC_A_ADDR; |
986 | #endif |
987 | #ifdef CONFIG_MVME162_SCC |
988 | if (MACH_IS_MVME16x) |
989 | p = (volatile char *)MVME_SCC_A_ADDR; |
990 | #endif |
991 | #ifdef CONFIG_BVME6000_SCC |
992 | if (MACH_IS_BVME6000) |
993 | p = (volatile char *)BVME_SCC_A_ADDR; |
994 | #endif |
995 | |
996 | do { |
997 | scc_delay(); |
998 | } |
999 | while (!(*p & 4)); |
1000 | scc_delay(); |
1001 | *p = 8; |
1002 | scc_delay(); |
1003 | *p = ch; |
1004 | } |
1005 | |
1006 | /* The console must be locked when we get here. */ |
1007 | |
1008 | static void scc_console_write (struct console *co, const char *str, unsigned count) |
1009 | { |
1010 | unsigned long flags; |
1011 | |
1012 | local_irq_save(flags); |
1013 | |
1014 | while (count--) |
1015 | { |
1016 | if (*str == '\n') |
1017 | scc_ch_write ('\r'); |
1018 | scc_ch_write (*str++); |
1019 | } |
1020 | local_irq_restore(flags); |
1021 | } |
1022 | |
1023 | static struct tty_driver *scc_console_device(struct console *c, int *index) |
1024 | { |
1025 | *index = c->index; |
1026 | return scc_driver; |
1027 | } |
1028 | |
1029 | |
1030 | static int __init scc_console_setup(struct console *co, char *options) |
1031 | { |
1032 | return 0; |
1033 | } |
1034 | |
1035 | |
1036 | static struct console sercons = { |
1037 | .name = "ttyS", |
1038 | .write = scc_console_write, |
1039 | .device = scc_console_device, |
1040 | .setup = scc_console_setup, |
1041 | .flags = CON_PRINTBUFFER, |
1042 | .index = -1, |
1043 | }; |
1044 | |
1045 | |
1046 | static int __init vme_scc_console_init(void) |
1047 | { |
1048 | if (vme_brdtype == VME_TYPE_MVME147 || |
1049 | vme_brdtype == VME_TYPE_MVME162 || |
1050 | vme_brdtype == VME_TYPE_MVME172 || |
1051 | vme_brdtype == VME_TYPE_BVME4000 || |
1052 | vme_brdtype == VME_TYPE_BVME6000) |
1053 | register_console(&sercons); |
1054 | return 0; |
1055 | } |
1056 | console_initcall(vme_scc_console_init); |