summaryrefslogblamecommitdiffstats
path: root/drivers/staging/ath6kl/miscdrv/ar3kconfig.c
blob: e0ea2183019dd9a0d03d67791f49bbcd3a50e4f8 (plain) (tree)

























                                                                                





















                                                
                                                   
 
                                                           
                                           

                                                       
                                       
                      


           
                                                                                




                                 
                                                           







                                                                           
                                                                     
        
                    

                          
                       




                  
                                                         
                                         

                                                       
                      
                                           


                 
                                                                               





                                                                             
                                                          





                                                                                        
                     




                                             
                    

                              
                               




                  
                                                                       
                                                           
                                                                      

                                                               
 
                      

                       
                       
                                      
                      






                                                                              
                                         










                                                                                            
                                                                                      



                                                                       
                     







                                                                                                       
                     








                                                                                     
                                       





















                                                                                                  
                    

                          
                           




                      
                                                                 
 
                      
                                                     
                

                             



                                                              
                                                         

                                                         





                                                                                    
                         
























                                                                                              
                         








                                                                                                       
                    

                                
                                 




                  
                                                            
 
                
                                                                               
                                                            

                             






                                                                      
                  














                                                                                             
                                 




                                                                
                                                                      
 
                   


                                           






                                                                        
                 



                                                                             
                             




                  
                                                           
 
                
                                                             
                                              






                                                                                                                                  
                                                






                                                                                                                                  
                                             

                                                               
                                               

                                                           
                                       
                                    

                             

                                    

                                                           













                                                                                        
                                 
     
                 











                                                                            
                                 
     
                 











                                                                               
                                 
     
                 











                                                                            
                                 
     
                 











                                                                               
                                 
     
                 







                                                                                                  
                                                   
 
                          










                                                                                                          
                                                                             
                     




                                                                   
                         





                                                    
                                            








                                                                                          
                         














                                                                                
                                                                            
                     



                      
                    






                                                                                                    
                                   
 
                          
                                                                         










                                                                                                          
                                                                             
                     





                                                                                          
                         




                                            
                                                                            
                     



                      
                    






                                                                                              
//------------------------------------------------------------------------------
// Copyright (c) 2009-2010 Atheros Corporation.  All rights reserved.
// 
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//
//------------------------------------------------------------------------------
//==============================================================================
// AR3K configuration implementation
//
// Author(s): ="Atheros"
//==============================================================================

#include "a_config.h"
#include "athdefs.h"
#include "a_osapi.h"
#define ATH_MODULE_NAME misc
#include "a_debug.h"
#include "common_drv.h"
#ifdef EXPORT_HCI_BRIDGE_INTERFACE
#include "export_hci_transport.h"
#else
#include "hci_transport_api.h"
#endif
#include "ar3kconfig.h"
#include "tlpm.h"

#define BAUD_CHANGE_COMMAND_STATUS_OFFSET   5
#define HCI_EVENT_RESP_TIMEOUTMS            3000
#define HCI_CMD_OPCODE_BYTE_LOW_OFFSET      0
#define HCI_CMD_OPCODE_BYTE_HI_OFFSET       1
#define HCI_EVENT_OPCODE_BYTE_LOW           3
#define HCI_EVENT_OPCODE_BYTE_HI            4
#define HCI_CMD_COMPLETE_EVENT_CODE         0xE
#define HCI_MAX_EVT_RECV_LENGTH             257
#define EXIT_MIN_BOOT_COMMAND_STATUS_OFFSET  5

int AthPSInitialize(struct ar3k_config_info *hdev);

static int SendHCICommand(struct ar3k_config_info *pConfig,
                               u8 *pBuffer,
                               int              Length)
{
    struct htc_packet  *pPacket = NULL;
    int    status = 0;
       
    do {   
        
        pPacket = (struct htc_packet *)A_MALLOC(sizeof(struct htc_packet));     
        if (NULL == pPacket) {
            status = A_NO_MEMORY;
            break;    
        }       
        
        A_MEMZERO(pPacket,sizeof(struct htc_packet));      
        SET_HTC_PACKET_INFO_TX(pPacket,
                               NULL,
                               pBuffer, 
                               Length,
                               HCI_COMMAND_TYPE, 
                               AR6K_CONTROL_PKT_TAG);
        
            /* issue synchronously */                                      
        status = HCI_TransportSendPkt(pConfig->pHCIDev,pPacket,true);
        
    } while (false);
   
    if (pPacket != NULL) {
        kfree(pPacket);
    }
        
    return status;
}

