diff options
author | Bing Zhao <bzhao@marvell.com> | 2014-03-29 01:54:39 +0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-03-29 06:37:06 +0400 |
commit | 0d3674084c89130bcaf15b1a69881b31f198ee72 (patch) | |
tree | ea7e78cb189e6df4d2ea6279a18a860f3976ae95 /drivers/bluetooth/btmrvl_sdio.c | |
parent | 3d5a76f08bbac55305da87f4c810279189f64297 (diff) | |
download | linux-0d3674084c89130bcaf15b1a69881b31f198ee72.tar.xz |
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>
Diffstat (limited to 'drivers/bluetooth/btmrvl_sdio.c')
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 52 |
1 files changed, 51 insertions, 1 deletions
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 7883793717fd..9dedca516ff5 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -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; - 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) 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); |