diff options
| author | Amitkumar Karwar <akarwar@marvell.com> | 2012-04-25 14:43:54 -0400 |
|---|---|---|
| committer | Gustavo Padovan <gustavo@padovan.org> | 2012-05-09 00:40:51 -0400 |
| commit | ba54a16ffacfc7121b6a799de1d08254cb0254b9 (patch) | |
| tree | 3919a7c8fc86315d3aebacf1787e68d8c0355de2 /drivers/bluetooth | |
| parent | 6ff9b5ef5e4e3f474689737640d0c01a96d0696d (diff) | |
Bluetooth: btmrvl: add support for SDIO suspend/resume callbacks
Host sleep is activated using already configured host sleep
parameters in suspend handler and it is cancelled in resume
handler.
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo@padovan.org>
Diffstat (limited to 'drivers/bluetooth')
| -rw-r--r-- | drivers/bluetooth/btmrvl_drv.h | 2 | ||||
| -rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 100 |
3 files changed, 104 insertions, 1 deletions
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 2c79e76b1ba6..94f2d65131c4 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h | |||
| @@ -67,6 +67,7 @@ struct btmrvl_adapter { | |||
| 67 | u8 wakeup_tries; | 67 | u8 wakeup_tries; |
| 68 | wait_queue_head_t cmd_wait_q; | 68 | wait_queue_head_t cmd_wait_q; |
| 69 | u8 cmd_complete; | 69 | u8 cmd_complete; |
| 70 | bool is_suspended; | ||
| 70 | }; | 71 | }; |
| 71 | 72 | ||
| 72 | struct btmrvl_private { | 73 | struct btmrvl_private { |
| @@ -142,6 +143,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd); | |||
| 142 | int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); | 143 | int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); |
| 143 | int btmrvl_enable_ps(struct btmrvl_private *priv); | 144 | int btmrvl_enable_ps(struct btmrvl_private *priv); |
| 144 | int btmrvl_prepare_command(struct btmrvl_private *priv); | 145 | int btmrvl_prepare_command(struct btmrvl_private *priv); |
| 146 | int btmrvl_enable_hs(struct btmrvl_private *priv); | ||
| 145 | 147 | ||
| 146 | #ifdef CONFIG_DEBUG_FS | 148 | #ifdef CONFIG_DEBUG_FS |
| 147 | void btmrvl_debugfs_init(struct hci_dev *hdev); | 149 | void btmrvl_debugfs_init(struct hci_dev *hdev); |
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index a880537ff8c5..681ca9d18e12 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c | |||
| @@ -262,7 +262,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv) | |||
| 262 | } | 262 | } |
| 263 | EXPORT_SYMBOL_GPL(btmrvl_enable_ps); | 263 | EXPORT_SYMBOL_GPL(btmrvl_enable_ps); |
| 264 | 264 | ||
| 265 | static int btmrvl_enable_hs(struct btmrvl_private *priv) | 265 | int btmrvl_enable_hs(struct btmrvl_private *priv) |
| 266 | { | 266 | { |
| 267 | struct sk_buff *skb; | 267 | struct sk_buff *skb; |
| 268 | struct btmrvl_cmd *cmd; | 268 | struct btmrvl_cmd *cmd; |
| @@ -298,6 +298,7 @@ static int btmrvl_enable_hs(struct btmrvl_private *priv) | |||
| 298 | 298 | ||
| 299 | return ret; | 299 | return ret; |
| 300 | } | 300 | } |
| 301 | EXPORT_SYMBOL_GPL(btmrvl_enable_hs); | ||
| 301 | 302 | ||
| 302 | int btmrvl_prepare_command(struct btmrvl_private *priv) | 303 | int btmrvl_prepare_command(struct btmrvl_private *priv) |
| 303 | { | 304 | { |
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index d7d8f83d1c02..a853244e7fd7 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c | |||
| @@ -1046,11 +1046,111 @@ static void btmrvl_sdio_remove(struct sdio_func *func) | |||
| 1046 | } | 1046 | } |
| 1047 | } | 1047 | } |
| 1048 | 1048 | ||
| 1049 | static int btmrvl_sdio_suspend(struct device *dev) | ||
| 1050 | { | ||
| 1051 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
| 1052 | struct btmrvl_sdio_card *card; | ||
| 1053 | struct btmrvl_private *priv; | ||
| 1054 | mmc_pm_flag_t pm_flags; | ||
| 1055 | struct hci_dev *hcidev; | ||
| 1056 | |||
| 1057 | if (func) { | ||
| 1058 | pm_flags = sdio_get_host_pm_caps(func); | ||
| 1059 | BT_DBG("%s: suspend: PM flags = 0x%x", sdio_func_id(func), | ||
| 1060 | pm_flags); | ||
| 1061 | if (!(pm_flags & MMC_PM_KEEP_POWER)) { | ||
| 1062 | BT_ERR("%s: cannot remain alive while suspended", | ||
| 1063 | sdio_func_id(func)); | ||
| 1064 | return -ENOSYS; | ||
| 1065 | } | ||
| 1066 | card = sdio_get_drvdata(func); | ||
| 1067 | if (!card || !card->priv) { | ||
| 1068 | BT_ERR("card or priv structure is not valid"); | ||
| 1069 | return 0; | ||
| 1070 | } | ||
| 1071 | } else { | ||
| 1072 | BT_ERR("sdio_func is not specified"); | ||
| 1073 | return 0; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | priv = card->priv; | ||
| 1077 | |||
| 1078 | if (priv->adapter->hs_state != HS_ACTIVATED) { | ||
| 1079 | if (btmrvl_enable_hs(priv)) { | ||
| 1080 | BT_ERR("HS not actived, suspend failed!"); | ||
| 1081 | return -EBUSY; | ||
| 1082 | } | ||
| 1083 | } | ||
| 1084 | hcidev = priv->btmrvl_dev.hcidev; | ||
| 1085 | BT_DBG("%s: SDIO suspend", hcidev->name); | ||
| 1086 | hci_suspend_dev(hcidev); | ||
| 1087 | skb_queue_purge(&priv->adapter->tx_queue); | ||
| 1088 | |||
| 1089 | priv->adapter->is_suspended = true; | ||
| 1090 | |||
| 1091 | /* We will keep the power when hs enabled successfully */ | ||
| 1092 | if (priv->adapter->hs_state == HS_ACTIVATED) { | ||
| 1093 | BT_DBG("suspend with MMC_PM_KEEP_POWER"); | ||
| 1094 | return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
| 1095 | } else { | ||
| 1096 | BT_DBG("suspend without MMC_PM_KEEP_POWER"); | ||
| 1097 | return 0; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | static int btmrvl_sdio_resume(struct device *dev) | ||
| 1102 | { | ||
| 1103 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
| 1104 | struct btmrvl_sdio_card *card; | ||
| 1105 | struct btmrvl_private *priv; | ||
| 1106 | mmc_pm_flag_t pm_flags; | ||
| 1107 | struct hci_dev *hcidev; | ||
| 1108 | |||
| 1109 | if (func) { | ||
| 1110 | pm_flags = sdio_get_host_pm_caps(func); | ||
| 1111 | BT_DBG("%s: resume: PM flags = 0x%x", sdio_func_id(func), | ||
| 1112 | pm_flags); | ||
| 1113 | card = sdio_get_drvdata(func); | ||
| 1114 | if (!card || !card->priv) { | ||
| 1115 | BT_ERR("card or priv structure is not valid"); | ||
| 1116 | return 0; | ||
| 1117 | } | ||
| 1118 | } else { | ||
| 1119 | BT_ERR("sdio_func is not specified"); | ||
| 1120 | return 0; | ||
| 1121 | } | ||
| 1122 | priv = card->priv; | ||
| 1123 | |||
| 1124 | if (!priv->adapter->is_suspended) { | ||
| 1125 | BT_DBG("device already resumed"); | ||
| 1126 | return 0; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | priv->adapter->is_suspended = false; | ||
| 1130 | hcidev = priv->btmrvl_dev.hcidev; | ||
| 1131 | BT_DBG("%s: SDIO resume", hcidev->name); | ||
| 1132 | hci_resume_dev(hcidev); | ||
| 1133 | priv->hw_wakeup_firmware(priv); | ||
| 1134 | priv->adapter->hs_state = HS_DEACTIVATED; | ||
| 1135 | BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name); | ||
| 1136 | |||
| 1137 | return 0; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | static const struct dev_pm_ops btmrvl_sdio_pm_ops = { | ||
| 1141 | .suspend = btmrvl_sdio_suspend, | ||
| 1142 | .resume = btmrvl_sdio_resume, | ||
| 1143 | }; | ||
| 1144 | |||
| 1049 | static struct sdio_driver bt_mrvl_sdio = { | 1145 | static struct sdio_driver bt_mrvl_sdio = { |
| 1050 | .name = "btmrvl_sdio", | 1146 | .name = "btmrvl_sdio", |
| 1051 | .id_table = btmrvl_sdio_ids, | 1147 | .id_table = btmrvl_sdio_ids, |
| 1052 | .probe = btmrvl_sdio_probe, | 1148 | .probe = btmrvl_sdio_probe, |
| 1053 | .remove = btmrvl_sdio_remove, | 1149 | .remove = btmrvl_sdio_remove, |
| 1150 | .drv = { | ||
| 1151 | .owner = THIS_MODULE, | ||
| 1152 | .pm = &btmrvl_sdio_pm_ops, | ||
| 1153 | } | ||
| 1054 | }; | 1154 | }; |
| 1055 | 1155 | ||
| 1056 | static int __init btmrvl_sdio_init_module(void) | 1156 | static int __init btmrvl_sdio_init_module(void) |
