ioc3_serial.c
来自「linux 内核源代码」· C语言 代码 · 共 2,194 行 · 第 1/4 页
C
2,194 行
/* Timeout is in ticks. Let's figure out how many chars we * can receive at the current baud rate in that interval * and set the rx threshold to that amount. There are 4 chars * per ring entry, so we'll divide the number of chars that will * arrive in timeout by 4. * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. */ threshold = timeout * port->ip_baud / 4000; if (threshold == 0) threshold = 1; /* otherwise we'll intr all the time! */ if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD) return 1; port->ip_sscr &= ~SSCR_RX_THRESHOLD; port->ip_sscr |= threshold; writel(port->ip_sscr, &port->ip_serial_regs->sscr); /* Now set the rx timeout to the given value * again timeout * SRTR_HZ / HZ */ timeout = timeout * SRTR_HZ / 100; if (timeout > SRTR_CNT) timeout = SRTR_CNT; writel(timeout, &port->ip_serial_regs->srtr); return 0;}/** * config_port - config the hardware * @port: port to config * @baud: baud rate for the port * @byte_size: data size * @stop_bits: number of stop bits * @parenb: parity enable ? * @parodd: odd parity ? */static inline intconfig_port(struct ioc3_port *port, int baud, int byte_size, int stop_bits, int parenb, int parodd){ char lcr, sizebits; int spiniter = 0; DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " "parodd %d\n", __FUNCTION__, ((struct uart_port *)port->ip_port)->line, baud, byte_size, stop_bits, parenb, parodd)); if (set_baud(port, baud)) return 1; switch (byte_size) { case 5: sizebits = UART_LCR_WLEN5; break; case 6: sizebits = UART_LCR_WLEN6; break; case 7: sizebits = UART_LCR_WLEN7; break; case 8: sizebits = UART_LCR_WLEN8; break; default: return 1; } /* Pause the DMA interface if necessary */ if (port->ip_sscr & SSCR_DMA_EN) { writel(port->ip_sscr | SSCR_DMA_PAUSE, &port->ip_serial_regs->sscr); while ((readl(&port->ip_serial_regs->sscr) & SSCR_PAUSE_STATE) == 0) { spiniter++; if (spiniter > MAXITER) return -1; } } /* Clear relevant fields in lcr */ lcr = readb(&port->ip_uart_regs->iu_lcr); lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | UART_LCR_PARITY | LCR_MASK_STOP_BITS); /* Set byte size in lcr */ lcr |= sizebits; /* Set parity */ if (parenb) { lcr |= UART_LCR_PARITY; if (!parodd) lcr |= UART_LCR_EPAR; } /* Set stop bits */ if (stop_bits) lcr |= UART_LCR_STOP /* 2 stop bits */ ; writeb(lcr, &port->ip_uart_regs->iu_lcr); /* Re-enable the DMA interface if necessary */ if (port->ip_sscr & SSCR_DMA_EN) { writel(port->ip_sscr, &port->ip_serial_regs->sscr); } port->ip_baud = baud; /* When we get within this number of ring entries of filling the * entire ring on tx, place an EXPLICIT intr to generate a lowat * notification when output has drained. */ port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; if (port->ip_tx_lowat == 0) port->ip_tx_lowat = 1; set_rx_timeout(port, 2); return 0;}/** * do_write - Write bytes to the port. Returns the number of bytes * actually written. Called from transmit_chars * @port: port to use * @buf: the stuff to write * @len: how many bytes in 'buf' */static inline int do_write(struct ioc3_port *port, char *buf, int len){ int prod_ptr, cons_ptr, total = 0; struct ring *outring; struct ring_entry *entry; struct port_hooks *hooks = port->ip_hooks; BUG_ON(!(len >= 0)); prod_ptr = port->ip_tx_prod; cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; outring = port->ip_outring; /* Maintain a 1-entry red-zone. The ring buffer is full when * (cons - prod) % ring_size is 1. Rather than do this subtraction * in the body of the loop, I'll do it now. */ cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; /* Stuff the bytes into the output */ while ((prod_ptr != cons_ptr) && (len > 0)) { int xx; /* Get 4 bytes (one ring entry) at a time */ entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); /* Invalidate all entries */ entry->ring_allsc = 0; /* Copy in some bytes */ for (xx = 0; (xx < 4) && (len > 0); xx++) { entry->ring_data[xx] = *buf++; entry->ring_sc[xx] = TXCB_VALID; len--; total++; } /* If we are within some small threshold of filling up the * entire ring buffer, we must place an EXPLICIT intr here * to generate a lowat interrupt in case we subsequently * really do fill up the ring and the caller goes to sleep. * No need to place more than one though. */ if (!(port->ip_flags & LOWAT_WRITTEN) && ((cons_ptr - prod_ptr) & PROD_CONS_MASK) <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) { port->ip_flags |= LOWAT_WRITTEN; entry->ring_sc[0] |= TXCB_INT_WHEN_DONE; } /* Go on to next entry */ prod_ptr += sizeof(struct ring_entry); prod_ptr &= PROD_CONS_MASK; } /* If we sent something, start DMA if necessary */ if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) { port->ip_sscr |= SSCR_DMA_EN; writel(port->ip_sscr, &port->ip_serial_regs->sscr); } /* Store the new producer pointer. If tx is disabled, we stuff the * data into the ring buffer, but we don't actually start tx. */ if (!uart_tx_stopped(port->ip_port)) { writel(prod_ptr, &port->ip_serial_regs->stpir); /* If we are now transmitting, enable tx_mt interrupt so we * can disable DMA if necessary when the tx finishes. */ if (total > 0) enable_intrs(port, hooks->intr_tx_mt); } port->ip_tx_prod = prod_ptr; return total;}/** * disable_intrs - disable interrupts * @port: port to enable * @mask: mask to use */static inline void disable_intrs(struct ioc3_port *port, uint32_t mask){ if (port->ip_card->ic_enable & mask) { ioc3_disable(port->ip_is, port->ip_idd, mask); port->ip_card->ic_enable &= ~mask; }}/** * set_notification - Modify event notification * @port: port to use * @mask: events mask * @set_on: set ? */static int set_notification(struct ioc3_port *port, int mask, int set_on){ struct port_hooks *hooks = port->ip_hooks; uint32_t intrbits, sscrbits; BUG_ON(!mask); intrbits = sscrbits = 0; if (mask & N_DATA_READY) intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); if (mask & N_OUTPUT_LOWAT) intrbits |= hooks->intr_tx_explicit; if (mask & N_DDCD) { intrbits |= hooks->intr_delta_dcd; sscrbits |= SSCR_RX_RING_DCD; } if (mask & N_DCTS) intrbits |= hooks->intr_delta_cts; if (set_on) { enable_intrs(port, intrbits); port->ip_notify |= mask; port->ip_sscr |= sscrbits; } else { disable_intrs(port, intrbits); port->ip_notify &= ~mask; port->ip_sscr &= ~sscrbits; } /* We require DMA if either DATA_READY or DDCD notification is * currently requested. If neither of these is requested and * there is currently no tx in progress, DMA may be disabled. */ if (port->ip_notify & (N_DATA_READY | N_DDCD)) port->ip_sscr |= SSCR_DMA_EN; else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt)) port->ip_sscr &= ~SSCR_DMA_EN; writel(port->ip_sscr, &port->ip_serial_regs->sscr); return 0;}/** * set_mcr - set the master control reg * @the_port: port to use * @mask1: mcr mask * @mask2: shadow mask */static inline int set_mcr(struct uart_port *the_port, int mask1, int mask2){ struct ioc3_port *port = get_ioc3_port(the_port); uint32_t shadow; int spiniter = 0; char mcr; if (!port) return -1; /* Pause the DMA interface if necessary */ if (port->ip_sscr & SSCR_DMA_EN) { writel(port->ip_sscr | SSCR_DMA_PAUSE, &port->ip_serial_regs->sscr); while ((readl(&port->ip_serial_regs->sscr) & SSCR_PAUSE_STATE) == 0) { spiniter++; if (spiniter > MAXITER) return -1; } } shadow = readl(&port->ip_serial_regs->shadow); mcr = (shadow & 0xff000000) >> 24; /* Set new value */ mcr |= mask1; shadow |= mask2; writeb(mcr, &port->ip_uart_regs->iu_mcr); writel(shadow, &port->ip_serial_regs->shadow); /* Re-enable the DMA interface if necessary */ if (port->ip_sscr & SSCR_DMA_EN) { writel(port->ip_sscr, &port->ip_serial_regs->sscr); } return 0;}/** * ioc3_set_proto - set the protocol for the port * @port: port to use * @proto: protocol to use */static int ioc3_set_proto(struct ioc3_port *port, int proto){ struct port_hooks *hooks = port->ip_hooks; switch (proto) { default: case PROTO_RS232: /* Clear the appropriate GIO pin */ DPRINT_CONFIG(("%s: rs232\n", __FUNCTION__)); writel(0, (&port->ip_idd->vma->gppr[0] + hooks->rs422_select_pin)); break; case PROTO_RS422: /* Set the appropriate GIO pin */ DPRINT_CONFIG(("%s: rs422\n", __FUNCTION__)); writel(1, (&port->ip_idd->vma->gppr[0] + hooks->rs422_select_pin)); break; } return 0;}/** * transmit_chars - upper level write, called with the_port->lock * @the_port: port to write */static void transmit_chars(struct uart_port *the_port){ int xmit_count, tail, head; int result; char *start; struct tty_struct *tty; struct ioc3_port *port = get_ioc3_port(the_port); struct uart_info *info; if (!the_port) return; if (!port) return; info = the_port->info; tty = info->tty; if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) { /* Nothing to do or hw stopped */ set_notification(port, N_ALL_OUTPUT, 0); return; } head = info->xmit.head; tail = info->xmit.tail; start = (char *)&info->xmit.buf[tail]; /* write out all the data or until the end of the buffer */ xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); if (xmit_count > 0) { result = do_write(port, start, xmit_count); if (result > 0) { /* booking */ xmit_count -= result; the_port->icount.tx += result; /* advance the pointers */ tail += result; tail &= UART_XMIT_SIZE - 1; info->xmit.tail = tail; start = (char *)&info->xmit.buf[tail]; } } if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS) uart_write_wakeup(the_port); if (uart_circ_empty(&info->xmit)) { set_notification(port, N_OUTPUT_LOWAT, 0); } else { set_notification(port, N_OUTPUT_LOWAT, 1); }}/** * ioc3_change_speed - change the speed of the port * @the_port: port to change * @new_termios: new termios settings * @old_termios: old termios settings */static voidioc3_change_speed(struct uart_port *the_port, struct ktermios *new_termios, struct ktermios *old_termios){ struct ioc3_port *port = get_ioc3_port(the_port); unsigned int cflag; int baud; int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; struct uart_info *info = the_port->info; cflag = new_termios->c_cflag; switch (cflag & CSIZE) { case CS5: new_data = 5; break; case CS6: new_data = 6; break; case CS7: new_data = 7; break; case CS8: new_data = 8; break; default: /* cuz we always need a default ... */ new_data = 5; break; } if (cflag & CSTOPB) { new_stop = 1; } if (cflag & PARENB) { new_parity_enable = 1; if (cflag & PARODD) new_parity = 1; } baud = uart_get_baud_rate(the_port, new_termios, old_termios, MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __FUNCTION__, baud, the_port->line)); if (!the_port->fifosize) the_port->fifosize = FIFO_SIZE; uart_update_timeout(the_port, cflag, baud); the_port->ignore_status_mask = N_ALL_INPUT; info->tty->low_latency = 1; if (I_IGNPAR(info->tty)) the_port->ignore_status_mask &= ~(N_PARITY_ERROR | N_FRAMING_ERROR); if (I_IGNBRK(info->tty)) { the_port->ignore_status_mask &= ~N_BREAK; if (I_IGNPAR(info->tty)) the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; } if (!(cflag & CREAD)) { /* ignore everything */ the_port->ignore_status_mask &= ~N_DATA_READY; } if (cflag & CRTSCTS) { /* enable hardware flow control */ port->ip_sscr |= SSCR_HFC_EN; } else { /* disable hardware flow control */ port->ip_sscr &= ~SSCR_HFC_EN; } writel(port->ip_sscr, &port->ip_serial_regs->sscr); /* Set the configuration and proper notification call */ DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " "config_port(baud %d data %d stop %d penable %d " " parity %d), notification 0x%x\n", __FUNCTION__, (void *)port, the_port->line, cflag, baud, new_data, new_stop, new_parity_enable, new_parity, the_port->ignore_status_mask)); if ((config_port(port, baud, /* baud */ new_data, /* byte size */ new_stop, /* stop bits */ new_parity_enable, /* set parity */ new_parity)) >= 0) { /* parity 1==odd */ set_notification(port, the_port->ignore_status_mask, 1); }}/** * ic3_startup_local - Start up the serial port - returns >= 0 if no errors * @the_port: Port to operate on */static inline int ic3_startup_local(struct uart_port *the_port){ struct ioc3_port *port; if (!the_port) { NOT_PROGRESS(); return -1; } port = get_ioc3_port(the_port); if (!port) { NOT_PROGRESS(); return -1; } local_open(port); /* set the protocol */ ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 : PROTO_RS422); return 0;}/* * ioc3_cb_output_lowat - called when the output low water mark is hit * @port: port to output */static void ioc3_cb_output_lowat(struct ioc3_port *port){ unsigned long pflags; /* the_port->lock is set on the call here */ if (port->ip_port) { spin_lock_irqsave(&port->ip_port->lock, pflags); transmit_chars(port->ip_port); spin_unlock_irqrestore(&port->ip_port->lock, pflags); }}/* * ioc3_cb_post_ncs - called for some basic errors * @port: port to use * @ncs: event */static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs){ struct uart_icount *icount; icount = &the_port->icount; if (ncs & NCS_BREAK) icount->brk++; if (ncs & NCS_FRAMING)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?