Bluetooth: Process interrupt in main thread of btmrvl driver as well
When driver is sending a command or data and the firmware is also sending a sleep event, sometimes it is observed that driver will continue to send the command/data to firmware right after processing sleep event. Once sleep event is processed driver is not supposed to send anything because firmware is in sleep state after that. Previously interrupt processing was done in SDIO interrupt callback handler. Now it is done in btmrvl driver main thread to solve the cross-sending properly. Signed-off-by: Amitkumar Karwar <akarwar@marvell.com> Signed-off-by: Bing Zhao <bzhao@marvell.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
2d0a03460a
commit
d1d10d7830
|
@ -76,6 +76,7 @@ struct btmrvl_private {
|
||||||
int (*hw_host_to_card) (struct btmrvl_private *priv,
|
int (*hw_host_to_card) (struct btmrvl_private *priv,
|
||||||
u8 *payload, u16 nb);
|
u8 *payload, u16 nb);
|
||||||
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
|
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
|
||||||
|
int (*hw_process_int_status) (struct btmrvl_private *priv);
|
||||||
spinlock_t driver_lock; /* spinlock used by driver */
|
spinlock_t driver_lock; /* spinlock used by driver */
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
void *debugfs_data;
|
void *debugfs_data;
|
||||||
|
|
|
@ -502,14 +502,17 @@ static int btmrvl_service_main_thread(void *data)
|
||||||
spin_lock_irqsave(&priv->driver_lock, flags);
|
spin_lock_irqsave(&priv->driver_lock, flags);
|
||||||
if (adapter->int_count) {
|
if (adapter->int_count) {
|
||||||
adapter->int_count = 0;
|
adapter->int_count = 0;
|
||||||
|
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||||
|
priv->hw_process_int_status(priv);
|
||||||
} else if (adapter->ps_state == PS_SLEEP &&
|
} else if (adapter->ps_state == PS_SLEEP &&
|
||||||
!skb_queue_empty(&adapter->tx_queue)) {
|
!skb_queue_empty(&adapter->tx_queue)) {
|
||||||
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||||
adapter->wakeup_tries++;
|
adapter->wakeup_tries++;
|
||||||
priv->hw_wakeup_firmware(priv);
|
priv->hw_wakeup_firmware(priv);
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
|
||||||
|
|
||||||
if (adapter->ps_state == PS_SLEEP)
|
if (adapter->ps_state == PS_SLEEP)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
* module_exit function is called.
|
* module_exit function is called.
|
||||||
*/
|
*/
|
||||||
static u8 user_rmmod;
|
static u8 user_rmmod;
|
||||||
|
static u8 sdio_ireg;
|
||||||
|
|
||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = {
|
||||||
.helper = "sd8688_helper.bin",
|
.helper = "sd8688_helper.bin",
|
||||||
|
@ -555,78 +556,79 @@ exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btmrvl_sdio_get_int_status(struct btmrvl_private *priv, u8 * ireg)
|
static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
|
||||||
{
|
{
|
||||||
int ret;
|
ulong flags;
|
||||||
u8 sdio_ireg = 0;
|
u8 ireg;
|
||||||
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
|
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
|
||||||
|
|
||||||
*ireg = 0;
|
spin_lock_irqsave(&priv->driver_lock, flags);
|
||||||
|
ireg = sdio_ireg;
|
||||||
|
sdio_ireg = 0;
|
||||||
|
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||||
|
|
||||||
sdio_ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret);
|
sdio_claim_host(card->func);
|
||||||
if (ret) {
|
if (ireg & DN_LD_HOST_INT_STATUS) {
|
||||||
BT_ERR("sdio_readb: read int status register failed");
|
|
||||||
ret = -EIO;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdio_ireg != 0) {
|
|
||||||
/*
|
|
||||||
* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
|
|
||||||
* Clear the interrupt status register and re-enable the
|
|
||||||
* interrupt.
|
|
||||||
*/
|
|
||||||
BT_DBG("sdio_ireg = 0x%x", sdio_ireg);
|
|
||||||
|
|
||||||
sdio_writeb(card->func, ~(sdio_ireg) & (DN_LD_HOST_INT_STATUS |
|
|
||||||
UP_LD_HOST_INT_STATUS),
|
|
||||||
HOST_INTSTATUS_REG, &ret);
|
|
||||||
if (ret) {
|
|
||||||
BT_ERR("sdio_writeb: clear int status register "
|
|
||||||
"failed");
|
|
||||||
ret = -EIO;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
|
|
||||||
if (priv->btmrvl_dev.tx_dnld_rdy)
|
if (priv->btmrvl_dev.tx_dnld_rdy)
|
||||||
BT_DBG("tx_done already received: "
|
BT_DBG("tx_done already received: "
|
||||||
" int_status=0x%x", sdio_ireg);
|
" int_status=0x%x", ireg);
|
||||||
else
|
else
|
||||||
priv->btmrvl_dev.tx_dnld_rdy = true;
|
priv->btmrvl_dev.tx_dnld_rdy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdio_ireg & UP_LD_HOST_INT_STATUS)
|
if (ireg & UP_LD_HOST_INT_STATUS)
|
||||||
btmrvl_sdio_card_to_host(priv);
|
btmrvl_sdio_card_to_host(priv);
|
||||||
|
|
||||||
*ireg = sdio_ireg;
|
sdio_release_host(card->func);
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
|
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void btmrvl_sdio_interrupt(struct sdio_func *func)
|
static void btmrvl_sdio_interrupt(struct sdio_func *func)
|
||||||
{
|
{
|
||||||
struct btmrvl_private *priv;
|
struct btmrvl_private *priv;
|
||||||
struct hci_dev *hcidev;
|
|
||||||
struct btmrvl_sdio_card *card;
|
struct btmrvl_sdio_card *card;
|
||||||
|
ulong flags;
|
||||||
u8 ireg = 0;
|
u8 ireg = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
card = sdio_get_drvdata(func);
|
card = sdio_get_drvdata(func);
|
||||||
if (card && card->priv) {
|
if (!card || !card->priv) {
|
||||||
priv = card->priv;
|
BT_ERR("sbi_interrupt(%p) card or priv is "
|
||||||
hcidev = priv->btmrvl_dev.hcidev;
|
"NULL, card=%p\n", func, card);
|
||||||
|
return;
|
||||||
if (btmrvl_sdio_get_int_status(priv, &ireg))
|
|
||||||
BT_ERR("reading HOST_INT_STATUS_REG failed");
|
|
||||||
else
|
|
||||||
BT_DBG("HOST_INT_STATUS_REG %#x", ireg);
|
|
||||||
|
|
||||||
btmrvl_interrupt(priv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv = card->priv;
|
||||||
|
|
||||||
|
ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret);
|
||||||
|
if (ret) {
|
||||||
|
BT_ERR("sdio_readb: read int status register failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
BT_DBG("ireg = 0x%x", ireg);
|
||||||
|
|
||||||
|
sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS |
|
||||||
|
UP_LD_HOST_INT_STATUS),
|
||||||
|
HOST_INTSTATUS_REG, &ret);
|
||||||
|
if (ret) {
|
||||||
|
BT_ERR("sdio_writeb: clear int status register failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->driver_lock, flags);
|
||||||
|
sdio_ireg |= ireg;
|
||||||
|
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||||
|
|
||||||
|
btmrvl_interrupt(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
|
static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
|
||||||
|
@ -930,6 +932,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
||||||
/* Initialize the interface specific function pointers */
|
/* Initialize the interface specific function pointers */
|
||||||
priv->hw_host_to_card = btmrvl_sdio_host_to_card;
|
priv->hw_host_to_card = btmrvl_sdio_host_to_card;
|
||||||
priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
|
priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
|
||||||
|
priv->hw_process_int_status = btmrvl_sdio_process_int_status;
|
||||||
|
|
||||||
if (btmrvl_register_hdev(priv)) {
|
if (btmrvl_register_hdev(priv)) {
|
||||||
BT_ERR("Register hdev failed!");
|
BT_ERR("Register hdev failed!");
|
||||||
|
|
Loading…
Reference in a new issue