2008-12-20 16:57:57 -08:00
/*
* Intel Wireless WiMAX Connection 2400 m
* SDIO RX handling
*
*
* Copyright ( C ) 2007 - 2008 Intel Corporation . All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
*
* Intel Corporation < linux - wimax @ intel . com >
* Dirk Brandewie < dirk . j . brandewie @ intel . com >
* - Initial implementation
*
*
* This handles the RX path on SDIO .
*
* The SDIO bus driver calls the " irq " routine when data is available .
* This is not a traditional interrupt routine since the SDIO bus
* driver calls us from its irq thread context . Because of this
* sleeping in the SDIO RX IRQ routine is okay .
*
* From there on , we obtain the size of the data that is available ,
* allocate an skb , copy it and then pass it to the generic driver ' s
* RX routine [ i2400m_rx ( ) ] .
*
* ROADMAP
*
* i2400ms_irq ( )
* i2400ms_rx ( )
* __i2400ms_rx_get_size ( )
2009-09-03 15:14:29 -07:00
* i2400m_is_boot_barker ( )
2008-12-20 16:57:57 -08:00
* i2400m_rx ( )
*
* i2400ms_rx_setup ( )
*
* i2400ms_rx_release ( )
*/
# include <linux/workqueue.h>
# include <linux/wait.h>
# include <linux/skbuff.h>
# include <linux/mmc/sdio.h>
# include <linux/mmc/sdio_func.h>
# include "i2400m-sdio.h"
# define D_SUBMODULE rx
# include "sdio-debug-levels.h"
2009-05-07 01:02:39 -07:00
static const __le32 i2400m_ACK_BARKER [ 4 ] = {
__constant_cpu_to_le32 ( I2400M_ACK_BARKER ) ,
__constant_cpu_to_le32 ( I2400M_ACK_BARKER ) ,
__constant_cpu_to_le32 ( I2400M_ACK_BARKER ) ,
__constant_cpu_to_le32 ( I2400M_ACK_BARKER )
} ;
2008-12-20 16:57:57 -08:00
/*
* Read and return the amount of bytes available for RX
*
* The RX size has to be read like this : byte reads of three
* sequential locations ; then glue ' em together .
*
* sdio_readl ( ) doesn ' t work .
*/
ssize_t __i2400ms_rx_get_size ( struct i2400ms * i2400ms )
{
int ret , cnt , val ;
ssize_t rx_size ;
unsigned xfer_size_addr ;
struct sdio_func * func = i2400ms - > func ;
struct device * dev = & i2400ms - > func - > dev ;
d_fnstart ( 7 , dev , " (i2400ms %p) \n " , i2400ms ) ;
xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR ;
rx_size = 0 ;
for ( cnt = 0 ; cnt < 3 ; cnt + + ) {
val = sdio_readb ( func , xfer_size_addr + cnt , & ret ) ;
if ( ret < 0 ) {
dev_err ( dev , " RX: Can't read byte %d of RX size from "
" 0x%08x: %d \n " , cnt , xfer_size_addr + cnt , ret ) ;
rx_size = ret ;
goto error_read ;
}
rx_size = rx_size < < 8 | ( val & 0xff ) ;
}
d_printf ( 6 , dev , " RX: rx_size is %ld \n " , ( long ) rx_size ) ;
error_read :
d_fnend ( 7 , dev , " (i2400ms %p) = %ld \n " , i2400ms , ( long ) rx_size ) ;
return rx_size ;
}
/*
* Read data from the device ( when in normal )
*
* Allocate an SKB of the right size , read the data in and then
* deliver it to the generic layer .
*
* We also check for a reboot barker . That means the device died and
* we have to reboot it .
*/
static
void i2400ms_rx ( struct i2400ms * i2400ms )
{
int ret ;
struct sdio_func * func = i2400ms - > func ;
struct device * dev = & func - > dev ;
struct i2400m * i2400m = & i2400ms - > i2400m ;
struct sk_buff * skb ;
ssize_t rx_size ;
d_fnstart ( 7 , dev , " (i2400ms %p) \n " , i2400ms ) ;
rx_size = __i2400ms_rx_get_size ( i2400ms ) ;
if ( rx_size < 0 ) {
ret = rx_size ;
goto error_get_size ;
}
2009-08-10 18:36:15 -07:00
/*
* Hardware quirk : make sure to clear the INTR status register
* AFTER getting the data transfer size .
*/
sdio_writeb ( func , 1 , I2400MS_INTR_CLEAR_ADDR , & ret ) ;
2009-05-07 01:02:39 -07:00
2008-12-20 16:57:57 -08:00
ret = - ENOMEM ;
skb = alloc_skb ( rx_size , GFP_ATOMIC ) ;
if ( NULL = = skb ) {
dev_err ( dev , " RX: unable to alloc skb \n " ) ;
goto error_alloc_skb ;
}
ret = sdio_memcpy_fromio ( func , skb - > data ,
I2400MS_DATA_ADDR , rx_size ) ;
if ( ret < 0 ) {
dev_err ( dev , " RX: SDIO data read failed: %d \n " , ret ) ;
goto error_memcpy_fromio ;
}
2009-05-07 01:02:39 -07:00
rmb ( ) ; /* make sure we get boot_mode from dev_reset_handle */
2009-09-03 15:14:29 -07:00
if ( unlikely ( i2400m - > boot_mode = = 1 ) ) {
2009-05-07 01:02:39 -07:00
spin_lock ( & i2400m - > rx_lock ) ;
i2400ms - > bm_ack_size = rx_size ;
spin_unlock ( & i2400m - > rx_lock ) ;
memcpy ( i2400m - > bm_ack_buf , skb - > data , rx_size ) ;
wake_up ( & i2400ms - > bm_wfa_wq ) ;
2009-09-18 11:50:50 -07:00
d_printf ( 5 , dev , " RX: SDIO boot mode message \n " ) ;
2009-05-07 01:02:39 -07:00
kfree_skb ( skb ) ;
2009-09-03 15:14:29 -07:00
goto out ;
}
ret = - EIO ;
if ( unlikely ( rx_size < sizeof ( __le32 ) ) ) {
dev_err ( dev , " HW BUG? only %zu bytes received \n " , rx_size ) ;
goto error_bad_size ;
}
if ( likely ( i2400m_is_d2h_barker ( skb - > data ) ) ) {
skb_put ( skb , rx_size ) ;
i2400m_rx ( i2400m , skb ) ;
} else if ( unlikely ( i2400m_is_boot_barker ( i2400m ,
skb - > data , rx_size ) ) ) {
2009-09-14 14:05:19 -07:00
ret = i2400m_dev_reset_handle ( i2400m , " device rebooted " ) ;
2009-05-07 01:02:39 -07:00
dev_err ( dev , " RX: SDIO reboot barker \n " ) ;
2008-12-20 16:57:57 -08:00
kfree_skb ( skb ) ;
} else {
2009-09-03 15:14:29 -07:00
i2400m_unknown_barker ( i2400m , skb - > data , rx_size ) ;
kfree_skb ( skb ) ;
2008-12-20 16:57:57 -08:00
}
2009-09-03 15:14:29 -07:00
out :
2008-12-20 16:57:57 -08:00
d_fnend ( 7 , dev , " (i2400ms %p) = void \n " , i2400ms ) ;
return ;
error_memcpy_fromio :
kfree_skb ( skb ) ;
error_alloc_skb :
error_get_size :
2009-09-03 15:14:29 -07:00
error_bad_size :
2008-12-20 16:57:57 -08:00
d_fnend ( 7 , dev , " (i2400ms %p) = %d \n " , i2400ms , ret ) ;
return ;
}
/*
* Process an interrupt from the SDIO card
*
* FIXME : need to process other events that are not just ready - to - read
*
* Checks there is data ready and then proceeds to read it .
*/
static
void i2400ms_irq ( struct sdio_func * func )
{
int ret ;
struct i2400ms * i2400ms = sdio_get_drvdata ( func ) ;
struct device * dev = & func - > dev ;
int val ;
d_fnstart ( 6 , dev , " (i2400ms %p) \n " , i2400ms ) ;
val = sdio_readb ( func , I2400MS_INTR_STATUS_ADDR , & ret ) ;
if ( ret < 0 ) {
dev_err ( dev , " RX: Can't read interrupt status: %d \n " , ret ) ;
goto error_no_irq ;
}
if ( ! val ) {
dev_err ( dev , " RX: BUG? got IRQ but no interrupt ready? \n " ) ;
goto error_no_irq ;
}
2009-05-07 01:02:39 -07:00
i2400ms_rx ( i2400ms ) ;
2008-12-20 16:57:57 -08:00
error_no_irq :
d_fnend ( 6 , dev , " (i2400ms %p) = void \n " , i2400ms ) ;
return ;
}
/*
* Setup SDIO RX
*
* Hooks up the IRQ handler and then enables IRQs .
*/
int i2400ms_rx_setup ( struct i2400ms * i2400ms )
{
int result ;
struct sdio_func * func = i2400ms - > func ;
struct device * dev = & func - > dev ;
2009-05-07 01:02:39 -07:00
struct i2400m * i2400m = & i2400ms - > i2400m ;
2008-12-20 16:57:57 -08:00
d_fnstart ( 5 , dev , " (i2400ms %p) \n " , i2400ms ) ;
2009-05-07 01:02:39 -07:00
init_waitqueue_head ( & i2400ms - > bm_wfa_wq ) ;
spin_lock ( & i2400m - > rx_lock ) ;
i2400ms - > bm_wait_result = - EINPROGRESS ;
2009-08-27 15:25:12 -07:00
/*
* Before we are about to enable the RX interrupt , make sure
* bm_ack_size is cleared to - EINPROGRESS which indicates
* no RX interrupt happened yet or the previous interrupt
* has been handled , we are ready to take the new interrupt
*/
i2400ms - > bm_ack_size = - EINPROGRESS ;
2009-05-07 01:02:39 -07:00
spin_unlock ( & i2400m - > rx_lock ) ;
2008-12-20 16:57:57 -08:00
sdio_claim_host ( func ) ;
result = sdio_claim_irq ( func , i2400ms_irq ) ;
if ( result < 0 ) {
dev_err ( dev , " Cannot claim IRQ: %d \n " , result ) ;
goto error_irq_claim ;
}
result = 0 ;
sdio_writeb ( func , 1 , I2400MS_INTR_ENABLE_ADDR , & result ) ;
if ( result < 0 ) {
sdio_release_irq ( func ) ;
dev_err ( dev , " Failed to enable interrupts %d \n " , result ) ;
}
error_irq_claim :
sdio_release_host ( func ) ;
d_fnend ( 5 , dev , " (i2400ms %p) = %d \n " , i2400ms , result ) ;
return result ;
}
/*
* Tear down SDIO RX
*
* Disables IRQs in the device and removes the IRQ handler .
*/
void i2400ms_rx_release ( struct i2400ms * i2400ms )
{
int result ;
struct sdio_func * func = i2400ms - > func ;
struct device * dev = & func - > dev ;
2009-05-07 01:02:39 -07:00
struct i2400m * i2400m = & i2400ms - > i2400m ;
2008-12-20 16:57:57 -08:00
d_fnstart ( 5 , dev , " (i2400ms %p) \n " , i2400ms ) ;
2009-05-07 01:02:39 -07:00
spin_lock ( & i2400m - > rx_lock ) ;
i2400ms - > bm_ack_size = - EINTR ;
spin_unlock ( & i2400m - > rx_lock ) ;
wake_up_all ( & i2400ms - > bm_wfa_wq ) ;
2008-12-20 16:57:57 -08:00
sdio_claim_host ( func ) ;
sdio_writeb ( func , 0 , I2400MS_INTR_ENABLE_ADDR , & result ) ;
sdio_release_irq ( func ) ;
sdio_release_host ( func ) ;
d_fnend ( 5 , dev , " (i2400ms %p) = %d \n " , i2400ms , result ) ;
}