📄 sio_ioc4.c
字号:
ports[1], ports[1]->ip_port_vhdl, intr_dev_vhdl); } { /* Port 2 */ port = ports[2]; port->ip_hooks = &hooks_array[2]; /* Get direct hooks to the serial regs and uart regs * for this port */ port->ip_serial = &(port->ip_ioc4->port_2); port->ip_uart = &(port->ip_ioc4->uart_2);#ifdef IOC4_SIO_DEBUG printk("==== %s : serial port 2 address 0x%p uart address 0x%p\n", __FUNCTION__, (void *)port->ip_serial, (void *)port->ip_uart);#endif /* If we don't already have a ring buffer, * set one up. */ if (port->ip_ring_buf_k0 == 0) {#if PAGE_SIZE >= TOTAL_RING_BUF_SIZE if ((port->ip_ring_buf_k0 = kvpalloc(1, VM_DIRECT, 0)) == 0) panic("ioc4_uart driver cannot allocate page\n");#else /* We need to allocate a chunk of memory on a * TOTAL_RING_BUF_SIZE boundary. */ { pgno_t pfn; caddr_t vaddr; if ((pfn = contig_memalloc(TOTAL_RING_BUF_SIZE / PAGE_SIZE, TOTAL_RING_BUF_SIZE / PAGE_SIZE, VM_DIRECT)) == 0) panic("ioc4_uart driver cannot allocate page\n"); ASSERT(small_pfn(pfn)); vaddr = small_pfntova_K0(pfn); (void) COLOR_VALIDATION(pfdat + pfn, colorof(vaddr), 0, VM_DIRECT); port->ip_ring_buf_k0 = vaddr; }#endif } ASSERT((((int64_t)port->ip_ring_buf_k0) & (TOTAL_RING_BUF_SIZE - 1)) == 0); memset(port->ip_ring_buf_k0, 0, TOTAL_RING_BUF_SIZE); port->ip_inring = RING(port, RX_0_OR_2); port->ip_outring = RING(port, TX_0_OR_2); /* Initialize the hardware for IOC4 */ hardware_init(port); if (hwgraph_edge_get(ports[0]->ip_port_vhdl, "d", &intr_dev_vhdl) != GRAPH_SUCCESS) { intr_dev_vhdl = ports[2]->ip_port_vhdl; } /* Attach interrupt handler */ ioc4_intr_connect(conn_vhdl, ioc4_sio_intr_type, IOC4_SIO_IR_S2, ioc4_serial_intr, ports[2], ports[2]->ip_port_vhdl, intr_dev_vhdl); ioc4_intr_connect(conn_vhdl, ioc4_other_intr_type, IOC4_OTHER_IR_S2_MEMERR, ioc4_dma_error_intr, ports[2], ports[2]->ip_port_vhdl, intr_dev_vhdl); } { /* Port 3 */ port = ports[3]; port->ip_hooks = &hooks_array[3]; port->ip_serial = &(port->ip_ioc4->port_3); port->ip_uart = &(port->ip_ioc4->uart_3);#ifdef IOC4_SIO_DEBUG printk("==== %s : serial port 3 address 0x%p uart address 0x%p\n", __FUNCTION__, (void *)port->ip_serial, (void *)port->ip_uart);#endif port->ip_ring_buf_k0 = ports[2]->ip_ring_buf_k0; port->ip_inring = RING(port, RX_1_OR_3); port->ip_outring = RING(port, TX_1_OR_3); /* Initialize the hardware for IOC4 */ hardware_init(port); if (hwgraph_edge_get(ports[3]->ip_port_vhdl, "d", &intr_dev_vhdl) != GRAPH_SUCCESS) { intr_dev_vhdl = ports[3]->ip_port_vhdl; } /* Attach interrupt handler */ ioc4_intr_connect(conn_vhdl, ioc4_sio_intr_type, IOC4_SIO_IR_S3, ioc4_serial_intr, ports[3], ports[3]->ip_port_vhdl, intr_dev_vhdl); ioc4_intr_connect(conn_vhdl, ioc4_other_intr_type, IOC4_OTHER_IR_S3_MEMERR, ioc4_dma_error_intr, ports[3], ports[3]->ip_port_vhdl, intr_dev_vhdl); }#ifdef DEBUG idbg_addfunc( "ioc4dump", idbg_ioc4dump );#endif return 0;}/* Shut down an IOC4 *//* ARGSUSED1 */voidioc4_serial_kill(ioc4port_t *port){ DEBUGINC(killed, 1); /* Notify upper layer that this port is no longer usable */ UP_DETACH(GPORT(port)); /* Clear everything in the sscr */ PCI_OUTW(&port->ip_serial->sscr, 0); port->ip_sscr = 0;#ifdef DEBUG /* Make sure nobody gets past the lock and accesses the hardware */ port->ip_ioc4 = 0; port->ip_serial = 0;#endif}/* * Open a port */static intioc4_open(sioport_t *port){ ioc4port_t *p = LPORT(port); int spin_success;#ifdef NOT_YET ASSERT(L_LOCKED(port, L_OPEN));#endif p->ip_flags = 0; p->ip_modem_bits = 0; /* Pause the DMA interface if necessary */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr | IOC4_SSCR_DMA_PAUSE); SPIN((PCI_INW(&p->ip_serial->sscr) & IOC4_SSCR_PAUSE_STATE) == 0, spin_success); if (!spin_success) { NOT_PROGRESS(); return(-1); } } /* Reset the input fifo. If the uart received chars while the port * was closed and DMA is not enabled, the uart may have a bunch of * chars hanging around in its RX fifo which will not be discarded * by rclr in the upper layer. We must get rid of them here. */ PCI_OUTB(&p->ip_uart->i4u_fcr, FCR_FIFOEN | FCR_RxFIFO); /* Set defaults */ SET_BAUD(p, 9600); PCI_OUTB(&p->ip_uart->i4u_lcr, LCR_BITS8 | LCR_1_STOP_BITS); /* Re-enable DMA, set default threshold to intr whenever there is * data available. */ p->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; p->ip_sscr |= 1; /* default threshold */ /* Plug in the new sscr. This implicitly clears the DMA_PAUSE * flag if it was set above */ PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr); PCI_OUTW(&p->ip_serial->srtr, 0); p->ip_tx_lowat = 1; dprintf(("ioc4 open successful\n")); return(0);}/* * Config hardware */static intioc4_config(sioport_t *port, int baud, int byte_size, int stop_bits, int parenb, int parodd){ ioc4port_t *p = LPORT(port); char lcr, sizebits; int spin_success;#ifdef NOT_YET ASSERT(L_LOCKED(port, L_CONFIG));#endif if (SET_BAUD(p, baud)) return(1); switch(byte_size) { case 5: sizebits = LCR_BITS5; break; case 6: sizebits = LCR_BITS6; break; case 7: sizebits = LCR_BITS7; break; case 8: sizebits = LCR_BITS8; break; default: dprintf(("invalid byte size port 0x%x size %d\n", port, byte_size)); return(1); } /* Pause the DMA interface if necessary */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr | IOC4_SSCR_DMA_PAUSE); SPIN((PCI_INW(&p->ip_serial->sscr) & IOC4_SSCR_PAUSE_STATE) == 0, spin_success); if (!spin_success) return(-1); } /* Clear relevant fields in lcr */ lcr = PCI_INB(&p->ip_uart->i4u_lcr); lcr &= ~(LCR_MASK_BITS_CHAR | LCR_EPS | LCR_PEN | LCR_MASK_STOP_BITS); /* Set byte size in lcr */ lcr |= sizebits; /* Set parity */ if (parenb) { lcr |= LCR_PEN; if (!parodd) lcr |= LCR_EPS; } /* Set stop bits */ if (stop_bits) lcr |= LCR_2_STOP_BITS; PCI_OUTB(&p->ip_uart->i4u_lcr, lcr); dprintf(("ioc4_config: lcr bits 0x%x\n", lcr)); /* Re-enable the DMA interface if necessary */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr); } p->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. */ p->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; if (p->ip_tx_lowat == 0) p->ip_tx_lowat = 1; ioc4_rx_timeout(port, p->ip_rx_timeout); return(0);}/* * Enable hardware flow control */static intioc4_enable_hfc(sioport_t *port, int enable){ ioc4port_t *p = LPORT(port);#ifdef NOT_YET ASSERT(L_LOCKED(port, L_ENABLE_HFC));#endif dprintf(("enable hfc port 0x%p, enb %d\n", (void *)port, enable)); if (enable) p->ip_sscr |= IOC4_SSCR_HFC_EN; else p->ip_sscr &= ~IOC4_SSCR_HFC_EN; PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr); return(0);}/* * Set external clock *//*ARGSUSED*/static intioc4_set_extclk(sioport_t *port, int clock_factor){#ifdef NOT_YET ASSERT(L_LOCKED(port, L_SET_EXTCLK)); /* XXX still todo */#endif /* only support 0 (no external clock) */ return(clock_factor);}/* * Write bytes to the hardware. Returns the number of bytes * actually written. */static intdo_ioc4_write(sioport_t *port, char *buf, int len){ int prod_ptr, cons_ptr, total; struct ring *outring; struct ring_entry *entry; ioc4port_t *p = LPORT(port); struct hooks *hooks = p->ip_hooks; DEBUGINC(write_bytes, len); DEBUGINC(write_cnt, 1); dprintf(("write port 0x%p, len %d\n", (void *)port, len)); ASSERT(len >= 0); prod_ptr = p->ip_tx_prod; cons_ptr = PCI_INW(&p->ip_serial->stcir) & PROD_CONS_MASK; outring = p->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; total = 0; /* Stuff the bytes into the output */ while ((prod_ptr != cons_ptr) && (len > 0)) { int x; /* Go 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(x = 0; (x < 4) && (len > 0); x++) { entry->ring_data[x] = *buf++; entry->ring_sc[x] = IOC4_TXCB_VALID; len--; total++; } DEBUGINC(tx_buf_used, x); DEBUGINC(tx_buf_cnt, 1); /* 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 (!(p->ip_flags & LOWAT_WRITTEN) && ((cons_ptr - prod_ptr) & PROD_CONS_MASK) <= p->ip_tx_lowat * (int)sizeof(struct ring_entry)) { p->ip_flags |= LOWAT_WRITTEN; entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE; dprintf(("write placing TX_EXPLICIT\n")); } /* Go on to next entry */ prod_ptr = (prod_ptr + (int) sizeof(struct ring_entry)) & PROD_CONS_MASK; } /* If we sent something, start DMA if necessary */ if (total > 0 && !(p->ip_sscr & IOC4_SSCR_DMA_EN)) { p->ip_sscr |= IOC4_SSCR_DMA_EN; PCI_OUTW(&p->ip_serial->sscr, p->ip_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 (!(p->ip_flags & TX_DISABLED)) { PCI_OUTW(&p->ip_serial->stpir, prod_ptr); /* 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(p, H_INTR_TX_MT); } p->ip_tx_prod = prod_ptr; dprintf(("write port 0x%p, wrote %d\n", (void *)port, total)); DEBUGINC(wrote_bytes, total); return(total);}/* Asynchronous write */static intioc4_write(sioport_t *port, char *buf, int len){#ifdef NOT_YET ASSERT(L_LOCKED(port, L_WRITE));#endif return(do_ioc4_write(port, buf, len));}/* Synchronous write */static intioc4_sync_write(sioport_t *port, char *buf, int len){ int bytes; ASSERT(sio_port_islocked(port)); bytes = do_ioc4_write(port, buf, len); /* Don't allow the system to hang if XOFF is in force */ if (len > 0 && bytes == 0 && (LPORT(port)->ip_flags & TX_DISABLED)) ioc4_enable_tx(port, 1); return(bytes);}/* Write flush */static voidioc4_wrflush(sioport_t *port){ ioc4port_t *p = LPORT(port); ASSERT(sio_port_islocked(port)); /* We can't flush if TX is disabled due to XOFF. */ if (!(PCI_INW(&p->ip_ioc4->sio_ir) & IOC4_SIO_IR_S0_TX_MT) && (p->ip_flags & TX_DISABLED)) ioc4_enable_tx(port, 1); /* Spin waiting for TX_MT to assert only if DMA is enabled. If we * are panicking and one of the other processors is already in * symmon, DMA will be disabled and TX_MT will never be asserted. * There may also be legitimate cases in the kernel where DMA is * disabled and we won't flush correctly here. */ while ((PCI_INW(&p->ip_serial->sscr) & (IOC4_SSCR_DMA_EN | IOC4_SSCR_PAUSE_STATE)) == IOC4_SSCR_DMA_EN && !(PCI_INW(&p->ip_ioc4->sio_ir) & IOC4_SIO_IR_S0_TX_MT)) { udelay(5); }}/* * Set or clear break condition on output */static intioc4_break(sioport_t *port, int brk){ ioc4port_t *p = LPORT(port); char lcr; int spin_success;#ifdef NOT_YET ASSERT(L_LOCKED(port, L_BREAK));#endif /* Pause the DMA interface if necessary */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr | IOC4_SSCR_DMA_PAUSE); SPIN((PCI_INW(&p->ip_serial->sscr) & IOC4_SSCR_PAUSE_STATE) == 0, spin_success); if (!spin_success) return(-1); } lcr = PCI_INB(&p->ip_uart->i4u_lcr); if (brk) { /* Set break */ PCI_OUTB(&p->ip_uart->i4u_lcr, lcr | LCR_SNDBRK); } else { /* Clear break */ PCI_OUTB(&p->ip_uart->i4u_lcr, lcr & ~LCR_SNDBRK); } /* Re-enable the DMA interface if necessary */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr); } dprintf(("break port 0x%p, brk %d\n", (void *)port, brk)); return(0);}static intioc4_enable_tx(sioport_t *port, int enb){ ioc4port_t *p = LPORT(port); struct hooks *hooks = p->ip_hooks; int spin_success;#ifdef NOT_YET ASSERT(L_LOCKED(port, L_ENABLE_TX));#endif /* If we're already in the desired state, we're done */ if ((enb && !(p->ip_flags & TX_DISABLED)) || (!enb && (p->ip_flags & TX_DISABLED))) return(0); /* Pause DMA */ if (p->ip_sscr & IOC4_SSCR_DMA_EN) { PCI_OUTW(&p->ip_serial->sscr, p->ip_sscr | IOC4_SSCR_DMA_PAUSE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -