⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sio_ioc4.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 4 页
字号:
	                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 + -