static void __ip22zilog_reset(struct uart_ip22zilog_port *up) { struct zilog_channel *channel; int i; if (up->flags & IP22ZILOG_FLAG_RESET_DONE) return; /* Let pending transmits finish. */ channel = ZILOG_CHANNEL_FROM_PORT(&up->port); for (i = 0; i < 1000; i++) { unsigned char stat = read_zsreg(channel, R1); if (stat & ALL_SNT) break; udelay(100); } if (!ZS_IS_CHANNEL_A(up)) { up++; channel = ZILOG_CHANNEL_FROM_PORT(&up->port); } write_zsreg(channel, R9, FHWRES); ZSDELAY_LONG(); (void) read_zsreg(channel, R0); up->flags |= IP22ZILOG_FLAG_RESET_DONE; up->next->flags |= IP22ZILOG_FLAG_RESET_DONE; }
/* * 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. */ static int __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); /* Nice buggy HW ... */ pmz_fix_zero_bug_scc(uap); /* Reset the channel */ uap->curregs[R9] = 0; write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB); zssync(uap); udelay(10); write_zsreg(uap, 9, 0); zssync(uap); /* Clear the interrupt registers */ write_zsreg(uap, R1, 0); write_zsreg(uap, R0, ERR_RES); write_zsreg(uap, R0, ERR_RES); write_zsreg(uap, R0, RES_H_IUS); write_zsreg(uap, R0, RES_H_IUS); /* Setup some valid baud rate */ uap->curregs[R4] = X16CLK | SB1; uap->curregs[R3] = Rx8; uap->curregs[R5] = Tx8 | RTS; if (!ZS_IS_IRDA(uap)) uap->curregs[R5] |= DTR; uap->curregs[R12] = 0; uap->curregs[R13] = 0; uap->curregs[R14] = BRENAB; /* Clear handshaking, enable BREAK interrupts */ uap->curregs[R15] = BRKIE; /* Master interrupt enable */ uap->curregs[R9] |= NV | MIE; pmz_load_zsregs(uap, uap->curregs); /* Enable receiver and transmitter. */ write_zsreg(uap, R3, uap->curregs[R3] |= RxENABLE); write_zsreg(uap, R5, uap->curregs[R5] |= TxENABLE); /* Remember status for DCD/CTS changes */ uap->prev_status = read_zsreg(uap, R0); return pwr_delay; }
/* * FixZeroBug....Works around a bug in the SCC receving 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. */ static void 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); write_zsreg(uap, 4, X1CLK | MONSYNC); write_zsreg(uap, 3, Rx8); write_zsreg(uap, 5, Tx8 | RTS); write_zsreg(uap, 9, NV); /* Didn't we already do this? */ write_zsreg(uap, 11, RCBR | TCBR); write_zsreg(uap, 12, 0); write_zsreg(uap, 13, 0); write_zsreg(uap, 14, (LOOPBAK | BRSRC)); write_zsreg(uap, 14, (LOOPBAK | BRSRC | BRENAB)); write_zsreg(uap, 3, Rx8 | RxENABLE); write_zsreg(uap, 0, RES_EXT_INT); write_zsreg(uap, 0, RES_EXT_INT); write_zsreg(uap, 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(uap, 9, NV); write_zsreg(uap, 4, X16CLK | SB_MASK); write_zsreg(uap, 3, Rx8); while (read_zsreg(uap, 0) & Rx_CH_AV) { (void)read_zsreg(uap, 8); write_zsreg(uap, 0, RES_EXT_INT); write_zsreg(uap, 0, ERR_RES); } }
static void __init ip22zilog_init_hw(void) { int i; for (i = 0; i < NUM_CHANNELS; i++) { struct uart_ip22zilog_port *up = &ip22zilog_port_table[i]; struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(&up->port); unsigned long flags; int baud, brg; spin_lock_irqsave(&up->port.lock, flags); if (ZS_IS_CHANNEL_A(up)) { write_zsreg(channel, R9, FHWRES); ZSDELAY_LONG(); (void) read_zsreg(channel, R0); } /* Normal serial TTY. */ up->parity_mask = 0xff; up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; up->curregs[R4] = PAR_EVEN | X16CLK | SB1; up->curregs[R3] = RxENAB | Rx8; up->curregs[R5] = TxENAB | Tx8; up->curregs[R9] = NV | MIE; up->curregs[R10] = NRZ; up->curregs[R11] = TCBR | RCBR; baud = 9600; brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); up->curregs[R12] = (brg & 0xff); up->curregs[R13] = (brg >> 8) & 0xff; up->curregs[R14] = BRENAB; __load_zsregs(channel, up->curregs); /* set master interrupt enable */ write_zsreg(channel, R9, up->curregs[R9]); spin_unlock_irqrestore(&up->port.lock, flags); } }