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

📄 hc_sl811.c

📁 This is cypress sl811 driver. I think it is very useful. Thankyou.
💻 C
📖 第 1 页 / 共 3 页
字号:
    if (pid != PID_IN) 
    {
        /* Load data into hc */

	SL811BufWrite(hci, SL11H_DATA_START, (__u8 *) data, len);
    }		
    
    /* transmit */

    SL11StartXaction(hci, (__u8) address, (__u8) endpoint, 
                    (__u8) pid, len, toggle, slow, urb_state);

    return len;
}


/************************************************************************
 * Function Name : hc_parse_trans
 *  
 * This function checks the status of the transmitted or received packet
 * and copy the data from the SL811HS register into a buffer.
 *
 * 1) Check the status of the packet 
 * 2) If successful, and IN packet then copy the data from the SL811HS register
 *    into a buffer
 *
 * Input:  hci = data structure for the host controller
 *         actbytes = pointer to actual number of bytes
 *         data = data buffer
 *         cc = packet status
 *         length = the urb transmit length
 *         pid = packet ID
 *         urb_state = the current stage of USB transaction
 *       
 * Return: 0 
 ***********************************************************************/

static inline int hc_parse_trans (hci_t * hci, int * actbytes, __u8 * data, 
		int * cc, int * toggle, int length, int pid, int urb_state)
{
    __u8 addr;
    __u8 len;

    DBGFUNC ("enter hc_parse_trans\n");
	
    /* get packet status; convert ack rcvd to ack-not-rcvd */

    *cc = (int) SL811Read(hci, SL11H_PKTSTATREG);


    if (*cc & (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | 
        SL11H_STATMASK_OVF | SL11H_STATMASK_NAK | SL11H_STATMASK_STALL))
    {
 	if (*cc & SL11H_STATMASK_OVF)
            DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = "
                "0x%x, TX_count=0x%x\n", *cc, 
                 SL811Read(hci, SL11H_BUFLNTHREG), 
                 SL811Read(hci, SL11H_XFERCNTREG));
	
    }
    else 
    {
        DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n", 
	 	 *cc, length);
		
	/* Successful data */

	if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP))
	{

	    /* Find the base address */
 
	    addr = SL811Read(hci, SL11H_BUFADDRREG);

	    /* Find the Transmit Length */
 
	    len = SL811Read(hci, SL11H_BUFLNTHREG);

	    /* The actual data length = xmit length reg - xfer count reg */

	    *actbytes = len - SL811Read(hci, SL11H_XFERCNTREG);

	    if ((data != NULL) && (*actbytes > 0))
	    {
		SL811BufRead (hci, addr, data, *actbytes); 
			
	    }
	    else if ((data == NULL) && (*actbytes <=0))
	    {	
	        DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", 
                         *actbytes);
		return 0;
	    }
	}
	else if (pid == PID_OUT)
        {
	    *actbytes = length;
        }
	else
	{
         // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid);
        }	
	*toggle = !*toggle;
    }	
	
    return 0;
}

/************************************************************************
 * Function Name : hc_start_int
 *  
 * This function enables SL811HS interrupts
 *
 * Input:  hci = data structure for the host controller
 *       
 * Return: none 
 ***********************************************************************/

static void hc_start_int (hci_t *hci)
{
#ifdef HC_SWITCH_INT	
    int mask = SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR |
               SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET;
    SL811Write (hci, IntEna, mask); 
#endif
}	

/************************************************************************
 * Function Name : hc_stop_int
 *  
 * This function disables SL811HS interrupts
 *
 * Input:  hci = data structure for the host controller
 *       
 * Return: none 
 ***********************************************************************/

static void hc_stop_int (hci_t *hci)
{
#ifdef HC_SWITCH_INT
    SL811Write(hci, SL11H_INTSTATREG, 0xff); 
//  SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV);

#endif
}


/************************************************************************
 * Function Name : handleInsRmvIntr
 *  
 * This function handles the insertion or removal of device on  SL811HS. 
 * It resets the controller and updates the port status
 *
 * Input:  hci = data structure for the host controller
 *       
 * Return: none 
 ***********************************************************************/

void handleInsRmvIntr (hci_t * hci)
{
    hcipriv_t * hp = &hci->hp; 

    USBReset(hci);
	
    /* Changes in connection status */

    hp->RHportStatus->portChange |=  PORT_CONNECT_CHANGE;

    /* Port Enable or Disable */

    if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT)
    {
    /* device is connected to the port:
     *    1) Enable port 
     *    2) Resume ?? 
     */
//               hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE;

		/* Over Current is not supported by the SL811 HW ?? */

		/* How about the Port Power ?? */

    }
    else
    {
        /* Device has disconnect:
	 *    1) Disable port
	 */

	 hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT);
	 hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE;
		
		
    }
}

/*****************************************************************
 *
 * Function Name: SL11StartXaction
 *  
 * This functions load the registers with appropriate value and 
 * transmit the packet.				  
 *
 * Input:  hci = data structure for the host controller
 *         addr = USB address of the device
 *         epaddr = endpoint number
 *         pid = packet ID
 *         len = data length
 *         toggle = USB toggle bit, either 0 or 1
 *         slow = speed of the device
 *         urb_state = the current stage of USB transaction
 *
 * Return: 0 = error; 1 = successful
 *                
 *****************************************************************/

int SL11StartXaction(hci_t *hci, __u8 addr, __u8 epaddr, int pid, int len, 
                     int toggle, int slow, int urb_state)
{
	
    hcipriv_t * hp = &hci->hp; 
    __u8 cmd = 0;
    __u8 setup_data[4];
    __u16 speed;

    speed = hp->RHportStatus->portStatus;
    if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow)
    {
        cmd |= SL11H_HCTLMASK_PREAMBLE; 
    }
    switch (pid)
    {
        case PID_SETUP:
	cmd &= SL11H_HCTLMASK_PREAMBLE;
	cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP 
            | SL11H_HCTLMASK_WRITE);
	break;

	case PID_OUT:
	cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE);
	cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP 
            | SL11H_HCTLMASK_WRITE);
 	if (toggle)
	{
	    cmd |= SL11H_HCTLMASK_SEQ;
	}
	break;
	
	case PID_IN:
	cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE);
	cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP);
	break;
	
        default:
	DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid);
	return 0;   
    }
    setup_data[0] = SL11H_DATA_START;
    setup_data[1] = len;
    setup_data[2] = (((pid & 0x0F) << 4 ) | (epaddr & 0xF));
    setup_data[3] = addr & 0x7F;

    SL811BufWrite(hci, SL11H_BUFADDRREG, (__u8 *) &setup_data[0], 4);

    SL811Write(hci, SL11H_HOSTCTLREG, cmd);

#if 0
    /* The SL811 has a hardware flaw when hub devices sends out
     * SE0 between packets. It has been found in a TI chipset and
     * cypress hub chipset. It causes the SL811 to hang
     * The workaround is to re-issue the preample again.
     */

    if ((cmd & SL11H_HCTLMASK_PREAMBLE))
    {
        SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); 
	SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable
    }
#endif
    return 1;
}

/*****************************************************************
 *
 * Function Name: hc_interrupt
 *
 * Interrupt service routine. 
 *
 * 1) determine the causes of interrupt
 * 2) clears all interrupts
 * 3) calls appropriate function to service the interrupt
 *
 * Input:  irq = interrupt line associated with the controller 
 *         hci = data structure for the host controller
 *         r = holds the snapshot of the processor's context before 
 *             the processor entered interrupt code. (not used here) 
 *
 * Return value  : None.
 *                
 *****************************************************************/

