Bluetooth: Add USB ID 13D3:3487 to ath3k
[linux-2.6-block.git] / drivers / bluetooth / btmrvl_sdio.c
index c6ef248de5e44880e2c7c5269010f95eb22fbf23..f425ddf91a2459c7af330584da6ab9b5018a84a6 100644 (file)
@@ -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;
 }