2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 2001 by David Brownell
2006-08-31 01:50:06 +04:00
*
2005-04-17 02:20:36 +04:00
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/* this file is part of ehci-hcd.c */
/*-------------------------------------------------------------------------*/
/*
* There ' s basically three types of memory :
* - data used only by the HCD . . . kmalloc is fine
* - async and periodic schedules , shared by HC and HCD . . . these
* need to use dma_pool or dma_alloc_coherent
2006-08-31 01:50:06 +04:00
* - driver buffers , read / written by HC . . . single shot DMA mapped
2005-04-17 02:20:36 +04:00
*
2007-05-01 20:29:37 +04:00
* There ' s also " register " data ( e . g . PCI or SOC ) , which is memory mapped .
2005-04-17 02:20:36 +04:00
* No memory seen by this driver is pageable .
*/
/*-------------------------------------------------------------------------*/
/* Allocate the key transfer structures from the previously allocated pool */
2007-05-01 20:29:37 +04:00
static inline void ehci_qtd_init ( struct ehci_hcd * ehci , struct ehci_qtd * qtd ,
dma_addr_t dma )
2005-04-17 02:20:36 +04:00
{
memset ( qtd , 0 , sizeof * qtd ) ;
qtd - > qtd_dma = dma ;
qtd - > hw_token = cpu_to_le32 ( QTD_STS_HALT ) ;
2007-05-01 20:29:37 +04:00
qtd - > hw_next = EHCI_LIST_END ( ehci ) ;
qtd - > hw_alt_next = EHCI_LIST_END ( ehci ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & qtd - > qtd_list ) ;
}
2005-10-21 11:21:58 +04:00
static struct ehci_qtd * ehci_qtd_alloc ( struct ehci_hcd * ehci , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
struct ehci_qtd * qtd ;
dma_addr_t dma ;
qtd = dma_pool_alloc ( ehci - > qtd_pool , flags , & dma ) ;
if ( qtd ! = NULL ) {
2007-05-01 20:29:37 +04:00
ehci_qtd_init ( ehci , qtd , dma ) ;
2005-04-17 02:20:36 +04:00
}
return qtd ;
}
static inline void ehci_qtd_free ( struct ehci_hcd * ehci , struct ehci_qtd * qtd )
{
dma_pool_free ( ehci - > qtd_pool , qtd , qtd - > qtd_dma ) ;
}
2007-05-17 23:21:19 +04:00
static void qh_destroy ( struct ehci_qh * qh )
2005-04-17 02:20:36 +04:00
{
struct ehci_hcd * ehci = qh - > ehci ;
/* clean qtds first, and know this is not linked */
if ( ! list_empty ( & qh - > qtd_list ) | | qh - > qh_next . ptr ) {
ehci_dbg ( ehci , " unused qh not empty! \n " ) ;
BUG ( ) ;
}
if ( qh - > dummy )
ehci_qtd_free ( ehci , qh - > dummy ) ;
2009-07-14 03:23:29 +04:00
dma_pool_free ( ehci - > qh_pool , qh - > hw , qh - > qh_dma ) ;
kfree ( qh ) ;
2005-04-17 02:20:36 +04:00
}
2005-10-21 11:21:58 +04:00
static struct ehci_qh * ehci_qh_alloc ( struct ehci_hcd * ehci , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
struct ehci_qh * qh ;
dma_addr_t dma ;
2009-07-14 03:23:29 +04:00
qh = kzalloc ( sizeof * qh , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! qh )
2009-07-14 03:23:29 +04:00
goto done ;
qh - > hw = ( struct ehci_qh_hw * )
dma_pool_alloc ( ehci - > qh_pool , flags , & dma ) ;
if ( ! qh - > hw )
goto fail ;
memset ( qh - > hw , 0 , sizeof * qh - > hw ) ;
2007-05-17 23:21:19 +04:00
qh - > refcount = 1 ;
2005-04-17 02:20:36 +04:00
qh - > ehci = ehci ;
qh - > qh_dma = dma ;
// INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD ( & qh - > qtd_list ) ;
/* dummy td enables safe urb queuing */
qh - > dummy = ehci_qtd_alloc ( ehci , flags ) ;
if ( qh - > dummy = = NULL ) {
ehci_dbg ( ehci , " no dummy td \n " ) ;
2009-07-14 03:23:29 +04:00
goto fail1 ;
2005-04-17 02:20:36 +04:00
}
2009-07-14 03:23:29 +04:00
done :
2005-04-17 02:20:36 +04:00
return qh ;
2009-07-14 03:23:29 +04:00
fail1 :
dma_pool_free ( ehci - > qh_pool , qh - > hw , qh - > qh_dma ) ;
fail :
kfree ( qh ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
/* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh * qh_get ( struct ehci_qh * qh )
{
2007-05-17 23:21:19 +04:00
WARN_ON ( ! qh - > refcount ) ;
qh - > refcount + + ;
2005-04-17 02:20:36 +04:00
return qh ;
}
static inline void qh_put ( struct ehci_qh * qh )
{
2007-05-17 23:21:19 +04:00
if ( ! - - qh - > refcount )
qh_destroy ( qh ) ;
2005-04-17 02:20:36 +04:00
}
/*-------------------------------------------------------------------------*/
2006-08-31 01:50:06 +04:00
/* The queue heads and transfer descriptors are managed from pools tied
2005-04-17 02:20:36 +04:00
* to each of the " per device " structures .
* This is the initialisation and cleanup code .
*/
static void ehci_mem_cleanup ( struct ehci_hcd * ehci )
{
2010-04-09 00:56:37 +04:00
free_cached_lists ( ehci ) ;
2005-04-17 02:20:36 +04:00
if ( ehci - > async )
qh_put ( ehci - > async ) ;
ehci - > async = NULL ;
/* DMA consistent memory and pools */
if ( ehci - > qtd_pool )
dma_pool_destroy ( ehci - > qtd_pool ) ;
ehci - > qtd_pool = NULL ;
if ( ehci - > qh_pool ) {
dma_pool_destroy ( ehci - > qh_pool ) ;
ehci - > qh_pool = NULL ;
}
if ( ehci - > itd_pool )
dma_pool_destroy ( ehci - > itd_pool ) ;
ehci - > itd_pool = NULL ;
if ( ehci - > sitd_pool )
dma_pool_destroy ( ehci - > sitd_pool ) ;
ehci - > sitd_pool = NULL ;
if ( ehci - > periodic )
dma_free_coherent ( ehci_to_hcd ( ehci ) - > self . controller ,
ehci - > periodic_size * sizeof ( u32 ) ,
ehci - > periodic , ehci - > periodic_dma ) ;
ehci - > periodic = NULL ;
/* shadow periodic table */
2005-04-19 04:39:34 +04:00
kfree ( ehci - > pshadow ) ;
2005-04-17 02:20:36 +04:00
ehci - > pshadow = NULL ;
}
/* remember to add cleanup code (above) if you add anything here */
2005-10-21 11:21:58 +04:00
static int ehci_mem_init ( struct ehci_hcd * ehci , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
int i ;
/* QTDs for control/bulk/intr transfers */
2006-08-31 01:50:06 +04:00
ehci - > qtd_pool = dma_pool_create ( " ehci_qtd " ,
2005-04-17 02:20:36 +04:00
ehci_to_hcd ( ehci ) - > self . controller ,
sizeof ( struct ehci_qtd ) ,
32 /* byte alignment (for hw parts) */ ,
4096 /* can't cross 4K */ ) ;
if ( ! ehci - > qtd_pool ) {
goto fail ;
}
/* QHs for control/bulk/intr transfers */
2006-08-31 01:50:06 +04:00
ehci - > qh_pool = dma_pool_create ( " ehci_qh " ,
2005-04-17 02:20:36 +04:00
ehci_to_hcd ( ehci ) - > self . controller ,
2009-07-14 03:23:29 +04:00
sizeof ( struct ehci_qh_hw ) ,
2005-04-17 02:20:36 +04:00
32 /* byte alignment (for hw parts) */ ,
4096 /* can't cross 4K */ ) ;
if ( ! ehci - > qh_pool ) {
goto fail ;
}
ehci - > async = ehci_qh_alloc ( ehci , flags ) ;
if ( ! ehci - > async ) {
goto fail ;
}
/* ITD for high speed ISO transfers */
2006-08-31 01:50:06 +04:00
ehci - > itd_pool = dma_pool_create ( " ehci_itd " ,
2005-04-17 02:20:36 +04:00
ehci_to_hcd ( ehci ) - > self . controller ,
sizeof ( struct ehci_itd ) ,
32 /* byte alignment (for hw parts) */ ,
4096 /* can't cross 4K */ ) ;
if ( ! ehci - > itd_pool ) {
goto fail ;
}
/* SITD for full/low speed split ISO transfers */
2006-08-31 01:50:06 +04:00
ehci - > sitd_pool = dma_pool_create ( " ehci_sitd " ,
2005-04-17 02:20:36 +04:00
ehci_to_hcd ( ehci ) - > self . controller ,
sizeof ( struct ehci_sitd ) ,
32 /* byte alignment (for hw parts) */ ,
4096 /* can't cross 4K */ ) ;
if ( ! ehci - > sitd_pool ) {
goto fail ;
}
/* Hardware periodic table */
ehci - > periodic = ( __le32 * )
dma_alloc_coherent ( ehci_to_hcd ( ehci ) - > self . controller ,
ehci - > periodic_size * sizeof ( __le32 ) ,
& ehci - > periodic_dma , 0 ) ;
if ( ehci - > periodic = = NULL ) {
goto fail ;
}
for ( i = 0 ; i < ehci - > periodic_size ; i + + )
2007-05-01 20:29:37 +04:00
ehci - > periodic [ i ] = EHCI_LIST_END ( ehci ) ;
2005-04-17 02:20:36 +04:00
/* software shadow of hardware table */
2006-02-27 23:29:43 +03:00
ehci - > pshadow = kcalloc ( ehci - > periodic_size , sizeof ( void * ) , flags ) ;
if ( ehci - > pshadow ! = NULL )
return 0 ;
2005-04-17 02:20:36 +04:00
fail :
ehci_dbg ( ehci , " couldn't init memory \n " ) ;
ehci_mem_cleanup ( ehci ) ;
return - ENOMEM ;
}