📄 sio_ioc4.c
字号:
if (port->ip_notify & N_DDCD) { PROGRESS(); if (shadow & IOC4_SHADOW_DCD) /* Notify upper layer of DCD */ UP_DDCD(gp, 1); else port->ip_flags |= DCD_ON; /* Flag delta DCD/no DCD */ } } /* Handle a CTS change */ if (sio_ir & H_INTR_DELTA_CTS) { DEBUGINC(dcts_intr, 1); PROGRESS(); /* ACK the interrupt */ PCI_OUTW(&port->ip_ioc4->sio_ir, H_INTR_DELTA_CTS); shadow = PCI_INW(&port->ip_serial->shadow); /* Notify upper layer */ if (port->ip_notify & N_DCTS) { if (shadow & IOC4_SHADOW_CTS) UP_DCTS(gp, 1); else UP_DCTS(gp, 0); } } /* RX timeout interrupt. Must be some data available. Put this * before the check for RX_HIGH since servicing this condition * may cause that condition to clear. */ if (sio_ir & H_INTR_RX_TIMER) { PROGRESS(); DEBUGINC(rx_timer_intr, 1); /* ACK the interrupt */ PCI_OUTW(&port->ip_ioc4->sio_ir, H_INTR_RX_TIMER); if (port->ip_notify & N_DATA_READY) UP_DATA_READY(gp); } /* RX high interrupt. Must be after RX_TIMER. */ else if (sio_ir & H_INTR_RX_HIGH) { DEBUGINC(rx_high_intr, 1); PROGRESS(); /* Data available, notify upper layer */ if (port->ip_notify & N_DATA_READY) UP_DATA_READY(gp); /* We can't ACK this interrupt. If up_data_ready didn't * cause the condition to clear, we'll have to disable * the interrupt until the data is drained by the upper layer. * If the read was aborted, don't disable the interrupt as * this may cause us to hang indefinitely. An aborted read * generally means that this interrupt hasn't been delivered * to the cpu yet anyway, even though we see it as asserted * when we read the sio_ir. */ if ((sio_ir = PENDING(port)) & H_INTR_RX_HIGH) { PROGRESS(); if ((port->ip_flags & READ_ABORTED) == 0) { mask_disable_intrs(port, H_INTR_RX_HIGH); port->ip_flags |= INPUT_HIGH; } else { DEBUGINC(read_aborted_detected, 1); /* We will be stuck in this loop forever, * higher level will never get time to finish */ rx_high_rd_aborted++; } } } /* We got a low water interrupt: notify upper layer to * send more data. Must come before TX_MT since servicing * this condition may cause that condition to clear. */ if (sio_ir & H_INTR_TX_EXPLICIT) { DEBUGINC(explicit_intr, 1); PROGRESS(); port->ip_flags &= ~LOWAT_WRITTEN; /* ACK the interrupt */ PCI_OUTW(&port->ip_ioc4->sio_ir, H_INTR_TX_EXPLICIT); if (port->ip_notify & N_OUTPUT_LOWAT) UP_OUTPUT_LOWAT(gp); } /* Handle TX_MT. Must come after TX_EXPLICIT. */ else if (sio_ir & H_INTR_TX_MT) { DEBUGINC(mt_intr, 1); PROGRESS(); /* If the upper layer is expecting a lowat notification * and we get to this point it probably means that for * some reason the TX_EXPLICIT didn't work as expected * (that can legitimately happen if the output buffer is * filled up in just the right way). So sent the notification * now. */ if (port->ip_notify & N_OUTPUT_LOWAT) { DEBUGINC(mt_lowat_intr, 1); PROGRESS(); if (port->ip_notify & N_OUTPUT_LOWAT) UP_OUTPUT_LOWAT(gp); /* We need to reload the sio_ir since the upcall may * have caused another write to occur, clearing * the TX_MT condition. */ sio_ir = PENDING(port); } /* If the TX_MT condition still persists even after the upcall, * we've got some work to do. */ if (sio_ir & H_INTR_TX_MT) { PROGRESS(); /* If we are not currently expecting DMA input, and the * transmitter has just gone idle, there is no longer any * reason for DMA, so disable it. */ if (!(port->ip_notify & (N_DATA_READY | N_DDCD))) { ASSERT(port->ip_sscr & IOC4_SSCR_DMA_EN); port->ip_sscr &= ~IOC4_SSCR_DMA_EN; PCI_OUTW(&port->ip_serial->sscr, port->ip_sscr); } /* Prevent infinite TX_MT interrupt */ mask_disable_intrs(port, H_INTR_TX_MT); } } sio_ir = PENDING(port); /* if the read was aborted and only H_INTR_RX_HIGH, * clear H_INTR_RX_HIGH, so we do not loop forever. */ if ( rx_high_rd_aborted && (sio_ir == H_INTR_RX_HIGH) ) { sio_ir &= ~H_INTR_RX_HIGH; } } while (sio_ir & H_INTR_ALL); SIO_UNLOCK_PORT(gp, flags); /* Re-enable interrupts before returning from interrupt handler. * Getting interrupted here is okay. It'll just v() our semaphore, and * we'll come through the loop again. */ IOC4_WRITE_IES(port->ip_ioc4_soft, port->ip_ienb, ioc4_sio_intr_type);}/*ARGSUSED*//* Service any pending DMA error interrupts on the given port */static voidioc4_dma_error_intr(intr_arg_t arg, ioc4reg_t other_ir){ ioc4port_t *port = (ioc4port_t *) arg; sioport_t *gp = GPORT(port); struct hooks *hooks = port->ip_hooks; unsigned int flags; SIO_LOCK_PORT(gp, flags); dprintf(("interrupt: other_ir 0x%x\n", other_ir)); /* ACK the interrupt */ PCI_OUTW(&port->ip_ioc4->other_ir, H_INTR_DMA_ERROR); printk( "DMA error on serial port %p\n", (void *)port->ip_port_vhdl); if (port->ip_ioc4->pci_err_addr_l & IOC4_PCI_ERR_ADDR_VLD) { printk( "PCI error address is 0x%lx, master is serial port %c %s\n", ((uint64_t) port->ip_ioc4->pci_err_addr_h << 32) | (port->ip_ioc4->pci_err_addr_l & IOC4_PCI_ERR_ADDR_ADDR_MSK), '1' + (char) ((port->ip_ioc4->pci_err_addr_l & IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1), (port->ip_ioc4->pci_err_addr_l & IOC4_PCI_ERR_ADDR_MST_TYP_MSK) ? "RX" : "TX"); if (port->ip_ioc4->pci_err_addr_l & IOC4_PCI_ERR_ADDR_MUL_ERR) printk( "Multiple errors occurred\n"); } SIO_UNLOCK_PORT(gp, flags); /* Re-enable DMA error interrupts */ IOC4_WRITE_IES(port->ip_ioc4_soft, H_INTR_DMA_ERROR, ioc4_other_intr_type);}/* Baud rate setting code */static intset_baud_ti(ioc4port_t *port, int baud){ int actual_baud; int diff; int lcr; unsigned short divisor; divisor = SER_DIVISOR(baud, IOC4_SER_XIN_CLK); if (!divisor) return(1); actual_baud = DIVISOR_TO_BAUD(divisor, IOC4_SER_XIN_CLK); diff = actual_baud - baud; if (diff < 0) diff = -diff; /* If we're within 1%, we've found a match */ if (diff * 100 > actual_baud) return(1); lcr = PCI_INB(&port->ip_uart->i4u_lcr); PCI_OUTB(&port->ip_uart->i4u_lcr, lcr | LCR_DLAB); PCI_OUTB(&port->ip_uart->i4u_dll, (char) divisor); PCI_OUTB(&port->ip_uart->i4u_dlm, (char) (divisor >> 8)); PCI_OUTB(&port->ip_uart->i4u_lcr, lcr); return(0);}/* Initialize the sio and ioc4 hardware for a given port */static inthardware_init(ioc4port_t *port){ ioc4reg_t sio_cr; struct hooks *hooks = port->ip_hooks; DEBUGINC(ports, 1); /* Idle the IOC4 serial interface */ PCI_OUTW(&port->ip_serial->sscr, IOC4_SSCR_RESET); /* Wait until any pending bus activity for this port has ceased */ do sio_cr = PCI_INW(&port->ip_ioc4->sio_cr); while(!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); /* Finish reset sequence */ PCI_OUTW(&port->ip_serial->sscr, 0); /* Once RESET is done, reload cached tx_prod and rx_cons values * and set rings to empty by making prod == cons */ port->ip_tx_prod = PCI_INW(&port->ip_serial->stcir) & PROD_CONS_MASK; PCI_OUTW(&port->ip_serial->stpir, port->ip_tx_prod); port->ip_rx_cons = PCI_INW(&port->ip_serial->srpir) & PROD_CONS_MASK; PCI_OUTW(&port->ip_serial->srcir, port->ip_rx_cons); /* Disable interrupts for this 16550 */ PCI_OUTB(&port->ip_uart->i4u_lcr, 0); /* clear DLAB */ PCI_OUTB(&port->ip_uart->i4u_ier, 0); /* Set the default baud */ SET_BAUD(port, port->ip_baud); /* Set line control to 8 bits no parity */ PCI_OUTB(&port->ip_uart->i4u_lcr, LCR_BITS8 | LCR_1_STOP_BITS); /* Enable the FIFOs */ PCI_OUTB(&port->ip_uart->i4u_fcr, FCR_FIFOEN); /* then reset 16550 FIFOs */ PCI_OUTB(&port->ip_uart->i4u_fcr, FCR_FIFOEN | FCR_RxFIFO | FCR_TxFIFO); /* Clear modem control register */ PCI_OUTB(&port->ip_uart->i4u_mcr, 0); /* Clear deltas in modem status register */ PCI_INB(&port->ip_uart->i4u_msr); /* Only do this once per port pair */ if (port->ip_hooks == &hooks_array[0] || port->ip_hooks == &hooks_array[2]) { iopaddr_t ring_pci_addr; volatile ioc4reg_t *sbbr_l; volatile ioc4reg_t *sbbr_h; if(port->ip_hooks == &hooks_array[0]) { sbbr_l = &port->ip_ioc4->sbbr01_l; sbbr_h = &port->ip_ioc4->sbbr01_h; } else { sbbr_l = &port->ip_ioc4->sbbr23_l; sbbr_h = &port->ip_ioc4->sbbr23_h; } /* Set the DMA address */ ring_pci_addr = ring_dmatrans(port->ip_conn_vhdl, port->ip_ring_buf_k0); PCI_OUTW(sbbr_h, (ioc4reg_t) ((__psunsigned_t) ring_pci_addr >> 32)); PCI_OUTW(sbbr_l, ((ioc4reg_t) (int64_t) ring_pci_addr | IOC4_BUF_SIZE_BIT));#ifdef IOC4_SIO_DEBUG { unsigned int tmp1, tmp2; tmp1 = PCI_INW(sbbr_l); tmp2 = PCI_INW(sbbr_h); printk("========== %s : sbbr_l [%p]/0x%x sbbr_h [%p]/0x%x\n", __FUNCTION__, (void *)sbbr_l, tmp1, (void *)sbbr_h, tmp2); }#endif } /* Set the receive timeout value to 10 msec */ PCI_OUTW(&port->ip_serial->srtr, IOC4_SRTR_HZ / 100); /* Set RX threshold, enable DMA */ /* Set high water mark at 3/4 of full ring */ port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); PCI_OUTW(&port->ip_serial->sscr, port->ip_sscr); /* Disable and clear all serial related interrupt bits */ IOC4_WRITE_IEC(port->ip_ioc4_soft, H_INTR_CLEAR, ioc4_sio_intr_type); port->ip_ienb &= ~H_INTR_CLEAR; PCI_OUTW(&port->ip_ioc4->sio_ir, H_INTR_CLEAR); return(0);}/* * Device initialization. * Called at *_attach() time for each * IOC4 with serial ports in the system. * If vhdl is GRAPH_VERTEX_NONE, do not do * any graph related work; otherwise, it * is the IOC4 vertex that should be used * for requesting pciio services. */intioc4_serial_attach(vertex_hdl_t conn_vhdl, void *ioc4){ /*REFERENCED*/ graph_error_t rc; ioc4_mem_t *ioc4_mem; vertex_hdl_t port_vhdl, ioc4_vhdl; vertex_hdl_t intr_dev_vhdl; ioc4port_t *port; ioc4port_t *ports[4]; static char *names[] = { "tty/1", "tty/2", "tty/3", "tty/4" }; int x, first_port = -1, last_port = -1; void *ioc4_soft; unsigned int ioc4_revid_min = 62; unsigned int ioc4_revid; /* IOC4 firmware must be at least rev 62 */ ioc4_revid = pciio_config_get(conn_vhdl, PCI_CFG_REV_ID, 1); if (ioc4_revid < ioc4_revid_min) { printk( "IOC4 serial ports not supported on firmware rev %d, please upgrade to rev %d or higher\n", ioc4_revid, ioc4_revid_min); return -1; } first_port = 0; last_port = 3; /* Get back pointer to the ioc4 soft area */ rc = hwgraph_traverse(conn_vhdl, EDGE_LBL_IOC4, &ioc4_vhdl); ASSERT(rc == GRAPH_SUCCESS); ioc4_soft = (void *)hwgraph_fastinfo_get(ioc4_vhdl); /* grab the PIO address */ ioc4_mem = (ioc4_mem_t *)ioc4; ASSERT(ioc4_mem != NULL); /* * Create port structures for each port */ NEWA(port, 4);#ifdef IOC4_SIO_DEBUG printk("%s : [addr 0x%p]\n", __FUNCTION__, (void *)port);#endif ports[0] = port++; ports[1] = port++; ports[2] = port++; ports[3] = port++;#if DEBUG { int slot = atomicAddInt(&next_saveport, 4) - 4; saveport[slot] = ports[0]; saveport[slot + 1] = ports[1]; saveport[slot + 2] = ports[2]; saveport[slot + 3] = ports[3]; ASSERT(slot < MAXSAVEPORT); }#endif#ifdef DEBUG if ((caddr_t) port != (caddr_t) &(port->ip_sioport)) panic("sioport is not first member of ioc4port struct\n");#endif /* Allocate buffers and jumpstart the hardware. */ for (x = first_port; x < (last_port + 1); x++) { port = ports[x];#ifdef IOC4_SIO_DEBUG printk("%s : initialize port %d [addr 0x%p/0x%p]\n", __FUNCTION__, x, (void *)port, (void *)GPORT(port));#endif port->ip_ioc4_soft = ioc4_soft; rc = hwgraph_path_add(conn_vhdl, names[x], &port_vhdl); ASSERT(rc == GRAPH_SUCCESS); port->ip_conn_vhdl = conn_vhdl; port->ip_port_vhdl = port_vhdl; port->ip_ienb = 0; hwgraph_fastinfo_set(port_vhdl, (arbitrary_info_t) port); /* Perform upper layer initialization. Create all device node * types including rs422 ports. */ ioc4_serial_initport(GPORT(port), x); port->ip_baud = 9600; /* Attach the calldown hooks so upper layer can call our * routines. */ port->ip_sioport.sio_calldown = &ioc4_calldown; /* Map in the IOC4 register area */ port->ip_ioc4 = ioc4_mem; } { /* Port 0 */ port = ports[0]; port->ip_hooks = &hooks_array[0]; /* Get direct hooks to the serial regs and uart regs * for this port */ port->ip_serial = &(port->ip_ioc4->port_0); port->ip_uart = &(port->ip_ioc4->uart_0);#ifdef IOC4_SIO_DEBUG printk("==== %s : serial port 0 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[0]->ip_port_vhdl; } /* Attach interrupt handlers */ ioc4_intr_connect(conn_vhdl, ioc4_sio_intr_type, IOC4_SIO_IR_S0, ioc4_serial_intr, ports[0], ports[0]->ip_port_vhdl, intr_dev_vhdl); ioc4_intr_connect(conn_vhdl, ioc4_other_intr_type, IOC4_OTHER_IR_S0_MEMERR, ioc4_dma_error_intr, ports[0], ports[0]->ip_port_vhdl, intr_dev_vhdl); } { /* Port 1 */ port = ports[1]; port->ip_hooks = &hooks_array[1]; port->ip_serial = &(port->ip_ioc4->port_1); port->ip_uart = &(port->ip_ioc4->uart_1);#ifdef IOC4_SIO_DEBUG printk("==== %s : serial port 1 address 0x%p uart address 0x%p\n", __FUNCTION__, (void *)port->ip_serial, (void *)port->ip_uart);#endif port->ip_ring_buf_k0 = ports[0]->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[1]->ip_port_vhdl, "d", &intr_dev_vhdl) != GRAPH_SUCCESS) { intr_dev_vhdl = ports[1]->ip_port_vhdl; } /* Attach interrupt handler */ ioc4_intr_connect(conn_vhdl, ioc4_sio_intr_type, IOC4_SIO_IR_S1, ioc4_serial_intr, ports[1], ports[1]->ip_port_vhdl, intr_dev_vhdl); ioc4_intr_connect(conn_vhdl, ioc4_other_intr_type, IOC4_OTHER_IR_S1_MEMERR, ioc4_dma_error_intr,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -