diff options
author | Bing Zhao <bzhao@marvell.com> | 2009-05-19 22:48:20 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-22 14:06:01 -0400 |
commit | d26285f873c3066fefe648b47b9ecf3ec18bcfbc (patch) | |
tree | c98fd544dcf32c71ff13bbb8cdf0e80602b460b7 /drivers | |
parent | b136a1414c94b4bda1057b4ffde26253d2527628 (diff) |
libertas: implement function init/shutdown commands for SD8688
SD8688 is a WLAN/Bluetooth combo chip and both functions are supported
in a single firmware image. FUNC_INIT and FUNC_SHUTDOWN commands are
implemented to utilize the multiple function feature.
When SD8688 card is inserted, the firmware image should be downloaded
only once through either WLAN function (Libertas driver) or Bluetooth
function (Bluetooth driver).
This patch adds function init/shutdown for SD8688 WLAN function only.
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/host.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 41 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 20 |
4 files changed, 62 insertions, 3 deletions
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index cbaafa653b6a..a4455ec7c354 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h | |||
@@ -321,6 +321,8 @@ struct lbs_private { | |||
321 | 321 | ||
322 | u32 monitormode; | 322 | u32 monitormode; |
323 | u8 fw_ready; | 323 | u8 fw_ready; |
324 | u8 fn_init_required; | ||
325 | u8 fn_shutdown_required; | ||
324 | }; | 326 | }; |
325 | 327 | ||
326 | extern struct cmd_confirm_sleep confirm_sleep; | 328 | extern struct cmd_confirm_sleep confirm_sleep; |
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index 8ff8ac9d817e..fe8f0cb737bc 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h | |||
@@ -86,6 +86,8 @@ | |||
86 | #define CMD_MESH_CONFIG_OLD 0x00a3 | 86 | #define CMD_MESH_CONFIG_OLD 0x00a3 |
87 | #define CMD_MESH_CONFIG 0x00ac | 87 | #define CMD_MESH_CONFIG 0x00ac |
88 | #define CMD_SET_BOOT2_VER 0x00a5 | 88 | #define CMD_SET_BOOT2_VER 0x00a5 |
89 | #define CMD_FUNC_INIT 0x00a9 | ||
90 | #define CMD_FUNC_SHUTDOWN 0x00aa | ||
89 | #define CMD_802_11_BEACON_CTRL 0x00b0 | 91 | #define CMD_802_11_BEACON_CTRL 0x00b0 |
90 | 92 | ||
91 | /* For the IEEE Power Save */ | 93 | /* For the IEEE Power Save */ |
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index e998c123e0c5..478d766abf8c 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c | |||
@@ -61,6 +61,7 @@ struct if_sdio_model { | |||
61 | int model; | 61 | int model; |
62 | const char *helper; | 62 | const char *helper; |
63 | const char *firmware; | 63 | const char *firmware; |
64 | struct if_sdio_card *card; | ||
64 | }; | 65 | }; |
65 | 66 | ||
66 | static struct if_sdio_model if_sdio_models[] = { | 67 | static struct if_sdio_model if_sdio_models[] = { |
@@ -69,18 +70,21 @@ static struct if_sdio_model if_sdio_models[] = { | |||
69 | .model = IF_SDIO_MODEL_8385, | 70 | .model = IF_SDIO_MODEL_8385, |
70 | .helper = "sd8385_helper.bin", | 71 | .helper = "sd8385_helper.bin", |
71 | .firmware = "sd8385.bin", | 72 | .firmware = "sd8385.bin", |
73 | .card = NULL, | ||
72 | }, | 74 | }, |
73 | { | 75 | { |
74 | /* 8686 */ | 76 | /* 8686 */ |
75 | .model = IF_SDIO_MODEL_8686, | 77 | .model = IF_SDIO_MODEL_8686, |
76 | .helper = "sd8686_helper.bin", | 78 | .helper = "sd8686_helper.bin", |
77 | .firmware = "sd8686.bin", | 79 | .firmware = "sd8686.bin", |
80 | .card = NULL, | ||
78 | }, | 81 | }, |
79 | { | 82 | { |
80 | /* 8688 */ | 83 | /* 8688 */ |
81 | .model = IF_SDIO_MODEL_8688, | 84 | .model = IF_SDIO_MODEL_8688, |
82 | .helper = "sd8688_helper.bin", | 85 | .helper = "sd8688_helper.bin", |
83 | .firmware = "sd8688.bin", | 86 | .firmware = "sd8688.bin", |
87 | .card = NULL, | ||
84 | }, | 88 | }, |
85 | }; | 89 | }; |
86 | 90 | ||
@@ -539,7 +543,6 @@ static int if_sdio_prog_helper(struct if_sdio_card *card) | |||
539 | ret = 0; | 543 | ret = 0; |
540 | 544 | ||
541 | release: | 545 | release: |
542 | sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); | ||
543 | sdio_release_host(card->func); | 546 | sdio_release_host(card->func); |
544 | kfree(chunk_buffer); | 547 | kfree(chunk_buffer); |
545 | release_fw: | 548 | release_fw: |
@@ -675,7 +678,6 @@ static int if_sdio_prog_real(struct if_sdio_card *card) | |||
675 | ret = 0; | 678 | ret = 0; |
676 | 679 | ||
677 | release: | 680 | release: |
678 | sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); | ||
679 | sdio_release_host(card->func); | 681 | sdio_release_host(card->func); |
680 | kfree(chunk_buffer); | 682 | kfree(chunk_buffer); |
681 | release_fw: | 683 | release_fw: |
@@ -718,6 +720,9 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) | |||
718 | goto out; | 720 | goto out; |
719 | 721 | ||
720 | success: | 722 | success: |
723 | sdio_claim_host(card->func); | ||
724 | sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); | ||
725 | sdio_release_host(card->func); | ||
721 | ret = 0; | 726 | ret = 0; |
722 | 727 | ||
723 | out: | 728 | out: |
@@ -903,6 +908,8 @@ static int if_sdio_probe(struct sdio_func *func, | |||
903 | goto free; | 908 | goto free; |
904 | } | 909 | } |
905 | 910 | ||
911 | if_sdio_models[i].card = card; | ||
912 | |||
906 | card->helper = if_sdio_models[i].helper; | 913 | card->helper = if_sdio_models[i].helper; |
907 | card->firmware = if_sdio_models[i].firmware; | 914 | card->firmware = if_sdio_models[i].firmware; |
908 | 915 | ||
@@ -985,6 +992,12 @@ static int if_sdio_probe(struct sdio_func *func, | |||
985 | if (ret) | 992 | if (ret) |
986 | goto reclaim; | 993 | goto reclaim; |
987 | 994 | ||
995 | /* | ||
996 | * FUNC_INIT is required for SD8688 WLAN/BT multiple functions | ||
997 | */ | ||
998 | priv->fn_init_required = | ||
999 | (card->model == IF_SDIO_MODEL_8688) ? 1 : 0; | ||
1000 | |||
988 | ret = lbs_start_card(priv); | 1001 | ret = lbs_start_card(priv); |
989 | if (ret) | 1002 | if (ret) |
990 | goto err_activate_card; | 1003 | goto err_activate_card; |
@@ -1025,23 +1038,30 @@ static void if_sdio_remove(struct sdio_func *func) | |||
1025 | { | 1038 | { |
1026 | struct if_sdio_card *card; | 1039 | struct if_sdio_card *card; |
1027 | struct if_sdio_packet *packet; | 1040 | struct if_sdio_packet *packet; |
1041 | int ret; | ||
1028 | 1042 | ||
1029 | lbs_deb_enter(LBS_DEB_SDIO); | 1043 | lbs_deb_enter(LBS_DEB_SDIO); |
1030 | 1044 | ||
1031 | card = sdio_get_drvdata(func); | 1045 | card = sdio_get_drvdata(func); |
1032 | 1046 | ||
1047 | lbs_stop_card(card->priv); | ||
1048 | |||
1033 | card->priv->surpriseremoved = 1; | 1049 | card->priv->surpriseremoved = 1; |
1034 | 1050 | ||
1035 | lbs_deb_sdio("call remove card\n"); | 1051 | lbs_deb_sdio("call remove card\n"); |
1036 | lbs_stop_card(card->priv); | ||
1037 | lbs_remove_card(card->priv); | 1052 | lbs_remove_card(card->priv); |
1038 | 1053 | ||
1039 | flush_workqueue(card->workqueue); | 1054 | flush_workqueue(card->workqueue); |
1040 | destroy_workqueue(card->workqueue); | 1055 | destroy_workqueue(card->workqueue); |
1041 | 1056 | ||
1042 | sdio_claim_host(func); | 1057 | sdio_claim_host(func); |
1058 | |||
1059 | /* Disable interrupts */ | ||
1060 | sdio_writeb(func, 0x00, IF_SDIO_H_INT_MASK, &ret); | ||
1061 | |||
1043 | sdio_release_irq(func); | 1062 | sdio_release_irq(func); |
1044 | sdio_disable_func(func); | 1063 | sdio_disable_func(func); |
1064 | |||
1045 | sdio_release_host(func); | 1065 | sdio_release_host(func); |
1046 | 1066 | ||
1047 | while (card->packets) { | 1067 | while (card->packets) { |
@@ -1084,8 +1104,23 @@ static int __init if_sdio_init_module(void) | |||
1084 | 1104 | ||
1085 | static void __exit if_sdio_exit_module(void) | 1105 | static void __exit if_sdio_exit_module(void) |
1086 | { | 1106 | { |
1107 | int i; | ||
1108 | struct if_sdio_card *card; | ||
1109 | |||
1087 | lbs_deb_enter(LBS_DEB_SDIO); | 1110 | lbs_deb_enter(LBS_DEB_SDIO); |
1088 | 1111 | ||
1112 | for (i = 0; i < ARRAY_SIZE(if_sdio_models); i++) { | ||
1113 | card = if_sdio_models[i].card; | ||
1114 | |||
1115 | /* | ||
1116 | * FUNC_SHUTDOWN is required for SD8688 WLAN/BT | ||
1117 | * multiple functions | ||
1118 | */ | ||
1119 | if (card && card->priv) | ||
1120 | card->priv->fn_shutdown_required = | ||
1121 | (card->model == IF_SDIO_MODEL_8688) ? 1 : 0; | ||
1122 | } | ||
1123 | |||
1089 | sdio_unregister_driver(&if_sdio_driver); | 1124 | sdio_unregister_driver(&if_sdio_driver); |
1090 | 1125 | ||
1091 | lbs_deb_leave(LBS_DEB_SDIO); | 1126 | lbs_deb_leave(LBS_DEB_SDIO); |
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 89575e448015..a58a12352672 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -1002,9 +1002,17 @@ static int lbs_setup_firmware(struct lbs_private *priv) | |||
1002 | { | 1002 | { |
1003 | int ret = -1; | 1003 | int ret = -1; |
1004 | s16 curlevel = 0, minlevel = 0, maxlevel = 0; | 1004 | s16 curlevel = 0, minlevel = 0, maxlevel = 0; |
1005 | struct cmd_header cmd; | ||
1005 | 1006 | ||
1006 | lbs_deb_enter(LBS_DEB_FW); | 1007 | lbs_deb_enter(LBS_DEB_FW); |
1007 | 1008 | ||
1009 | if (priv->fn_init_required) { | ||
1010 | memset(&cmd, 0, sizeof(cmd)); | ||
1011 | if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), | ||
1012 | lbs_cmd_copyback, (unsigned long) &cmd)) | ||
1013 | lbs_pr_alert("CMD_FUNC_INIT command failed\n"); | ||
1014 | } | ||
1015 | |||
1008 | /* Read MAC address from firmware */ | 1016 | /* Read MAC address from firmware */ |
1009 | memset(priv->current_addr, 0xff, ETH_ALEN); | 1017 | memset(priv->current_addr, 0xff, ETH_ALEN); |
1010 | ret = lbs_update_hw_spec(priv); | 1018 | ret = lbs_update_hw_spec(priv); |
@@ -1192,6 +1200,9 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) | |||
1192 | priv->mesh_open = 0; | 1200 | priv->mesh_open = 0; |
1193 | priv->infra_open = 0; | 1201 | priv->infra_open = 0; |
1194 | 1202 | ||
1203 | priv->fn_init_required = 0; | ||
1204 | priv->fn_shutdown_required = 0; | ||
1205 | |||
1195 | /* Setup the OS Interface to our functions */ | 1206 | /* Setup the OS Interface to our functions */ |
1196 | dev->netdev_ops = &lbs_netdev_ops; | 1207 | dev->netdev_ops = &lbs_netdev_ops; |
1197 | dev->watchdog_timeo = 5 * HZ; | 1208 | dev->watchdog_timeo = 5 * HZ; |
@@ -1373,11 +1384,20 @@ void lbs_stop_card(struct lbs_private *priv) | |||
1373 | struct net_device *dev; | 1384 | struct net_device *dev; |
1374 | struct cmd_ctrl_node *cmdnode; | 1385 | struct cmd_ctrl_node *cmdnode; |
1375 | unsigned long flags; | 1386 | unsigned long flags; |
1387 | struct cmd_header cmd; | ||
1376 | 1388 | ||
1377 | lbs_deb_enter(LBS_DEB_MAIN); | 1389 | lbs_deb_enter(LBS_DEB_MAIN); |
1378 | 1390 | ||
1379 | if (!priv) | 1391 | if (!priv) |
1380 | goto out; | 1392 | goto out; |
1393 | |||
1394 | if (priv->fn_shutdown_required) { | ||
1395 | memset(&cmd, 0, sizeof(cmd)); | ||
1396 | if (__lbs_cmd(priv, CMD_FUNC_SHUTDOWN, &cmd, sizeof(cmd), | ||
1397 | lbs_cmd_copyback, (unsigned long) &cmd)) | ||
1398 | lbs_pr_alert("CMD_FUNC_SHUTDOWN command failed\n"); | ||
1399 | } | ||
1400 | |||
1381 | dev = priv->dev; | 1401 | dev = priv->dev; |
1382 | 1402 | ||
1383 | netif_stop_queue(dev); | 1403 | netif_stop_queue(dev); |