blob: 9158d515c982410abbe4f18f3fead90746fa1233 [file] [log] [blame]
/** @file bt_sdiommc.c
* @brief This file contains SDIO IF (interface) module
* related functions.
*
* Copyright (C) 2007-2015, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available along with the File in the gpl.txt file or by writing to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include <linux/firmware.h>
#include <linux/mmc/sdio_func.h>
#include "bt_drv.h"
#include "bt_sdio.h"
/** define marvell vendor id */
#define MARVELL_VENDOR_ID 0x02df
/** Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/** Firmware name */
static char *fw_name;
/** fw serial download flag */
static int bt_fw_serial = 0;
/** request firmware nowait */
int bt_req_fw_nowait;
static int multi_fn = BIT(2);
#define DEFAULT_FW_NAME ""
/** FW header length for CRC check disable */
#define FW_CRC_HEADER_RB2 28
/** FW header for CRC check disable */
static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = {
0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25
};
/** FW header length for CRC check disable */
#define FW_CRC_HEADER_RB 24
/** FW header for CRC check disable */
static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = {
0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04,
0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/** Default firmware name */
#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin"
#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin"
#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin"
#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin"
#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin"
/** SD8787 chip revision ID */
#define SD8787_W0 0x30
#define SD8787_W1 0x31
#define SD8787_A0_A1 0x40
/** SD8797 chip revision ID */
#define SD8797_A0 0x00
#define SD8797_B0 0x10
/** SD8897 chip revision ID */
#define SD8897_A0 0x10
#define SD8897_B0 0x20
/** SD8887 chip revision ID */
#define SD8887_A0 0x0
#define SD8887_A2 0x2
#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin"
#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin"
#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin"
#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin"
#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin"
#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin"
#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin"
#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin"
#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin"
/** Function number 2 */
#define FN2 2
/** Device ID for SD8787 FN2 */
#define SD_DEVICE_ID_8787_BT_FN2 0x911A
/** Device ID for SD8787 FN3 */
#define SD_DEVICE_ID_8787_BT_FN3 0x911B
/** Device ID for SD8777 FN2 */
#define SD_DEVICE_ID_8777_BT_FN2 0x9132
/** Device ID for SD8777 FN3 */
#define SD_DEVICE_ID_8777_BT_FN3 0x9133
/** Device ID for SD8887 FN2 */
#define SD_DEVICE_ID_8887_BT_FN2 0x9136
/** Device ID for SD8887 FN3 */
#define SD_DEVICE_ID_8887_BT_FN3 0x9137
/** Device ID for SD8897 FN2 */
#define SD_DEVICE_ID_8897_BT_FN2 0x912E
/** Device ID for SD8897 FN3 */
#define SD_DEVICE_ID_8897_BT_FN3 0x912F
/** Device ID for SD8797 FN2 */
#define SD_DEVICE_ID_8797_BT_FN2 0x912A
/** Device ID for SD8797 FN3 */
#define SD_DEVICE_ID_8797_BT_FN3 0x912B
/** Array of SDIO device ids when multi_fn=0x12 */
static const struct sdio_device_id bt_ids[] = {
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)},
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)},
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)},
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)},
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)},
{}
};
MODULE_DEVICE_TABLE(sdio, bt_ids);
/********************************************************
Global Variables
********************************************************/
/** unregiser bus driver flag */
static u8 unregister;
#ifdef SDIO_SUSPEND_RESUME
/** PM keep power */
extern int mbt_pm_keep_power;
#endif
/********************************************************
Local Functions
********************************************************/
/**
* @brief This function gets rx_unit value
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sd_get_rx_unit(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u8 reg;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit;
ENTER();
reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
if (ret == BT_STATUS_SUCCESS)
priv->bt_dev.rx_unit = reg;
LEAVE();
return ret;
}
/**
* @brief This function reads fwstatus registers
*
* @param priv A pointer to bt_private structure
* @param dat A pointer to keep returned data
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
static int
sd_read_firmware_status(bt_private *priv, u16 * dat)
{
int ret = BT_STATUS_SUCCESS;
u8 fws0;
u8 fws1;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1;
ENTER();
fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
if (ret < 0) {
LEAVE();
return BT_STATUS_FAILURE;
}
fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
if (ret < 0) {
LEAVE();
return BT_STATUS_FAILURE;
}
*dat = (((u16) fws1) << 8) | fws0;
LEAVE();
return BT_STATUS_SUCCESS;
}
/**
* @brief This function reads rx length
*
* @param priv A pointer to bt_private structure
* @param dat A pointer to keep returned data
* @return BT_STATUS_SUCCESS or other error no.
*/
static int
sd_read_rx_len(bt_private *priv, u16 * dat)
{
int ret = BT_STATUS_SUCCESS;
u8 reg;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len;
ENTER();
reg = sdio_readb(card->func, card_rx_len_reg, &ret);
if (ret == BT_STATUS_SUCCESS)
*dat = (u16) reg << priv->bt_dev.rx_unit;
LEAVE();
return ret;
}
/**
* @brief This function enables the host interrupts mask
*
* @param priv A pointer to bt_private structure
* @param mask the interrupt mask
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
static int
sd_enable_host_int_mask(bt_private *priv, u8 mask)
{
int ret = BT_STATUS_SUCCESS;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
ENTER();
sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
if (ret) {
PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
ret = BT_STATUS_FAILURE;
}
LEAVE();
return ret;
}
/** @brief This function disables the host interrupts mask.
*
* @param priv A pointer to bt_private structure
* @param mask the interrupt mask
* @return BT_STATUS_SUCCESS or other error no.
*/
static int
sd_disable_host_int_mask(bt_private *priv, u8 mask)
{
int ret = BT_STATUS_FAILURE;
u8 host_int_mask;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
ENTER();
/* Read back the host_int_mask register */
host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
if (ret)
goto done;
/* Update with the mask and write back to the register */
host_int_mask &= ~mask;
sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
if (ret < 0) {
PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
goto done;
}
ret = BT_STATUS_SUCCESS;
done:
LEAVE();
return ret;
}
/**
* @brief This function polls the card status register
*
* @param priv A pointer to bt_private structure
* @param bits the bit mask
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
static int
sd_poll_card_status(bt_private *priv, u8 bits)
{
int tries;
int rval;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 cs;
u8 card_status_reg = priv->psdio_device->reg->card_status;
ENTER();
for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
cs = sdio_readb(card->func, card_status_reg, &rval);
if (rval != 0)
break;
if (rval == 0 && (cs & bits) == bits) {
LEAVE();
return BT_STATUS_SUCCESS;
}
udelay(1);
}
PRINTM(ERROR,
"BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
rval, tries, cs);
LEAVE();
return BT_STATUS_FAILURE;
}
/**
* @brief This function reads updates the Cmd52 value in dev structure
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or other error no.
*/
int
sd_read_cmd52_val(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u8 func, reg, val;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
ENTER();
func = priv->bt_dev.cmd52_func;
reg = priv->bt_dev.cmd52_reg;
sdio_claim_host(card->func);
if (func)
val = sdio_readb(card->func, reg, &ret);
else
val = sdio_f0_readb(card->func, reg, &ret);
sdio_release_host(card->func);
if (ret) {
PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
func, reg);
} else {
priv->bt_dev.cmd52_val = val;
}
LEAVE();
return ret;
}
/**
* @brief This function updates card reg based on the Cmd52 value in dev structure
*
* @param priv A pointer to bt_private structure
* @param func Stores func variable
* @param reg Stores reg variable
* @param val Stores val variable
* @return BT_STATUS_SUCCESS or other error no.
*/
int
sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
{
int ret = BT_STATUS_SUCCESS;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
ENTER();
if (val >= 0) {
/* Perform actual write only if val is provided */
sdio_claim_host(card->func);
if (func)
sdio_writeb(card->func, val, reg, &ret);
else
sdio_f0_writeb(card->func, val, reg, &ret);
sdio_release_host(card->func);
if (ret) {
PRINTM(ERROR,
"BT: Cannot write value (0x%x) to func %d reg %d\n",
val, func, reg);
goto done;
}
priv->bt_dev.cmd52_val = val;
}
/* Save current func and reg for future read */
priv->bt_dev.cmd52_func = func;
priv->bt_dev.cmd52_reg = reg;
done:
LEAVE();
return ret;
}
/**
* @brief This function updates card reg based on the Cmd52 value in dev structure
*
* @param priv A pointer to bt_private structure
* @param reg register to write
* @param val value
* @return BT_STATUS_SUCCESS or other error no.
*/
int
sd_write_reg(bt_private *priv, int reg, u8 val)
{
int ret = BT_STATUS_SUCCESS;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
ENTER();
sdio_claim_host(card->func);
sdio_writeb(card->func, val, reg, &ret);
sdio_release_host(card->func);
LEAVE();
return ret;
}
/**
* @brief This function reads updates the Cmd52 value in dev structure
*
* @param priv A pointer to bt_private structure
* @param reg register to read
* @return BT_STATUS_SUCCESS or other error no.
*/
int
sd_read_reg(bt_private *priv, int reg, u8 *data)
{
int ret = BT_STATUS_SUCCESS;
u8 val;
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
ENTER();
sdio_claim_host(card->func);
val = sdio_readb(card->func, reg, &ret);
sdio_release_host(card->func);
*data = val;
LEAVE();
return ret;
}
/**
* @brief This function probes the card
*
* @param func A pointer to sdio_func structure.
* @param id A pointer to structure sdio_device_id
* @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
*/
static int
sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
{
int ret = BT_STATUS_SUCCESS;
bt_private *priv = NULL;
struct sdio_mmc_card *card = NULL;
ENTER();
PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
id->device, id->class, func->num);
card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
if (!card) {
ret = -ENOMEM;
goto done;
}
card->func = func;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
/* wait for chip fully wake up */
if (!func->enable_timeout)
func->enable_timeout = 200;
#endif
sdio_claim_host(func);
ret = sdio_enable_func(func);
if (ret) {
sdio_disable_func(func);
sdio_release_host(func);
PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
kfree(card);
LEAVE();
return -EIO;
}
sdio_release_host(func);
priv = bt_add_card(card);
if (!priv) {
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
ret = BT_STATUS_FAILURE;
kfree(card);
}
done:
LEAVE();
return ret;
}
/**
* @brief This function checks if the firmware is ready to accept
* command or not.
*
* @param priv A pointer to bt_private structure
* @param pollnum Number of times to poll fw status
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sd_verify_fw_download(bt_private *priv, int pollnum)
{
int ret = BT_STATUS_FAILURE;
u16 firmwarestat = 0;
int tries;
ENTER();
/* Wait for firmware initialization event */
for (tries = 0; tries < pollnum; tries++) {
if (sd_read_firmware_status(priv, &firmwarestat) < 0)
continue;
if (firmwarestat == FIRMWARE_READY) {
PRINTM(MSG, "BT FW is active(%d)\n", tries);
ret = BT_STATUS_SUCCESS;
break;
}
mdelay(100);
}
if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS))
PRINTM(ERROR,
"Fail to poll firmware status: firmwarestat=0x%x\n",
firmwarestat);
LEAVE();
return ret;
}
/**
* @brief Transfers firmware to card
*
* @param priv A Pointer to bt_private structure
* @param fw A Pointer to fw image
* @param fw_len fw image len
* @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
*/
static int
sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
{
struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 *firmware = fw;
int firmwarelen = fw_len;
u8 base0;
u8 base1;
int ret = BT_STATUS_SUCCESS;
int offset;
void *tmpfwbuf = NULL;
int tmpfwbufsz;
u8 *fwbuf;
u16 len;
int txlen = 0;
int tx_blocks = 0;
int i = 0;
int tries = 0;
u8 sq_read_base_address_a0_reg =
priv->psdio_device->reg->sq_read_base_addr_a0;
u8 sq_read_base_address_a1_reg =
priv->psdio_device->reg->sq_read_base_addr_a1;
u8 crc_buffer = 0;
u8 *header_crc_fw = NULL;
u8 header_crc_fw_len = 0;
if (priv->card_type == CARD_TYPE_SD8787) {
header_crc_fw = fw_crc_header_rb_1;
header_crc_fw_len = FW_CRC_HEADER_RB;
} else if (priv->card_type == CARD_TYPE_SD8777) {
header_crc_fw = fw_crc_header_rb_2;
header_crc_fw_len = FW_CRC_HEADER_RB2;
}
ENTER();
PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
if (!tmpfwbuf) {
PRINTM(ERROR,
"BT: Unable to allocate buffer for firmware. Terminating download\n");
ret = BT_STATUS_FAILURE;
goto done;
}
/* Ensure aligned firmware buffer */
fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
if (!(priv->fw_crc_check)
&& ((priv->card_type == CARD_TYPE_SD8787) ||
(priv->card_type == CARD_TYPE_SD8777))
) {
/* CRC check not required, use custom header first */
firmware = header_crc_fw;
firmwarelen = header_crc_fw_len;
crc_buffer = 1;
}
/* Perform firmware data transfer */
offset = 0;
do {
/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
*/
ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
if (ret < 0) {
PRINTM(FATAL,
"BT: FW download with helper poll status timeout @ %d\n",
offset);
goto done;
}
if (!crc_buffer)
/* More data? */
if (offset >= firmwarelen)
break;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
base0 = sdio_readb(card->func,
sq_read_base_address_a0_reg, &ret);
if (ret) {
PRINTM(WARN, "Dev BASE0 register read failed:"
" base0=0x%04X(%d). Terminating download\n",
base0, base0);
ret = BT_STATUS_FAILURE;
goto done;
}
base1 = sdio_readb(card->func,
sq_read_base_address_a1_reg, &ret);
if (ret) {
PRINTM(WARN, "Dev BASE1 register read failed:"
" base1=0x%04X(%d). Terminating download\n",
base1, base1);
ret = BT_STATUS_FAILURE;
goto done;
}
len = (((u16) base1) << 8) | base0;
if (len != 0)
break;
udelay(10);
}
if (len == 0)
break;
else if (len > BT_UPLD_SIZE) {
PRINTM(FATAL,
"BT: FW download failure @ %d, invalid length %d\n",
offset, len);
ret = BT_STATUS_FAILURE;
goto done;
}
txlen = len;
if (len & BIT(0)) {
i++;
if (i > MAX_WRITE_IOMEM_RETRY) {
PRINTM(FATAL,
"BT: FW download failure @ %d, over max retry count\n",
offset);
ret = BT_STATUS_FAILURE;
goto done;
}
PRINTM(ERROR,
"BT: FW CRC error indicated by the helper:"
" len = 0x%04X, txlen = %d\n", len, txlen);
len &= ~BIT(0);
PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
/* Setting this to 0 to resend from same offset */
txlen = 0;
} else {
i = 0;
/* Set blocksize to transfer - checking for last block */
if (firmwarelen - offset < txlen)
txlen = firmwarelen - offset;
PRINTM(INFO, ".");
tx_blocks =
(txlen + SD_BLOCK_SIZE_FW_DL -
1) / SD_BLOCK_SIZE_FW_DL;
/* Copy payload to buffer */
memcpy(fwbuf, &firmware[offset], txlen);
}
/* Send data */
ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
tx_blocks * SD_BLOCK_SIZE_FW_DL);
if (ret < 0) {
PRINTM(ERROR,
"BT: FW download, write iomem (%d) failed @ %d\n",
i, offset);
sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
if (ret)
PRINTM(ERROR, "write ioreg failed (CFG)\n");
}
offset += txlen;
if (crc_buffer
&& ((priv->card_type == CARD_TYPE_SD8787) ||
(priv->card_type == CARD_TYPE_SD8777))
) {
if (offset >= header_crc_fw_len) {
/* Custom header download complete, restore
original FW */
offset = 0;
firmware = fw;
firmwarelen = fw_len;
crc_buffer = 0;
}
}
} while (TRUE);
PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
ret = BT_STATUS_SUCCESS;
done:
kfree(tmpfwbuf);
LEAVE();
return ret;
}
/**
* @brief request_firmware callback
*
* @param fw_firmware A pointer to firmware structure
* @param context A Pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
static int
sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
{
int ret = BT_STATUS_SUCCESS;
bt_private *priv = (bt_private *)context;
struct sdio_mmc_card *card = NULL;
struct m_dev *m_dev_bt = NULL;
struct m_dev *m_dev_fm = NULL;
struct m_dev *m_dev_nfc = NULL;
struct timeval tstamp;
ENTER();
m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
m_dev_fm = &priv->bt_dev.m_dev[FM_SEQ];
m_dev_nfc = &priv->bt_dev.m_dev[NFC_SEQ];
if ((priv == NULL) || (priv->adapter == NULL) ||
(priv->bt_dev.card == NULL) || (m_dev_bt == NULL) ||
(m_dev_fm == NULL) || (m_dev_nfc == NULL)) {
LEAVE();
return BT_STATUS_FAILURE;
}
card = (struct sdio_mmc_card *)priv->bt_dev.card;
if (!fw_firmware) {
do_gettimeofday(&tstamp);
if (tstamp.tv_sec >
(priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
PRINTM(ERROR,
"BT: No firmware image found. Skipping download\n");
ret = BT_STATUS_FAILURE;
goto done;
}
PRINTM(ERROR,
"BT: No firmware image found! Retrying download\n");
/* Wait a second here before calling the callback again */
os_sched_timeout(1000);
sd_download_firmware_w_helper(priv);
LEAVE();
return ret;
}
priv->firmware = fw_firmware;
if (BT_STATUS_FAILURE ==
sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
priv->firmware->size)) {
PRINTM(ERROR,
"BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
bt_req_fw_nowait);
sdio_release_host(card->func);
ret = BT_STATUS_FAILURE;
goto done;
}
/* check if the fimware is downloaded successfully or not */
if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
PRINTM(ERROR, "BT: FW failed to be active in time!\n");
ret = BT_STATUS_FAILURE;
sdio_release_host(card->func);
goto done;
}
sdio_release_host(card->func);
sd_enable_host_int(priv);
if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
PRINTM(ERROR,
"BT: sbi_register_conf_dpc failed. Terminating download\n");
ret = BT_STATUS_FAILURE;
goto done;
}
if (fw_firmware) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
if (!bt_req_fw_nowait)
#endif
release_firmware(fw_firmware);
}
LEAVE();
return ret;
done:
if (fw_firmware) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
if (!bt_req_fw_nowait)
#endif
release_firmware(fw_firmware);
}
/* For synchronous download cleanup will be done in add_card */
if (!bt_req_fw_nowait)
return ret;
PRINTM(INFO, "unregister device\n");
sbi_unregister_dev(priv);
((struct sdio_mmc_card *)card)->priv = NULL;
/* Stop the thread servicing the interrupts */
priv->adapter->SurpriseRemoved = TRUE;
wake_up_interruptible(&priv->MainThread.waitQ);
while (priv->MainThread.pid)
os_sched_timeout(1);
bt_proc_remove(priv);
clean_up_m_devs(priv);
bt_free_adapter(priv);
bt_priv_put(priv);
LEAVE();
return ret;
}
/**
* @brief request_firmware callback
* This function is invoked by request_firmware_nowait system call
*
* @param firmware A pointer to firmware structure
* @param context A Pointer to bt_private structure
* @return None
**/
static void
sd_request_fw_callback(const struct firmware *firmware, void *context)
{
ENTER();
sd_request_fw_dpc(firmware, context);
LEAVE();
return;
}
/**
* @brief This function downloads firmware image to the card.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
*/
int
sd_download_firmware_w_helper(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
int err;
char *cur_fw_name = NULL;
ENTER();
cur_fw_name = fw_name;
if (fw_name == NULL) {
if (priv->card_type == CARD_TYPE_SD8787)
cur_fw_name = DEFAULT_FW_NAME_8787;
else if (priv->card_type == CARD_TYPE_SD8777)
cur_fw_name = DEFAULT_FW_NAME_8777;
else if (priv->card_type == CARD_TYPE_SD8887) {
/* Check revision ID */
switch (priv->adapter->chip_rev) {
case SD8887_A0:
cur_fw_name = SD8887_A0_FW_NAME;
break;
case SD8887_A2:
if (bt_fw_serial == 1)
cur_fw_name = SD8887_A2_FW_NAME;
else
cur_fw_name = SD8887_A2_BT_FW_NAME;
break;
default:
cur_fw_name = DEFAULT_FW_NAME_8887;
break;
}
} else if (priv->card_type == CARD_TYPE_SD8897)
cur_fw_name = DEFAULT_FW_NAME_8897;
else if (priv->card_type == CARD_TYPE_SD8797)
cur_fw_name = DEFAULT_FW_NAME_8797;
}
if (bt_req_fw_nowait) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
cur_fw_name, priv->hotplug_device,
GFP_KERNEL, priv,
sd_request_fw_callback);
#else
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
cur_fw_name, priv->hotplug_device,
priv, sd_request_fw_callback);
#else
ret = request_firmware_nowait(THIS_MODULE,
cur_fw_name, priv->hotplug_device,
priv, sd_request_fw_callback);
#endif
#endif
if (ret < 0)
PRINTM(FATAL,
"BT: request_firmware_nowait() failed, error code = %#x\n",
ret);
} else {
err = request_firmware(&priv->firmware, cur_fw_name,
priv->hotplug_device);
if (err < 0) {
PRINTM(FATAL,
"BT: request_firmware() failed, error code = %#x\n",
err);
ret = BT_STATUS_FAILURE;
} else
ret = sd_request_fw_dpc(priv->firmware, priv);
}
LEAVE();
return ret;
}
/**
* @brief This function reads data from the card.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
static int
sd_card_to_host(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u16 buf_len = 0;
int buf_block_len;
int blksz;
struct sk_buff *skb = NULL;
u32 type;
u8 *payload = NULL;
struct hci_dev *hdev = NULL;
struct m_dev *mdev_fm = &(priv->bt_dev.m_dev[FM_SEQ]);
struct m_dev *mdev_nfc = &(priv->bt_dev.m_dev[NFC_SEQ]);
struct nfc_dev *nfc_dev =
(struct nfc_dev *)priv->bt_dev.m_dev[NFC_SEQ].dev_pointer;
struct fm_dev *fm_dev =
(struct fm_dev *)priv->bt_dev.m_dev[FM_SEQ].dev_pointer;
struct sdio_mmc_card *card = priv->bt_dev.card;
ENTER();
if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
if (!card || !card->func) {
PRINTM(ERROR, "BT: card or function is NULL!\n");
ret = BT_STATUS_FAILURE;
goto exit;
}
/* Read the length of data to be transferred */
ret = sd_read_rx_len(priv, &buf_len);
if (ret < 0) {
PRINTM(ERROR, "BT: card_to_host, read scratch reg failed\n");
ret = BT_STATUS_FAILURE;
goto exit;
}
/* Allocate buffer */
blksz = SD_BLOCK_SIZE;
buf_block_len = (buf_len + blksz - 1) / blksz;
if (buf_len <= BT_HEADER_LEN ||
(buf_block_len * blksz) > ALLOC_BUF_SIZE) {
PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
buf_len);
ret = BT_STATUS_FAILURE;
goto exit;
}
skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
if (skb == NULL) {
PRINTM(WARN, "BT: No free skb\n");
goto exit;
}
if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
skb_put(skb,
DMA_ALIGNMENT -
((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
skb_pull(skb,
DMA_ALIGNMENT -
((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
}
payload = skb->data;
ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
buf_block_len * blksz);
if (ret < 0) {
PRINTM(ERROR, "BT: card_to_host, read iomem failed: %d\n", ret);
kfree_skb(skb);
skb = NULL;
ret = BT_STATUS_FAILURE;
goto exit;
}
/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
(HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
buf_len = payload[0];
buf_len |= (u16) payload[1] << 8;
type = payload[3];
PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", hdev->name,
buf_len, type);
if (buf_len > buf_block_len * blksz) {
PRINTM(ERROR,
"BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
buf_len, buf_block_len * blksz);
ret = BT_STATUS_FAILURE;
kfree_skb(skb);
skb = NULL;
goto exit;
}
DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
switch (type) {
case HCI_ACLDATA_PKT:
bt_cb(skb)->pkt_type = type;
skb_put(skb, buf_len);
skb_pull(skb, BT_HEADER_LEN);
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
break;
case HCI_SCODATA_PKT:
bt_cb(skb)->pkt_type = type;
skb_put(skb, buf_len);
skb_pull(skb, BT_HEADER_LEN);
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
break;
case HCI_EVENT_PKT:
/** add EVT Demux */
bt_cb(skb)->pkt_type = type;
skb_put(skb, buf_len);
skb_pull(skb, BT_HEADER_LEN);
if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
break;
switch (skb->data[0]) {
case 0x0E:
/** cmd complete */
if (skb->data[3] == 0x80 && skb->data[4] == 0xFE) {
/** FM cmd complete */
if (fm_dev) {
skb->dev = (void *)mdev_fm;
mdev_recv_frame(skb);
mdev_fm->stat.byte_rx += buf_len;
}
} else if (skb->data[3] == 0x81 && skb->data[4] == 0xFE) {
/** NFC cmd complete */
if (nfc_dev) {
skb->dev = (void *)mdev_nfc;
mdev_recv_frame(skb);
mdev_nfc->stat.byte_rx += buf_len;
}
} else {
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
}
break;
case 0x0F:
/** cmd status */
if (skb->data[4] == 0x80 && skb->data[5] == 0xFE) {
/** FM cmd ststus */
if (fm_dev) {
skb->dev = (void *)mdev_fm;
mdev_recv_frame(skb);
mdev_fm->stat.byte_rx += buf_len;
}
} else if (skb->data[4] == 0x81 && skb->data[5] == 0xFE) {
/** NFC cmd ststus */
if (nfc_dev) {
skb->dev = (void *)mdev_nfc;
mdev_recv_frame(skb);
mdev_nfc->stat.byte_rx += buf_len;
}
} else {
/** BT cmd status */
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
}
break;
case 0xFF:
/** Vendor specific pkt */
if (skb->data[2] == 0xC0) {
/** NFC EVT */
if (nfc_dev) {
skb->dev = (void *)mdev_nfc;
mdev_recv_frame(skb);
mdev_nfc->stat.byte_rx += buf_len;
}
} else if (skb->data[2] >= 0x80 && skb->data[2] <= 0xAF) {
/** FM EVT */
if (fm_dev) {
skb->dev = (void *)mdev_fm;
mdev_recv_frame(skb);
mdev_fm->stat.byte_rx += buf_len;
}
} else {
/** BT EVT */
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
}
break;
default:
/** BT EVT */
if (hdev) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
break;
}
break;
case MRVL_VENDOR_PKT:
/* Just think here need to back compatible FM */
bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
skb_put(skb, buf_len);
skb_pull(skb, BT_HEADER_LEN);
if (hdev) {
if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) {
skb->dev = (void *)hdev;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
hci_recv_frame(skb);
#else
hci_recv_frame(hdev, skb);
#endif
hdev->stat.byte_rx += buf_len;
}
}
break;
default:
/* Driver specified event and command resp should be handle
here */
PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
kfree_skb(skb);
skb = NULL;
break;
}
exit:
if (ret) {
if (hdev)
hdev->stat.err_rx++;
PRINTM(ERROR, "error when recv pkt!\n");
}
LEAVE();
return ret;
}
/**
* @brief This function removes the card
*
* @param func A pointer to sdio_func structure
* @return N/A
*/
static void
sd_remove_card(struct sdio_func *func)
{
struct sdio_mmc_card *card;
ENTER();
if (func) {
card = sdio_get_drvdata(func);
if (card) {
if (!unregister && card->priv) {
PRINTM(INFO, "BT: card removed from sd slot\n");
((bt_private *)(card->priv))->adapter->
SurpriseRemoved = TRUE;
}
bt_remove_card(card->priv);
kfree(card);
}
}
LEAVE();
}
/**
* @brief This function handles the interrupt.
*
* @param func A pointer to sdio_func structure
* @return N/A
*/
static void
sd_interrupt(struct sdio_func *func)
{
bt_private *priv;
struct m_dev *m_dev = NULL;
struct sdio_mmc_card *card;
int ret = BT_STATUS_SUCCESS;
u8 ireg = 0;
u8 host_intstatus_reg = 0;
ENTER();
card = sdio_get_drvdata(func);
if (!card || !card->priv) {
PRINTM(INFO,
"BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
__func__, func, card);
LEAVE();
return;
}
priv = card->priv;
host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
if (priv->card_type == CARD_TYPE_SD8887 ||
priv->card_type == CARD_TYPE_SD8897) {
ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0,
SD_BLOCK_SIZE);
if (ret) {
PRINTM(ERROR,
"BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
ret);
goto done;
}
ireg = priv->adapter->hw_regs[host_intstatus_reg];
} else {
ireg = sdio_readb(card->func, host_intstatus_reg, &ret);
}
if (ret) {
PRINTM(ERROR,
"BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
ret);
goto done;
}
if (ireg != 0) {
/*
* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
* Clear the interrupt status register and re-enable
* the interrupt
*/
PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
ireg);
priv->adapter->irq_recv = ireg;
if (priv->card_type != CARD_TYPE_SD8887 &&
priv->card_type != CARD_TYPE_SD8897 &&
priv->card_type != CARD_TYPE_SD8797) {
sdio_writeb(card->func,
~(ireg) & (DN_LD_HOST_INT_STATUS |
UP_LD_HOST_INT_STATUS),
host_intstatus_reg, &ret);
if (ret) {
PRINTM(ERROR,
"BT: sdio_write_ioreg: clear int status register failed\n");
goto done;
}
}
} else {
PRINTM(ERROR, "BT: ERR: ireg=0\n");
}
OS_INT_DISABLE;
priv->adapter->sd_ireg |= ireg;
OS_INT_RESTORE;
bt_interrupt(m_dev);
done:
LEAVE();
}
/**
* @brief This function checks if the interface is ready to download
* or not while other download interfaces are present
*
* @param priv A pointer to bt_private structure
* @param val Winner status (0: winner)
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sd_check_winner_status(bt_private *priv, u8 *val)
{
int ret = BT_STATUS_SUCCESS;
u8 winner = 0;
struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
ENTER();
winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
if (ret != BT_STATUS_SUCCESS) {
LEAVE();
return BT_STATUS_FAILURE;
}
*val = winner;
LEAVE();
return ret;
}
#ifdef SDIO_SUSPEND_RESUME
#ifdef MMC_PM_KEEP_POWER
#ifdef MMC_PM_FUNC_SUSPENDED
/** @brief This function tells lower driver that BT is suspended
*
* @param priv A pointer to bt_private structure
* @return None
*/
void
bt_is_suspended(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
priv->adapter->is_suspended = TRUE;
sdio_func_suspended(card->func);
}
#endif
/** @brief This function handles client driver suspend
*
* @param dev A pointer to device structure
* @return BT_STATUS_SUCCESS or other error no.
*/
int
bt_sdio_suspend(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
mmc_pm_flag_t pm_flags = 0;
bt_private *priv = NULL;
struct sdio_mmc_card *cardp;
struct m_dev *m_dev = NULL;
struct hci_dev *hcidev;
ENTER();
pm_flags = sdio_get_host_pm_caps(func);
PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
pm_flags);
if (!(pm_flags & MMC_PM_KEEP_POWER)) {
PRINTM(ERROR,
"BT: %s: cannot remain alive while host is suspended\n",
sdio_func_id(func));
return -ENOSYS;
}
cardp = sdio_get_drvdata(func);
if (!cardp || !cardp->priv) {
PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
LEAVE();
return BT_STATUS_SUCCESS;
}
priv = cardp->priv;
if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
/* disable FM event mask */
if ((priv->bt_dev.m_dev[FM_SEQ].dev_type == FM_TYPE) &&
test_bit(HCI_RUNNING, &(priv->bt_dev.m_dev[FM_SEQ].flags)))
fm_set_intr_mask(priv, FM_DISABLE_INTR_MASK);
if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
PRINTM(CMD,
"BT: HS not actived the second time, force to suspend!\n");
}
}
}
m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
hcidev = (struct hci_dev *)m_dev->dev_pointer;
hci_suspend_dev(hcidev);
skb_queue_purge(&priv->adapter->tx_queue);
priv->adapter->is_suspended = TRUE;
LEAVE();
/* We will keep the power when hs enabled successfully */
if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
#ifdef MMC_PM_SKIP_RESUME_PROBE
PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
"MMC_PM_SKIP_RESUME_PROBE\n");
return sdio_set_host_pm_flags(func,
MMC_PM_KEEP_POWER |
MMC_PM_SKIP_RESUME_PROBE);
#else
PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
#endif
} else {
PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
return BT_STATUS_SUCCESS;
}
}
/** @brief This function handles client driver resume
*
* @param dev A pointer to device structure
* @return BT_STATUS_SUCCESS
*/
int
bt_sdio_resume(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
mmc_pm_flag_t pm_flags = 0;
bt_private *priv = NULL;
struct sdio_mmc_card *cardp;
struct m_dev *m_dev = NULL;
struct hci_dev *hcidev;
ENTER();
pm_flags = sdio_get_host_pm_caps(func);
PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
pm_flags);
cardp = sdio_get_drvdata(func);
if (!cardp || !cardp->priv) {
PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
LEAVE();
return BT_STATUS_SUCCESS;
}
priv = cardp->priv;
priv->adapter->is_suspended = FALSE;
m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
hcidev = (struct hci_dev *)m_dev->dev_pointer;
hci_resume_dev(hcidev);
sbi_wakeup_firmware(priv);
/* enable FM event mask */
if ((priv->bt_dev.m_dev[FM_SEQ].dev_type == FM_TYPE) &&
test_bit(HCI_RUNNING, &(priv->bt_dev.m_dev[FM_SEQ].flags)))
fm_set_intr_mask(priv, FM_DEFAULT_INTR_MASK);
priv->adapter->hs_state = HS_DEACTIVATED;
PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
LEAVE();
return BT_STATUS_SUCCESS;
}
#endif
#endif
/********************************************************
Global Functions
********************************************************/
#ifdef SDIO_SUSPEND_RESUME
#ifdef MMC_PM_KEEP_POWER
static const struct dev_pm_ops bt_sdio_pm_ops = {
.suspend = bt_sdio_suspend,
.resume = bt_sdio_resume,
};
#endif
#endif
static struct sdio_driver sdio_bt = {
.name = "sdio_bt",
.id_table = bt_ids,
.probe = sd_probe_card,
.remove = sd_remove_card,
#ifdef SDIO_SUSPEND_RESUME
#ifdef MMC_PM_KEEP_POWER
.drv = {
.pm = &bt_sdio_pm_ops,
}
#endif
#endif
};
/**
* @brief This function registers the bt module in bus driver.
*
* @return An int pointer that keeps returned value
*/
int *
sbi_register(void)
{
int *ret;
ENTER();
if (sdio_register_driver(&sdio_bt) != 0) {
PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
LEAVE();
return NULL;
} else
ret = (int *)1;
LEAVE();
return ret;
}
/**
* @brief This function de-registers the bt module in bus driver.
*
* @return N/A
*/
void
sbi_unregister(void)
{
ENTER();
unregister = TRUE;
sdio_unregister_driver(&sdio_bt);
LEAVE();
}
/**
* @brief This function registers the device.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sbi_register_dev(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u8 reg;
u8 chiprev;
struct sdio_mmc_card *card = priv->bt_dev.card;
struct sdio_func *func;
u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg;
u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg;
u8 card_revision_reg = priv->psdio_device->reg->card_revision;
u8 io_port_0_reg = priv->psdio_device->reg->io_port_0;
u8 io_port_1_reg = priv->psdio_device->reg->io_port_1;
u8 io_port_2_reg = priv->psdio_device->reg->io_port_2;
ENTER();
if (!card || !card->func) {
PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
goto failed;
}
func = card->func;
priv->hotplug_device = &func->dev;
/* Initialize the private structure */
strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
priv->bt_dev.ioport = 0;
priv->bt_dev.fn = func->num;
sdio_claim_host(func);
ret = sdio_claim_irq(func, sd_interrupt);
if (ret) {
PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
goto release_host;
}
ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
if (ret) {
PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
goto release_irq;
}
/* read Revision Register to get the chip revision number */
chiprev = sdio_readb(func, card_revision_reg, &ret);
if (ret) {
PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
goto release_irq;
}
priv->adapter->chip_rev = chiprev;
PRINTM(INFO, "revision=%#x\n", chiprev);
/*
* Read the HOST_INTSTATUS_REG for ACK the first interrupt got
* from the bootloader. If we don't do this we get a interrupt
* as soon as we register the irq.
*/
reg = sdio_readb(func, host_intstatus_reg, &ret);
if (ret < 0)
goto release_irq;
/* Read the IO port */
reg = sdio_readb(func, io_port_0_reg, &ret);
if (ret < 0)
goto release_irq;
else
priv->bt_dev.ioport |= reg;
reg = sdio_readb(func, io_port_1_reg, &ret);
if (ret < 0)
goto release_irq;
else
priv->bt_dev.ioport |= (reg << 8);
reg = sdio_readb(func, io_port_2_reg, &ret);
if (ret < 0)
goto release_irq;
else
priv->bt_dev.ioport |= (reg << 16);
PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
priv->bt_dev.ioport);
#define SDIO_INT_MASK 0x3F
if (priv->card_type == CARD_TYPE_SD8887 ||
priv->card_type == CARD_TYPE_SD8897 ||
priv->card_type == CARD_TYPE_SD8797) {
/* Set Host interrupt reset to read to clear */
reg = sdio_readb(func, host_int_rsr_reg, &ret);
if (ret < 0)
goto release_irq;
sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
if (ret < 0)
goto release_irq;
/* Set auto re-enable */
reg = sdio_readb(func, card_misc_cfg_reg, &ret);
if (ret < 0)
goto release_irq;
sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg,
&ret);
if (ret < 0)
goto release_irq;
}
sdio_set_drvdata(func, card);
sdio_release_host(func);
LEAVE();
return BT_STATUS_SUCCESS;
release_irq:
sdio_release_irq(func);
release_host:
sdio_release_host(func);
failed:
LEAVE();
return BT_STATUS_FAILURE;
}
/**
* @brief This function de-registers the device.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS
*/
int
sbi_unregister_dev(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
ENTER();
if (card && card->func) {
sdio_claim_host(card->func);
sdio_release_irq(card->func);
sdio_disable_func(card->func);
sdio_release_host(card->func);
sdio_set_drvdata(card->func, NULL);
}
LEAVE();
return BT_STATUS_SUCCESS;
}
/**
* @brief This function enables the host interrupts.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sd_enable_host_int(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
int ret;
ENTER();
if (!card || !card->func) {
LEAVE();
return BT_STATUS_FAILURE;
}
sdio_claim_host(card->func);
ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
sd_get_rx_unit(priv);
sdio_release_host(card->func);
LEAVE();
return ret;
}
/**
* @brief This function disables the host interrupts.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
*/
int
sd_disable_host_int(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
int ret;
ENTER();
if (!card || !card->func) {
LEAVE();
return BT_STATUS_FAILURE;
}
sdio_claim_host(card->func);
ret = sd_disable_host_int_mask(priv, HIM_DISABLE);
sdio_release_host(card->func);
LEAVE();
return ret;
}
/**
* @brief This function sends data to the card.
*
* @param priv A pointer to bt_private structure
* @param payload A pointer to the data/cmd buffer
* @param nb Length of data/cmd
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
int ret = BT_STATUS_SUCCESS;
int buf_block_len;
int blksz;
int i = 0;
u8 *buf = NULL;
void *tmpbuf = NULL;
int tmpbufsz;
ENTER();
if (!card || !card->func) {
PRINTM(ERROR, "BT: card or function is NULL!\n");
LEAVE();
return BT_STATUS_FAILURE;
}
buf = payload;
blksz = SD_BLOCK_SIZE;
buf_block_len = (nb + blksz - 1) / blksz;
/* Allocate buffer and copy payload */
if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
tmpbufsz = buf_block_len * blksz + DMA_ALIGNMENT;
tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL);
if (!tmpbuf) {
LEAVE();
return BT_STATUS_FAILURE;
}
/* Ensure 8-byte aligned CMD buffer */
buf = (u8 *)ALIGN_ADDR(tmpbuf, DMA_ALIGNMENT);
memcpy(buf, payload, nb);
}
sdio_claim_host(card->func);
#define MAX_WRITE_IOMEM_RETRY 2
do {
/* Transfer data to card */
ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
buf_block_len * blksz);
if (ret < 0) {
i++;
PRINTM(ERROR,
"BT: host_to_card, write iomem (%d) failed: %d\n",
i, ret);
if ((priv->card_type == CARD_TYPE_SD8887) ||
(priv->card_type == CARD_TYPE_SD8897))
break;
sdio_writeb(card->func, HOST_WO_CMD53_FINISH_HOST,
CONFIGURATION_REG, &ret);
udelay(20);
ret = BT_STATUS_FAILURE;
if (i > MAX_WRITE_IOMEM_RETRY)
goto exit;
} else {
PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
m_dev->name, nb);
DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
}
} while (ret == BT_STATUS_FAILURE);
priv->bt_dev.tx_dnld_rdy = FALSE;
exit:
sdio_release_host(card->func);
kfree(tmpbuf);
LEAVE();
return ret;
}
/**
* @brief This function downloads firmware
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
*/
int
sbi_download_fw(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
int ret = BT_STATUS_SUCCESS;
u8 winner = 0;
ENTER();
if (!card || !card->func) {
PRINTM(ERROR, "BT: card or function is NULL!\n");
ret = BT_STATUS_FAILURE;
goto exit;
}
sdio_claim_host(card->func);
if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
PRINTM(MSG, "BT: FW already downloaded!\n");
sdio_release_host(card->func);
sd_enable_host_int(priv);
if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
PRINTM(ERROR,
"BT: sbi_register_conf_dpc failed. Terminating download\n");
ret = BT_STATUS_FAILURE;
goto err_register;
}
goto exit;
}
/* Check if other interface is downloading */
ret = sd_check_winner_status(priv, &winner);
if (ret == BT_STATUS_FAILURE) {
PRINTM(FATAL, "BT read winner status failed!\n");
goto done;
}
if (winner) {
PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
winner);
/* check if the fimware is downloaded successfully or not */
if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
PRINTM(FATAL, "BT: FW failed to be active in time!\n");
ret = BT_STATUS_FAILURE;
goto done;
}
sdio_release_host(card->func);
sd_enable_host_int(priv);
if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
PRINTM(ERROR,
"BT: sbi_register_conf_dpc failed. Terminating download\n");
ret = BT_STATUS_FAILURE;
goto err_register;
}
goto exit;
}
do_gettimeofday(&priv->req_fw_time);
/* Download the main firmware via the helper firmware */
if (sd_download_firmware_w_helper(priv)) {
PRINTM(INFO, "BT: FW download failed!\n");
ret = BT_STATUS_FAILURE;
}
goto exit;
done:
sdio_release_host(card->func);
exit:
LEAVE();
return ret;
err_register:
LEAVE();
return ret;
}
/**
* @brief This function checks the interrupt status and handle it accordingly.
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS
*/
int
sbi_get_int_status(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u8 sdio_ireg = 0;
struct sdio_mmc_card *card = priv->bt_dev.card;
ENTER();
OS_INT_DISABLE;
sdio_ireg = priv->adapter->sd_ireg;
priv->adapter->sd_ireg = 0;
OS_INT_RESTORE;
sdio_claim_host(card->func);
priv->adapter->irq_done = sdio_ireg;
if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */
if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */
PRINTM(INFO,
"BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
priv->bt_dev.tx_dnld_rdy, sdio_ireg);
} else {
priv->bt_dev.tx_dnld_rdy = TRUE;
}
}
if (sdio_ireg & UP_LD_HOST_INT_STATUS)
sd_card_to_host(priv);
ret = BT_STATUS_SUCCESS;
sdio_release_host(card->func);
LEAVE();
return ret;
}
/**
* @brief This function wakeup firmware
*
* @param priv A pointer to bt_private structure
* @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
*/
int
sbi_wakeup_firmware(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
int ret = BT_STATUS_SUCCESS;
ENTER();
if (!card || !card->func) {
PRINTM(ERROR, "BT: card or function is NULL!\n");
LEAVE();
return BT_STATUS_FAILURE;
}
sdio_claim_host(card->func);
sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
sdio_release_host(card->func);
PRINTM(CMD, "BT wake up firmware\n");
LEAVE();
return ret;
}
/** @brief This function updates the SDIO card types
*
* @param priv A Pointer to the bt_private structure
* @param card A Pointer to card
*
* @return N/A
*/
void
sdio_update_card_type(bt_private *priv, void *card)
{
struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card;
/* Update card type */
if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 ||
cardp->func->device == SD_DEVICE_ID_8777_BT_FN3)
priv->card_type = CARD_TYPE_SD8777;
else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 ||
cardp->func->device == SD_DEVICE_ID_8787_BT_FN3)
priv->card_type = CARD_TYPE_SD8787;
else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 ||
cardp->func->device == SD_DEVICE_ID_8887_BT_FN3)
priv->card_type = CARD_TYPE_SD8887;
else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 ||
cardp->func->device == SD_DEVICE_ID_8897_BT_FN3)
priv->card_type = CARD_TYPE_SD8897;
else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 ||
cardp->func->device == SD_DEVICE_ID_8797_BT_FN3)
priv->card_type = CARD_TYPE_SD8797;
}
/**
* @brief This function get sdio device from card type
*
* @param pmadapter A pointer to mlan_adapter structure
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
int
sdio_get_sdio_device(bt_private *priv)
{
int ret = BT_STATUS_SUCCESS;
u16 card_type = priv->card_type;
ENTER();
switch (card_type) {
case CARD_TYPE_SD8777:
priv->psdio_device = &bt_sdio_sd8777;
break;
case CARD_TYPE_SD8787:
priv->psdio_device = &bt_sdio_sd8787;
break;
case CARD_TYPE_SD8887:
priv->psdio_device = &bt_sdio_sd8887;
break;
case CARD_TYPE_SD8897:
priv->psdio_device = &bt_sdio_sd8897;
break;
case CARD_TYPE_SD8797:
priv->psdio_device = &bt_sdio_sd8797;
break;
default:
PRINTM(ERROR, "BT can't get right card type \n");
ret = BT_STATUS_FAILURE;
break;
}
LEAVE();
return ret;
}
/** @brief This function dump the SDIO register
*
* @param priv A Pointer to the bt_private structure
*
* @return N/A
*/
void
bt_dump_sdio_regs(bt_private *priv)
{
struct sdio_mmc_card *card = priv->bt_dev.card;
int ret = BT_STATUS_SUCCESS;
char buf[256], *ptr;
u8 loop, func, data;
unsigned int reg, reg_start, reg_end;
u8 index = 0;
unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70,
0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda
};
u8 loop_num = 0;
unsigned int *reg_table = NULL;
u8 reg_table_size = 0;
if (priv->card_type == CARD_TYPE_SD8887) {
loop_num = 3;
reg_table = reg_table_8887;
reg_table_size = sizeof(reg_table_8887) / sizeof(int);
} else
loop_num = 2;
if (priv->adapter->ps_state)
sbi_wakeup_firmware(priv);
sdio_claim_host(card->func);
for (loop = 0; loop < loop_num; loop++) {
memset(buf, 0, sizeof(buf));
ptr = buf;
if (loop == 0) {
/* Read the registers of SDIO function0 */
func = loop;
reg_start = 0;
reg_end = 9;
} else if (loop == 2) {
/* Read specific registers of SDIO function1 */
index = 0;
func = 2;
reg_start = reg_table[index++];
reg_end = reg_table[reg_table_size - 1];
} else {
func = 2;
reg_start = 0;
reg_end = 0x09;
}
if (loop == 2)
ptr += sprintf(ptr, "SDIO Func%d: ", func);
else
ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func,
reg_start, reg_end);
for (reg = reg_start; reg <= reg_end;) {
if (func == 0)
data = sdio_f0_readb(card->func, reg, &ret);
else
data = sdio_readb(card->func, reg, &ret);
if (loop == 2)
ptr += sprintf(ptr, "(%#x)", reg);
if (!ret)
ptr += sprintf(ptr, "%02x ", data);
else {
ptr += sprintf(ptr, "ERR");
break;
}
if (loop == 2 && reg < reg_end)
reg = reg_table[index++];
else
reg++;
}
PRINTM(MSG, "%s\n", buf);
}
sdio_release_host(card->func);
}
module_param(fw_name, charp, 0);
MODULE_PARM_DESC(fw_name, "Firmware name");
module_param(bt_req_fw_nowait, int, 0);
MODULE_PARM_DESC(bt_req_fw_nowait,
"0: Use request_firmware API; 1: Use request_firmware_nowait API");
module_param(multi_fn, int, 0);
MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
module_param(bt_fw_serial, int, 0);
MODULE_PARM_DESC(bt_fw_serial,
"0: Support parallel download FW; 1: Support serial download FW");