Commit 0d367408 authored by Bing Zhao's avatar Bing Zhao Committed by Marcel Holtmann
Browse files

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: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 3d5a76f0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ struct btmrvl_device {
};

struct btmrvl_adapter {
	void *hw_regs_buf;
	u8 *hw_regs;
	u32 int_count;
	struct sk_buff_head tx_queue;
	u8 psmode;
+17 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <net/bluetooth/hci_core.h>

#include "btmrvl_drv.h"
#include "btmrvl_sdio.h"

#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)
{
	int buf_size;

	skb_queue_head_init(&priv->adapter->tx_queue);

	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);
}

@@ -348,6 +364,7 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
{
	skb_queue_purge(&priv->adapter->tx_queue);

	kfree(priv->adapter->hw_regs_buf);
	kfree(priv->adapter);

	priv->adapter = NULL;
+51 −1
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
	.io_port_0 = 0x00,
	.io_port_1 = 0x01,
	.io_port_2 = 0x02,
	.int_read_to_clear = false,
};
static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
	.cfg = 0x00,
@@ -80,6 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
	.io_port_0 = 0x78,
	.io_port_1 = 0x79,
	.io_port_2 = 0x7a,
	.int_read_to_clear = false,
};

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_1 = 0xd9,
	.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 = {
@@ -667,6 +672,23 @@ static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
	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)
{
	int ret;
@@ -714,7 +736,11 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func)

	priv = card->priv;

	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)
		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);

	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_release_host(func);
+3 −0
Original line number Diff line number Diff line
@@ -78,6 +78,9 @@ struct btmrvl_sdio_card_reg {
	u8 io_port_0;
	u8 io_port_1;
	u8 io_port_2;
	bool int_read_to_clear;
	u8 host_int_rsr;
	u8 card_misc_cfg;
};

struct btmrvl_sdio_card {