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 + -
显示快捷键?