diff options
author | Daniel Drake <dsd@laptop.org> | 2011-08-01 11:43:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-08-09 15:52:03 -0400 |
commit | 7e1f79a1f5fef8ac54def967b22a87909c74c8f1 (patch) | |
tree | 4decd3a1a18591d8ffb2d3aea4564aef36efd3e8 /drivers/net/wireless/libertas | |
parent | d2e7b3425c474300318e1d28b10a93c2401b9255 (diff) |
libertas: implement if_sdio runtime power management
The SDIO card is now fully powered down when the network interface is
brought down.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 277 |
1 files changed, 171 insertions, 106 deletions
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 387786e1b394..c962e21762dc 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/mmc/sdio_ids.h> | 39 | #include <linux/mmc/sdio_ids.h> |
40 | #include <linux/mmc/sdio.h> | 40 | #include <linux/mmc/sdio.h> |
41 | #include <linux/mmc/host.h> | 41 | #include <linux/mmc/host.h> |
42 | #include <linux/pm_runtime.h> | ||
42 | 43 | ||
43 | #include "host.h" | 44 | #include "host.h" |
44 | #include "decl.h" | 45 | #include "decl.h" |
@@ -47,6 +48,8 @@ | |||
47 | #include "cmd.h" | 48 | #include "cmd.h" |
48 | #include "if_sdio.h" | 49 | #include "if_sdio.h" |
49 | 50 | ||
51 | static void if_sdio_interrupt(struct sdio_func *func); | ||
52 | |||
50 | /* The if_sdio_remove() callback function is called when | 53 | /* The if_sdio_remove() callback function is called when |
51 | * user removes this module from kernel space or ejects | 54 | * user removes this module from kernel space or ejects |
52 | * the card from the slot. The driver handles these 2 cases | 55 | * the card from the slot. The driver handles these 2 cases |
@@ -757,6 +760,136 @@ out: | |||
757 | return ret; | 760 | return ret; |
758 | } | 761 | } |
759 | 762 | ||
763 | /********************************************************************/ | ||
764 | /* Power management */ | ||
765 | /********************************************************************/ | ||
766 | |||
767 | static int if_sdio_power_on(struct if_sdio_card *card) | ||
768 | { | ||
769 | struct sdio_func *func = card->func; | ||
770 | struct lbs_private *priv = card->priv; | ||
771 | struct mmc_host *host = func->card->host; | ||
772 | int ret; | ||
773 | |||
774 | sdio_claim_host(func); | ||
775 | |||
776 | ret = sdio_enable_func(func); | ||
777 | if (ret) | ||
778 | goto release; | ||
779 | |||
780 | /* For 1-bit transfers to the 8686 model, we need to enable the | ||
781 | * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 | ||
782 | * bit to allow access to non-vendor registers. */ | ||
783 | if ((card->model == MODEL_8686) && | ||
784 | (host->caps & MMC_CAP_SDIO_IRQ) && | ||
785 | (host->ios.bus_width == MMC_BUS_WIDTH_1)) { | ||
786 | u8 reg; | ||
787 | |||
788 | func->card->quirks |= MMC_QUIRK_LENIENT_FN0; | ||
789 | reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); | ||
790 | if (ret) | ||
791 | goto disable; | ||
792 | |||
793 | reg |= SDIO_BUS_ECSI; | ||
794 | sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); | ||
795 | if (ret) | ||
796 | goto disable; | ||
797 | } | ||
798 | |||
799 | card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); | ||
800 | if (ret) | ||
801 | goto disable; | ||
802 | |||
803 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; | ||
804 | if (ret) | ||
805 | goto disable; | ||
806 | |||
807 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; | ||
808 | if (ret) | ||
809 | goto disable; | ||
810 | |||
811 | sdio_release_host(func); | ||
812 | ret = if_sdio_prog_firmware(card); | ||
813 | sdio_claim_host(func); | ||
814 | if (ret) | ||
815 | goto disable; | ||
816 | |||
817 | /* | ||
818 | * Get rx_unit if the chip is SD8688 or newer. | ||
819 | * SD8385 & SD8686 do not have rx_unit. | ||
820 | */ | ||
821 | if ((card->model != MODEL_8385) | ||
822 | && (card->model != MODEL_8686)) | ||
823 | card->rx_unit = if_sdio_read_rx_unit(card); | ||
824 | else | ||
825 | card->rx_unit = 0; | ||
826 | |||
827 | /* | ||
828 | * Set up the interrupt handler late. | ||
829 | * | ||
830 | * If we set it up earlier, the (buggy) hardware generates a spurious | ||
831 | * interrupt, even before the interrupt has been enabled, with | ||
832 | * CCCR_INTx = 0. | ||
833 | * | ||
834 | * We register the interrupt handler late so that we can handle any | ||
835 | * spurious interrupts, and also to avoid generation of that known | ||
836 | * spurious interrupt in the first place. | ||
837 | */ | ||
838 | ret = sdio_claim_irq(func, if_sdio_interrupt); | ||
839 | if (ret) | ||
840 | goto disable; | ||
841 | |||
842 | /* | ||
843 | * Enable interrupts now that everything is set up | ||
844 | */ | ||
845 | sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); | ||
846 | if (ret) | ||
847 | goto release_irq; | ||
848 | |||
849 | sdio_release_host(func); | ||
850 | |||
851 | /* | ||
852 | * FUNC_INIT is required for SD8688 WLAN/BT multiple functions | ||
853 | */ | ||
854 | if (card->model == MODEL_8688) { | ||
855 | struct cmd_header cmd; | ||
856 | |||
857 | memset(&cmd, 0, sizeof(cmd)); | ||
858 | |||
859 | lbs_deb_sdio("send function INIT command\n"); | ||
860 | if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), | ||
861 | lbs_cmd_copyback, (unsigned long) &cmd)) | ||
862 | netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); | ||
863 | } | ||
864 | |||
865 | priv->fw_ready = 1; | ||
866 | |||
867 | return 0; | ||
868 | |||
869 | release_irq: | ||
870 | sdio_release_irq(func); | ||
871 | disable: | ||
872 | sdio_disable_func(func); | ||
873 | release: | ||
874 | sdio_release_host(func); | ||
875 | return ret; | ||
876 | } | ||
877 | |||
878 | static int if_sdio_power_off(struct if_sdio_card *card) | ||
879 | { | ||
880 | struct sdio_func *func = card->func; | ||
881 | struct lbs_private *priv = card->priv; | ||
882 | |||
883 | priv->fw_ready = 0; | ||
884 | |||
885 | sdio_claim_host(func); | ||
886 | sdio_release_irq(func); | ||
887 | sdio_disable_func(func); | ||
888 | sdio_release_host(func); | ||
889 | return 0; | ||
890 | } | ||
891 | |||
892 | |||
760 | /*******************************************************************/ | 893 | /*******************************************************************/ |
761 | /* Libertas callbacks */ | 894 | /* Libertas callbacks */ |
762 | /*******************************************************************/ | 895 | /*******************************************************************/ |
@@ -923,6 +1056,32 @@ static void if_sdio_reset_card(struct lbs_private *priv) | |||
923 | schedule_work(&card_reset_work); | 1056 | schedule_work(&card_reset_work); |
924 | } | 1057 | } |
925 | 1058 | ||
1059 | static int if_sdio_power_save(struct lbs_private *priv) | ||
1060 | { | ||
1061 | struct if_sdio_card *card = priv->card; | ||
1062 | int ret; | ||
1063 | |||
1064 | flush_workqueue(card->workqueue); | ||
1065 | |||
1066 | ret = if_sdio_power_off(card); | ||
1067 | |||
1068 | /* Let runtime PM know the card is powered off */ | ||
1069 | pm_runtime_put_sync(&card->func->dev); | ||
1070 | |||
1071 | return ret; | ||
1072 | } | ||
1073 | |||
1074 | static int if_sdio_power_restore(struct lbs_private *priv) | ||
1075 | { | ||
1076 | struct if_sdio_card *card = priv->card; | ||
1077 | |||
1078 | /* Make sure the card will not be powered off by runtime PM */ | ||
1079 | pm_runtime_get_sync(&card->func->dev); | ||
1080 | |||
1081 | return if_sdio_power_on(card); | ||
1082 | } | ||
1083 | |||
1084 | |||
926 | /*******************************************************************/ | 1085 | /*******************************************************************/ |
927 | /* SDIO callbacks */ | 1086 | /* SDIO callbacks */ |
928 | /*******************************************************************/ | 1087 | /*******************************************************************/ |
@@ -976,7 +1135,6 @@ static int if_sdio_probe(struct sdio_func *func, | |||
976 | int ret, i; | 1135 | int ret, i; |
977 | unsigned int model; | 1136 | unsigned int model; |
978 | struct if_sdio_packet *packet; | 1137 | struct if_sdio_packet *packet; |
979 | struct mmc_host *host = func->card->host; | ||
980 | 1138 | ||
981 | lbs_deb_enter(LBS_DEB_SDIO); | 1139 | lbs_deb_enter(LBS_DEB_SDIO); |
982 | 1140 | ||
@@ -1033,45 +1191,6 @@ static int if_sdio_probe(struct sdio_func *func, | |||
1033 | goto free; | 1191 | goto free; |
1034 | } | 1192 | } |
1035 | 1193 | ||
1036 | sdio_claim_host(func); | ||
1037 | |||
1038 | ret = sdio_enable_func(func); | ||
1039 | if (ret) | ||
1040 | goto release; | ||
1041 | |||
1042 | /* For 1-bit transfers to the 8686 model, we need to enable the | ||
1043 | * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 | ||
1044 | * bit to allow access to non-vendor registers. */ | ||
1045 | if ((card->model == MODEL_8686) && | ||
1046 | (host->caps & MMC_CAP_SDIO_IRQ) && | ||
1047 | (host->ios.bus_width == MMC_BUS_WIDTH_1)) { | ||
1048 | u8 reg; | ||
1049 | |||
1050 | func->card->quirks |= MMC_QUIRK_LENIENT_FN0; | ||
1051 | reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); | ||
1052 | if (ret) | ||
1053 | goto release_int; | ||
1054 | |||
1055 | reg |= SDIO_BUS_ECSI; | ||
1056 | sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); | ||
1057 | if (ret) | ||
1058 | goto release_int; | ||
1059 | } | ||
1060 | |||
1061 | card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); | ||
1062 | if (ret) | ||
1063 | goto release_int; | ||
1064 | |||
1065 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; | ||
1066 | if (ret) | ||
1067 | goto release_int; | ||
1068 | |||
1069 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; | ||
1070 | if (ret) | ||
1071 | goto release_int; | ||
1072 | |||
1073 | sdio_release_host(func); | ||
1074 | |||
1075 | sdio_set_drvdata(func, card); | 1194 | sdio_set_drvdata(func, card); |
1076 | 1195 | ||
1077 | lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " | 1196 | lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " |
@@ -1079,14 +1198,11 @@ static int if_sdio_probe(struct sdio_func *func, | |||
1079 | func->class, func->vendor, func->device, | 1198 | func->class, func->vendor, func->device, |
1080 | model, (unsigned)card->ioport); | 1199 | model, (unsigned)card->ioport); |
1081 | 1200 | ||
1082 | ret = if_sdio_prog_firmware(card); | ||
1083 | if (ret) | ||
1084 | goto reclaim; | ||
1085 | 1201 | ||
1086 | priv = lbs_add_card(card, &func->dev); | 1202 | priv = lbs_add_card(card, &func->dev); |
1087 | if (!priv) { | 1203 | if (!priv) { |
1088 | ret = -ENOMEM; | 1204 | ret = -ENOMEM; |
1089 | goto reclaim; | 1205 | goto free; |
1090 | } | 1206 | } |
1091 | 1207 | ||
1092 | card->priv = priv; | 1208 | card->priv = priv; |
@@ -1097,62 +1213,21 @@ static int if_sdio_probe(struct sdio_func *func, | |||
1097 | priv->exit_deep_sleep = if_sdio_exit_deep_sleep; | 1213 | priv->exit_deep_sleep = if_sdio_exit_deep_sleep; |
1098 | priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; | 1214 | priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; |
1099 | priv->reset_card = if_sdio_reset_card; | 1215 | priv->reset_card = if_sdio_reset_card; |
1216 | priv->power_save = if_sdio_power_save; | ||
1217 | priv->power_restore = if_sdio_power_restore; | ||
1100 | 1218 | ||
1101 | sdio_claim_host(func); | 1219 | ret = if_sdio_power_on(card); |
1102 | |||
1103 | /* | ||
1104 | * Get rx_unit if the chip is SD8688 or newer. | ||
1105 | * SD8385 & SD8686 do not have rx_unit. | ||
1106 | */ | ||
1107 | if ((card->model != MODEL_8385) | ||
1108 | && (card->model != MODEL_8686)) | ||
1109 | card->rx_unit = if_sdio_read_rx_unit(card); | ||
1110 | else | ||
1111 | card->rx_unit = 0; | ||
1112 | |||
1113 | /* | ||
1114 | * Set up the interrupt handler late. | ||
1115 | * | ||
1116 | * If we set it up earlier, the (buggy) hardware generates a spurious | ||
1117 | * interrupt, even before the interrupt has been enabled, with | ||
1118 | * CCCR_INTx = 0. | ||
1119 | * | ||
1120 | * We register the interrupt handler late so that we can handle any | ||
1121 | * spurious interrupts, and also to avoid generation of that known | ||
1122 | * spurious interrupt in the first place. | ||
1123 | */ | ||
1124 | ret = sdio_claim_irq(func, if_sdio_interrupt); | ||
1125 | if (ret) | ||
1126 | goto disable; | ||
1127 | |||
1128 | /* | ||
1129 | * Enable interrupts now that everything is set up | ||
1130 | */ | ||
1131 | sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); | ||
1132 | sdio_release_host(func); | ||
1133 | if (ret) | 1220 | if (ret) |
1134 | goto reclaim; | 1221 | goto err_activate_card; |
1135 | |||
1136 | priv->fw_ready = 1; | ||
1137 | |||
1138 | /* | ||
1139 | * FUNC_INIT is required for SD8688 WLAN/BT multiple functions | ||
1140 | */ | ||
1141 | if (card->model == MODEL_8688) { | ||
1142 | struct cmd_header cmd; | ||
1143 | |||
1144 | memset(&cmd, 0, sizeof(cmd)); | ||
1145 | |||
1146 | lbs_deb_sdio("send function INIT command\n"); | ||
1147 | if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), | ||
1148 | lbs_cmd_copyback, (unsigned long) &cmd)) | ||
1149 | netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); | ||
1150 | } | ||
1151 | 1222 | ||
1152 | ret = lbs_start_card(priv); | 1223 | ret = lbs_start_card(priv); |
1224 | if_sdio_power_off(card); | ||
1153 | if (ret) | 1225 | if (ret) |
1154 | goto err_activate_card; | 1226 | goto err_activate_card; |
1155 | 1227 | ||
1228 | /* Tell PM core that we don't need the card to be powered now */ | ||
1229 | pm_runtime_put_noidle(&func->dev); | ||
1230 | |||
1156 | out: | 1231 | out: |
1157 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | 1232 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); |
1158 | 1233 | ||
@@ -1161,14 +1236,6 @@ out: | |||
1161 | err_activate_card: | 1236 | err_activate_card: |
1162 | flush_workqueue(card->workqueue); | 1237 | flush_workqueue(card->workqueue); |
1163 | lbs_remove_card(priv); | 1238 | lbs_remove_card(priv); |
1164 | reclaim: | ||
1165 | sdio_claim_host(func); | ||
1166 | release_int: | ||
1167 | sdio_release_irq(func); | ||
1168 | disable: | ||
1169 | sdio_disable_func(func); | ||
1170 | release: | ||
1171 | sdio_release_host(func); | ||
1172 | free: | 1239 | free: |
1173 | destroy_workqueue(card->workqueue); | 1240 | destroy_workqueue(card->workqueue); |
1174 | while (card->packets) { | 1241 | while (card->packets) { |
@@ -1195,6 +1262,9 @@ static void if_sdio_remove(struct sdio_func *func) | |||
1195 | 1262 | ||
1196 | card = sdio_get_drvdata(func); | 1263 | card = sdio_get_drvdata(func); |
1197 | 1264 | ||
1265 | /* Undo decrement done above in if_sdio_probe */ | ||
1266 | pm_runtime_get_noresume(&func->dev); | ||
1267 | |||
1198 | if (user_rmmod && (card->model == MODEL_8688)) { | 1268 | if (user_rmmod && (card->model == MODEL_8688)) { |
1199 | /* | 1269 | /* |
1200 | * FUNC_SHUTDOWN is required for SD8688 WLAN/BT | 1270 | * FUNC_SHUTDOWN is required for SD8688 WLAN/BT |
@@ -1219,11 +1289,6 @@ static void if_sdio_remove(struct sdio_func *func) | |||
1219 | flush_workqueue(card->workqueue); | 1289 | flush_workqueue(card->workqueue); |
1220 | destroy_workqueue(card->workqueue); | 1290 | destroy_workqueue(card->workqueue); |
1221 | 1291 | ||
1222 | sdio_claim_host(func); | ||
1223 | sdio_release_irq(func); | ||
1224 | sdio_disable_func(func); | ||
1225 | sdio_release_host(func); | ||
1226 | |||
1227 | while (card->packets) { | 1292 | while (card->packets) { |
1228 | packet = card->packets; | 1293 | packet = card->packets; |
1229 | card->packets = card->packets->next; | 1294 | card->packets = card->packets->next; |