static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); struct uart_state *state; unsigned long flags; if (uap == NULL) { printk("HRM... pmz_suspend with NULL uap\n"); return 0; } if (pm_state.event == mdev->ofdev.dev.power.power_state.event) return 0; pmz_debug("suspend, switching to state %d\n", pm_state); state = pmz_uart_reg.state + uap->port.line; mutex_lock(&pmz_irq_mutex); mutex_lock(&state->mutex); spin_lock_irqsave(&uap->port.lock, flags); if (ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)) { /* Disable receiver and transmitter. */ uap->curregs[R3] &= ~RxENABLE; uap->curregs[R5] &= ~TxENABLE; /* Disable all interrupts and BRK assertion. */ uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); uap->curregs[R5] &= ~SND_BRK; pmz_load_zsregs(uap, uap->curregs); uap->flags |= PMACZILOG_FLAG_IS_ASLEEP; mb(); } spin_unlock_irqrestore(&uap->port.lock, flags); if (ZS_IS_OPEN(uap) || ZS_IS_OPEN(uap->mate)) if (ZS_IS_ASLEEP(uap->mate) && ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; disable_irq(uap->port.irq); } if (ZS_IS_CONS(uap)) uap->port.cons->flags &= ~CON_ENABLED; /* Shut the chip down */ pmz_set_scc_power(uap, 0); mutex_unlock(&state->mutex); mutex_unlock(&pmz_irq_mutex); pmz_debug("suspend, switching complete\n"); mdev->ofdev.dev.power.power_state = pm_state; 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; mutex_lock(&pmz_irq_mutex); /* 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); mutex_unlock(&pmz_irq_mutex); return; } /* Disable receiver and transmitter. */ uap->curregs[R3] &= ~RxENABLE; uap->curregs[R5] &= ~TxENABLE; /* Disable all interrupts and BRK assertion. */ uap->curregs[R5] &= ~SND_BRK; pmz_maybe_update_regs(uap); /* Shut the chip down */ pmz_set_scc_power(uap, 0); spin_unlock_irqrestore(&port->lock, flags); mutex_unlock(&pmz_irq_mutex); pmz_debug("pmz: shutdown() done.\n"); }
static int ip22zilog_startup(struct uart_port *port) { struct uart_ip22zilog_port *up = UART_ZILOG(port); unsigned long flags; if (ZS_IS_CONS(up)) return 0; spin_lock_irqsave(&port->lock, flags); __ip22zilog_startup(up); spin_unlock_irqrestore(&port->lock, flags); return 0; }
/* The port lock is held and interrupts are disabled. */ static void ip22zilog_stop_rx(struct uart_port *port) { struct uart_ip22zilog_port *up = UART_ZILOG(port); struct zilog_channel *channel; if (ZS_IS_CONS(up)) return; channel = ZILOG_CHANNEL_FROM_PORT(port); /* Disable all RX interrupts. */ up->curregs[R1] &= ~RxINT_MASK; ip22zilog_maybe_update_regs(up, channel); }
/* * The test for ZS_IS_CONS is explained by the following e-mail: ***** * From: Russell King <*****@*****.**> * Date: Sun, 8 Dec 2002 10:18:38 +0000 * * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, * > and I noticed that something is not right with reference * > counting in this case. It seems that when the console * > is open by kernel initially, this is not accounted * > as an open, and uart_startup is not called. * * That is correct. We are unable to call uart_startup when the serial * console is initialised because it may need to allocate memory (as * request_irq does) and the memory allocators may not have been * initialised. * * 1. initialise the port into a state where it can send characters in the * console write method. * * 2. don't do the actual hardware shutdown in your shutdown() method (but * do the normal software shutdown - ie, free irqs etc) ***** */ static void ip22zilog_shutdown(struct uart_port *port) { struct uart_ip22zilog_port *up = UART_ZILOG(port); struct zilog_channel *channel; unsigned long flags; if (ZS_IS_CONS(up)) return; spin_lock_irqsave(&port->lock, flags); channel = ZILOG_CHANNEL_FROM_PORT(port); /* Disable receiver and transmitter. */ up->curregs[R3] &= ~RxENAB; up->curregs[R5] &= ~TxENAB; /* Disable all interrupts and BRK assertion. */ up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); up->curregs[R5] &= ~SND_BRK; ip22zilog_maybe_update_regs(up, channel); spin_unlock_irqrestore(&port->lock, flags); }
/* * 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); }
static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel) { struct circ_buf *xmit; if (ZS_IS_CONS(up)) { unsigned char status = readb(&channel->control); ZSDELAY(); /* 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 becoming free. -DaveM */ if (!(status & Tx_BUF_EMP)) return; } up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE; if (ZS_REGS_HELD(up)) { __load_zsregs(channel, up->curregs); up->flags &= ~IP22ZILOG_FLAG_REGS_HELD; } if (ZS_TX_STOPPED(up)) { up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; goto ack_tx_int; } if (up->port.x_char) { up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; writeb(up->port.x_char, &channel->data); ZSDELAY(); ZS_WSYNC(channel); up->port.icount.tx++; up->port.x_char = 0; return; } if (up->port.state == NULL) goto ack_tx_int; xmit = &up->port.state->xmit; if (uart_circ_empty(xmit)) goto ack_tx_int; if (uart_tx_stopped(&up->port)) goto ack_tx_int; up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; writeb(xmit->buf[xmit->tail], &channel->data); ZSDELAY(); ZS_WSYNC(channel); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); return; ack_tx_int: writeb(RES_Tx_P, &channel->control); ZSDELAY(); ZS_WSYNC(channel); }
/* * 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); }
static int pmz_resume(struct macio_dev *mdev) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); struct uart_state *state; unsigned long flags; int pwr_delay = 0; if (uap == NULL) return 0; if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON) return 0; pmz_debug("resume, switching to state 0\n"); state = pmz_uart_reg.state + uap->port.line; mutex_lock(&pmz_irq_mutex); mutex_lock(&state->mutex); spin_lock_irqsave(&uap->port.lock, flags); if (!ZS_IS_OPEN(uap) && !ZS_IS_CONS(uap)) { spin_unlock_irqrestore(&uap->port.lock, flags); goto bail; } pwr_delay = __pmz_startup(uap); /* Take care of config that may have changed while asleep */ __pmz_set_termios(&uap->port, &uap->termios_cache, NULL); if (ZS_IS_OPEN(uap)) { /* Enable interrupts */ 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(&uap->port.lock, flags); if (ZS_IS_CONS(uap)) uap->port.cons->flags |= CON_ENABLED; /* Re-enable IRQ on the controller */ if (ZS_IS_OPEN(uap) && !ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; enable_irq(uap->port.irq); } bail: mutex_unlock(&state->mutex); 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); } pmz_debug("resume, switching complete\n"); mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON; return 0; }
static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel) { struct tty_struct *tty = up->port.info->tty; /* XXX info==NULL? */ while (1) { unsigned char ch, r1, flag; r1 = read_zsreg(channel, R1); if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { writeb(ERR_RES, &channel->control); ZSDELAY(); ZS_WSYNC(channel); } ch = readb(&channel->control); ZSDELAY(); /* This funny hack depends upon BRK_ABRT not interfering * with the other bits we care about in R1. */ if (ch & BRK_ABRT) r1 |= BRK_ABRT; ch = readb(&channel->data); ZSDELAY(); ch &= up->parity_mask; if (ZS_IS_CONS(up) && (r1 & BRK_ABRT)) { /* Wait for BREAK to deassert to avoid potentially * confusing the PROM. */ while (1) { ch = readb(&channel->control); ZSDELAY(); if (!(ch & BRK_ABRT)) break; } ip22_do_break(); return; } /* A real serial line, record the character and status. */ flag = TTY_NORMAL; up->port.icount.rx++; if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { if (r1 & BRK_ABRT) { r1 &= ~(PAR_ERR | CRC_ERR); up->port.icount.brk++; if (uart_handle_break(&up->port)) goto next_char; } else if (r1 & PAR_ERR) up->port.icount.parity++; else if (r1 & CRC_ERR) up->port.icount.frame++; if (r1 & Rx_OVR) up->port.icount.overrun++; r1 &= up->port.read_status_mask; if (r1 & BRK_ABRT) flag = TTY_BREAK; else if (r1 & PAR_ERR) flag = TTY_PARITY; else if (r1 & CRC_ERR) flag = TTY_FRAME; } if (uart_handle_sysrq_char(&up->port, ch)) goto next_char; if (up->port.ignore_status_mask == 0xff || (r1 & up->port.ignore_status_mask) == 0) tty_insert_flip_char(tty, ch, flag); if (r1 & Rx_OVR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); next_char: ch = readb(&channel->control); ZSDELAY(); if (!(ch & Rx_CH_AV)) break; } tty_flip_buffer_push(tty); }
static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel, struct pt_regs *regs) { struct tty_struct *tty = up->port.info->tty; /* XXX info==NULL? */ while (1) { unsigned char ch, r1; if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return; /* XXX Ignores SysRq when we need it most. Fix. */ } r1 = read_zsreg(channel, R1); if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { writeb(ERR_RES, &channel->control); ZSDELAY(); ZS_WSYNC(channel); } ch = readb(&channel->control); ZSDELAY(); /* This funny hack depends upon BRK_ABRT not interfering * with the other bits we care about in R1. */ if (ch & BRK_ABRT) r1 |= BRK_ABRT; ch = readb(&channel->data); ZSDELAY(); ch &= up->parity_mask; if (ZS_IS_CONS(up) && (r1 & BRK_ABRT)) { /* Wait for BREAK to deassert to avoid potentially * confusing the PROM. */ while (1) { ch = readb(&channel->control); ZSDELAY(); if (!(ch & BRK_ABRT)) break; } ip22_do_break(); return; } /* A real serial line, record the character and status. */ *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr = TTY_NORMAL; up->port.icount.rx++; if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { if (r1 & BRK_ABRT) { r1 &= ~(PAR_ERR | CRC_ERR); up->port.icount.brk++; if (uart_handle_break(&up->port)) goto next_char; } else if (r1 & PAR_ERR) up->port.icount.parity++; else if (r1 & CRC_ERR) up->port.icount.frame++; if (r1 & Rx_OVR) up->port.icount.overrun++; r1 &= up->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(&up->port, ch, regs)) goto next_char; if (up->port.ignore_status_mask == 0xff || (r1 & up->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: ch = readb(&channel->control); ZSDELAY(); if (!(ch & Rx_CH_AV)) break; } tty_flip_buffer_push(tty); }