?? pehci.c
字號:
/******************************************************************************************************
* NXP ISP176x Host Controller Interface code file
*
* (c) 2006 NXP B.V., All rights reserved. <usb.linux@nxp.com>
*
* 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
*
* File Name: pehci.c
*
* Refering linux kernel version 2.6.9
*
* History:
*
* Date Author Comments
* ------------------------------------------------------------------------------------------------------
* June 21, 2005 krishan Initial version
* July 4, 2005 krishan Handling unprotected urbs
* Feb 6, 2006 Grant H. Added ISO support
* April 21, 2006 Grant H. Added bug fixes
* May 02 2007 Prabhakar ported to 2.6.20 with backward compatibility with 2.6.9
*********************************************************************************************************
*/
#ifdef LINUX_269
#include <linux/config.h>
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/usb.h>
#include <linux/version.h>
#include <stdarg.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include "../hal/hal_intf.h"
#include "../hal/isp1761.h"
#include "pehci.h"
#define EHCI_TUNE_CERR 3
#define URB_NO_INTERRUPT 0x0080
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_RL_HS 0
#define EHCI_TUNE_MULT_HS 1
#ifdef CONFIG_ISO_SUPPORT
#define FALSE 0
#define TRUE (!FALSE)
extern void * phcd_iso_itd_to_ptd( phci_hcd *hcd,
struct ehci_itd *itd,
struct urb *urb,
void * ptd
);
extern unsigned long phcd_submit_iso( phci_hcd *hcd,
#ifdef LINUX_269
#else
struct usb_host_endpoint *ep,
#endif
struct urb *urb,
unsigned long *status
);
#endif /* CONFIG_ISO_SUPPORT */
#ifdef OTG
#include "otg.c"
#endif
/*Enable all other interrupt.*/
#ifdef MSEC_INT_BASED
#define INTR_ENABLE_MASK (HC_MSEC_INT)
#else
#define INTR_ENABLE_MASK (/*HC_MSEC_INT |*/ HC_INTL_INT | HC_ATL_INT | HC_EOT_INT /*| HC_ISO_INT*/)
#endif
/*---------------------------------------------------
* Globals for EHCI
-----------------------------------------------------*/
/* used when updating hcd data */
static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
static const char hcd_name [] = "ISP1761HCD";
static td_ptd_map_buff_t td_ptd_map_buff[TD_PTD_TOTAL_BUFF_TYPES]; /* td-ptd map buffer for all 1362 buffers */
static __u8 td_ptd_pipe_x_buff_type[TD_PTD_TOTAL_BUFF_TYPES] = {
TD_PTD_BUFF_TYPE_ATL,
TD_PTD_BUFF_TYPE_INTL,
TD_PTD_BUFF_TYPE_ISTL
};
/*global memory blocks*/
isp1761_mem_addr_t memalloc[BLK_TOTAL];
#include "mem.c"
#include "qtdptd.c"
#ifdef CONFIG_ISO_SUPPORT
#include "itdptd.c"
#endif /* CONFIG_ISO_SUPPORT */
static int
pehci_rh_control (struct usb_hcd *usb_hcd,u16 typeReq,
u16 wValue,u16 wIndex,char *buf,u16 wLength);
/*----------------------------------------------------*/
static void
pehci_complete_device_removal(phci_hcd *hcd,struct ehci_qh *qh)
{
td_ptd_map_t *td_ptd_map;
td_ptd_map_buff_t *td_ptd_buff;
if(qh->type == TD_PTD_BUFF_TYPE_ISTL){
return;
}
td_ptd_buff = &td_ptd_map_buff[qh->type];
td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
/*this flag should only be set when device is going*/
td_ptd_map->state = TD_PTD_REMOVE;
/*if nothing there*/
if(list_empty(&qh->qtd_list)){
if(td_ptd_map->state != TD_PTD_NEW){
phci_hcd_release_td_ptd_index(qh);
}
qha_free(qha_cache, qh);
qh = 0;
return;
}
/*MUST not come down below this*/
err("Never Error: Should not come to this portion of code\n");
return;
}
/*functions looks for the values in register
specified in ptr, if register values masked
with the mask and result is equal to done,
operation is successful else fails with timeout*/
static int
pehci_hcd_handshake (phci_hcd *hcd,u32 ptr,u32 mask,u32 done,int usec)
{
u32 result = 0;
do {
result = isp1761_reg_read32(hcd->dev,ptr,result);
if (result == ~(u32)0) /* card removed */
return -ENODEV;
result &= mask;
if (result == done)
return 0;
udelay (1);
usec--;
} while (usec > 0);
return -ETIMEDOUT;
}
#ifndef MSEC_INT_BASED
/*schedule atl and interrupt tds,
only when we are not running on sof interrupt
*/
static void
pehci_hcd_td_ptd_submit_urb(phci_hcd *hcd,struct ehci_qh *qh,u8 bufftype)
{
unsigned long flags;
struct ehci_qtd *qtd = 0;
struct urb *urb = 0;
struct _isp1761_qha *qha = 0;
u32 location = 0;
u32 skipmap = 0;
u32 buffstatus = 0;
u32 ormask = 0;
u32 intormask = 0;
u32 length = 0;
struct list_head *head;
td_ptd_map_t *td_ptd_map;
td_ptd_map_buff_t *ptd_map_buff;
struct isp1761_mem_addr *mem_addr = 0;
pehci_entry("++ %s: Entered\n",__FUNCTION__);
pehci_print("Buuffer type %d\n",bufftype);
spin_lock_irqsave(&hcd->lock, flags);
ptd_map_buff = &td_ptd_map_buff[bufftype];
#ifdef LINUX_269
qha = hcd->qha;
#else
qha = &hcd->qha;
#endif
switch(bufftype){
case TD_PTD_BUFF_TYPE_ATL:
skipmap = isp1761_reg_read32(hcd->dev, hcd->regs.atltdskipmap, skipmap);
ormask = isp1761_reg_read32(hcd->dev, hcd->regs.atl_irq_mask_or, ormask);
break;
case TD_PTD_BUFF_TYPE_INTL:
skipmap = isp1761_reg_read32(hcd->dev, hcd->regs.inttdskipmap, skipmap);
intormask = isp1761_reg_read32(hcd->dev, hcd->regs.int_irq_mask_or,intormask);
break;
default:
skipmap = isp1761_reg_read32(hcd->dev, hcd->regs.isotdskipmap, skipmap);
break;
}
buffstatus = isp1761_reg_read32(hcd->dev,hcd->regs.buffer_status,buffstatus);
/*header, qtd, and urb of current transfer*/
location = qh->qtd_ptd_index;
td_ptd_map = &ptd_map_buff->map_list[location];
if(!(qh->qh_state & QH_STATE_TAKE_NEXT)){
pehci_check("qh will schdule from interrupt routine,map %x\n",td_ptd_map->ptd_bitmap);
spin_unlock_irqrestore(&hcd->lock, flags);
return;
}
head = &qh->qtd_list;
qtd = list_entry(head->next, struct ehci_qtd, qtd_list);
/*already scheduled??????, may be from interrupt*/
if(!(qtd->state & QTD_STATE_NEW)){
pehci_check("qtd already in, state %x\n",qtd->state);
spin_unlock_irqrestore(&hcd->lock, flags);
return;
}
qtd->state &= ~QTD_STATE_NEW;
qtd->state |= QTD_STATE_SCHEDULED;
qh->qh_state &= ~QH_STATE_TAKE_NEXT;
/*take the first td*/
td_ptd_map->qtd = qtd;
/*take the urb*/
urb = qtd->urb;
ptd_map_buff->active_ptds++;
/*trust the atl worker, at this location there wont be any td*/
/*if this td is the last one*/
if(qtd->state & QTD_STATE_LAST){
qh->hw_current = cpu_to_le32(0);
/*else update the hw_next of qh to the next td*/
}else{
qh->hw_current = qtd->hw_next;
}
memset(qha, 0, sizeof( isp1761_qha));
pehci_check("td being scheduled : length: %d, device: %d, map: %x\n", qtd->length,urb->dev->devnum, td_ptd_map->ptd_bitmap);
/*NEW, now need to get the memory for this transfer*/
length = qtd->length;
mem_addr = &qtd->mem_addr;
phci_hcd_mem_alloc(length,mem_addr,0);
if(length && ((mem_addr->phy_addr == 0 )
|| (mem_addr->virt_addr == 0))){
err("Never Error: Can not allocate memory for the current td,length %d\n",length);
/*should not happen*/
/*can happen only when we exceed the limit of devices we support
MAX 4 mass storage at a time*/
}
phci_hcd_qha_from_qtd(hcd,qtd,qtd->urb, (void *)qha,
td_ptd_map->ptd_ram_data_addr,
qh
/*td_ptd_map->datatoggle*/);
if(qh->type == TD_PTD_BUFF_TYPE_INTL){
phci_hcd_qhint_schedule(hcd, qh,qtd,(isp1761_qhint *)qha,qtd->urb);
}
/*write qha into the header of the host controller*/
isp1761_mem_write(hcd->dev, td_ptd_map->ptd_header_addr,0,(u32 *)(qha),PHCI_QHA_LENGTH,0);
/*if this is SETUP/OUT token , then need to write into the buffer*/
/*length should be valid and supported by the ptd*/
if(qtd->length && (qtd->length <= HC_ATL_PL_SIZE))
switch(PTD_PID(qha->td_info2)){
case OUT_PID:
case SETUP_PID:
#ifdef LINUX_269
isp1761_mem_write(hcd->dev,(u32)mem_addr->phy_addr,0,(le32_to_cpu(qtd->hw_buf[0])),length,0);
#else
isp1761_mem_write(hcd->dev,(u32)mem_addr->phy_addr,0,(void*)(le32_to_cpu(qtd->hw_buf[0])),length,0);
#endif
break;
}
/*unskip the tds at this location*/
switch(bufftype){
case TD_PTD_BUFF_TYPE_ATL:
skipmap &= ~td_ptd_map->ptd_bitmap;
/*enable atl interrupts on donemap*/
ormask |= td_ptd_map->ptd_bitmap;
isp1761_reg_write32(hcd->dev, hcd->regs.atl_irq_mask_or, ormask);
break;
case TD_PTD_BUFF_TYPE_INTL:
skipmap &= ~td_ptd_map->ptd_bitmap;
intormask |= td_ptd_map->ptd_bitmap;
isp1761_reg_write32(hcd->dev, hcd->regs.int_irq_mask_or, intormask);
break;
case TD_PTD_BUFF_TYPE_ISTL:
skipmap &= ~td_ptd_map->ptd_bitmap;
isp1761_reg_write32(hcd->dev, hcd->regs.isotdskipmap, skipmap);
break;
}
/*if any new schedule, enable the atl buffer*/
switch(bufftype){
case TD_PTD_BUFF_TYPE_ATL:
isp1761_reg_write32(hcd->dev, hcd->regs.buffer_status, buffstatus | ATL_BUFFER);
isp1761_reg_write32(hcd->dev, hcd->regs.atltdskipmap, skipmap);
buffstatus |= ATL_BUFFER;
break;
case TD_PTD_BUFF_TYPE_INTL:
isp1761_reg_write32(hcd->dev, hcd->regs.buffer_status, buffstatus | INT_BUFFER);
isp1761_reg_write32(hcd->dev, hcd->regs.inttdskipmap, skipmap);
break;
case TD_PTD_BUFF_TYPE_ISTL:
/*not supposed to be seen here*/
isp1761_reg_write32(hcd->dev, hcd->regs.buffer_status, buffstatus | ISO_BUFFER);
break;
}
spin_unlock_irqrestore(&hcd->lock, flags);
pehci_entry("-- %s: Exit\n",__FUNCTION__);
return;
}
#endif
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -