Bluetooth: btmrvl: implement read-to-clear for SD8897 interrupts

For SD8897, CMD52 write_to_clear may have missing interrupts
under certain corner case condition. Use CMD53 read-to-clear
to fix the problem.

Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
Bing Zhao 2014-03-28 14:54:39 -07:00 committed by Marcel Holtmann
parent 3d5a76f08b
commit 0d3674084c
4 changed files with 73 additions and 1 deletions

View File

@ -59,6 +59,8 @@ struct btmrvl_device {
}; };
struct btmrvl_adapter { struct btmrvl_adapter {
void *hw_regs_buf;
u8 *hw_regs;
u32 int_count; u32 int_count;
struct sk_buff_head tx_queue; struct sk_buff_head tx_queue;
u8 psmode; u8 psmode;

View File

@ -24,6 +24,7 @@
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include "btmrvl_drv.h" #include "btmrvl_drv.h"
#include "btmrvl_sdio.h"
#define VERSION "1.0" #define VERSION "1.0"
@ -337,10 +338,25 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
static void btmrvl_init_adapter(struct btmrvl_private *priv) static void btmrvl_init_adapter(struct btmrvl_private *priv)
{ {
int buf_size;
skb_queue_head_init(&priv->adapter->tx_queue); skb_queue_head_init(&priv->adapter->tx_queue);
priv->adapter->ps_state = PS_AWAKE; priv->adapter->ps_state = PS_AWAKE;
buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
if (!priv->adapter->hw_regs_buf) {
priv->adapter->hw_regs = NULL;
BT_ERR("Unable to allocate buffer for hw_regs.");
} else {
priv->adapter->hw_regs =
(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
BTSDIO_DMA_ALIGN);
BT_DBG("hw_regs_buf=%p hw_regs=%p",
priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
}
init_waitqueue_head(&priv->adapter->cmd_wait_q); init_waitqueue_head(&priv->adapter->cmd_wait_q);
} }
@ -348,6 +364,7 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
{ {
skb_queue_purge(&priv->adapter->tx_queue); skb_queue_purge(&priv->adapter->tx_queue);
kfree(priv->adapter->hw_regs_buf);
kfree(priv->adapter); kfree(priv->adapter);
priv->adapter = NULL; priv->adapter = NULL;

View File

@ -64,6 +64,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
.io_port_0 = 0x00, .io_port_0 = 0x00,
.io_port_1 = 0x01, .io_port_1 = 0x01,
.io_port_2 = 0x02, .io_port_2 = 0x02,
.int_read_to_clear = false,
}; };
static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
.cfg = 0x00, .cfg = 0x00,
@ -80,6 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
.io_port_0 = 0x78, .io_port_0 = 0x78,
.io_port_1 = 0x79, .io_port_1 = 0x79,
.io_port_2 = 0x7a, .io_port_2 = 0x7a,
.int_read_to_clear = false,
}; };
static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = { static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
@ -97,6 +99,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
.io_port_0 = 0xd8, .io_port_0 = 0xd8,
.io_port_1 = 0xd9, .io_port_1 = 0xd9,
.io_port_2 = 0xda, .io_port_2 = 0xda,
.int_read_to_clear = true,
.host_int_rsr = 0x01,
.card_misc_cfg = 0xcc,
}; };
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@ -667,6 +672,23 @@ static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
return 0; return 0;
} }
static int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
{
struct btmrvl_adapter *adapter = card->priv->adapter;
int ret;
ret = sdio_readsb(card->func, adapter->hw_regs, 0, SDIO_BLOCK_SIZE);
if (ret) {
BT_ERR("sdio_readsb: read int hw_regs failed: %d", ret);
return ret;
}
*ireg = adapter->hw_regs[card->reg->host_intstatus];
BT_DBG("hw_regs[%#x]=%#x", card->reg->host_intstatus, *ireg);
return 0;
}
static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
{ {
int ret; int ret;
@ -714,7 +736,11 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func)
priv = card->priv; priv = card->priv;
ret = btmrvl_sdio_write_to_clear(card, &ireg); if (card->reg->int_read_to_clear)
ret = btmrvl_sdio_read_to_clear(card, &ireg);
else
ret = btmrvl_sdio_write_to_clear(card, &ireg);
if (ret) if (ret)
return; return;
@ -788,6 +814,30 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport); BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
if (card->reg->int_read_to_clear) {
reg = sdio_readb(func, card->reg->host_int_rsr, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
reg = sdio_readb(func, card->reg->card_misc_cfg, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
}
sdio_set_drvdata(func, card); sdio_set_drvdata(func, card);
sdio_release_host(func); sdio_release_host(func);

View File

@ -78,6 +78,9 @@ struct btmrvl_sdio_card_reg {
u8 io_port_0; u8 io_port_0;
u8 io_port_1; u8 io_port_1;
u8 io_port_2; u8 io_port_2;
bool int_read_to_clear;
u8 host_int_rsr;
u8 card_misc_cfg;
}; };
struct btmrvl_sdio_card { struct btmrvl_sdio_card {