aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2010-09-30 07:28:27 -0400
committerLuciano Coelho <luciano.coelho@nokia.com>2010-10-05 09:27:29 -0400
commit1f37cbc9363462c99794699442da39f36be0aaf7 (patch)
tree3f7bf42c5fa78b08010071da8f1977234e255c64 /drivers
parent5c57a901dc96fc81d0041282269b43542f170d2a (diff)
wl1271: Support firmware RX packet aggregation
Instead of retrieving one packet at a time from the firmware, try to retrieve all available packets at once. This optimization decreases the number of transactions, which saves CPU cycles and increases network throughput. Signed-off-by: Ido Yariv <ido@wizery.com> Tested-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h5
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c15
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.c63
3 files changed, 66 insertions, 17 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 779b130fdb3..8a4cd763e5a 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -130,6 +130,8 @@ enum {
130 130
131#define ACX_TX_DESCRIPTORS 32 131#define ACX_TX_DESCRIPTORS 32
132 132
133#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
134
133enum wl1271_state { 135enum wl1271_state {
134 WL1271_STATE_OFF, 136 WL1271_STATE_OFF,
135 WL1271_STATE_ON, 137 WL1271_STATE_ON,
@@ -408,6 +410,9 @@ struct wl1271 {
408 /* Rx memory pool address */ 410 /* Rx memory pool address */
409 struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; 411 struct wl1271_rx_mem_pool_addr rx_mem_pool_addr;
410 412
413 /* Intermediate buffer, used for packet aggregation */
414 u8 *aggr_buf;
415
411 /* The target interrupt mask */ 416 /* The target interrupt mask */
412 struct work_struct irq_work; 417 struct work_struct irq_work;
413 418
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index cb18f22bbc5..8071da10dbc 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -2459,6 +2459,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
2459 struct platform_device *plat_dev = NULL; 2459 struct platform_device *plat_dev = NULL;
2460 struct wl1271 *wl; 2460 struct wl1271 *wl;
2461 int i, ret; 2461 int i, ret;
2462 unsigned int order;
2462 2463
2463 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); 2464 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
2464 if (!hw) { 2465 if (!hw) {
@@ -2517,11 +2518,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
2517 2518
2518 wl1271_debugfs_init(wl); 2519 wl1271_debugfs_init(wl);
2519 2520
2521 order = get_order(WL1271_AGGR_BUFFER_SIZE);
2522 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
2523 if (!wl->aggr_buf) {
2524 ret = -ENOMEM;
2525 goto err_hw;
2526 }
2527
2520 /* Register platform device */ 2528 /* Register platform device */
2521 ret = platform_device_register(wl->plat_dev); 2529 ret = platform_device_register(wl->plat_dev);
2522 if (ret) { 2530 if (ret) {
2523 wl1271_error("couldn't register platform device"); 2531 wl1271_error("couldn't register platform device");
2524 goto err_hw; 2532 goto err_aggr;
2525 } 2533 }
2526 dev_set_drvdata(&wl->plat_dev->dev, wl); 2534 dev_set_drvdata(&wl->plat_dev->dev, wl);
2527 2535
@@ -2547,6 +2555,9 @@ err_bt_coex_state:
2547err_platform: 2555err_platform:
2548 platform_device_unregister(wl->plat_dev); 2556 platform_device_unregister(wl->plat_dev);
2549 2557
2558err_aggr:
2559 free_pages((unsigned long)wl->aggr_buf, order);
2560
2550err_hw: 2561err_hw:
2551 wl1271_debugfs_exit(wl); 2562 wl1271_debugfs_exit(wl);
2552 kfree(plat_dev); 2563 kfree(plat_dev);
@@ -2563,6 +2574,8 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
2563int wl1271_free_hw(struct wl1271 *wl) 2574int wl1271_free_hw(struct wl1271 *wl)
2564{ 2575{
2565 platform_device_unregister(wl->plat_dev); 2576 platform_device_unregister(wl->plat_dev);
2577 free_pages((unsigned long)wl->aggr_buf,
2578 get_order(WL1271_AGGR_BUFFER_SIZE));
2566 kfree(wl->plat_dev); 2579 kfree(wl->plat_dev);
2567 2580
2568 wl1271_debugfs_exit(wl); 2581 wl1271_debugfs_exit(wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index 94da5dd7723..bea133b6e48 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -74,7 +74,7 @@ static void wl1271_rx_status(struct wl1271 *wl,
74 } 74 }
75} 75}
76 76
77static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) 77static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
78{ 78{
79 struct wl1271_rx_descriptor *desc; 79 struct wl1271_rx_descriptor *desc;
80 struct sk_buff *skb; 80 struct sk_buff *skb;
@@ -87,16 +87,16 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
87 * workaround this by not retrieving them at all. 87 * workaround this by not retrieving them at all.
88 */ 88 */
89 if (unlikely(wl->state == WL1271_STATE_PLT)) 89 if (unlikely(wl->state == WL1271_STATE_PLT))
90 return; 90 return -EINVAL;
91 91
92 skb = __dev_alloc_skb(length, GFP_KERNEL); 92 skb = __dev_alloc_skb(length, GFP_KERNEL);
93 if (!skb) { 93 if (!skb) {
94 wl1271_error("Couldn't allocate RX frame"); 94 wl1271_error("Couldn't allocate RX frame");
95 return; 95 return -ENOMEM;
96 } 96 }
97 97
98 buf = skb_put(skb, length); 98 buf = skb_put(skb, length);
99 wl1271_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); 99 memcpy(buf, data, length);
100 100
101 /* the data read starts with the descriptor */ 101 /* the data read starts with the descriptor */
102 desc = (struct wl1271_rx_descriptor *) buf; 102 desc = (struct wl1271_rx_descriptor *) buf;
@@ -116,6 +116,8 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
116 skb_trim(skb, skb->len - desc->pad_len); 116 skb_trim(skb, skb->len - desc->pad_len);
117 117
118 ieee80211_rx_ni(wl->hw, skb); 118 ieee80211_rx_ni(wl->hw, skb);
119
120 return 0;
119} 121}
120 122
121void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) 123void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
@@ -124,31 +126,60 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
124 u32 buf_size; 126 u32 buf_size;
125 u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; 127 u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
126 u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; 128 u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
129 u32 rx_counter;
127 u32 mem_block; 130 u32 mem_block;
131 u32 pkt_length;
132 u32 pkt_offset;
128 133
129 while (drv_rx_counter != fw_rx_counter) { 134 while (drv_rx_counter != fw_rx_counter) {
130 mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); 135 buf_size = 0;
131 buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter); 136 rx_counter = drv_rx_counter;
137 while (rx_counter != fw_rx_counter) {
138 pkt_length = wl1271_rx_get_buf_size(status, rx_counter);
139 if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
140 break;
141 buf_size += pkt_length;
142 rx_counter++;
143 rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
144 }
132 145
133 if (buf_size == 0) { 146 if (buf_size == 0) {
134 wl1271_warning("received empty data"); 147 wl1271_warning("received empty data");
135 break; 148 break;
136 } 149 }
137 150
151 /*
152 * Choose the block we want to read
153 * For aggregated packets, only the first memory block should
154 * be retrieved. The FW takes care of the rest.
155 */
156 mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter);
138 wl->rx_mem_pool_addr.addr = (mem_block << 8) + 157 wl->rx_mem_pool_addr.addr = (mem_block << 8) +
139 le32_to_cpu(wl_mem_map->packet_memory_pool_start); 158 le32_to_cpu(wl_mem_map->packet_memory_pool_start);
140 wl->rx_mem_pool_addr.addr_extra = 159 wl->rx_mem_pool_addr.addr_extra =
141 wl->rx_mem_pool_addr.addr + 4; 160 wl->rx_mem_pool_addr.addr + 4;
142
143 /* Choose the block we want to read */
144 wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, 161 wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr,
145 sizeof(wl->rx_mem_pool_addr), false); 162 sizeof(wl->rx_mem_pool_addr), false);
146 163
147 wl1271_rx_handle_data(wl, buf_size); 164 /* Read all available packets at once */
148 165 wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
149 wl->rx_counter++; 166 buf_size, true);
150 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; 167
168 /* Split data into separate packets */
169 pkt_offset = 0;
170 while (pkt_offset < buf_size) {
171 pkt_length = wl1271_rx_get_buf_size(status,
172 drv_rx_counter);
173 if (wl1271_rx_handle_data(wl,
174 wl->aggr_buf + pkt_offset,
175 pkt_length) < 0)
176 break;
177 wl->rx_counter++;
178 drv_rx_counter++;
179 drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
180 pkt_offset += pkt_length;
181 }
151 } 182 }
152 183 wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS,
153 wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); 184 cpu_to_le32(wl->rx_counter));
154} 185}