📄 macserial.c
字号:
*/ 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 | RTS); 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->curregs, 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 (feature_test(info->dev_node, FEATURE_Serial_enable) < 0) return 0; /* don't have serial power control */ /* The timings looks strange but that's the ones MacOS seems to use for the internal modem. I think we can use a lot faster ones, at least whe not using the modem, this should be tested. */ if (state) { PWRDBG("ttyS%02d: powering up hardware\n", info->line); if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) { feature_set(info->dev_node, FEATURE_Serial_enable); mdelay(10); feature_set(info->dev_node, FEATURE_Serial_reset); mdelay(15); feature_clear(info->dev_node, FEATURE_Serial_reset); mdelay(10); } if (info->zs_chan_a == info->zs_channel) feature_set(info->dev_node, FEATURE_Serial_IO_A); else feature_set(info->dev_node, FEATURE_Serial_IO_B); delay = 10; if (info->is_cobalt_modem){ mdelay(300); feature_set(info->dev_node, FEATURE_Modem_power); mdelay(5); feature_clear(info->dev_node, FEATURE_Modem_power); mdelay(10); feature_set(info->dev_node, FEATURE_Modem_power); delay = 2500; /* wait for 2.5s before using */ }#ifdef CONFIG_PMAC_PBOOK if (info->is_irda) pmu_enable_irled(1);#endif /* CONFIG_PMAC_PBOOK */ } else { PWRDBG("ttyS%02d: shutting down hardware\n", info->line); if (info->is_cobalt_modem) { PWRDBG("ttyS%02d: shutting down modem\n", info->line); feature_clear(info->dev_node, FEATURE_Modem_power); mdelay(10); }#ifdef CONFIG_PMAC_PBOOK if (info->is_irda) pmu_enable_irled(0);#endif /* CONFIG_PMAC_PBOOK */ if (info->zs_chan_a == info->zs_channel && !info->is_irda) { PWRDBG("ttyS%02d: shutting down SCC channel A\n", info->line); feature_clear(info->dev_node, FEATURE_Serial_IO_A); } else if (!info->is_irda) { PWRDBG("ttyS%02d: shutting down SCC channel B\n", info->line); feature_clear(info->dev_node, FEATURE_Serial_IO_B); } /* XXX for now, shut down SCC core only on powerbooks */ if (is_powerbook && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) || feature_test(info->dev_node, FEATURE_Serial_IO_B))) { PWRDBG("ttyS%02d: shutting down SCC core\n", info->line); feature_set(info->dev_node, FEATURE_Serial_reset); mdelay(15); feature_clear(info->dev_node, FEATURE_Serial_reset); mdelay(25); feature_clear(info->dev_node, FEATURE_Serial_enable); mdelay(5); } } 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); udelay(30000); 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);}static void rs_flush_chars(struct tty_struct *tty){ struct mac_serial *info = (struct mac_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped || !info->xmit_buf) return; /* Enable transmitter */ transmit_chars(info);}static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, ret = 0; struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit_buf || !tmp_buf) return 0; if (from_user) { down(&tmp_buf_sem); while (1) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } save_flags(flags); cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -