Commit 2fc28de5 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Luciano Coelho

wlcore/wl18xx: add hw op for setting Tx HW checksum

Some chip families are capable of checksumming certain classes of Tx
packets in HW. Indicate this fact in the netdev features and perform the
HW checksum by protocol type for the 18xx family.

Fix the location of the skb network header when we move it so we can
rely on it when setting the checksum.
Signed-off-by: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent b8422dcb
...@@ -1288,6 +1288,13 @@ static void wl12xx_get_mac(struct wl1271 *wl) ...@@ -1288,6 +1288,13 @@ static void wl12xx_get_mac(struct wl1271 *wl)
wl12xx_get_fuse_mac(wl); wl12xx_get_fuse_mac(wl);
} }
static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
desc->wl12xx_reserved = 0;
}
static struct wlcore_ops wl12xx_ops = { static struct wlcore_ops wl12xx_ops = {
.identify_chip = wl12xx_identify_chip, .identify_chip = wl12xx_identify_chip,
.identify_fw = wl12xx_identify_fw, .identify_fw = wl12xx_identify_fw,
...@@ -1306,6 +1313,7 @@ static struct wlcore_ops wl12xx_ops = { ...@@ -1306,6 +1313,7 @@ static struct wlcore_ops wl12xx_ops = {
.sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask,
.get_pg_ver = wl12xx_get_pg_ver, .get_pg_ver = wl12xx_get_pg_ver,
.get_mac = wl12xx_get_mac, .get_mac = wl12xx_get_mac,
.set_tx_desc_csum = wl12xx_set_tx_desc_csum,
}; };
static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
......
...@@ -55,3 +55,29 @@ int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, ...@@ -55,3 +55,29 @@ int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
return ret; return ret;
} }
int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
{
struct wl18xx_acx_checksum_state *acx;
int ret;
wl1271_debug(DEBUG_ACX, "acx checksum state");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("failed to set Tx checksum state: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
...@@ -45,8 +45,24 @@ struct wl18xx_acx_host_config_bitmap { ...@@ -45,8 +45,24 @@ struct wl18xx_acx_host_config_bitmap {
} __packed; } __packed;
enum {
CHECKSUM_OFFLOAD_DISABLED = 0,
CHECKSUM_OFFLOAD_ENABLED = 1,
CHECKSUM_OFFLOAD_FAKE_RX = 2,
CHECKSUM_OFFLOAD_INVALID = 0xFF
};
struct wl18xx_acx_checksum_state {
struct acx_header header;
/* enum acx_checksum_state */
u8 checksum_state;
u8 pad[3];
} __packed;
int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
u32 sdio_blk_size, u32 extra_mem_blks, u32 sdio_blk_size, u32 extra_mem_blks,
u32 len_field_size); u32 len_field_size);
int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
#endif /* __WL12XX_ACX_H__ */ #endif /* __WL12XX_ACX_H__ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/ip.h>
#include "../wlcore/wlcore.h" #include "../wlcore/wlcore.h"
#include "../wlcore/debug.h" #include "../wlcore/debug.h"
...@@ -501,9 +502,38 @@ static int wl18xx_hw_init(struct wl1271 *wl) ...@@ -501,9 +502,38 @@ static int wl18xx_hw_init(struct wl1271 *wl)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl18xx_acx_set_checksum_state(wl);
if (ret != 0)
return ret;
return ret; return ret;
} }
static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
u32 ip_hdr_offset;
struct iphdr *ip_hdr;
if (skb->ip_summed != CHECKSUM_PARTIAL) {
desc->wl18xx_checksum_data = 0;
return;
}
ip_hdr_offset = skb_network_header(skb) - skb_mac_header(skb);
if (WARN_ON(ip_hdr_offset >= (1<<7))) {
desc->wl18xx_checksum_data = 0;
return;
}
desc->wl18xx_checksum_data = ip_hdr_offset << 1;
/* FW is interested only in the LSB of the protocol TCP=0 UDP=1 */
ip_hdr = (void *)skb_network_header(skb);
desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01);
}
static struct wlcore_ops wl18xx_ops = { static struct wlcore_ops wl18xx_ops = {
.identify_chip = wl18xx_identify_chip, .identify_chip = wl18xx_identify_chip,
.boot = wl18xx_boot, .boot = wl18xx_boot,
...@@ -517,6 +547,7 @@ static struct wlcore_ops wl18xx_ops = { ...@@ -517,6 +547,7 @@ static struct wlcore_ops wl18xx_ops = {
.tx_immediate_compl = wl18xx_tx_immediate_completion, .tx_immediate_compl = wl18xx_tx_immediate_completion,
.tx_delayed_compl = NULL, .tx_delayed_compl = NULL,
.hw_init = wl18xx_hw_init, .hw_init = wl18xx_hw_init,
.set_tx_desc_csum = wl18xx_set_tx_desc_csum,
}; };
int __devinit wl18xx_probe(struct platform_device *pdev) int __devinit wl18xx_probe(struct platform_device *pdev)
......
...@@ -1245,6 +1245,7 @@ enum { ...@@ -1245,6 +1245,7 @@ enum {
ACX_CONFIG_HANGOVER = 0x0042, ACX_CONFIG_HANGOVER = 0x0042,
ACX_FEATURE_CFG = 0x0043, ACX_FEATURE_CFG = 0x0043,
ACX_PROTECTION_CFG = 0x0044, ACX_PROTECTION_CFG = 0x0044,
ACX_CHECKSUM_CONFIG = 0x0045,
}; };
......
...@@ -119,4 +119,15 @@ static inline int wlcore_identify_fw(struct wl1271 *wl) ...@@ -119,4 +119,15 @@ static inline int wlcore_identify_fw(struct wl1271 *wl)
return 0; return 0;
} }
static inline void
wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
if (!wl->ops->set_tx_desc_csum)
BUG_ON(1);
wl->ops->set_tx_desc_csum(wl, desc, skb);
}
#endif #endif
...@@ -270,6 +270,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -270,6 +270,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (extra) { if (extra) {
int hdrlen = ieee80211_hdrlen(frame_control); int hdrlen = ieee80211_hdrlen(frame_control);
memmove(frame_start, hdr, hdrlen); memmove(frame_start, hdr, hdrlen);
skb_set_network_header(skb, skb_network_offset(skb) + extra);
} }
/* configure packet life time */ /* configure packet life time */
...@@ -332,9 +333,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -332,9 +333,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ieee80211_has_protected(frame_control)) ieee80211_has_protected(frame_control))
tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
desc->reserved = 0;
desc->tx_attr = cpu_to_le16(tx_attr); desc->tx_attr = cpu_to_le16(tx_attr);
wlcore_hw_set_tx_desc_csum(wl, desc, skb);
wlcore_hw_set_tx_desc_data_len(wl, desc, skb); wlcore_hw_set_tx_desc_data_len(wl, desc, skb);
} }
......
...@@ -130,7 +130,16 @@ struct wl1271_tx_hw_descr { ...@@ -130,7 +130,16 @@ struct wl1271_tx_hw_descr {
u8 tid; u8 tid;
/* host link ID (HLID) */ /* host link ID (HLID) */
u8 hlid; u8 hlid;
u8 reserved;
union {
u8 wl12xx_reserved;
/*
* bit 0 -> 0 = udp, 1 = tcp
* bit 1:7 -> IP header offset
*/
u8 wl18xx_checksum_data;
} __packed;
} __packed; } __packed;
enum wl1271_tx_hw_res_status { enum wl1271_tx_hw_res_status {
......
...@@ -61,6 +61,9 @@ struct wlcore_ops { ...@@ -61,6 +61,9 @@ struct wlcore_ops {
struct wl12xx_vif *wlvif); struct wl12xx_vif *wlvif);
s8 (*get_pg_ver)(struct wl1271 *wl); s8 (*get_pg_ver)(struct wl1271 *wl);
void (*get_mac)(struct wl1271 *wl); void (*get_mac)(struct wl1271 *wl);
void (*set_tx_desc_csum)(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb);
}; };
enum wlcore_partitions { enum wlcore_partitions {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment