Merge branch 'salted-string-hash'
[deliverable/linux.git] / drivers / bluetooth / btmrvl_sdio.c
index c6ef248de5e44880e2c7c5269010f95eb22fbf23..d02f2c14df32545782f4d7759bb4409e482cc063 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
@@ -1009,7 +1071,6 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
 {
        struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
        int ret = 0;
-       int buf_block_len;
        int blksz;
        int i = 0;
        u8 *buf = NULL;
@@ -1021,9 +1082,13 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
                return -EINVAL;
        }
 
+       blksz = DIV_ROUND_UP(nb, SDIO_BLOCK_SIZE) * SDIO_BLOCK_SIZE;
+
        buf = payload;
-       if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1)) {
-               tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN);
+       if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1) ||
+           nb < blksz) {
+               tmpbufsz = ALIGN_SZ(blksz, BTSDIO_DMA_ALIGN) +
+                          BTSDIO_DMA_ALIGN;
                tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL);
                if (!tmpbuf)
                        return -ENOMEM;
@@ -1031,15 +1096,12 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
                memcpy(buf, payload, nb);
        }
 
-       blksz = SDIO_BLOCK_SIZE;
-       buf_block_len = DIV_ROUND_UP(nb, blksz);
-
        sdio_claim_host(card->func);
 
        do {
                /* Transfer data to card */
                ret = sdio_writesb(card->func, card->ioport, buf,
-                                  buf_block_len * blksz);
+                                  blksz);
                if (ret < 0) {
                        i++;
                        BT_ERR("i=%d writesb failed: %d", i, ret);
@@ -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;
@@ -1553,6 +1625,7 @@ static int btmrvl_sdio_suspend(struct device *dev)
        if (priv->adapter->hs_state != HS_ACTIVATED) {
                if (btmrvl_enable_hs(priv)) {
                        BT_ERR("HS not actived, suspend failed!");
+                       priv->adapter->is_suspending = false;
                        return -EBUSY;
                }
        }
@@ -1606,6 +1679,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;
 }
 
This page took 0.0296 seconds and 5 git commands to generate.