Blame view
drivers/tty/cyclades.c
111 KB
1da177e4c
|
1 2 3 |
#undef BLOCKMOVE #define Z_WAKE #undef Z_EXT_CHARS_IN_BUFFER |
1da177e4c
|
4 5 |
/* |
1da177e4c
|
6 7 8 9 10 |
* This file contains the driver for the Cyclades async multiport * serial boards. * * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>. * Modified and maintained by Marcio Saito <marcio@cyclades.com>. |
1da177e4c
|
11 |
* |
ebdb51359
|
12 |
* Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com> |
1da177e4c
|
13 14 15 16 17 |
* * Much of the design and some of the code came from serial.c * which was copyright (C) 1991, 1992 Linus Torvalds. It was * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, * and then fixed as suggested by Michael K. Johnson 12/12/92. |
c8e1693a4
|
18 |
* Converted to pci probing and cleaned up by Jiri Slaby. |
1da177e4c
|
19 |
* |
1da177e4c
|
20 |
*/ |
ebdb51359
|
21 |
#define CY_VERSION "2.6" |
096dcfce3
|
22 |
|
1da177e4c
|
23 24 25 |
/* If you need to install more boards than NR_CARDS, change the constant in the definition below. No other change is necessary to support up to eight boards. Beyond that you'll have to extend cy_isa_addresses. */ |
02f1175c8
|
26 |
#define NR_CARDS 4 |
1da177e4c
|
27 28 29 30 31 |
/* If the total number of ports is larger than NR_PORTS, change this constant in the definition below. No other change is necessary to support more boards/ports. */ |
02f1175c8
|
32 |
#define NR_PORTS 256 |
1da177e4c
|
33 |
|
1da177e4c
|
34 35 36 37 38 39 40 41 42 43 44 |
#define ZO_V1 0 #define ZO_V2 1 #define ZE_V1 2 #define SERIAL_PARANOIA_CHECK #undef CY_DEBUG_OPEN #undef CY_DEBUG_THROTTLE #undef CY_DEBUG_OTHER #undef CY_DEBUG_IO #undef CY_DEBUG_COUNT #undef CY_DEBUG_DTR |
1da177e4c
|
45 46 47 48 |
#undef CY_DEBUG_INTERRUPTS #undef CY_16Y_HACK #undef CY_ENABLE_MONITORING #undef CY_PCI_DEBUG |
1da177e4c
|
49 |
/* |
15ed6cc0b
|
50 |
* Include section |
1da177e4c
|
51 |
*/ |
1da177e4c
|
52 53 54 55 56 57 58 |
#include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> |
33f0f88f1
|
59 |
#include <linux/tty_flip.h> |
1da177e4c
|
60 61 62 63 64 65 66 67 68 69 70 71 |
#include <linux/serial.h> #include <linux/major.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/cyclades.h> #include <linux/mm.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/bitops.h> |
054f5b0aa
|
72 |
#include <linux/firmware.h> |
9f56fad74
|
73 |
#include <linux/device.h> |
5a0e3ad6a
|
74 |
#include <linux/slab.h> |
1da177e4c
|
75 |
|
15ed6cc0b
|
76 |
#include <linux/io.h> |
15ed6cc0b
|
77 |
#include <linux/uaccess.h> |
1da177e4c
|
78 |
|
1da177e4c
|
79 80 81 82 83 |
#include <linux/kernel.h> #include <linux/pci.h> #include <linux/stat.h> #include <linux/proc_fs.h> |
444697d61
|
84 |
#include <linux/seq_file.h> |
1da177e4c
|
85 |
|
02f1175c8
|
86 |
static void cy_send_xchar(struct tty_struct *tty, char ch); |
1da177e4c
|
87 |
|
1da177e4c
|
88 89 90 |
#ifndef SERIAL_XMIT_SIZE #define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) #endif |
1da177e4c
|
91 92 |
#define STD_COM_FLAGS (0) |
054f5b0aa
|
93 94 95 96 |
/* firmware stuff */ #define ZL_MAX_BLOCKS 16 #define DRIVER_VERSION 0x02010203 #define RAM_SIZE 0x80000 |
054f5b0aa
|
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
enum zblock_type { ZBLOCK_PRG = 0, ZBLOCK_FPGA = 1 }; struct zfile_header { char name[64]; char date[32]; char aux[32]; u32 n_config; u32 config_offset; u32 n_blocks; u32 block_offset; u32 reserved[9]; } __attribute__ ((packed)); struct zfile_config { char name[64]; u32 mailbox; u32 function; u32 n_blocks; u32 block_list[ZL_MAX_BLOCKS]; } __attribute__ ((packed)); struct zfile_block { u32 type; u32 file_offset; u32 ram_offset; u32 size; } __attribute__ ((packed)); |
1da177e4c
|
127 128 129 130 131 132 133 134 |
static struct tty_driver *cy_serial_driver; #ifdef CONFIG_ISA /* This is the address lookup table. The driver will probe for Cyclom-Y/ISA boards at all addresses in here. If you want the driver to probe addresses at a different address, add it to this table. If the driver is probing some other board and causing problems, remove the offending address from this table. |
1da177e4c
|
135 136 137 |
*/ static unsigned int cy_isa_addresses[] = { |
02f1175c8
|
138 139 140 141 142 143 144 145 146 |
0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000, 0, 0, 0, 0, 0, 0, 0, 0 |
1da177e4c
|
147 |
}; |
02f1175c8
|
148 |
|
fe971071a
|
149 |
#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses) |
1da177e4c
|
150 |
|
3046d50ea
|
151 152 |
static long maddr[NR_CARDS]; static int irq[NR_CARDS]; |
1da177e4c
|
153 154 155 |
module_param_array(maddr, long, NULL, 0); module_param_array(irq, int, NULL, 0); |
1da177e4c
|
156 |
|
02f1175c8
|
157 |
#endif /* CONFIG_ISA */ |
1da177e4c
|
158 159 160 161 162 |
/* This is the per-card data structure containing address, irq, number of channels, etc. This driver supports a maximum of NR_CARDS cards. */ static struct cyclades_card cy_card[NR_CARDS]; |
02f1175c8
|
163 |
static int cy_next_channel; /* next minor available */ |
1da177e4c
|
164 165 |
/* |
1da177e4c
|
166 167 |
* This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra |
77451e53e
|
168 |
* are accessed via settings in info->port.flags. |
1da177e4c
|
169 170 171 172 173 |
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, * HI VHI * 20 */ |
ebdb51359
|
174 |
static const int baud_table[] = { |
02f1175c8
|
175 176 177 178 |
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, 230400, 0 }; |
ebdb51359
|
179 |
static const char baud_co_25[] = { /* 25 MHz clock option table */ |
02f1175c8
|
180 181 182 183 184 |
/* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
ebdb51359
|
185 |
static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ |
02f1175c8
|
186 187 188 |
0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 }; |
ebdb51359
|
189 |
static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ |
02f1175c8
|
190 191 192 193 194 195 |
/* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
ebdb51359
|
196 |
static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ |
02f1175c8
|
197 198 199 200 |
0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, 0x21 }; |
ebdb51359
|
201 |
static const char baud_cor3[] = { /* receive threshold */ |
02f1175c8
|
202 203 204 205 |
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07 }; |
1da177e4c
|
206 207 208 |
/* * The Cyclades driver implements HW flow control as any serial driver. |
15ed6cc0b
|
209 210 211 212 213 214 |
* The cyclades_port structure member rflow and the vector rflow_thr * allows us to take advantage of a special feature in the CD1400 to avoid * data loss even when the system interrupt latency is too high. These flags * are to be used only with very special applications. Setting these flags * requires the use of a special cable (DTR and RTS reversed). In the new * CD1400-based boards (rev. 6.00 or later), there is no need for special |
1da177e4c
|
215 216 |
* cables. */ |
ebdb51359
|
217 |
static const char rflow_thr[] = { /* rflow threshold */ |
02f1175c8
|
218 219 220 221 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }; |
1da177e4c
|
222 223 224 225 |
/* The Cyclom-Ye has placed the sequential chips in non-sequential * address order. This look-up table overcomes that problem. */ |
f0eefdc30
|
226 |
static const unsigned int cy_chip_offset[] = { 0x0000, |
02f1175c8
|
227 228 229 230 231 232 233 234 |
0x0400, 0x0800, 0x0C00, 0x0200, 0x0600, 0x0A00, 0x0E00 }; |
1da177e4c
|
235 236 |
/* PCI related definitions */ |
1da177e4c
|
237 |
#ifdef CONFIG_PCI |
ebdb51359
|
238 |
static const struct pci_device_id cy_pci_dev_id[] = { |
15ed6cc0b
|
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
/* PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, /* PCI > 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) }, /* 4Y PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) }, /* 4Y PCI > 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) }, /* 8Y PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) }, /* 8Y PCI > 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) }, /* Z PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) }, /* Z PCI > 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) }, |
893de2dff
|
255 |
{ } /* end of table */ |
02f1175c8
|
256 |
}; |
893de2dff
|
257 |
MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); |
1da177e4c
|
258 259 260 |
#endif static void cy_start(struct tty_struct *); |
d13549f80
|
261 |
static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); |
1a86b5e34
|
262 |
static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); |
1da177e4c
|
263 264 |
#ifdef CONFIG_ISA static unsigned detect_isa_irq(void __iomem *); |
02f1175c8
|
265 |
#endif /* CONFIG_ISA */ |
1da177e4c
|
266 |
|
1da177e4c
|
267 268 269 270 271 |
#ifndef CONFIG_CYZ_INTR static void cyz_poll(unsigned long); /* The Cyclades-Z polling cycle is defined by this variable */ static long cyz_polling_cycle = CZ_DEF_POLL; |
8d06afab7
|
272 |
static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0); |
1da177e4c
|
273 |
|
02f1175c8
|
274 |
#else /* CONFIG_CYZ_INTR */ |
1da177e4c
|
275 276 |
static void cyz_rx_restart(unsigned long); static struct timer_list cyz_rx_full_timer[NR_PORTS]; |
02f1175c8
|
277 |
#endif /* CONFIG_CYZ_INTR */ |
1da177e4c
|
278 |
|
3aeea5b92
|
279 280 281 282 283 284 285 286 287 288 289 290 291 |
static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) { struct cyclades_card *card = port->card; cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); } static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) { struct cyclades_card *card = port->card; return readb(port->u.cyy.base_addr + (reg << card->bus_index)); } |
2693f485c
|
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
static inline bool cy_is_Z(struct cyclades_card *card) { return card->num_chips == (unsigned int)-1; } static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr) { return readl(&ctl_addr->init_ctrl) & (1 << 17); } static inline bool cyz_fpga_loaded(struct cyclades_card *card) { return __cyz_fpga_loaded(card->ctl_addr.p9060); } static inline bool cyz_is_loaded(struct cyclades_card *card) { struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS; return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) && readl(&fw_id->signature) == ZFIRM_ID; } |
02f1175c8
|
314 |
static inline int serial_paranoia_check(struct cyclades_port *info, |
ebdb51359
|
315 |
const char *name, const char *routine) |
1da177e4c
|
316 317 |
{ #ifdef SERIAL_PARANOIA_CHECK |
02f1175c8
|
318 |
if (!info) { |
217191910
|
319 320 321 |
printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) " "in %s ", name, routine); |
02f1175c8
|
322 323 |
return 1; } |
02f1175c8
|
324 |
if (info->magic != CYCLADES_MAGIC) { |
217191910
|
325 326 327 |
printk(KERN_WARNING "cyc Warning: bad magic number for serial " "struct (%s) in %s ", name, routine); |
02f1175c8
|
328 329 |
return 1; } |
1da177e4c
|
330 |
#endif |
02f1175c8
|
331 |
return 0; |
ebdb51359
|
332 |
} |
1da177e4c
|
333 |
|
1da177e4c
|
334 335 336 337 338 339 340 341 342 343 |
/***********************************************************/ /********* Start of block of Cyclom-Y specific code ********/ /* This routine waits up to 1000 micro-seconds for the previous command to the Cirrus chip to complete and then issues the new command. An error is returned if the previous command didn't finish within the time limit. This function is only called from inside spinlock-protected code. */ |
3aeea5b92
|
344 |
static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) |
1da177e4c
|
345 |
{ |
3aeea5b92
|
346 |
void __iomem *ccr = base_addr + (CyCCR << index); |
ad39c3004
|
347 |
unsigned int i; |
1da177e4c
|
348 |
|
02f1175c8
|
349 350 |
/* Check to see that the previous command has completed */ for (i = 0; i < 100; i++) { |
3aeea5b92
|
351 |
if (readb(ccr) == 0) |
02f1175c8
|
352 |
break; |
02f1175c8
|
353 |
udelay(10L); |
1da177e4c
|
354 |
} |
02f1175c8
|
355 356 357 |
/* if the CCR never cleared, the previous command didn't finish within the "reasonable time" */ if (i == 100) |
096dcfce3
|
358 |
return -1; |
1da177e4c
|
359 |
|
02f1175c8
|
360 |
/* Issue the new command */ |
3aeea5b92
|
361 |
cy_writeb(ccr, cmd); |
1da177e4c
|
362 |
|
096dcfce3
|
363 |
return 0; |
3aeea5b92
|
364 365 366 367 368 369 370 |
} static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) { return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, port->card->bus_index); } |
1da177e4c
|
371 372 373 |
#ifdef CONFIG_ISA /* ISA interrupt detection code */ |
15ed6cc0b
|
374 |
static unsigned detect_isa_irq(void __iomem *address) |
1da177e4c
|
375 |
{ |
02f1175c8
|
376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
int irq; unsigned long irqs, flags; int save_xir, save_car; int index = 0; /* IRQ probing is only for ISA */ /* forget possible initially masked and pending IRQ */ irq = probe_irq_off(probe_irq_on()); /* Clear interrupts on the board first */ cy_writeb(address + (Cy_ClrIntr << index), 0); /* Cy_ClrIntr is 0x1800 */ irqs = probe_irq_on(); /* Wait ... */ |
f6e208c11
|
390 |
msleep(5); |
02f1175c8
|
391 392 393 394 |
/* Enable the Tx interrupts on the CD1400 */ local_irq_save(flags); cy_writeb(address + (CyCAR << index), 0); |
3aeea5b92
|
395 |
__cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); |
02f1175c8
|
396 397 398 |
cy_writeb(address + (CyCAR << index), 0); cy_writeb(address + (CySRER << index), |
db05c3b1d
|
399 |
readb(address + (CySRER << index)) | CyTxRdy); |
02f1175c8
|
400 401 402 |
local_irq_restore(flags); /* Wait ... */ |
f6e208c11
|
403 |
msleep(5); |
02f1175c8
|
404 405 406 407 408 |
/* Check which interrupt is in use */ irq = probe_irq_off(irqs); /* Clean up */ |
db05c3b1d
|
409 410 |
save_xir = (u_char) readb(address + (CyTIR << index)); save_car = readb(address + (CyCAR << index)); |
02f1175c8
|
411 412 |
cy_writeb(address + (CyCAR << index), (save_xir & 0x3)); cy_writeb(address + (CySRER << index), |
db05c3b1d
|
413 |
readb(address + (CySRER << index)) & ~CyTxRdy); |
02f1175c8
|
414 415 416 417 418 419 |
cy_writeb(address + (CyTIR << index), (save_xir & 0x3f)); cy_writeb(address + (CyCAR << index), (save_car)); cy_writeb(address + (Cy_ClrIntr << index), 0); /* Cy_ClrIntr is 0x1800 */ return (irq > 0) ? irq : 0; |
1da177e4c
|
420 |
} |
02f1175c8
|
421 |
#endif /* CONFIG_ISA */ |
1da177e4c
|
422 |
|
ce97a0976
|
423 424 |
static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, void __iomem *base_addr) |
e941027f5
|
425 426 427 |
{ struct cyclades_port *info; struct tty_struct *tty; |
65f76a82e
|
428 |
int len, index = cinfo->bus_index; |
3aeea5b92
|
429 |
u8 ivr, save_xir, channel, save_car, data, char_count; |
e941027f5
|
430 |
|
e941027f5
|
431 |
#ifdef CY_DEBUG_INTERRUPTS |
ce97a0976
|
432 433 |
printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d ", chip); |
e941027f5
|
434 |
#endif |
ce97a0976
|
435 |
/* determine the channel & change to that context */ |
65f76a82e
|
436 437 |
save_xir = readb(base_addr + (CyRIR << index)); channel = save_xir & CyIRChannel; |
ce97a0976
|
438 |
info = &cinfo->ports[channel + chip * 4]; |
3aeea5b92
|
439 440 441 |
save_car = cyy_readb(info, CyCAR); cyy_writeb(info, CyCAR, save_xir); ivr = cyy_readb(info, CyRIVR) & CyIVRMask; |
ce97a0976
|
442 |
|
d13549f80
|
443 |
tty = tty_port_tty_get(&info->port); |
ce97a0976
|
444 |
/* if there is nowhere to put the data, discard it */ |
d13549f80
|
445 |
if (tty == NULL) { |
3aeea5b92
|
446 447 |
if (ivr == CyIVRRxEx) { /* exception */ data = cyy_readb(info, CyRDSR); |
ce97a0976
|
448 |
} else { /* normal character reception */ |
3aeea5b92
|
449 |
char_count = cyy_readb(info, CyRDCR); |
ce97a0976
|
450 |
while (char_count--) |
3aeea5b92
|
451 |
data = cyy_readb(info, CyRDSR); |
ce97a0976
|
452 453 454 455 |
} goto end; } /* there is an open port for this data */ |
3aeea5b92
|
456 457 |
if (ivr == CyIVRRxEx) { /* exception */ data = cyy_readb(info, CyRDSR); |
ce97a0976
|
458 459 460 461 462 463 464 465 466 467 468 469 470 |
/* For statistics only */ if (data & CyBREAK) info->icount.brk++; else if (data & CyFRAME) info->icount.frame++; else if (data & CyPARITY) info->icount.parity++; else if (data & CyOVERRUN) info->icount.overrun++; if (data & info->ignore_status_mask) { info->icount.rx++; |
d13549f80
|
471 |
tty_kref_put(tty); |
ce97a0976
|
472 473 474 475 476 477 |
return; } if (tty_buffer_request_room(tty, 1)) { if (data & info->read_status_mask) { if (data & CyBREAK) { tty_insert_flip_char(tty, |
3aeea5b92
|
478 479 |
cyy_readb(info, CyRDSR), TTY_BREAK); |
ce97a0976
|
480 |
info->icount.rx++; |
77451e53e
|
481 |
if (info->port.flags & ASYNC_SAK) |
ce97a0976
|
482 483 |
do_SAK(tty); } else if (data & CyFRAME) { |
15ed6cc0b
|
484 |
tty_insert_flip_char(tty, |
3aeea5b92
|
485 486 |
cyy_readb(info, CyRDSR), TTY_FRAME); |
ce97a0976
|
487 488 489 490 491 |
info->icount.rx++; info->idle_stats.frame_errs++; } else if (data & CyPARITY) { /* Pieces of seven... */ tty_insert_flip_char(tty, |
3aeea5b92
|
492 493 |
cyy_readb(info, CyRDSR), TTY_PARITY); |
ce97a0976
|
494 495 496 497 498 499 500 501 502 503 504 |
info->icount.rx++; info->idle_stats.parity_errs++; } else if (data & CyOVERRUN) { tty_insert_flip_char(tty, 0, TTY_OVERRUN); info->icount.rx++; /* If the flip buffer itself is overflowing, we still lose the next incoming character. */ tty_insert_flip_char(tty, |
3aeea5b92
|
505 506 |
cyy_readb(info, CyRDSR), TTY_FRAME); |
02f1175c8
|
507 |
info->icount.rx++; |
02f1175c8
|
508 |
info->idle_stats.overruns++; |
ce97a0976
|
509 510 511 512 513 514 515 516 |
/* These two conditions may imply */ /* a normal read should be done. */ /* } else if(data & CyTIMEOUT) { */ /* } else if(data & CySPECHAR) { */ } else { tty_insert_flip_char(tty, 0, TTY_NORMAL); info->icount.rx++; |
02f1175c8
|
517 |
} |
ce97a0976
|
518 519 520 521 522 523 524 525 526 527 528 529 |
} else { tty_insert_flip_char(tty, 0, TTY_NORMAL); info->icount.rx++; } } else { /* there was a software buffer overrun and nothing * could be done about it!!! */ info->icount.buf_overrun++; info->idle_stats.overruns++; } } else { /* normal character reception */ /* load # chars available from the chip */ |
3aeea5b92
|
530 |
char_count = cyy_readb(info, CyRDCR); |
e941027f5
|
531 532 |
#ifdef CY_ENABLE_MONITORING |
ce97a0976
|
533 534 535 536 537 |
++info->mon.int_count; info->mon.char_count += char_count; if (char_count > info->mon.char_max) info->mon.char_max = char_count; info->mon.char_last = char_count; |
e941027f5
|
538 |
#endif |
ce97a0976
|
539 540 |
len = tty_buffer_request_room(tty, char_count); while (len--) { |
3aeea5b92
|
541 |
data = cyy_readb(info, CyRDSR); |
ce97a0976
|
542 543 544 |
tty_insert_flip_char(tty, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; |
e941027f5
|
545 |
#ifdef CY_16Y_HACK |
ce97a0976
|
546 |
udelay(10L); |
e941027f5
|
547 |
#endif |
e941027f5
|
548 |
} |
ce97a0976
|
549 |
info->idle_stats.recv_idle = jiffies; |
e941027f5
|
550 |
} |
ce97a0976
|
551 |
tty_schedule_flip(tty); |
d13549f80
|
552 |
tty_kref_put(tty); |
ce97a0976
|
553 554 |
end: /* end of service */ |
3aeea5b92
|
555 556 |
cyy_writeb(info, CyRIR, save_xir & 0x3f); cyy_writeb(info, CyCAR, save_car); |
ce97a0976
|
557 |
} |
e941027f5
|
558 |
|
65f76a82e
|
559 |
static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, |
ce97a0976
|
560 561 562 |
void __iomem *base_addr) { struct cyclades_port *info; |
d13549f80
|
563 |
struct tty_struct *tty; |
65f76a82e
|
564 565 |
int char_count, index = cinfo->bus_index; u8 save_xir, channel, save_car, outch; |
ce97a0976
|
566 567 568 569 |
/* Since we only get here when the transmit buffer is empty, we know we can always stuff a dozen characters. */ |
e941027f5
|
570 |
#ifdef CY_DEBUG_INTERRUPTS |
ce97a0976
|
571 572 |
printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d ", chip); |
e941027f5
|
573 |
#endif |
ce97a0976
|
574 |
/* determine the channel & change to that context */ |
65f76a82e
|
575 576 |
save_xir = readb(base_addr + (CyTIR << index)); channel = save_xir & CyIRChannel; |
ce97a0976
|
577 578 |
save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); |
ce97a0976
|
579 |
info = &cinfo->ports[channel + chip * 4]; |
d13549f80
|
580 581 |
tty = tty_port_tty_get(&info->port); if (tty == NULL) { |
3aeea5b92
|
582 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); |
ce97a0976
|
583 584 |
goto end; } |
02f1175c8
|
585 |
|
ce97a0976
|
586 587 |
/* load the on-chip space for outbound data */ char_count = info->xmit_fifo_size; |
02f1175c8
|
588 |
|
ce97a0976
|
589 590 |
if (info->x_char) { /* send special char */ outch = info->x_char; |
3aeea5b92
|
591 |
cyy_writeb(info, CyTDR, outch); |
ce97a0976
|
592 593 594 595 |
char_count--; info->icount.tx++; info->x_char = 0; } |
02f1175c8
|
596 |
|
ce97a0976
|
597 598 |
if (info->breakon || info->breakoff) { if (info->breakon) { |
3aeea5b92
|
599 600 |
cyy_writeb(info, CyTDR, 0); cyy_writeb(info, CyTDR, 0x81); |
ce97a0976
|
601 602 |
info->breakon = 0; char_count -= 2; |
e941027f5
|
603 |
} |
ce97a0976
|
604 |
if (info->breakoff) { |
3aeea5b92
|
605 606 |
cyy_writeb(info, CyTDR, 0); cyy_writeb(info, CyTDR, 0x83); |
ce97a0976
|
607 608 |
info->breakoff = 0; char_count -= 2; |
e941027f5
|
609 |
} |
ce97a0976
|
610 |
} |
02f1175c8
|
611 |
|
ce97a0976
|
612 613 |
while (char_count-- > 0) { if (!info->xmit_cnt) { |
3aeea5b92
|
614 615 616 |
if (cyy_readb(info, CySRER) & CyTxMpty) { cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxMpty); |
ce97a0976
|
617 |
} else { |
3aeea5b92
|
618 619 |
cyy_writeb(info, CySRER, CyTxMpty | (cyy_readb(info, CySRER) & ~CyTxRdy)); |
02f1175c8
|
620 |
} |
ce97a0976
|
621 622 |
goto done; } |
77451e53e
|
623 |
if (info->port.xmit_buf == NULL) { |
3aeea5b92
|
624 625 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); |
ce97a0976
|
626 627 |
goto done; } |
d13549f80
|
628 |
if (tty->stopped || tty->hw_stopped) { |
3aeea5b92
|
629 630 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); |
ce97a0976
|
631 632 633 634 635 636 637 638 639 640 641 |
goto done; } /* Because the Embedded Transmit Commands have been enabled, * we must check to see if the escape character, NULL, is being * sent. If it is, we must ensure that there is room for it to * be doubled in the output stream. Therefore we no longer * advance the pointer when the character is fetched, but * rather wait until after the check for a NULL output * character. This is necessary because there may not be room * for the two chars needed to send a NULL.) */ |
77451e53e
|
642 |
outch = info->port.xmit_buf[info->xmit_tail]; |
ce97a0976
|
643 644 645 646 |
if (outch) { info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); |
3aeea5b92
|
647 |
cyy_writeb(info, CyTDR, outch); |
ce97a0976
|
648 649 650 |
info->icount.tx++; } else { if (char_count > 1) { |
02f1175c8
|
651 652 |
info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & |
ce97a0976
|
653 |
(SERIAL_XMIT_SIZE - 1); |
3aeea5b92
|
654 655 |
cyy_writeb(info, CyTDR, outch); cyy_writeb(info, CyTDR, 0); |
02f1175c8
|
656 |
info->icount.tx++; |
ce97a0976
|
657 |
char_count--; |
02f1175c8
|
658 |
} |
e941027f5
|
659 |
} |
e941027f5
|
660 |
} |
ce97a0976
|
661 |
done: |
d13549f80
|
662 663 |
tty_wakeup(tty); tty_kref_put(tty); |
ce97a0976
|
664 665 |
end: /* end of service */ |
3aeea5b92
|
666 667 |
cyy_writeb(info, CyTIR, save_xir & 0x3f); cyy_writeb(info, CyCAR, save_car); |
ce97a0976
|
668 |
} |
02f1175c8
|
669 |
|
ce97a0976
|
670 671 672 673 |
static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, void __iomem *base_addr) { struct cyclades_port *info; |
d13549f80
|
674 |
struct tty_struct *tty; |
65f76a82e
|
675 676 |
int index = cinfo->bus_index; u8 save_xir, channel, save_car, mdm_change, mdm_status; |
ce97a0976
|
677 678 |
/* determine the channel & change to that context */ |
65f76a82e
|
679 680 |
save_xir = readb(base_addr + (CyMIR << index)); channel = save_xir & CyIRChannel; |
ce97a0976
|
681 |
info = &cinfo->ports[channel + chip * 4]; |
3aeea5b92
|
682 683 |
save_car = cyy_readb(info, CyCAR); cyy_writeb(info, CyCAR, save_xir); |
ce97a0976
|
684 |
|
3aeea5b92
|
685 686 |
mdm_change = cyy_readb(info, CyMISR); mdm_status = cyy_readb(info, CyMSVR1); |
ce97a0976
|
687 |
|
d13549f80
|
688 689 |
tty = tty_port_tty_get(&info->port); if (!tty) |
ce97a0976
|
690 691 692 693 694 695 696 697 698 699 700 701 |
goto end; if (mdm_change & CyANY_DELTA) { /* For statistics only */ if (mdm_change & CyDCD) info->icount.dcd++; if (mdm_change & CyCTS) info->icount.cts++; if (mdm_change & CyDSR) info->icount.dsr++; if (mdm_change & CyRI) info->icount.rng++; |
bdc04e317
|
702 |
wake_up_interruptible(&info->port.delta_msr_wait); |
ce97a0976
|
703 |
} |
77451e53e
|
704 |
if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { |
174e6fe01
|
705 706 707 |
if (mdm_status & CyDCD) wake_up_interruptible(&info->port.open_wait); else |
d13549f80
|
708 |
tty_hangup(tty); |
ce97a0976
|
709 |
} |
77451e53e
|
710 |
if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { |
d13549f80
|
711 |
if (tty->hw_stopped) { |
ce97a0976
|
712 713 714 |
if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ |
d13549f80
|
715 |
tty->hw_stopped = 0; |
3aeea5b92
|
716 717 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); |
d13549f80
|
718 |
tty_wakeup(tty); |
02f1175c8
|
719 |
} |
ce97a0976
|
720 721 722 723 |
} else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ |
d13549f80
|
724 |
tty->hw_stopped = 1; |
3aeea5b92
|
725 726 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); |
02f1175c8
|
727 |
} |
e941027f5
|
728 |
} |
e941027f5
|
729 |
} |
ce97a0976
|
730 731 732 733 |
/* if (mdm_change & CyDSR) { } if (mdm_change & CyRI) { }*/ |
d13549f80
|
734 |
tty_kref_put(tty); |
ce97a0976
|
735 736 |
end: /* end of service */ |
3aeea5b92
|
737 738 |
cyy_writeb(info, CyMIR, save_xir & 0x3f); cyy_writeb(info, CyCAR, save_car); |
e941027f5
|
739 |
} |
1da177e4c
|
740 741 742 743 |
/* The real interrupt service routine is called whenever the card wants its hand held--chars received, out buffer empty, modem change, etc. */ |
02f1175c8
|
744 |
static irqreturn_t cyy_interrupt(int irq, void *dev_id) |
1da177e4c
|
745 |
{ |
02f1175c8
|
746 |
int status; |
f74290342
|
747 |
struct cyclades_card *cinfo = dev_id; |
02f1175c8
|
748 |
void __iomem *base_addr, *card_base_addr; |
65f76a82e
|
749 |
unsigned int chip, too_many, had_work; |
02f1175c8
|
750 |
int index; |
02f1175c8
|
751 |
|
f74290342
|
752 |
if (unlikely(cinfo == NULL)) { |
1da177e4c
|
753 |
#ifdef CY_DEBUG_INTERRUPTS |
15ed6cc0b
|
754 755 756 |
printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d ", irq); |
1da177e4c
|
757 |
#endif |
02f1175c8
|
758 759 760 761 762 |
return IRQ_NONE; /* spurious interrupt */ } card_base_addr = cinfo->base_addr; index = cinfo->bus_index; |
f1e83c6c1
|
763 764 765 |
/* card was not initialized yet (e.g. DEBUG_SHIRQ) */ if (unlikely(card_base_addr == NULL)) return IRQ_HANDLED; |
02f1175c8
|
766 767 768 769 770 771 772 773 774 775 776 |
/* This loop checks all chips in the card. Make a note whenever _any_ chip had some work to do, as this is considered an indication that there will be more to do. Only when no chip has any work does this outermost loop exit. */ do { had_work = 0; for (chip = 0; chip < cinfo->num_chips; chip++) { base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); too_many = 0; |
db05c3b1d
|
777 |
while ((status = readb(base_addr + |
02f1175c8
|
778 779 780 781 782 783 784 |
(CySVRR << index))) != 0x00) { had_work++; /* The purpose of the following test is to ensure that no chip can monopolize the driver. This forces the chips to be checked in a round-robin fashion (after draining each of a bunch (1000) of characters). */ |
ce97a0976
|
785 |
if (1000 < too_many++) |
02f1175c8
|
786 |
break; |
1c0a387c1
|
787 |
spin_lock(&cinfo->card_lock); |
ce97a0976
|
788 789 790 791 792 793 |
if (status & CySRReceive) /* rx intr */ cyy_chip_rx(cinfo, chip, base_addr); if (status & CySRTransmit) /* tx intr */ cyy_chip_tx(cinfo, chip, base_addr); if (status & CySRModem) /* modem intr */ cyy_chip_modem(cinfo, chip, base_addr); |
1c0a387c1
|
794 |
spin_unlock(&cinfo->card_lock); |
02f1175c8
|
795 796 797 798 799 800 801 802 803 804 805 |
} } } while (had_work); /* clear interrupts */ spin_lock(&cinfo->card_lock); cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0); /* Cy_ClrIntr is 0x1800 */ spin_unlock(&cinfo->card_lock); return IRQ_HANDLED; } /* cyy_interrupt */ |
1da177e4c
|
806 |
|
4d7682005
|
807 808 809 810 |
static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, unsigned int clear) { struct cyclades_card *card = info->card; |
3aeea5b92
|
811 |
int channel = info->line - card->first_line; |
0d3487294
|
812 |
u32 rts, dtr, msvrr, msvrd; |
4d7682005
|
813 |
|
4d7682005
|
814 |
channel &= 0x03; |
4d7682005
|
815 |
|
0d3487294
|
816 817 818 819 820 821 822 823 824 825 826 |
if (info->rtsdtr_inv) { msvrr = CyMSVR2; msvrd = CyMSVR1; rts = CyDTR; dtr = CyRTS; } else { msvrr = CyMSVR1; msvrd = CyMSVR2; rts = CyRTS; dtr = CyDTR; } |
4d7682005
|
827 |
if (set & TIOCM_RTS) { |
3aeea5b92
|
828 829 |
cyy_writeb(info, CyCAR, channel); cyy_writeb(info, msvrr, rts); |
4d7682005
|
830 831 |
} if (clear & TIOCM_RTS) { |
3aeea5b92
|
832 833 |
cyy_writeb(info, CyCAR, channel); cyy_writeb(info, msvrr, ~rts); |
4d7682005
|
834 835 |
} if (set & TIOCM_DTR) { |
3aeea5b92
|
836 837 |
cyy_writeb(info, CyCAR, channel); cyy_writeb(info, msvrd, dtr); |
4d7682005
|
838 839 840 841 842 |
#ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info raising DTR "); printk(KERN_DEBUG " status: 0x%x, 0x%x ", |
3aeea5b92
|
843 844 |
cyy_readb(info, CyMSVR1), cyy_readb(info, CyMSVR2)); |
4d7682005
|
845 846 847 |
#endif } if (clear & TIOCM_DTR) { |
3aeea5b92
|
848 849 |
cyy_writeb(info, CyCAR, channel); cyy_writeb(info, msvrd, ~dtr); |
4d7682005
|
850 851 852 853 854 |
#ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info dropping DTR "); printk(KERN_DEBUG " status: 0x%x, 0x%x ", |
3aeea5b92
|
855 856 |
cyy_readb(info, CyMSVR1), cyy_readb(info, CyMSVR2)); |
4d7682005
|
857 858 859 |
#endif } } |
1da177e4c
|
860 861 |
/***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ |
15ed6cc0b
|
862 |
/******** Start of block of Cyclades-Z specific code *******/ |
1da177e4c
|
863 864 865 |
/***********************************************************/ static int |
02f1175c8
|
866 |
cyz_fetch_msg(struct cyclades_card *cinfo, |
15ed6cc0b
|
867 |
__u32 *channel, __u8 *cmd, __u32 *param) |
1da177e4c
|
868 |
{ |
f0eefdc30
|
869 |
struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; |
02f1175c8
|
870 |
unsigned long loc_doorbell; |
97e87f8eb
|
871 |
loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); |
02f1175c8
|
872 873 |
if (loc_doorbell) { *cmd = (char)(0xff & loc_doorbell); |
db05c3b1d
|
874 875 |
*channel = readl(&board_ctrl->fwcmd_channel); *param = (__u32) readl(&board_ctrl->fwcmd_param); |
97e87f8eb
|
876 |
cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff); |
02f1175c8
|
877 878 879 880 |
return 1; } return 0; } /* cyz_fetch_msg */ |
1da177e4c
|
881 882 |
static int |
02f1175c8
|
883 |
cyz_issue_cmd(struct cyclades_card *cinfo, |
1a86b5e34
|
884 |
__u32 channel, __u8 cmd, __u32 param) |
1da177e4c
|
885 |
{ |
f0eefdc30
|
886 |
struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; |
1a86b5e34
|
887 |
__u32 __iomem *pci_doorbell; |
65f76a82e
|
888 |
unsigned int index; |
02f1175c8
|
889 |
|
2693f485c
|
890 |
if (!cyz_is_loaded(cinfo)) |
096dcfce3
|
891 |
return -1; |
15ed6cc0b
|
892 |
|
02f1175c8
|
893 |
index = 0; |
97e87f8eb
|
894 |
pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; |
db05c3b1d
|
895 |
while ((readl(pci_doorbell) & 0xff) != 0) { |
15ed6cc0b
|
896 |
if (index++ == 1000) |
db05c3b1d
|
897 |
return (int)(readl(pci_doorbell) & 0xff); |
02f1175c8
|
898 899 900 901 902 |
udelay(50L); } cy_writel(&board_ctrl->hcmd_channel, channel); cy_writel(&board_ctrl->hcmd_param, param); cy_writel(pci_doorbell, (long)cmd); |
096dcfce3
|
903 |
return 0; |
02f1175c8
|
904 |
} /* cyz_issue_cmd */ |
1da177e4c
|
905 |
|
f0eefdc30
|
906 |
static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) |
1da177e4c
|
907 |
{ |
f0eefdc30
|
908 |
struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; |
875b206b5
|
909 |
struct cyclades_card *cinfo = info->card; |
65f76a82e
|
910 |
unsigned int char_count; |
02f1175c8
|
911 |
int len; |
1da177e4c
|
912 |
#ifdef BLOCKMOVE |
ce71b0ffd
|
913 |
unsigned char *buf; |
1da177e4c
|
914 |
#else |
02f1175c8
|
915 |
char data; |
1da177e4c
|
916 |
#endif |
ad39c3004
|
917 |
__u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; |
1da177e4c
|
918 |
|
db05c3b1d
|
919 920 921 922 |
rx_get = new_rx_get = readl(&buf_ctrl->rx_get); rx_put = readl(&buf_ctrl->rx_put); rx_bufsize = readl(&buf_ctrl->rx_bufsize); rx_bufaddr = readl(&buf_ctrl->rx_bufaddr); |
02f1175c8
|
923 924 925 926 |
if (rx_put >= rx_get) char_count = rx_put - rx_get; else char_count = rx_put - rx_get + rx_bufsize; |
1da177e4c
|
927 |
|
02f1175c8
|
928 |
if (char_count) { |
1da177e4c
|
929 |
#ifdef CY_ENABLE_MONITORING |
02f1175c8
|
930 931 932 933 934 |
info->mon.int_count++; info->mon.char_count += char_count; if (char_count > info->mon.char_max) info->mon.char_max = char_count; info->mon.char_last = char_count; |
1da177e4c
|
935 |
#endif |
f74290342
|
936 |
if (tty == NULL) { |
02f1175c8
|
937 938 939 940 941 |
/* flush received characters */ new_rx_get = (new_rx_get + char_count) & (rx_bufsize - 1); info->rflush_count++; } else { |
1da177e4c
|
942 |
#ifdef BLOCKMOVE |
02f1175c8
|
943 944 945 |
/* we'd like to use memcpy(t, f, n) and memset(s, c, count) for performance, but because of buffer boundaries, there may be several steps to the operation */ |
ce71b0ffd
|
946 947 948 949 950 951 952 953 954 955 956 957 958 |
while (1) { len = tty_prepare_flip_string(tty, &buf, char_count); if (!len) break; len = min_t(unsigned int, min(len, char_count), rx_bufsize - new_rx_get); memcpy_fromio(buf, cinfo->base_addr + rx_bufaddr + new_rx_get, len); new_rx_get = (new_rx_get + len) & |
02f1175c8
|
959 |
(rx_bufsize - 1); |
ce71b0ffd
|
960 961 962 |
char_count -= len; info->icount.rx += len; info->idle_stats.recv_bytes += len; |
02f1175c8
|
963 |
} |
1da177e4c
|
964 |
#else |
02f1175c8
|
965 966 |
len = tty_buffer_request_room(tty, char_count); while (len--) { |
db05c3b1d
|
967 |
data = readb(cinfo->base_addr + rx_bufaddr + |
02f1175c8
|
968 |
new_rx_get); |
15ed6cc0b
|
969 970 |
new_rx_get = (new_rx_get + 1) & (rx_bufsize - 1); |
02f1175c8
|
971 972 973 974 |
tty_insert_flip_char(tty, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; } |
1da177e4c
|
975 976 |
#endif #ifdef CONFIG_CYZ_INTR |
02f1175c8
|
977 978 |
/* Recalculate the number of chars in the RX buffer and issue a cmd in case it's higher than the RX high water mark */ |
db05c3b1d
|
979 |
rx_put = readl(&buf_ctrl->rx_put); |
02f1175c8
|
980 981 982 983 |
if (rx_put >= rx_get) char_count = rx_put - rx_get; else char_count = rx_put - rx_get + rx_bufsize; |
65f76a82e
|
984 |
if (char_count >= readl(&buf_ctrl->rx_threshold) && |
ebafeeff0
|
985 986 987 988 |
!timer_pending(&cyz_rx_full_timer[ info->line])) mod_timer(&cyz_rx_full_timer[info->line], jiffies + 1); |
1da177e4c
|
989 |
#endif |
02f1175c8
|
990 991 992 993 994 |
info->idle_stats.recv_idle = jiffies; tty_schedule_flip(tty); } /* Update rx_get */ cy_writel(&buf_ctrl->rx_get, new_rx_get); |
1da177e4c
|
995 |
} |
1da177e4c
|
996 |
} |
f0eefdc30
|
997 |
static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) |
1da177e4c
|
998 |
{ |
f0eefdc30
|
999 |
struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; |
875b206b5
|
1000 |
struct cyclades_card *cinfo = info->card; |
65f76a82e
|
1001 1002 |
u8 data; unsigned int char_count; |
1da177e4c
|
1003 |
#ifdef BLOCKMOVE |
02f1175c8
|
1004 |
int small_count; |
1da177e4c
|
1005 |
#endif |
ad39c3004
|
1006 |
__u32 tx_put, tx_get, tx_bufsize, tx_bufaddr; |
1da177e4c
|
1007 |
|
02f1175c8
|
1008 1009 |
if (info->xmit_cnt <= 0) /* Nothing to transmit */ return; |
1da177e4c
|
1010 |
|
db05c3b1d
|
1011 1012 1013 1014 |
tx_get = readl(&buf_ctrl->tx_get); tx_put = readl(&buf_ctrl->tx_put); tx_bufsize = readl(&buf_ctrl->tx_bufsize); tx_bufaddr = readl(&buf_ctrl->tx_bufaddr); |
02f1175c8
|
1015 1016 1017 1018 |
if (tx_put >= tx_get) char_count = tx_get - tx_put - 1 + tx_bufsize; else char_count = tx_get - tx_put - 1; |
1da177e4c
|
1019 |
|
02f1175c8
|
1020 |
if (char_count) { |
1da177e4c
|
1021 |
|
f74290342
|
1022 |
if (tty == NULL) |
02f1175c8
|
1023 |
goto ztxdone; |
1da177e4c
|
1024 |
|
02f1175c8
|
1025 1026 |
if (info->x_char) { /* send special char */ data = info->x_char; |
1da177e4c
|
1027 |
|
02f1175c8
|
1028 1029 1030 1031 1032 |
cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); tx_put = (tx_put + 1) & (tx_bufsize - 1); info->x_char = 0; char_count--; info->icount.tx++; |
02f1175c8
|
1033 |
} |
1da177e4c
|
1034 |
#ifdef BLOCKMOVE |
02f1175c8
|
1035 1036 1037 1038 1039 1040 1041 1042 |
while (0 < (small_count = min_t(unsigned int, tx_bufsize - tx_put, min_t(unsigned int, (SERIAL_XMIT_SIZE - info->xmit_tail), min_t(unsigned int, info->xmit_cnt, char_count))))) { memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), |
77451e53e
|
1043 |
&info->port.xmit_buf[info->xmit_tail], |
02f1175c8
|
1044 1045 1046 1047 1048 1049 1050 1051 |
small_count); tx_put = (tx_put + small_count) & (tx_bufsize - 1); char_count -= small_count; info->icount.tx += small_count; info->xmit_cnt -= small_count; info->xmit_tail = (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); |
02f1175c8
|
1052 |
} |
1da177e4c
|
1053 |
#else |
02f1175c8
|
1054 |
while (info->xmit_cnt && char_count) { |
77451e53e
|
1055 |
data = info->port.xmit_buf[info->xmit_tail]; |
02f1175c8
|
1056 1057 1058 1059 1060 1061 1062 1063 |
info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); tx_put = (tx_put + 1) & (tx_bufsize - 1); char_count--; info->icount.tx++; |
02f1175c8
|
1064 |
} |
1da177e4c
|
1065 |
#endif |
ebafeeff0
|
1066 |
tty_wakeup(tty); |
7fa57a0cd
|
1067 |
ztxdone: |
02f1175c8
|
1068 1069 |
/* Update tx_put */ cy_writel(&buf_ctrl->tx_put, tx_put); |
1da177e4c
|
1070 |
} |
1da177e4c
|
1071 |
} |
02f1175c8
|
1072 |
static void cyz_handle_cmd(struct cyclades_card *cinfo) |
1da177e4c
|
1073 |
{ |
f0eefdc30
|
1074 |
struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; |
02f1175c8
|
1075 1076 |
struct tty_struct *tty; struct cyclades_port *info; |
101b81590
|
1077 |
__u32 channel, param, fw_ver; |
1a86b5e34
|
1078 |
__u8 cmd; |
02f1175c8
|
1079 1080 |
int special_count; int delta_count; |
db05c3b1d
|
1081 |
fw_ver = readl(&board_ctrl->fw_version); |
02f1175c8
|
1082 1083 1084 1085 |
while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { special_count = 0; delta_count = 0; |
dd025c0c7
|
1086 |
info = &cinfo->ports[channel]; |
d13549f80
|
1087 |
tty = tty_port_tty_get(&info->port); |
15ed6cc0b
|
1088 |
if (tty == NULL) |
02f1175c8
|
1089 |
continue; |
f74290342
|
1090 |
|
02f1175c8
|
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 |
switch (cmd) { case C_CM_PR_ERROR: tty_insert_flip_char(tty, 0, TTY_PARITY); info->icount.rx++; special_count++; break; case C_CM_FR_ERROR: tty_insert_flip_char(tty, 0, TTY_FRAME); info->icount.rx++; special_count++; break; case C_CM_RXBRK: tty_insert_flip_char(tty, 0, TTY_BREAK); info->icount.rx++; special_count++; break; case C_CM_MDCD: info->icount.dcd++; delta_count++; |
77451e53e
|
1110 |
if (info->port.flags & ASYNC_CHECK_CD) { |
f0eefdc30
|
1111 1112 |
u32 dcd = fw_ver > 241 ? param : readl(&info->u.cyz.ch_ctrl->rs_status); |
174e6fe01
|
1113 |
if (dcd & C_RS_DCD) |
77451e53e
|
1114 |
wake_up_interruptible(&info->port.open_wait); |
174e6fe01
|
1115 |
else |
d13549f80
|
1116 |
tty_hangup(tty); |
02f1175c8
|
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 |
} break; case C_CM_MCTS: info->icount.cts++; delta_count++; break; case C_CM_MRI: info->icount.rng++; delta_count++; break; case C_CM_MDSR: info->icount.dsr++; delta_count++; break; |
1da177e4c
|
1131 |
#ifdef Z_WAKE |
02f1175c8
|
1132 |
case C_CM_IOCTLW: |
ebafeeff0
|
1133 |
complete(&info->shutdown_wait); |
02f1175c8
|
1134 |
break; |
1da177e4c
|
1135 1136 |
#endif #ifdef CONFIG_CYZ_INTR |
02f1175c8
|
1137 1138 1139 1140 |
case C_CM_RXHIWM: case C_CM_RXNNDT: case C_CM_INTBACK2: /* Reception Interrupt */ |
1da177e4c
|
1141 |
#ifdef CY_DEBUG_INTERRUPTS |
217191910
|
1142 1143 1144 |
printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " "port %ld ", info->card, channel); |
1da177e4c
|
1145 |
#endif |
f0eefdc30
|
1146 |
cyz_handle_rx(info, tty); |
02f1175c8
|
1147 1148 1149 1150 1151 |
break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: case C_CM_INTBACK: /* Transmission Interrupt */ |
1da177e4c
|
1152 |
#ifdef CY_DEBUG_INTERRUPTS |
217191910
|
1153 1154 1155 |
printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " "port %ld ", info->card, channel); |
1da177e4c
|
1156 |
#endif |
f0eefdc30
|
1157 |
cyz_handle_tx(info, tty); |
02f1175c8
|
1158 1159 1160 1161 1162 1163 1164 1165 1166 |
break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: /* should do something with this !!! */ break; default: break; } if (delta_count) |
bdc04e317
|
1167 |
wake_up_interruptible(&info->port.delta_msr_wait); |
02f1175c8
|
1168 1169 |
if (special_count) tty_schedule_flip(tty); |
d13549f80
|
1170 |
tty_kref_put(tty); |
1da177e4c
|
1171 |
} |
1da177e4c
|
1172 1173 1174 |
} #ifdef CONFIG_CYZ_INTR |
02f1175c8
|
1175 |
static irqreturn_t cyz_interrupt(int irq, void *dev_id) |
1da177e4c
|
1176 |
{ |
f74290342
|
1177 |
struct cyclades_card *cinfo = dev_id; |
1da177e4c
|
1178 |
|
2693f485c
|
1179 |
if (unlikely(!cyz_is_loaded(cinfo))) { |
1da177e4c
|
1180 |
#ifdef CY_DEBUG_INTERRUPTS |
217191910
|
1181 1182 1183 |
printk(KERN_DEBUG "cyz_interrupt: board not yet loaded " "(IRQ%d). ", irq); |
1da177e4c
|
1184 |
#endif |
02f1175c8
|
1185 1186 |
return IRQ_NONE; } |
1da177e4c
|
1187 |
|
02f1175c8
|
1188 1189 |
/* Handle the interrupts */ cyz_handle_cmd(cinfo); |
1da177e4c
|
1190 |
|
02f1175c8
|
1191 1192 |
return IRQ_HANDLED; } /* cyz_interrupt */ |
1da177e4c
|
1193 |
|
02f1175c8
|
1194 |
static void cyz_rx_restart(unsigned long arg) |
1da177e4c
|
1195 |
{ |
02f1175c8
|
1196 |
struct cyclades_port *info = (struct cyclades_port *)arg; |
875b206b5
|
1197 |
struct cyclades_card *card = info->card; |
02f1175c8
|
1198 |
int retval; |
875b206b5
|
1199 |
__u32 channel = info->line - card->first_line; |
02f1175c8
|
1200 |
unsigned long flags; |
9fa1b3b18
|
1201 |
spin_lock_irqsave(&card->card_lock, flags); |
875b206b5
|
1202 |
retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L); |
02f1175c8
|
1203 |
if (retval != 0) { |
217191910
|
1204 1205 |
printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x ", |
02f1175c8
|
1206 1207 |
info->line, retval); } |
9fa1b3b18
|
1208 |
spin_unlock_irqrestore(&card->card_lock, flags); |
1da177e4c
|
1209 |
} |
02f1175c8
|
1210 |
#else /* CONFIG_CYZ_INTR */ |
1da177e4c
|
1211 |
|
02f1175c8
|
1212 |
static void cyz_poll(unsigned long arg) |
1da177e4c
|
1213 |
{ |
02f1175c8
|
1214 1215 |
struct cyclades_card *cinfo; struct cyclades_port *info; |
b70509066
|
1216 |
unsigned long expires = jiffies + HZ; |
65f76a82e
|
1217 |
unsigned int port, card; |
1da177e4c
|
1218 |
|
02f1175c8
|
1219 1220 |
for (card = 0; card < NR_CARDS; card++) { cinfo = &cy_card[card]; |
2693f485c
|
1221 |
if (!cy_is_Z(cinfo)) |
02f1175c8
|
1222 |
continue; |
2693f485c
|
1223 |
if (!cyz_is_loaded(cinfo)) |
02f1175c8
|
1224 |
continue; |
1da177e4c
|
1225 |
/* Skip first polling cycle to avoid racing conditions with the FW */ |
02f1175c8
|
1226 |
if (!cinfo->intr_enabled) { |
02f1175c8
|
1227 1228 1229 |
cinfo->intr_enabled = 1; continue; } |
1da177e4c
|
1230 |
|
02f1175c8
|
1231 |
cyz_handle_cmd(cinfo); |
1da177e4c
|
1232 |
|
02f1175c8
|
1233 |
for (port = 0; port < cinfo->nports; port++) { |
d13549f80
|
1234 |
struct tty_struct *tty; |
dd025c0c7
|
1235 |
info = &cinfo->ports[port]; |
d13549f80
|
1236 1237 1238 |
tty = tty_port_tty_get(&info->port); /* OK to pass NULL to the handle functions below. They need to drop the data in that case. */ |
02f1175c8
|
1239 |
if (!info->throttle) |
f0eefdc30
|
1240 1241 |
cyz_handle_rx(info, tty); cyz_handle_tx(info, tty); |
d13549f80
|
1242 |
tty_kref_put(tty); |
02f1175c8
|
1243 1244 |
} /* poll every 'cyz_polling_cycle' period */ |
b70509066
|
1245 |
expires = jiffies + cyz_polling_cycle; |
1da177e4c
|
1246 |
} |
b70509066
|
1247 |
mod_timer(&cyz_timerlist, expires); |
02f1175c8
|
1248 |
} /* cyz_poll */ |
1da177e4c
|
1249 |
|
02f1175c8
|
1250 |
#endif /* CONFIG_CYZ_INTR */ |
1da177e4c
|
1251 1252 1253 |
/********** End of block of Cyclades-Z specific code *********/ /***********************************************************/ |
1da177e4c
|
1254 1255 1256 |
/* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */ |
d13549f80
|
1257 |
static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) |
1da177e4c
|
1258 |
{ |
875b206b5
|
1259 |
struct cyclades_card *card; |
02f1175c8
|
1260 1261 |
unsigned long flags; int retval = 0; |
cc7fdf49d
|
1262 |
int channel; |
02f1175c8
|
1263 |
unsigned long page; |
1da177e4c
|
1264 |
|
02f1175c8
|
1265 |
card = info->card; |
875b206b5
|
1266 |
channel = info->line - card->first_line; |
1da177e4c
|
1267 |
|
02f1175c8
|
1268 1269 1270 |
page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; |
1da177e4c
|
1271 |
|
9fa1b3b18
|
1272 |
spin_lock_irqsave(&card->card_lock, flags); |
1da177e4c
|
1273 |
|
cc7fdf49d
|
1274 |
if (info->port.flags & ASYNC_INITIALIZED) |
02f1175c8
|
1275 |
goto errout; |
1da177e4c
|
1276 |
|
02f1175c8
|
1277 |
if (!info->type) { |
d13549f80
|
1278 |
set_bit(TTY_IO_ERROR, &tty->flags); |
02f1175c8
|
1279 1280 |
goto errout; } |
1da177e4c
|
1281 |
|
77451e53e
|
1282 |
if (info->port.xmit_buf) |
02f1175c8
|
1283 1284 |
free_page(page); else |
77451e53e
|
1285 |
info->port.xmit_buf = (unsigned char *)page; |
1da177e4c
|
1286 |
|
9fa1b3b18
|
1287 |
spin_unlock_irqrestore(&card->card_lock, flags); |
1da177e4c
|
1288 |
|
d13549f80
|
1289 |
cy_set_line_char(info, tty); |
1da177e4c
|
1290 |
|
2693f485c
|
1291 |
if (!cy_is_Z(card)) { |
02f1175c8
|
1292 |
channel &= 0x03; |
1da177e4c
|
1293 |
|
9fa1b3b18
|
1294 |
spin_lock_irqsave(&card->card_lock, flags); |
1da177e4c
|
1295 |
|
3aeea5b92
|
1296 |
cyy_writeb(info, CyCAR, channel); |
1da177e4c
|
1297 |
|
3aeea5b92
|
1298 |
cyy_writeb(info, CyRTPR, |
02f1175c8
|
1299 1300 |
(info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ |
1da177e4c
|
1301 |
|
3aeea5b92
|
1302 |
cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); |
1da177e4c
|
1303 |
|
4d7682005
|
1304 |
cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); |
1da177e4c
|
1305 |
|
3aeea5b92
|
1306 |
cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); |
02f1175c8
|
1307 |
} else { |
f0eefdc30
|
1308 |
struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; |
1da177e4c
|
1309 |
|
2693f485c
|
1310 |
if (!cyz_is_loaded(card)) |
02f1175c8
|
1311 |
return -ENODEV; |
1da177e4c
|
1312 |
|
1da177e4c
|
1313 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1314 |
printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " |
f0eefdc30
|
1315 1316 |
"base_addr %p ", card, channel, card->base_addr); |
1da177e4c
|
1317 |
#endif |
9fa1b3b18
|
1318 |
spin_lock_irqsave(&card->card_lock, flags); |
1da177e4c
|
1319 |
|
f0eefdc30
|
1320 |
cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); |
1da177e4c
|
1321 1322 |
#ifdef Z_WAKE #ifdef CONFIG_CYZ_INTR |
f0eefdc30
|
1323 |
cy_writel(&ch_ctrl->intr_enable, |
02f1175c8
|
1324 1325 |
C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); |
1da177e4c
|
1326 |
#else |
f0eefdc30
|
1327 |
cy_writel(&ch_ctrl->intr_enable, |
02f1175c8
|
1328 1329 |
C_IN_IOCTLW | C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ |
1da177e4c
|
1330 1331 |
#else #ifdef CONFIG_CYZ_INTR |
f0eefdc30
|
1332 |
cy_writel(&ch_ctrl->intr_enable, |
02f1175c8
|
1333 1334 |
C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_MDCD); |
1da177e4c
|
1335 |
#else |
f0eefdc30
|
1336 |
cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); |
02f1175c8
|
1337 1338 |
#endif /* CONFIG_CYZ_INTR */ #endif /* Z_WAKE */ |
875b206b5
|
1339 |
retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); |
02f1175c8
|
1340 |
if (retval != 0) { |
217191910
|
1341 1342 1343 |
printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was " "%x ", info->line, retval); |
02f1175c8
|
1344 |
} |
1da177e4c
|
1345 |
|
02f1175c8
|
1346 |
/* Flush RX buffers before raising DTR and RTS */ |
875b206b5
|
1347 |
retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L); |
02f1175c8
|
1348 |
if (retval != 0) { |
217191910
|
1349 1350 1351 |
printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was " "%x ", info->line, retval); |
02f1175c8
|
1352 |
} |
1da177e4c
|
1353 |
|
02f1175c8
|
1354 1355 |
/* set timeout !!! */ /* set RTS and DTR !!! */ |
4d7682005
|
1356 |
tty_port_raise_dtr_rts(&info->port); |
1da177e4c
|
1357 |
|
02f1175c8
|
1358 |
/* enable send, recv, modem !!! */ |
cc7fdf49d
|
1359 |
} |
02f1175c8
|
1360 |
|
cc7fdf49d
|
1361 |
info->port.flags |= ASYNC_INITIALIZED; |
1da177e4c
|
1362 |
|
cc7fdf49d
|
1363 1364 1365 1366 1367 1368 1369 1370 1371 |
clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); info->idle_stats.in_use = info->idle_stats.recv_idle = info->idle_stats.xmit_idle = jiffies; spin_unlock_irqrestore(&card->card_lock, flags); |
1da177e4c
|
1372 1373 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1374 1375 |
printk(KERN_DEBUG "cyc startup done "); |
1da177e4c
|
1376 1377 1378 1379 |
#endif return 0; errout: |
9fa1b3b18
|
1380 |
spin_unlock_irqrestore(&card->card_lock, flags); |
cc7fdf49d
|
1381 |
free_page(page); |
1da177e4c
|
1382 |
return retval; |
02f1175c8
|
1383 |
} /* startup */ |
1da177e4c
|
1384 |
|
02f1175c8
|
1385 |
static void start_xmit(struct cyclades_port *info) |
1da177e4c
|
1386 |
{ |
3aeea5b92
|
1387 |
struct cyclades_card *card = info->card; |
02f1175c8
|
1388 |
unsigned long flags; |
3aeea5b92
|
1389 |
int channel = info->line - card->first_line; |
1da177e4c
|
1390 |
|
2693f485c
|
1391 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
1392 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
1393 1394 |
cyy_writeb(info, CyCAR, channel & 0x03); cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); |
9fa1b3b18
|
1395 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
1396 |
} else { |
1da177e4c
|
1397 |
#ifdef CONFIG_CYZ_INTR |
02f1175c8
|
1398 |
int retval; |
1da177e4c
|
1399 |
|
9fa1b3b18
|
1400 |
spin_lock_irqsave(&card->card_lock, flags); |
875b206b5
|
1401 |
retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L); |
02f1175c8
|
1402 |
if (retval != 0) { |
217191910
|
1403 1404 1405 |
printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was " "%x ", info->line, retval); |
02f1175c8
|
1406 |
} |
9fa1b3b18
|
1407 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
1408 1409 1410 1411 1412 |
#else /* CONFIG_CYZ_INTR */ /* Don't have to do anything at this time */ #endif /* CONFIG_CYZ_INTR */ } } /* start_xmit */ |
1da177e4c
|
1413 1414 1415 1416 1417 |
/* * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. */ |
d13549f80
|
1418 |
static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) |
1da177e4c
|
1419 |
{ |
875b206b5
|
1420 |
struct cyclades_card *card; |
02f1175c8
|
1421 |
unsigned long flags; |
02f1175c8
|
1422 |
|
77451e53e
|
1423 |
if (!(info->port.flags & ASYNC_INITIALIZED)) |
02f1175c8
|
1424 |
return; |
02f1175c8
|
1425 1426 |
card = info->card; |
2693f485c
|
1427 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
1428 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
1429 1430 |
/* Clear delta_msr_wait queue to avoid mem leaks. */ |
bdc04e317
|
1431 |
wake_up_interruptible(&info->port.delta_msr_wait); |
1da177e4c
|
1432 |
|
77451e53e
|
1433 |
if (info->port.xmit_buf) { |
02f1175c8
|
1434 |
unsigned char *temp; |
77451e53e
|
1435 1436 |
temp = info->port.xmit_buf; info->port.xmit_buf = NULL; |
02f1175c8
|
1437 1438 |
free_page((unsigned long)temp); } |
4d7682005
|
1439 1440 |
if (tty->termios->c_cflag & HUPCL) cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); |
3aeea5b92
|
1441 |
cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); |
02f1175c8
|
1442 1443 |
/* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ |
d13549f80
|
1444 |
set_bit(TTY_IO_ERROR, &tty->flags); |
77451e53e
|
1445 |
info->port.flags &= ~ASYNC_INITIALIZED; |
9fa1b3b18
|
1446 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
1447 |
} else { |
1da177e4c
|
1448 |
#ifdef CY_DEBUG_OPEN |
0e7f4194a
|
1449 |
int channel = info->line - card->first_line; |
217191910
|
1450 |
printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " |
f0eefdc30
|
1451 1452 |
"base_addr %p ", card, channel, card->base_addr); |
1da177e4c
|
1453 |
#endif |
2693f485c
|
1454 |
if (!cyz_is_loaded(card)) |
02f1175c8
|
1455 |
return; |
1da177e4c
|
1456 |
|
9fa1b3b18
|
1457 |
spin_lock_irqsave(&card->card_lock, flags); |
1da177e4c
|
1458 |
|
77451e53e
|
1459 |
if (info->port.xmit_buf) { |
02f1175c8
|
1460 |
unsigned char *temp; |
77451e53e
|
1461 1462 |
temp = info->port.xmit_buf; info->port.xmit_buf = NULL; |
02f1175c8
|
1463 |
free_page((unsigned long)temp); |
1da177e4c
|
1464 |
} |
02f1175c8
|
1465 |
|
4d7682005
|
1466 1467 |
if (tty->termios->c_cflag & HUPCL) tty_port_lower_dtr_rts(&info->port); |
1da177e4c
|
1468 |
|
d13549f80
|
1469 |
set_bit(TTY_IO_ERROR, &tty->flags); |
77451e53e
|
1470 |
info->port.flags &= ~ASYNC_INITIALIZED; |
02f1175c8
|
1471 |
|
9fa1b3b18
|
1472 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
1473 |
} |
1da177e4c
|
1474 1475 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1476 1477 |
printk(KERN_DEBUG "cyc shutdown done "); |
1da177e4c
|
1478 |
#endif |
02f1175c8
|
1479 |
} /* shutdown */ |
1da177e4c
|
1480 1481 1482 1483 1484 1485 |
/* * ------------------------------------------------------------ * cy_open() and friends * ------------------------------------------------------------ */ |
1da177e4c
|
1486 1487 1488 1489 |
/* * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. */ |
02f1175c8
|
1490 |
static int cy_open(struct tty_struct *tty, struct file *filp) |
1da177e4c
|
1491 |
{ |
02f1175c8
|
1492 |
struct cyclades_port *info; |
65f76a82e
|
1493 1494 |
unsigned int i, line; int retval; |
1da177e4c
|
1495 |
|
02f1175c8
|
1496 |
line = tty->index; |
15ed6cc0b
|
1497 |
if (tty->index < 0 || NR_PORTS <= line) |
02f1175c8
|
1498 |
return -ENODEV; |
15ed6cc0b
|
1499 |
|
dd025c0c7
|
1500 1501 1502 1503 1504 1505 1506 |
for (i = 0; i < NR_CARDS; i++) if (line < cy_card[i].first_line + cy_card[i].nports && line >= cy_card[i].first_line) break; if (i >= NR_CARDS) return -ENODEV; info = &cy_card[i].ports[line - cy_card[i].first_line]; |
15ed6cc0b
|
1507 |
if (info->line < 0) |
02f1175c8
|
1508 |
return -ENODEV; |
1da177e4c
|
1509 |
|
02f1175c8
|
1510 1511 1512 1513 |
/* If the card's firmware hasn't been loaded, treat it as absent from the system. This will make the user pay attention. */ |
2693f485c
|
1514 |
if (cy_is_Z(info->card)) { |
875b206b5
|
1515 |
struct cyclades_card *cinfo = info->card; |
02f1175c8
|
1516 |
struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS; |
2693f485c
|
1517 1518 |
if (!cyz_is_loaded(cinfo)) { if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) && |
101b81590
|
1519 1520 |
readl(&firm_id->signature) == ZFIRM_HLT) { |
217191910
|
1521 1522 1523 1524 1525 1526 |
printk(KERN_ERR "cyc:Cyclades-Z Error: you " "need an external power supply for " "this number of ports. Firmware " "halted. "); |
02f1175c8
|
1527 |
} else { |
217191910
|
1528 1529 1530 |
printk(KERN_ERR "cyc:Cyclades-Z firmware not " "yet loaded "); |
02f1175c8
|
1531 1532 1533 1534 1535 1536 1537 1538 1539 |
} return -ENODEV; } #ifdef CONFIG_CYZ_INTR else { /* In case this Z board is operating in interrupt mode, its interrupts should be enabled as soon as the first open happens to one of its ports. */ if (!cinfo->intr_enabled) { |
97e87f8eb
|
1540 |
u16 intr; |
02f1175c8
|
1541 |
|
02f1175c8
|
1542 |
/* Enable interrupts on the PLX chip */ |
97e87f8eb
|
1543 1544 1545 1546 |
intr = readw(&cinfo->ctl_addr.p9060-> intr_ctrl_stat) | 0x0900; cy_writew(&cinfo->ctl_addr.p9060-> intr_ctrl_stat, intr); |
02f1175c8
|
1547 1548 1549 1550 |
/* Enable interrupts on the FW */ retval = cyz_issue_cmd(cinfo, 0, C_CM_IRQ_ENBL, 0L); if (retval != 0) { |
217191910
|
1551 1552 1553 |
printk(KERN_ERR "cyc:IRQ enable retval " "was %x ", retval); |
02f1175c8
|
1554 |
} |
02f1175c8
|
1555 1556 |
cinfo->intr_enabled = 1; } |
1da177e4c
|
1557 |
} |
02f1175c8
|
1558 1559 1560 1561 |
#endif /* CONFIG_CYZ_INTR */ /* Make sure this Z port really exists in hardware */ if (info->line > (cinfo->first_line + cinfo->nports - 1)) return -ENODEV; |
1da177e4c
|
1562 |
} |
1da177e4c
|
1563 |
#ifdef CY_DEBUG_OTHER |
217191910
|
1564 1565 |
printk(KERN_DEBUG "cyc:cy_open ttyC%d ", info->line); |
1da177e4c
|
1566 |
#endif |
02f1175c8
|
1567 |
tty->driver_data = info; |
15ed6cc0b
|
1568 |
if (serial_paranoia_check(info, tty->name, "cy_open")) |
02f1175c8
|
1569 |
return -ENODEV; |
15ed6cc0b
|
1570 |
|
1da177e4c
|
1571 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1572 1573 |
printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d ", info->line, |
77451e53e
|
1574 |
info->port.count); |
1da177e4c
|
1575 |
#endif |
77451e53e
|
1576 |
info->port.count++; |
1da177e4c
|
1577 |
#ifdef CY_DEBUG_COUNT |
217191910
|
1578 1579 |
printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d ", |
77451e53e
|
1580 |
current->pid, info->port.count); |
1da177e4c
|
1581 |
#endif |
1da177e4c
|
1582 |
|
02f1175c8
|
1583 1584 1585 |
/* * If the port is the middle of closing, bail out now */ |
77451e53e
|
1586 |
if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { |
be1bc2889
|
1587 |
wait_event_interruptible_tty(info->port.close_wait, |
77451e53e
|
1588 1589 |
!(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; |
02f1175c8
|
1590 |
} |
1da177e4c
|
1591 |
|
02f1175c8
|
1592 1593 1594 |
/* * Start up serial port */ |
d13549f80
|
1595 |
retval = cy_startup(info, tty); |
15ed6cc0b
|
1596 |
if (retval) |
02f1175c8
|
1597 |
return retval; |
1da177e4c
|
1598 |
|
f07375794
|
1599 |
retval = tty_port_block_til_ready(&info->port, tty, filp); |
02f1175c8
|
1600 |
if (retval) { |
1da177e4c
|
1601 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1602 1603 1604 |
printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " "with %d ", retval); |
1da177e4c
|
1605 |
#endif |
02f1175c8
|
1606 1607 |
return retval; } |
1da177e4c
|
1608 |
|
02f1175c8
|
1609 |
info->throttle = 0; |
d13549f80
|
1610 |
tty_port_tty_set(&info->port, tty); |
1da177e4c
|
1611 |
|
02f1175c8
|
1612 |
#ifdef CY_DEBUG_OPEN |
217191910
|
1613 1614 |
printk(KERN_DEBUG "cyc:cy_open done "); |
02f1175c8
|
1615 1616 1617 |
#endif return 0; } /* cy_open */ |
1da177e4c
|
1618 1619 1620 1621 |
/* * cy_wait_until_sent() --- wait until the transmitter is empty */ |
02f1175c8
|
1622 |
static void cy_wait_until_sent(struct tty_struct *tty, int timeout) |
1da177e4c
|
1623 |
{ |
875b206b5
|
1624 |
struct cyclades_card *card; |
cab9bdd14
|
1625 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
1626 1627 1628 1629 1630 1631 1632 1633 |
unsigned long orig_jiffies; int char_time; if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent")) return; if (info->xmit_fifo_size == 0) return; /* Just in case.... */ |
1da177e4c
|
1634 |
|
02f1175c8
|
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 |
orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. * * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time <= 0) char_time = 1; if (timeout < 0) timeout = 0; if (timeout) char_time = min(char_time, timeout); /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't * ever clear. This assumes the UART isn't doing flow * control, which is currently the case. Hence, if it ever * takes longer than info->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*info->timeout. */ if (!timeout || timeout > 2 * info->timeout) timeout = 2 * info->timeout; |
8bab534b5
|
1663 |
|
02f1175c8
|
1664 |
card = info->card; |
2693f485c
|
1665 |
if (!cy_is_Z(card)) { |
3aeea5b92
|
1666 |
while (cyy_readb(info, CySRER) & CyTxRdy) { |
02f1175c8
|
1667 1668 1669 1670 1671 1672 |
if (msleep_interruptible(jiffies_to_msecs(char_time))) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } |
1da177e4c
|
1673 |
} |
02f1175c8
|
1674 1675 |
/* Run one more char cycle */ msleep_interruptible(jiffies_to_msecs(char_time * 5)); |
1da177e4c
|
1676 |
} |
978e595f8
|
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 |
static void cy_flush_buffer(struct tty_struct *tty) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; int channel, retval; unsigned long flags; #ifdef CY_DEBUG_IO printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d ", info->line); #endif if (serial_paranoia_check(info, tty->name, "cy_flush_buffer")) return; card = info->card; channel = info->line - card->first_line; spin_lock_irqsave(&card->card_lock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&card->card_lock, flags); |
2693f485c
|
1698 |
if (cy_is_Z(card)) { /* If it is a Z card, flush the on-board |
978e595f8
|
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 |
buffers as well */ spin_lock_irqsave(&card->card_lock, flags); retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L); if (retval != 0) { printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d " "was %x ", info->line, retval); } spin_unlock_irqrestore(&card->card_lock, flags); } tty_wakeup(tty); } /* cy_flush_buffer */ |
e936ffd5c
|
1711 |
static void cy_do_close(struct tty_port *port) |
1da177e4c
|
1712 |
{ |
e936ffd5c
|
1713 1714 |
struct cyclades_port *info = container_of(port, struct cyclades_port, port); |
9fa1b3b18
|
1715 |
struct cyclades_card *card; |
02f1175c8
|
1716 |
unsigned long flags; |
3aeea5b92
|
1717 |
int channel; |
1da177e4c
|
1718 |
|
9fa1b3b18
|
1719 |
card = info->card; |
3aeea5b92
|
1720 |
channel = info->line - card->first_line; |
9fa1b3b18
|
1721 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
1722 |
|
2693f485c
|
1723 |
if (!cy_is_Z(card)) { |
02f1175c8
|
1724 |
/* Stop accepting input */ |
3aeea5b92
|
1725 1726 |
cyy_writeb(info, CyCAR, channel & 0x03); cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); |
77451e53e
|
1727 |
if (info->port.flags & ASYNC_INITIALIZED) { |
15ed6cc0b
|
1728 1729 |
/* Waiting for on-board buffers to be empty before closing the port */ |
9fa1b3b18
|
1730 |
spin_unlock_irqrestore(&card->card_lock, flags); |
e936ffd5c
|
1731 |
cy_wait_until_sent(port->tty, info->timeout); |
9fa1b3b18
|
1732 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
1733 1734 1735 |
} } else { #ifdef Z_WAKE |
15ed6cc0b
|
1736 1737 |
/* Waiting for on-board buffers to be empty before closing the port */ |
f0eefdc30
|
1738 |
struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; |
02f1175c8
|
1739 |
int retval; |
f0eefdc30
|
1740 |
if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { |
9fa1b3b18
|
1741 |
retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); |
02f1175c8
|
1742 |
if (retval != 0) { |
217191910
|
1743 1744 1745 |
printk(KERN_DEBUG "cyc:cy_close retval on " "ttyC%d was %x ", info->line, retval); |
02f1175c8
|
1746 |
} |
9fa1b3b18
|
1747 |
spin_unlock_irqrestore(&card->card_lock, flags); |
2c7fea992
|
1748 |
wait_for_completion_interruptible(&info->shutdown_wait); |
9fa1b3b18
|
1749 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
1750 |
} |
1da177e4c
|
1751 |
#endif |
02f1175c8
|
1752 |
} |
9fa1b3b18
|
1753 |
spin_unlock_irqrestore(&card->card_lock, flags); |
e936ffd5c
|
1754 1755 |
cy_shutdown(info, port->tty); } |
1da177e4c
|
1756 |
|
e936ffd5c
|
1757 1758 1759 1760 1761 1762 1763 1764 1765 |
/* * This routine is called when a particular tty device is closed. */ static void cy_close(struct tty_struct *tty, struct file *filp) { struct cyclades_port *info = tty->driver_data; if (!info || serial_paranoia_check(info, tty->name, "cy_close")) return; tty_port_close(&info->port, tty, filp); |
02f1175c8
|
1766 |
} /* cy_close */ |
1da177e4c
|
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 |
/* This routine gets called when tty_write has put something into * the write_queue. The characters may come from user space or * kernel space. * * This routine will return the number of characters actually * accepted for writing. * * If the port is not already transmitting stuff, start it off by * enabling interrupts. The interrupt service routine will then * ensure that the characters are sent. * If the port is already active, there is no need to kick it. * */ |
02f1175c8
|
1781 |
static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) |
1da177e4c
|
1782 |
{ |
cab9bdd14
|
1783 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
1784 1785 |
unsigned long flags; int c, ret = 0; |
1da177e4c
|
1786 1787 |
#ifdef CY_DEBUG_IO |
217191910
|
1788 1789 |
printk(KERN_DEBUG "cyc:cy_write ttyC%d ", info->line); |
1da177e4c
|
1790 |
#endif |
15ed6cc0b
|
1791 |
if (serial_paranoia_check(info, tty->name, "cy_write")) |
02f1175c8
|
1792 |
return 0; |
1da177e4c
|
1793 |
|
77451e53e
|
1794 |
if (!info->port.xmit_buf) |
02f1175c8
|
1795 |
return 0; |
1da177e4c
|
1796 |
|
9fa1b3b18
|
1797 |
spin_lock_irqsave(&info->card->card_lock, flags); |
02f1175c8
|
1798 |
while (1) { |
1a4e2351e
|
1799 1800 |
c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1)); c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head)); |
02f1175c8
|
1801 1802 1803 |
if (c <= 0) break; |
77451e53e
|
1804 |
memcpy(info->port.xmit_buf + info->xmit_head, buf, c); |
02f1175c8
|
1805 1806 1807 1808 1809 1810 1811 |
info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; buf += c; count -= c; ret += c; } |
9fa1b3b18
|
1812 |
spin_unlock_irqrestore(&info->card->card_lock, flags); |
02f1175c8
|
1813 1814 1815 |
info->idle_stats.xmit_bytes += ret; info->idle_stats.xmit_idle = jiffies; |
15ed6cc0b
|
1816 |
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) |
02f1175c8
|
1817 |
start_xmit(info); |
15ed6cc0b
|
1818 |
|
02f1175c8
|
1819 1820 |
return ret; } /* cy_write */ |
1da177e4c
|
1821 1822 1823 1824 1825 1826 1827 1828 |
/* * This routine is called by the kernel to write a single * character to the tty device. If the kernel uses this routine, * it must call the flush_chars() routine (if defined) when it is * done stuffing characters into the driver. If there is no room * in the queue, the character is ignored. */ |
76b25a550
|
1829 |
static int cy_put_char(struct tty_struct *tty, unsigned char ch) |
1da177e4c
|
1830 |
{ |
cab9bdd14
|
1831 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
1832 |
unsigned long flags; |
1da177e4c
|
1833 1834 |
#ifdef CY_DEBUG_IO |
217191910
|
1835 1836 |
printk(KERN_DEBUG "cyc:cy_put_char ttyC%d ", info->line); |
1da177e4c
|
1837 |
#endif |
02f1175c8
|
1838 |
if (serial_paranoia_check(info, tty->name, "cy_put_char")) |
76b25a550
|
1839 |
return 0; |
1da177e4c
|
1840 |
|
77451e53e
|
1841 |
if (!info->port.xmit_buf) |
76b25a550
|
1842 |
return 0; |
1da177e4c
|
1843 |
|
9fa1b3b18
|
1844 |
spin_lock_irqsave(&info->card->card_lock, flags); |
90cc30185
|
1845 |
if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) { |
9fa1b3b18
|
1846 |
spin_unlock_irqrestore(&info->card->card_lock, flags); |
76b25a550
|
1847 |
return 0; |
02f1175c8
|
1848 |
} |
1da177e4c
|
1849 |
|
77451e53e
|
1850 |
info->port.xmit_buf[info->xmit_head++] = ch; |
02f1175c8
|
1851 1852 |
info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; |
1da177e4c
|
1853 1854 |
info->idle_stats.xmit_bytes++; info->idle_stats.xmit_idle = jiffies; |
9fa1b3b18
|
1855 |
spin_unlock_irqrestore(&info->card->card_lock, flags); |
76b25a550
|
1856 |
return 1; |
02f1175c8
|
1857 |
} /* cy_put_char */ |
1da177e4c
|
1858 1859 1860 |
/* * This routine is called by the kernel after it has written a |
15ed6cc0b
|
1861 |
* series of characters to the tty device using put_char(). |
1da177e4c
|
1862 |
*/ |
02f1175c8
|
1863 |
static void cy_flush_chars(struct tty_struct *tty) |
1da177e4c
|
1864 |
{ |
cab9bdd14
|
1865 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
1866 |
|
1da177e4c
|
1867 |
#ifdef CY_DEBUG_IO |
217191910
|
1868 1869 |
printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d ", info->line); |
1da177e4c
|
1870 |
#endif |
02f1175c8
|
1871 1872 |
if (serial_paranoia_check(info, tty->name, "cy_flush_chars")) return; |
1da177e4c
|
1873 |
|
02f1175c8
|
1874 |
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || |
77451e53e
|
1875 |
!info->port.xmit_buf) |
02f1175c8
|
1876 |
return; |
1da177e4c
|
1877 |
|
02f1175c8
|
1878 1879 |
start_xmit(info); } /* cy_flush_chars */ |
1da177e4c
|
1880 1881 1882 1883 1884 1885 1886 |
/* * This routine returns the numbers of characters the tty driver * will accept for queuing to be written. This number is subject * to change as output buffers get emptied, or if the output flow * control is activated. */ |
02f1175c8
|
1887 |
static int cy_write_room(struct tty_struct *tty) |
1da177e4c
|
1888 |
{ |
cab9bdd14
|
1889 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
1890 |
int ret; |
1da177e4c
|
1891 |
#ifdef CY_DEBUG_IO |
217191910
|
1892 1893 |
printk(KERN_DEBUG "cyc:cy_write_room ttyC%d ", info->line); |
1da177e4c
|
1894 |
#endif |
02f1175c8
|
1895 1896 1897 1898 1899 1900 1901 |
if (serial_paranoia_check(info, tty->name, "cy_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret; } /* cy_write_room */ |
1da177e4c
|
1902 |
|
02f1175c8
|
1903 |
static int cy_chars_in_buffer(struct tty_struct *tty) |
1da177e4c
|
1904 |
{ |
cab9bdd14
|
1905 |
struct cyclades_port *info = tty->driver_data; |
1da177e4c
|
1906 |
|
02f1175c8
|
1907 1908 |
if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) return 0; |
1da177e4c
|
1909 |
#ifdef Z_EXT_CHARS_IN_BUFFER |
f0eefdc30
|
1910 |
if (!cy_is_Z(info->card)) { |
02f1175c8
|
1911 |
#endif /* Z_EXT_CHARS_IN_BUFFER */ |
1da177e4c
|
1912 |
#ifdef CY_DEBUG_IO |
217191910
|
1913 1914 1915 |
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d ", info->line, info->xmit_cnt); |
1da177e4c
|
1916 |
#endif |
02f1175c8
|
1917 |
return info->xmit_cnt; |
1da177e4c
|
1918 |
#ifdef Z_EXT_CHARS_IN_BUFFER |
02f1175c8
|
1919 |
} else { |
f0eefdc30
|
1920 |
struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; |
02f1175c8
|
1921 |
int char_count; |
ad39c3004
|
1922 |
__u32 tx_put, tx_get, tx_bufsize; |
02f1175c8
|
1923 |
|
db05c3b1d
|
1924 1925 1926 |
tx_get = readl(&buf_ctrl->tx_get); tx_put = readl(&buf_ctrl->tx_put); tx_bufsize = readl(&buf_ctrl->tx_bufsize); |
02f1175c8
|
1927 1928 1929 1930 |
if (tx_put >= tx_get) char_count = tx_put - tx_get; else char_count = tx_put - tx_get + tx_bufsize; |
1da177e4c
|
1931 |
#ifdef CY_DEBUG_IO |
217191910
|
1932 1933 1934 |
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d ", info->line, info->xmit_cnt + char_count); |
1da177e4c
|
1935 |
#endif |
096dcfce3
|
1936 |
return info->xmit_cnt + char_count; |
02f1175c8
|
1937 1938 1939 |
} #endif /* Z_EXT_CHARS_IN_BUFFER */ } /* cy_chars_in_buffer */ |
1da177e4c
|
1940 1941 1942 1943 1944 1945 |
/* * ------------------------------------------------------------ * cy_ioctl() and friends * ------------------------------------------------------------ */ |
1a86b5e34
|
1946 |
static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) |
1da177e4c
|
1947 |
{ |
02f1175c8
|
1948 |
int co, co_val, bpr; |
1a86b5e34
|
1949 |
__u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 : |
02f1175c8
|
1950 |
25000000); |
1da177e4c
|
1951 |
|
02f1175c8
|
1952 1953 1954 1955 |
if (baud == 0) { info->tbpr = info->tco = info->rbpr = info->rco = 0; return; } |
1da177e4c
|
1956 |
|
02f1175c8
|
1957 1958 1959 1960 1961 |
/* determine which prescaler to use */ for (co = 4, co_val = 2048; co; co--, co_val >>= 2) { if (cy_clock / co_val / baud > 63) break; } |
1da177e4c
|
1962 |
|
02f1175c8
|
1963 1964 1965 |
bpr = (cy_clock / co_val * 2 / baud + 1) / 2; if (bpr > 255) bpr = 255; |
1da177e4c
|
1966 |
|
02f1175c8
|
1967 1968 |
info->tbpr = info->rbpr = bpr; info->tco = info->rco = co; |
1da177e4c
|
1969 1970 1971 1972 1973 1974 |
} /* * This routine finds or computes the various line characteristics. * It used to be called config_setup */ |
d13549f80
|
1975 |
static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) |
1da177e4c
|
1976 |
{ |
875b206b5
|
1977 |
struct cyclades_card *card; |
02f1175c8
|
1978 |
unsigned long flags; |
3aeea5b92
|
1979 |
int channel; |
02f1175c8
|
1980 |
unsigned cflag, iflag; |
02f1175c8
|
1981 1982 |
int baud, baud_rate = 0; int i; |
d13549f80
|
1983 |
if (!tty->termios) /* XXX can this happen at all? */ |
02f1175c8
|
1984 |
return; |
15ed6cc0b
|
1985 1986 |
if (info->line == -1) |
02f1175c8
|
1987 |
return; |
15ed6cc0b
|
1988 |
|
d13549f80
|
1989 1990 |
cflag = tty->termios->c_cflag; iflag = tty->termios->c_iflag; |
1da177e4c
|
1991 |
|
02f1175c8
|
1992 1993 1994 |
/* * Set up the tty->alt_speed kludge */ |
d13549f80
|
1995 1996 1997 1998 1999 2000 2001 2002 |
if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) tty->alt_speed = 57600; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) tty->alt_speed = 115200; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) tty->alt_speed = 230400; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) tty->alt_speed = 460800; |
02f1175c8
|
2003 2004 |
card = info->card; |
875b206b5
|
2005 |
channel = info->line - card->first_line; |
02f1175c8
|
2006 |
|
2693f485c
|
2007 |
if (!cy_is_Z(card)) { |
46fb78252
|
2008 |
u32 cflags; |
02f1175c8
|
2009 |
/* baud rate */ |
d13549f80
|
2010 |
baud = tty_get_baud_rate(tty); |
77451e53e
|
2011 |
if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == |
02f1175c8
|
2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 |
ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; else baud_rate = info->baud; } else if (baud > CD1400_MAX_SPEED) { baud = CD1400_MAX_SPEED; } /* find the baud index */ for (i = 0; i < 20; i++) { |
15ed6cc0b
|
2022 |
if (baud == baud_table[i]) |
02f1175c8
|
2023 |
break; |
02f1175c8
|
2024 |
} |
15ed6cc0b
|
2025 |
if (i == 20) |
02f1175c8
|
2026 |
i = 19; /* CD1400_MAX_SPEED */ |
02f1175c8
|
2027 |
|
77451e53e
|
2028 |
if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == |
02f1175c8
|
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 |
ASYNC_SPD_CUST) { cyy_baud_calc(info, baud_rate); } else { if (info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[i]; /* Tx BPR */ info->tco = baud_co_60[i]; /* Tx CO */ info->rbpr = baud_bpr_60[i]; /* Rx BPR */ info->rco = baud_co_60[i]; /* Rx CO */ } else { info->tbpr = baud_bpr_25[i]; /* Tx BPR */ info->tco = baud_co_25[i]; /* Tx CO */ info->rbpr = baud_bpr_25[i]; /* Rx BPR */ info->rco = baud_co_25[i]; /* Rx CO */ } } if (baud_table[i] == 134) { /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; |
77451e53e
|
2049 |
} else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == |
02f1175c8
|
2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 |
ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; } else if (baud_table[i]) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_table[i]) + 2; /* this needs to be propagated into the card info */ } else { info->timeout = 0; } /* By tradition (is it a standard?) a baud rate of zero implies the line should be/has been closed. A bit later in this routine such a test is performed. */ /* byte size and parity */ info->cor5 = 0; info->cor4 = 0; /* receive threshold */ info->cor3 = (info->default_threshold ? info->default_threshold : baud_cor3[i]); info->cor2 = CyETC; switch (cflag & CSIZE) { case CS5: info->cor1 = Cy_5_BITS; break; case CS6: info->cor1 = Cy_6_BITS; break; case CS7: info->cor1 = Cy_7_BITS; break; case CS8: info->cor1 = Cy_8_BITS; break; } |
15ed6cc0b
|
2085 |
if (cflag & CSTOPB) |
02f1175c8
|
2086 |
info->cor1 |= Cy_2_STOP; |
15ed6cc0b
|
2087 |
|
02f1175c8
|
2088 |
if (cflag & PARENB) { |
15ed6cc0b
|
2089 |
if (cflag & PARODD) |
02f1175c8
|
2090 |
info->cor1 |= CyPARITY_O; |
15ed6cc0b
|
2091 |
else |
02f1175c8
|
2092 |
info->cor1 |= CyPARITY_E; |
15ed6cc0b
|
2093 |
} else |
02f1175c8
|
2094 |
info->cor1 |= CyPARITY_NONE; |
02f1175c8
|
2095 2096 2097 |
/* CTS flow control flag */ if (cflag & CRTSCTS) { |
77451e53e
|
2098 |
info->port.flags |= ASYNC_CTS_FLOW; |
02f1175c8
|
2099 2100 |
info->cor2 |= CyCtsAE; } else { |
77451e53e
|
2101 |
info->port.flags &= ~ASYNC_CTS_FLOW; |
02f1175c8
|
2102 2103 2104 |
info->cor2 &= ~CyCtsAE; } if (cflag & CLOCAL) |
77451e53e
|
2105 |
info->port.flags &= ~ASYNC_CHECK_CD; |
02f1175c8
|
2106 |
else |
77451e53e
|
2107 |
info->port.flags |= ASYNC_CHECK_CD; |
1da177e4c
|
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 |
/*********************************************** The hardware option, CyRtsAO, presents RTS when the chip has characters to send. Since most modems use RTS as reverse (inbound) flow control, this option is not used. If inbound flow control is necessary, DTR can be programmed to provide the appropriate signals for use with a non-standard cable. Contact Marcio Saito for details. ***********************************************/ |
02f1175c8
|
2118 |
channel &= 0x03; |
1da177e4c
|
2119 |
|
9fa1b3b18
|
2120 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
2121 |
cyy_writeb(info, CyCAR, channel); |
02f1175c8
|
2122 2123 |
/* tx and rx baud rate */ |
3aeea5b92
|
2124 2125 2126 2127 |
cyy_writeb(info, CyTCOR, info->tco); cyy_writeb(info, CyTBPR, info->tbpr); cyy_writeb(info, CyRCOR, info->rco); cyy_writeb(info, CyRBPR, info->rbpr); |
02f1175c8
|
2128 2129 |
/* set line characteristics according configuration */ |
3aeea5b92
|
2130 2131 2132 2133 2134 2135 2136 |
cyy_writeb(info, CySCHR1, START_CHAR(tty)); cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); cyy_writeb(info, CyCOR1, info->cor1); cyy_writeb(info, CyCOR2, info->cor2); cyy_writeb(info, CyCOR3, info->cor3); cyy_writeb(info, CyCOR4, info->cor4); cyy_writeb(info, CyCOR5, info->cor5); |
02f1175c8
|
2137 |
|
3aeea5b92
|
2138 2139 |
cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | CyCOR3ch); |
02f1175c8
|
2140 |
|
15ed6cc0b
|
2141 |
/* !!! Is this needed? */ |
3aeea5b92
|
2142 2143 |
cyy_writeb(info, CyCAR, channel); cyy_writeb(info, CyRTPR, |
02f1175c8
|
2144 2145 |
(info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ |
46fb78252
|
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 |
cflags = CyCTS; if (!C_CLOCAL(tty)) cflags |= CyDSR | CyRI | CyDCD; /* without modem intr */ cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); /* act on 1->0 modem transitions */ if ((cflag & CRTSCTS) && info->rflow) cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); else cyy_writeb(info, CyMCOR1, cflags); /* act on 0->1 modem transitions */ cyy_writeb(info, CyMCOR2, cflags); |
02f1175c8
|
2158 |
|
4d7682005
|
2159 2160 2161 2162 |
if (i == 0) /* baud rate is zero, turn off line */ cyy_change_rts_dtr(info, 0, TIOCM_DTR); else cyy_change_rts_dtr(info, TIOCM_DTR, 0); |
1da177e4c
|
2163 |
|
d13549f80
|
2164 |
clear_bit(TTY_IO_ERROR, &tty->flags); |
9fa1b3b18
|
2165 |
spin_unlock_irqrestore(&card->card_lock, flags); |
1da177e4c
|
2166 |
|
1da177e4c
|
2167 |
} else { |
f0eefdc30
|
2168 |
struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; |
1a86b5e34
|
2169 |
__u32 sw_flow; |
02f1175c8
|
2170 |
int retval; |
1da177e4c
|
2171 |
|
2693f485c
|
2172 |
if (!cyz_is_loaded(card)) |
02f1175c8
|
2173 |
return; |
1da177e4c
|
2174 |
|
02f1175c8
|
2175 |
/* baud rate */ |
d13549f80
|
2176 |
baud = tty_get_baud_rate(tty); |
77451e53e
|
2177 |
if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == |
02f1175c8
|
2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 |
ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; else baud_rate = info->baud; } else if (baud > CYZ_MAX_SPEED) { baud = CYZ_MAX_SPEED; } cy_writel(&ch_ctrl->comm_baud, baud); if (baud == 134) { /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; |
77451e53e
|
2192 |
} else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == |
02f1175c8
|
2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 |
ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; } else if (baud) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud) + 2; /* this needs to be propagated into the card info */ } else { info->timeout = 0; } |
1da177e4c
|
2203 |
|
02f1175c8
|
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 |
/* byte size and parity */ switch (cflag & CSIZE) { case CS5: cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5); break; case CS6: cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6); break; case CS7: cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7); break; case CS8: cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8); break; } if (cflag & CSTOPB) { cy_writel(&ch_ctrl->comm_data_l, |
db05c3b1d
|
2221 |
readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); |
02f1175c8
|
2222 2223 |
} else { cy_writel(&ch_ctrl->comm_data_l, |
db05c3b1d
|
2224 |
readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); |
02f1175c8
|
2225 2226 |
} if (cflag & PARENB) { |
15ed6cc0b
|
2227 |
if (cflag & PARODD) |
02f1175c8
|
2228 |
cy_writel(&ch_ctrl->comm_parity, C_PR_ODD); |
15ed6cc0b
|
2229 |
else |
02f1175c8
|
2230 |
cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN); |
15ed6cc0b
|
2231 |
} else |
02f1175c8
|
2232 |
cy_writel(&ch_ctrl->comm_parity, C_PR_NONE); |
1da177e4c
|
2233 |
|
02f1175c8
|
2234 2235 2236 |
/* CTS flow control flag */ if (cflag & CRTSCTS) { cy_writel(&ch_ctrl->hw_flow, |
db05c3b1d
|
2237 |
readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); |
02f1175c8
|
2238 |
} else { |
db05c3b1d
|
2239 2240 |
cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); |
02f1175c8
|
2241 2242 2243 |
} /* As the HW flow control is done in firmware, the driver doesn't need to care about it */ |
77451e53e
|
2244 |
info->port.flags &= ~ASYNC_CTS_FLOW; |
02f1175c8
|
2245 2246 2247 2248 2249 2250 2251 2252 2253 |
/* XON/XOFF/XANY flow control flags */ sw_flow = 0; if (iflag & IXON) { sw_flow |= C_FL_OXX; if (iflag & IXANY) sw_flow |= C_FL_OIXANY; } cy_writel(&ch_ctrl->sw_flow, sw_flow); |
875b206b5
|
2254 |
retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L); |
02f1175c8
|
2255 |
if (retval != 0) { |
217191910
|
2256 2257 2258 |
printk(KERN_ERR "cyc:set_line_char retval on ttyC%d " "was %x ", info->line, retval); |
02f1175c8
|
2259 2260 2261 |
} /* CD sensitivity */ |
15ed6cc0b
|
2262 |
if (cflag & CLOCAL) |
77451e53e
|
2263 |
info->port.flags &= ~ASYNC_CHECK_CD; |
15ed6cc0b
|
2264 |
else |
77451e53e
|
2265 |
info->port.flags |= ASYNC_CHECK_CD; |
1da177e4c
|
2266 |
|
02f1175c8
|
2267 2268 |
if (baud == 0) { /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, |
db05c3b1d
|
2269 |
readl(&ch_ctrl->rs_control) & ~C_RS_DTR); |
1da177e4c
|
2270 |
#ifdef CY_DEBUG_DTR |
217191910
|
2271 2272 |
printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR "); |
1da177e4c
|
2273 |
#endif |
02f1175c8
|
2274 2275 |
} else { cy_writel(&ch_ctrl->rs_control, |
db05c3b1d
|
2276 |
readl(&ch_ctrl->rs_control) | C_RS_DTR); |
1da177e4c
|
2277 |
#ifdef CY_DEBUG_DTR |
217191910
|
2278 2279 |
printk(KERN_DEBUG "cyc:set_line_char raising Z DTR "); |
1da177e4c
|
2280 |
#endif |
02f1175c8
|
2281 |
} |
1da177e4c
|
2282 |
|
15ed6cc0b
|
2283 |
retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); |
02f1175c8
|
2284 |
if (retval != 0) { |
217191910
|
2285 2286 2287 |
printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d " "was %x ", info->line, retval); |
02f1175c8
|
2288 |
} |
1da177e4c
|
2289 |
|
d13549f80
|
2290 |
clear_bit(TTY_IO_ERROR, &tty->flags); |
1da177e4c
|
2291 |
} |
02f1175c8
|
2292 |
} /* set_line_char */ |
1da177e4c
|
2293 |
|
6c28181cf
|
2294 |
static int cy_get_serial_info(struct cyclades_port *info, |
15ed6cc0b
|
2295 |
struct serial_struct __user *retinfo) |
1da177e4c
|
2296 |
{ |
875b206b5
|
2297 |
struct cyclades_card *cinfo = info->card; |
6c28181cf
|
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 |
struct serial_struct tmp = { .type = info->type, .line = info->line, .port = (info->card - cy_card) * 0x100 + info->line - cinfo->first_line, .irq = cinfo->irq, .flags = info->port.flags, .close_delay = info->port.close_delay, .closing_wait = info->port.closing_wait, .baud_base = info->baud, .custom_divisor = info->custom_divisor, .hub6 = 0, /*!!! */ }; |
02f1175c8
|
2311 |
return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; |
6c28181cf
|
2312 |
} |
1da177e4c
|
2313 2314 |
static int |
d13549f80
|
2315 |
cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, |
15ed6cc0b
|
2316 |
struct serial_struct __user *new_info) |
1da177e4c
|
2317 |
{ |
02f1175c8
|
2318 |
struct serial_struct new_serial; |
25c3cdf80
|
2319 |
int ret; |
02f1175c8
|
2320 2321 2322 |
if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; |
02f1175c8
|
2323 |
|
25c3cdf80
|
2324 |
mutex_lock(&info->port.mutex); |
02f1175c8
|
2325 |
if (!capable(CAP_SYS_ADMIN)) { |
44b7d1b37
|
2326 |
if (new_serial.close_delay != info->port.close_delay || |
02f1175c8
|
2327 2328 2329 |
new_serial.baud_base != info->baud || (new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != |
77451e53e
|
2330 |
(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) |
25c3cdf80
|
2331 2332 |
{ mutex_unlock(&info->port.mutex); |
02f1175c8
|
2333 |
return -EPERM; |
25c3cdf80
|
2334 |
} |
77451e53e
|
2335 |
info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | |
02f1175c8
|
2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 |
(new_serial.flags & ASYNC_USR_MASK); info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; |
77451e53e
|
2349 |
info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | |
02f1175c8
|
2350 |
(new_serial.flags & ASYNC_FLAGS); |
44b7d1b37
|
2351 2352 |
info->port.close_delay = new_serial.close_delay * HZ / 100; info->port.closing_wait = new_serial.closing_wait * HZ / 100; |
1da177e4c
|
2353 2354 |
check_and_exit: |
77451e53e
|
2355 |
if (info->port.flags & ASYNC_INITIALIZED) { |
d13549f80
|
2356 |
cy_set_line_char(info, tty); |
25c3cdf80
|
2357 |
ret = 0; |
02f1175c8
|
2358 |
} else { |
25c3cdf80
|
2359 |
ret = cy_startup(info, tty); |
02f1175c8
|
2360 |
} |
25c3cdf80
|
2361 2362 |
mutex_unlock(&info->port.mutex); return ret; |
02f1175c8
|
2363 |
} /* set_serial_info */ |
1da177e4c
|
2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 |
/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ |
15ed6cc0b
|
2375 |
static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) |
1da177e4c
|
2376 |
{ |
3aeea5b92
|
2377 |
struct cyclades_card *card = info->card; |
02f1175c8
|
2378 2379 |
unsigned int result; unsigned long flags; |
3aeea5b92
|
2380 |
u8 status; |
1da177e4c
|
2381 |
|
2693f485c
|
2382 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
2383 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
2384 |
status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); |
9fa1b3b18
|
2385 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2386 2387 2388 2389 2390 2391 |
result = (status ? 0 : TIOCSER_TEMT); } else { /* Not supported yet */ return -EINVAL; } return put_user(result, (unsigned long __user *)value); |
1da177e4c
|
2392 |
} |
60b33c133
|
2393 |
static int cy_tiocmget(struct tty_struct *tty) |
1da177e4c
|
2394 |
{ |
cab9bdd14
|
2395 |
struct cyclades_port *info = tty->driver_data; |
875b206b5
|
2396 |
struct cyclades_card *card; |
3aeea5b92
|
2397 |
int result; |
02f1175c8
|
2398 |
|
bf9d89295
|
2399 |
if (serial_paranoia_check(info, tty->name, __func__)) |
02f1175c8
|
2400 |
return -ENODEV; |
1da177e4c
|
2401 |
|
02f1175c8
|
2402 |
card = info->card; |
0d3487294
|
2403 |
|
2693f485c
|
2404 |
if (!cy_is_Z(card)) { |
0d3487294
|
2405 |
unsigned long flags; |
3aeea5b92
|
2406 2407 |
int channel = info->line - card->first_line; u8 status; |
1da177e4c
|
2408 |
|
9fa1b3b18
|
2409 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
2410 2411 2412 |
cyy_writeb(info, CyCAR, channel & 0x03); status = cyy_readb(info, CyMSVR1); status |= cyy_readb(info, CyMSVR2); |
9fa1b3b18
|
2413 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 |
if (info->rtsdtr_inv) { result = ((status & CyRTS) ? TIOCM_DTR : 0) | ((status & CyDTR) ? TIOCM_RTS : 0); } else { result = ((status & CyRTS) ? TIOCM_RTS : 0) | ((status & CyDTR) ? TIOCM_DTR : 0); } result |= ((status & CyDCD) ? TIOCM_CAR : 0) | ((status & CyRI) ? TIOCM_RNG : 0) | ((status & CyDSR) ? TIOCM_DSR : 0) | ((status & CyCTS) ? TIOCM_CTS : 0); |
1da177e4c
|
2426 |
} else { |
0d3487294
|
2427 2428 2429 2430 2431 |
u32 lstatus; if (!cyz_is_loaded(card)) { result = -ENODEV; goto end; |
02f1175c8
|
2432 |
} |
1da177e4c
|
2433 |
|
0d3487294
|
2434 2435 2436 2437 2438 2439 2440 |
lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); |
02f1175c8
|
2441 |
} |
0d3487294
|
2442 |
end: |
02f1175c8
|
2443 2444 |
return result; } /* cy_tiomget */ |
1da177e4c
|
2445 2446 |
static int |
20b9d1771
|
2447 |
cy_tiocmset(struct tty_struct *tty, |
02f1175c8
|
2448 |
unsigned int set, unsigned int clear) |
1da177e4c
|
2449 |
{ |
cab9bdd14
|
2450 |
struct cyclades_port *info = tty->driver_data; |
875b206b5
|
2451 |
struct cyclades_card *card; |
02f1175c8
|
2452 |
unsigned long flags; |
02f1175c8
|
2453 |
|
bf9d89295
|
2454 |
if (serial_paranoia_check(info, tty->name, __func__)) |
02f1175c8
|
2455 2456 2457 |
return -ENODEV; card = info->card; |
2693f485c
|
2458 |
if (!cy_is_Z(card)) { |
4d7682005
|
2459 2460 2461 |
spin_lock_irqsave(&card->card_lock, flags); cyy_change_rts_dtr(info, set, clear); spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2462 |
} else { |
0d3487294
|
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 |
struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; int retval, channel = info->line - card->first_line; u32 rs; if (!cyz_is_loaded(card)) return -ENODEV; spin_lock_irqsave(&card->card_lock, flags); rs = readl(&ch_ctrl->rs_control); if (set & TIOCM_RTS) rs |= C_RS_RTS; if (clear & TIOCM_RTS) rs &= ~C_RS_RTS; if (set & TIOCM_DTR) { rs |= C_RS_DTR; |
1da177e4c
|
2478 |
#ifdef CY_DEBUG_DTR |
0d3487294
|
2479 2480 |
printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR "); |
1da177e4c
|
2481 |
#endif |
0d3487294
|
2482 2483 2484 |
} if (clear & TIOCM_DTR) { rs &= ~C_RS_DTR; |
1da177e4c
|
2485 |
#ifdef CY_DEBUG_DTR |
0d3487294
|
2486 2487 2488 |
printk(KERN_DEBUG "cyc:set_modem_info clearing " "Z DTR "); |
1da177e4c
|
2489 |
#endif |
02f1175c8
|
2490 |
} |
0d3487294
|
2491 |
cy_writel(&ch_ctrl->rs_control, rs); |
9fa1b3b18
|
2492 |
retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); |
0d3487294
|
2493 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2494 |
if (retval != 0) { |
217191910
|
2495 2496 2497 |
printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " "was %x ", info->line, retval); |
02f1175c8
|
2498 |
} |
1da177e4c
|
2499 |
} |
02f1175c8
|
2500 |
return 0; |
0d3487294
|
2501 |
} |
1da177e4c
|
2502 2503 2504 2505 |
/* * cy_break() --- routine which turns the break handling on or off */ |
9e98966c7
|
2506 |
static int cy_break(struct tty_struct *tty, int break_state) |
1da177e4c
|
2507 |
{ |
cab9bdd14
|
2508 |
struct cyclades_port *info = tty->driver_data; |
9fa1b3b18
|
2509 |
struct cyclades_card *card; |
02f1175c8
|
2510 |
unsigned long flags; |
9e98966c7
|
2511 |
int retval = 0; |
1da177e4c
|
2512 |
|
02f1175c8
|
2513 |
if (serial_paranoia_check(info, tty->name, "cy_break")) |
9e98966c7
|
2514 |
return -EINVAL; |
1da177e4c
|
2515 |
|
9fa1b3b18
|
2516 2517 2518 |
card = info->card; spin_lock_irqsave(&card->card_lock, flags); |
2693f485c
|
2519 |
if (!cy_is_Z(card)) { |
02f1175c8
|
2520 2521 2522 2523 2524 2525 2526 |
/* Let the transmit ISR take care of this (since it requires stuffing characters into the output stream). */ if (break_state == -1) { if (!info->breakon) { info->breakon = 1; if (!info->xmit_cnt) { |
9fa1b3b18
|
2527 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2528 |
start_xmit(info); |
9fa1b3b18
|
2529 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
2530 2531 2532 2533 2534 2535 |
} } } else { if (!info->breakoff) { info->breakoff = 1; if (!info->xmit_cnt) { |
9fa1b3b18
|
2536 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2537 |
start_xmit(info); |
9fa1b3b18
|
2538 |
spin_lock_irqsave(&card->card_lock, flags); |
02f1175c8
|
2539 2540 |
} } |
1da177e4c
|
2541 |
} |
1da177e4c
|
2542 |
} else { |
02f1175c8
|
2543 |
if (break_state == -1) { |
9fa1b3b18
|
2544 2545 |
retval = cyz_issue_cmd(card, info->line - card->first_line, |
02f1175c8
|
2546 2547 |
C_CM_SET_BREAK, 0L); if (retval != 0) { |
217191910
|
2548 2549 2550 |
printk(KERN_ERR "cyc:cy_break (set) retval on " "ttyC%d was %x ", info->line, retval); |
02f1175c8
|
2551 2552 |
} } else { |
9fa1b3b18
|
2553 2554 |
retval = cyz_issue_cmd(card, info->line - card->first_line, |
02f1175c8
|
2555 2556 |
C_CM_CLR_BREAK, 0L); if (retval != 0) { |
217191910
|
2557 2558 2559 2560 |
printk(KERN_DEBUG "cyc:cy_break (clr) retval " "on ttyC%d was %x ", info->line, retval); |
02f1175c8
|
2561 |
} |
1da177e4c
|
2562 |
} |
1da177e4c
|
2563 |
} |
9fa1b3b18
|
2564 |
spin_unlock_irqrestore(&card->card_lock, flags); |
9e98966c7
|
2565 |
return retval; |
02f1175c8
|
2566 |
} /* cy_break */ |
1da177e4c
|
2567 |
|
02f1175c8
|
2568 |
static int set_threshold(struct cyclades_port *info, unsigned long value) |
1da177e4c
|
2569 |
{ |
3aeea5b92
|
2570 |
struct cyclades_card *card = info->card; |
02f1175c8
|
2571 |
unsigned long flags; |
1da177e4c
|
2572 |
|
2693f485c
|
2573 |
if (!cy_is_Z(card)) { |
02f1175c8
|
2574 2575 |
info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; |
1da177e4c
|
2576 |
|
9fa1b3b18
|
2577 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
2578 2579 |
cyy_writeb(info, CyCOR3, info->cor3); cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); |
9fa1b3b18
|
2580 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2581 2582 2583 |
} return 0; } /* set_threshold */ |
1da177e4c
|
2584 |
|
15ed6cc0b
|
2585 2586 |
static int get_threshold(struct cyclades_port *info, unsigned long __user *value) |
1da177e4c
|
2587 |
{ |
3aeea5b92
|
2588 |
struct cyclades_card *card = info->card; |
1da177e4c
|
2589 |
|
2693f485c
|
2590 |
if (!cy_is_Z(card)) { |
3aeea5b92
|
2591 |
u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; |
02f1175c8
|
2592 |
return put_user(tmp, value); |
02f1175c8
|
2593 |
} |
f74290342
|
2594 |
return 0; |
02f1175c8
|
2595 |
} /* get_threshold */ |
1da177e4c
|
2596 |
|
02f1175c8
|
2597 |
static int set_timeout(struct cyclades_port *info, unsigned long value) |
1da177e4c
|
2598 |
{ |
3aeea5b92
|
2599 |
struct cyclades_card *card = info->card; |
02f1175c8
|
2600 |
unsigned long flags; |
1da177e4c
|
2601 |
|
2693f485c
|
2602 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
2603 |
spin_lock_irqsave(&card->card_lock, flags); |
3aeea5b92
|
2604 |
cyy_writeb(info, CyRTPR, value & 0xff); |
9fa1b3b18
|
2605 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2606 2607 2608 |
} return 0; } /* set_timeout */ |
1da177e4c
|
2609 |
|
15ed6cc0b
|
2610 2611 |
static int get_timeout(struct cyclades_port *info, unsigned long __user *value) |
1da177e4c
|
2612 |
{ |
3aeea5b92
|
2613 |
struct cyclades_card *card = info->card; |
1da177e4c
|
2614 |
|
2693f485c
|
2615 |
if (!cy_is_Z(card)) { |
3aeea5b92
|
2616 |
u8 tmp = cyy_readb(info, CyRTPR); |
02f1175c8
|
2617 |
return put_user(tmp, value); |
02f1175c8
|
2618 |
} |
f74290342
|
2619 |
return 0; |
02f1175c8
|
2620 |
} /* get_timeout */ |
1da177e4c
|
2621 |
|
6c28181cf
|
2622 2623 |
static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, struct cyclades_icount *cprev) |
1da177e4c
|
2624 |
{ |
6c28181cf
|
2625 2626 2627 |
struct cyclades_icount cnow; unsigned long flags; int ret; |
1da177e4c
|
2628 |
|
6c28181cf
|
2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 |
spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; /* atomic copy */ spin_unlock_irqrestore(&info->card->card_lock, flags); ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); *cprev = cnow; return ret; } |
1da177e4c
|
2642 2643 2644 2645 2646 2647 2648 |
/* * This routine allows the tty driver to implement device- * specific ioctl's. If the ioctl number passed in cmd is * not recognized by the driver, it should return ENOIOCTLCMD. */ static int |
6caa76b77
|
2649 |
cy_ioctl(struct tty_struct *tty, |
02f1175c8
|
2650 |
unsigned int cmd, unsigned long arg) |
1da177e4c
|
2651 |
{ |
cab9bdd14
|
2652 |
struct cyclades_port *info = tty->driver_data; |
6c28181cf
|
2653 |
struct cyclades_icount cnow; /* kernel counter temps */ |
02f1175c8
|
2654 2655 2656 2657 2658 2659 |
int ret_val = 0; unsigned long flags; void __user *argp = (void __user *)arg; if (serial_paranoia_check(info, tty->name, "cy_ioctl")) return -ENODEV; |
1da177e4c
|
2660 2661 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2662 2663 2664 |
printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx ", info->line, cmd, arg); |
1da177e4c
|
2665 |
#endif |
02f1175c8
|
2666 2667 |
switch (cmd) { case CYGETMON: |
6c28181cf
|
2668 2669 2670 2671 2672 |
if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { ret_val = -EFAULT; break; } memset(&info->mon, 0, sizeof(info->mon)); |
02f1175c8
|
2673 2674 2675 2676 2677 2678 2679 2680 |
break; case CYGETTHRESH: ret_val = get_threshold(info, argp); break; case CYSETTHRESH: ret_val = set_threshold(info, arg); break; case CYGETDEFTHRESH: |
6c28181cf
|
2681 2682 |
ret_val = put_user(info->default_threshold, (unsigned long __user *)argp); |
02f1175c8
|
2683 2684 |
break; case CYSETDEFTHRESH: |
6c28181cf
|
2685 |
info->default_threshold = arg & 0x0f; |
02f1175c8
|
2686 2687 2688 2689 2690 2691 2692 2693 |
break; case CYGETTIMEOUT: ret_val = get_timeout(info, argp); break; case CYSETTIMEOUT: ret_val = set_timeout(info, arg); break; case CYGETDEFTIMEOUT: |
6c28181cf
|
2694 2695 |
ret_val = put_user(info->default_timeout, (unsigned long __user *)argp); |
02f1175c8
|
2696 2697 |
break; case CYSETDEFTIMEOUT: |
6c28181cf
|
2698 |
info->default_timeout = arg & 0xff; |
02f1175c8
|
2699 |
break; |
1da177e4c
|
2700 |
case CYSETRFLOW: |
02f1175c8
|
2701 |
info->rflow = (int)arg; |
02f1175c8
|
2702 |
break; |
1da177e4c
|
2703 |
case CYGETRFLOW: |
02f1175c8
|
2704 2705 |
ret_val = info->rflow; break; |
1da177e4c
|
2706 |
case CYSETRTSDTR_INV: |
02f1175c8
|
2707 |
info->rtsdtr_inv = (int)arg; |
02f1175c8
|
2708 |
break; |
1da177e4c
|
2709 |
case CYGETRTSDTR_INV: |
02f1175c8
|
2710 2711 |
ret_val = info->rtsdtr_inv; break; |
1da177e4c
|
2712 |
case CYGETCD1400VER: |
02f1175c8
|
2713 2714 |
ret_val = info->chip_rev; break; |
1da177e4c
|
2715 2716 |
#ifndef CONFIG_CYZ_INTR case CYZSETPOLLCYCLE: |
02f1175c8
|
2717 |
cyz_polling_cycle = (arg * HZ) / 1000; |
02f1175c8
|
2718 |
break; |
1da177e4c
|
2719 |
case CYZGETPOLLCYCLE: |
02f1175c8
|
2720 2721 2722 |
ret_val = (cyz_polling_cycle * 1000) / HZ; break; #endif /* CONFIG_CYZ_INTR */ |
1da177e4c
|
2723 |
case CYSETWAIT: |
44b7d1b37
|
2724 |
info->port.closing_wait = (unsigned short)arg * HZ / 100; |
02f1175c8
|
2725 |
break; |
1da177e4c
|
2726 |
case CYGETWAIT: |
44b7d1b37
|
2727 |
ret_val = info->port.closing_wait / (HZ / 100); |
02f1175c8
|
2728 2729 |
break; case TIOCGSERIAL: |
6c28181cf
|
2730 |
ret_val = cy_get_serial_info(info, argp); |
02f1175c8
|
2731 2732 |
break; case TIOCSSERIAL: |
d13549f80
|
2733 |
ret_val = cy_set_serial_info(info, tty, argp); |
02f1175c8
|
2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 |
break; case TIOCSERGETLSR: /* Get line status register */ ret_val = get_lsr_info(info, argp); break; /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ |
1da177e4c
|
2744 |
case TIOCMIWAIT: |
9fa1b3b18
|
2745 |
spin_lock_irqsave(&info->card->card_lock, flags); |
02f1175c8
|
2746 |
/* note the counters on entry */ |
2c7fea992
|
2747 |
cnow = info->icount; |
9fa1b3b18
|
2748 |
spin_unlock_irqrestore(&info->card->card_lock, flags); |
bdc04e317
|
2749 |
ret_val = wait_event_interruptible(info->port.delta_msr_wait, |
6c28181cf
|
2750 |
cy_cflags_changed(info, arg, &cnow)); |
2c7fea992
|
2751 |
break; |
1da177e4c
|
2752 |
|
02f1175c8
|
2753 2754 2755 2756 2757 2758 |
/* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ |
02f1175c8
|
2759 2760 2761 |
default: ret_val = -ENOIOCTLCMD; } |
1da177e4c
|
2762 2763 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2764 2765 |
printk(KERN_DEBUG "cyc:cy_ioctl done "); |
1da177e4c
|
2766 |
#endif |
02f1175c8
|
2767 2768 |
return ret_val; } /* cy_ioctl */ |
1da177e4c
|
2769 |
|
0587102cf
|
2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 |
static int cy_get_icount(struct tty_struct *tty, struct serial_icounter_struct *sic) { struct cyclades_port *info = tty->driver_data; struct cyclades_icount cnow; /* Used to snapshot */ unsigned long flags; spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); sic->cts = cnow.cts; sic->dsr = cnow.dsr; sic->rng = cnow.rng; sic->dcd = cnow.dcd; sic->rx = cnow.rx; sic->tx = cnow.tx; sic->frame = cnow.frame; sic->overrun = cnow.overrun; sic->parity = cnow.parity; sic->brk = cnow.brk; sic->buf_overrun = cnow.buf_overrun; return 0; } |
1da177e4c
|
2794 2795 2796 2797 2798 2799 |
/* * This routine allows the tty driver to be notified when * device's termios settings have changed. Note that a * well-designed tty driver should be prepared to accept the case * where old == NULL, and try to do something rational. */ |
02f1175c8
|
2800 |
static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) |
1da177e4c
|
2801 |
{ |
cab9bdd14
|
2802 |
struct cyclades_port *info = tty->driver_data; |
1da177e4c
|
2803 2804 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2805 2806 |
printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d ", info->line); |
1da177e4c
|
2807 |
#endif |
d13549f80
|
2808 |
cy_set_line_char(info, tty); |
02f1175c8
|
2809 2810 2811 2812 2813 2814 |
if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; cy_start(tty); } |
1da177e4c
|
2815 |
#if 0 |
02f1175c8
|
2816 2817 2818 2819 2820 2821 2822 2823 |
/* * No need to wake up processes in open wait, since they * sample the CLOCAL flag once, and don't recheck it. * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) |
77451e53e
|
2824 |
wake_up_interruptible(&info->port.open_wait); |
1da177e4c
|
2825 |
#endif |
02f1175c8
|
2826 |
} /* cy_set_termios */ |
1da177e4c
|
2827 2828 2829 2830 |
/* This function is used to send a high-priority XON/XOFF character to the device. */ |
02f1175c8
|
2831 |
static void cy_send_xchar(struct tty_struct *tty, char ch) |
1da177e4c
|
2832 |
{ |
cab9bdd14
|
2833 |
struct cyclades_port *info = tty->driver_data; |
875b206b5
|
2834 2835 |
struct cyclades_card *card; int channel; |
1da177e4c
|
2836 |
|
02f1175c8
|
2837 |
if (serial_paranoia_check(info, tty->name, "cy_send_xchar")) |
1da177e4c
|
2838 |
return; |
02f1175c8
|
2839 |
info->x_char = ch; |
1da177e4c
|
2840 2841 |
if (ch) |
02f1175c8
|
2842 |
cy_start(tty); |
1da177e4c
|
2843 2844 |
card = info->card; |
875b206b5
|
2845 |
channel = info->line - card->first_line; |
1da177e4c
|
2846 |
|
2693f485c
|
2847 |
if (cy_is_Z(card)) { |
02f1175c8
|
2848 |
if (ch == STOP_CHAR(tty)) |
875b206b5
|
2849 |
cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L); |
02f1175c8
|
2850 |
else if (ch == START_CHAR(tty)) |
875b206b5
|
2851 |
cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L); |
1da177e4c
|
2852 2853 2854 2855 2856 2857 2858 |
} } /* This routine is called by the upper-layer tty layer to signal that incoming characters should be throttled because the input buffers are close to full. */ |
02f1175c8
|
2859 |
static void cy_throttle(struct tty_struct *tty) |
1da177e4c
|
2860 |
{ |
cab9bdd14
|
2861 |
struct cyclades_port *info = tty->driver_data; |
875b206b5
|
2862 |
struct cyclades_card *card; |
02f1175c8
|
2863 |
unsigned long flags; |
1da177e4c
|
2864 2865 |
#ifdef CY_DEBUG_THROTTLE |
02f1175c8
|
2866 |
char buf[64]; |
1da177e4c
|
2867 |
|
217191910
|
2868 2869 |
printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d ", tty_name(tty, buf), |
02f1175c8
|
2870 |
tty->ldisc.chars_in_buffer(tty), info->line); |
1da177e4c
|
2871 |
#endif |
15ed6cc0b
|
2872 |
if (serial_paranoia_check(info, tty->name, "cy_throttle")) |
02f1175c8
|
2873 |
return; |
02f1175c8
|
2874 2875 2876 2877 |
card = info->card; if (I_IXOFF(tty)) { |
2693f485c
|
2878 |
if (!cy_is_Z(card)) |
02f1175c8
|
2879 2880 2881 2882 |
cy_send_xchar(tty, STOP_CHAR(tty)); else info->throttle = 1; } |
1da177e4c
|
2883 |
|
02f1175c8
|
2884 |
if (tty->termios->c_cflag & CRTSCTS) { |
2693f485c
|
2885 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
2886 |
spin_lock_irqsave(&card->card_lock, flags); |
4d7682005
|
2887 |
cyy_change_rts_dtr(info, 0, TIOCM_RTS); |
9fa1b3b18
|
2888 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2889 2890 2891 2892 |
} else { info->throttle = 1; } } |
02f1175c8
|
2893 |
} /* cy_throttle */ |
1da177e4c
|
2894 2895 2896 2897 2898 2899 |
/* * This routine notifies the tty driver that it should signal * that characters can now be sent to the tty without fear of * overrunning the input buffers of the line disciplines. */ |
02f1175c8
|
2900 |
static void cy_unthrottle(struct tty_struct *tty) |
1da177e4c
|
2901 |
{ |
cab9bdd14
|
2902 |
struct cyclades_port *info = tty->driver_data; |
875b206b5
|
2903 |
struct cyclades_card *card; |
02f1175c8
|
2904 |
unsigned long flags; |
1da177e4c
|
2905 2906 |
#ifdef CY_DEBUG_THROTTLE |
02f1175c8
|
2907 |
char buf[64]; |
217191910
|
2908 2909 |
printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d ", |
15ed6cc0b
|
2910 |
tty_name(tty, buf), tty_chars_in_buffer(tty), info->line); |
1da177e4c
|
2911 |
#endif |
15ed6cc0b
|
2912 |
if (serial_paranoia_check(info, tty->name, "cy_unthrottle")) |
02f1175c8
|
2913 |
return; |
1da177e4c
|
2914 |
|
02f1175c8
|
2915 2916 2917 2918 2919 |
if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else cy_send_xchar(tty, START_CHAR(tty)); |
1da177e4c
|
2920 |
} |
1da177e4c
|
2921 |
|
02f1175c8
|
2922 2923 |
if (tty->termios->c_cflag & CRTSCTS) { card = info->card; |
2693f485c
|
2924 |
if (!cy_is_Z(card)) { |
9fa1b3b18
|
2925 |
spin_lock_irqsave(&card->card_lock, flags); |
4d7682005
|
2926 |
cyy_change_rts_dtr(info, TIOCM_RTS, 0); |
9fa1b3b18
|
2927 |
spin_unlock_irqrestore(&card->card_lock, flags); |
02f1175c8
|
2928 2929 2930 2931 |
} else { info->throttle = 0; } } |
02f1175c8
|
2932 |
} /* cy_unthrottle */ |
1da177e4c
|
2933 2934 2935 2936 |
/* cy_start and cy_stop provide software output flow control as a function of XON/XOFF, software CTS, and other such stuff. */ |
02f1175c8
|
2937 |
static void cy_stop(struct tty_struct *tty) |
1da177e4c
|
2938 |
{ |
02f1175c8
|
2939 |
struct cyclades_card *cinfo; |
cab9bdd14
|
2940 |
struct cyclades_port *info = tty->driver_data; |
3aeea5b92
|
2941 |
int channel; |
02f1175c8
|
2942 |
unsigned long flags; |
1da177e4c
|
2943 2944 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2945 2946 |
printk(KERN_DEBUG "cyc:cy_stop ttyC%d ", info->line); |
1da177e4c
|
2947 |
#endif |
02f1175c8
|
2948 2949 |
if (serial_paranoia_check(info, tty->name, "cy_stop")) return; |
1da177e4c
|
2950 |
|
875b206b5
|
2951 |
cinfo = info->card; |
02f1175c8
|
2952 |
channel = info->line - cinfo->first_line; |
2693f485c
|
2953 |
if (!cy_is_Z(cinfo)) { |
9fa1b3b18
|
2954 |
spin_lock_irqsave(&cinfo->card_lock, flags); |
3aeea5b92
|
2955 2956 |
cyy_writeb(info, CyCAR, channel & 0x03); cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); |
9fa1b3b18
|
2957 |
spin_unlock_irqrestore(&cinfo->card_lock, flags); |
02f1175c8
|
2958 |
} |
02f1175c8
|
2959 |
} /* cy_stop */ |
1da177e4c
|
2960 |
|
02f1175c8
|
2961 |
static void cy_start(struct tty_struct *tty) |
1da177e4c
|
2962 |
{ |
02f1175c8
|
2963 |
struct cyclades_card *cinfo; |
cab9bdd14
|
2964 |
struct cyclades_port *info = tty->driver_data; |
3aeea5b92
|
2965 |
int channel; |
02f1175c8
|
2966 |
unsigned long flags; |
1da177e4c
|
2967 2968 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2969 2970 |
printk(KERN_DEBUG "cyc:cy_start ttyC%d ", info->line); |
1da177e4c
|
2971 |
#endif |
02f1175c8
|
2972 2973 |
if (serial_paranoia_check(info, tty->name, "cy_start")) return; |
1da177e4c
|
2974 |
|
875b206b5
|
2975 |
cinfo = info->card; |
02f1175c8
|
2976 |
channel = info->line - cinfo->first_line; |
2693f485c
|
2977 |
if (!cy_is_Z(cinfo)) { |
9fa1b3b18
|
2978 |
spin_lock_irqsave(&cinfo->card_lock, flags); |
3aeea5b92
|
2979 2980 |
cyy_writeb(info, CyCAR, channel & 0x03); cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); |
9fa1b3b18
|
2981 |
spin_unlock_irqrestore(&cinfo->card_lock, flags); |
02f1175c8
|
2982 |
} |
02f1175c8
|
2983 |
} /* cy_start */ |
1da177e4c
|
2984 |
|
1da177e4c
|
2985 2986 2987 |
/* * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */ |
02f1175c8
|
2988 |
static void cy_hangup(struct tty_struct *tty) |
1da177e4c
|
2989 |
{ |
cab9bdd14
|
2990 |
struct cyclades_port *info = tty->driver_data; |
02f1175c8
|
2991 |
|
1da177e4c
|
2992 |
#ifdef CY_DEBUG_OTHER |
217191910
|
2993 2994 |
printk(KERN_DEBUG "cyc:cy_hangup ttyC%d ", info->line); |
1da177e4c
|
2995 |
#endif |
02f1175c8
|
2996 2997 |
if (serial_paranoia_check(info, tty->name, "cy_hangup")) return; |
1da177e4c
|
2998 |
|
02f1175c8
|
2999 |
cy_flush_buffer(tty); |
d13549f80
|
3000 |
cy_shutdown(info, tty); |
174e6fe01
|
3001 |
tty_port_hangup(&info->port); |
02f1175c8
|
3002 |
} /* cy_hangup */ |
1da177e4c
|
3003 |
|
f07375794
|
3004 3005 3006 3007 3008 |
static int cyy_carrier_raised(struct tty_port *port) { struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; |
f07375794
|
3009 3010 |
unsigned long flags; int channel = info->line - cinfo->first_line; |
f07375794
|
3011 |
u32 cd; |
f07375794
|
3012 |
spin_lock_irqsave(&cinfo->card_lock, flags); |
3aeea5b92
|
3013 3014 |
cyy_writeb(info, CyCAR, channel & 0x03); cd = cyy_readb(info, CyMSVR1) & CyDCD; |
f07375794
|
3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 |
spin_unlock_irqrestore(&cinfo->card_lock, flags); return cd; } static void cyy_dtr_rts(struct tty_port *port, int raise) { struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; |
f07375794
|
3025 |
unsigned long flags; |
f07375794
|
3026 3027 |
spin_lock_irqsave(&cinfo->card_lock, flags); |
4d7682005
|
3028 3029 |
cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, raise ? 0 : TIOCM_RTS | TIOCM_DTR); |
f07375794
|
3030 3031 3032 3033 3034 3035 3036 |
spin_unlock_irqrestore(&cinfo->card_lock, flags); } static int cyz_carrier_raised(struct tty_port *port) { struct cyclades_port *info = container_of(port, struct cyclades_port, port); |
f07375794
|
3037 |
|
f0eefdc30
|
3038 |
return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; |
f07375794
|
3039 3040 3041 3042 3043 3044 3045 |
} static void cyz_dtr_rts(struct tty_port *port, int raise) { struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; |
f0eefdc30
|
3046 |
struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; |
f07375794
|
3047 3048 |
int ret, channel = info->line - cinfo->first_line; u32 rs; |
f0eefdc30
|
3049 |
rs = readl(&ch_ctrl->rs_control); |
f07375794
|
3050 3051 3052 3053 |
if (raise) rs |= C_RS_RTS | C_RS_DTR; else rs &= ~(C_RS_RTS | C_RS_DTR); |
f0eefdc30
|
3054 |
cy_writel(&ch_ctrl->rs_control, rs); |
f07375794
|
3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 |
ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); if (ret != 0) printk(KERN_ERR "%s: retval on ttyC%d was %x ", __func__, info->line, ret); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "%s: raising Z DTR ", __func__); #endif } static const struct tty_port_operations cyy_port_ops = { .carrier_raised = cyy_carrier_raised, .dtr_rts = cyy_dtr_rts, |
e936ffd5c
|
3069 |
.shutdown = cy_do_close, |
f07375794
|
3070 3071 3072 3073 3074 |
}; static const struct tty_port_operations cyz_port_ops = { .carrier_raised = cyz_carrier_raised, .dtr_rts = cyz_dtr_rts, |
e936ffd5c
|
3075 |
.shutdown = cy_do_close, |
f07375794
|
3076 |
}; |
1da177e4c
|
3077 3078 3079 3080 3081 3082 3083 |
/* * --------------------------------------------------------------------- * cy_init() and friends * * cy_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- */ |
dd025c0c7
|
3084 |
static int __devinit cy_init_card(struct cyclades_card *cinfo) |
0809e2671
|
3085 3086 |
{ struct cyclades_port *info; |
f0eefdc30
|
3087 |
unsigned int channel, port; |
0809e2671
|
3088 |
|
3046d50ea
|
3089 |
spin_lock_init(&cinfo->card_lock); |
963118eef
|
3090 |
cinfo->intr_enabled = 0; |
3046d50ea
|
3091 |
|
963118eef
|
3092 3093 |
cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports), GFP_KERNEL); |
dd025c0c7
|
3094 3095 3096 3097 3098 |
if (cinfo->ports == NULL) { printk(KERN_ERR "Cyclades: cannot allocate ports "); return -ENOMEM; } |
f0eefdc30
|
3099 3100 3101 |
for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; channel++, port++) { info = &cinfo->ports[channel]; |
44b7d1b37
|
3102 |
tty_port_init(&info->port); |
3046d50ea
|
3103 |
info->magic = CYCLADES_MAGIC; |
875b206b5
|
3104 |
info->card = cinfo; |
3046d50ea
|
3105 |
info->line = port; |
3046d50ea
|
3106 |
|
44b7d1b37
|
3107 3108 |
info->port.closing_wait = CLOSING_WAIT_DELAY; info->port.close_delay = 5 * HZ / 10; |
77451e53e
|
3109 |
info->port.flags = STD_COM_FLAGS; |
2c7fea992
|
3110 |
init_completion(&info->shutdown_wait); |
3046d50ea
|
3111 |
|
2693f485c
|
3112 |
if (cy_is_Z(cinfo)) { |
f0eefdc30
|
3113 3114 |
struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; struct ZFW_CTRL *zfw_ctrl; |
f07375794
|
3115 |
info->port.ops = &cyz_port_ops; |
0809e2671
|
3116 |
info->type = PORT_STARTECH; |
f0eefdc30
|
3117 3118 3119 3120 3121 |
zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; |
101b81590
|
3122 |
if (cinfo->hw_ver == ZO_V1) |
0809e2671
|
3123 3124 |
info->xmit_fifo_size = CYZ_FIFO_SIZE; else |
3046d50ea
|
3125 |
info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; |
0809e2671
|
3126 |
#ifdef CONFIG_CYZ_INTR |
3991428d9
|
3127 3128 |
setup_timer(&cyz_rx_full_timer[port], cyz_rx_restart, (unsigned long)info); |
0809e2671
|
3129 |
#endif |
3046d50ea
|
3130 |
} else { |
f0eefdc30
|
3131 |
unsigned short chip_number; |
963118eef
|
3132 |
int index = cinfo->bus_index; |
f0eefdc30
|
3133 |
|
f07375794
|
3134 |
info->port.ops = &cyy_port_ops; |
0809e2671
|
3135 |
info->type = PORT_CIRRUS; |
0809e2671
|
3136 |
info->xmit_fifo_size = CyMAX_CHAR_FIFO; |
3046d50ea
|
3137 |
info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; |
0809e2671
|
3138 3139 |
info->cor2 = CyETC; info->cor3 = 0x08; /* _very_ small rcv threshold */ |
3046d50ea
|
3140 |
|
f0eefdc30
|
3141 |
chip_number = channel / CyPORTS_PER_CHIP; |
3aeea5b92
|
3142 3143 3144 |
info->u.cyy.base_addr = cinfo->base_addr + (cy_chip_offset[chip_number] << index); info->chip_rev = cyy_readb(info, CyGFRCR); |
15ed6cc0b
|
3145 3146 |
if (info->chip_rev >= CD1400_REV_J) { |
0809e2671
|
3147 3148 3149 3150 3151 |
/* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[13]; /* Tx BPR */ info->tco = baud_co_60[13]; /* Tx CO */ info->rbpr = baud_bpr_60[13]; /* Rx BPR */ info->rco = baud_co_60[13]; /* Rx CO */ |
0809e2671
|
3152 3153 3154 3155 3156 3157 |
info->rtsdtr_inv = 1; } else { info->tbpr = baud_bpr_25[13]; /* Tx BPR */ info->tco = baud_co_25[13]; /* Tx CO */ info->rbpr = baud_bpr_25[13]; /* Rx BPR */ info->rco = baud_co_25[13]; /* Rx CO */ |
0809e2671
|
3158 3159 |
info->rtsdtr_inv = 0; } |
3046d50ea
|
3160 3161 |
info->read_status_mask = CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY | CyFRAME | CyOVERRUN; |
0809e2671
|
3162 |
} |
3046d50ea
|
3163 |
|
0809e2671
|
3164 |
} |
3046d50ea
|
3165 3166 |
#ifndef CONFIG_CYZ_INTR |
2693f485c
|
3167 |
if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) { |
3046d50ea
|
3168 3169 3170 3171 3172 3173 3174 |
mod_timer(&cyz_timerlist, jiffies + 1); #ifdef CY_PCI_DEBUG printk(KERN_DEBUG "Cyclades-Z polling initialized "); #endif } #endif |
dd025c0c7
|
3175 |
return 0; |
0809e2671
|
3176 |
} |
1da177e4c
|
3177 3178 |
/* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ |
31b4f0a11
|
3179 3180 |
static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr, int index) |
1da177e4c
|
3181 |
{ |
02f1175c8
|
3182 3183 3184 3185 3186 3187 3188 3189 |
unsigned int chip_number; void __iomem *base_addr; cy_writeb(true_base_addr + (Cy_HwReset << index), 0); /* Cy_HwReset is 0x1400 */ cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0); /* Cy_ClrIntr is 0x1800 */ udelay(500L); |
15ed6cc0b
|
3190 3191 |
for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD; chip_number++) { |
02f1175c8
|
3192 3193 3194 |
base_addr = true_base_addr + (cy_chip_offset[chip_number] << index); mdelay(1); |
db05c3b1d
|
3195 |
if (readb(base_addr + (CyCCR << index)) != 0x00) { |
02f1175c8
|
3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 |
/************* printk(" chip #%d at %#6lx is never idle (CCR != 0) ", chip_number, (unsigned long)base_addr); *************/ return chip_number; } cy_writeb(base_addr + (CyGFRCR << index), 0); udelay(10L); /* The Cyclom-16Y does not decode address bit 9 and therefore cannot distinguish between references to chip 0 and a non- existent chip 4. If the preceding clearing of the supposed chip 4 GFRCR register appears at chip 0, there is no chip 4 and this must be a Cyclom-16Y, not a Cyclom-32Ye. */ |
db05c3b1d
|
3213 |
if (chip_number == 4 && readb(true_base_addr + |
02f1175c8
|
3214 3215 3216 3217 3218 3219 3220 |
(cy_chip_offset[0] << index) + (CyGFRCR << index)) == 0) { return chip_number; } cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET); mdelay(1); |
db05c3b1d
|
3221 |
if (readb(base_addr + (CyGFRCR << index)) == 0x00) { |
02f1175c8
|
3222 3223 3224 3225 3226 3227 3228 3229 |
/* printk(" chip #%d at %#6lx is not responding ", chip_number, (unsigned long)base_addr); printk("(GFRCR stayed 0) ", */ return chip_number; } |
db05c3b1d
|
3230 |
if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) != |
02f1175c8
|
3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 |
0x40) { /* printk(" chip #%d at %#6lx is not valid (GFRCR == " "%#2x) ", chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]); */ return chip_number; } cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL); |
db05c3b1d
|
3242 |
if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) { |
02f1175c8
|
3243 3244 3245 3246 3247 3248 3249 3250 |
/* It is a CD1400 rev. J or later */ /* Impossible to reach 5ms with this chip. Changed to 2ms instead (f = 500 Hz). */ cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS); } else { /* f = 200 Hz */ cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS); } |
1da177e4c
|
3251 |
|
02f1175c8
|
3252 3253 3254 3255 |
/* printk(" chip #%d at %#6lx is rev 0x%2x ", chip_number, (unsigned long)base_addr, |
db05c3b1d
|
3256 |
readb(base_addr+(CyGFRCR<<index))); |
02f1175c8
|
3257 3258 3259 3260 |
*/ } return chip_number; } /* cyy_init_card */ |
1da177e4c
|
3261 3262 3263 3264 3265 3266 3267 |
/* * --------------------------------------------------------------------- * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. * sets global variables and return the number of ISA boards found. * --------------------------------------------------------------------- */ |
02f1175c8
|
3268 |
static int __init cy_detect_isa(void) |
1da177e4c
|
3269 3270 |
{ #ifdef CONFIG_ISA |
02f1175c8
|
3271 3272 3273 |
unsigned short cy_isa_irq, nboard; void __iomem *cy_isa_address; unsigned short i, j, cy_isa_nchan; |
02f1175c8
|
3274 |
int isparam = 0; |
1da177e4c
|
3275 |
|
02f1175c8
|
3276 |
nboard = 0; |
1da177e4c
|
3277 |
|
1da177e4c
|
3278 |
/* Check for module parameters */ |
02f1175c8
|
3279 3280 3281 3282 3283 3284 3285 |
for (i = 0; i < NR_CARDS; i++) { if (maddr[i] || i) { isparam = 1; cy_isa_addresses[i] = maddr[i]; } if (!maddr[i]) break; |
1da177e4c
|
3286 |
} |
1da177e4c
|
3287 |
|
02f1175c8
|
3288 3289 3290 |
/* scan the address table probing for Cyclom-Y/ISA boards */ for (i = 0; i < NR_ISA_ADDRS; i++) { unsigned int isa_address = cy_isa_addresses[i]; |
15ed6cc0b
|
3291 |
if (isa_address == 0x0000) |
096dcfce3
|
3292 |
return nboard; |
1da177e4c
|
3293 |
|
02f1175c8
|
3294 |
/* probe for CD1400... */ |
cd989b3a8
|
3295 |
cy_isa_address = ioremap_nocache(isa_address, CyISA_Ywin); |
3137553d3
|
3296 3297 3298 3299 3300 3301 |
if (cy_isa_address == NULL) { printk(KERN_ERR "Cyclom-Y/ISA: can't remap base " "address "); continue; } |
02f1175c8
|
3302 3303 3304 |
cy_isa_nchan = CyPORTS_PER_CHIP * cyy_init_card(cy_isa_address, 0); if (cy_isa_nchan == 0) { |
3137553d3
|
3305 |
iounmap(cy_isa_address); |
02f1175c8
|
3306 3307 |
continue; } |
209043635
|
3308 |
|
196b3167e
|
3309 |
if (isparam && i < NR_CARDS && irq[i]) |
02f1175c8
|
3310 |
cy_isa_irq = irq[i]; |
1da177e4c
|
3311 |
else |
02f1175c8
|
3312 3313 3314 |
/* find out the board's irq by probing */ cy_isa_irq = detect_isa_irq(cy_isa_address); if (cy_isa_irq == 0) { |
217191910
|
3315 3316 3317 |
printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the " "IRQ could not be detected. ", |
02f1175c8
|
3318 |
(unsigned long)cy_isa_address); |
3137553d3
|
3319 |
iounmap(cy_isa_address); |
02f1175c8
|
3320 3321 3322 3323 |
continue; } if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) { |
217191910
|
3324 3325 3326 3327 |
printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " "more channels are available. Change NR_PORTS " "in cyclades.c and recompile kernel. ", |
02f1175c8
|
3328 |
(unsigned long)cy_isa_address); |
3137553d3
|
3329 |
iounmap(cy_isa_address); |
096dcfce3
|
3330 |
return nboard; |
02f1175c8
|
3331 3332 3333 |
} /* fill the next cy_card structure available */ for (j = 0; j < NR_CARDS; j++) { |
f74290342
|
3334 |
if (cy_card[j].base_addr == NULL) |
02f1175c8
|
3335 3336 3337 |
break; } if (j == NR_CARDS) { /* no more cy_cards available */ |
217191910
|
3338 3339 3340 3341 |
printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no " "more cards can be used. Change NR_CARDS in " "cyclades.c and recompile kernel. ", |
02f1175c8
|
3342 |
(unsigned long)cy_isa_address); |
3137553d3
|
3343 |
iounmap(cy_isa_address); |
096dcfce3
|
3344 |
return nboard; |
02f1175c8
|
3345 3346 3347 3348 |
} /* allocate IRQ */ if (request_irq(cy_isa_irq, cyy_interrupt, |
9cfb5c05f
|
3349 |
0, "Cyclom-Y", &cy_card[j])) { |
217191910
|
3350 3351 3352 3353 |
printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " "could not allocate IRQ#%d. ", (unsigned long)cy_isa_address, cy_isa_irq); |
3137553d3
|
3354 |
iounmap(cy_isa_address); |
096dcfce3
|
3355 |
return nboard; |
02f1175c8
|
3356 3357 3358 3359 |
} /* set cy_card */ cy_card[j].base_addr = cy_isa_address; |
97e87f8eb
|
3360 |
cy_card[j].ctl_addr.p9050 = NULL; |
02f1175c8
|
3361 3362 3363 |
cy_card[j].irq = (int)cy_isa_irq; cy_card[j].bus_index = 0; cy_card[j].first_line = cy_next_channel; |
963118eef
|
3364 3365 |
cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP; cy_card[j].nports = cy_isa_nchan; |
3137553d3
|
3366 3367 3368 3369 3370 3371 |
if (cy_init_card(&cy_card[j])) { cy_card[j].base_addr = NULL; free_irq(cy_isa_irq, &cy_card[j]); iounmap(cy_isa_address); continue; } |
02f1175c8
|
3372 |
nboard++; |
217191910
|
3373 3374 3375 |
printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: " "%d channels starting from port %d ", |
02f1175c8
|
3376 3377 |
j + 1, (unsigned long)cy_isa_address, (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), |
217191910
|
3378 |
cy_isa_irq, cy_isa_nchan, cy_next_channel); |
6ad1ccc19
|
3379 3380 3381 |
for (j = cy_next_channel; j < cy_next_channel + cy_isa_nchan; j++) tty_register_device(cy_serial_driver, j, NULL); |
02f1175c8
|
3382 3383 |
cy_next_channel += cy_isa_nchan; } |
096dcfce3
|
3384 |
return nboard; |
1da177e4c
|
3385 |
#else |
096dcfce3
|
3386 |
return 0; |
02f1175c8
|
3387 3388 |
#endif /* CONFIG_ISA */ } /* cy_detect_isa */ |
1da177e4c
|
3389 |
|
58936d8d9
|
3390 |
#ifdef CONFIG_PCI |
054f5b0aa
|
3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 |
static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) { unsigned int a; for (a = 0; a < size && *str; a++, str++) if (*str & 0x80) return -EINVAL; for (; a < size; a++, str++) if (*str) return -EINVAL; return 0; } |
f61e761e2
|
3405 |
static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data, |
054f5b0aa
|
3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 |
unsigned int size) { for (; size > 0; size--) { cy_writel(fpga, *data++); udelay(10); } } static void __devinit plx_init(struct pci_dev *pdev, int irq, struct RUNTIME_9060 __iomem *addr) |
1da177e4c
|
3416 |
{ |
02f1175c8
|
3417 |
/* Reset PLX */ |
054f5b0aa
|
3418 |
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); |
02f1175c8
|
3419 |
udelay(100L); |
054f5b0aa
|
3420 |
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); |
02f1175c8
|
3421 3422 |
/* Reload Config. Registers from EEPROM */ |
054f5b0aa
|
3423 |
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); |
02f1175c8
|
3424 |
udelay(100L); |
054f5b0aa
|
3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 |
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, * the IRQ is lost and, thus, we have to re-write it to the PCI config. * registers. This will remain here until we find a permanent fix. */ pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); } static int __devinit __cyz_load_fw(const struct firmware *fw, const char *name, const u32 mailbox, void __iomem *base, void __iomem *fpga) { |
f61e761e2
|
3438 3439 3440 3441 |
const void *ptr = fw->data; const struct zfile_header *h = ptr; const struct zfile_config *c, *cs; const struct zfile_block *b, *bs; |
054f5b0aa
|
3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 |
unsigned int a, tmp, len = fw->size; #define BAD_FW KERN_ERR "Bad firmware: " if (len < sizeof(*h)) { printk(BAD_FW "too short: %u<%zu ", len, sizeof(*h)); return -EINVAL; } cs = ptr + h->config_offset; bs = ptr + h->block_offset; if ((void *)(cs + h->n_config) > ptr + len || (void *)(bs + h->n_blocks) > ptr + len) { printk(BAD_FW "too short"); return -EINVAL; } if (cyc_isfwstr(h->name, sizeof(h->name)) || cyc_isfwstr(h->date, sizeof(h->date))) { printk(BAD_FW "bad formatted header string "); return -EINVAL; } if (strncmp(name, h->name, sizeof(h->name))) { printk(BAD_FW "bad name '%s' (expected '%s') ", h->name, name); return -EINVAL; } tmp = 0; for (c = cs; c < cs + h->n_config; c++) { for (a = 0; a < c->n_blocks; a++) if (c->block_list[a] > h->n_blocks) { printk(BAD_FW "bad block ref number in cfgs "); return -EINVAL; } if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ tmp++; } if (!tmp) { printk(BAD_FW "nothing appropriate "); return -EINVAL; } for (b = bs; b < bs + h->n_blocks; b++) if (b->file_offset + b->size > len) { printk(BAD_FW "bad block data offset "); return -EINVAL; } /* everything is OK, let's seek'n'load it */ for (c = cs; c < cs + h->n_config; c++) if (c->mailbox == mailbox && c->function == 0) break; for (a = 0; a < c->n_blocks; a++) { b = &bs[c->block_list[a]]; if (b->type == ZBLOCK_FPGA) { if (fpga != NULL) cyz_fpga_copy(fpga, ptr + b->file_offset, b->size); } else { if (base != NULL) memcpy_toio(base + b->ram_offset, ptr + b->file_offset, b->size); } } #undef BAD_FW return 0; } static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, struct RUNTIME_9060 __iomem *ctl_addr, int irq) { const struct firmware *fw; struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; struct CUSTOM_REG __iomem *cust = base_addr; struct ZFW_CTRL __iomem *pt_zfwctrl; |
c4923b4f1
|
3524 |
void __iomem *tmp; |
963118eef
|
3525 |
u32 mailbox, status, nchan; |
054f5b0aa
|
3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 |
unsigned int i; int retval; retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); if (retval) { dev_err(&pdev->dev, "can't get firmware "); goto err; } /* Check whether the firmware is already loaded and running. If positive, skip this board */ |
2693f485c
|
3538 |
if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { |
054f5b0aa
|
3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 |
u32 cntval = readl(base_addr + 0x190); udelay(100); if (cntval != readl(base_addr + 0x190)) { /* FW counter is working, FW is running */ dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " "Skipping board. "); retval = 0; goto err_rel; } } /* start boot */ cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & ~0x00030800UL); mailbox = readl(&ctl_addr->mail_box_0); |
2693f485c
|
3557 |
if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) { |
054f5b0aa
|
3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 |
/* stops CPU and set window to beginning of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); cy_writel(&cust->cpu_stop, 0); cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); udelay(100); } plx_init(pdev, irq, ctl_addr); if (mailbox != 0) { /* load FPGA */ retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, base_addr); if (retval) goto err_rel; |
2693f485c
|
3573 |
if (!__cyz_fpga_loaded(ctl_addr)) { |
054f5b0aa
|
3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 |
dev_err(&pdev->dev, "fw upload successful, but fw is " "not loaded "); goto err_rel; } } /* stops CPU and set window to beginning of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); cy_writel(&cust->cpu_stop, 0); cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); udelay(100); /* clear memory */ |
c4923b4f1
|
3588 |
for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) |
054f5b0aa
|
3589 3590 3591 3592 |
cy_writeb(tmp, 255); if (mailbox != 0) { /* set window to last 512K of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); |
c4923b4f1
|
3593 |
for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++) |
054f5b0aa
|
3594 3595 3596 |
cy_writeb(tmp, 255); /* set window to beginning of RAM */ cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); |
054f5b0aa
|
3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 |
} retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); release_firmware(fw); if (retval) goto err; /* finish boot and start boards */ cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); cy_writel(&cust->cpu_start, 0); cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); i = 0; while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) msleep(100); if (status != ZFIRM_ID) { if (status == ZFIRM_HLT) { dev_err(&pdev->dev, "you need an external power supply " "for this number of ports. Firmware halted and " "board reset. "); retval = -EIO; goto err; } dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " "some more time ", status); while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 200) msleep(100); if (status != ZFIRM_ID) { dev_err(&pdev->dev, "Board not started in 20 seconds! " "Giving up. (fid->signature = 0x%x) ", status); dev_info(&pdev->dev, "*** Warning ***: if you are " "upgrading the FW, please power cycle the " "system before loading the new FW to the " "Cyclades-Z. "); |
2693f485c
|
3636 |
if (__cyz_fpga_loaded(ctl_addr)) |
054f5b0aa
|
3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 |
plx_init(pdev, irq, ctl_addr); retval = -EIO; goto err; } dev_dbg(&pdev->dev, "Firmware started after %d seconds. ", i / 10); } pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p ", base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), base_addr + readl(&fid->zfwctrl_addr)); |
963118eef
|
3652 |
nchan = readl(&pt_zfwctrl->board_ctrl.n_channel); |
054f5b0aa
|
3653 3654 |
dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u ", |
963118eef
|
3655 |
readl(&pt_zfwctrl->board_ctrl.fw_version), nchan); |
054f5b0aa
|
3656 |
|
963118eef
|
3657 |
if (nchan == 0) { |
054f5b0aa
|
3658 3659 3660 3661 |
dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " "check the connection between the Z host card and the " "serial expanders. "); |
2693f485c
|
3662 |
if (__cyz_fpga_loaded(ctl_addr)) |
054f5b0aa
|
3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 |
plx_init(pdev, irq, ctl_addr); dev_info(&pdev->dev, "Null number of ports detected. Board " "reset. "); retval = 0; goto err; } cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); /* Early firmware failed to start looking for commands. This enables firmware interrupts for those commands. */ cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | (1 << 17)); cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | 0x00030800UL); |
963118eef
|
3683 |
return nchan; |
054f5b0aa
|
3684 3685 3686 3687 |
err_rel: release_firmware(fw); err: return retval; |
1da177e4c
|
3688 |
} |
58936d8d9
|
3689 3690 |
static int __devinit cy_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
1da177e4c
|
3691 |
{ |
3137553d3
|
3692 3693 |
void __iomem *addr0 = NULL, *addr2 = NULL; char *card_name = NULL; |
101b81590
|
3694 |
u32 uninitialized_var(mailbox); |
3137553d3
|
3695 3696 3697 |
unsigned int device_id, nchan = 0, card_no, i; unsigned char plx_ver; int retval, irq; |
02f1175c8
|
3698 |
|
58936d8d9
|
3699 3700 3701 3702 |
retval = pci_enable_device(pdev); if (retval) { dev_err(&pdev->dev, "cannot enable device "); |
3137553d3
|
3703 |
goto err; |
58936d8d9
|
3704 |
} |
1da177e4c
|
3705 |
|
58936d8d9
|
3706 |
/* read PCI configuration area */ |
3137553d3
|
3707 |
irq = pdev->irq; |
58936d8d9
|
3708 |
device_id = pdev->device & ~PCI_DEVICE_ID_MASK; |
1da177e4c
|
3709 |
|
3137553d3
|
3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 |
#if defined(__alpha__) if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low " "addresses on Alpha systems. "); retval = -EIO; goto err_dis; } #endif if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) { dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low " "addresses "); retval = -EIO; goto err_dis; } if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring " "it... "); pdev->resource[2].flags &= ~IORESOURCE_IO; } retval = pci_request_regions(pdev, "cyclades"); if (retval) { dev_err(&pdev->dev, "failed to reserve resources "); goto err_dis; } retval = -EIO; |
58936d8d9
|
3742 3743 |
if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { |
3137553d3
|
3744 |
card_name = "Cyclom-Y"; |
1da177e4c
|
3745 |
|
24e6fd4cd
|
3746 3747 |
addr0 = ioremap_nocache(pci_resource_start(pdev, 0), CyPCI_Yctl); |
3137553d3
|
3748 3749 3750 3751 |
if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region "); goto err_reg; |
58936d8d9
|
3752 |
} |
24e6fd4cd
|
3753 3754 |
addr2 = ioremap_nocache(pci_resource_start(pdev, 2), CyPCI_Ywin); |
3137553d3
|
3755 3756 3757 3758 |
if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region "); goto err_unmap; |
58936d8d9
|
3759 |
} |
1da177e4c
|
3760 |
|
3137553d3
|
3761 3762 |
nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1); if (nchan == 0) { |
217191910
|
3763 3764 3765 |
dev_err(&pdev->dev, "Cyclom-Y PCI host card with no " "Serial-Modules "); |
c847d47cb
|
3766 |
goto err_unmap; |
58936d8d9
|
3767 |
} |
58936d8d9
|
3768 |
} else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { |
3137553d3
|
3769 |
struct RUNTIME_9060 __iomem *ctl_addr; |
217191910
|
3770 |
|
24e6fd4cd
|
3771 3772 |
ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0), CyPCI_Zctl); |
3137553d3
|
3773 3774 3775 3776 3777 |
if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region "); goto err_reg; } |
58936d8d9
|
3778 3779 |
/* Disable interrupts on the PLX before resetting it */ |
97e87f8eb
|
3780 3781 |
cy_writew(&ctl_addr->intr_ctrl_stat, readw(&ctl_addr->intr_ctrl_stat) & ~0x0900); |
58936d8d9
|
3782 |
|
054f5b0aa
|
3783 |
plx_init(pdev, irq, addr0); |
02f1175c8
|
3784 |
|
101b81590
|
3785 |
mailbox = readl(&ctl_addr->mail_box_0); |
58936d8d9
|
3786 |
|
24e6fd4cd
|
3787 3788 |
addr2 = ioremap_nocache(pci_resource_start(pdev, 2), mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); |
3137553d3
|
3789 3790 3791 3792 |
if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region "); goto err_unmap; |
58936d8d9
|
3793 3794 3795 |
} if (mailbox == ZE_V1) { |
3137553d3
|
3796 |
card_name = "Cyclades-Ze"; |
58936d8d9
|
3797 |
} else { |
3137553d3
|
3798 |
card_name = "Cyclades-8Zo"; |
1da177e4c
|
3799 |
#ifdef CY_PCI_DEBUG |
3137553d3
|
3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 |
if (mailbox == ZO_V1) { cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA " "id %lx, ver %lx ", (ulong)(0xff & readl(&((struct CUSTOM_REG *)addr2)-> fpga_id)), (ulong)(0xff & readl(&((struct CUSTOM_REG *)addr2)-> fpga_version))); cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); } else { dev_info(&pdev->dev, "Cyclades-Z/PCI: New " "Cyclades-Z board. FPGA not loaded "); } |
1da177e4c
|
3815 |
#endif |
3137553d3
|
3816 3817 3818 3819 3820 3821 |
/* The following clears the firmware id word. This ensures that the driver will not attempt to talk to the board until it has been properly initialized. */ if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) cy_writel(addr2 + ID_ADDRESS, 0L); |
58936d8d9
|
3822 |
} |
ace08c3c4
|
3823 3824 |
retval = cyz_load_fw(pdev, addr2, addr0, irq); |
963118eef
|
3825 |
if (retval <= 0) |
ace08c3c4
|
3826 |
goto err_unmap; |
963118eef
|
3827 |
nchan = retval; |
3137553d3
|
3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 |
} if ((cy_next_channel + nchan) > NR_PORTS) { dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " "channels are available. Change NR_PORTS in " "cyclades.c and recompile kernel. "); goto err_unmap; } /* fill the next cy_card structure available */ for (card_no = 0; card_no < NR_CARDS; card_no++) { if (cy_card[card_no].base_addr == NULL) break; } if (card_no == NR_CARDS) { /* no more cy_cards available */ dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no " "more cards can be used. Change NR_CARDS in " "cyclades.c and recompile kernel. "); goto err_unmap; } if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { /* allocate IRQ */ retval = request_irq(irq, cyy_interrupt, IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]); if (retval) { dev_err(&pdev->dev, "could not allocate IRQ "); goto err_unmap; |
58936d8d9
|
3859 |
} |
963118eef
|
3860 |
cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; |
3137553d3
|
3861 |
} else { |
f0eefdc30
|
3862 3863 3864 3865 |
struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; struct ZFW_CTRL __iomem *zfw_ctrl; zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); |
101b81590
|
3866 3867 |
cy_card[card_no].hw_ver = mailbox; cy_card[card_no].num_chips = (unsigned int)-1; |
f0eefdc30
|
3868 |
cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; |
02f1175c8
|
3869 |
#ifdef CONFIG_CYZ_INTR |
58936d8d9
|
3870 |
/* allocate IRQ only if board has an IRQ */ |
3137553d3
|
3871 3872 |
if (irq != 0 && irq != 255) { retval = request_irq(irq, cyz_interrupt, |
58936d8d9
|
3873 |
IRQF_SHARED, "Cyclades-Z", |
3137553d3
|
3874 |
&cy_card[card_no]); |
58936d8d9
|
3875 |
if (retval) { |
217191910
|
3876 3877 |
dev_err(&pdev->dev, "could not allocate IRQ "); |
3137553d3
|
3878 |
goto err_unmap; |
02f1175c8
|
3879 |
} |
58936d8d9
|
3880 |
} |
02f1175c8
|
3881 |
#endif /* CONFIG_CYZ_INTR */ |
3137553d3
|
3882 |
} |
02f1175c8
|
3883 |
|
3137553d3
|
3884 3885 |
/* set cy_card */ cy_card[card_no].base_addr = addr2; |
97e87f8eb
|
3886 |
cy_card[card_no].ctl_addr.p9050 = addr0; |
3137553d3
|
3887 3888 3889 |
cy_card[card_no].irq = irq; cy_card[card_no].bus_index = 1; cy_card[card_no].first_line = cy_next_channel; |
963118eef
|
3890 |
cy_card[card_no].nports = nchan; |
3137553d3
|
3891 3892 3893 |
retval = cy_init_card(&cy_card[card_no]); if (retval) goto err_null; |
58936d8d9
|
3894 |
|
3137553d3
|
3895 |
pci_set_drvdata(pdev, &cy_card[card_no]); |
58936d8d9
|
3896 |
|
3137553d3
|
3897 3898 3899 3900 3901 3902 |
if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo || device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { /* enable interrupts in the PCI interface */ plx_ver = readb(addr2 + CyPLX_VER) & 0x0f; switch (plx_ver) { case PLX_9050: |
3137553d3
|
3903 3904 3905 3906 3907 3908 |
cy_writeb(addr0 + 0x4c, 0x43); break; case PLX_9060: case PLX_9080: default: /* Old boards, use PLX_9060 */ |
97e87f8eb
|
3909 3910 3911 3912 3913 |
{ struct RUNTIME_9060 __iomem *ctl_addr = addr0; plx_init(pdev, irq, ctl_addr); cy_writew(&ctl_addr->intr_ctrl_stat, readw(&ctl_addr->intr_ctrl_stat) | 0x0900); |
3137553d3
|
3914 3915 |
break; } |
97e87f8eb
|
3916 |
} |
58936d8d9
|
3917 |
} |
3137553d3
|
3918 3919 3920 3921 3922 3923 |
dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from " "port %d. ", card_name, card_no + 1, nchan, cy_next_channel); for (i = cy_next_channel; i < cy_next_channel + nchan; i++) tty_register_device(cy_serial_driver, i, &pdev->dev); cy_next_channel += nchan; |
58936d8d9
|
3924 |
return 0; |
3137553d3
|
3925 3926 3927 3928 |
err_null: cy_card[card_no].base_addr = NULL; free_irq(irq, &cy_card[card_no]); err_unmap: |
24e6fd4cd
|
3929 |
iounmap(addr0); |
3137553d3
|
3930 |
if (addr2) |
24e6fd4cd
|
3931 |
iounmap(addr2); |
3137553d3
|
3932 3933 3934 3935 3936 3937 |
err_reg: pci_release_regions(pdev); err_dis: pci_disable_device(pdev); err: return retval; |
58936d8d9
|
3938 |
} |
58936d8d9
|
3939 |
|
6747cd93f
|
3940 |
static void __devexit cy_pci_remove(struct pci_dev *pdev) |
58936d8d9
|
3941 |
{ |
38d090932
|
3942 |
struct cyclades_card *cinfo = pci_get_drvdata(pdev); |
f3851e73e
|
3943 |
unsigned int i; |
38d090932
|
3944 |
|
85c93fa95
|
3945 |
/* non-Z with old PLX */ |
2693f485c
|
3946 |
if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) == |
c2ad4c751
|
3947 |
PLX_9050) |
97e87f8eb
|
3948 |
cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0); |
85c93fa95
|
3949 3950 |
else #ifndef CONFIG_CYZ_INTR |
2693f485c
|
3951 |
if (!cy_is_Z(cinfo)) |
85c93fa95
|
3952 |
#endif |
97e87f8eb
|
3953 3954 3955 |
cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat, readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) & ~0x0900); |
85c93fa95
|
3956 |
|
24e6fd4cd
|
3957 |
iounmap(cinfo->base_addr); |
97e87f8eb
|
3958 3959 |
if (cinfo->ctl_addr.p9050) iounmap(cinfo->ctl_addr.p9050); |
38d090932
|
3960 3961 |
if (cinfo->irq #ifndef CONFIG_CYZ_INTR |
2693f485c
|
3962 |
&& !cy_is_Z(cinfo) |
38d090932
|
3963 3964 3965 3966 3967 3968 |
#endif /* CONFIG_CYZ_INTR */ ) free_irq(cinfo->irq, cinfo); pci_release_regions(pdev); cinfo->base_addr = NULL; |
6ad1ccc19
|
3969 3970 3971 |
for (i = cinfo->first_line; i < cinfo->first_line + cinfo->nports; i++) tty_unregister_device(cy_serial_driver, i); |
dd025c0c7
|
3972 3973 |
cinfo->nports = 0; kfree(cinfo->ports); |
38d090932
|
3974 |
} |
6747cd93f
|
3975 3976 3977 3978 3979 3980 3981 |
static struct pci_driver cy_pci_driver = { .name = "cyclades", .id_table = cy_pci_dev_id, .probe = cy_pci_probe, .remove = __devexit_p(cy_pci_remove) }; #endif |
444697d61
|
3982 |
static int cyclades_proc_show(struct seq_file *m, void *v) |
1da177e4c
|
3983 |
{ |
02f1175c8
|
3984 |
struct cyclades_port *info; |
dd025c0c7
|
3985 |
unsigned int i, j; |
02f1175c8
|
3986 |
__u32 cur_jifs = jiffies; |
444697d61
|
3987 |
seq_puts(m, "Dev TimeOpen BytesOut IdleOut BytesIn " |
02f1175c8
|
3988 3989 |
"IdleIn Overruns Ldisc "); |
02f1175c8
|
3990 |
/* Output one line for each known port */ |
dd025c0c7
|
3991 3992 3993 |
for (i = 0; i < NR_CARDS; i++) for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; |
d13549f80
|
3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 |
if (info->port.count) { /* XXX is the ldisc num worth this? */ struct tty_struct *tty; struct tty_ldisc *ld; int num = 0; tty = tty_port_tty_get(&info->port); if (tty) { ld = tty_ldisc_ref(tty); if (ld) { num = ld->ops->num; tty_ldisc_deref(ld); } tty_kref_put(tty); } |
444697d61
|
4008 |
seq_printf(m, "%3d %8lu %10lu %8lu " |
d13549f80
|
4009 4010 |
"%10lu %8lu %9lu %6d ", info->line, |
dd025c0c7
|
4011 4012 4013 4014 4015 4016 |
(cur_jifs - info->idle_stats.in_use) / HZ, info->idle_stats.xmit_bytes, (cur_jifs - info->idle_stats.xmit_idle)/ HZ, info->idle_stats.recv_bytes, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, |
d13549f80
|
4017 4018 |
num); } else |
444697d61
|
4019 |
seq_printf(m, "%3d %8lu %10lu %8lu " |
dd025c0c7
|
4020 4021 4022 |
"%10lu %8lu %9lu %6ld ", info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); |
02f1175c8
|
4023 |
} |
444697d61
|
4024 4025 4026 4027 4028 4029 |
return 0; } static int cyclades_proc_open(struct inode *inode, struct file *file) { return single_open(file, cyclades_proc_show, NULL); |
1da177e4c
|
4030 |
} |
444697d61
|
4031 4032 4033 4034 4035 4036 4037 |
static const struct file_operations cyclades_proc_fops = { .owner = THIS_MODULE, .open = cyclades_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; |
1da177e4c
|
4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 |
/* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a first found, first allocated manner. That is, this code searches for Cyclom cards in the system. As each is found, it is probed to discover how many chips (and thus how many ports) are present. These ports are mapped to the tty ports 32 and upward in monotonic fashion. If an 8-port card is replaced with a 16-port card, the port mapping on a following card will shift. This approach is different from what is used in the other serial device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. If there are more cards with more ports than have been statically allocated above, a warning is printed and the extra ports are ignored. */ |
b68e31d0e
|
4055 |
static const struct tty_operations cy_ops = { |
02f1175c8
|
4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 |
.open = cy_open, .close = cy_close, .write = cy_write, .put_char = cy_put_char, .flush_chars = cy_flush_chars, .write_room = cy_write_room, .chars_in_buffer = cy_chars_in_buffer, .flush_buffer = cy_flush_buffer, .ioctl = cy_ioctl, .throttle = cy_throttle, .unthrottle = cy_unthrottle, .set_termios = cy_set_termios, .stop = cy_stop, .start = cy_start, .hangup = cy_hangup, .break_ctl = cy_break, .wait_until_sent = cy_wait_until_sent, |
02f1175c8
|
4073 4074 |
.tiocmget = cy_tiocmget, .tiocmset = cy_tiocmset, |
0587102cf
|
4075 |
.get_icount = cy_get_icount, |
444697d61
|
4076 |
.proc_fops = &cyclades_proc_fops, |
1da177e4c
|
4077 |
}; |
02f1175c8
|
4078 |
static int __init cy_init(void) |
1da177e4c
|
4079 |
{ |
dd025c0c7
|
4080 |
unsigned int nboards; |
9dacf3b2f
|
4081 |
int retval = -ENOMEM; |
02f1175c8
|
4082 4083 4084 |
cy_serial_driver = alloc_tty_driver(NR_PORTS); if (!cy_serial_driver) |
9dacf3b2f
|
4085 |
goto err; |
217191910
|
4086 |
|
64a14b51b
|
4087 4088 |
printk(KERN_INFO "Cyclades driver " CY_VERSION " "); |
02f1175c8
|
4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 |
/* Initialize the tty_driver structure */ cy_serial_driver->owner = THIS_MODULE; cy_serial_driver->driver_name = "cyclades"; cy_serial_driver->name = "ttyC"; cy_serial_driver->major = CYCLADES_MAJOR; cy_serial_driver->minor_start = 0; cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; cy_serial_driver->subtype = SERIAL_TYPE_NORMAL; cy_serial_driver->init_termios = tty_std_termios; cy_serial_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; |
6ad1ccc19
|
4102 |
cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; |
02f1175c8
|
4103 |
tty_set_operations(cy_serial_driver, &cy_ops); |
9dacf3b2f
|
4104 4105 4106 4107 4108 4109 |
retval = tty_register_driver(cy_serial_driver); if (retval) { printk(KERN_ERR "Couldn't register Cyclades serial driver "); goto err_frtty; } |
02f1175c8
|
4110 |
|
02f1175c8
|
4111 4112 4113 4114 4115 4116 4117 4118 |
/* the code below is responsible to find the boards. Each different type of board has its own detection routine. If a board is found, the next cy_card structure available is set by the detection routine. These functions are responsible for checking the availability of cy_card and cy_port data structures and updating the cy_next_channel. */ /* look for isa boards */ |
14a55a678
|
4119 |
nboards = cy_detect_isa(); |
02f1175c8
|
4120 |
|
6747cd93f
|
4121 |
#ifdef CONFIG_PCI |
02f1175c8
|
4122 |
/* look for pci boards */ |
6747cd93f
|
4123 |
retval = pci_register_driver(&cy_pci_driver); |
d941ea7d4
|
4124 4125 4126 4127 |
if (retval && !nboards) { tty_unregister_driver(cy_serial_driver); goto err_frtty; } |
6747cd93f
|
4128 |
#endif |
9dacf3b2f
|
4129 4130 |
return 0; |
9dacf3b2f
|
4131 4132 4133 4134 |
err_frtty: put_tty_driver(cy_serial_driver); err: return retval; |
02f1175c8
|
4135 |
} /* cy_init */ |
1da177e4c
|
4136 |
|
02f1175c8
|
4137 |
static void __exit cy_cleanup_module(void) |
1da177e4c
|
4138 |
{ |
dd025c0c7
|
4139 |
struct cyclades_card *card; |
65f76a82e
|
4140 |
unsigned int i, e1; |
1da177e4c
|
4141 4142 |
#ifndef CONFIG_CYZ_INTR |
b70509066
|
4143 |
del_timer_sync(&cyz_timerlist); |
1da177e4c
|
4144 |
#endif /* CONFIG_CYZ_INTR */ |
15ed6cc0b
|
4145 4146 |
e1 = tty_unregister_driver(cy_serial_driver); if (e1) |
217191910
|
4147 4148 4149 |
printk(KERN_ERR "failed to unregister Cyclades serial " "driver(%d) ", e1); |
1da177e4c
|
4150 |
|
6747cd93f
|
4151 4152 4153 |
#ifdef CONFIG_PCI pci_unregister_driver(&cy_pci_driver); #endif |
02f1175c8
|
4154 |
for (i = 0; i < NR_CARDS; i++) { |
dd025c0c7
|
4155 4156 |
card = &cy_card[i]; if (card->base_addr) { |
85c93fa95
|
4157 |
/* clear interrupt */ |
dd025c0c7
|
4158 4159 |
cy_writeb(card->base_addr + Cy_ClrIntr, 0); iounmap(card->base_addr); |
97e87f8eb
|
4160 4161 |
if (card->ctl_addr.p9050) iounmap(card->ctl_addr.p9050); |
dd025c0c7
|
4162 |
if (card->irq |
1da177e4c
|
4163 |
#ifndef CONFIG_CYZ_INTR |
2693f485c
|
4164 |
&& !cy_is_Z(card) |
1da177e4c
|
4165 |
#endif /* CONFIG_CYZ_INTR */ |
02f1175c8
|
4166 |
) |
dd025c0c7
|
4167 |
free_irq(card->irq, card); |
65f76a82e
|
4168 |
for (e1 = card->first_line; e1 < card->first_line + |
dd025c0c7
|
4169 |
card->nports; e1++) |
6ad1ccc19
|
4170 |
tty_unregister_device(cy_serial_driver, e1); |
dd025c0c7
|
4171 |
kfree(card->ports); |
02f1175c8
|
4172 4173 |
} } |
f2462bfe5
|
4174 4175 |
put_tty_driver(cy_serial_driver); |
1da177e4c
|
4176 4177 4178 4179 4180 4181 |
} /* cy_cleanup_module */ module_init(cy_init); module_exit(cy_cleanup_module); MODULE_LICENSE("GPL"); |
c8e1693a4
|
4182 |
MODULE_VERSION(CY_VERSION); |
9f56fad74
|
4183 |
MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR); |
e6c4ef984
|
4184 |
MODULE_FIRMWARE("cyzfirm.bin"); |