📄 macserial.c
字号:
* 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 fix_zero_bug_scc(struct mac_serial * info){ write_zsreg(info->zs_channel, 9, (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); udelay(10); write_zsreg(info->zs_channel, 9, ((info->zs_channel == info->zs_chan_a? CHRA: CHRB) | NV)); write_zsreg(info->zs_channel, 4, (X1CLK | EXTSYNC)); /* I think this is wrong....but, I just copying code.... */ write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE)); write_zsreg(info->zs_channel, 5, (8 & ~TxENAB)); write_zsreg(info->zs_channel, 9, NV); /* Didn't we already do this? */ write_zsreg(info->zs_channel, 11, (RCBR | TCBR)); write_zsreg(info->zs_channel, 12, 0); write_zsreg(info->zs_channel, 13, 0); write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR)); write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR | BRENABL)); write_zsreg(info->zs_channel, 3, (8 | RxENABLE)); write_zsreg(info->zs_channel, 0, RES_EXT_INT); write_zsreg(info->zs_channel, 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(info->zs_channel, 9, NV); write_zsreg(info->zs_channel, 4, PAR_ENA); write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE)); while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) { (void)read_zsreg(info->zs_channel, 8); write_zsreg(info->zs_channel, 0, RES_EXT_INT); write_zsreg(info->zs_channel, 0, ERR_RES); }}static int setup_scc(struct mac_serial * info){ unsigned long flags; OPNDBG("setting up ttyS%d SCC...\n", info->line); save_flags(flags); cli(); /* Disable interrupts */ /* Nice buggy HW ... */ fix_zero_bug_scc(info); /* * Reset the chip. */ write_zsreg(info->zs_channel, 9, (info->zs_channel == info->zs_chan_a? CHRA: CHRB)); udelay(10); write_zsreg(info->zs_channel, 9, 0); /* * Clear the receive FIFO. */ ZS_CLEARFIFO(info->zs_channel); info->xmit_fifo_size = 1; /* * Reset DMAs */ if (info->has_dma) dma_init(info); /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); write_zsreg(info->zs_channel, 0, RES_H_IUS); /* * Turn on RTS and DTR. */ if (!info->is_irda) zs_rtsdtr(info, 1); /* * Finally, enable sequencing and interrupts */ if (!info->dma_initted) { /* interrupt on ext/status changes, all received chars, transmit ready */ info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); } else { /* interrupt on ext/status changes, W/Req pin is receive DMA request */ info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB)) | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); write_zsreg(info->zs_channel, 1, info->curregs[1]); /* enable W/Req pin */ info->curregs[1] |= WT_RDY_ENAB; write_zsreg(info->zs_channel, 1, info->curregs[1]); /* enable interrupts on transmit ready and receive errors */ info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB; } info->pendregs[1] = info->curregs[1]; info->curregs[3] |= (RxENABLE | Rx8); info->pendregs[3] = info->curregs[3]; info->curregs[5] |= (TxENAB | Tx8); info->pendregs[5] = info->curregs[5]; info->curregs[9] |= (NV | MIE); info->pendregs[9] = info->curregs[9]; write_zsreg(info->zs_channel, 3, info->curregs[3]); write_zsreg(info->zs_channel, 5, info->curregs[5]); write_zsreg(info->zs_channel, 9, info->curregs[9]); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * Set the speed of the serial port */ change_speed(info, 0); /* Save the current value of RR0 */ info->read_reg_zero = read_zsreg(info->zs_channel, 0); restore_flags(flags); if (info->dma_initted) { spin_lock_irqsave(&info->rx_dma_lock, flags); rxdma_start(info, 0); info->poll_dma_timer.expires = RX_DMA_TIMER; add_timer(&info->poll_dma_timer); spin_unlock_irqrestore(&info->rx_dma_lock, flags); } return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct mac_serial * info){ OPNDBG("Shutting down serial port %d (irq %d)....\n", info->line, info->irq); if (!(info->flags & ZILOG_INITIALIZED)) { OPNDBG("(already shutdown)\n"); return; } if (info->has_dma) { del_timer(&info->poll_dma_timer); dbdma_reset(info->tx_dma); dbdma_reset(&info->rx->dma); disable_irq(info->tx_dma_irq); disable_irq(info->rx_dma_irq); } disable_irq(info->irq); info->pendregs[1] = info->curregs[1] = 0; write_zsreg(info->zs_channel, 1, 0); /* no interrupts */ info->curregs[3] &= ~RxENABLE; info->pendregs[3] = info->curregs[3]; write_zsreg(info->zs_channel, 3, info->curregs[3]); info->curregs[5] &= ~TxENAB; if (!info->tty || C_HUPCL(info->tty)) info->curregs[5] &= ~DTR; info->pendregs[5] = info->curregs[5]; write_zsreg(info->zs_channel, 5, info->curregs[5]); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); set_scc_power(info, 0); if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } if (info->has_dma && info->dma_priv) { kfree(info->dma_priv); info->dma_priv = NULL; info->dma_initted = 0; } memset(info->curregs, 0, sizeof(info->curregs)); memset(info->pendregs, 0, sizeof(info->pendregs)); info->flags &= ~ZILOG_INITIALIZED;}/* * 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 set_scc_power(struct mac_serial * info, int state){ int delay = 0; if (state) { PWRDBG("ttyS%d: powering up hardware\n", info->line); pmac_call_feature( PMAC_FTR_SCC_ENABLE, info->dev_node, info->port_type, 1); if (info->is_internal_modem) { pmac_call_feature( PMAC_FTR_MODEM_ENABLE, info->dev_node, 0, 1); delay = 2500; /* wait for 2.5s before using */ } else if (info->is_irda) mdelay(50); /* Do better here once the problems * with blocking have been ironed out */ } else { /* TODO: Make that depend on a timer, don't power down * immediately */ PWRDBG("ttyS%d: shutting down hardware\n", info->line); if (info->is_internal_modem) { PWRDBG("ttyS%d: shutting down modem\n", info->line); pmac_call_feature( PMAC_FTR_MODEM_ENABLE, info->dev_node, 0, 0); } pmac_call_feature( PMAC_FTR_SCC_ENABLE, info->dev_node, info->port_type, 0); } return delay;}static void irda_rts_pulses(struct mac_serial *info, int w){ unsigned long flags; udelay(w); save_flags(flags); cli(); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); udelay(2); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); udelay(8); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB); udelay(4); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); restore_flags(flags);}/* * Set the irda codec on the imac to the specified baud rate. */static void irda_setup(struct mac_serial *info){ int code, speed, t; unsigned long flags; speed = info->tty->termios->c_cflag & CBAUD; if (speed < B2400 || speed > B115200) return; code = 0x4d + B115200 - speed; /* disable serial interrupts and receive DMA */ write_zsreg(info->zs_channel, 1, info->curregs[1] & ~0x9f); /* wait for transmitter to drain */ t = 10000; while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0 || (read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { if (--t <= 0) { printk(KERN_ERR "transmitter didn't drain\n"); return; } udelay(10); } udelay(100); /* set to 8 bits, no parity, 19200 baud, RTS on, DTR off */ write_zsreg(info->zs_channel, 4, X16CLK | SB1); write_zsreg(info->zs_channel, 11, TCBR | RCBR); t = BPS_TO_BRG(19200, ZS_CLOCK/16); write_zsreg(info->zs_channel, 12, t); write_zsreg(info->zs_channel, 13, t >> 8); write_zsreg(info->zs_channel, 14, BRENABL); write_zsreg(info->zs_channel, 3, Rx8 | RxENABLE); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); /* set TxD low for ~104us and pulse RTS */ udelay(1000); save_flags(flags); cli(); write_zsdata(info->zs_channel, 0xfe); irda_rts_pulses(info, 150); restore_flags(flags); irda_rts_pulses(info, 180); irda_rts_pulses(info, 50); udelay(100); /* assert DTR, wait 30ms, talk to the chip */ write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS | DTR); mdelay(30); while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) read_zsdata(info->zs_channel); write_zsdata(info->zs_channel, 1); t = 1000; while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { if (--t <= 0) { printk(KERN_ERR "irda_setup timed out on 1st byte\n"); goto out; } udelay(10); } t = read_zsdata(info->zs_channel); if (t != 4) printk(KERN_ERR "irda_setup 1st byte = %x\n", t); write_zsdata(info->zs_channel, code); t = 1000; while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) { if (--t <= 0) { printk(KERN_ERR "irda_setup timed out on 2nd byte\n"); goto out; } udelay(10); } t = read_zsdata(info->zs_channel); if (t != code) printk(KERN_ERR "irda_setup 2nd byte = %x (%x)\n", t, code); /* Drop DTR again and do some more RTS pulses */ out: udelay(100); write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS); irda_rts_pulses(info, 80); /* We should be right to go now. We assume that load_zsregs will get called soon to load up the correct baud rate etc. */ info->curregs[5] = (info->curregs[5] | RTS) & ~DTR; info->pendregs[5] = info->curregs[5];}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct mac_serial *info, struct termios *old_termios){ unsigned cflag; int bits; int brg, baud; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; baud = tty_get_baud_rate(info->tty); if (baud == 0) { if (old_termios) { info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); cflag = info->tty->termios->c_cflag; baud = tty_get_baud_rate(info->tty); } else baud = info->zs_baud; } if (baud > 230400) baud = 230400; else if (baud == 0) baud = 38400; save_flags(flags); cli(); info->zs_baud = baud; info->clk_divisor = 16; BAUDBG(KERN_DEBUG "set speed to %d bds, ", baud); switch (baud) { case ZS_CLOCK/16: /* 230400 */ info->curregs[4] = X16CLK; info->curregs[11] = 0; break; case ZS_CLOCK/32: /* 115200 */ info->curregs[4] = X32CLK; info->curregs[11] = 0; break; default: info->curregs[4] = X16CLK; info->curregs[11] = TCBR | RCBR; brg = BPS_TO_BRG(baud, ZS_CLOCK/info->clk_divisor); info->curregs[12] = (brg & 255); info->curregs[13] = ((brg >> 8) & 255); info->curregs[14] = BRENABL; } /* byte size and parity */ info->curregs[3] &= ~RxNBITS_MASK; info->curregs[5] &= ~TxNBITS_MASK; switch (cflag & CSIZE) { case CS5: info->curregs[3] |= Rx5; info->curregs[5] |= Tx5; BAUDBG("5 bits, "); bits = 7; break; case CS6: info->curregs[3] |= Rx6; info->curregs[5] |= Tx6; BAUDBG("6 bits, "); bits = 8; break; case CS7: info->curregs[3] |= Rx7; info->curregs[5] |= Tx7; BAUDBG("7 bits, "); bits = 9; break; case CS8: default: /* defaults to 8 bits */ info->curregs[3] |= Rx8; info->curregs[5] |= Tx8; BAUDBG("8 bits, "); bits = 10; break; } info->pendregs[3] = info->curregs[3]; info->pendregs[5] = info->curregs[5]; info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); if (cflag & CSTOPB) { info->curregs[4] |= SB2; bits++; BAUDBG("2 stop, "); } else { info->curregs[4] |= SB1; BAUDBG("1 stop, "); } if (cflag & PARENB) { bits++; info->curregs[4] |= PAR_ENA; BAUDBG("parity, "); } if (!(cflag & PARODD)) { info->curregs[4] |= PAR_EVEN; } info->pendregs[4] = info->curregs[4]; if (!(cflag & CLOCAL)) { if (!(info->curregs[15] & DCDIE)) info->read_reg_zero = read_zsreg(info->zs_channel, 0); info->curregs[15] |= DCDIE; } else info->curregs[15] &= ~DCDIE; if (cflag & CRTSCTS) { info->curregs[15] |= CTSIE; if ((read_zsreg(info->zs_channel, 0) & CTS) != 0) info->tx_stopped = 1; } else { info->curregs[15] &= ~CTSIE; info->tx_stopped = 0; } info->pendregs[15] = info->curregs[15]; /* Calc timeout value. This is pretty broken with high baud rates with HZ=100. This code would love a larger HZ and a >1 fifo size, but this is not a priority. The resulting value must be >HZ/2 */ info->timeout = ((info->xmit_fifo_size*HZ*bits) / baud); info->timeout += HZ/50+1; /* Add .02 seconds of slop */ BAUDBG("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ, (int)info->baud_base); /* set the irda codec to the right rate */ if (info->is_irda) irda_setup(info); /* Load up the new values */ load_zsregs(info->zs_channel, info->curregs); restore_flags(flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -