cy7c67200_300_hcd.c

来自「linux嵌入式课程实践中的一个关于声卡驱动程序 。」· C语言 代码 · 共 2,356 行 · 第 1/4 页

C
2,356
字号
/*-------------------------------------------------------------------------*//*-------------------------------------------------------------------------* * CY7c67200/300 USB HCD for Linux Version 0.1 (10/28/2001) *  * requires (includes) CY7c7200_300_simple.[hc] simple generic HCD frontend *   * COPYRIGHT(C) 2002 by CYPRESS SEMICONDUCTOR INC. * *-------------------------------------------------------------------------* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *-------------------------------------------------------------------------*/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/smp_lock.h>#include <linux/list.h>#include <linux/ioport.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/usb.h>#include "67300.h"#include "cy7c67200_300_common.h"#include "cy7c67200_300_lcd.h"#include "lcp_cmd.h"#include "lcp_data.h"#include "cy7c67200_300_otg.h"#include "cy7c67200_300_hcd.h"#include "cy7c67200_300_hcd_simple.h"#include "cy7c67200_300_debug.h"#include "usbd/bi/cy7c67200_300_pcd.h"#include "hwfixes.h"#include "cp_bios.h"#include "usbd/dedev/de3_bios.h"#include "usbd/dedev/de1_bios.h"#undef HC_URB_TIMEOUT#undef HC_SWITCH_INT#undef HC_ENABLE_ISOCint urb_debug = 0;#define HC_SWITCH_INT#define DEFAULT_EOT        	 		1200#define DFLT_HOST1_IRQ_ENBL    		0xC2F1#define DFLT_HOST2_IRQ_ENBL    		0x02F1#define DFLT_PERIPHERAL1_IRQ_ENBL	0xC200#define DFLT_PERIPHERAL2_IRQ_ENBL	0x0200/* configures to use SW INT on USB Reset or manually poll regs */#undef ALT_USB_HOST_RESET #define CY67X00_ADDR_SIZE 			0x1000/* Default USB Reset time in (1 = 10ms) */#define DFLT_USB_RESET_TIME			11/* MACROS *//* The following are used by the peripheral driver */// extern unsigned int usb_address[2];static int cy67x00_hw_reset (void);static int cy67x00_get_hw_config (void);static unsigned short get_port_stat(hci_t *hci, int port_num);static int __devinit hc_init_port (cy_priv_t * cy_priv, int port_num);static int __devinit hc_init_sie (cy_priv_t * cy_priv, int sie_num);static int __devinit hcd_init (cy_priv_t * cy_priv, int port_num);static void init_irq(void);void hc_host_usb_reset1 (unsigned long port_num);static void hc_host_usb_reset2 (unsigned short response,                                unsigned short value,                                int port_num,                                cy_priv_t * cy_priv);static void hc_host_usb_init2 (unsigned short response,                               unsigned short value,                               int sie_num,                               cy_priv_t * cy_priv);void set_port_change(hci_t * hci, __u16 bitPos, int port_num);int rh_connect_rh (cy_priv_t * cy_priv, int port_num);;		/* The base_addr, data_reg_addr, and irq number are board specific. * The current values are design to run on the Accelent SA1110 IDP * NOTE: values need to modify for different development boards  */ static int base_addr 	 =	0xF1000000;static int data_reg_addr = 	0xF1000100;static int irq = IRQ_GPIO_EZHOST;static int trc_addr = 0xd7300000;static cy_priv_t * g_cy_priv;static int reset_in_progress[] = {0, 0, 0, 0};struct timer_list PORT0_timer;struct timer_list PORT1_timer;struct timer_list PORT2_timer;struct timer_list PORT3_timer;/* forward declarations */MODULE_PARM(urb_debug,"i");MODULE_PARM_DESC(urb_debug,"debug urb messages, default is 0 (no)");MODULE_PARM(base_addr,"i");MODULE_PARM_DESC(base_addr,"cy7c67200/300 base address 0xd8000000");MODULE_PARM(data_reg_addr,"i");MODULE_PARM_DESC(data_reg_addr,"cy7c67200/300 data register address 0xd8000100");MODULE_PARM(irq,"i");MODULE_PARM_DESC(irq,"IRQ 2 (default)");void HWTrace(unsigned short data){//    outw(data, trc_addr);}void HWData(unsigned short data){//    outw(data, trc_addr + 0xbee);}int is_host_mode(cy_priv_t * cy_priv, int sie_num){	if (cy_priv->system_mode[sie_num] == HOST_ROLE)		return (TRUE);		else 		return (FALSE);}/************************************************************************ * Function Name : hcd_host_usb_init *   * This function initialzes the sie of the CY7C67200/300 controller  * to a usb host mode. This can be done by calling the BIOS SW INT * * Input:  cy_priv = private data  *         port: 0 = SIE 0 port A *               1 = SIE 0 port B *               2 = SIE 1 port A *               3 = SIE 1 port B *        * Return: none *                 ***********************************************************************/static void hc_host_usb_init (cy_priv_t * cy_priv, int sie_num){    lcd_int_data_t int_data;	int i;	cy_dbg("enter hcd_host_usb_init - sie_num = 0x%x", sie_num);	/* setup the lcd interrupt data structure */	for (i=0; i<=14; i++) {	   	int_data.reg[i] = 0;	}	/* Determine which SIE */    if (sie_num == 0)	{	    int_data.int_num = HUSB_SIE1_INIT_INT;	}	else	{	    int_data.int_num = HUSB_SIE2_INIT_INT;	}   	/* Issue LCP command */    lcd_exec_interrupt (&int_data, hc_host_usb_init2, sie_num, cy_priv);	HWTrace(0x9101);}static void hc_host_usb_init2 (unsigned short response,                               unsigned short value,                               int sie_num,                               cy_priv_t * cy_priv){	unsigned short intStat;	cy_dbg("enter hcd_host_usb_init2 - sie_num = 0x%x", sie_num);	HWTrace(0x9102);	if (sie_num == 0) {		lcd_read_reg (SIE1_INT_EN_REG, &intStat, cy_priv);		intStat |= B_WAKE_IRQ_EN | A_WAKE_IRQ_EN | B_CHG_IRQ_EN | A_CHG_IRQ_EN | VBUS_IRQ_EN | ID_IRQ_EN;		lcd_write_reg(SIE1_INT_EN_REG, intStat, cy_priv);	} else {		lcd_read_reg (SIE2_INT_EN_REG, &intStat, cy_priv);		intStat |= B_WAKE_IRQ_EN | A_WAKE_IRQ_EN | B_CHG_IRQ_EN | A_CHG_IRQ_EN;		lcd_write_reg(SIE2_INT_EN_REG, intStat, cy_priv);	}}/************************************************************************ * Function Name : hcd_host_start_timer *   * This function starts the appropriate PORT timer. * Input:  port_num = Port number to reset *         time = time to wait before reset in 10ms increments * * Return: N/A *                 ***********************************************************************/static void hc_host_start_timer(int port_num, int time){    if( time < 0 )    {        return;    }	switch(port_num)	{	case PORT0:	    if( timer_pending(&PORT0_timer) )	    {	        cy_err("ERROR: PORT0 timer in use - restarting");	    }	    PORT0_timer.data = (unsigned long) port_num;	    mod_timer( &PORT0_timer, jiffies + time );	break;	case PORT1:	    if( timer_pending(&PORT1_timer) )	    {	        cy_err("ERROR: PORT1 timer in use - restarting");	    }	    PORT1_timer.data = (unsigned long) port_num;	    mod_timer( &PORT1_timer, jiffies + time );	break;	case PORT2:	    if( timer_pending(&PORT2_timer) )	    {	        cy_err("ERROR: PORT2 timer in use - restarting");	    }	    PORT2_timer.data = (unsigned long) port_num;	    mod_timer( &PORT2_timer, jiffies + time );	break;	case PORT3:	    if( timer_pending(&PORT3_timer) )	    {	        cy_err("ERROR: PORT3 timer in use - restarting");	    }	    PORT3_timer.data = (unsigned long) port_num;	    mod_timer( &PORT3_timer, jiffies + time );	break;	}}/************************************************************************ * Function Name : hcd_host_usb_reset *   * This function resets the usb_port of CY7C67200/300 controller and  * detects the speed of	the connecting device				   * * Input:  cy_priv = data structure for the host controller *         port_num: 0 = SIE 0 port A *                   1 = SIE 0 port B *                   2 = SIE 1 port A *                   3 = SIE 1 port B *        * Return: N/A *                 ***********************************************************************/static void hc_host_usb_reset (cy_priv_t * cy_priv, int port_num){	hci_t *hci = (hci_t *) cy_priv->hci;	otg_t * otg = cy_priv->otg;	int i;    unsigned short usb_ctl_val;    cy_dbg("enter hcd_host_usb_reset - port_num = 0x%x", port_num);    if( reset_in_progress[port_num] != 0 )    {		cy_err("hc_host_usb_reset: reset in-progress\n");        return;    }    else    {        reset_in_progress[port_num] = 1;    }	/* Check if OTG Device */    if( otg != NULL )    {		/* Check if A-Device */        if( otg->id == A_DEV )        {			/* Delay for over 100 ms */			for (i=0; i<105; i++) {	            mdelay(1);				/* Watch for possible disconnect */					    lcd_read_reg(USB1_CTL_REG, &usb_ctl_val, cy_priv);				if (!(usb_ctl_val & (A_DP_STAT | A_DM_STAT))) {					/* Device disconnected, cancel reset */			        reset_in_progress[port_num] = 0;                    otg->b_conn = FALSE;					return;				}			}        }		/* Reset the board */		hc_host_usb_reset1((unsigned long) port_num);    }    else    {		/* Start timer - call hc_host_usb_reset1 when timeout */		hc_host_start_timer(port_num, DFLT_USB_RESET_TIME);    }}void hc_host_usb_reset1 (unsigned long port_num){	cy_priv_t * cy_priv = g_cy_priv;	hci_t *hci = (hci_t *) cy_priv->hci;    hcipriv_t *hp = &hci->port[port_num];	lcd_int_data_t int_data;	otg_t * otg = cy_priv->otg;	int i;    unsigned short usb_ctl_val;    cy_dbg("enter hcd_host_usb_reset1 - port_num = 0x%x", port_num);	HWTrace (0x3200);		if (cy_priv == NULL)	{		cy_err("hc_host_usb_reset: cy_priv = NULL\n");		return;	}	if (hci == NULL)	{		cy_err("hc_host_usb_reset: hci = NULL\n");		return;	}	if (hp == NULL)	{		cy_err("hc_host_usb_reset: hp = NULL\n");		return;	}		/* Enable the port to become a host */	hp->host_disabled = 0;		/* Set the SIE as a host */    	HWTrace (0x3209);    HWData (port_num);    int_data.int_num = HUSB_RESET_INT;	int_data.reg[0] = 60;		 // Reset USB port for 50 ms   	int_data.reg[1] = port_num;	 // set the port number   	for (i=2; i<=14; i++) {	   	int_data.reg[i] = 0;	}    lcd_exec_interrupt (&int_data, hc_host_usb_reset2, port_num, cy_priv);    }static void hc_host_usb_reset2 (unsigned short response,                                unsigned short value,                                int port_num,                                cy_priv_t * cy_priv){	hci_t *hci = (hci_t *) cy_priv->hci;    hcipriv_t *hp = &hci->port[port_num];    sie_t * sie = &hci->sie[port_num/2];	volatile unsigned short intStat = 0;    unsigned short usb_ctl_val;	otg_t * otg = cy_priv->otg;	unsigned short ctl_reg = 0;	unsigned short no_device = 0;	unsigned short fs_device = 0;    cy_dbg("enter hcd_host_usb_reset2 - port_num = 0x%x", port_num);	if (cy_priv == NULL)	{		cy_err("hc_host_usb_reset2: cy_priv = NULL\n");		return;	}	if (hci == NULL)	{		cy_err("hc_host_usb_reset2: hci = NULL\n");		return;	}	if (hp == NULL)	{		cy_err("hc_host_usb_reset2: hp = NULL\n");		return;	}    HWTrace (0x3201);	switch(port_num)	{	case PORT0:		ctl_reg = USB1_CTL_REG;		no_device = (A_DP_STAT | A_DM_STAT);		fs_device = A_DP_STAT;	break;	case PORT1:		ctl_reg = USB1_CTL_REG;		no_device = (B_DP_STAT | B_DM_STAT);		fs_device = B_DP_STAT;	break;	case PORT2:		ctl_reg = USB2_CTL_REG;		no_device = (A_DP_STAT | A_DM_STAT);		fs_device = A_DP_STAT;	break;	case PORT3:		ctl_reg = USB2_CTL_REG;		no_device = (B_DP_STAT | B_DM_STAT);		fs_device = B_DP_STAT;	break;	}    /* Check if a device has been connected */    lcd_read_reg(ctl_reg, &usb_ctl_val, cy_priv);    if (!(usb_ctl_val & no_device))    {	    cy_dbg("hcd_host_usb_reset2: no device");        HWTrace (0x3202);        /* no device connected */		HWTrace(0x6000);        hp->RHportStatus->portStatus &= ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT);        		if (otg != NULL)        {			otg->b_conn = FALSE;			otg->a_conn = FALSE;            //cy_err("ACON1:0");        }    }    else    {		HWTrace (0x3203);        /* check for low speed or full speed by reading D+ and D- lines */            if (usb_ctl_val & fs_device)        {		    cy_dbg("hcd_host_usb_reset2: full speed device");			HWTrace (0x3204);            /* full speed */			/* configure to be a host */			if (otg != NULL)			{                if(otg->id == A_DEV)                {                    otg->b_conn = TRUE;                }                else                {                    otg->a_conn = TRUE;                    //cy_err("ACON2:1");                }			}			HWTrace(0x6001);            hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT);            hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT;         }        else        {			HWTrace (0x3205);		    cy_dbg("hcd_host_usb_reset2: low speed device");            			/* configure to be a host */			if (otg != NULL)			{                if(otg->id == A_DEV)                {                    otg->b_conn = TRUE;                }                else                {                    otg->a_conn = TRUE;                    //cy_err("ACON3:1");                }			}			HWTrace(0x6002);            hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT                 | PORT_LOW_SPEED_DEV_ATTACH_STAT);		}	}    update_otg_state(otg);	/* Enable the HPI SOF interrupt */	lcd_read_reg(HPI_SIE_IE, (unsigned short *) &intStat, cy_priv);	if ((port_num == PORT0) || (port_num == PORT1))	{		intStat |= SOFEOP1_TO_HPI_EN;	}	else	{		intStat |= SOFEOP2_TO_HPI_EN | SOFEOP2_TO_CPU_EN |		           RESUME2_TO_HPI_EN | SIE2MSG_TO_HPI_EN;	}	lcd_write_reg(HPI_SIE_IE, intStat, cy_priv);	/* Set HUSB_pEOT time */	lcd_write_reg(HUSB_pEOT, 4800, cy_priv);	sie->td_active = FALSE;    /* Indicate change in connection status */	set_port_change(hci, PORT_CONNECT_CHANGE, port_num);    reset_in_progress[port_num] = 0;}void hc_flush_data_cache (hci_t * hci, void * data, int len) {}/************************************************************************ * Function Name : hc_parse_td *   * This function checks the status of the transmitted or received packet * and copy the data from the CY7C67200_300 register into a buffer. * * 1) Check the status of the packet  * * 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  ***********************************************************************/int hc_parse_td (cy_priv_t * cy_priv, td_t * td, int td_addr, int * actbytes,                                int *cc, int * activeFlag, int * toggle, int pipetype){	int transfer_status;	char residue;	int pid = td->pid_ep & TD_PIDEPMASK_PID;	int len = td->port_length & TD_PORTLENMASK_DL;	int retryCnt = 0;

⌨️ 快捷键说明

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