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