/* * 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; }
static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct uart_pmac_port *uap = to_pmz(port); unsigned long baud; pmz_debug("pmz: set_termios()\n"); if (ZS_IS_ASLEEP(uap)) return; memcpy(&uap->termios_cache, termios, sizeof(struct ktermios)); /* XXX Check which revs of machines actually allow 1 and 4Mb speeds * on the IR dongle. Note that the IRTTY driver currently doesn't know * about the FIR mode and high speed modes. So these are unused. For * implementing proper support for these, we should probably add some * DMA as well, at least on the Rx side, which isn't a simple thing * at this point. */ if (ZS_IS_IRDA(uap)) { /* Calc baud rate */ baud = uart_get_baud_rate(port, termios, old, 1200, 4000000); pmz_debug("pmz: switch IRDA to %ld bauds\n", baud); /* Cet the irda codec to the right rate */ pmz_irda_setup(uap, &baud); /* Set final baud rate */ pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); pmz_load_zsregs(uap, uap->curregs); zssync(uap); } else { baud = uart_get_baud_rate(port, termios, old, 1200, 230400); pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); /* Make sure modem status interrupts are correctly configured */ if (UART_ENABLE_MS(&uap->port, termios->c_cflag)) { uap->curregs[R15] |= DCDIE | SYNCIE | CTSIE; uap->flags |= PMACZILOG_FLAG_MODEM_STATUS; } else { uap->curregs[R15] &= ~(DCDIE | SYNCIE | CTSIE); uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS; } /* Load registers to the chip */ pmz_maybe_update_regs(uap); } uart_update_timeout(port, termios->c_cflag, baud); pmz_debug("pmz: set_termios() 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"); }
/* * 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); }
/* * 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]); } }
/* * 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 held and interrupts are disabled. */ 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 = read_zsreg(uap, R0); ret = 0; if (status & DCD) ret |= TIOCM_CAR; if (status & SYNC_HUNT) ret |= TIOCM_DSR; if (!(status & CTS)) ret |= TIOCM_CTS; return ret; }
/* * Print a string to the serial port trying not to disturb * any possible real use of the port... */ static void pmz_console_write(struct console *con, const char *s, unsigned int count) { struct uart_pmac_port *uap = &pmz_ports[con->index]; unsigned long flags; if (ZS_IS_ASLEEP(uap)) return; spin_lock_irqsave(&uap->port.lock, flags); /* Turn of interrupts and enable the transmitter. */ write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); write_zsreg(uap, R5, uap->curregs[5] | TxENABLE | RTS | DTR); uart_console_write(&uap->port, s, count, pmz_console_putchar); /* Restore the values in the registers. */ write_zsreg(uap, R1, uap->curregs[1]); /* Don't disable the transmitter. */ spin_unlock_irqrestore(&uap->port.lock, flags); }
/* * Kick the Tx side. * The port lock is held and interrupts are disabled. */ static void pmz_start_tx(struct uart_port *port) { 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"); }
/* * 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; mutex_lock(&pmz_irq_mutex); 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, IRQF_SHARED, "PowerMac Zilog", uap)) { dev_err(&uap->dev->ofdev.dev, "Unable to register zs interrupt handler.\n"); pmz_set_scc_power(uap, 0); mutex_unlock(&pmz_irq_mutex); return -ENXIO; } mutex_unlock(&pmz_irq_mutex); /* 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); msleep(pwr_delay); } /* 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_transmit_chars(struct uart_pmac_port *uap) { struct circ_buf *xmit; if (ZS_IS_ASLEEP(uap)) return; if (ZS_IS_CONS(uap)) { unsigned char status = read_zsreg(uap, R0); /* TX still busy? Just wait for the next TX done interrupt. * * It can occur because of how we do serial console writes. It would * be nice to transmit console writes just like we normally would for * a TTY line. (ie. buffered and TX interrupt driven). That is not * easy because console writes cannot sleep. One solution might be * to poll on enough port->xmit space becomming free. -DaveM */ if (!(status & Tx_BUF_EMP)) return; } uap->flags &= ~PMACZILOG_FLAG_TX_ACTIVE; if (ZS_REGS_HELD(uap)) { pmz_load_zsregs(uap, uap->curregs); uap->flags &= ~PMACZILOG_FLAG_REGS_HELD; } if (ZS_TX_STOPPED(uap)) { uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; goto ack_tx_int; } if (uap->port.x_char) { uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; write_zsdata(uap, uap->port.x_char); zssync(uap); uap->port.icount.tx++; uap->port.x_char = 0; return; } if (uap->port.info == NULL) goto ack_tx_int; xmit = &uap->port.info->xmit; if (uart_circ_empty(xmit)) { uart_write_wakeup(&uap->port); goto ack_tx_int; } if (uart_tx_stopped(&uap->port)) goto ack_tx_int; uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; write_zsdata(uap, xmit->buf[xmit->tail]); zssync(uap); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uap->port.icount.tx++; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return; ack_tx_int: write_zsreg(uap, R0, RES_Tx_P); zssync(uap); }
/* * Load all registers to reprogram the port * This function must only be called when the TX is not busy. The UART * port lock must be held and local interrupts disabled. */ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) { int i; if (ZS_IS_ASLEEP(uap)) return; /* Let pending transmits finish. */ for (i = 0; i < 1000; i++) { unsigned char stat = read_zsreg(uap, R1); if (stat & ALL_SNT) break; udelay(100); } ZS_CLEARERR(uap); zssync(uap); ZS_CLEARFIFO(uap); zssync(uap); ZS_CLEARERR(uap); /* Disable all interrupts. */ write_zsreg(uap, R1, regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); /* Set parity, sync config, stop bits, and clock divisor. */ write_zsreg(uap, R4, regs[R4]); /* Set misc. TX/RX control bits. */ write_zsreg(uap, R10, regs[R10]); /* Set TX/RX controls sans the enable bits. */ write_zsreg(uap, R3, regs[R3] & ~RxENABLE); write_zsreg(uap, R5, regs[R5] & ~TxENABLE); /* now set R7 "prime" on ESCC */ write_zsreg(uap, R15, regs[R15] | EN85C30); write_zsreg(uap, R7, regs[R7P]); /* make sure we use R7 "non-prime" on ESCC */ write_zsreg(uap, R15, regs[R15] & ~EN85C30); /* Synchronous mode config. */ write_zsreg(uap, R6, regs[R6]); write_zsreg(uap, R7, regs[R7]); /* Disable baud generator. */ write_zsreg(uap, R14, regs[R14] & ~BRENAB); /* Clock mode control. */ write_zsreg(uap, R11, regs[R11]); /* Lower and upper byte of baud rate generator divisor. */ write_zsreg(uap, R12, regs[R12]); write_zsreg(uap, R13, regs[R13]); /* Now rewrite R14, with BRENAB (if set). */ write_zsreg(uap, R14, regs[R14]); /* Reset external status interrupts. */ write_zsreg(uap, R0, RES_EXT_INT); write_zsreg(uap, R0, RES_EXT_INT); /* Rewrite R3/R5, this time without enables masked. */ write_zsreg(uap, R3, regs[R3]); write_zsreg(uap, R5, regs[R5]); /* Rewrite R1, this time without IRQ enabled masked. */ write_zsreg(uap, R1, regs[R1]); /* Enable interrupts */ write_zsreg(uap, R9, regs[R9]); }
static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap, struct pt_regs *regs) { struct tty_struct *tty = NULL; unsigned char ch, r1, drop, error; int loops = 0; retry: /* The interrupt can be enabled when the port isn't open, typically * that happens when using one port is open and the other closed (stale * interrupt) or when one port is used as a console. */ if (!ZS_IS_OPEN(uap)) { pmz_debug("pmz: draining input\n"); /* Port is closed, drain input data */ for (;;) { if ((++loops) > 1000) goto flood; (void)read_zsreg(uap, R1); write_zsreg(uap, R0, ERR_RES); (void)read_zsdata(uap); ch = read_zsreg(uap, R0); if (!(ch & Rx_CH_AV)) break; } return NULL; } /* Sanity check, make sure the old bug is no longer happening */ if (uap->port.info == NULL || uap->port.info->tty == NULL) { WARN_ON(1); (void)read_zsdata(uap); return NULL; } tty = uap->port.info->tty; while (1) { error = 0; drop = 0; if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { /* Have to drop the lock here */ pmz_debug("pmz: flip overflow\n"); spin_unlock(&uap->port.lock); tty->flip.work.func((void *)tty); spin_lock(&uap->port.lock); if (tty->flip.count >= TTY_FLIPBUF_SIZE) drop = 1; if (ZS_IS_ASLEEP(uap)) return NULL; if (!ZS_IS_OPEN(uap)) goto retry; } r1 = read_zsreg(uap, R1); ch = read_zsdata(uap); if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { write_zsreg(uap, R0, ERR_RES); zssync(uap); } ch &= uap->parity_mask; if (ch == 0 && uap->prev_status & BRK_ABRT) r1 |= BRK_ABRT; /* A real serial line, record the character and status. */ if (drop) goto next_char; *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr = TTY_NORMAL; uap->port.icount.rx++; if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) { error = 1; if (r1 & BRK_ABRT) { pmz_debug("pmz: got break !\n"); r1 &= ~(PAR_ERR | CRC_ERR); uap->port.icount.brk++; if (uart_handle_break(&uap->port)) { pmz_debug("pmz: do handle break !\n"); goto next_char; } } else if (r1 & PAR_ERR) uap->port.icount.parity++; else if (r1 & CRC_ERR) uap->port.icount.frame++; if (r1 & Rx_OVR) uap->port.icount.overrun++; r1 &= uap->port.read_status_mask; if (r1 & BRK_ABRT) *tty->flip.flag_buf_ptr = TTY_BREAK; else if (r1 & PAR_ERR) *tty->flip.flag_buf_ptr = TTY_PARITY; else if (r1 & CRC_ERR) *tty->flip.flag_buf_ptr = TTY_FRAME; } if (uart_handle_sysrq_char(&uap->port, ch, regs)) { pmz_debug("pmz: sysrq swallowed the char\n"); goto next_char; } if (uap->port.ignore_status_mask == 0xff || (r1 & uap->port.ignore_status_mask) == 0) { tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } if ((r1 & Rx_OVR) && tty->flip.count < TTY_FLIPBUF_SIZE) { *tty->flip.flag_buf_ptr = TTY_OVERRUN; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } next_char: /* We can get stuck in an infinite loop getting char 0 when the * line is in a wrong HW state, we break that here. * When that happens, I disable the receive side of the driver. * Note that what I've been experiencing is a real irq loop where * I'm getting flooded regardless of the actual port speed. * Something stange is going on with the HW */ if ((++loops) > 1000) goto flood; ch = read_zsreg(uap, R0); if (!(ch & Rx_CH_AV)) break; } return tty; flood: uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); write_zsreg(uap, R1, uap->curregs[R1]); zssync(uap); dev_err(&uap->dev->ofdev.dev, "pmz: rx irq flood !\n"); return tty; }