cyclades.c

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

C
2,048
字号
	/* end of service */	cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f);	cy_writeb(base_addr + (CyCAR << index), save_car);}static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,		void __iomem *base_addr){	struct cyclades_port *info;	int index = cinfo->bus_index;	u8 save_xir, channel, save_car, mdm_change, mdm_status;	/* determine the channel & change to that context */	save_xir = readb(base_addr + (CyMIR << 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);	mdm_change = readb(base_addr + (CyMISR << index));	mdm_status = readb(base_addr + (CyMSVR1 << index));	if (!info->tty)		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++;		wake_up_interruptible(&info->delta_msr_wait);	}	if ((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)) {		if (!(mdm_status & CyDCD)) {			tty_hangup(info->tty);			info->flags &= ~ASYNC_NORMAL_ACTIVE;		}		wake_up_interruptible(&info->open_wait);	}	if ((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)) {		if (info->tty->hw_stopped) {			if (mdm_status & CyCTS) {				/* cy_start isn't used				   because... !!! */				info->tty->hw_stopped = 0;				cy_writeb(base_addr + (CySRER << index),					readb(base_addr + (CySRER << index)) |						CyTxRdy);				tty_wakeup(info->tty);			}		} else {			if (!(mdm_status & CyCTS)) {				/* cy_stop isn't used				   because ... !!! */				info->tty->hw_stopped = 1;				cy_writeb(base_addr + (CySRER << index),					readb(base_addr + (CySRER << index)) &						~CyTxRdy);			}		}	}/*	if (mdm_change & CyDSR) {	}	if (mdm_change & CyRI) {	}*/end:	/* end of service */	cy_writeb(base_addr + (CyMIR << index), save_xir & 0x3f);	cy_writeb(base_addr + (CyCAR << index), save_car);}/* The real interrupt service routine is called   whenever the card wants its hand held--chars   received, out buffer empty, modem change, etc. */static irqreturn_t cyy_interrupt(int irq, void *dev_id){	int status;	struct cyclades_card *cinfo = dev_id;	void __iomem *base_addr, *card_base_addr;	unsigned int chip, too_many, had_work;	int index;	if (unlikely(cinfo == NULL)) {#ifdef CY_DEBUG_INTERRUPTS		printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",irq);#endif		return IRQ_NONE;	/* spurious interrupt */	}	card_base_addr = cinfo->base_addr;	index = cinfo->bus_index;	/* card was not initialized yet (e.g. DEBUG_SHIRQ) */	if (unlikely(card_base_addr == NULL))		return IRQ_HANDLED;	/* 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;			while ((status = readb(base_addr +						(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).			 */				if (1000 < too_many++)					break;				spin_lock(&cinfo->card_lock);				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);				spin_unlock(&cinfo->card_lock);			}		}	} 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 *//***********************************************************//********* End of block of Cyclom-Y specific code **********//******** Start of block of Cyclades-Z specific code *********//***********************************************************/static intcyz_fetch_msg(struct cyclades_card *cinfo,		__u32 * channel, __u8 * cmd, __u32 * param){	struct FIRM_ID __iomem *firm_id;	struct ZFW_CTRL __iomem *zfw_ctrl;	struct BOARD_CTRL __iomem *board_ctrl;	unsigned long loc_doorbell;	firm_id = cinfo->base_addr + ID_ADDRESS;	if (!ISZLOADED(*cinfo)) {		return -1;	}	zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);	board_ctrl = &zfw_ctrl->board_ctrl;	loc_doorbell = readl(&((struct RUNTIME_9060 __iomem *)				  (cinfo->ctl_addr))->loc_doorbell);	if (loc_doorbell) {		*cmd = (char)(0xff & loc_doorbell);		*channel = readl(&board_ctrl->fwcmd_channel);		*param = (__u32) readl(&board_ctrl->fwcmd_param);		cy_writel(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->			  loc_doorbell, 0xffffffff);		return 1;	}	return 0;}				/* cyz_fetch_msg */static intcyz_issue_cmd(struct cyclades_card *cinfo,		__u32 channel, __u8 cmd, __u32 param){	struct FIRM_ID __iomem *firm_id;	struct ZFW_CTRL __iomem *zfw_ctrl;	struct BOARD_CTRL __iomem *board_ctrl;	__u32 __iomem *pci_doorbell;	unsigned int index;	firm_id = cinfo->base_addr + ID_ADDRESS;	if (!ISZLOADED(*cinfo)) {		return -1;	}	zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);	board_ctrl = &zfw_ctrl->board_ctrl;	index = 0;	pci_doorbell =	    &((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->pci_doorbell;	while ((readl(pci_doorbell) & 0xff) != 0) {		if (index++ == 1000) {			return (int)(readl(pci_doorbell) & 0xff);		}		udelay(50L);	}	cy_writel(&board_ctrl->hcmd_channel, channel);	cy_writel(&board_ctrl->hcmd_param, param);	cy_writel(pci_doorbell, (long)cmd);	return 0;}				/* cyz_issue_cmd */static void cyz_handle_rx(struct cyclades_port *info,		struct BUF_CTRL __iomem *buf_ctrl){	struct cyclades_card *cinfo = info->card;	struct tty_struct *tty = info->tty;	unsigned int char_count;	int len;#ifdef BLOCKMOVE	unsigned char *buf;#else	char data;#endif	__u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;	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);	if (rx_put >= rx_get)		char_count = rx_put - rx_get;	else		char_count = rx_put - rx_get + rx_bufsize;	if (char_count) {#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		if (tty == NULL) {			/* flush received characters */			new_rx_get = (new_rx_get + char_count) &					(rx_bufsize - 1);			info->rflush_count++;		} else {#ifdef BLOCKMOVE		/* 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 */			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) &						(rx_bufsize - 1);				char_count -= len;				info->icount.rx += len;				info->idle_stats.recv_bytes += len;			}#else			len = tty_buffer_request_room(tty, char_count);			while (len--) {				data = readb(cinfo->base_addr + rx_bufaddr +						new_rx_get);				new_rx_get = (new_rx_get + 1)& (rx_bufsize - 1);				tty_insert_flip_char(tty, data, TTY_NORMAL);				info->idle_stats.recv_bytes++;				info->icount.rx++;			}#endif#ifdef CONFIG_CYZ_INTR		/* Recalculate the number of chars in the RX buffer and issue		   a cmd in case it's higher than the RX high water mark */			rx_put = readl(&buf_ctrl->rx_put);			if (rx_put >= rx_get)				char_count = rx_put - rx_get;			else				char_count = rx_put - rx_get + rx_bufsize;			if (char_count >= readl(&buf_ctrl->rx_threshold) &&					!timer_pending(&cyz_rx_full_timer[							info->line]))				mod_timer(&cyz_rx_full_timer[info->line],						jiffies + 1);#endif			info->idle_stats.recv_idle = jiffies;			tty_schedule_flip(tty);		}		/* Update rx_get */		cy_writel(&buf_ctrl->rx_get, new_rx_get);	}}static void cyz_handle_tx(struct cyclades_port *info,		struct BUF_CTRL __iomem *buf_ctrl){	struct cyclades_card *cinfo = info->card;	struct tty_struct *tty = info->tty;	u8 data;	unsigned int char_count;#ifdef BLOCKMOVE	int small_count;#endif	__u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;	if (info->xmit_cnt <= 0)	/* Nothing to transmit */		return;	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);	if (tx_put >= tx_get)		char_count = tx_get - tx_put - 1 + tx_bufsize;	else		char_count = tx_get - tx_put - 1;	if (char_count) {		if (tty == NULL)			goto ztxdone;		if (info->x_char) {	/* send special char */			data = info->x_char;			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++;		}#ifdef BLOCKMOVE		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),					&info->xmit_buf[info->xmit_tail],					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);		}#else		while (info->xmit_cnt && char_count) {			data = info->xmit_buf[info->xmit_tail];			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++;		}#endif		tty_wakeup(tty);ztxdone:		/* Update tx_put */		cy_writel(&buf_ctrl->tx_put, tx_put);	}}static void cyz_handle_cmd(struct cyclades_card *cinfo){	struct tty_struct *tty;	struct cyclades_port *info;	static struct FIRM_ID __iomem *firm_id;	static struct ZFW_CTRL __iomem *zfw_ctrl;	static struct BOARD_CTRL __iomem *board_ctrl;	static struct CH_CTRL __iomem *ch_ctrl;	static struct BUF_CTRL __iomem *buf_ctrl;	__u32 channel;	__u8 cmd;	__u32 param;	__u32 hw_ver, fw_ver;	int special_count;	int delta_count;	firm_id = cinfo->base_addr + ID_ADDRESS;	zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff);	board_ctrl = &zfw_ctrl->board_ctrl;	fw_ver = readl(&board_ctrl->fw_version);	hw_ver = readl(&((struct RUNTIME_9060 __iomem *)(cinfo->ctl_addr))->			mail_box_0);	while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {		special_count = 0;		delta_count = 0;		info = &cinfo->ports[channel];		if ((tty = info->tty) == NULL)			continue;

⌨️ 快捷键说明

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