diff options
author | Mohamed Abbas <mabbas@linux.intel.com> | 2008-04-21 18:41:51 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-05-07 15:02:15 -0400 |
commit | 5da4b55f78fb2ed40926b775d4f7c791594ecbd7 (patch) | |
tree | a12ecf0d26552869151738998e88e03e65bcb7c3 /drivers/net/wireless/iwlwifi/iwl4965-base.c | |
parent | 7eafd25d9559bd0f652449c222d38d63412e3d4a (diff) |
iwlwifi: Add power level support
Add power level support
Signed-off-by: Mohamed Abbas <mabbas@linux.intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl4965-base.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl4965-base.c | 217 |
1 files changed, 20 insertions, 197 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 50f12a6133e8..6cb54580fe6b 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c | |||
@@ -879,6 +879,13 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv) | |||
879 | return 0; | 879 | return 0; |
880 | } | 880 | } |
881 | 881 | ||
882 | void iwl4965_update_chain_flags(struct iwl_priv *priv) | ||
883 | { | ||
884 | |||
885 | iwl4965_set_rxon_chain(priv); | ||
886 | iwl4965_commit_rxon(priv); | ||
887 | } | ||
888 | |||
882 | static int iwl4965_send_bt_config(struct iwl_priv *priv) | 889 | static int iwl4965_send_bt_config(struct iwl_priv *priv) |
883 | { | 890 | { |
884 | struct iwl4965_bt_cmd bt_cmd = { | 891 | struct iwl4965_bt_cmd bt_cmd = { |
@@ -1366,184 +1373,6 @@ static void iwl4965_activate_qos(struct iwl_priv *priv, u8 force) | |||
1366 | } | 1373 | } |
1367 | } | 1374 | } |
1368 | 1375 | ||
1369 | /* | ||
1370 | * Power management (not Tx power!) functions | ||
1371 | */ | ||
1372 | #define MSEC_TO_USEC 1024 | ||
1373 | |||
1374 | #define NOSLP __constant_cpu_to_le16(0), 0, 0 | ||
1375 | #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 | ||
1376 | #define SLP_TIMEOUT(T) __constant_cpu_to_le32((T) * MSEC_TO_USEC) | ||
1377 | #define SLP_VEC(X0, X1, X2, X3, X4) {__constant_cpu_to_le32(X0), \ | ||
1378 | __constant_cpu_to_le32(X1), \ | ||
1379 | __constant_cpu_to_le32(X2), \ | ||
1380 | __constant_cpu_to_le32(X3), \ | ||
1381 | __constant_cpu_to_le32(X4)} | ||
1382 | |||
1383 | |||
1384 | /* default power management (not Tx power) table values */ | ||
1385 | /* for tim 0-10 */ | ||
1386 | static struct iwl4965_power_vec_entry range_0[IWL_POWER_AC] = { | ||
1387 | {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
1388 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, | ||
1389 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), SLP_VEC(2, 4, 6, 7, 7)}, 0}, | ||
1390 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), SLP_VEC(2, 6, 9, 9, 10)}, 0}, | ||
1391 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 10)}, 1}, | ||
1392 | {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 10)}, 1} | ||
1393 | }; | ||
1394 | |||
1395 | /* for tim > 10 */ | ||
1396 | static struct iwl4965_power_vec_entry range_1[IWL_POWER_AC] = { | ||
1397 | {{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, | ||
1398 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), | ||
1399 | SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, | ||
1400 | {{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), | ||
1401 | SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, | ||
1402 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), | ||
1403 | SLP_VEC(2, 6, 9, 9, 0xFF)}, 0}, | ||
1404 | {{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, | ||
1405 | {{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), | ||
1406 | SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} | ||
1407 | }; | ||
1408 | |||
1409 | int iwl4965_power_init_handle(struct iwl_priv *priv) | ||
1410 | { | ||
1411 | int rc = 0, i; | ||
1412 | struct iwl4965_power_mgr *pow_data; | ||
1413 | int size = sizeof(struct iwl4965_power_vec_entry) * IWL_POWER_AC; | ||
1414 | u16 pci_pm; | ||
1415 | |||
1416 | IWL_DEBUG_POWER("Initialize power \n"); | ||
1417 | |||
1418 | pow_data = &(priv->power_data); | ||
1419 | |||
1420 | memset(pow_data, 0, sizeof(*pow_data)); | ||
1421 | |||
1422 | pow_data->active_index = IWL_POWER_RANGE_0; | ||
1423 | pow_data->dtim_val = 0xffff; | ||
1424 | |||
1425 | memcpy(&pow_data->pwr_range_0[0], &range_0[0], size); | ||
1426 | memcpy(&pow_data->pwr_range_1[0], &range_1[0], size); | ||
1427 | |||
1428 | rc = pci_read_config_word(priv->pci_dev, PCI_LINK_CTRL, &pci_pm); | ||
1429 | if (rc != 0) | ||
1430 | return 0; | ||
1431 | else { | ||
1432 | struct iwl4965_powertable_cmd *cmd; | ||
1433 | |||
1434 | IWL_DEBUG_POWER("adjust power command flags\n"); | ||
1435 | |||
1436 | for (i = 0; i < IWL_POWER_AC; i++) { | ||
1437 | cmd = &pow_data->pwr_range_0[i].cmd; | ||
1438 | |||
1439 | if (pci_pm & 0x1) | ||
1440 | cmd->flags &= ~IWL_POWER_PCI_PM_MSK; | ||
1441 | else | ||
1442 | cmd->flags |= IWL_POWER_PCI_PM_MSK; | ||
1443 | } | ||
1444 | } | ||
1445 | return rc; | ||
1446 | } | ||
1447 | |||
1448 | static int iwl4965_update_power_cmd(struct iwl_priv *priv, | ||
1449 | struct iwl4965_powertable_cmd *cmd, u32 mode) | ||
1450 | { | ||
1451 | int rc = 0, i; | ||
1452 | u8 skip; | ||
1453 | u32 max_sleep = 0; | ||
1454 | struct iwl4965_power_vec_entry *range; | ||
1455 | u8 period = 0; | ||
1456 | struct iwl4965_power_mgr *pow_data; | ||
1457 | |||
1458 | if (mode > IWL_POWER_INDEX_5) { | ||
1459 | IWL_DEBUG_POWER("Error invalid power mode \n"); | ||
1460 | return -1; | ||
1461 | } | ||
1462 | pow_data = &(priv->power_data); | ||
1463 | |||
1464 | if (pow_data->active_index == IWL_POWER_RANGE_0) | ||
1465 | range = &pow_data->pwr_range_0[0]; | ||
1466 | else | ||
1467 | range = &pow_data->pwr_range_1[1]; | ||
1468 | |||
1469 | memcpy(cmd, &range[mode].cmd, sizeof(struct iwl4965_powertable_cmd)); | ||
1470 | |||
1471 | #ifdef IWL_MAC80211_DISABLE | ||
1472 | if (priv->assoc_network != NULL) { | ||
1473 | unsigned long flags; | ||
1474 | |||
1475 | period = priv->assoc_network->tim.tim_period; | ||
1476 | } | ||
1477 | #endif /*IWL_MAC80211_DISABLE */ | ||
1478 | skip = range[mode].no_dtim; | ||
1479 | |||
1480 | if (period == 0) { | ||
1481 | period = 1; | ||
1482 | skip = 0; | ||
1483 | } | ||
1484 | |||
1485 | if (skip == 0) { | ||
1486 | max_sleep = period; | ||
1487 | cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
1488 | } else { | ||
1489 | __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]; | ||
1490 | max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; | ||
1491 | cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; | ||
1492 | } | ||
1493 | |||
1494 | for (i = 0; i < IWL_POWER_VEC_SIZE; i++) { | ||
1495 | if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) | ||
1496 | cmd->sleep_interval[i] = cpu_to_le32(max_sleep); | ||
1497 | } | ||
1498 | |||
1499 | IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags); | ||
1500 | IWL_DEBUG_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); | ||
1501 | IWL_DEBUG_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); | ||
1502 | IWL_DEBUG_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", | ||
1503 | le32_to_cpu(cmd->sleep_interval[0]), | ||
1504 | le32_to_cpu(cmd->sleep_interval[1]), | ||
1505 | le32_to_cpu(cmd->sleep_interval[2]), | ||
1506 | le32_to_cpu(cmd->sleep_interval[3]), | ||
1507 | le32_to_cpu(cmd->sleep_interval[4])); | ||
1508 | |||
1509 | return rc; | ||
1510 | } | ||
1511 | |||
1512 | static int iwl4965_send_power_mode(struct iwl_priv *priv, u32 mode) | ||
1513 | { | ||
1514 | u32 uninitialized_var(final_mode); | ||
1515 | int rc; | ||
1516 | struct iwl4965_powertable_cmd cmd; | ||
1517 | |||
1518 | /* If on battery, set to 3, | ||
1519 | * if plugged into AC power, set to CAM ("continuously aware mode"), | ||
1520 | * else user level */ | ||
1521 | switch (mode) { | ||
1522 | case IWL_POWER_BATTERY: | ||
1523 | final_mode = IWL_POWER_INDEX_3; | ||
1524 | break; | ||
1525 | case IWL_POWER_AC: | ||
1526 | final_mode = IWL_POWER_MODE_CAM; | ||
1527 | break; | ||
1528 | default: | ||
1529 | final_mode = mode; | ||
1530 | break; | ||
1531 | } | ||
1532 | |||
1533 | cmd.keep_alive_beacons = 0; | ||
1534 | |||
1535 | iwl4965_update_power_cmd(priv, &cmd, final_mode); | ||
1536 | |||
1537 | rc = iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, sizeof(cmd), &cmd); | ||
1538 | |||
1539 | if (final_mode == IWL_POWER_MODE_CAM) | ||
1540 | clear_bit(STATUS_POWER_PMI, &priv->status); | ||
1541 | else | ||
1542 | set_bit(STATUS_POWER_PMI, &priv->status); | ||
1543 | |||
1544 | return rc; | ||
1545 | } | ||
1546 | |||
1547 | int iwl4965_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header) | 1376 | int iwl4965_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header) |
1548 | { | 1377 | { |
1549 | /* Filter incoming packets to determine if they are targeted toward | 1378 | /* Filter incoming packets to determine if they are targeted toward |
@@ -5304,8 +5133,6 @@ static void iwl4965_alive_start(struct iwl_priv *priv) | |||
5304 | priv->active_rate = priv->rates_mask; | 5133 | priv->active_rate = priv->rates_mask; |
5305 | priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; | 5134 | priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; |
5306 | 5135 | ||
5307 | iwl4965_send_power_mode(priv, IWL_POWER_LEVEL(priv->power_mode)); | ||
5308 | |||
5309 | if (iwl_is_associated(priv)) { | 5136 | if (iwl_is_associated(priv)) { |
5310 | struct iwl4965_rxon_cmd *active_rxon = | 5137 | struct iwl4965_rxon_cmd *active_rxon = |
5311 | (struct iwl4965_rxon_cmd *)(&priv->active_rxon); | 5138 | (struct iwl4965_rxon_cmd *)(&priv->active_rxon); |
@@ -5838,6 +5665,8 @@ static void iwl4965_bg_request_scan(struct work_struct *data) | |||
5838 | direct_mask, | 5665 | direct_mask, |
5839 | (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); | 5666 | (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); |
5840 | 5667 | ||
5668 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | | ||
5669 | RXON_FILTER_BCON_AWARE_MSK); | ||
5841 | cmd.len += le16_to_cpu(scan->tx_cmd.len) + | 5670 | cmd.len += le16_to_cpu(scan->tx_cmd.len) + |
5842 | scan->channel_count * sizeof(struct iwl4965_scan_channel); | 5671 | scan->channel_count * sizeof(struct iwl4965_scan_channel); |
5843 | cmd.data = scan; | 5672 | cmd.data = scan; |
@@ -6000,6 +5829,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) | |||
6000 | 5829 | ||
6001 | iwl4965_activate_qos(priv, 0); | 5830 | iwl4965_activate_qos(priv, 0); |
6002 | 5831 | ||
5832 | iwl_power_update_mode(priv, 0); | ||
6003 | /* we have just associated, don't start scan too early */ | 5833 | /* we have just associated, don't start scan too early */ |
6004 | priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; | 5834 | priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; |
6005 | } | 5835 | } |
@@ -6990,6 +6820,8 @@ static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) | |||
6990 | iwl4965_commit_rxon(priv); | 6820 | iwl4965_commit_rxon(priv); |
6991 | } | 6821 | } |
6992 | 6822 | ||
6823 | iwl_power_update_mode(priv, 0); | ||
6824 | |||
6993 | /* Per mac80211.h: This is only used in IBSS mode... */ | 6825 | /* Per mac80211.h: This is only used in IBSS mode... */ |
6994 | if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { | 6826 | if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { |
6995 | 6827 | ||
@@ -7321,20 +7153,11 @@ static ssize_t store_power_level(struct device *d, | |||
7321 | goto out; | 7153 | goto out; |
7322 | } | 7154 | } |
7323 | 7155 | ||
7324 | if ((mode < 1) || (mode > IWL_POWER_LIMIT) || (mode == IWL_POWER_AC)) | 7156 | rc = iwl_power_set_user_mode(priv, mode); |
7325 | mode = IWL_POWER_AC; | 7157 | if (rc) { |
7326 | else | 7158 | IWL_DEBUG_MAC80211("failed setting power mode.\n"); |
7327 | mode |= IWL_POWER_ENABLED; | 7159 | goto out; |
7328 | |||
7329 | if (mode != priv->power_mode) { | ||
7330 | rc = iwl4965_send_power_mode(priv, IWL_POWER_LEVEL(mode)); | ||
7331 | if (rc) { | ||
7332 | IWL_DEBUG_MAC80211("failed setting power mode.\n"); | ||
7333 | goto out; | ||
7334 | } | ||
7335 | priv->power_mode = mode; | ||
7336 | } | 7160 | } |
7337 | |||
7338 | rc = count; | 7161 | rc = count; |
7339 | 7162 | ||
7340 | out: | 7163 | out: |
@@ -7364,7 +7187,7 @@ static ssize_t show_power_level(struct device *d, | |||
7364 | struct device_attribute *attr, char *buf) | 7187 | struct device_attribute *attr, char *buf) |
7365 | { | 7188 | { |
7366 | struct iwl_priv *priv = dev_get_drvdata(d); | 7189 | struct iwl_priv *priv = dev_get_drvdata(d); |
7367 | int level = IWL_POWER_LEVEL(priv->power_mode); | 7190 | int level = priv->power_data.power_mode; |
7368 | char *p = buf; | 7191 | char *p = buf; |
7369 | 7192 | ||
7370 | p += sprintf(p, "%d ", level); | 7193 | p += sprintf(p, "%d ", level); |
@@ -7382,14 +7205,14 @@ static ssize_t show_power_level(struct device *d, | |||
7382 | timeout_duration[level - 1] / 1000, | 7205 | timeout_duration[level - 1] / 1000, |
7383 | period_duration[level - 1] / 1000); | 7206 | period_duration[level - 1] / 1000); |
7384 | } | 7207 | } |
7385 | 7208 | /* | |
7386 | if (!(priv->power_mode & IWL_POWER_ENABLED)) | 7209 | if (!(priv->power_mode & IWL_POWER_ENABLED)) |
7387 | p += sprintf(p, " OFF\n"); | 7210 | p += sprintf(p, " OFF\n"); |
7388 | else | 7211 | else |
7389 | p += sprintf(p, " \n"); | 7212 | p += sprintf(p, " \n"); |
7390 | 7213 | */ | |
7214 | p += sprintf(p, " \n"); | ||
7391 | return (p - buf + 1); | 7215 | return (p - buf + 1); |
7392 | |||
7393 | } | 7216 | } |
7394 | 7217 | ||
7395 | static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, | 7218 | static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, |