📄 pmac_zilog.c
字号:
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 + -