Commit | Line | Data |
---|---|---|
0b99d589 LL |
1 | /* |
2 | * Copyright 2003 Digi International (www.digi.com) | |
3 | * Scott H Kilau <Scott_Kilau at digi dot com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2, or (at your option) | |
8 | * any later version. | |
5ca46fd4 | 9 | * |
0b99d589 | 10 | * This program is distributed in the hope that it will be useful, |
5ca46fd4 LL |
11 | * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the |
12 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
0b99d589 | 13 | * PURPOSE. See the GNU General Public License for more details. |
0b99d589 LL |
14 | */ |
15 | ||
16 | #include <linux/kernel.h> | |
0b99d589 LL |
17 | #include <linux/sched.h> /* For jiffies, task states */ |
18 | #include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */ | |
19 | #include <linux/delay.h> /* For udelay */ | |
4a75ffa8 | 20 | #include <linux/io.h> /* For read[bwl]/write[bwl] */ |
0b99d589 LL |
21 | #include <linux/serial.h> /* For struct async_serial */ |
22 | #include <linux/serial_reg.h> /* For the various UART offsets */ | |
23 | #include <linux/pci.h> | |
24 | ||
25 | #include "dgnc_driver.h" /* Driver main header file */ | |
26 | #include "dgnc_cls.h" | |
27 | #include "dgnc_tty.h" | |
0b99d589 | 28 | |
0b99d589 LL |
29 | static inline void cls_set_cts_flow_control(struct channel_t *ch) |
30 | { | |
446393e9 EA |
31 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
32 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
33 | unsigned char isr_fcr = 0; | |
0b99d589 | 34 | |
0b99d589 LL |
35 | /* |
36 | * The Enhanced Register Set may only be accessed when | |
37 | * the Line Control Register is set to 0xBFh. | |
38 | */ | |
39 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
40 | ||
41 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 42 | |
0b99d589 LL |
43 | /* Turn on CTS flow control, turn off IXON flow control */ |
44 | isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR); | |
45 | isr_fcr &= ~(UART_EXAR654_EFR_IXON); | |
46 | ||
47 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
48 | ||
49 | /* Write old LCR value back out, which turns enhanced access off */ | |
50 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
51 | ||
cb3714a6 | 52 | /* |
53 | * Enable interrupts for CTS flow, turn off interrupts for | |
54 | * received XOFF chars | |
55 | */ | |
0b99d589 LL |
56 | ier |= (UART_EXAR654_IER_CTSDSR); |
57 | ier &= ~(UART_EXAR654_IER_XOFF); | |
58 | writeb(ier, &ch->ch_cls_uart->ier); | |
59 | ||
60 | /* Set the usual FIFO values */ | |
61 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
62 | ||
63 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | | |
64 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
65 | &ch->ch_cls_uart->isr_fcr); | |
66 | ||
67 | ch->ch_t_tlevel = 16; | |
0b99d589 LL |
68 | } |
69 | ||
0b99d589 LL |
70 | static inline void cls_set_ixon_flow_control(struct channel_t *ch) |
71 | { | |
446393e9 EA |
72 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
73 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
74 | unsigned char isr_fcr = 0; | |
0b99d589 | 75 | |
0b99d589 LL |
76 | /* |
77 | * The Enhanced Register Set may only be accessed when | |
78 | * the Line Control Register is set to 0xBFh. | |
79 | */ | |
80 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
81 | ||
82 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 83 | |
0b99d589 LL |
84 | /* Turn on IXON flow control, turn off CTS flow control */ |
85 | isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON); | |
86 | isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR); | |
87 | ||
88 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
89 | ||
90 | /* Now set our current start/stop chars while in enhanced mode */ | |
91 | writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); | |
92 | writeb(0, &ch->ch_cls_uart->lsr); | |
93 | writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); | |
94 | writeb(0, &ch->ch_cls_uart->spr); | |
95 | ||
96 | /* Write old LCR value back out, which turns enhanced access off */ | |
97 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
98 | ||
cb3714a6 | 99 | /* |
100 | * Disable interrupts for CTS flow, turn on interrupts for | |
101 | * received XOFF chars | |
102 | */ | |
0b99d589 LL |
103 | ier &= ~(UART_EXAR654_IER_CTSDSR); |
104 | ier |= (UART_EXAR654_IER_XOFF); | |
105 | writeb(ier, &ch->ch_cls_uart->ier); | |
106 | ||
107 | /* Set the usual FIFO values */ | |
108 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
109 | ||
110 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | | |
111 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
112 | &ch->ch_cls_uart->isr_fcr); | |
0b99d589 LL |
113 | } |
114 | ||
0b99d589 LL |
115 | static inline void cls_set_no_output_flow_control(struct channel_t *ch) |
116 | { | |
446393e9 EA |
117 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
118 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
119 | unsigned char isr_fcr = 0; | |
0b99d589 | 120 | |
0b99d589 LL |
121 | /* |
122 | * The Enhanced Register Set may only be accessed when | |
123 | * the Line Control Register is set to 0xBFh. | |
124 | */ | |
125 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
126 | ||
127 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 128 | |
0b99d589 LL |
129 | /* Turn off IXON flow control, turn off CTS flow control */ |
130 | isr_fcr |= (UART_EXAR654_EFR_ECB); | |
131 | isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON); | |
132 | ||
133 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
134 | ||
135 | /* Write old LCR value back out, which turns enhanced access off */ | |
136 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
137 | ||
cb3714a6 | 138 | /* |
139 | * Disable interrupts for CTS flow, turn off interrupts for | |
140 | * received XOFF chars | |
141 | */ | |
0b99d589 LL |
142 | ier &= ~(UART_EXAR654_IER_CTSDSR); |
143 | ier &= ~(UART_EXAR654_IER_XOFF); | |
144 | writeb(ier, &ch->ch_cls_uart->ier); | |
145 | ||
146 | /* Set the usual FIFO values */ | |
147 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
148 | ||
149 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | | |
150 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
151 | &ch->ch_cls_uart->isr_fcr); | |
152 | ||
153 | ch->ch_r_watermark = 0; | |
cb3714a6 | 154 | ch->ch_t_tlevel = 16; |
155 | ch->ch_r_tlevel = 16; | |
0b99d589 LL |
156 | } |
157 | ||
0b99d589 LL |
158 | static inline void cls_set_rts_flow_control(struct channel_t *ch) |
159 | { | |
446393e9 EA |
160 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
161 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
162 | unsigned char isr_fcr = 0; | |
0b99d589 | 163 | |
0b99d589 LL |
164 | /* |
165 | * The Enhanced Register Set may only be accessed when | |
166 | * the Line Control Register is set to 0xBFh. | |
167 | */ | |
168 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
169 | ||
170 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 171 | |
0b99d589 LL |
172 | /* Turn on RTS flow control, turn off IXOFF flow control */ |
173 | isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR); | |
174 | isr_fcr &= ~(UART_EXAR654_EFR_IXOFF); | |
175 | ||
176 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
177 | ||
178 | /* Write old LCR value back out, which turns enhanced access off */ | |
179 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
180 | ||
181 | /* Enable interrupts for RTS flow */ | |
182 | ier |= (UART_EXAR654_IER_RTSDTR); | |
183 | writeb(ier, &ch->ch_cls_uart->ier); | |
184 | ||
185 | /* Set the usual FIFO values */ | |
186 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
187 | ||
188 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 | | |
189 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
190 | &ch->ch_cls_uart->isr_fcr); | |
191 | ||
0b99d589 LL |
192 | ch->ch_r_watermark = 4; |
193 | ch->ch_r_tlevel = 8; | |
0b99d589 LL |
194 | } |
195 | ||
0b99d589 LL |
196 | static inline void cls_set_ixoff_flow_control(struct channel_t *ch) |
197 | { | |
446393e9 EA |
198 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
199 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
200 | unsigned char isr_fcr = 0; | |
0b99d589 | 201 | |
0b99d589 LL |
202 | /* |
203 | * The Enhanced Register Set may only be accessed when | |
204 | * the Line Control Register is set to 0xBFh. | |
205 | */ | |
206 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
207 | ||
208 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 209 | |
0b99d589 LL |
210 | /* Turn on IXOFF flow control, turn off RTS flow control */ |
211 | isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF); | |
212 | isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR); | |
213 | ||
214 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
215 | ||
216 | /* Now set our current start/stop chars while in enhanced mode */ | |
217 | writeb(ch->ch_startc, &ch->ch_cls_uart->mcr); | |
218 | writeb(0, &ch->ch_cls_uart->lsr); | |
219 | writeb(ch->ch_stopc, &ch->ch_cls_uart->msr); | |
220 | writeb(0, &ch->ch_cls_uart->spr); | |
221 | ||
222 | /* Write old LCR value back out, which turns enhanced access off */ | |
223 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
224 | ||
225 | /* Disable interrupts for RTS flow */ | |
226 | ier &= ~(UART_EXAR654_IER_RTSDTR); | |
227 | writeb(ier, &ch->ch_cls_uart->ier); | |
228 | ||
229 | /* Set the usual FIFO values */ | |
230 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
231 | ||
232 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | | |
233 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
234 | &ch->ch_cls_uart->isr_fcr); | |
0b99d589 LL |
235 | } |
236 | ||
0b99d589 LL |
237 | static inline void cls_set_no_input_flow_control(struct channel_t *ch) |
238 | { | |
446393e9 EA |
239 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
240 | unsigned char ier = readb(&ch->ch_cls_uart->ier); | |
241 | unsigned char isr_fcr = 0; | |
0b99d589 | 242 | |
0b99d589 LL |
243 | /* |
244 | * The Enhanced Register Set may only be accessed when | |
245 | * the Line Control Register is set to 0xBFh. | |
246 | */ | |
247 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
248 | ||
249 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 250 | |
0b99d589 LL |
251 | /* Turn off IXOFF flow control, turn off RTS flow control */ |
252 | isr_fcr |= (UART_EXAR654_EFR_ECB); | |
253 | isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF); | |
254 | ||
255 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
256 | ||
257 | /* Write old LCR value back out, which turns enhanced access off */ | |
258 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
259 | ||
260 | /* Disable interrupts for RTS flow */ | |
261 | ier &= ~(UART_EXAR654_IER_RTSDTR); | |
262 | writeb(ier, &ch->ch_cls_uart->ier); | |
263 | ||
264 | /* Set the usual FIFO values */ | |
265 | writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr); | |
266 | ||
267 | writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 | | |
268 | UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR), | |
269 | &ch->ch_cls_uart->isr_fcr); | |
270 | ||
cb3714a6 | 271 | ch->ch_t_tlevel = 16; |
272 | ch->ch_r_tlevel = 16; | |
0b99d589 LL |
273 | } |
274 | ||
0b99d589 LL |
275 | /* |
276 | * cls_clear_break. | |
277 | * Determines whether its time to shut off break condition. | |
278 | * | |
279 | * No locks are assumed to be held when calling this function. | |
280 | * channel lock is held and released in this function. | |
281 | */ | |
282 | static inline void cls_clear_break(struct channel_t *ch, int force) | |
283 | { | |
707505b5 | 284 | unsigned long flags; |
0b99d589 LL |
285 | |
286 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
287 | return; | |
288 | ||
707505b5 | 289 | spin_lock_irqsave(&ch->ch_lock, flags); |
0b99d589 LL |
290 | |
291 | /* Bail if we aren't currently sending a break. */ | |
292 | if (!ch->ch_stop_sending_break) { | |
707505b5 | 293 | spin_unlock_irqrestore(&ch->ch_lock, flags); |
0b99d589 LL |
294 | return; |
295 | } | |
296 | ||
297 | /* Turn break off, and unset some variables */ | |
298 | if (ch->ch_flags & CH_BREAK_SENDING) { | |
f0dcc9fa | 299 | if (time_after(jiffies, ch->ch_stop_sending_break) || force) { |
446393e9 | 300 | unsigned char temp = readb(&ch->ch_cls_uart->lcr); |
8aa5d0d8 | 301 | |
cb3714a6 | 302 | writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr); |
0b99d589 LL |
303 | ch->ch_flags &= ~(CH_BREAK_SENDING); |
304 | ch->ch_stop_sending_break = 0; | |
0b99d589 LL |
305 | } |
306 | } | |
707505b5 | 307 | spin_unlock_irqrestore(&ch->ch_lock, flags); |
0b99d589 LL |
308 | } |
309 | ||
a2237a2d | 310 | static void cls_copy_data_from_uart_to_queue(struct channel_t *ch) |
0b99d589 | 311 | { |
a2237a2d DY |
312 | int qleft = 0; |
313 | unsigned char linestatus = 0; | |
314 | unsigned char error_mask = 0; | |
315 | ushort head; | |
316 | ushort tail; | |
707505b5 | 317 | unsigned long flags; |
0b99d589 | 318 | |
a2237a2d | 319 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) |
0b99d589 LL |
320 | return; |
321 | ||
a2237a2d DY |
322 | spin_lock_irqsave(&ch->ch_lock, flags); |
323 | ||
324 | /* cache head and tail of queue */ | |
325 | head = ch->ch_r_head; | |
326 | tail = ch->ch_r_tail; | |
327 | ||
328 | /* Store how much space we have left in the queue */ | |
329 | qleft = tail - head - 1; | |
330 | if (qleft < 0) | |
331 | qleft += RQUEUEMASK + 1; | |
332 | ||
333 | /* | |
334 | * Create a mask to determine whether we should | |
335 | * insert the character (if any) into our queue. | |
336 | */ | |
337 | if (ch->ch_c_iflag & IGNBRK) | |
338 | error_mask |= UART_LSR_BI; | |
0b99d589 | 339 | |
0b99d589 | 340 | while (1) { |
a2237a2d | 341 | linestatus = readb(&ch->ch_cls_uart->lsr); |
0b99d589 | 342 | |
a2237a2d | 343 | if (!(linestatus & (UART_LSR_DR))) |
0b99d589 | 344 | break; |
0b99d589 | 345 | |
a2237a2d DY |
346 | /* |
347 | * Discard character if we are ignoring the error mask. | |
348 | */ | |
349 | if (linestatus & error_mask) { | |
350 | linestatus = 0; | |
351 | readb(&ch->ch_cls_uart->txrx); | |
352 | continue; | |
0b99d589 LL |
353 | } |
354 | ||
a2237a2d DY |
355 | /* |
356 | * If our queue is full, we have no choice but to drop some | |
357 | * data. The assumption is that HWFLOW or SWFLOW should have | |
358 | * stopped things way way before we got to this point. | |
359 | * | |
360 | * I decided that I wanted to ditch the oldest data first, | |
361 | * I hope thats okay with everyone? Yes? Good. | |
362 | */ | |
363 | while (qleft < 1) { | |
364 | tail = (tail + 1) & RQUEUEMASK; | |
365 | ch->ch_r_tail = tail; | |
366 | ch->ch_err_overrun++; | |
367 | qleft++; | |
0b99d589 LL |
368 | } |
369 | ||
a2237a2d DY |
370 | ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE |
371 | | UART_LSR_FE); | |
372 | ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx); | |
0b99d589 | 373 | |
a2237a2d DY |
374 | qleft--; |
375 | ||
376 | if (ch->ch_equeue[head] & UART_LSR_PE) | |
377 | ch->ch_err_parity++; | |
378 | if (ch->ch_equeue[head] & UART_LSR_BI) | |
379 | ch->ch_err_break++; | |
380 | if (ch->ch_equeue[head] & UART_LSR_FE) | |
381 | ch->ch_err_frame++; | |
382 | ||
383 | /* Add to, and flip head if needed */ | |
384 | head = (head + 1) & RQUEUEMASK; | |
385 | ch->ch_rxcount++; | |
0b99d589 | 386 | } |
a2237a2d DY |
387 | |
388 | /* | |
389 | * Write new final heads to channel structure. | |
390 | */ | |
391 | ch->ch_r_head = head & RQUEUEMASK; | |
392 | ch->ch_e_head = head & EQUEUEMASK; | |
393 | ||
394 | spin_unlock_irqrestore(&ch->ch_lock, flags); | |
0b99d589 LL |
395 | } |
396 | ||
a2237a2d DY |
397 | /* Make the UART raise any of the output signals we want up */ |
398 | static void cls_assert_modem_signals(struct channel_t *ch) | |
0b99d589 | 399 | { |
a2237a2d | 400 | unsigned char out; |
0b99d589 | 401 | |
a2237a2d | 402 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) |
0b99d589 | 403 | return; |
0b99d589 | 404 | |
a2237a2d DY |
405 | out = ch->ch_mostat; |
406 | ||
407 | if (ch->ch_flags & CH_LOOPBACK) | |
408 | out |= UART_MCR_LOOP; | |
409 | ||
410 | writeb(out, &ch->ch_cls_uart->mcr); | |
411 | ||
412 | /* Give time for the UART to actually drop the signals */ | |
413 | udelay(10); | |
414 | } | |
415 | ||
416 | static void cls_copy_data_from_queue_to_uart(struct channel_t *ch) | |
417 | { | |
418 | ushort head; | |
419 | ushort tail; | |
420 | int n; | |
421 | int qlen; | |
422 | uint len_written = 0; | |
423 | unsigned long flags; | |
0b99d589 | 424 | |
3d47910b | 425 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) |
0b99d589 | 426 | return; |
0b99d589 | 427 | |
a2237a2d | 428 | spin_lock_irqsave(&ch->ch_lock, flags); |
0b99d589 | 429 | |
a2237a2d DY |
430 | /* No data to write to the UART */ |
431 | if (ch->ch_w_tail == ch->ch_w_head) | |
432 | goto exit_unlock; | |
0b99d589 | 433 | |
a2237a2d DY |
434 | /* If port is "stopped", don't send any data to the UART */ |
435 | if ((ch->ch_flags & CH_FORCED_STOP) || | |
436 | (ch->ch_flags & CH_BREAK_SENDING)) | |
437 | goto exit_unlock; | |
0b99d589 | 438 | |
a2237a2d DY |
439 | if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) |
440 | goto exit_unlock; | |
0b99d589 | 441 | |
a2237a2d | 442 | n = 32; |
0b99d589 | 443 | |
a2237a2d DY |
444 | /* cache head and tail of queue */ |
445 | head = ch->ch_w_head & WQUEUEMASK; | |
446 | tail = ch->ch_w_tail & WQUEUEMASK; | |
447 | qlen = (head - tail) & WQUEUEMASK; | |
0b99d589 | 448 | |
a2237a2d DY |
449 | /* Find minimum of the FIFO space, versus queue length */ |
450 | n = min(n, qlen); | |
0b99d589 | 451 | |
a2237a2d | 452 | while (n > 0) { |
cb3714a6 | 453 | /* |
a2237a2d DY |
454 | * If RTS Toggle mode is on, turn on RTS now if not already set, |
455 | * and make sure we get an event when the data transfer has | |
456 | * completed. | |
457 | */ | |
458 | if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) { | |
459 | if (!(ch->ch_mostat & UART_MCR_RTS)) { | |
460 | ch->ch_mostat |= (UART_MCR_RTS); | |
461 | cls_assert_modem_signals(ch); | |
462 | } | |
463 | ch->ch_tun.un_flags |= (UN_EMPTY); | |
464 | } | |
465 | ||
466 | /* | |
467 | * If DTR Toggle mode is on, turn on DTR now if not already set, | |
468 | * and make sure we get an event when the data transfer has | |
469 | * completed. | |
470 | */ | |
471 | if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) { | |
472 | if (!(ch->ch_mostat & UART_MCR_DTR)) { | |
473 | ch->ch_mostat |= (UART_MCR_DTR); | |
474 | cls_assert_modem_signals(ch); | |
475 | } | |
476 | ch->ch_tun.un_flags |= (UN_EMPTY); | |
477 | } | |
478 | writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx); | |
479 | ch->ch_w_tail++; | |
480 | ch->ch_w_tail &= WQUEUEMASK; | |
481 | ch->ch_txcount++; | |
482 | len_written++; | |
483 | n--; | |
484 | } | |
485 | ||
486 | if (len_written > 0) | |
487 | ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); | |
488 | ||
489 | exit_unlock: | |
490 | spin_unlock_irqrestore(&ch->ch_lock, flags); | |
491 | } | |
492 | ||
493 | static void cls_parse_modem(struct channel_t *ch, unsigned char signals) | |
494 | { | |
495 | unsigned char msignals = signals; | |
496 | unsigned long flags; | |
497 | ||
498 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
499 | return; | |
500 | ||
501 | /* | |
502 | * Do altpin switching. Altpin switches DCD and DSR. | |
503 | * This prolly breaks DSRPACE, so we should be more clever here. | |
504 | */ | |
505 | spin_lock_irqsave(&ch->ch_lock, flags); | |
506 | if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { | |
507 | unsigned char mswap = signals; | |
508 | ||
509 | if (mswap & UART_MSR_DDCD) { | |
510 | msignals &= ~UART_MSR_DDCD; | |
511 | msignals |= UART_MSR_DDSR; | |
512 | } | |
513 | if (mswap & UART_MSR_DDSR) { | |
514 | msignals &= ~UART_MSR_DDSR; | |
515 | msignals |= UART_MSR_DDCD; | |
516 | } | |
517 | if (mswap & UART_MSR_DCD) { | |
518 | msignals &= ~UART_MSR_DCD; | |
519 | msignals |= UART_MSR_DSR; | |
520 | } | |
521 | if (mswap & UART_MSR_DSR) { | |
522 | msignals &= ~UART_MSR_DSR; | |
523 | msignals |= UART_MSR_DCD; | |
524 | } | |
525 | } | |
526 | spin_unlock_irqrestore(&ch->ch_lock, flags); | |
527 | ||
528 | /* | |
529 | * Scrub off lower bits. They signify delta's, which I don't | |
530 | * care about | |
531 | */ | |
532 | signals &= 0xf0; | |
533 | ||
534 | spin_lock_irqsave(&ch->ch_lock, flags); | |
535 | if (msignals & UART_MSR_DCD) | |
536 | ch->ch_mistat |= UART_MSR_DCD; | |
537 | else | |
538 | ch->ch_mistat &= ~UART_MSR_DCD; | |
539 | ||
540 | if (msignals & UART_MSR_DSR) | |
541 | ch->ch_mistat |= UART_MSR_DSR; | |
542 | else | |
543 | ch->ch_mistat &= ~UART_MSR_DSR; | |
544 | ||
545 | if (msignals & UART_MSR_RI) | |
546 | ch->ch_mistat |= UART_MSR_RI; | |
547 | else | |
548 | ch->ch_mistat &= ~UART_MSR_RI; | |
549 | ||
550 | if (msignals & UART_MSR_CTS) | |
551 | ch->ch_mistat |= UART_MSR_CTS; | |
552 | else | |
553 | ch->ch_mistat &= ~UART_MSR_CTS; | |
554 | spin_unlock_irqrestore(&ch->ch_lock, flags); | |
555 | } | |
556 | ||
557 | /* Parse the ISR register for the specific port */ | |
558 | static inline void cls_parse_isr(struct dgnc_board *brd, uint port) | |
559 | { | |
560 | struct channel_t *ch; | |
561 | unsigned char isr = 0; | |
562 | unsigned long flags; | |
563 | ||
564 | /* | |
565 | * No need to verify board pointer, it was already | |
566 | * verified in the interrupt routine. | |
567 | */ | |
568 | ||
569 | if (port >= brd->nasync) | |
570 | return; | |
571 | ||
572 | ch = brd->channels[port]; | |
573 | if (ch->magic != DGNC_CHANNEL_MAGIC) | |
574 | return; | |
575 | ||
576 | /* Here we try to figure out what caused the interrupt to happen */ | |
577 | while (1) { | |
578 | isr = readb(&ch->ch_cls_uart->isr_fcr); | |
579 | ||
580 | /* Bail if no pending interrupt on port */ | |
581 | if (isr & UART_IIR_NO_INT) | |
582 | break; | |
583 | ||
584 | /* Receive Interrupt pending */ | |
585 | if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) { | |
586 | /* Read data from uart -> queue */ | |
a2237a2d DY |
587 | cls_copy_data_from_uart_to_queue(ch); |
588 | dgnc_check_queue_flow_control(ch); | |
589 | } | |
590 | ||
591 | /* Transmit Hold register empty pending */ | |
592 | if (isr & UART_IIR_THRI) { | |
593 | /* Transfer data (if any) from Write Queue -> UART. */ | |
594 | spin_lock_irqsave(&ch->ch_lock, flags); | |
595 | ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); | |
a2237a2d DY |
596 | spin_unlock_irqrestore(&ch->ch_lock, flags); |
597 | cls_copy_data_from_queue_to_uart(ch); | |
598 | } | |
599 | ||
a2237a2d DY |
600 | /* Parse any modem signal changes */ |
601 | cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); | |
602 | } | |
603 | } | |
604 | ||
605 | /* Channel lock MUST be held before calling this function! */ | |
606 | static void cls_flush_uart_write(struct channel_t *ch) | |
607 | { | |
608 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
609 | return; | |
610 | ||
611 | writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), | |
612 | &ch->ch_cls_uart->isr_fcr); | |
613 | usleep_range(10, 20); | |
614 | ||
615 | ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); | |
616 | } | |
617 | ||
618 | /* Channel lock MUST be held before calling this function! */ | |
619 | static void cls_flush_uart_read(struct channel_t *ch) | |
620 | { | |
621 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
622 | return; | |
623 | ||
624 | /* | |
625 | * For complete POSIX compatibility, we should be purging the | |
626 | * read FIFO in the UART here. | |
627 | * | |
628 | * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also | |
629 | * incorrectly flushes write data as well as just basically trashing the | |
630 | * FIFO. | |
631 | * | |
632 | * Presumably, this is a bug in this UART. | |
633 | */ | |
634 | ||
635 | udelay(10); | |
636 | } | |
637 | ||
638 | /* | |
639 | * cls_param() | |
640 | * Send any/all changes to the line to the UART. | |
641 | */ | |
642 | static void cls_param(struct tty_struct *tty) | |
643 | { | |
644 | unsigned char lcr = 0; | |
645 | unsigned char uart_lcr = 0; | |
646 | unsigned char ier = 0; | |
647 | unsigned char uart_ier = 0; | |
648 | uint baud = 9600; | |
649 | int quot = 0; | |
650 | struct dgnc_board *bd; | |
651 | struct channel_t *ch; | |
652 | struct un_t *un; | |
653 | ||
654 | if (!tty || tty->magic != TTY_MAGIC) | |
655 | return; | |
656 | ||
657 | un = (struct un_t *)tty->driver_data; | |
658 | if (!un || un->magic != DGNC_UNIT_MAGIC) | |
659 | return; | |
660 | ||
661 | ch = un->un_ch; | |
662 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
663 | return; | |
664 | ||
665 | bd = ch->ch_bd; | |
666 | if (!bd || bd->magic != DGNC_BOARD_MAGIC) | |
667 | return; | |
668 | ||
669 | /* | |
670 | * If baud rate is zero, flush queues, and set mval to drop DTR. | |
671 | */ | |
672 | if ((ch->ch_c_cflag & (CBAUD)) == 0) { | |
673 | ch->ch_r_head = 0; | |
674 | ch->ch_r_tail = 0; | |
675 | ch->ch_e_head = 0; | |
676 | ch->ch_e_tail = 0; | |
677 | ch->ch_w_head = 0; | |
678 | ch->ch_w_tail = 0; | |
679 | ||
680 | cls_flush_uart_write(ch); | |
681 | cls_flush_uart_read(ch); | |
682 | ||
683 | /* The baudrate is B0 so all modem lines are to be dropped. */ | |
684 | ch->ch_flags |= (CH_BAUD0); | |
685 | ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); | |
686 | cls_assert_modem_signals(ch); | |
687 | ch->ch_old_baud = 0; | |
688 | return; | |
689 | } else if (ch->ch_custom_speed) { | |
690 | baud = ch->ch_custom_speed; | |
691 | /* Handle transition from B0 */ | |
692 | if (ch->ch_flags & CH_BAUD0) { | |
693 | ch->ch_flags &= ~(CH_BAUD0); | |
694 | ||
695 | /* | |
696 | * Bring back up RTS and DTR... | |
697 | * Also handle RTS or DTR toggle if set. | |
698 | */ | |
699 | if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)) | |
700 | ch->ch_mostat |= (UART_MCR_RTS); | |
701 | if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) | |
702 | ch->ch_mostat |= (UART_MCR_DTR); | |
703 | } | |
704 | ||
705 | } else { | |
706 | int iindex = 0; | |
707 | int jindex = 0; | |
708 | ||
709 | ulong bauds[4][16] = { | |
710 | { /* slowbaud */ | |
711 | 0, 50, 75, 110, | |
712 | 134, 150, 200, 300, | |
713 | 600, 1200, 1800, 2400, | |
714 | 4800, 9600, 19200, 38400 }, | |
715 | { /* slowbaud & CBAUDEX */ | |
716 | 0, 57600, 115200, 230400, | |
717 | 460800, 150, 200, 921600, | |
718 | 600, 1200, 1800, 2400, | |
719 | 4800, 9600, 19200, 38400 }, | |
720 | { /* fastbaud */ | |
721 | 0, 57600, 76800, 115200, | |
722 | 131657, 153600, 230400, 460800, | |
723 | 921600, 1200, 1800, 2400, | |
724 | 4800, 9600, 19200, 38400 }, | |
725 | { /* fastbaud & CBAUDEX */ | |
726 | 0, 57600, 115200, 230400, | |
727 | 460800, 150, 200, 921600, | |
728 | 600, 1200, 1800, 2400, | |
729 | 4800, 9600, 19200, 38400 } | |
730 | }; | |
731 | ||
732 | /* | |
733 | * Only use the TXPrint baud rate if the terminal | |
cb3714a6 | 734 | * unit is NOT open |
735 | */ | |
736 | if (!(ch->ch_tun.un_flags & UN_ISOPEN) && | |
e352d3f1 | 737 | (un->un_type == DGNC_PRINT)) |
0b99d589 LL |
738 | baud = C_BAUD(ch->ch_pun.un_tty) & 0xff; |
739 | else | |
740 | baud = C_BAUD(ch->ch_tun.un_tty) & 0xff; | |
741 | ||
742 | if (ch->ch_c_cflag & CBAUDEX) | |
743 | iindex = 1; | |
744 | ||
745 | if (ch->ch_digi.digi_flags & DIGI_FAST) | |
746 | iindex += 2; | |
747 | ||
748 | jindex = baud; | |
749 | ||
cb3714a6 | 750 | if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && |
e352d3f1 | 751 | (jindex < 16)) { |
5ca46fd4 | 752 | baud = bauds[iindex][jindex]; |
0b99d589 | 753 | } else { |
0b99d589 LL |
754 | baud = 0; |
755 | } | |
756 | ||
757 | if (baud == 0) | |
758 | baud = 9600; | |
759 | ||
760 | /* Handle transition from B0 */ | |
761 | if (ch->ch_flags & CH_BAUD0) { | |
762 | ch->ch_flags &= ~(CH_BAUD0); | |
763 | ||
764 | /* | |
765 | * Bring back up RTS and DTR... | |
766 | * Also handle RTS or DTR toggle if set. | |
767 | */ | |
768 | if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)) | |
769 | ch->ch_mostat |= (UART_MCR_RTS); | |
770 | if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) | |
771 | ch->ch_mostat |= (UART_MCR_DTR); | |
772 | } | |
773 | } | |
774 | ||
cb3714a6 | 775 | if (ch->ch_c_cflag & PARENB) |
0b99d589 | 776 | lcr |= UART_LCR_PARITY; |
0b99d589 | 777 | |
cb3714a6 | 778 | if (!(ch->ch_c_cflag & PARODD)) |
0b99d589 | 779 | lcr |= UART_LCR_EPAR; |
0b99d589 LL |
780 | |
781 | /* | |
782 | * Not all platforms support mark/space parity, | |
783 | * so this will hide behind an ifdef. | |
784 | */ | |
785 | #ifdef CMSPAR | |
786 | if (ch->ch_c_cflag & CMSPAR) | |
5ca46fd4 | 787 | lcr |= UART_LCR_SPAR; |
0b99d589 LL |
788 | #endif |
789 | ||
790 | if (ch->ch_c_cflag & CSTOPB) | |
791 | lcr |= UART_LCR_STOP; | |
792 | ||
793 | switch (ch->ch_c_cflag & CSIZE) { | |
794 | case CS5: | |
795 | lcr |= UART_LCR_WLEN5; | |
796 | break; | |
797 | case CS6: | |
798 | lcr |= UART_LCR_WLEN6; | |
799 | break; | |
800 | case CS7: | |
801 | lcr |= UART_LCR_WLEN7; | |
802 | break; | |
803 | case CS8: | |
804 | default: | |
805 | lcr |= UART_LCR_WLEN8; | |
806 | break; | |
807 | } | |
808 | ||
587abd7b SL |
809 | uart_ier = readb(&ch->ch_cls_uart->ier); |
810 | ier = uart_ier; | |
0b99d589 LL |
811 | uart_lcr = readb(&ch->ch_cls_uart->lcr); |
812 | ||
813 | if (baud == 0) | |
814 | baud = 9600; | |
815 | ||
816 | quot = ch->ch_bd->bd_dividend / baud; | |
817 | ||
818 | if (quot != 0 && ch->ch_old_baud != baud) { | |
819 | ch->ch_old_baud = baud; | |
820 | writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr); | |
821 | writeb((quot & 0xff), &ch->ch_cls_uart->txrx); | |
822 | writeb((quot >> 8), &ch->ch_cls_uart->ier); | |
823 | writeb(lcr, &ch->ch_cls_uart->lcr); | |
cb3714a6 | 824 | } |
0b99d589 LL |
825 | |
826 | if (uart_lcr != lcr) | |
827 | writeb(lcr, &ch->ch_cls_uart->lcr); | |
828 | ||
cb3714a6 | 829 | if (ch->ch_c_cflag & CREAD) |
0b99d589 | 830 | ier |= (UART_IER_RDI | UART_IER_RLSI); |
cb3714a6 | 831 | else |
0b99d589 | 832 | ier &= ~(UART_IER_RDI | UART_IER_RLSI); |
0b99d589 LL |
833 | |
834 | /* | |
835 | * Have the UART interrupt on modem signal changes ONLY when | |
836 | * we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set. | |
837 | */ | |
cb3714a6 | 838 | if ((ch->ch_digi.digi_flags & CTSPACE) || |
c76d294f CM |
839 | (ch->ch_digi.digi_flags & RTSPACE) || |
840 | (ch->ch_c_cflag & CRTSCTS) || | |
841 | !(ch->ch_digi.digi_flags & DIGI_FORCEDCD) || | |
842 | !(ch->ch_c_cflag & CLOCAL)) | |
843 | ier |= UART_IER_MSI; | |
cb3714a6 | 844 | else |
c76d294f | 845 | ier &= ~UART_IER_MSI; |
0b99d589 LL |
846 | |
847 | ier |= UART_IER_THRI; | |
848 | ||
849 | if (ier != uart_ier) | |
850 | writeb(ier, &ch->ch_cls_uart->ier); | |
851 | ||
852 | if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) { | |
853 | cls_set_cts_flow_control(ch); | |
cb3714a6 | 854 | } else if (ch->ch_c_iflag & IXON) { |
855 | /* | |
856 | * If start/stop is set to disable, then we should | |
857 | * disable flow control | |
858 | */ | |
859 | if ((ch->ch_startc == _POSIX_VDISABLE) || | |
e352d3f1 | 860 | (ch->ch_stopc == _POSIX_VDISABLE)) |
0b99d589 LL |
861 | cls_set_no_output_flow_control(ch); |
862 | else | |
863 | cls_set_ixon_flow_control(ch); | |
cb3714a6 | 864 | } else { |
0b99d589 LL |
865 | cls_set_no_output_flow_control(ch); |
866 | } | |
867 | ||
868 | if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) { | |
869 | cls_set_rts_flow_control(ch); | |
cb3714a6 | 870 | } else if (ch->ch_c_iflag & IXOFF) { |
871 | /* | |
872 | * If start/stop is set to disable, then we should disable | |
873 | * flow control | |
874 | */ | |
875 | if ((ch->ch_startc == _POSIX_VDISABLE) || | |
e352d3f1 | 876 | (ch->ch_stopc == _POSIX_VDISABLE)) |
0b99d589 LL |
877 | cls_set_no_input_flow_control(ch); |
878 | else | |
879 | cls_set_ixoff_flow_control(ch); | |
cb3714a6 | 880 | } else { |
0b99d589 LL |
881 | cls_set_no_input_flow_control(ch); |
882 | } | |
883 | ||
884 | cls_assert_modem_signals(ch); | |
885 | ||
886 | /* Get current status of the modem signals now */ | |
887 | cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); | |
888 | } | |
889 | ||
0b99d589 LL |
890 | /* |
891 | * Our board poller function. | |
892 | */ | |
893 | static void cls_tasklet(unsigned long data) | |
894 | { | |
6f418259 | 895 | struct dgnc_board *bd = (struct dgnc_board *)data; |
0b99d589 | 896 | struct channel_t *ch; |
707505b5 | 897 | unsigned long flags; |
0b99d589 LL |
898 | int i; |
899 | int state = 0; | |
900 | int ports = 0; | |
901 | ||
1f26adc9 | 902 | if (!bd || bd->magic != DGNC_BOARD_MAGIC) |
0b99d589 | 903 | return; |
0b99d589 LL |
904 | |
905 | /* Cache a couple board values */ | |
707505b5 | 906 | spin_lock_irqsave(&bd->bd_lock, flags); |
0b99d589 LL |
907 | state = bd->state; |
908 | ports = bd->nasync; | |
707505b5 | 909 | spin_unlock_irqrestore(&bd->bd_lock, flags); |
0b99d589 LL |
910 | |
911 | /* | |
912 | * Do NOT allow the interrupt routine to read the intr registers | |
913 | * Until we release this lock. | |
914 | */ | |
707505b5 | 915 | spin_lock_irqsave(&bd->bd_intr_lock, flags); |
0b99d589 LL |
916 | |
917 | /* | |
918 | * If board is ready, parse deeper to see if there is anything to do. | |
919 | */ | |
920 | if ((state == BOARD_READY) && (ports > 0)) { | |
0b99d589 LL |
921 | /* Loop on each port */ |
922 | for (i = 0; i < ports; i++) { | |
923 | ch = bd->channels[i]; | |
0b99d589 LL |
924 | |
925 | /* | |
926 | * NOTE: Remember you CANNOT hold any channel | |
927 | * locks when calling input. | |
928 | * During input processing, its possible we | |
929 | * will call ld, which might do callbacks back | |
930 | * into us. | |
931 | */ | |
932 | dgnc_input(ch); | |
933 | ||
934 | /* | |
935 | * Channel lock is grabbed and then released | |
936 | * inside this routine. | |
937 | */ | |
938 | cls_copy_data_from_queue_to_uart(ch); | |
939 | dgnc_wakeup_writes(ch); | |
940 | ||
941 | /* | |
942 | * Check carrier function. | |
943 | */ | |
944 | dgnc_carrier(ch); | |
945 | ||
946 | /* | |
947 | * The timing check of turning off the break is done | |
948 | * inside clear_break() | |
949 | */ | |
950 | if (ch->ch_stop_sending_break) | |
951 | cls_clear_break(ch, 0); | |
952 | } | |
953 | } | |
954 | ||
707505b5 | 955 | spin_unlock_irqrestore(&bd->bd_intr_lock, flags); |
0b99d589 LL |
956 | } |
957 | ||
0b99d589 LL |
958 | /* |
959 | * cls_intr() | |
960 | * | |
961 | * Classic specific interrupt handler. | |
962 | */ | |
963 | static irqreturn_t cls_intr(int irq, void *voidbrd) | |
964 | { | |
fc33bd29 | 965 | struct dgnc_board *brd = voidbrd; |
0b99d589 | 966 | uint i = 0; |
446393e9 | 967 | unsigned char poll_reg; |
707505b5 | 968 | unsigned long flags; |
0b99d589 | 969 | |
0b99d589 | 970 | /* |
1f26adc9 RD |
971 | * Check to make sure it didn't receive interrupt with a null board |
972 | * associated or a board pointer that wasn't ours. | |
0b99d589 | 973 | */ |
1f26adc9 | 974 | if (!brd || brd->magic != DGNC_BOARD_MAGIC) |
0b99d589 | 975 | return IRQ_NONE; |
0b99d589 | 976 | |
707505b5 | 977 | spin_lock_irqsave(&brd->bd_intr_lock, flags); |
0b99d589 | 978 | |
a2237a2d DY |
979 | /* |
980 | * Check the board's global interrupt offset to see if we | |
981 | * we actually do have an interrupt pending for us. | |
982 | */ | |
983 | poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET); | |
984 | ||
985 | /* If 0, no interrupts pending */ | |
986 | if (!poll_reg) { | |
987 | spin_unlock_irqrestore(&brd->bd_intr_lock, flags); | |
988 | return IRQ_NONE; | |
0b99d589 LL |
989 | } |
990 | ||
a2237a2d DY |
991 | /* Parse each port to find out what caused the interrupt */ |
992 | for (i = 0; i < brd->nasync; i++) | |
993 | cls_parse_isr(brd, i); | |
994 | ||
0b99d589 | 995 | /* |
a2237a2d | 996 | * Schedule tasklet to more in-depth servicing at a better time. |
0b99d589 | 997 | */ |
a2237a2d | 998 | tasklet_schedule(&brd->helper_tasklet); |
0b99d589 | 999 | |
a2237a2d DY |
1000 | spin_unlock_irqrestore(&brd->bd_intr_lock, flags); |
1001 | ||
1002 | return IRQ_HANDLED; | |
1003 | } | |
1004 | ||
1005 | static void cls_disable_receiver(struct channel_t *ch) | |
1006 | { | |
1007 | unsigned char tmp = readb(&ch->ch_cls_uart->ier); | |
1008 | ||
1009 | tmp &= ~(UART_IER_RDI); | |
1010 | writeb(tmp, &ch->ch_cls_uart->ier); | |
1011 | } | |
1012 | ||
1013 | static void cls_enable_receiver(struct channel_t *ch) | |
1014 | { | |
1015 | unsigned char tmp = readb(&ch->ch_cls_uart->ier); | |
1016 | ||
1017 | tmp |= (UART_IER_RDI); | |
1018 | writeb(tmp, &ch->ch_cls_uart->ier); | |
0b99d589 LL |
1019 | } |
1020 | ||
0b99d589 LL |
1021 | /* |
1022 | * This function basically goes to sleep for secs, or until | |
1023 | * it gets signalled that the port has fully drained. | |
1024 | */ | |
1025 | static int cls_drain(struct tty_struct *tty, uint seconds) | |
1026 | { | |
707505b5 | 1027 | unsigned long flags; |
0b99d589 | 1028 | struct channel_t *ch; |
cb3714a6 | 1029 | struct un_t *un; |
0b99d589 | 1030 | |
cb3714a6 | 1031 | if (!tty || tty->magic != TTY_MAGIC) |
8f90ef80 | 1032 | return -ENXIO; |
0b99d589 | 1033 | |
6f418259 | 1034 | un = (struct un_t *)tty->driver_data; |
cb3714a6 | 1035 | if (!un || un->magic != DGNC_UNIT_MAGIC) |
8f90ef80 | 1036 | return -ENXIO; |
0b99d589 | 1037 | |
5ca46fd4 | 1038 | ch = un->un_ch; |
cb3714a6 | 1039 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) |
8f90ef80 | 1040 | return -ENXIO; |
0b99d589 | 1041 | |
707505b5 | 1042 | spin_lock_irqsave(&ch->ch_lock, flags); |
0b99d589 | 1043 | un->un_flags |= UN_EMPTY; |
707505b5 | 1044 | spin_unlock_irqrestore(&ch->ch_lock, flags); |
0b99d589 LL |
1045 | |
1046 | /* | |
1047 | * NOTE: Do something with time passed in. | |
1048 | */ | |
0b99d589 LL |
1049 | |
1050 | /* If ret is non-zero, user ctrl-c'ed us */ | |
0b99d589 | 1051 | |
ca5dd0b8 HS |
1052 | return wait_event_interruptible(un->un_flags_wait, |
1053 | ((un->un_flags & UN_EMPTY) == 0)); | |
0b99d589 | 1054 | } |
5ca46fd4 | 1055 | |
0b99d589 LL |
1056 | static void cls_send_start_character(struct channel_t *ch) |
1057 | { | |
1058 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
1059 | return; | |
1060 | ||
1061 | if (ch->ch_startc != _POSIX_VDISABLE) { | |
1062 | ch->ch_xon_sends++; | |
1063 | writeb(ch->ch_startc, &ch->ch_cls_uart->txrx); | |
cb3714a6 | 1064 | } |
0b99d589 LL |
1065 | } |
1066 | ||
0b99d589 LL |
1067 | static void cls_send_stop_character(struct channel_t *ch) |
1068 | { | |
1069 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
1070 | return; | |
1071 | ||
1072 | if (ch->ch_stopc != _POSIX_VDISABLE) { | |
1073 | ch->ch_xoff_sends++; | |
1074 | writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx); | |
cb3714a6 | 1075 | } |
0b99d589 LL |
1076 | } |
1077 | ||
0b99d589 LL |
1078 | /* Inits UART */ |
1079 | static void cls_uart_init(struct channel_t *ch) | |
1080 | { | |
446393e9 EA |
1081 | unsigned char lcrb = readb(&ch->ch_cls_uart->lcr); |
1082 | unsigned char isr_fcr = 0; | |
0b99d589 LL |
1083 | |
1084 | writeb(0, &ch->ch_cls_uart->ier); | |
1085 | ||
1086 | /* | |
1087 | * The Enhanced Register Set may only be accessed when | |
1088 | * the Line Control Register is set to 0xBFh. | |
1089 | */ | |
1090 | writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr); | |
1091 | ||
1092 | isr_fcr = readb(&ch->ch_cls_uart->isr_fcr); | |
5ca46fd4 | 1093 | |
0b99d589 LL |
1094 | /* Turn on Enhanced/Extended controls */ |
1095 | isr_fcr |= (UART_EXAR654_EFR_ECB); | |
1096 | ||
1097 | writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr); | |
1098 | ||
1099 | /* Write old LCR value back out, which turns enhanced access off */ | |
1100 | writeb(lcrb, &ch->ch_cls_uart->lcr); | |
1101 | ||
cb3714a6 | 1102 | /* Clear out UART and FIFO */ |
0b99d589 LL |
1103 | readb(&ch->ch_cls_uart->txrx); |
1104 | ||
ea753f2a | 1105 | writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, |
e352d3f1 | 1106 | &ch->ch_cls_uart->isr_fcr); |
0b99d589 LL |
1107 | udelay(10); |
1108 | ||
1109 | ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); | |
1110 | ||
1111 | readb(&ch->ch_cls_uart->lsr); | |
1112 | readb(&ch->ch_cls_uart->msr); | |
1113 | } | |
1114 | ||
0b99d589 LL |
1115 | /* |
1116 | * Turns off UART. | |
1117 | */ | |
1118 | static void cls_uart_off(struct channel_t *ch) | |
1119 | { | |
1120 | writeb(0, &ch->ch_cls_uart->ier); | |
1121 | } | |
1122 | ||
0b99d589 LL |
1123 | /* |
1124 | * cls_get_uarts_bytes_left. | |
1125 | * Returns 0 is nothing left in the FIFO, returns 1 otherwise. | |
1126 | * | |
1127 | * The channel lock MUST be held by the calling function. | |
1128 | */ | |
1129 | static uint cls_get_uart_bytes_left(struct channel_t *ch) | |
1130 | { | |
446393e9 EA |
1131 | unsigned char left = 0; |
1132 | unsigned char lsr = 0; | |
0b99d589 LL |
1133 | |
1134 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
1135 | return 0; | |
1136 | ||
1137 | lsr = readb(&ch->ch_cls_uart->lsr); | |
1138 | ||
1139 | /* Determine whether the Transmitter is empty or not */ | |
1140 | if (!(lsr & UART_LSR_TEMT)) { | |
3d47910b | 1141 | if (ch->ch_flags & CH_TX_FIFO_EMPTY) |
0b99d589 | 1142 | tasklet_schedule(&ch->ch_bd->helper_tasklet); |
0b99d589 | 1143 | left = 1; |
cb3714a6 | 1144 | } else { |
0b99d589 LL |
1145 | ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); |
1146 | left = 0; | |
1147 | } | |
1148 | ||
1149 | return left; | |
1150 | } | |
1151 | ||
0b99d589 LL |
1152 | /* |
1153 | * cls_send_break. | |
1154 | * Starts sending a break thru the UART. | |
1155 | * | |
1156 | * The channel lock MUST be held by the calling function. | |
1157 | */ | |
1158 | static void cls_send_break(struct channel_t *ch, int msecs) | |
1159 | { | |
1160 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
1161 | return; | |
1162 | ||
1163 | /* | |
1164 | * If we receive a time of 0, this means turn off the break. | |
1165 | */ | |
1166 | if (msecs == 0) { | |
1167 | /* Turn break off, and unset some variables */ | |
1168 | if (ch->ch_flags & CH_BREAK_SENDING) { | |
446393e9 | 1169 | unsigned char temp = readb(&ch->ch_cls_uart->lcr); |
8aa5d0d8 | 1170 | |
0b99d589 LL |
1171 | writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr); |
1172 | ch->ch_flags &= ~(CH_BREAK_SENDING); | |
1173 | ch->ch_stop_sending_break = 0; | |
0b99d589 LL |
1174 | } |
1175 | return; | |
cb3714a6 | 1176 | } |
0b99d589 LL |
1177 | |
1178 | /* | |
1179 | * Set the time we should stop sending the break. | |
1180 | * If we are already sending a break, toss away the existing | |
1181 | * time to stop, and use this new value instead. | |
1182 | */ | |
1183 | ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs); | |
1184 | ||
1185 | /* Tell the UART to start sending the break */ | |
1186 | if (!(ch->ch_flags & CH_BREAK_SENDING)) { | |
446393e9 | 1187 | unsigned char temp = readb(&ch->ch_cls_uart->lcr); |
8aa5d0d8 | 1188 | |
0b99d589 LL |
1189 | writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr); |
1190 | ch->ch_flags |= (CH_BREAK_SENDING); | |
0b99d589 LL |
1191 | } |
1192 | } | |
1193 | ||
0b99d589 LL |
1194 | /* |
1195 | * cls_send_immediate_char. | |
1196 | * Sends a specific character as soon as possible to the UART, | |
1197 | * jumping over any bytes that might be in the write queue. | |
1198 | * | |
1199 | * The channel lock MUST be held by the calling function. | |
1200 | */ | |
1201 | static void cls_send_immediate_char(struct channel_t *ch, unsigned char c) | |
1202 | { | |
1203 | if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) | |
1204 | return; | |
1205 | ||
1206 | writeb(c, &ch->ch_cls_uart->txrx); | |
1207 | } | |
1208 | ||
03425f55 | 1209 | static void cls_vpd(struct dgnc_board *brd) |
0b99d589 | 1210 | { |
cb3714a6 | 1211 | ulong vpdbase; /* Start of io base of the card */ |
1212 | u8 __iomem *re_map_vpdbase;/* Remapped memory of the card */ | |
0b99d589 LL |
1213 | int i = 0; |
1214 | ||
0b99d589 LL |
1215 | vpdbase = pci_resource_start(brd->pdev, 3); |
1216 | ||
1217 | /* No VPD */ | |
1218 | if (!vpdbase) | |
1219 | return; | |
1220 | ||
1221 | re_map_vpdbase = ioremap(vpdbase, 0x400); | |
1222 | ||
1223 | if (!re_map_vpdbase) | |
1224 | return; | |
1225 | ||
cb3714a6 | 1226 | /* Store the VPD into our buffer */ |
1227 | for (i = 0; i < 0x40; i++) { | |
0b99d589 | 1228 | brd->vpd[i] = readb(re_map_vpdbase + i); |
cb3714a6 | 1229 | pr_info("%x ", brd->vpd[i]); |
1230 | } | |
1231 | pr_info("\n"); | |
0b99d589 | 1232 | |
e24bb0ed | 1233 | iounmap(re_map_vpdbase); |
0b99d589 | 1234 | } |
a2237a2d DY |
1235 | |
1236 | struct board_ops dgnc_cls_ops = { | |
1237 | .tasklet = cls_tasklet, | |
1238 | .intr = cls_intr, | |
1239 | .uart_init = cls_uart_init, | |
1240 | .uart_off = cls_uart_off, | |
1241 | .drain = cls_drain, | |
1242 | .param = cls_param, | |
1243 | .vpd = cls_vpd, | |
1244 | .assert_modem_signals = cls_assert_modem_signals, | |
1245 | .flush_uart_write = cls_flush_uart_write, | |
1246 | .flush_uart_read = cls_flush_uart_read, | |
1247 | .disable_receiver = cls_disable_receiver, | |
1248 | .enable_receiver = cls_enable_receiver, | |
1249 | .send_break = cls_send_break, | |
1250 | .send_start_character = cls_send_start_character, | |
1251 | .send_stop_character = cls_send_stop_character, | |
1252 | .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart, | |
1253 | .get_uart_bytes_left = cls_get_uart_bytes_left, | |
1254 | .send_immediate_char = cls_send_immediate_char | |
1255 | }; |