rt2x00: change beaconing setup on RT2800
authorStanislaw Gruszka <sgruszka@redhat.com>
Thu, 5 Jun 2014 11:52:24 +0000 (13:52 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 19 Jun 2014 19:49:15 +0000 (15:49 -0400)
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>
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00queue.h

index c17fcf272728cb06ae25e95787003f6f59f52dba..c45b2d31ea6544c939eb1881c6bb3973da970561 100644 (file)
@@ -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));
 }
 
+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)
 {
        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,
                                   entry->skb->len + padding_len);
+       __set_bit(ENTRY_BCN_ENABLED, &entry->flags);
+
+       /*
+        * Change global beacons settings.
+        */
+       rt2800_update_beacons_setup(rt2x00dev);
 
        /*
         * Restore beaconing state.
@@ -1053,7 +1093,12 @@ void rt2800_clear_beacon(struct queue_entry *entry)
         * Clear beacon.
         */
        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.
         */
index c48125be0e34247cfe30ba51be437bb03ef66ed3..2233b911a1d7d7c5992db5681dcc1907d9719769 100644 (file)
@@ -353,6 +353,7 @@ struct txentry_desc {
  */
 enum queue_entry_flags {
        ENTRY_BCN_ASSIGNED,
+       ENTRY_BCN_ENABLED,
        ENTRY_OWNER_DEVICE_DATA,
        ENTRY_DATA_PENDING,
        ENTRY_DATA_IO_FAILED,