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 /drivers/char/vme_scc.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 'drivers/char/vme_scc.c')
-rw-r--r-- | drivers/char/vme_scc.c | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c new file mode 100644 index 000000000000..19ba83635dd7 --- /dev/null +++ b/drivers/char/vme_scc.c | |||
@@ -0,0 +1,1056 @@ | |||
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); | ||