Skip to content

Commit 26270bc

Browse files
moore-brosholtmann
authored andcommitted
Bluetooth: btmtksdio: move interrupt service to work
btmtksdio belongs to WIFI/BT combo chip that would serve two radios in one sdio_irq so that we have to move interrupt service to worker to ensure ISR as short as possible. The worker would serve the both Tx and Rx in a batch to effectively reduce many interrupts to the host and to avoid excessive sdio lock contention between various context (even from WiFi driver) and help to be more efficient to complete command/event transation. Co-developed-by: Mark-yw Chen <[email protected]> Signed-off-by: Mark-yw Chen <[email protected]> Signed-off-by: Sean Wang <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 77b210d commit 26270bc

File tree

1 file changed

+63
-69
lines changed

1 file changed

+63
-69
lines changed

drivers/bluetooth/btmtksdio.c

Lines changed: 63 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ struct btmtksdio_dev {
9898
struct sdio_func *func;
9999
struct device *dev;
100100

101-
struct work_struct tx_work;
101+
struct work_struct txrx_work;
102102
unsigned long tx_state;
103103
struct sk_buff_head txq;
104104

@@ -249,32 +249,6 @@ static u32 btmtksdio_drv_own_query(struct btmtksdio_dev *bdev)
249249
return sdio_readl(bdev->func, MTK_REG_CHLPCR, NULL);
250250
}
251251

252-
static void btmtksdio_tx_work(struct work_struct *work)
253-
{
254-
struct btmtksdio_dev *bdev = container_of(work, struct btmtksdio_dev,
255-
tx_work);
256-
struct sk_buff *skb;
257-
int err;
258-
259-
pm_runtime_get_sync(bdev->dev);
260-
261-
sdio_claim_host(bdev->func);
262-
263-
while ((skb = skb_dequeue(&bdev->txq))) {
264-
err = btmtksdio_tx_packet(bdev, skb);
265-
if (err < 0) {
266-
bdev->hdev->stat.err_tx++;
267-
skb_queue_head(&bdev->txq, skb);
268-
break;
269-
}
270-
}
271-
272-
sdio_release_host(bdev->func);
273-
274-
pm_runtime_mark_last_busy(bdev->dev);
275-
pm_runtime_put_autosuspend(bdev->dev);
276-
}
277-
278252
static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
279253
{
280254
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
@@ -425,63 +399,81 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size)
425399
return err;
426400
}
427401

428-
static void btmtksdio_interrupt(struct sdio_func *func)
402+
static void btmtksdio_txrx_work(struct work_struct *work)
429403
{
430-
struct btmtksdio_dev *bdev = sdio_get_drvdata(func);
404+
struct btmtksdio_dev *bdev = container_of(work, struct btmtksdio_dev,
405+
txrx_work);
406+
unsigned long txrx_timeout;
407+
struct sk_buff *skb;
431408
u32 int_status;
432409
u16 rx_size;
433-
434-
/* It is required that the host gets ownership from the device before
435-
* accessing any register, however, if SDIO host is not being released,
436-
* a potential deadlock probably happens in a circular wait between SDIO
437-
* IRQ work and PM runtime work. So, we have to explicitly release SDIO
438-
* host here and claim again after the PM runtime work is all done.
439-
*/
440-
sdio_release_host(bdev->func);
410+
int err;
441411

442412
pm_runtime_get_sync(bdev->dev);
443413

444414
sdio_claim_host(bdev->func);
445415

446416
/* Disable interrupt */
447-
sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
417+
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
448418

449-
int_status = sdio_readl(func, MTK_REG_CHISR, NULL);
419+
while ((skb = skb_dequeue(&bdev->txq))) {
420+
err = btmtksdio_tx_packet(bdev, skb);
421+
if (err < 0) {
422+
bdev->hdev->stat.err_tx++;
423+
skb_queue_head(&bdev->txq, skb);
424+
break;
425+
}
426+
}
450427

451-
/* Ack an interrupt as soon as possible before any operation on
452-
* hardware.
453-
*
454-
* Note that we don't ack any status during operations to avoid race
455-
* condition between the host and the device such as it's possible to
456-
* mistakenly ack RX_DONE for the next packet and then cause interrupts
457-
* not be raised again but there is still pending data in the hardware
458-
* FIFO.
459-
*/
460-
sdio_writel(func, int_status, MTK_REG_CHISR, NULL);
428+
txrx_timeout = jiffies + 5 * HZ;
429+
430+
do {
431+
int_status = sdio_readl(bdev->func, MTK_REG_CHISR, NULL);
432+
433+
/* Ack an interrupt as soon as possible before any operation on
434+
* hardware.
435+
*
436+
* Note that we don't ack any status during operations to avoid race
437+
* condition between the host and the device such as it's possible to
438+
* mistakenly ack RX_DONE for the next packet and then cause interrupts
439+
* not be raised again but there is still pending data in the hardware
440+
* FIFO.
441+
*/
442+
sdio_writel(bdev->func, int_status, MTK_REG_CHISR, NULL);
443+
444+
if (int_status & FW_OWN_BACK_INT)
445+
bt_dev_dbg(bdev->hdev, "Get fw own back");
446+
447+
if (int_status & TX_EMPTY)
448+
schedule_work(&bdev->txrx_work);
449+
else if (unlikely(int_status & TX_FIFO_OVERFLOW))
450+
bt_dev_warn(bdev->hdev, "Tx fifo overflow");
451+
452+
if (int_status & RX_DONE_INT) {
453+
rx_size = (int_status & RX_PKT_LEN) >> 16;
454+
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
455+
bdev->hdev->stat.err_rx++;
456+
}
461457

462-
if (unlikely(!int_status))
463-
bt_dev_err(bdev->hdev, "CHISR is 0");
458+
} while (int_status || time_is_before_jiffies(txrx_timeout));
464459

465-
if (int_status & FW_OWN_BACK_INT)
466-
bt_dev_dbg(bdev->hdev, "Get fw own back");
460+
/* Enable interrupt */
461+
sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, 0);
467462

