cyclades.c

来自「linux 内核源代码」· C语言 代码 · 共 2,048 行 · 第 1/5 页

C
2,048
字号
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 */#ifdef CONFIG_PCIstatic struct pci_device_id cy_pci_dev_id[] __devinitdata = {	{ 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) },	/* 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) },	/* 4Y 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) },	/* 8Y 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) },	/* Z PCI > 1Mb */	{ }			/* end of table */};MODULE_DEVICE_TABLE(pci, cy_pci_dev_id);#endifstatic void cy_start(struct tty_struct *);static void set_line_char(struct cyclades_port *);static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);#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 DEFINE_TIMER(cyz_timerlist, 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 int serial_paranoia_check(struct cyclades_port *info,		char *name, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	if (!info) {		printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "				"in %s\n", name, routine);		return 1;	}	if (info->magic != CYCLADES_MAGIC) {		printk(KERN_WARNING "cyc Warning: bad magic number for serial "				"struct (%s) in %s\n", name, routine);		return 1;	}#endif	return 0;}				/* serial_paranoia_check *//***********************************************************//********* 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 int cyy_issue_cmd(void __iomem * base_addr, u_char cmd, int index){	unsigned int i;	/* Check to see that the previous command has completed */	for (i = 0; i < 100; i++) {		if (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);	cy_writeb(address + (CyCAR << index), 0);	cy_writeb(address + (CySRER << index),		  readb(address + (CySRER << index)) | CyTxRdy);	local_irq_restore(flags);	/* Wait ... */	udelay(5000L);	/* Check which interrupt is in use */	irq = probe_irq_off(irqs);	/* Clean up */	save_xir = (u_char) readb(address + (CyTIR << index));	save_car = readb(address + (CyCAR << index));	cy_writeb(address + (CyCAR << index), (save_xir & 0x3));	cy_writeb(address + (CySRER << index),		  readb(address + (CySRER << index)) & ~CyTxRdy);	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;}#endif				/* CONFIG_ISA */static void cyy_chip_rx(struct cyclades_card *cinfo, int chip,		void __iomem *base_addr){	struct cyclades_port *info;	struct tty_struct *tty;	int len, index = cinfo->bus_index;	u8 save_xir, channel, save_car, data, char_count;#ifdef CY_DEBUG_INTERRUPTS	printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);#endif	/* determine the channel & change to that context */	save_xir = readb(base_addr + (CyRIR << index));	channel = save_xir & CyIRChannel;	info = &cinfo->ports[channel + chip * 4];	save_car = readb(base_addr + (CyCAR << index));	cy_writeb(base_addr + (CyCAR << index), save_xir);	/* if there is nowhere to put the data, discard it */	if (info->tty == NULL) {		if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) ==				CyIVRRxEx) {	/* exception */			data = readb(base_addr + (CyRDSR << index));		} else {	/* normal character reception */			char_count = readb(base_addr + (CyRDCR << index));			while (char_count--)				data = readb(base_addr + (CyRDSR << index));		}		goto end;	}	/* there is an open port for this data */	tty = info->tty;	if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) ==			CyIVRRxEx) {	/* exception */		data = readb(base_addr + (CyRDSR << index));		/* 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++;			return;		}		if (tty_buffer_request_room(tty, 1)) {			if (data & info->read_status_mask) {				if (data & CyBREAK) {					tty_insert_flip_char(tty,						readb(base_addr + (CyRDSR <<							index)), TTY_BREAK);					info->icount.rx++;					if (info->flags & ASYNC_SAK)						do_SAK(tty);				} else if (data & CyFRAME) {					tty_insert_flip_char( tty,						readb(base_addr + (CyRDSR <<							index)), TTY_FRAME);					info->icount.rx++;					info->idle_stats.frame_errs++;				} else if (data & CyPARITY) {					/* Pieces of seven... */					tty_insert_flip_char(tty,						readb(base_addr + (CyRDSR <<							index)), TTY_PARITY);					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,						readb(base_addr + (CyRDSR <<							index)), TTY_FRAME);					info->icount.rx++;					info->idle_stats.overruns++;				/* 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++;				}			} 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 */		char_count = readb(base_addr + (CyRDCR << index));#ifdef CY_ENABLE_MONITORING		++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;#endif		len = tty_buffer_request_room(tty, char_count);		while (len--) {			data = readb(base_addr + (CyRDSR << index));			tty_insert_flip_char(tty, data, TTY_NORMAL);			info->idle_stats.recv_bytes++;			info->icount.rx++;#ifdef CY_16Y_HACK			udelay(10L);#endif		}		info->idle_stats.recv_idle = jiffies;	}	tty_schedule_flip(tty);end:	/* end of service */	cy_writeb(base_addr + (CyRIR << index), save_xir & 0x3f);	cy_writeb(base_addr + (CyCAR << index), save_car);}static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,		void __iomem *base_addr){	struct cyclades_port *info;	int char_count, index = cinfo->bus_index;	u8 save_xir, channel, save_car, outch;	/* Since we only get here when the transmit buffer	   is empty, we know we can always stuff a dozen	   characters. */#ifdef CY_DEBUG_INTERRUPTS	printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);#endif	/* determine the channel & change to that context */	save_xir = readb(base_addr + (CyTIR << index));	channel = save_xir & CyIRChannel;	save_car = readb(base_addr + (CyCAR << index));	cy_writeb(base_addr + (CyCAR << index), save_xir);	/* validate the port# (as configured and open) */	if (channel + chip * 4 >= cinfo->nports) {		cy_writeb(base_addr + (CySRER << index),			  readb(base_addr + (CySRER << index)) & ~CyTxRdy);		goto end;	}	info = &cinfo->ports[channel + chip * 4];	if (info->tty == NULL) {		cy_writeb(base_addr + (CySRER << index),			  readb(base_addr + (CySRER << index)) & ~CyTxRdy);		goto end;	}	/* load the on-chip space for outbound data */	char_count = info->xmit_fifo_size;	if (info->x_char) {	/* send special char */		outch = info->x_char;		cy_writeb(base_addr + (CyTDR << index), outch);		char_count--;		info->icount.tx++;		info->x_char = 0;	}	if (info->breakon || info->breakoff) {		if (info->breakon) {			cy_writeb(base_addr + (CyTDR << index), 0);			cy_writeb(base_addr + (CyTDR << index), 0x81);			info->breakon = 0;			char_count -= 2;		}		if (info->breakoff) {			cy_writeb(base_addr + (CyTDR << index), 0);			cy_writeb(base_addr + (CyTDR << index), 0x83);			info->breakoff = 0;			char_count -= 2;		}	}	while (char_count-- > 0) {		if (!info->xmit_cnt) {			if (readb(base_addr + (CySRER << index)) & CyTxMpty) {				cy_writeb(base_addr + (CySRER << index),					readb(base_addr + (CySRER << index)) &						~CyTxMpty);			} else {				cy_writeb(base_addr + (CySRER << index),					(readb(base_addr + (CySRER << index)) &						~CyTxRdy) | CyTxMpty);			}			goto done;		}		if (info->xmit_buf == NULL) {			cy_writeb(base_addr + (CySRER << index),				readb(base_addr + (CySRER << index)) &					~CyTxRdy);			goto done;		}		if (info->tty->stopped || info->tty->hw_stopped) {			cy_writeb(base_addr + (CySRER << index),				readb(base_addr + (CySRER << index)) &					~CyTxRdy);			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.)		 */		outch = info->xmit_buf[info->xmit_tail];		if (outch) {			info->xmit_cnt--;			info->xmit_tail = (info->xmit_tail + 1) &					(SERIAL_XMIT_SIZE - 1);			cy_writeb(base_addr + (CyTDR << index), outch);			info->icount.tx++;		} else {			if (char_count > 1) {				info->xmit_cnt--;				info->xmit_tail = (info->xmit_tail + 1) &					(SERIAL_XMIT_SIZE - 1);				cy_writeb(base_addr + (CyTDR << index), outch);				cy_writeb(base_addr + (CyTDR << index), 0);				info->icount.tx++;				char_count--;			}		}	}done:	tty_wakeup(info->tty);end:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?