📄 cyclades.c
字号:
return result;}#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif#define IS_CYC_Z(card) ((card).num_chips == -1)#define Z_FPGA_CHECK(card) \ ((cy_readl(&((struct RUNTIME_9060 *) \ ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \ ((card).ctl_addr))->mail_box_0)) || \ Z_FPGA_CHECK(card)) && \ (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \ ((card).base_addr+ID_ADDRESS))->signature)))#ifndef SERIAL_XMIT_SIZE#define SERIAL_XMIT_SIZE (MIN(PAGE_SIZE, 4096))#endif#define WAKEUP_CHARS 256#define STD_COM_FLAGS (0)#define JIFFIES_DIFF(n, j) ((j) - (n))static DECLARE_TASK_QUEUE(tq_cyclades);static struct tty_driver cy_serial_driver, cy_callout_driver;static int serial_refcount;#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. The cy_setup function extracts additional addresses from the boot options line. The form is "cyclades=address,address..."*/static unsigned char *cy_isa_addresses[] = { (unsigned char *) 0xD0000, (unsigned char *) 0xD2000, (unsigned char *) 0xD4000, (unsigned char *) 0xD6000, (unsigned char *) 0xD8000, (unsigned char *) 0xDA000, (unsigned char *) 0xDC000, (unsigned char *) 0xDE000, 0,0,0,0,0,0,0,0};#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*))#ifdef MODULEstatic long maddr[NR_CARDS] = { 0, };static int irq[NR_CARDS] = { 0, };MODULE_PARM(maddr, "1-" __MODULE_STRING(NR_CARDS) "l");MODULE_PARM(irq, "1-" __MODULE_STRING(NR_CARDS) "i");#endif#endif /* CONFIG_ISA *//* 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];/* This is the per-channel data structure containing pointers, flags and variables for the port. This driver supports a maximum of NR_PORTS.*/static struct cyclades_port cy_port[NR_PORTS];static int cy_next_channel; /* next minor available */static struct tty_struct *serial_table[NR_PORTS];static struct termios *serial_termios[NR_PORTS];static struct termios *serial_termios_locked[NR_PORTS];/* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. This buffer is * allocated when the first cy_open occurs. */static unsigned char *tmp_buf;DECLARE_MUTEX(tmp_buf_sem);/* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra * are accessed via settings in info->flags. * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, * HI VHI * 20 */static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, 230400, 0};static char baud_co_25[] = { /* 25 MHz clock option table */ /* 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};static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15};static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ /* 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};static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, 0x21};static char baud_cor3[] = { /* receive threshold */ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07};/* * The Cyclades driver implements HW flow control as any serial driver. * 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 * cables. */static char rflow_thr[] = { /* rflow threshold */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a};/* The Cyclom-Ye has placed the sequential chips in non-sequential * address order. This look-up table overcomes that problem. */static int cy_chip_offset [] = { 0x0000, 0x0400, 0x0800, 0x0C00, 0x0200, 0x0600, 0x0A00, 0x0E00 };/* PCI related definitions */static unsigned short cy_pci_nboard;static unsigned short cy_isa_nboard;static unsigned short cy_nboard;#ifdef CONFIG_PCIstatic unsigned short cy_pci_dev_id[] = { PCI_DEVICE_ID_CYCLOM_Y_Lo, /* PCI < 1Mb */ PCI_DEVICE_ID_CYCLOM_Y_Hi, /* PCI > 1Mb */ PCI_DEVICE_ID_CYCLOM_4Y_Lo, /* 4Y PCI < 1Mb */ PCI_DEVICE_ID_CYCLOM_4Y_Hi, /* 4Y PCI > 1Mb */ PCI_DEVICE_ID_CYCLOM_8Y_Lo, /* 8Y PCI < 1Mb */ PCI_DEVICE_ID_CYCLOM_8Y_Hi, /* 8Y PCI > 1Mb */ PCI_DEVICE_ID_CYCLOM_Z_Lo, /* Z PCI < 1Mb */ PCI_DEVICE_ID_CYCLOM_Z_Hi, /* Z PCI > 1Mb */ 0 /* end of table */ };#endifstatic void cy_start(struct tty_struct *);static void set_line_char(struct cyclades_port *);static int cyz_issue_cmd(struct cyclades_card *, uclong, ucchar, uclong);#ifdef CONFIG_ISAstatic unsigned detect_isa_irq (volatile ucchar *);#endif /* CONFIG_ISA */static int cyclades_get_proc_info(char *, char **, off_t , int , int *, void *);#ifndef CONFIG_CYZ_INTRstatic void cyz_poll(unsigned long);/* The Cyclades-Z polling cycle is defined by this variable */static long cyz_polling_cycle = CZ_DEF_POLL;static int cyz_timeron = 0;static struct timer_list cyz_timerlist = { function: cyz_poll};#else /* CONFIG_CYZ_INTR */static void cyz_rx_restart(unsigned long);static struct timer_list cyz_rx_full_timer[NR_PORTS];#endif /* CONFIG_CYZ_INTR */static inline intserial_paranoia_check(struct cyclades_port *info, kdev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = "cyc Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = "cyc Warning: null cyclades_port for (%s) in %s\n"; static const char *badrange = "cyc Warning: cyclades_port out of range for (%s) in %s\n"; if (!info) { printk(badinfo, kdevname(device), routine); return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ printk(badrange, kdevname(device), routine); return 1; } if (info->magic != CYCLADES_MAGIC) { printk(badmagic, kdevname(device), routine); return 1; }#endif return 0;} /* serial_paranoia_check *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver * (also known as the "bottom half"). This can be called any * number of times for any channel without harm. */static inline voidcy_sched_event(struct cyclades_port *info, int event){ info->event |= 1 << event; /* remember what kind of event and who */ queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ mark_bh(CYCLADES_BH); /* then trigger event */} /* cy_sched_event *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using cy_sched_event(), and they get done here. * * This is done through one level of indirection--the task queue. * When a hardware interrupt service routine wants service by the * driver's bottom half, it enqueues the appropriate tq_struct (one * per port) to the tq_cyclades work queue and sets a request flag * via mark_bh for processing that queue. When the time is right, * do_cyclades_bh is called (because of the mark_bh) and it requests * that the work queue be processed. * * Although this may seem unwieldy, it gives the system a way to * pass an argument (in this case the pointer to the cyclades_port * structure) to the bottom half of the driver. Previous kernels * had to poll every port to see if that port needed servicing. */static voiddo_cyclades_bh(void){ run_task_queue(&tq_cyclades);} /* do_cyclades_bh */static voiddo_softint(void *private_){ struct cyclades_port *info = (struct cyclades_port *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); info->flags &= ~(ASYNC_NORMAL_ACTIVE| ASYNC_CALLOUT_ACTIVE); } if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { wake_up_interruptible(&info->open_wait); }#ifdef CONFIG_CYZ_INTR if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) { if (cyz_rx_full_timer[info->line].function == NULL) { cyz_rx_full_timer[info->line].expires = jiffies + 1; cyz_rx_full_timer[info->line].function = cyz_rx_restart; cyz_rx_full_timer[info->line].data = (unsigned long)info; add_timer(&cyz_rx_full_timer[info->line]); } }#endif if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) { wake_up_interruptible(&info->delta_msr_wait); } if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ (tty->ldisc.write_wakeup)(tty); } wake_up_interruptible(&tty->write_wait); }#ifdef Z_WAKE if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) { wake_up_interruptible(&info->shutdown_wait); }#endif} /* do_softint *//***********************************************************//********* 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. */static intcyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index){ volatile int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -