// SPDX-License-Identifier: GPL-2.0+ /* * Driver for PowerMac Z85c30 based ESCC cell found in the * "macio" ASICs of various PowerMac models * * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) * * Derived from drivers/macintosh/macserial.c by Paul Mackerras * and drivers/serial/sunzilog.c by David S. Miller * * Hrm... actually, I ripped most of sunzilog (Thanks David !) and * adapted special tweaks needed for us. I don't think it's worth * merging back those though. The DMA code still has to get in * and once done, I expect that driver to remain fairly stable in * the long term, unless we change the driver model again... * * 2004-08-06 Harald Welte <laforge@gnumonks.org> * - Enable BREAK interrupt * - Add support for sysreq * * TODO: - Add DMA support * - Defer port shutdown to a few seconds after close * - maybe put something right into uap->clk_divisor
*/
/* * For the sake of early serial console, we can do a pre-probe * (optional) of the ports at rather early boot time.
*/ staticstruct uart_pmac_port pmz_ports[MAX_ZS_PORTS]; staticint pmz_ports_count;
/* * 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.
*/ staticvoid pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs)
{ int i;
/* Let pending transmits finish. */ for (i = 0; i < 1000; i++) { unsignedchar stat = read_zsreg(uap, R1); if (stat & ALL_SNT) break;
udelay(100);
}
/* * We do like sunzilog to avoid disrupting pending Tx * Reprogram the Zilog channel HW registers with the copies found in the * software state struct. If the transmitter is busy, we defer this update * until the next TX complete interrupt. Else, we do it right now. * * The UART port lock must be held and local interrupts disabled.
*/ staticvoid pmz_maybe_update_regs(struct uart_pmac_port *uap)
{ if (!ZS_REGS_HELD(uap)) { if (ZS_TX_ACTIVE(uap)) {
uap->flags |= PMACZILOG_FLAG_REGS_HELD;
} else {
pmz_debug("pmz: maybe_update_regs: updating\n");
pmz_load_zsregs(uap, uap->curregs);
}
}
}
/* Sanity check, make sure the old bug is no longer happening */ if (uap->port.state == NULL) {
WARN_ON(1);
(void)read_zsdata(uap); returnfalse;
}
port = &uap->port.state->port;
status = read_zsreg(uap, R0);
write_zsreg(uap, R0, RES_EXT_INT);
zssync(uap);
if (ZS_IS_OPEN(uap) && ZS_WANTS_MODEM_STATUS(uap)) { if (status & SYNC_HUNT)
uap->port.icount.dsr++;
/* The Zilog just gives us an interrupt when DCD/CTS/etc. change. * But it does not tell us which bit has changed, we have to keep * track of this ourselves. * The CTS input is inverted for some reason. -- paulus
*/ if ((status ^ uap->prev_status) & DCD)
uart_handle_dcd_change(&uap->port,
(status & DCD)); if ((status ^ uap->prev_status) & CTS)
uart_handle_cts_change(&uap->port,
!(status & CTS));
if (ZS_IS_CONS(uap)) { unsignedchar 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 becoming 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;
}
/* Under some circumstances, we see interrupts reported for * a closed channel. The interrupt mask in R1 is clear, but * R3 still signals the interrupts and we see them when taking * an interrupt for the other channel (this could be a qemu * bug but since the ESCC doc doesn't specify precsiely whether * R3 interrup status bits are masked by R1 interrupt enable * bits, better safe than sorry). --BenH.
*/ if (!ZS_IS_OPEN(uap)) goto ack_tx_int;
/* Channel A */
push = false; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { if (!ZS_IS_OPEN(uap_a)) {
pmz_debug("ChanA interrupt while not open !\n"); goto skip_a;
}
write_zsreg(uap_a, R0, RES_H_IUS);
zssync(uap_a); if (r3 & CHAEXT)
pmz_status_handle(uap_a); if (r3 & CHARxIP)
push = pmz_receive_chars(uap_a); if (r3 & CHATxIP)
pmz_transmit_chars(uap_a);
rc = IRQ_HANDLED;
}
skip_a:
uart_port_unlock(&uap_a->port); if (push)
tty_flip_buffer_push(&uap->port.state->port);
if (!uap_b) goto out;
uart_port_lock(&uap_b->port);
push = false; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { if (!ZS_IS_OPEN(uap_b)) {
pmz_debug("ChanB interrupt while not open !\n"); goto skip_b;
}
write_zsreg(uap_b, R0, RES_H_IUS);
zssync(uap_b); if (r3 & CHBEXT)
pmz_status_handle(uap_b); if (r3 & CHBRxIP)
push = pmz_receive_chars(uap_b); if (r3 & CHBTxIP)
pmz_transmit_chars(uap_b);
rc = IRQ_HANDLED;
}
skip_b:
uart_port_unlock(&uap_b->port); if (push)
tty_flip_buffer_push(&uap->port.state->port);
out: return rc;
}
/* * Peek the status register, lock not held by caller
*/ staticinline u8 pmz_peek_status(struct uart_pmac_port *uap)
{ unsignedlong flags;
u8 status;
uart_port_lock_irqsave(&uap->port, &flags);
status = read_zsreg(uap, R0);
uart_port_unlock_irqrestore(&uap->port, flags);
return status;
}
/* * Check if transmitter is empty * The port lock is not held.
*/ staticunsignedint pmz_tx_empty(struct uart_port *port)
{ unsignedchar status;
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 ?
*/ staticvoid pmz_set_mctrl(struct uart_port *port, unsignedint mctrl)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedchar 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_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;
/* * 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.
*/ staticunsignedint pmz_get_mctrl(struct uart_port *port)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedchar status; unsignedint ret;
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;
}
/* * Stop TX side. Dealt like sunzilog at next Tx interrupt, * though for DMA, we will have to do a bit more. * The port lock is held and interrupts are disabled.
*/ staticvoid pmz_stop_tx(struct uart_port *port)
{
to_pmz(port)->flags |= PMACZILOG_FLAG_TX_STOPPED;
}
/* * Kick the Tx side. * The port lock is held and interrupts are disabled.
*/ staticvoid pmz_start_tx(struct uart_port *port)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedchar status;
/* 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 tty_port *tport = &port->state->port; unsignedchar ch;
if (!uart_fifo_get(&uap->port, &ch)) return;
write_zsdata(uap, ch);
zssync(uap);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
}
}
/* * 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.
*/ staticvoid pmz_stop_rx(struct uart_port *port)
{ struct uart_pmac_port *uap = to_pmz(port);
/* * Enable modem status change interrupts * The port lock is held.
*/ staticvoid pmz_enable_ms(struct uart_port *port)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedchar new_reg;
if (ZS_IS_IRDA(uap)) return;
new_reg = uap->curregs[R15] | (DCDIE | SYNCIE | CTSIE); if (new_reg != uap->curregs[R15]) {
uap->curregs[R15] = new_reg;
/* NOTE: Not subject to 'transmitter active' rule. */
write_zsreg(uap, R15, uap->curregs[R15]);
}
}
/* * Control break state emission * The port lock is not held.
*/ staticvoid pmz_break_ctl(struct uart_port *port, int break_state)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedchar set_bits, clear_bits, new_reg; unsignedlong flags;
set_bits = clear_bits = 0;
if (break_state)
set_bits |= SND_BRK; else
clear_bits |= SND_BRK;
/* * 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.
*/ staticint 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;
}
#else
staticint pmz_set_scc_power(struct uart_pmac_port *uap, int state)
{ return 0;
}
#endif/* !CONFIG_PPC_PMAC */
/* * FixZeroBug....Works around a bug in the SCC receiving 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.
*/ staticvoid 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);
/* 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);
/* * 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.
*/ staticint __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);
/* * This is the "normal" startup routine, using the above one * wrapped with the lock and doing a schedule delay
*/ staticint pmz_startup(struct uart_port *port)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedlong flags; int pwr_delay = 0;
uap->flags |= PMACZILOG_FLAG_IS_OPEN;
/* A console is never powered down. Else, power up and * initialize the chip
*/ if (!ZS_IS_CONS(uap)) {
uart_port_lock_irqsave(port, &flags);
pwr_delay = __pmz_startup(uap);
uart_port_unlock_irqrestore(port, flags);
}
sprintf(uap->irq_name, PMACZILOG_NAME"%d", uap->port.line); if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED,
uap->irq_name, uap)) {
pmz_error("Unable to register zs interrupt handler.\n");
pmz_set_scc_power(uap, 0); return -ENXIO;
}
/* 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 interrupt requests for the channel */
uart_port_lock_irqsave(port, &flags);
pmz_interrupt_control(uap, 1);
uart_port_unlock_irqrestore(port, flags);
if (!ZS_IS_CONS(uap))
pmz_set_scc_power(uap, 0); /* Shut the chip down */
uart_port_unlock_irqrestore(port, flags);
}
/* Shared by TTY driver and serial console setup. The port lock is held * and local interrupts are disabled.
*/ staticvoid pmz_convert_to_zs(struct uart_pmac_port *uap, unsignedint cflag, unsignedint iflag, unsignedlong baud)
{ int brg;
/* Switch to external clocking for IrDA high clock rates. That * code could be re-used for Midi interfaces with different * multipliers
*/ if (baud >= 115200 && ZS_IS_IRDA(uap)) {
uap->curregs[R4] = X1CLK;
uap->curregs[R11] = RCTRxCP | TCTRxCP;
uap->curregs[R14] = 0; /* BRG off */
uap->curregs[R12] = 0;
uap->curregs[R13] = 0;
uap->flags |= PMACZILOG_FLAG_IS_EXTCLK;
} else { switch (baud) { case ZS_CLOCK/16: /* 230400 */
uap->curregs[R4] = X16CLK;
uap->curregs[R11] = 0;
uap->curregs[R14] = 0; break; case ZS_CLOCK/32: /* 115200 */
uap->curregs[R4] = X32CLK;
uap->curregs[R11] = 0;
uap->curregs[R14] = 0; break; default:
uap->curregs[R4] = X16CLK;
uap->curregs[R11] = TCBR | RCBR;
brg = BPS_TO_BRG(baud, ZS_CLOCK / 16);
uap->curregs[R12] = (brg & 255);
uap->curregs[R13] = ((brg >> 8) & 255);
uap->curregs[R14] = BRENAB;
}
uap->flags &= ~PMACZILOG_FLAG_IS_EXTCLK;
}
/* Character size, stop bits, and parity. */
uap->curregs[3] &= ~RxN_MASK;
uap->curregs[5] &= ~TxN_MASK;
uap->port.ignore_status_mask = 0; if (iflag & IGNPAR)
uap->port.ignore_status_mask |= CRC_ERR | PAR_ERR; if (iflag & IGNBRK) {
uap->port.ignore_status_mask |= BRK_ABRT; if (iflag & IGNPAR)
uap->port.ignore_status_mask |= Rx_OVR;
}
if ((cflag & CREAD) == 0)
uap->port.ignore_status_mask = 0xff;
}
/* * Set the irda codec on the imac to the specified baud rate.
*/ staticvoid pmz_irda_setup(struct uart_pmac_port *uap, unsignedlong *baud)
{
u8 cmdbyte; int t, version;
switch (*baud) { /* SIR modes */ case 2400:
cmdbyte = 0x53; break; case 4800:
cmdbyte = 0x52; break; case 9600:
cmdbyte = 0x51; break; case 19200:
cmdbyte = 0x50; break; case 38400:
cmdbyte = 0x4f; break; case 57600:
cmdbyte = 0x4e; break; case 115200:
cmdbyte = 0x4d; break; /* The FIR modes aren't really supported at this point, how * do we select the speed ? via the FCR on KeyLargo ?
*/ case 1152000:
cmdbyte = 0; break; case 4000000:
cmdbyte = 0; break; default: /* 9600 */
cmdbyte = 0x51;
*baud = 9600; break;
}
/* Wait for transmitter to drain */
t = 10000; while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0
|| (read_zsreg(uap, R1) & ALL_SNT) == 0) { if (--t <= 0) {
pmz_error("transmitter didn't drain\n"); return;
}
udelay(10);
}
/* Drain the receiver too */
t = 100;
(void)read_zsdata(uap);
(void)read_zsdata(uap);
(void)read_zsdata(uap);
mdelay(10); while (read_zsreg(uap, R0) & Rx_CH_AV) {
read_zsdata(uap);
mdelay(10); if (--t <= 0) {
pmz_error("receiver didn't drain\n"); return;
}
}
/* 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);
}
/* The port lock is not held. */ staticvoid pmz_set_termios(struct uart_port *port, struct ktermios *termios, conststruct ktermios *old)
{ struct uart_pmac_port *uap = to_pmz(port); unsignedlong flags;
uart_port_lock_irqsave(port, &flags);
/* Disable IRQs on the port */
pmz_interrupt_control(uap, 0);
/* Setup new port configuration */
__pmz_set_termios(port, termios, old);
/* Re-enable IRQs on the port */ if (ZS_IS_OPEN(uap))
pmz_interrupt_control(uap, 1);
if (ZS_IS_IRDA(uap)) return"Z85c30 ESCC - Infrared port"; elseif (ZS_IS_INTMODEM(uap)) return"Z85c30 ESCC - Internal modem"; return"Z85c30 ESCC - Serial port";
}
/* We do not request/release mappings of the registers here, this * happens at early serial probe time.
*/ staticvoid pmz_release_port(struct uart_port *port)
{
}
/* These do not need to do anything interesting either. */ staticvoid pmz_config_port(struct uart_port *port, int flags)
{
}
/* We do not support letting the user mess with the divisor, IRQ, etc. */ staticint pmz_verify_port(struct uart_port *port, struct serial_struct *ser)
{ return -EINVAL;
}
/* * Setup one port structure after probing, HW is down at this point, * Unlike sunzilog, we don't need to pre-init the spinlock as we don't * register our console before uart_add_one_port() is called
*/ staticint __init pmz_init_port(struct uart_pmac_port *uap)
{ struct device_node *np = uap->node; constchar *conn; conststruct slot_names_prop { int count; char name[1];
} *slots; int len; struct resource r_ports;
/* * Fixup for the port on Gatwick for which the device-tree has * missing interrupts. Normally, the macio_dev would contain * fixed up interrupt info, but we use the device-tree directly * here due to early probing so we need the fixup too.
*/ if (uap->port.irq == 0 &&
np->parent && np->parent->parent &&
of_device_is_compatible(np->parent->parent, "gatwick")) { /* IRQs on gatwick are offset by 64 */
uap->port.irq = irq_create_mapping(NULL, 64 + 15);
}
/* Setup some valid baud rate information in the register * shadows so we don't write crap there before baud rate is * first initialized.
*/
pmz_convert_to_zs(uap, CS8, 0, 9600);
return 0;
}
/* * Get rid of a port on module removal
*/ staticvoid pmz_dispose_port(struct uart_pmac_port *uap)
{ struct device_node *np;
/* * Called upon match with an escc node in the device-tree.
*/ staticint pmz_attach(struct macio_dev *mdev, conststruct of_device_id *match)
{ struct uart_pmac_port *uap; int i;
/* Iterate the pmz_ports array to find a matching entry
*/ for (i = 0; i < MAX_ZS_PORTS; i++) if (pmz_ports[i].node == mdev->ofdev.dev.of_node) break; if (i >= MAX_ZS_PORTS) return -ENODEV;
/* We still activate the port even when failing to request resources * to work around bugs in ancient Apple device-trees
*/ if (macio_request_resources(uap->dev, "pmac_zilog"))
printk(KERN_WARNING "%pOFn: Failed to request resource" ", port still active\n",
uap->node); else
uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
/* * That one should not be called, macio isn't really a hotswap device, * we don't expect one of those serial ports to go away...
*/ staticvoid pmz_detach(struct macio_dev *mdev)
{ struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev);
/* * Probe all ports in the system and build the ports array, we register * with the serial layer later, so we get a proper struct device which * allows the tty to attach properly. This is later than it used to be * but the tty layer really wants it that way.
*/ staticint __init pmz_probe(void)
{ struct device_node *node_p, *node_a, *node_b, *np; int count = 0; int rc;
/* * Find all escc chips in the system
*/
for_each_node_by_name(node_p, "escc") { /* * First get channel A/B node pointers * * TODO: Add routines with proper locking to do that...
*/
node_a = node_b = NULL;
for_each_child_of_node(node_p, np) { if (of_node_name_prefix(np, "ch-a"))
node_a = of_node_get(np); elseif (of_node_name_prefix(np, "ch-b"))
node_b = of_node_get(np);
} if (!node_a && !node_b) {
of_node_put(node_a);
of_node_put(node_b);
printk(KERN_ERR "pmac_zilog: missing node %c for escc %pOF\n",
(!node_a) ? 'a' : 'b', node_p); continue;
}
/* * Fill basic fields in the port structures
*/ if (node_b != NULL) {
pmz_ports[count].mate = &pmz_ports[count+1];
pmz_ports[count+1].mate = &pmz_ports[count];
}
pmz_ports[count].flags = PMACZILOG_FLAG_IS_CHANNEL_A;
pmz_ports[count].node = node_a;
pmz_ports[count+1].node = node_b;
pmz_ports[count].port.line = count;
pmz_ports[count+1].port.line = count+1;
/* * Setup the ports for real
*/
rc = pmz_init_port(&pmz_ports[count]); if (rc == 0 && node_b != NULL)
rc = pmz_init_port(&pmz_ports[count+1]); if (rc != 0) {
of_node_put(node_a);
of_node_put(node_b);
memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port));
memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port)); continue;
}
count += 2;
}
pmz_ports_count = count;
return 0;
}
#else
/* On PCI PowerMacs, pmz_probe() does an explicit search of the OpenFirmware * tree to obtain the device_nodes needed to start the console before the * macio driver. On Macs without OpenFirmware, global platform_devices take * the place of those device_nodes.
*/ externstruct platform_device scc_a_pdev, scc_b_pdev;
staticint pmz_attach(struct platform_device *pdev)
{ struct uart_pmac_port *uap; int i;
/* Iterate the pmz_ports array to find a matching entry */ for (i = 0; i < pmz_ports_count; i++) if (pmz_ports[i].pdev == pdev) break; if (i >= pmz_ports_count) return -ENODEV;
/* * Register the driver, console driver and ports with the serial * core
*/ staticint __init pmz_register(void)
{
pmz_uart_reg.nr = pmz_ports_count;
pmz_uart_reg.cons = PMACZILOG_CONSOLE;
/* * Register this driver with the serial core
*/ return uart_register_driver(&pmz_uart_reg);
}
/* * First, we need to do a direct OF-based probe pass. We * do that because we want serial console up before the * macio stuffs calls us back, and since that makes it * easier to pass the proper number of channels to * uart_register_driver()
*/ if (pmz_ports_count == 0)
pmz_probe();
/* * Bail early if no port found
*/ if (pmz_ports_count == 0) return -ENODEV;
/* * Now we register with the serial layer
*/
rc = pmz_register(); if (rc) {
printk(KERN_ERR "pmac_zilog: Error registering serial device, disabling pmac_zilog.\n" "pmac_zilog: Did another serial driver already claim the minors?\n"); /* effectively "pmz_unprobe()" */ for (i=0; i < pmz_ports_count; i++)
pmz_dispose_port(&pmz_ports[i]); return rc;
}
/* * Then we register the macio driver itself
*/ #ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); #else return platform_driver_register(&pmz_driver); #endif
}
staticvoid __exit exit_pmz(void)
{ int i;
#ifdef CONFIG_PPC_PMAC /* Get rid of macio-driver (detach from macio) */
macio_unregister_driver(&pmz_driver); #else
platform_driver_unregister(&pmz_driver); #endif
for (i = 0; i < pmz_ports_count; i++) { struct uart_pmac_port *uport = &pmz_ports[i]; #ifdef CONFIG_PPC_PMAC if (uport->node != NULL)
pmz_dispose_port(uport); #else if (uport->pdev != NULL)
pmz_dispose_port(uport); #endif
} /* Unregister UART driver */
uart_unregister_driver(&pmz_uart_reg);
}
/* Wait for the transmit buffer to empty. */ while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0)
udelay(5);
write_zsdata(uap, ch);
}
/* * Print a string to the serial port trying not to disturb * any possible real use of the port...
*/ staticvoid pmz_console_write(struct console *con, constchar *s, unsignedint count)
{ struct uart_pmac_port *uap = &pmz_ports[con->index]; unsignedlong flags;
uart_port_lock_irqsave(&uap->port, &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);
/* Restore the values in the registers. */
write_zsreg(uap, R1, uap->curregs[1]); /* Don't disable the transmitter. */
uart_port_unlock_irqrestore(&uap->port, flags);
}
/* * Setup the serial console
*/ staticint __init pmz_console_setup(struct console *co, char *options)
{ struct uart_pmac_port *uap; struct uart_port *port; int baud = 38400; int bits = 8; int parity = 'n'; int flow = 'n'; unsignedlong pwr_delay;
/* * XServe's default to 57600 bps
*/ if (of_machine_is_compatible("RackMac1,1")
|| of_machine_is_compatible("RackMac1,2")
|| of_machine_is_compatible("MacRISC4"))
baud = 57600;
/* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support.
*/ if (co->index >= pmz_ports_count)
co->index = 0;
uap = &pmz_ports[co->index]; #ifdef CONFIG_PPC_PMAC if (uap->node == NULL) return -ENODEV; #else if (uap->pdev == NULL) return -ENODEV; #endif
port = &uap->port;
/* * Mark port as beeing a console
*/
uap->flags |= PMACZILOG_FLAG_IS_CONS;
/* * Temporary fix for uart layer who didn't setup the spinlock yet
*/
spin_lock_init(&port->lock);
/* * Enable the hardware
*/
pwr_delay = __pmz_startup(uap); if (pwr_delay)
mdelay(pwr_delay);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.