cyclades.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,734 行 · 第 1/5 页
C
1,734 行
#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 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. The cy_setup function extracts additional addresses from the boot options line. The form is "cyclades=address,address..."*/static unsigned int cy_isa_addresses[] = { 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 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 *//* * 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(void __iomem *);#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 = TIMER_INITIALIZER(cyz_poll, 0, 0);#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, char *name, 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, name, routine); return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ printk(badrange, name, routine); return 1; } if (info->magic != CYCLADES_MAGIC) { printk(badmagic, name, 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 */ schedule_work(&info->tqueue);} /* 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 keventd work queue and sets a request flag * 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_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; } 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)) { tty_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(void __iomem *base_addr, u_char cmd, int index){ volatile int i; /* Check to see that the previous command has completed */ for(i = 0 ; i < 100 ; i++){ if (cy_readb(base_addr+(CyCCR<<index)) == 0){ break; } udelay(10L); } /* if the CCR never cleared, the previous command didn't finish within the "reasonable time" */ if (i == 100) return (-1); /* Issue the new command */ cy_writeb(base_addr+(CyCCR<<index), cmd); return(0);} /* cyy_issue_cmd */#ifdef CONFIG_ISA/* ISA interrupt detection code */static unsigned detect_isa_irq(void __iomem *address){ 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 ... */ udelay(5000L); /* Enable the Tx interrupts on the CD1400 */ local_irq_save(flags); cy_writeb(address + (CyCAR<<index), 0); cyy_issue_cmd(address, CyCHAN_CTL|CyENB_XMTR, index);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?