diff options
Diffstat (limited to 'drivers/bluetooth/btmrvl_sdio.c')
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index c6ef248de5e4..f425ddf91a24 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -52,6 +52,68 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = { {"EXTLAST", NULL, 0, 0xFE}, }; +static const struct of_device_id btmrvl_sdio_of_match_table[] = { + { .compatible = "marvell,sd8897-bt" }, + { .compatible = "marvell,sd8997-bt" }, + { } +}; + +static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) +{ + struct btmrvl_plt_wake_cfg *cfg = priv; + + if (cfg->irq_bt >= 0) { + pr_info("%s: wake by bt", __func__); + cfg->wake_by_bt = true; + disable_irq_nosync(irq); + } + + return IRQ_HANDLED; +} + +/* This function parses device tree node using mmc subnode devicetree API. + * The device node is saved in card->plt_of_node. + * If the device tree node exists and includes interrupts attributes, this + * function will request platform specific wakeup interrupt. + */ +static int btmrvl_sdio_probe_of(struct device *dev, + struct btmrvl_sdio_card *card) +{ + struct btmrvl_plt_wake_cfg *cfg; + int ret; + + if (!dev->of_node || + !of_match_node(btmrvl_sdio_of_match_table, dev->of_node)) { + pr_err("sdio platform data not available"); + return -1; + } + + card->plt_of_node = dev->of_node; + + card->plt_wake_cfg = devm_kzalloc(dev, sizeof(*card->plt_wake_cfg), + GFP_KERNEL); + cfg = card->plt_wake_cfg; + if (cfg && card->plt_of_node) { + cfg->irq_bt = irq_of_parse_and_map(card->plt_of_node, 0); + if (!cfg->irq_bt) { + dev_err(dev, "fail to parse irq_bt from device tree"); + } else { + ret = devm_request_irq(dev, cfg->irq_bt, + btmrvl_wake_irq_bt, + IRQF_TRIGGER_LOW, + "bt_wake", cfg); + if (ret) { + dev_err(dev, + "Failed to request irq_bt %d (%d)\n", + cfg->irq_bt, ret); + } + disable_irq(cfg->irq_bt); + } + } + + return 0; +} + /* The btmrvl_sdio_remove() callback function is called * when user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases @@ -1464,6 +1526,9 @@ static int btmrvl_sdio_probe(struct sdio_func *func, btmrvl_sdio_enable_host_int(card); + /* Device tree node parsing and platform specific configuration*/ + btmrvl_sdio_probe_of(&func->dev, card); + priv = btmrvl_add_card(card); if (!priv) { BT_ERR("Initializing card failed!"); @@ -1544,6 +1609,13 @@ static int btmrvl_sdio_suspend(struct device *dev) return 0; } + /* Enable platform specific wakeup interrupt */ + if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) { + card->plt_wake_cfg->wake_by_bt = false; + enable_irq(card->plt_wake_cfg->irq_bt); + enable_irq_wake(card->plt_wake_cfg->irq_bt); + } + priv = card->priv; priv->adapter->is_suspending = true; hcidev = priv->btmrvl_dev.hcidev; @@ -1606,6 +1678,13 @@ static int btmrvl_sdio_resume(struct device *dev) BT_DBG("%s: SDIO resume", hcidev->name); hci_resume_dev(hcidev); + /* Disable platform specific wakeup interrupt */ + if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) { + disable_irq_wake(card->plt_wake_cfg->irq_bt); + if (!card->plt_wake_cfg->wake_by_bt) + disable_irq(card->plt_wake_cfg->irq_bt); + } + return 0; } |