static void hc_interrupt (int irq, void * __hci, struct pt_regs * r)
{
    char ii;
    hci_t * hci = __hci;
    int isExcessNak = 0;
    int urb_state = 0;
    char tmpIrq = 0;

    /* Get value from interrupt status register */
	
    ii = SL811Read(hci, SL11H_INTSTATREG);
    
    if (ii & SL11H_INTMASK_INSRMV)
    {
        /* Device insertion or removal detected for the USB port */

        SL811Write(hci, SL11H_INTENBLREG,0);	
	SL811Write(hci, SL11H_CTLREG1,0);
	mdelay(100);			// wait for device stable 
        handleInsRmvIntr(hci);
        return;
    }

    /* Clear all interrupts */

    SL811Write(hci, SL11H_INTSTATREG, 0xff);   	
    	
    if (ii & SL11H_INTMASK_XFERDONE)
    {
        /* USB Done interrupt occurred */

	urb_state = sh_done_list(hci, &isExcessNak);
#ifdef WARNING
	if (hci->td_array->len >0)
	    printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n",
	 	    hci->td_array->len);
#endif
	if (hci->td_array->len == 0 && !isExcessNak && 
	    !(ii & SL11H_INTMASK_SOFINTR) && (urb_state ==0))
	{
	    if (urb_state == 0)
            {
                /* All urb_state has not been finished yet! 
                 * continue with the current urb transaction 
                 */

		if (hci->last_packet_nak == 0)	
		{
                    if (!usb_pipecontrol (hci->td_array->td[0].urb->pipe)) 
		        sh_add_packet(hci, hci->td_array->td [0].urb); 
	        }
	    }
            else
	    {
                /* The last transaction has completed:
                 * schedule the next transaction 
                 */

               	sh_schedule_trans(hci, 0);
	    }
        }
        SL811Write(hci, SL11H_INTSTATREG, 0xff);   	
	return;
    }

    if (ii & SL11H_INTMASK_SOFINTR)
    {
        hci->frame_number = (hci->frame_number + 1) % 2048;
	if (hci->td_array->len == 0)
            sh_schedule_trans(hci, 1);
	else
	{
	    if (sofWaitCnt++ > 100)	
	    {
	        /* The last transaction has not completed.
                 * Need to retire the current td, and let
                 * it transmit again later on.
		 * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER 
		 *  GET TO THIS POINT)
                 */
		
	        DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", 
                         hci->td_array->len);
		urb_print (hci->td_array->td [hci->td_array->len-1].urb,
				       "INTERRUPT", 0);
	        sh_done_list (hci, &isExcessNak);
		SL811Write(hci, SL11H_INTSTATREG, 0xff);   	
		hci->td_array->len =0;
	        sofWaitCnt=0;
	    }
	}
	tmpIrq = SL811Read(hci, SL11H_INTSTATREG) 
		 & SL811Read(hci, SL11H_INTENBLREG);
	if (tmpIrq)
	{
	    DBG("IRQ occurred while service SOF: irq = 0x%x\n",tmpIrq);

	    /* If we receive a DONE IRQ after schedule, need to 
	     * handle DONE IRQ again 
	     */

	    if (tmpIrq & SL11H_INTMASK_XFERDONE)
	    {
	        DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n",tmpIrq);
		urb_state = sh_done_list (hci, &isExcessNak);
	    }
	    SL811Write(hci, SL11H_INTSTATREG, 0xff);   	
	}
    }
    else
    { 
        DBG("SL811 ISR: unknown, int = 0x%x \n", ii);
    }

    SL811Write(hci, SL11H_INTSTATREG, 0xff);   	
    return;
}

/*****************************************************************
 *
 * Function Name: hc_reset
 *
 * This function does register test and resets the SL811HS 
 * controller.
 *
 * Input:  hci = data structure for the host controller
 *
 * Return value  : 0
 *                
 *****************************************************************/

static int hc_reset (hci_t * hci)
{
    int attachFlag = 0;
    
    DBGFUNC ("Enter hc_reset\n");
    regTest(hci);
    attachFlag = USBReset(hci);
    if (attachFlag)
    {
        setPortChange (hci, PORT_CONNECT_CHANGE);
    }
    return (0);
}

/*****************************************************************
 *
 * Function Name: hc_alloc_trans_buffer
 *
 * This function allocates all transfer buffer  
 *
 * Input:  hci = data structure for the host controller
 *
 * Return value  : 0
 *                
 *****************************************************************/

static int hc_alloc_trans_buffer (hci_t * hci)
{
    hcipriv_t * hp = &hci->hp; 
    int maxlen;

    hp->itl0_len = 0;
    hp->itl1_len = 0;
    hp->atl_len = 0;

    hp->itl_buffer_len = 1024;
    hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */


    maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len;

    hp->tl = kmalloc (maxlen, GFP_KERNEL);
	
    if (!hp->tl)
	return -ENOMEM;

    memset (hp->tl, 0, maxlen);
    return 0;
}


/*****************************************************************
 *
 * Function Name: getPortStatusAndChange
 *
 * This function gets the ports status from SL811 and format it 
 * to a USB request format
 *
 * Input:  hci = data structure for the host controller
 *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -