]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/ath6kl/miscdrv/ar3kconfig.c
Merge branch 'master' into tk71
[mv-sheeva.git] / drivers / staging / ath6kl / miscdrv / ar3kconfig.c
diff --git a/drivers/staging/ath6kl/miscdrv/ar3kconfig.c b/drivers/staging/ath6kl/miscdrv/ar3kconfig.c
new file mode 100644 (file)
index 0000000..83bc5be
--- /dev/null
@@ -0,0 +1,566 @@
+//------------------------------------------------------------------------------
+// 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_types.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
+
+A_STATUS AthPSInitialize(AR3K_CONFIG_INFO *hdev);
+
+static A_STATUS SendHCICommand(AR3K_CONFIG_INFO *pConfig,
+                               A_UINT8          *pBuffer,
+                               int              Length)
+{
+    HTC_PACKET  *pPacket = NULL;
+    A_STATUS    status = A_OK;
+       
+    do {   
+        
+        pPacket = (HTC_PACKET *)A_MALLOC(sizeof(HTC_PACKET));     
+        if (NULL == pPacket) {
+            status = A_NO_MEMORY;
+            break;    
+        }       
+        
+        A_MEMZERO(pPacket,sizeof(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) {
+        A_FREE(pPacket);
+    }
+        
+    return status;
+}
+
+static A_STATUS RecvHCIEvent(AR3K_CONFIG_INFO *pConfig,
+                             A_UINT8          *pBuffer,
+                             int              *pLength)
+{
+    A_STATUS    status = A_OK; 
+    HTC_PACKET  *pRecvPacket = NULL;
+    
+    do {
+                 
+        pRecvPacket = (HTC_PACKET *)A_MALLOC(sizeof(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(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 (A_FAILED(status)) {
+            break;    
+        }
+
+        *pLength = pRecvPacket->ActualLength;
+        
+    } while (FALSE);
+       
+    if (pRecvPacket != NULL) {
+        A_FREE(pRecvPacket);    
+    }
+    
+    return status;
+} 
+    
+A_STATUS SendHCICommandWaitCommandComplete(AR3K_CONFIG_INFO *pConfig,
+                                           A_UINT8          *pHCICommand,
+                                           int              CmdLength,
+                                           A_UINT8          **ppEventBuffer,
+                                           A_UINT8          **ppBufferToFree)
+{
+    A_STATUS    status = A_OK;   
+    A_UINT8     *pBuffer = NULL;
+    A_UINT8     *pTemp;
+    int         length;
+    A_BOOL      commandComplete = FALSE;
+    A_UINT8     opCodeBytes[2];
+                               
+    do {
+        
+        length = max(HCI_MAX_EVT_RECV_LENGTH,CmdLength);
+        length += pConfig->pHCIProps->HeadRoom + pConfig->pHCIProps->TailRoom;
+        length += pConfig->pHCIProps->IOBlockPad;
+                                     
+        pBuffer = (A_UINT8 *)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 */
+        A_MEMCPY(pBuffer + pConfig->pHCIProps->HeadRoom,pHCICommand,CmdLength);         
+            /* send command */
+        status = SendHCICommand(pConfig,
+                                pBuffer + pConfig->pHCIProps->HeadRoom,
+                                CmdLength);
+        if (A_FAILED(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 (A_FAILED(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) {
+        A_FREE(pBuffer);    
+    }
+    
+    return status;    
+}
+
+static A_STATUS AR3KConfigureHCIBaud(AR3K_CONFIG_INFO *pConfig)
+{
+    A_STATUS    status = A_OK;
+    A_UINT8     hciBaudChangeCommand[] =  {0x0c,0xfc,0x2,0,0};
+    A_UINT16    baudVal; 
+    A_UINT8     *pEvent = NULL;
+    A_UINT8     *pBufferToFree = NULL;
+    
+    do {
+        
+        if (pConfig->Flags & AR3K_CONFIG_FLAG_SET_AR3K_BAUD) {
+            baudVal = (A_UINT16)(pConfig->AR3KBaudRate / 100);
+            hciBaudChangeCommand[3] = (A_UINT8)baudVal;
+            hciBaudChangeCommand[4] = (A_UINT8)(baudVal >> 8);
+            
+            status = SendHCICommandWaitCommandComplete(pConfig,
+                                                       hciBaudChangeCommand,
+                                                       sizeof(hciBaudChangeCommand),
+                                                       &pEvent,
+                                                       &pBufferToFree);          
+            if (A_FAILED(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 (A_FAILED(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) {
+        A_FREE(pBufferToFree);    
+    }
+        
+    return status;
+}
+
+static A_STATUS AR3KExitMinBoot(AR3K_CONFIG_INFO *pConfig)
+{
+    A_STATUS  status;
+    A_CHAR    exitMinBootCmd[] = {0x25,0xFC,0x0c,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
+                                  0x00,0x00,0x00,0x00,0x00};
+    A_UINT8   *pEvent = NULL;
+    A_UINT8   *pBufferToFree = NULL;
+    
+    status = SendHCICommandWaitCommandComplete(pConfig,
+                                               exitMinBootCmd,
+                                               sizeof(exitMinBootCmd),
+                                               &pEvent,
+                                               &pBufferToFree);
+    
+    if (A_SUCCESS(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) {
+        A_FREE(pBufferToFree);    
+    }
+    
+    return status;                                              
+}
+                                 
+static A_STATUS AR3KConfigureSendHCIReset(AR3K_CONFIG_INFO *pConfig)
+{
+    A_STATUS status = A_OK;
+    A_UINT8 hciResetCommand[] = {0x03,0x0c,0x0};
+    A_UINT8 *pEvent = NULL;
+    A_UINT8 *pBufferToFree = NULL;
+
+    status = SendHCICommandWaitCommandComplete( pConfig,
+                                                hciResetCommand,
+                                                sizeof(hciResetCommand),
+                                                &pEvent,
+                                                &pBufferToFree );
+
+    if (A_FAILED(status)) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("AR3K Config: HCI reset failed! \n"));
+    }
+
+    if (pBufferToFree != NULL) {
+        A_FREE(pBufferToFree);
+    }
+
+    return status;
+}
+
+static A_STATUS AR3KEnableTLPM(AR3K_CONFIG_INFO *pConfig)
+{
+    A_STATUS  status;
+    /* AR3K vendor specific command for Host Wakeup Config */
+    A_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 */
+    A_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 */
+    A_CHAR    hostWakeupEnable[] = {0x31,0xFC,0x4,
+                                    0x01,0x00,0x00,0x00};
+    /* AR3K vendor specific command for Target Wakeup Enable */
+    A_CHAR    targetWakeupEnable[] = {0x31,0xFC,0x4,
+                                      0x06,0x00,0x00,0x00};
+    /* AR3K vendor specific command for Sleep Enable */
+    A_CHAR    sleepEnable[] = {0x4,0xFC,0x1,
+                               0x1};
+    A_UINT8   *pEvent = NULL;
+    A_UINT8   *pBufferToFree = NULL;
+    
+    if (0 != pConfig->IdleTimeout) {
+        A_UINT8 idle_lsb = pConfig->IdleTimeout & 0xFF;
+        A_UINT8 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) {
+        A_FREE(pBufferToFree);    
+    }
+    if (A_FAILED(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) {
+        A_FREE(pBufferToFree);    
+    }
+    if (A_FAILED(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) {
+        A_FREE(pBufferToFree);    
+    }
+    if (A_FAILED(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) {
+        A_FREE(pBufferToFree);    
+    }
+    if (A_FAILED(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) {
+        A_FREE(pBufferToFree);    
+    }
+    if (A_FAILED(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;                                              
+}
+
+A_STATUS AR3KConfigure(AR3K_CONFIG_INFO *pConfig)
+{
+    A_STATUS        status = A_OK; 
+        
+    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 (A_FAILED(status)) {
+            break;    
+        }
+      
+        if (pConfig->Flags & AR3K_CONFIG_FLAG_FORCE_MINBOOT_EXIT) {
+            status =  AR3KExitMinBoot(pConfig);   
+            if (A_FAILED(status)) {
+                break;    
+            }    
+        }
+        
+       
+        /* Load patching and PST file if available*/
+        if (A_OK != 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 (A_FAILED(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 (A_FAILED(status)) {
+            break;    
+        }     
+    
+    
+    } while (FALSE);   
+    
+  
+    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Configuration Complete (status = %d) \n",status));
+    
+    return status;
+}
+
+A_STATUS AR3KConfigureExit(void *config)
+{
+    A_STATUS        status = A_OK; 
+    AR3K_CONFIG_INFO *pConfig = (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 (A_FAILED(status)) {
+            break;    
+        }
+      
+        if (pConfig->Flags & 
+                (AR3K_CONFIG_FLAG_SET_AR3K_BAUD | AR3K_CONFIG_FLAG_SET_AR6K_SCALE_STEP)) {
+            status = AR3KConfigureHCIBaud(pConfig);      
+            if (A_FAILED(status)) {
+                break;    
+            }
+        }
+
+           /* re-enable asynchronous recv */
+        status = HCI_TransportEnableDisableAsyncRecv(pConfig->pHCIDev,TRUE);
+        if (A_FAILED(status)) {
+            break;    
+        }     
+    
+    
+    } while (FALSE);   
+    
+  
+    AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("AR3K Config: Cleanup Complete (status = %d) \n",status));
+    
+    return status;
+}
+