Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btmrvl: add platform specific wakeup interrupt support

On some arm-based platforms, we need to configure platform specific
parameters by device tree node and also define our node as a child
node of parent SDIO host controller.
This patch parses these parameters from device tree. It includes
calibration data download to firmware, wakeup pin configured to firmware,
and soc specific wake up gpio, which will be set as wakeup interrupt pin.

Signed-off-by: Xinming Hu <huxm@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

authored by

Xinming Hu and committed by
Marcel Holtmann
bb7f4f0b 0065d1c5

+116 -15
+11
drivers/bluetooth/btmrvl_drv.h
··· 23 23 #include <linux/bitops.h> 24 24 #include <linux/slab.h> 25 25 #include <net/bluetooth/bluetooth.h> 26 + #include <linux/err.h> 27 + #include <linux/gpio.h> 28 + #include <linux/gfp.h> 29 + #include <linux/interrupt.h> 30 + #include <linux/io.h> 31 + #include <linux/of_gpio.h> 32 + #include <linux/of_platform.h> 33 + #include <linux/platform_device.h> 34 + #include <linux/pm_runtime.h> 35 + #include <linux/slab.h> 36 + #include <linux/of_irq.h> 26 37 27 38 #define BTM_HEADER_LEN 4 28 39 #define BTM_UPLD_SIZE 2312
+20 -15
drivers/bluetooth/btmrvl_main.c
··· 510 510 static int btmrvl_check_device_tree(struct btmrvl_private *priv) 511 511 { 512 512 struct device_node *dt_node; 513 + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 513 514 u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE]; 514 - int ret; 515 - u32 val; 515 + int ret = 0; 516 + u16 gpio, gap; 516 517 517 - for_each_compatible_node(dt_node, NULL, "btmrvl,cfgdata") { 518 - ret = of_property_read_u32(dt_node, "btmrvl,gpio-gap", &val); 519 - if (!ret) 520 - priv->btmrvl_dev.gpio_gap = val; 518 + if (card->plt_of_node) { 519 + dt_node = card->plt_of_node; 520 + ret = of_property_read_u16(dt_node, "marvell,wakeup-pin", 521 + &gpio); 522 + if (ret) 523 + gpio = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; 521 524 522 - ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data", 525 + ret = of_property_read_u16(dt_node, "marvell,wakeup-gap-ms", 526 + &gap); 527 + if (ret) 528 + gap = (u8)(priv->btmrvl_dev.gpio_gap & 0x00ff); 529 + 530 + priv->btmrvl_dev.gpio_gap = (gpio << 8) + gap; 531 + 532 + ret = of_property_read_u8_array(dt_node, "marvell,cal-data", 523 533 cal_data + BT_CAL_HDR_LEN, 524 534 BT_CAL_DATA_SIZE); 525 - if (ret) { 526 - of_node_put(dt_node); 535 + if (ret) 527 536 return ret; 528 - } 529 537 530 538 BT_DBG("Use cal data from device tree"); 531 539 ret = btmrvl_download_cal_data(priv, cal_data, 532 540 BT_CAL_DATA_SIZE); 533 - if (ret) { 541 + if (ret) 534 542 BT_ERR("Fail to download calibrate data"); 535 - of_node_put(dt_node); 536 - return ret; 537 - } 538 543 } 539 544 540 - return 0; 545 + return ret; 541 546 } 542 547 543 548 static int btmrvl_setup(struct hci_dev *hdev)
+79
drivers/bluetooth/btmrvl_sdio.c
··· 52 52 {"EXTLAST", NULL, 0, 0xFE}, 53 53 }; 54 54 55 + static const struct of_device_id btmrvl_sdio_of_match_table[] = { 56 + { .compatible = "marvell,sd8897-bt" }, 57 + { .compatible = "marvell,sd8997-bt" }, 58 + { } 59 + }; 60 + 61 + static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) 62 + { 63 + struct btmrvl_plt_wake_cfg *cfg = priv; 64 + 65 + if (cfg->irq_bt >= 0) { 66 + pr_info("%s: wake by bt", __func__); 67 + cfg->wake_by_bt = true; 68 + disable_irq_nosync(irq); 69 + } 70 + 71 + return IRQ_HANDLED; 72 + } 73 + 74 + /* This function parses device tree node using mmc subnode devicetree API. 75 + * The device node is saved in card->plt_of_node. 76 + * If the device tree node exists and includes interrupts attributes, this 77 + * function will request platform specific wakeup interrupt. 78 + */ 79 + static int btmrvl_sdio_probe_of(struct device *dev, 80 + struct btmrvl_sdio_card *card) 81 + { 82 + struct btmrvl_plt_wake_cfg *cfg; 83 + int ret; 84 + 85 + if (!dev->of_node || 86 + !of_match_node(btmrvl_sdio_of_match_table, dev->of_node)) { 87 + pr_err("sdio platform data not available"); 88 + return -1; 89 + } 90 + 91 + card->plt_of_node = dev->of_node; 92 + 93 + card->plt_wake_cfg = devm_kzalloc(dev, sizeof(*card->plt_wake_cfg), 94 + GFP_KERNEL); 95 + cfg = card->plt_wake_cfg; 96 + if (cfg && card->plt_of_node) { 97 + cfg->irq_bt = irq_of_parse_and_map(card->plt_of_node, 0); 98 + if (!cfg->irq_bt) { 99 + dev_err(dev, "fail to parse irq_bt from device tree"); 100 + } else { 101 + ret = devm_request_irq(dev, cfg->irq_bt, 102 + btmrvl_wake_irq_bt, 103 + IRQF_TRIGGER_LOW, 104 + "bt_wake", cfg); 105 + if (ret) { 106 + dev_err(dev, 107 + "Failed to request irq_bt %d (%d)\n", 108 + cfg->irq_bt, ret); 109 + } 110 + disable_irq(cfg->irq_bt); 111 + } 112 + } 113 + 114 + return 0; 115 + } 116 + 55 117 /* The btmrvl_sdio_remove() callback function is called 56 118 * when user removes this module from kernel space or ejects 57 119 * the card from the slot. The driver handles these 2 cases ··· 1526 1464 1527 1465 btmrvl_sdio_enable_host_int(card); 1528 1466 1467 + /* Device tree node parsing and platform specific configuration*/ 1468 + btmrvl_sdio_probe_of(&func->dev, card); 1469 + 1529 1470 priv = btmrvl_add_card(card); 1530 1471 if (!priv) { 1531 1472 BT_ERR("Initializing card failed!"); ··· 1609 1544 return 0; 1610 1545 } 1611 1546 1547 + /* Enable platform specific wakeup interrupt */ 1548 + if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) { 1549 + card->plt_wake_cfg->wake_by_bt = false; 1550 + enable_irq(card->plt_wake_cfg->irq_bt); 1551 + enable_irq_wake(card->plt_wake_cfg->irq_bt); 1552 + } 1553 + 1612 1554 priv = card->priv; 1613 1555 priv->adapter->is_suspending = true; 1614 1556 hcidev = priv->btmrvl_dev.hcidev; ··· 1677 1605 priv->adapter->is_suspended = false; 1678 1606 BT_DBG("%s: SDIO resume", hcidev->name); 1679 1607 hci_resume_dev(hcidev); 1608 + 1609 + /* Disable platform specific wakeup interrupt */ 1610 + if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) { 1611 + disable_irq_wake(card->plt_wake_cfg->irq_bt); 1612 + if (!card->plt_wake_cfg->wake_by_bt) 1613 + disable_irq(card->plt_wake_cfg->irq_bt); 1614 + } 1680 1615 1681 1616 return 0; 1682 1617 }
+6
drivers/bluetooth/btmrvl_sdio.h
··· 62 62 63 63 #define FIRMWARE_READY 0xfedc 64 64 65 + struct btmrvl_plt_wake_cfg { 66 + int irq_bt; 67 + bool wake_by_bt; 68 + }; 65 69 66 70 struct btmrvl_sdio_card_reg { 67 71 u8 cfg; ··· 101 97 u16 sd_blksz_fw_dl; 102 98 u8 rx_unit; 103 99 struct btmrvl_private *priv; 100 + struct device_node *plt_of_node; 101 + struct btmrvl_plt_wake_cfg *plt_wake_cfg; 104 102 }; 105 103 106 104 struct btmrvl_sdio_device {