static int RecvHCIEvent(struct ar3k_config_info *pConfig,
                             u8 *pBuffer,
                             int              *pLength)
{
    int    status = 0;
    struct htc_packet  *pRecvPacket = NULL;
    
    do {
                 
        pRecvPacket = (struct htc_packet *)A_MALLOC(sizeof(struct htc_packet));
        if (NULL == pRecvPacket) {
            status = A_NO_MEMORY;
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to alloc HTC struct \n"));
            break;    
        }     
        
        A_MEMZERO(pRecvPacket,sizeof(struct htc_packet)); 
         
        SET_HTC_PACKET_INFO_RX_REFILL(pRecvPacket,NULL,pBuffer,*pLength,HCI_EVENT_TYPE);
        
        status = HCI_TransportRecvHCIEventSync(pConfig->pHCIDev,
                                               pRecvPacket,
                                               HCI_EVENT_RESP_TIMEOUTMS);
        if (status) {
            break;    
        }

        *pLength = pRecvPacket->ActualLength;
        
    } while (false);
       
    if (pRecvPacket != NULL) {
        kfree(pRecvPacket);    
    }
    
    return status;
} 
    
int SendHCICommandWaitCommandComplete(struct ar3k_config_info *pConfig,
                                           u8 *pHCICommand,
                                           int              CmdLength,
                                           u8 **ppEventBuffer,
                                           u8 **ppBufferToFree)
{
    int    status = 0;
    u8 *pBuffer = NULL;
    u8 *pTemp;
    int         length;
    bool      commandComplete = false;
    u8 opCodeBytes[2];
                               
    do {
        
        length = max(HCI_MAX_EVT_RECV_LENGTH,CmdLength);
        length += pConfig->pHCIProps->HeadRoom + pConfig->pHCIProps->TailRoom;
        length += pConfig->pHCIProps->IOBlockPad;
                                     
        pBuffer = (u8 *)A_MALLOC(length);
        if (NULL == pBuffer) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: Failed to allocate bt buffer \n"));
            status = A_NO_MEMORY;
            break;    
        }
        
            /* get the opcodes to check the command complete event */
        opCodeBytes[0] = pHCICommand[HCI_CMD_OPCODE_BYTE_LOW_OFFSET];
        opCodeBytes[1] = pHCICommand[HCI_CMD_OPCODE_BYTE_HI_OFFSET];
        
            /* copy HCI command */
        memcpy(pBuffer + pConfig->pHCIProps->HeadRoom,pHCICommand,CmdLength);         
            /* send command */
        status = SendHCICommand(pConfig,
                                pBuffer + pConfig->pHCIProps->HeadRoom,
                                CmdLength);
        if (status) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: Failed to send HCI Command (%d) \n", status));
            AR_DEBUG_PRINTBUF(pHCICommand,CmdLength,"HCI Bridge Failed HCI Command");
            break;    
        }   
        
            /* reuse buffer to capture command complete event */
        A_MEMZERO(pBuffer,length);
        status = RecvHCIEvent(pConfig,pBuffer,&length);        
        if (status) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: HCI event recv failed \n"));
            AR_DEBUG_PRINTBUF(pHCICommand,CmdLength,"HCI Bridge Failed HCI Command");
            break;    
        }
        
        pTemp = pBuffer + pConfig->pHCIProps->HeadRoom;        
        if (pTemp[0] == HCI_CMD_COMPLETE_EVENT_CODE) {
            if ((pTemp[HCI_EVENT_OPCODE_BYTE_LOW] == opCodeBytes[0]) &&
                (pTemp[HCI_EVENT_OPCODE_BYTE_HI] == opCodeBytes[1])) {
                commandComplete = true;
            }
        }
        
        if (!commandComplete) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: Unexpected HCI event : %d \n",pTemp[0]));
            AR_DEBUG_PRINTBUF(pTemp,pTemp[1],"Unexpected HCI event");
            status = A_ECOMM;
            break;    
        }       
        
        if (ppEventBuffer != NULL) {
                /* caller wants to look at the event */
            *ppEventBuffer = pTemp;
            if (ppBufferToFree == NULL) {
                status = A_EINVAL;
                break;        
            }
                /* caller must free the buffer */
            *ppBufferToFree = pBuffer;
            pBuffer = NULL;            
        }
        
    } while (false);

    if (pBuffer != NULL) {
        kfree(pBuffer);    
    }
    
    return status;    
}

static int AR3KConfigureHCIBaud(struct ar3k_config_info *pConfig)
{
    int    status = 0;
    u8 hciBaudChangeCommand[] =  {0x0c,0xfc,0x2,0,0};
    u16 baudVal;
    u8 *pEvent = NULL;
    u8 *pBufferToFree = NULL;
    
    do {
        
        if (pConfig->Flags & AR3K_CONFIG_FLAG_SET_AR3K_BAUD) {
            baudVal = (u16)(pConfig->AR3KBaudRate / 100);
            hciBaudChangeCommand[3] = (u8)baudVal;
            hciBaudChangeCommand[4] = (u8)(baudVal >> 8);
            
            status = SendHCICommandWaitCommandComplete(pConfig,
                                                       hciBaudChangeCommand,
                                                       sizeof(hciBaudChangeCommand),
                                                       &pEvent,
                                                       &pBufferToFree);          
            if (status) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: Baud rate change failed! \n"));  
                break;    
            }
            
            if (pEvent[BAUD_CHANGE_COMMAND_STATUS_OFFSET] != 0) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("AR3K Config: Baud change command event status failed: %d \n", 
                                pEvent[BAUD_CHANGE_COMMAND_STATUS_OFFSET]));
                status = A_ECOMM; 
                break;           
            } 
            
            AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
                    ("AR3K Config: Baud Changed to %d \n",pConfig->AR3KBaudRate));  
        }
        
        if (pConfig->Flags & AR3K_CONFIG_FLAG_AR3K_BAUD_CHANGE_DELAY) {
                /* some versions of AR3K do not switch baud immediately, up to 300MS */
            A_MDELAY(325);
        }
        
        if (pConfig->Flags & AR3K_CONFIG_FLAG_SET_AR6K_SCALE_STEP) {
            /* Tell target to change UART baud rate for AR6K */
            status = HCI_TransportSetBaudRate(pConfig->pHCIDev, pConfig->AR3KBaudRate);

            if (status) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("AR3K Config: failed to set scale and step values: %d \n", status));
                break;    
            }
    
            AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
                    ("AR3K Config: Baud changed to %d for AR6K\n", pConfig->AR3KBaudRate));            
        }
                
    } while (false);
                        
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
        
    return status;
}

static int AR3KExitMinBoot(struct ar3k_config_info *pConfig)
{
    int  status;
    char exitMinBootCmd[] = {0x25,0xFC,0x0c,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
                                  0x00,0x00,0x00,0x00,0x00};
    u8 *pEvent = NULL;
    u8 *pBufferToFree = NULL;
    
    status = SendHCICommandWaitCommandComplete(pConfig,
                                               exitMinBootCmd,
                                               sizeof(exitMinBootCmd),
                                               &pEvent,
                                               &pBufferToFree);
    
    if (!status) {
        if (pEvent[EXIT_MIN_BOOT_COMMAND_STATUS_OFFSET] != 0) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                ("AR3K Config: MinBoot exit command event status failed: %d \n", 
                            pEvent[EXIT_MIN_BOOT_COMMAND_STATUS_OFFSET]));
            status = A_ECOMM;            
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_INFO, 
                                ("AR3K Config: MinBoot Exit Command Complete (Success) \n"));
            A_MDELAY(1);
        }
    } else {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: MinBoot Exit Failed! \n"));    
    }
    
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    
    return status;                                              
}
                                 