468-
if (int_status & TX_EMPTY)
469-
schedule_work(&bdev->tx_work);
470-
else if (unlikely(int_status & TX_FIFO_OVERFLOW))
471-
bt_dev_warn(bdev->hdev, "Tx fifo overflow");
463+
sdio_release_host(bdev->func);
472464

473-
if (int_status & RX_DONE_INT) {
474-
rx_size = (int_status & RX_PKT_LEN) >> 16;
465+
pm_runtime_mark_last_busy(bdev->dev);
466+
pm_runtime_put_autosuspend(bdev->dev);
467+
}
475468

476-
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
477-
bdev->hdev->stat.err_rx++;
478-
}
469+
static void btmtksdio_interrupt(struct sdio_func *func)
470+
{
471+
struct btmtksdio_dev *bdev = sdio_get_drvdata(func);
479472

480-
/* Enable interrupt */
481-
sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL);
473+
/* Disable interrupt */
474+
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
482475

483-
pm_runtime_mark_last_busy(bdev->dev);
484-
pm_runtime_put_autosuspend(bdev->dev);
476+
schedule_work(&bdev->txrx_work);
485477
}
486478

487479
static int btmtksdio_open(struct hci_dev *hdev)
@@ -583,6 +575,8 @@ static int btmtksdio_close(struct hci_dev *hdev)
583575

584576
sdio_release_irq(bdev->func);
585577

578+
cancel_work_sync(&bdev->txrx_work);
579+
586580
/* Return ownership to the device */
587581
sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, NULL);
588582

@@ -604,7 +598,7 @@ static int btmtksdio_flush(struct hci_dev *hdev)
604598

605599
skb_queue_purge(&bdev->txq);
606600

607-
cancel_work_sync(&bdev->tx_work);
601+
cancel_work_sync(&bdev->txrx_work);
608602

609603
return 0;
610604
}
@@ -795,7 +789,7 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
795789

796790
skb_queue_tail(&bdev->txq, skb);
797791

798-
schedule_work(&bdev->tx_work);
792+
schedule_work(&bdev->txrx_work);
799793

800794
return 0;
801795
}
@@ -818,7 +812,7 @@ static int btmtksdio_probe(struct sdio_func *func,
818812
bdev->dev = &func->dev;
819813
bdev->func = func;
820814

821-
INIT_WORK(&bdev->tx_work, btmtksdio_tx_work);
815+
INIT_WORK(&bdev->txrx_work, btmtksdio_txrx_work);
822816
skb_queue_head_init(&bdev->txq);
823817

824818
/* Initialize and register HCI device */

0 commit comments

Comments
 (0)