浏览代码

rt2x00: change beaconing setup on RT2800

As reported by Matthias, on 5572 chip, even if we clear up TXWI
of corresponding beacon, hardware still try to send it or do other
action that increase power consumption peak up to 1A.

To avoid the issue, setup beaconing dynamically by configuring offsets
of currently active beacons and MAC_BSSID_DW1_BSS_BCN_NUM variable,
which limit number of beacons that hardware will try to send.

Reported-by: Matthias Fend <Matthias.Fend@wolfvision.net>
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Acked-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Stanislaw Gruszka 11 年之前
父节点
当前提交
ba08910e04
共有 2 个文件被更改,包括 46 次插入0 次删除
  1. 45 0
      drivers/net/wireless/rt2x00/rt2800lib.c
  2. 1 0
      drivers/net/wireless/rt2x00/rt2x00queue.h

+ 45 - 0
drivers/net/wireless/rt2x00/rt2800lib.c

@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
 	return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
 	return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
 }
 }
 
 
+static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev)
+{
+	struct data_queue *queue = rt2x00dev->bcn;
+	struct queue_entry *entry;
+	int i, bcn_num = 0;
+	u64 off, reg = 0;
+	u32 bssid_dw1;
+
+	/*
+	 * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers.
+	 */
+	for (i = 0; i < queue->limit; i++) {
+		entry = &queue->entries[i];
+		if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags))
+			continue;
+		off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx);
+		reg |= off << (8 * bcn_num);
+		bcn_num++;
+	}
+
+	WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing);
+
+	rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg);
+	rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32));
+
+	/*
+	 * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons.
+	 */
+	rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1);
+	rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM,
+			   bcn_num > 0 ? bcn_num - 1 : 0);
+	rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1);
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 
 
 	rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
 	rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
 				   entry->skb->len + padding_len);
 				   entry->skb->len + padding_len);
+	__set_bit(ENTRY_BCN_ENABLED, &entry->flags);
+
+	/*
+	 * Change global beacons settings.
+	 */
+	rt2800_update_beacons_setup(rt2x00dev);
 
 
 	/*
 	/*
 	 * Restore beaconing state.
 	 * Restore beaconing state.
@@ -1053,7 +1093,12 @@ void rt2800_clear_beacon(struct queue_entry *entry)
 	 * Clear beacon.
 	 * Clear beacon.
 	 */
 	 */
 	rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
 	rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
+	__clear_bit(ENTRY_BCN_ENABLED, &entry->flags);
 
 
+	/*
+	 * Change global beacons settings.
+	 */
+	rt2800_update_beacons_setup(rt2x00dev);
 	/*
 	/*
 	 * Restore beaconing state.
 	 * Restore beaconing state.
 	 */
 	 */

+ 1 - 0
drivers/net/wireless/rt2x00/rt2x00queue.h

@@ -353,6 +353,7 @@ struct txentry_desc {
  */
  */
 enum queue_entry_flags {
 enum queue_entry_flags {
 	ENTRY_BCN_ASSIGNED,
 	ENTRY_BCN_ASSIGNED,
+	ENTRY_BCN_ENABLED,
 	ENTRY_OWNER_DEVICE_DATA,
 	ENTRY_OWNER_DEVICE_DATA,
 	ENTRY_DATA_PENDING,
 	ENTRY_DATA_PENDING,
 	ENTRY_DATA_IO_FAILED,
 	ENTRY_DATA_IO_FAILED,