static int AR3KConfigureSendHCIReset(struct ar3k_config_info *pConfig)
{
    int status = 0;
    u8 hciResetCommand[] = {0x03,0x0c,0x0};
    u8 *pEvent = NULL;
    u8 *pBufferToFree = NULL;

    status = SendHCICommandWaitCommandComplete( pConfig,
                                                hciResetCommand,
                                                sizeof(hciResetCommand),
                                                &pEvent,
                                                &pBufferToFree );

    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: HCI reset failed! \n"));
    }

    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);
    }

    return status;
}

static int AR3KEnableTLPM(struct ar3k_config_info *pConfig)
{
    int  status;
    /* AR3K vendor specific command for Host Wakeup Config */
    char hostWakeupConfig[] = {0x31,0xFC,0x18,
                                    0x02,0x00,0x00,0x00,
                                    0x01,0x00,0x00,0x00,
                                    TLPM_DEFAULT_IDLE_TIMEOUT_LSB,TLPM_DEFAULT_IDLE_TIMEOUT_MSB,0x00,0x00,    //idle timeout in ms
                                    0x00,0x00,0x00,0x00,
                                    TLPM_DEFAULT_WAKEUP_TIMEOUT_MS,0x00,0x00,0x00,    //wakeup timeout in ms
                                    0x00,0x00,0x00,0x00};
    /* AR3K vendor specific command for Target Wakeup Config */
    char targetWakeupConfig[] = {0x31,0xFC,0x18,
                                      0x04,0x00,0x00,0x00,
                                      0x01,0x00,0x00,0x00,
                                      TLPM_DEFAULT_IDLE_TIMEOUT_LSB,TLPM_DEFAULT_IDLE_TIMEOUT_MSB,0x00,0x00,  //idle timeout in ms
                                      0x00,0x00,0x00,0x00,
                                      TLPM_DEFAULT_WAKEUP_TIMEOUT_MS,0x00,0x00,0x00,  //wakeup timeout in ms
                                      0x00,0x00,0x00,0x00};
    /* AR3K vendor specific command for Host Wakeup Enable */
    char hostWakeupEnable[] = {0x31,0xFC,0x4,
                                    0x01,0x00,0x00,0x00};
    /* AR3K vendor specific command for Target Wakeup Enable */
    char targetWakeupEnable[] = {0x31,0xFC,0x4,
                                      0x06,0x00,0x00,0x00};
    /* AR3K vendor specific command for Sleep Enable */
    char sleepEnable[] = {0x4,0xFC,0x1,
                               0x1};
    u8 *pEvent = NULL;
    u8 *pBufferToFree = NULL;
    
    if (0 != pConfig->IdleTimeout) {
        u8 idle_lsb = pConfig->IdleTimeout & 0xFF;
        u8 idle_msb = (pConfig->IdleTimeout & 0xFF00) >> 8;
        hostWakeupConfig[11] = targetWakeupConfig[11] = idle_lsb;
        hostWakeupConfig[12] = targetWakeupConfig[12] = idle_msb;
    }

    if (0 != pConfig->WakeupTimeout) {
        hostWakeupConfig[19] = targetWakeupConfig[19] = (pConfig->WakeupTimeout & 0xFF);
    }

    status = SendHCICommandWaitCommandComplete(pConfig,
                                               hostWakeupConfig,
                                               sizeof(hostWakeupConfig),
                                               &pEvent,
                                               &pBufferToFree);
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HostWakeup Config Failed! \n"));    
        return status;
    }
    
    pEvent = NULL;
    pBufferToFree = NULL;
    status = SendHCICommandWaitCommandComplete(pConfig,
                                               targetWakeupConfig,
                                               sizeof(targetWakeupConfig),
                                               &pEvent,
                                               &pBufferToFree);
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Target Wakeup Config Failed! \n"));    
        return status;
    }

    pEvent = NULL;
    pBufferToFree = NULL;
    status = SendHCICommandWaitCommandComplete(pConfig,
                                               hostWakeupEnable,
                                               sizeof(hostWakeupEnable),
                                               &pEvent,
                                               &pBufferToFree);
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HostWakeup Enable Failed! \n"));    
        return status;
    }

    pEvent = NULL;
    pBufferToFree = NULL;
    status = SendHCICommandWaitCommandComplete(pConfig,
                                               targetWakeupEnable,
                                               sizeof(targetWakeupEnable),
                                               &pEvent,
                                               &pBufferToFree);
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Target Wakeup Enable Failed! \n"));    
        return status;
    }

    pEvent = NULL;
    pBufferToFree = NULL;
    status = SendHCICommandWaitCommandComplete(pConfig,
                                               sleepEnable,
                                               sizeof(sleepEnable),
                                               &pEvent,
                                               &pBufferToFree);
    if (pBufferToFree != NULL) {
        kfree(pBufferToFree);    
    }
    if (status) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Sleep Enable Failed! \n"));    
    }
    
    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: Enable TLPM Completed (status = %d) \n",status));

    return status;                                              
}

