📄 hc_sl811.c
字号:
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 + -