static void clear_device(const uintptr_t *port) { write_omap(port[OMAP_UART_IER], 0); // Disable all interrupts /* Clear FIFOs */ set_port(port[OMAP_UART_FCR], OMAP_FCR_RXCLR | OMAP_FCR_TXCLR, OMAP_FCR_RXCLR | OMAP_FCR_TXCLR); read_omap(port[OMAP_UART_LSR]); // Clear Line Status Interrupt read_omap(port[OMAP_UART_MSR]); // Clear Modem Interrupt }
/* * Process data in a line status register */ static int process_lsr(DEV_OMAP *dev, unsigned char lsr) { unsigned key = 0, eventflag = 0; // Return immediately if no errors. if((lsr & (OMAP_LSR_BI|OMAP_LSR_OE|OMAP_LSR_FE|OMAP_LSR_PE)) == 0) return(0); // Save the error as out-of-band data which can be retrieved via devctl(). dev->tty.oband_data |= (lsr >> 1) & 0x0f; atomic_set(&dev->tty.flags, OBAND_DATA); // Uncomment for post 1.0 since there was no time to test sufficiently // if(dev->tty.notify[2].cnt) { // dev->tty.notify[2].cnt = 0; // Disarm // dev->tty.notify[2].event.sigev_value.sival_int |= _NOTIFY_COND_OBAND; // atomic_set(&dev->tty.flags, EVENT_NOTIFY_OBAND); // eventflag = 1; // } // Read whatever input data happens to be in the buffer to "eat" the // spurious data associated with break, parity error, etc. key = read_omap(dev->port[OMAP_UART_RHR]); if(lsr & OMAP_LSR_BI) key |= TTI_BREAK; else if(lsr & OMAP_LSR_OE) key |= TTI_OVERRUN; else if(lsr & OMAP_LSR_FE) key |= TTI_FRAME; else if(lsr & OMAP_LSR_PE) key |= TTI_PARITY; return(tti(&dev->tty, key) | eventflag); }
static void seromap_enable(DEV_OMAP *dev, int enable) { uintptr_t *port = dev->port; write_omap(port[OMAP_UART_LCR], 0x80); if (!enable) { atomic_set(&dev->pwm_flag, SEROMAP_PWM_PAGED); // If HW flow control is ON, assert the RTS line if (dev->tty.c_cflag & IHFLOW) set_port(port[OMAP_UART_MCR], OMAP_MCR_DTR|OMAP_MCR_RTS, 0); while (!(read_omap(port[OMAP_UART_LSR]) & OMAP_LSR_TSRE)) ; nanospin_ns(1000000); // pause for 1ms write_omap(port[OMAP_UART_MDR1], 0x07); write_omap(port[OMAP_UART_DLL], 0xFF); write_omap(port[OMAP_UART_DLH], 0xFF); } else { write_omap(port[OMAP_UART_DLL], dev->brd); write_omap(port[OMAP_UART_DLH], (dev->brd >> 8) & 0xff); write_omap(port[OMAP_UART_MDR1], 0x00); // If HW flow control is ON, de-assert the RTS line if(dev->tty.c_cflag & IHFLOW) set_port(port[OMAP_UART_MCR], OMAP_MCR_DTR|OMAP_MCR_RTS, OMAP_MCR_DTR|OMAP_MCR_RTS); // Allow data transmission to resume atomic_clr(&dev->pwm_flag, SEROMAP_PWM_PAGED); } write_omap(port[OMAP_UART_LCR], dev->lcr); }
void set_port(unsigned port, unsigned mask, unsigned data) { unsigned char c; c = read_omap(port); write_omap(port, (c & ~mask) | (data & mask)); }
// // Clean up the device then add it to the interrupt list and enable it. // void ser_attach_intr(DEV_OMAP *dev) { uintptr_t *port = dev->port; // interrupt sources except transmit and modem status interrupt unsigned ier = OMAP_IER_RHR|OMAP_IER_LS; // According to the National bug sheet you must wait for the transmit // holding register to be empty. do { } while((read_omap(port[OMAP_UART_LSR]) & OMAP_LSR_TXRDY) == 0); clear_device(port); dev->iid = InterruptAttach(dev->intr, ser_intr, dev, 0, 0); // Enable modem status interrupt (default) if (!dev->no_msr_int) { ier |= OMAP_IER_MS; } // Enable interrupt sources. write_omap(port[OMAP_UART_IER], ier); }
/* * Serial interrupt handler */ const struct sigevent * ser_intr(void *area, int id) { int status, cnt; unsigned char msr, lsr; DEV_OMAP *dev = area; struct sigevent *event = NULL; unsigned iir; uintptr_t *port = dev->port; #ifdef PWR_MAN /* Our idle state can be changed by a devctl so we must use a spinlock */ InterruptLock(&dev->idle_spinlock); #endif while (1) { status = 0; #ifdef PWR_MAN if (dev->idle) { omap_clock_enable_isr(dev); #ifdef WINBT omap_force_rts(dev, 0); // once we are in idle mode the only interrupt that can wake us up is from am CTS line change tti(&dev->tty, TTI_OHW_STOP); // start a spare timer for debouncing, if this timer actually // expires, then this was a CTS glitch, if the timer // is cleared by having data on the RX line, it will send up the // oband notification to wake up the host. dev->signal_oband_notification = 1; if( dev->tty.un.s.spare_tmr == 0 ){ atomic_set (&dev->tty.eflags, EVENT_TIMER_QUEUE); dev->tty.un.s.spare_tmr = 4; // queue the event here because the switch statement can just exit // without actually queuing the event with setting the status flag if((dev->tty.flags & EVENT_QUEUED) == 0) { event = &ttyctrl.event; dev_lock(&ttyctrl); ttyctrl.event_queue[ttyctrl.num_events++] = &dev->tty; atomic_set(&dev->tty.flags, EVENT_QUEUED); dev_unlock(&ttyctrl); continue; } } #endif // End of #ifdef WINBT } unsigned ssr = read_omap(port[OMAP_UART_SSR]); if (ssr & OMAP_SSR_WAKEUP_STS) { /* Clear the wake up interrupt */ set_port(port[OMAP_UART_SCR], OMAP_SCR_WAKEUPEN, 0); } #endif iir = read_omap(port[OMAP_UART_IIR]) & OMAP_II_MASK; switch(iir) { case OMAP_II_RX: // Receive data case OMAP_II_RXTO: // Receive data timeout case OMAP_II_LS: // Line status change cnt = 0; lsr = read_omap(port[OMAP_UART_LSR]); do { if( lsr & (OMAP_LSR_BI|OMAP_LSR_OE|OMAP_LSR_FE|OMAP_LSR_PE) ) { // Error character status |= process_lsr(dev, lsr); } else { // Good character status |= tti(&dev->tty, (read_omap(port[OMAP_UART_RHR])) & 0xff); cnt++; } lsr = read_omap(port[OMAP_UART_LSR]); } while(lsr & OMAP_LSR_RXRDY && cnt < FIFO_SIZE); #ifdef WINBT if( cnt && dev->signal_oband_notification ){ // received data after a CTS wake up // notify the host that it's a valid CTS wakeup. dev->signal_oband_notification = 0; dev->tty.oband_data |= _OBAND_SER_MS; atomic_set(&dev->tty.flags, OBAND_DATA); atomic_set(&dev->tty.flags, EVENT_NOTIFY_OBAND); status |= 1; } if (cnt && dev->tty.un.s.spare_tmr) { // received data, clear spare timer dev->tty.un.s.spare_tmr = 0; } #endif break; case OMAP_II_TX: // Transmit buffer empty // disable thr interrupt set_port(dev->port[OMAP_UART_IER], OMAP_IER_THR, 0); dev->tty.un.s.tx_tmr = 0; /* Send event to io-char, tto() will be processed at thread time */ atomic_set(&dev->tty.flags, EVENT_TTO); status |= 1; break; case OMAP_II_MS: // Modem change msr = read_omap(port[OMAP_UART_MSR]); if(msr & OMAP_MSR_DDCD) { status |= tti(&dev->tty, (msr & OMAP_MSR_DCD) ? TTI_CARRIER : TTI_HANGUP); } if((msr & OMAP_MSR_DCTS) && (dev->tty.c_cflag & OHFLOW)) { status |= tti(&dev->tty, (msr & OMAP_MSR_CTS) ? TTI_OHW_CONT : TTI_OHW_STOP); } /* OBAND notification of Modem status change */ dev->tty.oband_data |= _OBAND_SER_MS; atomic_set(&dev->tty.flags, OBAND_DATA); atomic_set(&dev->tty.flags, EVENT_NOTIFY_OBAND); status |= 1; break; case OMAP_II_NOINTR: // No interrupt if (read_omap(port[OMAP_UART_SSR]) & OMAP_SSR_WAKEUP_STS) { // Wake up interrupt set_port(port[OMAP_UART_SCR], OMAP_SCR_WAKEUPEN, 0); // clear wakeup interrupt set_port(port[OMAP_UART_SCR], OMAP_SCR_WAKEUPEN, OMAP_SCR_WAKEUPEN); // re-enable wakeup interrupt } default: goto done; } if (status) { if((dev->tty.flags & EVENT_QUEUED) == 0) { event = &ttyctrl.event; dev_lock(&ttyctrl); ttyctrl.event_queue[ttyctrl.num_events++] = &dev->tty; atomic_set(&dev->tty.flags, EVENT_QUEUED); dev_unlock(&ttyctrl); } } } done: #ifdef PWR_MAN InterruptUnlock(&dev->idle_spinlock); #endif return (event); }
DEV_OMAP * create_device(TTYINIT_OMAP *dip, unsigned unit, unsigned maxim_xcvr_kick) { DEV_OMAP *dev; unsigned i; uintptr_t port; unsigned char msr; unsigned char tlr = 0, tcr = 0; #ifdef PWR_MAN clk_enable_t clk_cfg = clk_enable_none; #endif // Get a device entry and the input/output buffers for it. if ((dev = malloc(sizeof(*dev))) == NULL) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Allocation of device entry failed (%d)", errno); return (dev); } memset(dev, 0, sizeof(*dev)); // Get buffers. dev->tty.ibuf.head = dev->tty.ibuf.tail = dev->tty.ibuf.buff = malloc(dev->tty.ibuf.size = dip->tty.isize); if (dev->tty.ibuf.buff == NULL) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Allocation of input buffer failed (%d)", errno); free(dev); return (NULL); } dev->tty.obuf.head = dev->tty.obuf.tail = dev->tty.obuf.buff = malloc(dev->tty.obuf.size = dip->tty.osize); if (dev->tty.obuf.buff == NULL) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Allocation of output buffer failed (%d)", errno); free(dev->tty.ibuf.buff); free(dev); return (NULL); } dev->tty.cbuf.head = dev->tty.cbuf.tail = dev->tty.cbuf.buff = malloc(dev->tty.cbuf.size = dip->tty.csize); if (dev->tty.cbuf.buff == NULL) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Allocation of canonical buffer failed (%d)", errno); free(dev->tty.ibuf.buff); free(dev->tty.obuf.buff); free(dev); return (NULL); } if (dip->tty.highwater) dev->tty.highwater = dip->tty.highwater; else dev->tty.highwater = dev->tty.ibuf.size - (FIFO_SIZE * 2); strcpy(dev->tty.name, dip->tty.name); dev->tty.baud = dip->tty.baud; dev->tty.fifo = dip->tty.fifo; dev->tty.verbose = dip->tty.verbose; port = mmap_device_io(OMAP_UART_SIZE, dip->tty.port); for (i = 0; i < OMAP_UART_SIZE; i += 4) dev->port[i] = port + i; dev->intr = dip->tty.intr; dev->clk = dip->tty.clk; dev->div = dip->tty.div; dev->tty.flags = EDIT_INSERT | LOSES_TX_INTR; dev->tty.c_cflag = dip->tty.c_cflag; dev->tty.c_iflag = dip->tty.c_iflag; dev->tty.c_lflag = dip->tty.c_lflag; dev->tty.c_oflag = dip->tty.c_oflag; dev->tty.lflags = dip->tty.lflags; if (dip->tty.logging_path[0] != NULL) dev->tty.logging_path = strdup(dip->tty.logging_path); #ifdef PWR_MAN dev->physbase = dip->tty.port; if (omap_clock_toggle_init(dev) != EOK) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Fail to initialize clocks for PM!"); free(dev->tty.ibuf.buff); free(dev->tty.obuf.buff); free(dev); return (NULL); } #ifdef WINBT clk_cfg = clk_enable_smart_wkup; if (omap_force_rts_init(dev) != EOK) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "io-char: Fail to initialize force_rts for PM!"); free(dev->tty.ibuf.buff); free(dev->tty.obuf.buff); free(dev); return (NULL); } #endif omap_clock_enable(dev, clk_cfg); #ifdef WINBT bt_ctrl_init(); #endif #endif /* Set auto_rts mode */ dev->auto_rts_enable = dip->auto_rts_enable; // Do not enable MSR interrupt dev->no_msr_int = dip->no_msr_int; // Initialize termios cc codes to an ANSI terminal. ttc(TTC_INIT_CC, &dev->tty, 0); // Initialize the device's name. // Assume that the basename is set in device name. This will attach // to the path assigned by the unit number/minor number combination unit = SET_NAME_NUMBER(unit) | NUMBER_DEV_FROM_USER; ttc(TTC_INIT_TTYNAME, &dev->tty, unit); // see if we have a maxim rs-232 transceiver that needs to be // kicked after it goes to sleep dev->kick_maxim = maxim_xcvr_kick; // Only setup IRQ handler for non-pcmcia devices. // Pcmcia devices will have this done later when card is inserted. if (dip->tty.port != 0 && dip->tty.intr != _NTO_INTR_SPARE) { #ifdef OMAP5910 /* * Don't change default mode set in the very distant past. Even though * MODE_SELECT should be DISABLE before changing DLH, DLL. */ // enable the UART write_omap(dev->port[OMAP_UART_MDR1], OMAP_MDR1_MODE_16X); #else /* * TRM states: Before initializing or modifying clock parameter controls * (DLH, DLL), MODE_SELECT must be set to 0x7 (DISABLE). Failure to observe * this rule can result in unpredictable module behavior. */ write_omap(dev->port[OMAP_UART_MDR1], OMAP_MDR1_MODE_DISABLE); #endif /* Work around for silicon errata i202 states: */ /* Need a delay = 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS) */ nanospin_ns(200); /* Clear FIFOs */ set_port(dev->port[OMAP_UART_FCR], OMAP_FCR_RXCLR | OMAP_FCR_TXCLR, OMAP_FCR_RXCLR | OMAP_FCR_TXCLR); /* Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and TX_FIFO_E bit is 1 */ i = 5; while ((read_omap(dev->port[OMAP_UART_LSR]) & (OMAP_UART_LSR_THRE | OMAP_UART_LSR_DR)) != OMAP_UART_LSR_THRE) { nanospin_ns(200); if (--i == 0) { break; /* No need to do anything drastic if FIFO is still not empty */ } } // enable access to divisor registers write_omap(dev->port[OMAP_UART_LCR], OMAP_LCR_DLAB); write_omap(dev->port[OMAP_UART_DLL], 0); write_omap(dev->port[OMAP_UART_DLH], 0); /* Switch to config mode B to get access to EFR Register */ write_omap(dev->port[OMAP_UART_LCR], 0xBF); /* Enable access to TLR register */ set_port(dev->port[OMAP_UART_EFR], OMAP_EFR_ENHANCED, OMAP_EFR_ENHANCED); /* Switch to operational mode to get acces to MCR register */ write_omap(dev->port[OMAP_UART_LCR], 0x00); /* set MCR bit 6 to enable access to TCR and TLR registers */ set_port(dev->port[OMAP_UART_MCR], OMAP_MCR_TCRTLR, OMAP_MCR_TCRTLR); write_omap(dev->port[OMAP_UART_FCR], OMAP_FCR_ENABLE|OMAP_FCR_RXCLR|OMAP_FCR_TXCLR); tcr = 0x0e; /* set auto-rts assert at 56 bytes, restore at 0 bytes */ if (dev->tty.fifo) { /* Set RX fifo trigger level */ switch (dev->tty.fifo >> 4) { case FIFO_TRIG_8: default: tlr = 0x20; break; case FIFO_TRIG_16: tlr = 0x40; break; case FIFO_TRIG_32: tlr = 0x80; break; case FIFO_TRIG_56: tlr = 0xe0; break; case FIFO_TRIG_60: tlr = 0xf0; tcr = 0x0f; /* Ensure auto-rts trigger is not less the RX trigger */ break; } /* Set TX fifo trigger level */ switch (dev->tty.fifo & 0x0f) { case FIFO_TRIG_8: default: tlr |= 0x02; break; case FIFO_TRIG_16: tlr |= 0x04; break; case FIFO_TRIG_32: tlr |= 0x08; break; case FIFO_TRIG_56: tlr |= 0x0e; break; case FIFO_TRIG_60: tlr |= 0x0f; break; } } write_omap(dev->port[OMAP_UART_TCR], tcr); write_omap(dev->port[OMAP_UART_TLR], tlr); #ifdef PWR_MAN write_omap(dev->port[OMAP_UART_SCR], OMAP_SCR_WAKEUPEN); #else write_omap(dev->port[OMAP_UART_SCR], 0x00); #endif /* Switch back to Config mode B to gain access to EFR again */ write_omap(dev->port[OMAP_UART_LCR], 0xBF); /* remove access to TLR register */ set_port(dev->port[OMAP_UART_EFR], OMAP_EFR_ENHANCED, 0); /* Switch to operational mode to get acces to MCR register */ write_omap(dev->port[OMAP_UART_LCR], 0x00); /* clr MCR bit 6 to remove access to TCR and TLR registers */ set_port(dev->port[OMAP_UART_MCR], OMAP_MCR_TCRTLR, 0); ser_stty(dev); ser_attach_intr(dev); }
int tto(TTYDEV *ttydev, int action, int arg1) { TTYBUF *bup = &ttydev->obuf; DEV_OMAP *dev = (DEV_OMAP *)ttydev; const uintptr_t *port = dev->port; unsigned char c; #ifdef PWR_MAN if (dev->idle) { /* Check the client lists for notify conditions */ return(tto_checkclients(&dev->tty)); } #endif switch(action) { case TTO_STTY: // if (dev->driver_pmd.cur_mode == PM_MODE_ACTIVE) ser_stty(dev); return (0); case TTO_CTRL: if(arg1 & _SERCTL_BRK_CHG) set_port(port[OMAP_UART_LCR], OMAP_LCR_BREAK, arg1 &_SERCTL_BRK ? OMAP_LCR_BREAK : 0); if(arg1 & _SERCTL_DTR_CHG) set_port(port[OMAP_UART_MCR], OMAP_MCR_DTR, arg1 & _SERCTL_DTR ? OMAP_MCR_DTR : 0); if(arg1 & _SERCTL_RTS_CHG) { if (dev->auto_rts_enable) { /* For auto-rts enable/disable RX & LS interrupts to assert/clear * input flow control (the FIFO will automatically handle the RTS line) */ if (arg1 & _SERCTL_RTS) write_omap(port[OMAP_UART_IER], read_omap(port[OMAP_UART_IER] ) | OMAP_IER_RHR | OMAP_IER_LS ); else write_omap(port[OMAP_UART_IER], read_omap(port[OMAP_UART_IER] ) & ~(OMAP_IER_RHR | OMAP_IER_LS ) ); } else set_port(port[OMAP_UART_MCR], OMAP_MCR_RTS, arg1 & _SERCTL_RTS ? OMAP_MCR_RTS : 0); } #ifdef WINBT if (arg1 & _CTL_TIMED_CHG) { slogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), _SLOG_ERROR, "%s: Turning clocks OFF due to CTS glitch\n", __FUNCTION__); dev->signal_oband_notification = 0; omap_clock_disable(dev); } #endif return (0); case TTO_LINESTATUS: return (((read_omap(port[OMAP_UART_MSR]) << 8) | read_omap(port[OMAP_UART_MCR])) & 0xf003); case TTO_DATA: case TTO_EVENT: break; default: return (0); } while (bup->cnt > 0 && (!(read_omap(port[OMAP_UART_SSR]) & OMAP_SSR_TXFULL)) ) { /* * If the OSW_PAGED_OVERRIDE flag is set then allow * transmit of character even if output is suspended via * the OSW_PAGED flag. This flag implies that the next * character in the obuf is a software flow control * charater (STOP/START). * Note: tx_inject sets it up so that the contol * character is at the start (tail) of the buffer. * */ if (dev->tty.flags & (OHW_PAGED | OSW_PAGED) && !(dev->tty.xflags & OSW_PAGED_OVERRIDE)) break; /* Get character from obuf and do any output processing */ dev_lock(&dev->tty); c = tto_getchar(&dev->tty); dev_unlock(&dev->tty); /* Print the character */ dev->tty.un.s.tx_tmr = 3; /* Timeout 3 */ write_omap(port[OMAP_UART_THR], c); /* Clear the OSW_PAGED_OVERRIDE flag as we only want * one character to be transmitted in this case. */ if (dev->tty.xflags & OSW_PAGED_OVERRIDE){ atomic_clr(&dev->tty.xflags, OSW_PAGED_OVERRIDE); break; } } /* If there is still data in the obuf and we are not in a flow * controlled state then turn TX interrupts back on to notify us * when the hardware is ready for more characters. */ if (bup->cnt > 0 && !(dev->tty.flags & (OHW_PAGED|OSW_PAGED)) ) { // enable all interrupts set_port(dev->port[OMAP_UART_IER], OMAP_IER_THR, OMAP_IER_THR); } /* Check the client lists for notify conditions */ return(tto_checkclients(&dev->tty)); }