int AR3KConfigure(struct ar3k_config_info *pConfig)
{
    int        status = 0;
        
    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Configuring AR3K ...\n"));
                                
    do {
        
        if ((pConfig->pHCIDev == NULL) || (pConfig->pHCIProps == NULL) || (pConfig->pHIFDevice == NULL)) {
            status = A_EINVAL;
            break;    
        }
        
            /* disable asynchronous recv while we issue commands and receive events synchronously */
        status = HCI_TransportEnableDisableAsyncRecv(pConfig->pHCIDev,false);
        if (status) {
            break;    
        }
      
        if (pConfig->Flags & AR3K_CONFIG_FLAG_FORCE_MINBOOT_EXIT) {
            status =  AR3KExitMinBoot(pConfig);   
            if (status) {
                break;    
            }    
        }
        
       
        /* Load patching and PST file if available*/
        if (0 != AthPSInitialize(pConfig)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Patch Download Failed!\n"));
        }

        /* Send HCI reset to make PS tags take effect*/
        AR3KConfigureSendHCIReset(pConfig);

 	if (pConfig->Flags & 
                (AR3K_CONFIG_FLAG_SET_AR3K_BAUD | AR3K_CONFIG_FLAG_SET_AR6K_SCALE_STEP)) {
            status = AR3KConfigureHCIBaud(pConfig);      
            if (status) {
                break;    
            }
        }     



        if (pConfig->PwrMgmtEnabled) {
            /* the delay is required after the previous HCI reset before further
             * HCI commands can be issued
             */
            A_MDELAY(200);
            AR3KEnableTLPM(pConfig);
        }
               
           /* re-enable asynchronous recv */
        status = HCI_TransportEnableDisableAsyncRecv(pConfig->pHCIDev,true);
        if (status) {
            break;    
        }     
    
    
    } while (false);
    
  
    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Configuration Complete (status = %d) \n",status));
    
    return status;
}

int AR3KConfigureExit(void *config)
{
    int        status = 0;
    struct ar3k_config_info *pConfig = (struct ar3k_config_info *)config;
        
    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Cleaning up AR3K ...\n"));
                                
    do {
        
        if ((pConfig->pHCIDev == NULL) || (pConfig->pHCIProps == NULL) || (pConfig->pHIFDevice == NULL)) {
            status = A_EINVAL;
            break;    
        }
        
            /* disable asynchronous recv while we issue commands and receive events synchronously */
        status = HCI_TransportEnableDisableAsyncRecv(pConfig->pHCIDev,false);
        if (status) {
            break;    
        }
      
        if (pConfig->Flags & 
                (AR3K_CONFIG_FLAG_SET_AR3K_BAUD | AR3K_CONFIG_FLAG_SET_AR6K_SCALE_STEP)) {
            status = AR3KConfigureHCIBaud(pConfig);      
            if (status) {
                break;    
            }
        }

           /* re-enable asynchronous recv */
        status = HCI_TransportEnableDisableAsyncRecv(pConfig->pHCIDev,true);
        if (status) {
            break;    
        }     
    
    
    } while (false);
    
  
    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Cleanup Complete (status = %d) \n",status));
    
    return status;
}