⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pmac_zilog.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
	unsigned long flags;	u8 status;		spin_lock_irqsave(&uap->port.lock, flags);	status = read_zsreg(uap, R0);	spin_unlock_irqrestore(&uap->port.lock, flags);	return status;}/*  * Check if transmitter is empty * The port lock is not held. */static unsigned int pmz_tx_empty(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char status;	if (ZS_IS_ASLEEP(uap) || uap->node == NULL)		return TIOCSER_TEMT;	status = pmz_peek_status(to_pmz(port));	if (status & Tx_BUF_EMP)		return TIOCSER_TEMT;	return 0;}/*  * Set Modem Control (RTS & DTR) bits * The port lock is held and interrupts are disabled. * Note: Shall we really filter out RTS on external ports or * should that be dealt at higher level only ? */static void pmz_set_mctrl(struct uart_port *port, unsigned int mctrl){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char set_bits, clear_bits;        /* Do nothing for irda for now... */	if (ZS_IS_IRDA(uap))		return;	/* We get called during boot with a port not up yet */	if (ZS_IS_ASLEEP(uap) ||	    !(ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)))		return;	set_bits = clear_bits = 0;	if (ZS_IS_INTMODEM(uap)) {		if (mctrl & TIOCM_RTS)			set_bits |= RTS;		else			clear_bits |= RTS;	}	if (mctrl & TIOCM_DTR)		set_bits |= DTR;	else		clear_bits |= DTR;	/* NOTE: Not subject to 'transmitter active' rule.  */ 	uap->curregs[R5] |= set_bits;	uap->curregs[R5] &= ~clear_bits;	if (ZS_IS_ASLEEP(uap))		return;	write_zsreg(uap, R5, uap->curregs[R5]);	pmz_debug("pmz_set_mctrl: set bits: %x, clear bits: %x -> %x\n",		  set_bits, clear_bits, uap->curregs[R5]);	zssync(uap);}/*  * Get Modem Control bits (only the input ones, the core will * or that with a cached value of the control ones) * The port lock is not held. */static unsigned int pmz_get_mctrl(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char status;	unsigned int ret;	if (ZS_IS_ASLEEP(uap) || uap->node == NULL)		return 0;	status = pmz_peek_status(to_pmz(port));	ret = 0;	if (status & DCD)		ret |= TIOCM_CAR;	if (status & SYNC_HUNT)		ret |= TIOCM_DSR;	if (!(status & CTS))		ret |= TIOCM_CTS;	return ret;}/*  * Stop TX side. Dealt like sunzilog at next Tx interrupt, * though for DMA, we will have to do a bit more. What is * the meaning of the tty_stop bit ? XXX * The port lock is held and interrupts are disabled. */static void pmz_stop_tx(struct uart_port *port, unsigned int tty_stop){	to_pmz(port)->flags |= PMACZILOG_FLAG_TX_STOPPED;}/*  * Kick the Tx side. * The port lock is held and interrupts are disabled. */static void pmz_start_tx(struct uart_port *port, unsigned int tty_start){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char status;	pmz_debug("pmz: start_tx()\n");	uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;	uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED;	if (ZS_IS_ASLEEP(uap) || uap->node == NULL)		return;	status = read_zsreg(uap, R0);	/* TX busy?  Just wait for the TX done interrupt.  */	if (!(status & Tx_BUF_EMP))		return;	/* Send the first character to jump-start the TX done	 * IRQ sending engine.	 */	if (port->x_char) {		write_zsdata(uap, port->x_char);		zssync(uap);		port->icount.tx++;		port->x_char = 0;	} else {		struct circ_buf *xmit = &port->info->xmit;		write_zsdata(uap, xmit->buf[xmit->tail]);		zssync(uap);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		port->icount.tx++;		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)			uart_write_wakeup(&uap->port);	}	pmz_debug("pmz: start_tx() done.\n");}/*  * Stop Rx side, basically disable emitting of * Rx interrupts on the port. We don't disable the rx * side of the chip proper though * The port lock is held. */static void pmz_stop_rx(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	if (ZS_IS_ASLEEP(uap) || uap->node == NULL)		return;	pmz_debug("pmz: stop_rx()()\n");	/* Disable all RX interrupts.  */	uap->curregs[R1] &= ~RxINT_MASK;	pmz_maybe_update_regs(uap);	pmz_debug("pmz: stop_rx() done.\n");}/*  * Enable modem status change interrupts * The port lock is held. */static void pmz_enable_ms(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char new_reg;	if (ZS_IS_IRDA(uap) || uap->node == NULL)		return;	new_reg = uap->curregs[R15] | (DCDIE | SYNCIE | CTSIE);	if (new_reg != uap->curregs[R15]) {		uap->curregs[R15] = new_reg;		if (ZS_IS_ASLEEP(uap))			return;		/* NOTE: Not subject to 'transmitter active' rule.  */ 		write_zsreg(uap, R15, uap->curregs[R15]);	}}/*  * Control break state emission * The port lock is not held. */static void pmz_break_ctl(struct uart_port *port, int break_state){	struct uart_pmac_port *uap = to_pmz(port);	unsigned char set_bits, clear_bits, new_reg;	unsigned long flags;	if (uap->node == NULL)		return;	set_bits = clear_bits = 0;	if (break_state)		set_bits |= SND_BRK;	else		clear_bits |= SND_BRK;	spin_lock_irqsave(&port->lock, flags);	new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits;	if (new_reg != uap->curregs[R5]) {		uap->curregs[R5] = new_reg;		/* NOTE: Not subject to 'transmitter active' rule.  */ 		if (ZS_IS_ASLEEP(uap))			return;		write_zsreg(uap, R5, uap->curregs[R5]);	}	spin_unlock_irqrestore(&port->lock, flags);}/* * Turn power on or off to the SCC and associated stuff * (port drivers, modem, IR port, etc.) * Returns the number of milliseconds we should wait before * trying to use the port. */static int pmz_set_scc_power(struct uart_pmac_port *uap, int state){	int delay = 0;	int rc;	if (state) {		rc = pmac_call_feature(			PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 1);		pmz_debug("port power on result: %d\n", rc);		if (ZS_IS_INTMODEM(uap)) {			rc = pmac_call_feature(				PMAC_FTR_MODEM_ENABLE, uap->node, 0, 1);			delay = 2500;	/* wait for 2.5s before using */			pmz_debug("modem power result: %d\n", rc);		}	} else {		/* TODO: Make that depend on a timer, don't power down		 * immediately		 */		if (ZS_IS_INTMODEM(uap)) {			rc = pmac_call_feature(				PMAC_FTR_MODEM_ENABLE, uap->node, 0, 0);			pmz_debug("port power off result: %d\n", rc);		}		pmac_call_feature(PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 0);	}	return delay;}/* * FixZeroBug....Works around a bug in the SCC receving channel. * Inspired from Darwin code, 15 Sept. 2000  -DanM * * The following sequence prevents a problem that is seen with O'Hare ASICs * (most versions -- also with some Heathrow and Hydra ASICs) where a zero * at the input to the receiver becomes 'stuck' and locks up the receiver. * This problem can occur as a result of a zero bit at the receiver input * coincident with any of the following events: * *	The SCC is initialized (hardware or software). *	A framing error is detected. *	The clocking option changes from synchronous or X1 asynchronous *		clocking to X16, X32, or X64 asynchronous clocking. *	The decoding mode is changed among NRZ, NRZI, FM0, or FM1. * * This workaround attempts to recover from the lockup condition by placing * the SCC in synchronous loopback mode with a fast clock before programming * any of the asynchronous modes. */static void pmz_fix_zero_bug_scc(struct uart_pmac_port *uap){	write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB);	zssync(uap);	udelay(10);	write_zsreg(uap, 9, (ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB) | NV);	zssync(uap);	write_zsreg(uap, 4, X1CLK | MONSYNC);	write_zsreg(uap, 3, Rx8);	write_zsreg(uap, 5, Tx8 | RTS);	write_zsreg(uap, 9, NV);	/* Didn't we already do this? */	write_zsreg(uap, 11, RCBR | TCBR);	write_zsreg(uap, 12, 0);	write_zsreg(uap, 13, 0);	write_zsreg(uap, 14, (LOOPBAK | BRSRC));	write_zsreg(uap, 14, (LOOPBAK | BRSRC | BRENAB));	write_zsreg(uap, 3, Rx8 | RxENABLE);	write_zsreg(uap, 0, RES_EXT_INT);	write_zsreg(uap, 0, RES_EXT_INT);	write_zsreg(uap, 0, RES_EXT_INT);	/* to kill some time */	/* The channel should be OK now, but it is probably receiving	 * loopback garbage.	 * Switch to asynchronous mode, disable the receiver,	 * and discard everything in the receive buffer.	 */	write_zsreg(uap, 9, NV);	write_zsreg(uap, 4, X16CLK | SB_MASK);	write_zsreg(uap, 3, Rx8);	while (read_zsreg(uap, 0) & Rx_CH_AV) {		(void)read_zsreg(uap, 8);		write_zsreg(uap, 0, RES_EXT_INT);		write_zsreg(uap, 0, ERR_RES);	}}/* * Real startup routine, powers up the hardware and sets up * the SCC. Returns a delay in ms where you need to wait before * actually using the port, this is typically the internal modem * powerup delay. This routine expect the lock to be taken. */static int __pmz_startup(struct uart_pmac_port *uap){	int pwr_delay = 0;	memset(&uap->curregs, 0, sizeof(uap->curregs));	/* Power up the SCC & underlying hardware (modem/irda) */	pwr_delay = pmz_set_scc_power(uap, 1);	/* Nice buggy HW ... */	pmz_fix_zero_bug_scc(uap);	/* Reset the channel */	uap->curregs[R9] = 0;	write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB);	zssync(uap);	udelay(10);	write_zsreg(uap, 9, 0);	zssync(uap);	/* Clear the interrupt registers */	write_zsreg(uap, R1, 0);	write_zsreg(uap, R0, ERR_RES);	write_zsreg(uap, R0, ERR_RES);	write_zsreg(uap, R0, RES_H_IUS);	write_zsreg(uap, R0, RES_H_IUS);	/* Setup some valid baud rate */	uap->curregs[R4] = X16CLK | SB1;	uap->curregs[R3] = Rx8;	uap->curregs[R5] = Tx8 | RTS;	if (!ZS_IS_IRDA(uap))		uap->curregs[R5] |= DTR;	uap->curregs[R12] = 0;	uap->curregs[R13] = 0;	uap->curregs[R14] = BRENAB;	/* Clear handshaking */	uap->curregs[R15] = 0;	/* Master interrupt enable */	uap->curregs[R9] |= NV | MIE;	pmz_load_zsregs(uap, uap->curregs);	/* Enable receiver and transmitter.  */	write_zsreg(uap, R3, uap->curregs[R3] |= RxENABLE);	write_zsreg(uap, R5, uap->curregs[R5] |= TxENABLE);	/* Remember status for DCD/CTS changes */	uap->prev_status = read_zsreg(uap, R0);	return pwr_delay;}static void pmz_irda_reset(struct uart_pmac_port *uap){	uap->curregs[R5] |= DTR;	write_zsreg(uap, R5, uap->curregs[R5]);	zssync(uap);	mdelay(110);	uap->curregs[R5] &= ~DTR;	write_zsreg(uap, R5, uap->curregs[R5]);	zssync(uap);	mdelay(10);}/* * This is the "normal" startup routine, using the above one * wrapped with the lock and doing a schedule delay */static int pmz_startup(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	unsigned long flags;	int pwr_delay = 0;	pmz_debug("pmz: startup()\n");	if (ZS_IS_ASLEEP(uap))		return -EAGAIN;	if (uap->node == NULL)		return -ENODEV;	down(&pmz_irq_sem);	uap->flags |= PMACZILOG_FLAG_IS_OPEN;	/* A console is never powered down. Else, power up and	 * initialize the chip	 */	if (!ZS_IS_CONS(uap)) {		spin_lock_irqsave(&port->lock, flags);		pwr_delay = __pmz_startup(uap);		spin_unlock_irqrestore(&port->lock, flags);	}		pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON;	if (request_irq(uap->port.irq, pmz_interrupt, SA_SHIRQ, "PowerMac Zilog", uap)) {		dev_err(&uap->dev->ofdev.dev,			"Unable to register zs interrupt handler.\n");		pmz_set_scc_power(uap, 0);		up(&pmz_irq_sem);		return -ENXIO;	}	up(&pmz_irq_sem);	/* Right now, we deal with delay by blocking here, I'll be	 * smarter later on	 */	if (pwr_delay != 0) {		pmz_debug("pmz: delaying %d ms\n", pwr_delay);		set_current_state(TASK_UNINTERRUPTIBLE);		schedule_timeout((pwr_delay * HZ)/1000);	}	/* IrDA reset is done now */	if (ZS_IS_IRDA(uap))		pmz_irda_reset(uap);	/* Enable interrupts emission from the chip */	spin_lock_irqsave(&port->lock, flags);	uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB;	if (!ZS_IS_EXTCLK(uap))		uap->curregs[R1] |= EXT_INT_ENAB;	write_zsreg(uap, R1, uap->curregs[R1]);       	spin_unlock_irqrestore(&port->lock, flags);	pmz_debug("pmz: startup() done.\n");	return 0;}static void pmz_shutdown(struct uart_port *port){	struct uart_pmac_port *uap = to_pmz(port);	unsigned long flags;	pmz_debug("pmz: shutdown()\n");	if (uap->node == NULL)		return;	down(&pmz_irq_sem);	/* Release interrupt handler */       	free_irq(uap->port.irq, uap);	spin_lock_irqsave(&port->lock, flags);	uap->flags &= ~PMACZILOG_FLAG_IS_OPEN;	if (!ZS_IS_OPEN(uap->mate))		pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON;	/* Disable interrupts */	if (!ZS_IS_ASLEEP(uap)) {		uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);		write_zsreg(uap, R1, uap->curregs[R1]);		zssync(uap);	}	if (ZS_IS_CONS(uap) || ZS_IS_ASLEEP(uap)) {		spin_unlock_irqrestore(&port->lock, flags);		up(&pmz_irq_sem);		return;	}	/* Disable receiver and transmitter.  */	uap->curregs[R3] &= ~RxENABLE;	uap->curregs[R5] &= ~TxENABLE;	/* Disable all interrupts and BRK assertion.  */

⌨️ 快捷键说明

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